Well-posed problem generation with machine-checkable answer certificates
candidates 171 (rejected 0, duplicates 0)
unique 171
levels L1:18 L2:29 L3:56 L4:38 L5:30
reverify 171 ok, 0 failed| 形态 | 例子 | 后果 |
|---|---|---|
| 无解 | 要求「求两根」 | 学生算到判别式为负,怀疑自己 |
| 多解 | 分式方程去分母后引入增根 | 答案对不上,老师要逐个解释 |
| 退化 | 十道题里四道的常数项都是 0 | 练不到东西 |
answer {
r1: int in [-9, 9]
r2: int in [-9, 9]
}
param {
a: 1
b: -(a * (r1 + r2)) # 韦达定理,由根反解
c: a * r1 * r2
}
require {
r1 < r2
}生成:选根 (r1, r2) → 韦达 → 系数 (a, b, c)
验证:取系数 (a, b, c) → 判别式 → 开方 → 求根公式 → 比对解集git clone https://github.com/DWS-ai-nb/kiln
cd kiln
moon update# 构建(Windows 上没有 MSVC 时用 js 后端)
moon build --target js
# 体检:生成全题池并逐题独立复核
moon run cmd/cli --target js -- check --schema examples/quadratic.kiln
# 出卷:10 题,固定种子
moon run cmd/cli --target js -- gen --schema examples/quadratic.kiln \
--count 10 --seed 7 --out out
# 按难度配额组卷
moon run cmd/cli --target js -- gen --schema examples/quadratic.kiln \
--levels 1:2,2:3,3:3,4:2 --seed 2026 --out out
# 复核一份卷子:不读 schema,不重跑生成器,只凭证重算
moon run cmd/cli --target js -- verify --file out/paper.json
# 平行卷:给 A 卷造一份结构等价的 B 卷(难度逐题对齐、结构标签一致、题目不重)
moon run cmd/cli --target js -- parallel --schema examples/quadratic.kiln --file out/paper.json --seed 11 --out out-b
# 跨周复用:这周发的题,下周不再出现
moon run cmd/cli --target js -- gen --schema examples/quadratic.kiln \
--count 8 --seed 22 --out out --exclude out/ledger.txt --record out/ledger.txt| 文件 | 族 | 说明 |
|---|---|---|
| examples/quadratic.kiln | quadratic | 整数根一元二次方程,171 道唯一题 |
| examples/rational.kiln | rational | 分式方程,312 道唯一题 |
| examples/rational_with_extraneous.kiln | rational | 反面教材:故意不写 r != k,让验证器拦下 26 道增根题 |
组卷失败:L5 需要 30 题,可用的只有 12 题(缺口 18,可能受结构多样性上限限制)$ python3 tools/verify_reference.py out/paper.json
schema=quadratic_int_roots items=4 passed=4 failed=0$ python3 tools/verify_reference.py tampered.json
1: answer-mismatch: formula gives [Fraction(-4, 1), Fraction(0, 1)], declared [Fraction(99, 1)]
schema=quadratic_int_roots items=4 passed=3 failed=1 # 退出码 1| 子句 | 作用 |
|---|---|
| schema "name" | 唯一标识 |
| family quadratic | 选择验证策略 |
| title "..." | 卷面标题 |
| answer { ... } | 答案变量,必须带整数域 [lo, hi] |
| param { ... } | 参数:带域则枚举,带表达式则推导(可引用前面已绑定的变量) |
| require { ... } | 并列条件,多条之间是「且」;表达式引用未定义变量会在加载时报错 |
| ask "..." | 题面模板,{poly} 由验证族渲染 |
| 文件 | 职责 | 关键点 |
|---|---|---|
| rat.mbt | 精确有理数 | 归一化不变量(den>0、互素、零为 0/1);精确开方 |
| poly.mbt | 一元多项式 | 正规形;canonical_key 做同解规范化 |
| lexer.mbt | 词法 | 不抛异常,坏字符变 TBad 由语法层定位 |
| ast.mbt | 表达式与求值 | 除以零返回 None 而非崩溃,语义上就是「此处无定义」 |
| parse.mbt | 递归下降语法 | 错误一律带行号;解析与校验分离 |
| engine.mbt | 枚举与题池 | 深度优先枚举推导链,叶子处做条件检查与去重 |
| verify.mbt | 独立验证 | 判别式 → 开方 → 求根公式,与生成路径完全分离 |
| family_quadratic.mbt | 二次族 | 逆向构造、题面渲染、可解释难度、结构标签 |
| family_rational.mbt | 分式族 | 交叉相乘验证、增根判定、分母零点检查 |
| canon(在 poly.mbt) | 结构指纹 | 同解规范化 |
| paper.mbt | 组卷 | 难度配额 + 结构多样性 + 已选集合 + 不可行解释 |
| render.mbt | 输出 | HTML / Markdown,全部转义 |
| paper_io.mbt | 卷子文件 | kiln.paper/1 的读写与独立校验 |
| tools/verify_reference.py | 参考校验器 | 112 行 Python,与实现零共享 |
| rng.mbt | 确定性随机 | splitmix64,20 行,不依赖平台熵 |
| cmd/cli | 命令行 | 四个动词,无配置文件;账本读写在 CLI 层 |
| golden_wbtest.mbt | 跨后端黄金测试 | 把整卷输出钉死,两个后端同时校验 |
| properties_test.mbt | 属性测试 | 验证器一致性、指纹缩放不变、指纹幂等 |
schema.kiln ──parse──> Schema ──enumerate──> 候选参数组
│
family build(逆向)
│
verify(正向,独立)
│
┌───────────────────┴──────────────┐
WellPosed NotWellPosed
│ │
Item + 凭证 + 指纹 计入 rejects
│
paper.assemble(配额 / 多样性 / 去重)
│
render → HTML / Markdown$ moon run cmd/cli --target js -- check --schema examples/rational_with_extraneous.kiln
candidates 338 (rejected 26, duplicates 0)
rejects 1 distinct reason(s)
- extraneous-rootpub suberror SchemaError {
SchemaError(String)
}pub enum CmpOp {
CLt
CLe
CGt
CGe
CEq
CNe
}pub enum Family {
FQuadratic
FRational
}pub struct Rat {
num : Int64
den : Int64
}pub struct Rng {
state : UInt64
}pub enum Tok {
TIdent(String)
TNum(Int64)
TStr(String)
TPunct(String)
TBad(Char)
TEof
}pub enum Verdict {
WellPosed
NotQuadratic
NoRealRoots
DoubleRoot
IrrationalRoots
ExtraneousRoot
Identity
NotLinear
AnswerMismatch(String)
}fn escape_html(s : String) -> Stringfn parse_int_opt(s : String) -> Int?fn replace_all(s : String, from : String, to : String) -> StringInstall
Download zipWell-posed problem generation with machine-checkable answer certificates