灯塔场景工程

河蟹 AI 让你"父慈子笑":首个灯塔场景的 K12 工程实践

“会不会讲”有六科教学 Skill 做依据,真正先啃的是三个问题:讲错了怎么办?孩子数据放哪?再加一个场景,平台会不会变脏?架构上就一句话:内不脏,外不扰民。

微信公众号首发 本文首发于微信公众号「hexclaw」(ID: hexclaw)官方技术专栏 · 发表于 2026年10月4日 · 预计阅读 14 分钟
微信原文 ↗

一个通用 Agent 平台,能不能长出一个真正可信的垂直场景,还不把自己搞脏,还不扰民?"通用还是垂直"这道老题,Agent 时代又问了一遍。河蟹这一版挑了个最不容出错的场景来验证——辅导孩子写作业:AI 只要错一次,家长就会错着教一遍,孩子错着记一轮。

这场"最不容出错"的辅导落到日常,就是那句老梗——"不写作业母慈子孝,一写作业鸡飞狗跳"。这句梗背后,是官方媒体写了五年的公共情绪问题:《中国青年报》2021 年就发过《家长如何摆脱"恐辅症"》,2026 年 2 月省委宣传部旗下公号《浙江宣传》仍在追问《为什么辅导作业会"上火抓狂"》。五年过去,在我们看来,这个问题还没有被哪个产品真正接住。

倒是标题先接住了——搜一下"AI 辅导作业",一个标题公式反复出现:从"鸡飞狗跳"到"母慈子孝",只差一个 AI。公式有它对的地方:拍照、识题、给答案,今天的多模态模型都会,连头部厂商都在往"引导式学习"走——Anthropic 的 Claude for Education Learning mode、OpenAI 的 ChatGPT Study mode、Google 的 Gemini Guided Learning,2025 年相继发布,面向从高校到 K12 的不同学段。但套着这个公式的文章,都在测"AI 会不会讲、讲得顺不顺",很少把"讲错了怎么办"当成工程问题展开。说白了:你敢不敢把 AI 给出的答案,拿去教自己的孩子?越是在崩溃边缘的家长,越没有余力去核对它的每一步。

而家长要的,也远不止一个对的答案:用孩子学过的方法把题教会,别拿"二元一次方程组"把五年级孩子讲懵——还有错题、学情这种最敏感的家庭数据,不能不明不白地送进云端。

河蟹 AI 的桌面版 v0.5.0-beta.3(后端 hexclaw v0.5.0-beta.1),把作业辅导当成平台的第一个"灯塔场景"做了:K12 家长备课助手——让家长像名师一样辅导孩子,孩子不直接对着 AI,默认本机部署时学习资产留在本机。本版本只做小学一至六年级,初中、高中放在长远规划里。标题里把"孝"换成了"笑"——孩子笑得出来,比什么都孝。这篇要较真的,是"只差一个 AI"这个等式——中间到底隔着什么:先回答"AI 凭什么会教"——一组循证教学 Skill(从维果茨基到波利亚,每一招都有教育学出处),加一套"对错归 Go、话术归 Markdown"的双层架构;再拆三个跟"教育"没关系、跟"工程"全有关系的信任问题。


本文核心判断
让 AI 进辅导场景,真正难的不是"会不会讲题"——大模型早就会讲题了。难的是三个信任问题:
① 讲错了怎么办——验算证据链,把程序执行证据与模型判断分开记录;
② 数据放哪——保存位置随本机 / 云端服务而定,云模型调用按用途与数据类检查;
③ 平台会不会被场景腐蚀——K12 按编译期可移除的场景包组织,以移除后平台仍能独立工作为架构判据。
30 秒版:
桌面版 v0.5.0-beta.3 把作业辅导做成河蟹的第一个场景包。一张照片先保存来源,再逐题识别:空白题给家长答案和讲法,已答题独立判定。能精确求值的走当前服务内的确定性程序;可靠题目资产可复用已验证解法;其余进入 Solver + Verifier。计算型验证由应用执行模型生成的程序,终值、过程、家长讲法分别有证据。错题按 1 / 3 / 7 / 14 天阶梯复习,学习档案可导出;持久任务和分项投递回执把“算出来”接到“家长收到”。后端六缝、Manifest v2 与桌面注册槽负责装配。数据在哪保存、模型在哪里运行,是两个要分别回答的问题。
当前 K12 辅导闭环:本次来源识别、空白与已答分流、真实终稿、错题复习及作答回传
图 1|从一张作业照片到下一次复习。空白题不记错题;已作答的题目类作业交付正文和真实批注原图。题库复用的是解法,本次作答仍须判断。

先把定位说清楚:不是搜题器,是"教家长怎么教"

产品形态一句话:家长在模板库点一下「作业辅导助手」,填写孩子称呼、年级学期,以及数学教材版本,生成这个孩子专属的辅导助手实例。本版覆盖小学六科,当前可配置的教材版本与进度集中在数学;多孩家庭建多个实例,各自保存错题、学情与记忆。

之后拍一张作业照片进来,系统逐题回收孩子的手写作答、自动判定学科,再按「这道题写了没」智能分流——空白题直接给家长答案和讲法,已答题回收答案后批改、判错的无感入错题本;接着复习入队,周五投递到期错题,再按需加入练习集,一条闭环。桌面端能用,本版绑定钉钉机器人私聊之后,在手机上发照片也能用;不必先逐题确认一遍 OCR,只有影响结论的来源事实确实不清楚时,才补最少的信息。家庭群、微信、飞书不在本版 K12 接入范围内。

这一版把「拍照」这步做深了。逐题识别同时保存题干、孩子当前有效作答与作答区域;划掉的旧算式不能混进活动答案。分类初判也不是终身判决:初判为空白卷,随后却识别出明确数学作答时,程序会在保存来源事实的同一事务中纠正有效意图,保留原分类回执。空白题给家长答案与讲法,不编“学生答案”,不记错题或掌握证据;已作答的题目类作业定稿保留批注原图。某道题看不清、坐标缺失或越界,就在文字区说明,不猜坐标补红叉;这不等于整张已作答试卷可以省略批注图。

这些取舍不是随手定的——整个设计压着三条第一性原则:

  • 年级正确性——讲解只用孩子学过的知识。年级学期来自辅导档案,本版开放一年级上到六年级下;数学再结合课标参照和已关联教材的当前进度。查表、预检、档案注入是确定性的;"这套解法是否越界"的语义识别仍有模型参与——硬的归硬,软的认软。
  • 信任可见——求解结果保留证据类型,展示验算徽章的入口据此区分;作业批改页以批注原图和逐题结论呈现。宁可说不确定,不可错得自信。
  • 给家长答案和讲法——完整答案、解题过程,以及家长拿来就能讲的步骤:先问什么、孩子卡住怎么接、最后怎么检查理解。苏格拉底式追问是家长教孩子的方法,不是让家长先自己做一遍题的门槛。

辅导里还留着一道情绪守门:遇到"孩子哭了 / 生气 / 烦了"这类信号,给家长的讲法先接住情绪——共情安抚、肯定孩子已经付出的努力、把题拆成更小的步骤;完整答案和备课参考照常给,不让家长卡在等待解锁的流程里。晚上九、十点的辅导现场,很多时候不是题不会讲,是情绪没被接住。


AI 凭什么会教:教学法一半编译在 Go 里,一半写在 Markdown 里

聊信任问题之前,先回答家长会问的第一个问题:AI 凭什么会教?

后端 v0.5.0-beta.1 随场景包出厂 seed 了一组教学 Skill(编译进二进制,首启写入服务所在机器)。基座还是「年级边界约束」与「K12 教学法」,学科老师则收敛到小学六科——数学、语文、英语、科学、信息科技、美术,再配识题批改、阅读、写作、词汇、组卷等专业能力。学科老师叠在基座之上,红线以基座为准,各科只补专业内容。Skill 清单从随包目录生成,由 Manifest 声明;批改与复习的落库动作另由两个引擎工具(k12_grade / k12_review)接进工具循环。

里面写的是循证教学法,不是"你是一个耐心的老师"这种空话。基座 skill 开篇就交代了方法论根基:分步教学的理论骨架是维果茨基「最近发展区」+ 责任逐渐释放模型(I do → We do → You do);全程贯穿罗森希恩《教学的原则》的"小步子推进、每步检查理解、维持高成功率";情绪话术按德韦克「成长型思维」定调——先肯定努力与方法,不评判聪明与否,不比较;复习节奏 = 检索练习 + 间隔重复(这也是后文错题本那套阶梯的理论出处)。skill 原文里有句话把姿态摆得很正:"你不必向家长背这些名词,但你的每个动作都应落在它们上。"当然,这些框架是设计依据,不是对学习效果的实证承诺。

学科层再各自叠加:数学老师以波利亚《怎样解题》四步(理解→计划→执行→回顾)为讲题骨架,巩固走顾泠沅"变式教学"(青浦经验)——换数、换情境、换设问,让孩子在变化中抓不变的模型,而不是背题;给五年级讲方程只说"等式两边同加减"、不说"移项变号"。作文点评给结构、素材和具体修改示范,也可以给家长完整参考稿,但不覆盖孩子原作。美术点评先看得见的画面证据,再给家长肯定、提问和下一次可试的小动作,不编孩子的创作意图。方法论最后要落成家长当下能说的一句话、能带的一步,不是把教育学名词贴在回答上。Skill 中的验收口径仍是目标,不是实测榜单。

但对工程读者,真正值得抄的是双层架构。这个拆分有个诚实的起点:最早的调研文档判断"作业批改 90% 可以通过 Skill 实现"——真做下来发现,剩下那 10%(年级硬边界、验算证据、错题状态、多孩隔离)恰好决定这个产品能不能被信任,而它们全都不该写在 prompt 里。所以教学能力刻意拆成两层:

  • 管"对错"的确定性部分进 Go——课标查表与知识点预检、输入版本与来源绑定、数值比对与状态迁移,全部编译进二进制。这些事模型说了不算,prompt 写得再华丽也改不了一行判定逻辑;识题和讲解里的模型判断,则不能冒充程序保证。
  • 管"怎么说"的进 Markdown——讲解话术、苏格拉底追问的措辞、情绪引导、"超纲说法→学段替代"对照表,运行时注入 system prompt。可读、可改、可版本化,社区可以修改已挂载 Skill 中的话术与教学流程;实际行为仍受辅导助手人设和程序契约约束。

软约束负责像个好老师,硬约束保证不出格——凡是 prompt 管不住的事,就不要指望 prompt。这套"把人的方法论蒸馏成 Skill 文件"的玩法,《前 leader Skill》那篇演示过一次——这回蒸馏的对象,从一个人换成了一门学科的教法。

下面按三个信任问题逐层拆,看看这次发版把这些约束具体落在了哪些代码里。


第一关:AI 讲题讲错了怎么办——验算证据链

先承认一个大模型厂不爱放在首页的事实:LLM 不是计算器。它按 token 概率生成"看起来像答案的数字",多步算术是概率性出错的——而且错的时候语气跟对的时候一样自信。

聊天场景里算错一道题无伤大雅。辅导场景里不行:家长会拿着 AI 的讲解去教孩子——开篇那条二次放大的链(AI 错、家长错教、孩子错记),正是从这一步开始咬合的。

河蟹的解法是把学术界两个被反复验证的思路接成一条工程链:自洽采样(Self-Consistency,Wang et al., ICLR 2023)负责提高解题正确率,程序辅助验证(PAL 一脉,Gao et al., arXiv 2022 / ICML 2023——让语言模型把计算交给解释器执行)负责把"对不对"从模型嘴里夺回来,交给确定性的代码执行。

Solver:先跟自己吵一架

复杂题的解题不只靠一次生成。engine/solve.go 里 Solver 有两种加固姿势:self_consistency(同一方法多次采样、多数表决,上限 maxSelfConsistency = 5,兜住延迟和成本)和 method_diversity(刻意用两种不同解法各解一遍,殊途同归才可信)。

力度由复杂度分流决定:长题、多问和特定推理关键词会提高求解强度,简单算术避免多轮采样。当前还会从复杂度判定副本中剔除教材附录、印刷示例和已选定的单题编号,真实题面仍完整保留,避免排版文字把简单题推成“难题”。更靠前的一条路,是用 Go AST + big.Rat精确处理四则运算、最简整数比、一次方程,以及结构明确的分数、单位用量和部分几何应用题;没有完整识别出结构,就回到模型链。

Verifier:程序交出来,执行由应用负责

设计上,裁决不能只靠模型再“看一眼”。最新代码把可由数值比较器处理的计算型验证收敛为独立节点:Verifier 用新上下文接收题目、完整候选解法和学段约束,只交 Python 程序与逐步审计 JSON;应用再调用 code_exec,读取真实执行回执。这一步省掉了模型决定何时调用工具、再口述执行结果的往返,但没有省掉执行。

computed, executed := receipt.computed(task)switch {case program.Scope == "OUT_OF_SCOPE": verdict = verdictOutOfScopecase process == "INVALID": verdict = verdictDisagreecase executed && answersEqual(computed, input.Candidate): verdict = verdictAgreecase executed && answersDefinitelyDiffer(computed, input.Candidate): verdict = verdictDisagree}

这段来自 engine/solve_verifier_node.go 的实际裁决代码,顺序就是含义:先看学段、再看过程,最后比较数值。模型回复中的“我算过了”不算执行证据;本题输入摘要、运行 ID、成功状态、退出码与 stdout 都要对上。其他题型仍沿原有工具循环,验证子 Agent 只允许使用代码执行工具,不再向下派生。候选解法仍在输入中,因此新上下文不等于信息隔离;题意理解与逐步语义审计也仍有模型参与。

裁决结果是四态,不是二态:

verdictUnverifiable verifyVerdict = iota // 非计算题/无法判定/verifier 不可用verdictAgreeverdictDisagreeverdictOutOfScope // 解法用了 constraint 白名单之外的方法(超纲), // 需学段内重解

第四态 out_of_scope 在这里承接教育场景的约束:哪怕答案是对的,检出解法超纲也不放行——比如给五年级的题上了二元一次方程组。在非直接批改的求解链中,具备明确学段约束与剩余预算时,会尝试按学段内方法重解一次,再复验完整过程;仍不符合约束,就保留超纲状态。判定的参照系包括档案年级 + 课标映射表(内置人教版数学表的知识点 key 主要覆盖四年级上到初一下(同一知识点跨年级出现时,构造器保留最早年级,避免误判超纲);其中初中条目也用于识别超纲,不代表本版开放初中辅导)——约束表是确定的,"方法是否越界"的识别仍由 Verifier 对照白名单做语义判断,是强参照下的模型判断,不是形式化证明。

这套设计最实在的变化,是把“裁判是否真的动过笔”从提示词变成可查事实。模型可以写错程序,来源也可能读错;但没有本题成功执行回执,就不能借一句自述亮出“已程序验算”。后一次执行失败,也不能借前一次的成功回执交差。

计算型验证链:模型生成程序和审计,应用执行,回执与过程共同裁决
图 2|计算型验证的责任边界:模型提交程序与审计,应用执行并读取本题回执。答案一致不能覆盖过程错误或超纲判定;图为代码机制示意。

数值护栏:连"裁判"也不全信

还有一层容易被忽略的防御:代码算对了,模型的口头判定却说反了怎么办?当前数值护栏会归一化可比较的分数、小数、百分数与受支持的数量表达式,再按 answerTol = 1e-6 比对。但“数字相同”有前提:提供了完整解法,就必须明确通过过程审计,才能纠正口头分歧。PROCESS: INVALID 与 OUT_OF_SCOPE 不会被相同终值洗掉。

换成家长听得懂的话:最后碰巧算对,不代表中间每一步都能照着教。批改也区分“答错”和“答案正确、过程有问题”;后者展示具体步骤问题与讲法,不自动记成错题。无法可靠解析或比较的答案保留不确定性,不把字符串不同硬判成算错。

不只验答案,也审家长要讲的那几句话

家长备课指南包含答案、完整步骤、学段方法、易错点、教学顺序、追问和检查方法七个字段。解法与讲法可以同轮生成,但仍只是候选;讲法绑定所选解法摘要,另做逐项审计。一道题的答案正确,不能替错误例题或误导性追问背书。这部分是模型内容审计,不能冒充数值程序证明。

证据诚实:程序验算与模型判断,不能混为一谈

链条的最后一环是把证据强度如实保留下来。领域证据分六类(数值执行 / 符号执行 / 异构模型复核 / 同源启发 / 原词重现 / 无),下面是后端的强信任判定节选。当前展示徽章的入口会区分“已程序验算”和“已核对”;并非每个结果页都展示徽章,也不能仅凭绿色判断证据强度。

func (e SolveEvidence) StrongTrust() bool { switch e.EvidenceType { case EvidenceNumericExec, EvidenceSymbolicExec, EvidenceVerbatim: return true case EvidenceHeterogeneousModel: return e.SolverModel != "" && e.SolverModel != e.VerifierModel default: return false }}

这里藏着这一节最诚实的一句话:河蟹的 Solver 和 Verifier 默认是同一个底座模型。独立性来自 fresh context 和程序执行接地,不来自模型异构。所以"两个同源模型互相点头"只能算模型复核依据,不等于程序验算。而 程序执行证据也有一处必须说清:复杂题由运行时核对本题输入摘要、沙箱运行 ID、成功状态和实际输出(CodeExecutionReceipt 携带 InputDigest 与 RunID),回执与本题对上,才有程序验算依据;确定性快路则直接来自程序求值。原工具循环可在预算内补验;独立计算验证节点缺少有效回执、执行失败或调用结果未知时,保留对应失败或待核实状态,不能包装成已完成验算。可解析的模型判断仍须与程序执行证据分开。遇到无法消解的分歧,结果应保留不确定性。这条"内部审计不外露"的纪律也贯到批改输出:错因文本会先洗掉 verifier 自查过程里的勾叉核对与"是否…"自问行,只把一句人话错因交给家长——验算的草稿纸不进用户界面。

同源,但不等于"随便哪个模型都行"。这里有一处容易被忽略的选路:当前一份 K12 持久批改任务会冻结创建时选定的、同时支持图片与文本的模型,识题、解题和验算都沿这条路走,不在半路偷偷换人。通用 solve 只有在未显式指定 provider/model 时,才使用单独配置的推理模型。会看图不等于一定会批改,实际选用模型的能力仍是整条链的前提。

把"对不对"从模型的语气里剥出来,变成一条可查的证据链——这是辅导场景和聊天场景在工程上的分水岭。

从聊天请求变成持久任务:恢复不是再跑一遍

一张作业照片会穿过识题、解题、验算、定稿与投递。当前先保存原图引用、来源摘要、模型路由、请求摘要与阶段预算,物理调用区分 prepared、sent、succeeded、outcome_unknown。恢复时复用已经完成的单元,只从明确安全的检查点接续;发出请求后结果未知,要先核对原请求,不能盲目重发。页面状态与服务任务分开,关闭页面也不等于取消已经持久化的任务。

题库能省哪一步,不能省哪一步

这里容易混淆三件事:教材接地、跨任务题目资产、同一任务内复用。verified_text 指已核验教材正文直读,冻结教材、页码和源摘要,回答“这段依据从哪来”;题目资产按可靠题干、学段等事实匹配,保存版本与采用回执,复用已经验证的解法;同次任务已验过的解法再交给 grader 比对学生作答,避免从头重算。命中答案资产,仍不能跳过本次图片识别、学生答案和步骤判断;更不能用后来命中的资产覆盖此前未决调用的账本。

可信不是免费的。普通 solve 默认有 5 分钟墙钟护栏;K12 持久批改使用任务冻结的阶段预算。按 2026-10-03 的本机安装版报告,一张当前杂乱数学作业的完整钉钉链路仍用时 285.507 秒,其中识题复核 248.214 秒,120 秒目标尚未达到。该次使用 gpt-6-sol,16 题完整处理,正文与批注 PNG 均实际收到;这只能证明对应样本、模型与安装版,不能外推为准确率榜单或所有模型性能。它也提醒我们:省掉求解调用,不代表最慢的识题阶段一起消失。


第二关(上):错题记在哪——记录原语 + 艾宾浩斯式复习引擎

批改判错的题会自动落进错题本。最初,它落在一个领域中性的记录原语 agent_records 上(storage migration V8);随着错题和积累的业务约束变得明确,当前改由 k12_mistakes、k12_accumulations 等类型化表承接(V9),场景安装台账落到 V10,去重键回填在 V11。通用的是记录契约,领域字段则有自己的位置。

记录契约仍用 collection 区分记录集;类型化表保留 status(状态机)、due_at(到期调度)、version(乐观锁)等通用列,题干、作答、错因等落到明确的领域列。三个细节值得抄:

  • 幂等去重由数据库约束与同题匹配共同承接——各类型化表上设 UNIQUE(agent_name, dedupe_key),其中错题键由来源会话与规范化题干生成(吸收空白、大小写级的 OCR 微差);积累本则按学科、类型与规范化内容去重。批改还会匹配当前孩子已有的唯一精确错题来源,命中则更新复习结果——数据库键本身不承诺跨会话全局唯一。
  • 隔离键是不可变主键——挂在 agents(name) 外键上(改名改的是 display_name),多孩多实例的隔离由 schema 保证,删实例级联清数据。
  • 通用性有第二个化身作证——语文/英语的「积累本」(默写错词、好词好句、古诗)是同一记录契约上的另一个 collection,由积累表持久化。

错题的来源也不限于 App 内批改。家长遇到一道没经过拍照批改的题——老师口头讲评的、课外册子上的、孩子自己说没弄懂的——可以直接「记一条错题」轻量入库:题目和孩子作答存进去,错因留空时由一次轻量 reasoning 归纳成一句话,同样进复习队列排期。这条路径刻意不跑那套对抗验算链——它不是要判对错,只是把一条你已经知道错了的题记下来,不为了记账付一次全链路的延迟。

容量口径也交代一句:最早的调研文档按单机一学年约 450 条记录估算,这是单用户桌面的设计量级,不是容量实测上限。这个前提一旦走向家庭云同步或多用户,分页、增量同步和冲突模型就得一起重做,单机方案不能直接横向放大。

复习引擎:四级阶梯,越做对推得越远

错题有五种状态:new / explained / retried / mastered / archived。通常从新错题、已讲解、已复练走向已掌握;抽查未过会回到复习,归档也支持恢复。驱动状态往前走的是复习引擎,核心就这么几行:

// reviewIntervalLadder 艾宾浩斯式间隔阶梯(秒):// 卡片每成功重做一次,下次复习推得更远var reviewIntervalLadder = []int64{ 1 * 86400, 3 * 86400, 7 * 86400, 14 * 86400,}// MasteryGapInterval 掌握判定的最小间隔(秒):// 第二次做对距上次 ≥ 此值 → masteredconst MasteryGapInterval int64 = 3 * 86400 // 3 天

规则用人话说:错题入库第 1 天到期;首次做对,推到 3 天后。当前系统复批判对时,若记录已处于 retried、保留了上次做对时间,且间隔 ≥3 天,就可转为“已掌握”、移出队列。这是基于状态与时间间隔的固定规则,不等于严格检查“连续两次正确”;未达掌握条件时,复习阶梯按 1 / 3 / 7 / 14 天推进,14 天封顶。未掌握题不因久未练习自动隐藏。生成同知识点变式题时,相似题本身也要过一遍验算链才进入可打印的练习集。

熟悉 Anki / SuperMemo 的读者会认出这是间隔重复(spaced repetition)的骨架。诚实地说:1/3/7/14 这组数值是产品预设的固定阶梯——借鉴间隔重复研究的量级,不是艾宾浩斯实验的原始数据,也没有 SM-2 那套动态难度因子。对错题本这个量级(一个孩子一学期几十上百道错题),固定阶梯的复杂度收益比是合适的,先把闭环跑通。

这套引擎在家长侧收敛成几个零负担的动作:「本周复习」把到期错题排进复习;加入「练习集」后,打印或发送给家长,孩子做完拍照回传,系统复批并更新排期。家长也可以手动记结果,用于推进复习,但系统掌握仍以实际作答的验证为据。发现原作业识别错了,可以纠正来源、重新批改;纠正走不可变纠正链(每次纠正追加历史终稿而非覆盖),若它唯一产生的错题记录已不成立,系统自动撤回并保留审计,来源纠错不算孩子复练。学习档案可导出 PDF、Word(.docx)和 Markdown,PDF 走平台 render 服务的 pandoc + typst 排版;练习集分别提供题目卷、答案卷的打印与 PDF 保存。

还有一个学科纵深的彩蛋:语文默写、英语单词这类练习走 verbatim(原词重现)证据,参考答案直接取自积累原文。积累可生成默写或补空题并加入练习集,做完支持拍照回传复批。这里要分清两件事:出题时答案忠实于原文,不等于已经验证孩子写对;学生作答另走批改链。证据类型体系是按学科可扩展的,不是数学专用。


家长读者可以跳了:下面两节(出口网关、场景包架构)写给工程读者。只关心"怎么用起来"的家长,直接跳到文末「诚实写一下:这个版本的边界」,再去配套的《「父慈子笑」上手篇》跟着截图操作。

第二关(下):孩子的数据出不出网——fail-closed 出口网关

先把产品口径亮出来:数据保存在哪里,和模型在哪里运行,是两件事。默认本机服务把错题、学情和档案存入本机 SQLite,原图、教材与备份也在本机;切到云端服务后,会话、学习记录、模型与钉钉连接由该服务管理,切换不会自动迁移两端数据。再看模型与消息的出网路径:

  • 模型调用——取决于当前服务配置的 Provider。只有本机服务配本地模型的对应调用留在这台电脑;云端服务上的“本地模型”也运行在云端服务所在机器。
  • 敏感数据类(孩子档案、作业照片、错题记录、学情记忆)——云模型调用按用途和数据类检查,白名单允许识图、附图聊天及解题验算所需的数据;本地保存不等于模型调用不出网,实际发送范围取决于调用用途和所选模型。
  • IM 投递——把复习卷、提醒发到绑定的钉钉私聊,内容就到了 IM 服务商那里。这是用户主动开启的第三方通道,开关在用户手里,不归出口网关管。

还有一句要说破:这套口径管的是河蟹这一侧放不放行。数据一旦合法出网——发给云端视觉/大模型或 IM 服务商——对方侧的留存与使用遵其服务条款,河蟹管不到,也不假装管得到。

当前 egress 包定义 9 种用途(Purpose)与 6 种数据类(DataClass)。下面是固定用途表:识图、主动附图聊天、解题验算与画像维护各自允许必要的数据类。还有一个不能漏写的分支:当前聊天实际开启记忆时,general_chat / vision_chat 允许携带 memory;这个开关只属于本次请求,新的子请求不会继承。不能据一张白名单表就宣传“学情记忆永不出网”。

// sensitiveClasses 默认不得出本机的数据类。var sensitiveClasses = map[DataClass]bool{ ClassSensitiveProfile: true, ClassSensitiveMedia: true, ClassRecord: true, ClassMemory: true,}// purposeAllowsSensitive 白名单:允许携敏感数据出网的用途。var purposeAllowsSensitive = map[Purpose]map[DataClass]bool{ PurposeMemoryProfile: {ClassMemory: true}, PurposeVisionOCR: {ClassSensitiveMedia: true}, PurposeVisionChat: {ClassSensitiveMedia: true}, // 用户主动附图 PurposeSolveVerify: {ClassSensitiveProfile: true, ClassSensitiveMedia: true},}

在 K12 的调用点,这四个数据类分别对应孩子档案、作业照片、错题/积累记录、学情记忆——但注意,这个映射只存在于调用点,egress 包本身一个领域词都没有(为什么这很重要,第三关讲)。

关键性质是 fail-closed:每个要出网的请求必须先在 context 里声明"我是什么用途、带了哪些数据类"(请求信封不继承父级声明,防止用途漂移);到了网关,没带信封的、数据类为空的,一律直接拦截。忘了声明不是漏网,是断网。这个 Guard 接在真实调用点上——vision 识别、聊天链路、embedding、cron 编译,都从这道门过。定性也要说准:它守的是云模型方向的调用链,不是操作系统级的全局网络出口;数据类由调用点标注,不是自动内容识别。

这道网关的价值,是把当前调用的用途和数据类显式化,并让判定可追查。它不是“数据永不出网”的证明,也不能替代对实际服务位置、已启用记忆和第三方投递的说明。
本机与云端服务的数据保存位置、模型出网和独立 IM 投递通道
图 3|保存位置、模型调用与 IM 投递分别看。显式启用的当前聊天记忆可以随云模型请求发送;投递的正文与附件会进入对应 IM 服务商。

第三关(写给架构师):加一个 K12,平台会不会烂掉

这是这次发版在架构上下注最重的地方,也是对"通用平台想做垂直场景"的团队最有参考价值的一段。

风险是真实发生过的:更早的一轮探索里,"错题/复习/备考"这类领域词一度散进了引擎、API、适配器层的 多个平台文件——每加一个场景就往内核里长一批 if,三个场景之后平台就没法看了。后端 v0.5.0-beta.1 围绕这条边界清债,把答案收敛成一个机制:场景包(Scenario Pack)+ 六个注入缝。

打个不那么技术的比方:把平台想成一个会展中心——它自己从不办活动,却预留好一整套标准接口:电源、承重吊点、网络、舞台灯光线、检票闸机。今天办婚礼、明天办发布会,场地一根线都不用重铺,主办方只管带自己的鲜花、桌牌、流程单往接口上挂。K12 就是第一场住进来的活动;而河蟹这套"接口",摊开就是下面这张 scenario.Registry:

// scenario.Registry —— 六个资源注入缝(节选)type Registry struct { Records *records.RecordSchemaRegistry // 记录集 schema Constraints *ConstraintRegistry // 约束域(key 如 "math") Views *ViewExtensionRegistry // 视图描述符 Modes *ModeFeatureRegistry // 模式路由关键词 Buttons *ButtonRegistry // 交互按钮 Evals *EvalSuiteRegistry // eval 套件}

错题本 schema、年级白名单、场景按钮,分别从 Records / Constraints / Buttons 缝注入。现在六缝外面还包了一层 Manifest v2:统一声明场景版本、能力、挂载点、依赖和定时工作流;Install 校验后注册资源,返回安装回执,卸载按回执清理本场景资源、保留业务数据。会展中心不只留好接口,也有了进撤场清单。规则只有三条,但每条都可证伪——这比“分层规范”之类的口头架构硬得多:

  • 装配纯声明式——Assemble(*Pack) 只做注册,源码注释里写明铁律:不出现 if pack == k12 这样的业务分支。
  • 领域规则集中、组合根接线——错题、复习与学情等业务规则主要留在 scenarios/k12/,组合根负责接线;当前 cmd/hexclaw/ 下有多个 K12 适配文件。K12 路由通过通用的 srv.Mount("/api/k12", …) 挂载——通用路由层只负责挂载 handler。不过引擎当前仍包含家长讲法输出与审计协议,不能把分层目标写成“所有领域语义都已清零”。
  • 以可移除为验收判据——移除 scenarios/k12 及组合根中对应的装配与适配代码后,平台仍能独立编译运行,是这套架构的验收判据,期望的是:活动一撤场,场地立刻恢复毛坯,明天照样办别的。反过来,用空平台挂满六缝的 mock 包检查是否需要改内核(相当于办一场“什么都不办”的空场彩排,专门验证接口都还在)。这就是内部说的 AP-1 双向门。准确说,这是源码级、编译期的装配边界,不是面向用户的热插拔插件市场。

桌面端是同构的镜像:src/contracts/(契约层,顶层 barrel 严格执行领域无关红线)+ src/shell/(领域无关的通用壳)+ src/features/k12/(场景包)。前端的 scenarioRegistry 提供 14 个领域无关的注册槽——视图描述符、记录集 Schema、身份投影、会话增强组件、智能体卡扩展与徽章、建档模板、Webhook 管理、全局氛围展示、外观设置、定时任务展示等。聊天壳用 Vue 动态组件按视图描述符渲染场景增强——头部 tab、消息徽章、记录集视图都由场景包注册声明(前端静态注册为主,后端 GET /api/k12/view-descriptor 动态下发的目前主要是 composer 快捷入口),场景主要通过 <component :is> 等扩展机制装配。不过当前 ChatView 仍直接保留 K12 图片附件的身份与读取适配,注册槽已经建立,残留耦合也真实存在;源码移除边界仍须结合实际构建验收。玩过 VS Code 插件体系的读者会觉得眼熟:这就是 contribution points 的思路——平台定义缝,场景声明填充物,运行时只做装配。

Manifest v2、后端六个资源注入缝与桌面注册扩展的装配关系
图 4|Manifest 与六缝负责声明和装配,桌面通过 contracts / shell 接入场景扩展。这是源码级边界,不是热插拔市场;注册机制存在,也不等于当前每处依赖都已消除。

为什么值得下这么重的注?因为 K12 是灯塔,不是终点。同一套六缝,下一个场景包无论是健身教练还是法律助理,平台侧需要改动的代码量应该趋近于零——这是"通用平台 + 垂直场景"这条路能不能走远的判据。

"改动趋近于零"是句抽象话,把这六缝摊开、换一个和 K12 八竿子打不着的领域填一遍,灵活性才有体感。下面拿一个法律咨询助手做示意——是设想,不是已发布的场景包——同样六个缝,填的东西全变:

注入缝 K12 家长助手(已落地) 法律咨询助手(设想)
Records
记录集
错题本:题目 / 错因 / 知识点 / 复习态 案件档:诉求 / 事实 / 证据清单
Constraints
约束域
年级课标边界——不给五年级讲二元一次方程组 诉讼时效 / 管辖核对——缺事实先提示,不出"再拖拖"的馊主意
Views
视图
错题本 tab、批改 ✓/✗ 徽章 案件卡 tab、争议焦点标注
Modes
模式路由
"拍照批改" / "复习"关键词 "起草文书" / "风险评估"关键词
Buttons
交互
加入练习集、打印 一键生成起诉状草稿
Evals
套件
批改判定、过程质量与自信错答评估目标 法条引用准确率、时效判断门槛

同一张 scenario.Registry,如果既有契约足以承载右边这一列,就只需在 scenarios/law/ 里声明资源、再在组合根注册。这就是"通用底座长出垂直产品"在代码上的确切含义:通用机制(解题验算、记录契约、出口网关、cron、eval)是平台的,领域(错题还是案由、年级还是时效)是场景包的。

代价也要写清楚:六缝是一层真实的间接——descriptor 协议要维护、mock 场景包要陪着测试走;而且在第二个场景包出现之前,它的普适性仍是有待真实场景继续验证的假设。这笔投资赌的是"场景数 ≥ 2",赌错了它就是过度设计。这也是把 AP-1 门做成可证伪的原因之一:赌错了,至少能立刻知道。

放到行业坐标里,这套机制补的是第三层:MCP 回答"Agent 能调用什么"(工具与数据源的统一协议),Agent Skills 回答"应该怎么做"(流程知识的可移植打包),而场景包想回答"怎样成为一个领域产品"——除了工具和 prompt,还要一起装配 UI、业务状态、硬约束、证据模型、自动化和 eval。这一层是河蟹自己的工程抽象,不是行业标准;但它解释了为什么"装几个 K12 Skill"不等于"做出一个家长辅导产品"。


加一味自动化:到期错题在周五晚上七点来

错题攒下了,复习队列排好了,最后一公里是"家长根本想不起来打开 App"。后端 v0.5.0-beta.1 在创建辅导实例时自动生成四个默认 cron 任务:

任务 cron 表达式 投递物
错题卷(每周五) 0 19 * * 5 到期错题整理成的 Markdown 内容
回传提醒(每天) 0 20 * * * 前一天已打印或发送、尚未完成回传的卷子,每卷提醒一次
学期确认(3/1) 0 9 1 3 * 确认是否切换学期,确认后更新档案
学期确认(9/1) 0 9 1 9 * 确认是否切换学期,确认后更新档案

投递机制延续了“赛博牛马”那篇讲过的 Starlark 沙箱脚本,但这次的脚本零 LLM 参与:定时用 http_get 访问当前服务的 K12 投递端点,有内容才发到绑定的钉钉私聊或桌面,空内容静默跳过——没有到期错题的那个周五,家长不会收到一条“本周暂无错题哦~”的废话推送。任务幂等键是“实例名/任务类型”,多孩家庭各自的提醒互不挤占。周五任务整理、投递到期错题;练习集由家长按需添加。使用边界也交代一句:本机 cron 依赖电脑和河蟹服务在线;云端服务模式依赖服务器在线,电脑关机不等于云端任务停止。

手机这最后一公里也要讲证据。点一次「发送到手机」,由系统投递到该辅导助手当前有效的绑定目标;正文、批注图或其他附件分别记录状态。平台受理不等于家长已收到,必要组成部分送达,才是整批送达。结果未知时先查询原批次;明确失败时只恢复失败子项。

图片任务的自动回传也遵循产物完整性要求:空白卷发送答案与讲法,已作答的题目类作业发送正文和实际批注图,不能用“请求成功”掩盖图片缺失;作文和美术作品发送点评正文。

辅导内容也讲同样的来源纪律:教材依据、AI 归纳、本地记录、程序验算与学情信号分别标识。作文和美术点评从可见证据出发,给出家长可以怎样肯定、怎样问、下一次只试哪一点。来源不明的内容,不伪装成教材权威。


诚实写一下:这个版本的边界

照系列惯例,把已实现机制和仍然存在的边界分开摆出来:

  • 课标参照与教材进度还不是一回事。内置课标表以人教版数学为主;当前支持关联数学教材并设置单元、课时或页码进度。其他小学学科按档案与各科教学 Skill 辅导,尚无同等的教材进度配置。
  • 原图批改仍依赖来源与定位质量——已作答的题目类作业必须交付真实批注原图;无法可靠定位的题保留文字说明,不猜坐标。空白卷按自身语义只给讲解,不制造批注图。
  • 程序验算已经保留过程与超纲否决,但不等于形式化证明——真实回执能证明程序运行与数值结果;题意理解、模型生成的程序是否忠于题意、逐步语义审计和教学效果仍有各自边界。
  • 识题与复批仍依赖来源质量——原图模糊、题干缺失或手写作答不清,会影响结论;纠正来源后重新批改,按新输入更新结果,来源纠错不累计为孩子复练。
  • Solver 和 Verifier 默认同源。异构模型互查的缝留好了(证据类型里有 heterogeneous_model,且要求 solver ≠ verifier 才算强证据),但当前同源模型相互认同本身不构成程序执行证据。这也是徽章文案不敢写"双重校验"的原因。
  • 批改准确率和速度依赖所选模型。本地、云端 Provider 均可配置,图片任务需实际选用支持读图的模型;模型、机器和作业类型不同,效果也不同。云端调用费用以服务商规则为准。
  • 复习引擎是固定阶梯,不是 SM-2 那种带难度因子的自适应算法;对错题本的量级够用,但"简化版"三个字要写明。
  • 持续运行依赖当前服务在线——本机模式要求电脑和服务运行;云端模式要求服务器与相关模型可用。两端数据各自保存,切换不会自动迁移。
  • 完整链路仍有性能瓶颈——120 秒目标尚未达到,既有成功样本也不代表全部模型、图片质量或部署环境均通过。

这次发版不止做了 K12

K12 是这版的主角,但为了让它站住,平台层同步动了几件事,对非家长用户同样有效:

  • 🛡️ 执行原语收敛——代码/命令执行以 code_exec 沙箱为主入口,集中提供超时、资源与执行回执管理;旧 code / shell 已标记弃用,但仍保留配置开启的兼容入口,尚未从源码中完全移除
  • 🧩 场景注入基建——六缝 Registry、引擎侧的场景注入与回复元数据通道、通用 Mount 钩子,都是平台级机制,第二个场景包直接复用
  • 📦 通用记录本 + 出口网关——records 契约和 egress 策略不姓 K12:结构化记录与受控云模型调用复用通用机制,K12 类型化表留在场景数据层
  • 🖥️ 桌面场景扩展三件套——contracts / shell / features 的前端分层 + 后端下发视图描述符,由场景注册自己的界面扩展
  • 📚 备份与导出保留真实资产——.hexbak 用 SHA-256 校验完整性;学习档案覆盖本周复习、错题、练习集、积累、作品。PDF / Word 直接保存服务端冻结的产物;Markdown 带附件时打成 ZIP,包含 archive.md、manifest.json 和原件。练习集另有题目卷、答案卷的打印与 PDF 保存。
  • 📖 教材接地与题目资产复用——教材正文直读回答“依据在哪”;合格题目资产复用回答“哪些解法不必重算”。二者都不代替对本次学生作答的识别与判断,也不承诺整页秒级。
  • 🌐 本机 / 云端双形态——后端可以随桌面同机运行,也可以接入云端单实例服务。钉钉辅导跟随绑定的服务;数据位置与可用时间由对应部署决定。

最后放三个往前看的判断:验算链的下一格是真异构 Verifier——缝已经留好(solver ≠ verifier 模型才算强证据),差的是把第二个模型的选路与证据生产真正接起来;课标表会沿 Constraints 缝按教材版本扩展(北师大版、苏教版);而六缝架构,要等第二个场景包来完成它的证伪测试——它站住了,开篇那一问才算真正有了答案:通用平台能长出一个可信的垂直场景,还不把自己搞脏,"通用平台 + 垂直场景"这条路也才算真正走通。领域会换,纪律不换:把概率智能和确定性约束分开;把结果和证据分开;把事实、画像与文档分开;把场景语义留在场景包;让每次对话最终变成可执行的下一步,而不是沉进聊天历史。


拍照搜题给孩子一个答案;家长要的,是把这道题教会的那二十分钟。
从「鸡飞狗跳」到「父慈子笑」,靠的不是 AI 更会说——
是讲对了有证据,讲错了敢承认,错题有来处,复习接得上。


这篇讲的是"它凭什么可信";"怎么上手用起来"(下载、建档、拍第一张作业照),在配套的《「父慈子笑」上手篇》里,跟着截图一步步展开。

延伸阅读——河蟹系列前作:

  1. "赛博牛马"养成记 — 一句话编译成会自愈的沙箱脚本(本篇 cron 投递的底座)
  2. 前 leader Skill — 把方法论蒸馏成 Skill 文件(本篇教学 Skill 的同款玩法)
  3. Loop Engineering — 从 Prompt 到 Loop 的四代范式纲领
  4. 会"做梦"的 Agent 记忆 — 学情画像背后的记忆系统
  5. v0.4.0 维语本地化 — 维语本地化的工程笔记
  6. 架构详解 — 一个人 × Claude Code × 6 仓生态

参考资料

源码索引:engine/solve_verifier_node.go(独立验证)、engine/solve.go(过程否决)、scenarios/k12/usecase/grading_problem_assets.go(题目资产)、egress/egress.go(出网判定);桌面 src/shell/scenario/registry.ts(14 槽)、ChatView.vue(资产适配)、features/k12/export.ts(导出)。