MVP 阶段没有拆微服务。选型的判断标准只有一个:一个人能不能可靠地部署和回滚它。
为了让 MVP 能先跑通闭环、再逐个换成真实实现:
| AI | mock / openai-compatible组卷侧已接 DeepSeek;作业报告类仍 mock |
| Storage | local / cos / wechat-cloud后两者为占位,未接真实 SDK |
| Repository | PrismaRepository / MemoryRepository测试不依赖数据库 |
自然语言组卷最容易的做法,是把整句话丢给大模型让它返回 JSON。我没这么做,因为那样每一轮对话都要花钱、都有延迟、都可能不稳定。实际做法是四级漏斗,前面能拦下的绝不往后放。
知识点反查支持裸知识点——老师只说"积分"、不说单元,系统也要能找到。这就意味着检索必须能在没有 unit 上下文的情况下全局反查。
而一旦允许全局反查,下面这个 P0 就必然会发生。
偶发、无规律,而且测试用例还断言"通过"。前端也看不出异常——未解析项列表是空的,界面显示一切正常。
U1/U2/U4)subject假设"题号越大越难"。问题有两个:这个假设从没被验证过;而且同一句需求两次生成,难度分布会漂。老师问"你凭什么说这题偏难",答不出来。
难度定义为该题在同一 unit 内的分位。同一句需求两次生成,难度分布一致;每一档都能解释成"这一档指的是 unit 内第几分位"。
A-Level 各单元的绝对难度本来就不可比——P1 的"难"和 FP3 的"难"不是一回事。老师说"P1 偏难一点",指的是在 P1 的范围内偏难,不是跨单元的绝对难度。分位数正好表达这个语义。
副作用:新灌进来的题必须等该 unit 的分布重算之后才有难度值。这是上面「难度未标」的直接原因,也是这个设计要付的代价。
组卷能不能用,取决于一件枯燥的事——每一道题有没有被正确分类到知识点。几千道题让老师逐题 review 不现实,全交给模型又不可信。
高置信、双 Agent 一致 → 直接进生产库,可被检索到。
有分歧或置信度中等 → 进复核队列,等教师拍板。不上生产。
低置信或明显异常 → 隔离,连复核队列都不进,等重跑或人工介入。
我一开始用同一个模型既做题又复核,一致率很好看。后来意识到这是同源偏置:同一个模型对自己的判断天然认同,一致率高只说明它前后一致,不说明它对。
解决办法是引入不同代际的更强模型做抽样校准,并把分歧单独归档成文档。这件事有两组各自独立的数据可以说明它的必要性。
物理本体重建后做全量复核:637 道已接受的题,独立复核 Agent 与管线的一致率 85.9%,90 处分歧里裁定管线错分 79 处。关键是这 79 处高度集中在 4 个知识边界上——驻波、emf 与电路、散射与加速器、矢量与 SUVAT。这类成片的系统性错误,逐题抽检是发现不了的,只有做全量对照才会浮出来。
后来做化学 / 生物 / 经济 / 心理四科提质时,我把更强模型的校准固化进流程,专门对冲同源偏置。结果它拦下了 20 处复核 Agent 的"过纠"——心理学科的过纠率高达 25.8%,其中一个单元甚至被一边倒地判了 16/16。不换模型,这 20 处会被当成修正直接写进库里。最终 164 处确认纠正回写。
Edexcel IAL 六科,基本 100% 带原始题图。CIE / AQA 已跑通分类 pilot,产品里明确标注「建设中」。
| 科目 | 题量 | 状态与备注 |
|---|---|---|
| Mathematics 数学 | 2,649 | 含 FP1/FP2/FP3 的教师方法标签维度(与 chapter 解耦,仅供自然语言组卷使用) |
| Chemistry 化学 | 1,056 | 提质后残留污染 0.9% |
| Physics 物理 | 556 | 本体已迭代至 v3(44 → 27 节点) |
| Economics 经济 | 353 | 论述题需剥除点线答题格后再切图 |
| Biology 生物 | 317 | — |
| Psychology 心理 | 308 | — |
| 合计 | 5,239 | 另有选择题专卷与速练模块 |
官方章节是考试局视角,按考纲归档。老师按"怎么讲、常用哪个方法"组织。所以除了章节维度,我额外引入了教师方法标签作为第二个组卷维度——与 chapter 解耦,只服务自然语言组卷。因为老师经常不是按章节出卷,而是按方法出卷("考他会不会用换元")。
物理本体 v3 做的主要动作是收敛:44 个节点砍到 27 个。太细的分类,老师同样不会用——他脑子里没有那么多格子。重跑后结构上仅一个单元的 review 率真降,其余持平,说明细分带来的不是精度而是噪声。
题库里存的是逐题切出来的图片片段(fragments)。要拼成一份能直接打印的卷子,中间这几步都得自己做。
从原卷 PDF 按题切出图片片段,跨页题需要跨页拼接。经济这类论述题还要先剥掉点线答题格,否则切出来一片格子。
原题号(第 3 题 / 第 1 题 / 第 10 题)在新卷里重排为 1–4;选择题与大题分段独立编号;原题号保留在来源行里。
水印在渲染阶段合进页面而不是叠一层,不可被简单去除。
原卷页脚、草稿区、"rough working" 条带在切图阶段就被滤掉,不带进新卷。
题目卷与答案卷分开渲染成两个 PDF。答案卷带官方评分点(B1 / M1 / A1)。
U:e3e17e38 · WS:65efa3b3 · B:5c9d8b42 · UTC——使用者、卷子编号、构建号、生成时间。
code2Session → JWTprisma migrate deploy,禁用 db push跨科串题那个已经在上面讲过了,这里是另外三个。第三个到现在还没修好,我照原样写在这儿。
现象:判重脚本报出 412 道重复。
根因:判重用的 full_question_text 是页级字段——同一页上的多道小题拿到的是同一段文本,实际切图完全正确。差点为此重跑整条管线。
规则:字段的粒度就是它的语义。判重前先确认这个字段描述的是"页"还是"题"。
现象:真机上一片"请求失败"。
根因:我用管理员的角色模拟功能在测,而模拟身份用的是一个没有实体的 ID,后端归属校验自然全部拒绝。
规则:真机回归必须用真实账号,模拟身份只能用于 UI 预览。表象错误和真实根因经常不在同一层。
现象:同一批测试有时全绿有时红,受害文件每次还不一样。
我原本的归因:supertest 每个请求起一个临时 server,端口复用产生竞态。我照这个思路做了「全局持久化一个 server」的修复,当时连跑全绿。
后来推翻了:vitest.config.ts 里明写着 fileParallelism: false——测试文件根本不并行,竞态说不通;而且那个修复没合进当前分支。独立复跑 7 轮,仍有 3 轮复现。目前更像是某个文件里 vi.doMock('../config/env.js') 的跨文件泄漏。
规则:一个说得通的根因不等于对的根因,没做证伪就不算定位。另外要把间歇失败和稳定失败分开记——本机没起 Postgres 时 dev-login 必然报错,那条每次都红,跟这个 flaky 是两回事,混在一起会让验收判断彻底失效。
题库分类质量有门禁、复核、校准和内部一致率;但"这份卷子是不是老师想要的那份"目前只靠约束满足和教师复核,没有正式评测集。下一步:建一个 20 份规模的教师打分集,把"效果评估"从意识变成实践。
不同老师对同一道题的分类本来就会有分歧,目前走 review_queue 由教师拍板,但没有算过 IAA / Kappa。
分位数口径定好了,但回填是分批跑的。新灌进来的题必须等该 unit 的分布重算之后才有难度值。
无 CI/CD;数据库单点;文件存储仍是本地,COS / 微信云存储为占位;视频链路未部署;小程序尚未提交微信审核。