目录
7.1 Reflexion 与 Self-Refine:反馈被使用,需要明确的循环
摘要:前四篇讨论交付物验收,这一篇继续追问验收之后怎样指导修改。本文沿着 AI Tutor Engine 的评审卡、会话历史和模型请求,定位默认操作路径未自动传递详细评审意见的问题,同时保留首次完整历史初始化和主动粘贴等可达路径作为对照。文章结合 Reflexion、Self-Refine、CRITIC 和 Agent 记忆管理工作,提出按提交与评审尝试顺序选取反馈、限制上下文预算、更新过期意见的契约,并用独立参考实现和请求级适配测试验证规则。本文未声称已完成生产修复或真实模型效果评估。
1. 评审结束以后,任务还没有结束
上一篇文章拆解了 Evidence Pipeline:系统采集证据,对照 Rubric 进行预检、CI 直判和语义评审,再由代码聚合结果。这条链路回答了当前提交是否达到验收要求。
先前一直没有仔细说明,在这里简单介绍:AI Tutor(信科院智能助手)是项目制课程Ai导师教学引擎,但对于一个系统,学生还会继续问:“ 刚才没通过,接下来应该怎么改?” 这要求下一轮导师知道评的是哪一次提交,哪条标准没有通过,依据是什么,需要修改成果还是补充证据。如果导师没有拿到这些信息,即使验收模块工作正常,后续指导也可能重新询问已经检查过的内容,或者给出和评审意见没有关联的建议。
因此,这一篇检查一个具体问题:评审模块生成的意见,经过哪些路径,才能成为下一轮导师实际收到的信息?
本文基于 2026 年 10 月 17 日的本地代码副本;10 月 20 日复核时,相关主链路文件与副本一致。原有路径验证运行了真实接口、数据库读写和请求组装,模型服务使用固定响应替代。本文另附独立参考实现;参考实现、原项目和临时适配测试会分别说明,均未调用真实模型或线上服务。
本文所说的默认操作路径,具体指学生已有指导会话,点击提交验收,再在同一任务中继续指导,且没有手动粘贴完整评审意见。检查发现的是这条路径缺少自动反馈装入,不能据此推断整个系统完全没有传递渠道,也没有线上样本支持估算受影响用户的比例。
2. 页面上有评审卡,下一轮模型却未必收到意见
当前前端有两个需要区分的集合:
chat.value:组织页面消息,能够保存评审卡、图片等结构化内容。history.value:组织发送给指导接口的文本历史。
它们服务于不同用途,内容也不自动同步。
doReview() 收到结果以后,会把评审卡加入页面列表。下面是原实现的相关片段,省略了状态更新和通过后的处理:
chat.value.push({
role: 'assistant',
payload: {
message: `评审完成:${
j.data.evaluation.status}(${
j.data.score} 分)`
},
review: j.data
});
页面通过 review.evaluation.criteria 展示逐条结果,通过 review.evaluation.next_step 展示下一步建议。但这个分支没有把这些内容追加到 history.value。后续指导请求携带的则是:
history: history.value.slice(-20)
未通过时出现的 “切回指导、修改后重新提交” 提示,也由 pushSystem() 写进页面列表。这个函数明确只负责展示,不写入模型历史,这样做可以避免把界面状态条混进对话,但评审反馈还需要一条专门的传递路径。否则,用户在同一个页面里看见了意见,模型却可能没有收到这些意见。
这里的问题已经发生在请求构造阶段,还没有涉及模型是否擅长理解长上下文。

图 1 默认路径中,页面、文本历史和指导上下文之间的三处断裂
3. 刷新页面后,评审卡恢复了什么?
接下来检查恢复逻辑。
restoreChat() 优先读取服务端会话,再使用本地缓存补充评审卡等富内容。这可以让页面重新显示评审结果。随后,它会根据页面消息重建文本历史。对于 assistant 消息,取的是:
{
role: 'assistant', content: m.payload?.message || '' }
评审卡的 payload.message 是状态和分数摘要,比如:
评审完成:NEED_REVIEW(0 分)
具体未通过项、判定理由和 next_step 保存在 review 对象里,没有在这一步被展开,因此,当前恢复逻辑可以同时出现两种结果:页面仍能展示完整评审卡;重建出的模型历史只有评审摘要。
知道刚才未通过 和 知道为什么未通过,能够支持的后续指导显然不同。
4. 只补前端历史,还不能解决已有会话的问题
继续往后端追踪,还能看到第二个边界,指导接口采用服务端会话作为历史真相源。它按会话和任务定位记录,只有没有对应会话时,才使用请求中的 history 初始化:
skey = f"{sid}:{req.task_id}"
sess = db.get_session(skey)
if not sess or sess.task_id != req.task_id:
sess = AISession(...)
if req.history:
sess.history = list(req.history)
这意味着,即使前端把完整评审意见加入请求,只要服务端已经存在该会话,接口也不会因此合并这份新历史。
服务端优先的策略本身有作用:它避免客户端历史反复覆盖已经保存的对话。不过,评审链和指导链必须另外约定怎样共享反馈。当前评审链确实会持久化结果。save_evaluation() 将序列化后的 evaluation 写入 evaluations.criteria_json,其中包含逐条标准和下一步建议。
但在已核对的指导主链路中,build_context() 组装的是课程、任务目标、步骤、验收标准和教学材料;接口还会加入代码证据、调试状态和会话历史,没有读取最近一次完整评审反馈。
已有数据保存下来,也有明确的读取方。但上述默认操作路径没有主动读取详细评审结果。首次初始化历史或主动粘贴仍可能带入反馈,下一节专门把它们作为正向对照。
5. 怎样验证这件事,而不是靠模型回答猜测?
直接询问模型 “你记得刚才哪里没通过吗” ,会混入另一个因素:
模型可能根据课程标准或学生话语推测原因。
这次验证采用更窄的方法:在构造的评审响应里放入一个唯一标记,再检查它是否出现在下一次发往模型服务的 messages 中。
测试复制了引擎源码,使用独立 SQLite 数据库;通过真实 FastAPI 接口执行评审和指导,保留了原有的模型客户端、JSON 校验、历史裁剪和请求序列化流程。模型 HTTP 请求由本地 MockTransport 截获并返回固定响应,外部连接被阻断。
前端部分则提取并执行原有 doReview() 和 restoreChat() 函数,只移除了执行所需的 TypeScript 类型标注,并替代页面状态、存储和请求依赖。这里没有挂载浏览器界面。

图 2 默认路径与显式携带意见的正向对照采用相同的标记检查方法
原实现共完成 11 个后端及历史裁剪场景、3 个前端函数场景。这里不是随机抽样或用户实验,每一行都是预设条件下的确定性检查;首次完整历史场景是主动构造的正向对照,并不意味着当前前端默认会发送这些细节。后文参考实现的新增测试另行统计,不能混成同一个成功率。
| 场景及构造条件 | 观察结果 |
|---|---|
| 评审完成并落库 | 完整反馈存在于评审结果中,原会话历史未改变 |
| 已有会话继续指导 | 模型请求中没有评审反馈标记 |
| 已有会话,请求 history 主动附带完整意见 | 仍使用服务端历史,没有合并反馈 |
| 首次创建会话,history 明确包含完整意见 | 标记能够进入模型请求 |
| 首次创建会话,history 只有评审摘要 | 具体意见没有恢复 |
| 同一学生换一个新会话继续指导 | 没有自动读取该学生最近的评审意见 |
| 学生在当前消息主动粘贴意见 | 标记能够进入模型请求 |
| 前端恢复页面消息 | 评审卡保留细节,文本历史只有状态和分数摘要 |
这些结果支持的结论是:
在“已有会话 → 验收 → 同任务继续指导”的默认路径中,详细评审意见没有自动装入下一次模型请求;系统仍有显式初始化和主动粘贴等可达路径。
它们不支持 任何场景都不会传入评审意见。显式初始化完整历史、学生主动粘贴,都是已经验证过的例外。
它们也没有证明真实模型一定会指导错误,更不能给出用户受影响频率或缺口造成的平均损失。本次唯一标记用于定位记录传递,不用于判断语义理解:反馈可能被有效转述而不保留原标记,也可能标记存在却没有被正确使用。这里结合固定输出、源码路径和序列化请求确认字段是否漏传;实际指导效果需要另行评估。
测试覆盖可以分成三层:前端函数和后端请求分别已检查;整个浏览器从点击验收到下一次发送的完整操作仍未验证;真实模型是否据此正确指导也未验证。下面不把三者相互替代。
6. 把意见追加到聊天里,仍然有有效期问题
一种局部修补方式,是把评审意见转成聊天消息;但必须写入服务端实际使用的历史,或通过无会话时的初始化入口,单改前端数组不能修复已有会话。即使接通,当前链路还有历史窗口:前端发送最近 20 条消息;后端组装请求时取服务端历史的最近 12 条消息;模型客户端再按 HISTORY_CHAR_LIMIT = 6000 裁剪非 system 消息。
这里的 12 指消息条目,不能直接写成 12 轮完整问答;6000 也是历史字符预算,不能理解成整个请求严格不超过 6000 字符。当前裁剪实现会保留使累计长度首次超过预算的那条消息,system 消息另行保留。
测试还构造了两种情况:反馈消息落到最近 12 条之外;较新的长消息使旧反馈在客户端裁剪时被移除。两种情况下,标记都没有进入最终保留的历史。
更重要的是,意见有时会过期。学生已经再次提交并通过,上一版仍缺少运行证据的意见就不能继续作为当前阻断项。
所以需要定义的,不只是反馈怎么进入上下文,还包括它对应哪次提交、什么时候更新,以及什么时候退出上下文。
7. 与反馈学习、迭代改写和 Agent 记忆工作的关系
把验收结果送回下一轮推理,并不是一个新的研究方向。本文需要说明的是:这些方法对反馈的使用有哪些前提,而当前教学系统在哪个环节尚未满足这些前提。
| 工作 | 反馈怎样参与后续尝试 | 本文借鉴的设计问题 | 当前系统与它的区别 |
|---|---|---|---|
| Reflexion | 将任务反馈转成语言化反思,保存在情节记忆中供后续尝试使用 | 什么信息应被保留并带入下一次尝试 | 当前保存的是评审记录,尚未实现完整的反思生成与自主重试机制 |
| Self-Refine | 同一个模型反复生成反馈并据此改写输出 | 反馈是否具体、可操作,迭代何时停止 | 当前由学生修改成果,导师提供指导,不是同模型自主改写链 |
| CRITIC | 通过工具验证获取批评,再据此修正输出 | 反馈依据能否与外部验证关联 | 当前有 CI 等取证输入,但没有复现其工具交互批评与改写算法 |
| MemGPT | 在外部存储和当前上下文之间显式移动信息 | 哪些持久化信息应被装入当前请求 | 本文采用按学生、任务和提交精确查询的确定性方案,不是模型自主调页 |
7.1 Reflexion 与 Self-Refine:反馈被使用,需要明确的循环
Reflexion 将任务反馈转成语言化反思并保存为可复用记忆,使后续尝试能够参考此前经验。它启发这里的问题是:
评审记录写入数据库以后,由谁负责把有用部分带回下一次推理?
但 “把一份评审 JSON 放进上下文” 并不等于实现了 Reflexion 的反思机制。Reflexion 原文
Self-Refine 使用同一模型生成反馈、改写输出,并按任务条件结束迭代;论文还强调反馈应当具体且可操作。本文则保留教学系统的职责边界:导师解释评审意见,学生修改,再由验收链判断结果。这里的退出依据不能只是导师说 “已经改好”,而应是新的评审状态。Self-Refine 原文
7.2 CRITIC:外部验证与模型意见需要保留区别
CRITIC 通过搜索、代码执行等工具验证输出,把验证所得的批评用于后续修正。本文与它的联系,在于反馈应能追溯到验证依据;差异在于本文定位的是反馈传递契约,尚未建立自主工具验证和自动修正循环。CI 的明确结论、Reviewer 的语义判断及学生自述,不能在传递时被压成同等强度的 “已经确认”。CRITIC 原文
7.3 MemGPT 与上下文工程:保存和装入是不同操作
MemGPT 区分外部存储与模型当前能够访问的上下文,并提供显式的信息移动机制。本文面对的是范围更窄、定位键已知的业务记录,因此先选择确定性查询和有界装入,而不引入向量相似检索或模型自主记忆管理。--MemGPT 原文
Anthropic 的上下文工程文章也把重点放在每次推理时如何选择和维护有限信息。这里借用的是按需装入和预算管理的工程思路,并不把这篇工程文章当成方案有效性的学术实验证据。--Effective context engineering for AI agents
还需区分 “没送进去” 和 “送进去但没用好”。Lost in the Middle 在其多文档问答与键值检索实验中观察到相关信息位置对表现的影响;它支持继续检验输入信息的使用效果,但不能解释本文已发生在模型调用之前的漏传,也不能据此断言当前项目使用的模型都有同等程度的位置偏差。--Lost in the Middle,TACL 2024
因此,本文在这些工作中的位置,是补齐一个具体系统使用反馈的工程前提:
来源可追踪、对象不串用、版本不混淆、预算可检查。
本文没有复现上述方法的性能实验,也不主张提出新的反馈学习算法
8. 把设计草案收紧为可执行的反馈契约
为检验这些设计选择,本文另附独立参考实现:提交顺序、评审尝试状态、反馈选择和长度预算已经可以运行测试。但它尚未合入 AI Tutor Engine,也没有完成生产迁移。以下必须区分 参考实现已验证的规则 和 实际项目已经上线的能力。
8.1 用服务端顺序定义最近,不用完成时间猜测
参考实现增加两种服务端生成的递增序号:submission_seq 表示受理提交的顺序;attempt_seq 表示开始评审尝试的顺序。时间戳用于展示,不决定新旧。
这样规定是为了处理两个具体问题:同一秒可能有两次提交;旧提交的评审也可能比新提交更晚完成。仅按评审完成时间取最新,会让晚返回的旧结论覆盖新成果。
每次指导的反馈选择按图 3 执行。先按学生、项目与任务选择 submission_seq 最大的提交,再在该提交内选择 Rubric、Prompt、Engine 版本一致、attempt_seq 最大的尝试。模型名保留为来源信息,不因换模型自动把既有评审定义为失效。

图 3 反馈选择器的判定顺序与七种输出状态
参考库用一条 SQLite 查询取得当前提交与相应尝试,避免这两部分读取来自不同查询时刻。每次构造指导请求重新选择;已经发出的请求只对构造时所见版本负责,不宣称能够撤回发出后的旧请求。
这比“取最近一次已完成评审”更保守。例如 A 已经 FAIL,B 刚提交但评审服务报错,此时返回 B 的 unavailable,当前待处理项为空。A 可以在复盘页查询,但不能未经验证就被描述为 B 仍存在的问题。
同一份提交的重复评审也按开始顺序区分:
较新尝试尚未完成时,不回退旧结论;
较旧尝试晚完成时,也不覆盖更新尝试。
参考实现因此保存不可变尝试记录,而不是不断覆盖同一行。

图 4 控制返回顺序的例子:旧提交晚完成,不改变最新提交的结论
这涉及真实的数据模型变化。 当前项目没有这里定义的两个不可变递增事件序号;现有提交查询按 submitted_at, rowid 排序,Evaluation 按 submission_id 覆盖。不能把现有 rowid 不加说明地当成新协议的持久化事件序号。实际接入仍需新增事件或尝试表、迁移历史记录,并绑定项目的真实身份鉴权;历史顺序无法可靠恢复的记录,应标为来源顺序不确定,不进入自动当前反馈。
8.2 明确每种状态允许做什么
| 反馈状态 | 当前待处理项 | 导师应采用的处理方式 |
|---|---|---|
none |
无 | 按任务与学生当前输入指导,不声称已有评审 |
pending |
无 | 说明最新提交尚无完成的兼容评审,不将旧意见升级为新结论 |
unavailable |
无 | 说明评审服务故障,不能解释为学生项目不合格 |
stale_rules |
无 | 说明规则版本变化,需要按当前规则重新评审 |
invalid |
无 | 说明评审记录不满足契约,转系统排查,不转成学生 FAIL |
passed |
无交付阻断项 | 新通过结果替换旧待办;学习缺口可单独用于教学 |
current |
本次未通过的 acceptance 条目 | 按条目状态和理由指导学生核验、修改或补证据 |
current 内仍要区分 FAIL 与 NEED_REVIEW。前者是该轮已判定不达标,后者还可能是证据不足或冲突;仅凭状态不足以生成自动修复命令。参考实现保留原始理由,不新增未经验证的根因分类器。
学习缺口和 reflection 也不并入交付阻断清单。反馈选择器不能代替原评审链的 Rubric 覆盖检查和 Evidence 真实性验证。
8.3 给出起始预算,并说明它的依据
参考实现以 Python len() 统计序列化后 Unicode 字符数。起始配置如下:
| 参数 | 初始值 | 处理方式 |
|---|---|---|
| 整个反馈数据块 | 2400 字符 | 包括 JSON、来源元数据和边界标记;不含固定角色指令 |
| 单次条目上限 | 4 条 | FAIL 优先,同状态按 rubric_id 排序,保证稳定 |
| criterion | 每项 80 字符 | 省略尾部时加标记 |
| reason / evidence | 各每项 120 字符 | 保留摘要,标注具体被截断字段 |
| next_step | 160 字符 | 标注是否截断,不据摘要直接执行命令 |
| rubric_id | 最长 64 字符 | 超限拒绝该记录,不截短标识符 |
这组数值有一个有限的估算依据:当前课程配置有 50 个任务,每个任务的 acceptance 标准都不超过 4 条。使用这些真实 criterion 和 rubric_id,分别填入 120 字符的构造理由、120 字符的构造证据,以及 160 字符的下一步建议,再补上来源字段后,序列化数据块的最大长度为 2125 字符。因此先取 2400,为该组夹具留出 275 字符余量。
这里的 50 个任务是课程配置,不是 50 名学生或 50 次真实评审;理由与证据长度也是构造条件。这个估算只能说明初始预算与当前 Schema 规模相容,不能证明摘要保真、教学最优或真实模型效果最优。
图 5 展示预算装入与停止条件。实现保留完整 JSON,显式记录 total_items、omitted_count 和 truncated_fields;不静默省略,也不截短标识符。元数据本身超限时返回 context_budget_exceeded 的最小 invalid 状态,不把系统问题记为学生失败。

图 5 预算装入流程:字段限长、完整条目试装,以及显式省略
2400 字符不等于 2400 token,更不保证整个请求装得进模型窗口。固定指令、任务材料、代码和普通历史还需要基于实际模型的 tokenizer 与窗口配置做总预算检查;这部分全局适配尚未完成。超长字段的摘要也可能丢失关键限定条件,因此导师不能据被截断的理由作自动执行或新验收判定,完整意见仍应可查。
8.4 让反馈进入显式上下文,并保持数据边界
参考适配器在 build_context() 和 build_system_prompt() 边界读取反馈,再序列化成单独的数据块。这样不再依赖前端历史是否追加、服务端是否已有会话,或一条普通消息是否还在最近窗口。
state: current
submission_id: revision1
attempt_seq: 1
snapshot_hash: ...
review_status: NEED_REVIEW
items: [对应标准、理由、证据摘要及截断标记]
当前引擎的裁剪函数单独保留首条 system 消息,临时适配器利用这个既有行为,把有界反馈数据放入该消息的专门区段,并在前面加入固定使用规则。这只是本地兼容性选择,不代表所有服务商或所有产品都应把业务数据放在最高优先级角色中。
评审文字、仓库片段和学生自述仍属于不可信业务数据,不能通过这个区段覆盖导师规则。JSON 分隔和固定提示有助于表达边界,但本文只测试了序列化,不声称已证明模型抵抗提示注入的能力。正式接入时还应选择服务商支持的合适数据角色并单独验证。
8.5 参考实现验证了什么
参考实现通过了 26 个确定性规则测试,覆盖身份/项目/任务隔离、同秒提交、时间戳倒序、旧请求晚返回、新提交待评审、规则版本变化、系统故障、PASS 清除旧待办,以及长文本和特殊字符的预算边界。
另外,在原引擎副本上通过临时上下文适配器执行了 6 个请求级场景:
| 场景 | 发给模拟模型服务的请求中观察到的结果 |
|---|---|
| 未加适配器,参考库中已有意见 | 原接口不会自动读取这份新数据源 |
| 加入适配器,已有长会话 | 当前意见进入显式上下文,经过历史裁剪后仍存在 |
| 更换学生身份 | 不传入另一名学生的反馈 |
| 出现更新、尚未评审的提交 | 旧意见退出当前区段,状态为 pending |
| 新尝试发生服务故障 | 传入 unavailable,不沿用旧失败待办 |
| 更新尝试已经 PASS | 传入 passed,旧阻断标记不再出现 |
这 6 个场景使用独立参考库、临时函数适配和模拟 HTTP 响应。它们证明选择规则能够接入原引擎的请求构造边界,不等于生产补丁已完成。数据库迁移、真实身份绑定、浏览器完整链路、真实模型理解及线上反馈效果,仍未验证。
9. 重新提交已有记录,仍需区分复用与重新判断
本次还检查了重复提交。
在相同证据快照和版本化评审 key 下,第二次提交可以命中已有结果,不再调用模型。与此同时,系统仍记录新的提交和评审历史,并关联上一版提交。
测试观察到的是两次提交、两条评审历史,以及第二次复用缓存结果。这里既有真实的提交事件,也有复用的判定。
因此,文章或后续分析不能把 “新增一条评审历史” 直接解释成 “模型重新独立判断了一次”。修改后是否重新评审,仍取决于证据快照和缓存 key 是否变化。上一篇提到的 head_sha 不参与评审缓存 key 的边界,在这里仍然成立。
评审服务故障也需要单独保留语义。构造服务商 502 响应时,接口返回 REVIEW_UNAVAILABLE,新增提交没有被写成一条学生失败 Evaluation。
这个行为应当延续到后续指导:平台暂时无法评审,不能被转换成 “你的项目又没通过,请继续改代码”。
10. 下一步先验证反馈传递,再评价指导效果
这个缺口并不需要先积累大量真实用户数据才能确认。检查请求内容,就能验证某条意见是否进入模型上下文;构造前后两次评审,也可以检查旧意见是否正确退出。
但是,信息传到了 和 指导有效了 仍是两层问题。
前者可以先用确定性测试验证:是否定位到正确学生和任务,是否引用正确提交,是否带上未解决项,是否误用旧评审,以及系统故障是否仍被独立处理。
后者需要真实模型和实际使用记录:导师是否正确理解原因,建议是否可执行,学生是否能够据此补充有效证据,重新提交后是否解决原问题,是否引入新的遗漏。
参考实现已在传递规则和临时适配层完成可执行验证,但没有完成生产修复,也没有给出指导成功率、重验通过率或效率提升数字。下一步效果实验可以比较“学生主动粘贴完整意见”和“服务端自动装入同样意见”两组,固定任务、反馈内容和模型设置,再检查反馈利用、错误归因、修订结果和成本;这项实验目前尚未开展。

图 6 参考方案中,指导、学生修改与重新验收构成有版本边界的循环
前四篇把验收链路建立起来以后,这次检查让我继续关注链路之间的连接:评审产生了结构化结果,指导也有自己的上下文,但它们仍需要明确的数据传递契约。接下来值得补上的,就是这份契约把哪次提交的哪些意见交给导师,允许它据此做什么,再用什么结果确认问题已经解决。
参考与延伸
- Shinn et al.,2023,Reflexion: Language Agents with Verbal Reinforcement Learning。语言化反思与跨尝试记忆;本文不复现其性能实验。
- Madaan et al.,2023,Self-Refine: Iterative Refinement with Self-Feedback。同模型反馈与改写循环;本文保留学生参与修改。
- Gou et al.,2023 年预印本,CRITIC: Large Language Models Can Self-Correct with Tool-Interactive Critiquing。通过工具验证支持批评与修正;本文不宣称实现其完整算法。
- Packer et al.,2023 年预印本,MemGPT: Towards LLMs as Operating Systems。外部记忆与当前上下文之间的显式移动。
- Liu et al.,2024,Lost in the Middle: How Language Models Use Long Contexts,TACL 12:157–173。区分输入可达和信息利用;不把其历史模型实验直接推广到当前项目模型。
- Anthropic,2025-09-29,Effective context engineering for AI agents。工程实践资料,区别于同行评议论文。
项目实践:AI Tutor Engine(信科院智能助手)。原路径核验基于 2026 年 10 月 17 日代码副本,相关主链路文件于 10 月 20 日复核一致。原实现有 11 个后端及历史裁剪场景和 3 个前端函数场景;独立参考实现另有 26 个规则测试和 6 个临时适配场景。它们属于不同测试层次,不代表真实模型准确率、用户成功率或生产端到端验收。参考实现未合入原项目,前端未完成浏览器全链路验证,模型输出为构造响应。