反馈层:把环境信号变成下一轮可执行的最小修正
| 难度 | 阅读时间 | 最后验证 | 作者 |
|---|---|---|---|
| 进阶 | 13 分钟 | 2026-07-11 | LearnPrompt 编辑部 |
Agent 改了一轮没修好,你把「再认真检查一下」写回提示词,它又改一轮,还是不对,最后越改越乱。多数人下一步继续加形容词,或者只把最后一行报错粘过去。
但决定下一轮能不能修好的,从来不是措辞,而是反馈信号的质量。同样是一次失败,只握着「失败了」三个字,下一步就只能整段重写或换方向瞎猜;握着「文件 X 第 5 行,期望 hello-world,实际 Hello-World」,搜索范围就会收窄到小写化相关修复。反馈层就是决定你手里握着哪一种信号、并把它变成下一轮动作的那一层。
读完你能做什么
标题为“读完你能做什么”的章节你会学会用四个属性审计任何一条反馈信号,并把它接进一个能自己收敛的回路:
- 可定位:信号指到具体文件、行号或配置,而不是泛泛的「有问题」。
- 可判断:两个人看同一条信号,得出同一个 pass/fail。
- 低延迟:从行动到拿到信号的时间越短,闭环越紧。
- 可执行:信号能直接翻成下一步动作,不用再解释一轮。
你还会看到反馈层与状态、记忆、评测、编排四层的分工,并运行一个最小仓库,亲眼看到模糊反馈只回「构建未通过」、可操作反馈把同一次失败收窄到小写化相关修复。
图注:左边是一轮回路和它的停止/升级出口,右边解释信号颗粒度如何决定下一轮改动的大小;模糊反馈的问题是把两件事都拖垮。
先看一个失败:反馈里只有「失败了」三个字
标题为“先看一个失败:反馈里只有「失败了」三个字”的章节假设你的验收信号长这样:
FAIL: 构建未通过句子没错,但它没回答任何一个能推动下一步的问题。哪个文件、哪一行出了错?期望的是什么,实际拿到的又是什么?三十个用例里挂了几个,是同一类问题还是各不相同?
拿着这条信号,Agent 和人一样,只能扩大搜索范围:要么把整个函数重写一遍,要么凭直觉换个实现方向。Claude Code 官方文档把这个处境说得很直接:没有可跑的检查时,「看起来做完了」是唯一可用的信号,于是你自己变成了验证回路,每一个错误都要等你去发现。反馈层要解决的,正是让信号本身带上足够的定位,让下一轮不必靠猜。
好反馈的四个属性
标题为“好反馈的四个属性”的章节把官方「给可验证信号」的建议推到底,一条反馈信号有没有用,可以拆成四个能被逐一检查的属性。下面这张表是审计一条信号的最小清单:
| 属性 | 它回答的问题 | 反例 | 正例 |
|---|---|---|---|
| 可定位 | 错在哪里? | 构建未通过 | test/slugify.test.mjs:5 |
| 可判断 | 算过还是没过? | 看着差不多 | 期望 hello-world,实际 Hello-World |
| 低延迟 | 多久拿到? | 全站构建 40 秒 | 针对性单测 40 毫秒 |
| 可执行 | 下一步做什么? | 再认真点 | trim 后补一步 toLowerCase |
左列都无法机械检查,右列每一格都能落到一个真实文件、命令或判定上。官方给的对照例子是同一个道理:修构建失败时,把 the build fails with this error: [paste error] 连同报错原文喂进去、并要求验证构建成功、修根因而不是压掉报错,远胜于只说一句「构建挂了」。可执行的意思,就是信号本身已经把下一步动作指出来了。
四个属性里,可判断最容易被忽略。Anthropic 关于评测的工程文章给了一个很硬的判据:一个好任务,是两个领域专家各自独立判定都会得到同一个 pass/fail。如果同一条信号,两个人看完一个说过、一个说没过,那它根本不构成反馈,只是又一次需要争论的观点。
缩短反馈延迟:把最快的验收放进最内圈
标题为“缩短反馈延迟:把最快的验收放进最内圈”的章节反馈延迟不是玄学,就是一条命令的耗时。全站构建几十秒,一条针对性单元测试几十毫秒。官方把「紧的反馈回路」直接列为最好的结果来源,工程含义很朴素:内圈用最快最精准的信号,外圈才用慢而全面的信号。
Claude Code 文档给出的四档 gating,本质就是按延迟和成本从内到外排列:
- 一条 prompt 内让模型自查并迭代,最快,随手可用。
- 把检查设成
/goal条件,由一个独立 evaluator 在每一轮之后复检,直到条件成立。 - 用 Stop hook 作为确定性闸,它把你的脚本当作退出条件,不通过就不让这一回合结束。
- 请第二意见:让一个 verification subagent 用全新上下文去试着推翻结果,做事的那个就不再是打分的那个。
实践上,你不会每次都跑最外圈。改一行代码时,先跑那条最快、最贴近改动的单测;等它绿了,再跑更慢的全量构建或独立评审。把慢信号放到内圈,等于每改一行都罚自己几十秒,回路自然松掉。
从失败证据定位最小改动
标题为“从失败证据定位最小改动”的章节可操作信号的价值,在于它自带定位。断言给出期望值和实际值,测试名和行号把问题缩到具体用例。诊断动作因此变成一件很收敛的事:把期望与实际之间的差,映射到相关代码区域。信号能缩小搜索空间,但通常不能单独证明唯一实现。
关键是抵住「顺手多改几处」的冲动。Anthropic 的评测文章提醒,通常应该评产物而不是评路径——验收只认输出对不对,不规定你走哪条实现路线。反过来对写代码的人也成立:既然验收只盯输出,你应该先选择一个能解释证据的最小候选修复,而不是趁机重构;它通过当前验收,只证明这个候选足以满足现有用例,不证明它是唯一答案。改动面越小,下一轮验收能告诉你的因果就越干净。
何时停止、何时升级
标题为“何时停止、何时升级”的章节反馈层最容易被漏掉的,是回路的两个出口:什么时候停,什么时候不该继续在原地转。
停止条件应该写在前面,而不是靠感觉。Anthropic 关于长任务 Agent 的研究里,生成者和评审者会先谈好一份 sprint contract:动工之前就把「done 长什么样」定下来,任一硬指标没达到,这个 sprint 就算失败。落到日常,就是全部验收通过才收工,别在绿灯之后继续改——改动越过验收线之后每一笔都是纯风险。
升级条件同样要显式。当信号连续两轮不再变化,或者已经撞上时间与预算,就该换人、换层或换策略,而不是死循环。官方给了两条很具体的线:Stop hook 会在连续 8 次阻塞后被 Claude Code 覆盖并结束这一回合,这是一个确定性的上限;以及,如果同一个问题你已经纠正超过两次,正确动作是 /clear 重开、用一条更好的提示重来,而不是在被失败尝试污染的上下文里继续加轮次。长任务研究里也一样:sprint 失败时,系统把「哪里错了」的详细反馈交回生成者去改,而不是无脑重试同一条路径。
一个简单的判断法:如果每一轮信号都在变、每一轮改动面都在缩小,回路在收敛,继续;如果信号连续不变、改动却越铺越大,回路在发散,停手升级。
反馈层和其他四层的分工
标题为“反馈层和其他四层的分工”的章节反馈层很关键,但它只负责「把信号变成下一步动作」,不负责存储、判决和调度。分不清边界,就会把本该别处解决的问题塞进反馈里。
- 状态层回答「下一轮要记住什么」。目标、上次失败的证据属于状态;反馈是这一轮从环境刚拿到的新信号。反馈会流进状态,但它本身不是状态。
- 记忆层回答「跨会话记住什么」。用户偏好、项目事实是记忆;反馈是回路内、即时、每次行动都会刷新的东西。
- 评测层回答「怎么判过没过」。graders 和用例产出 pass/fail 判决;反馈层更宽,是拿到判决和报错之后,把它翻成下一步最小动作。评测给判决,反馈层给动作。
- 编排层回答「谁跑、按什么顺序、何时停」。反馈是编排的停止条件所读取的内容;反馈是信号,编排是读信号之后的控制流。
还有一条容易踩的线:Anthropic 明说,分离「做事的 agent」和「评审的 agent」是一个强杠杆,因为自评常常会自信地夸奖自己的产物——哪怕在旁人看来质量明显平庸。所以生成者报告的「我做完了」只能当线索,真正的反馈得来自它之外的测试、diff 审查或独立评审。
Showcase:模糊反馈 vs 可操作反馈
标题为“Showcase:模糊反馈 vs 可操作反馈”的章节为了让「信号颗粒度决定改动大小」不停留在口号,本文附了一个无网络依赖的最小仓库,保留了 fail → 诊断 → 最小 patch → pass 的可复现全过程,并把两条反馈通道并排放在一起。完整目录、命令与脱敏输出见 研究与 Showcase。
任务对象是一个把标题转成 URL slug 的函数,它的验收标准由三条用例规定:小写、连字符、去标点。当前保存的实现漏了小写化这一步。
第一步,走模糊反馈通道。它跑同一份测试,却只把退出码翻成一句结论,故意丢掉全部定位信息,代表「全站构建只说失败了」这类粗颗粒信号。2026-07-11 在 macOS、Node v24.11.0 下的真实结果:
$ node checkers/vague-check.mjsFAIL: 构建未通过(无更多信息) (exit 1)拿着这条信号,你不知道哪个文件、哪一行、期望什么、实际什么,下一步只能瞎猜或整段重写。
第二步,走可操作反馈通道。同一次失败,node --test 自带文件、行号、期望值和实际值:
$ node --test repo/test/slugify.test.mjs✖ lowercases and hyphenates words✔ collapses repeated whitespace✔ drops punctuationℹ pass 2 ℹ fail 1 ℹ duration_ms 39.34
test at repo/test/slugify.test.mjs:5 - expected 'hello-world' + actual 'Hello-World'三条只挂一条,证据把搜索范围收窄到大小写:期望全小写、实际首字母大写。它排除了整段重写的必要,但没有规定只能用哪一种代码实现。
第三步,选择一个与证据一致的最小候选改动,并重跑同一条验收命令。本文选择在 trim() 之后补一步 .toLowerCase(),只改一行:
$ cp patch/slugify.mjs repo/src/slugify.mjs$ node --test repo/test/slugify.test.mjs✔ lowercases and hyphenates words✔ collapses repeated whitespace✔ drops punctuationℹ pass 3 ℹ fail 0 (exit 0)退出码从 1 变 0,这才叫修好,而不是「我觉得应该好了」。
这个 Showcase 证明的是:反馈的价值不在报没报错,而在信号是否带定位;同一次失败,可操作反馈能把搜索范围收窄到小写化问题,并让一个一行候选补丁接受同一验收,模糊反馈只给一个布尔结论。它没有证明这个补丁是唯一答案;两条通道跑的是同一份测试和同一次失败,差别只在信号颗粒度,因此不构成任何模型排名,本 Showcase 也未运行在线大模型。一次 pass 也不等于真实任务长期可靠。
什么时候不该用反馈层解决
标题为“什么时候不该用反馈层解决”的章节反馈层不是万能的。遇到下面这些情况,先别在信号上使劲:
- 失败来自工具缺失或权限不足。这属于能力层或约束层,信号再清楚,也跑不动一条不存在的命令。
- 根本没有可跑的验收。先补一条能出 pass/fail 的检查——测试、构建退出码、linter、把输出和 fixture 做 diff 的脚本——否则无信号可闭环。
- 同一个问题反复纠正无效。这是升级、换层或换策略的时机,而不是继续在同一回路里加轮次;官方的经验线是纠正超过两次就重来。
- 你想把「绝对不能删生产库」这类硬边界交给反馈。那要靠 deny 规则、沙箱或 PreToolUse 钩子在动作发生前拦住,反馈只在动作发生后才到。
一个判断法:如果补一条更细的信号,能让下一轮从瞎猜变成一处最小改动,那是反馈层的活;如果补再多信号都改变不了环境、工具或强制边界,那就换层解决。
练习:给你的反馈回路做一次四属性审计
标题为“练习:给你的反馈回路做一次四属性审计”的章节找一个最近反复没修好的任务,把你当时手里的反馈信号,填进这张表:
可定位: 信号有没有指到具体文件/行号,还是只有“有问题”可判断: 两个人看同一条信号,会不会得出同一个过/不过低延迟: 你跑的是最快那条验收,还是每轮都在等全量构建可执行: 信号能不能直接翻成下一步动作,还是又要再解释一轮停止/升级: 全过就停了吗;信号连续不变时,你换策略了还是继续硬转任何一行答不上「落到了某个真实资源」,就是这次没修好的嫌疑属性。只补最薄弱的一条——比如把全量构建换成一条针对性单测,或把「失败了」换成带 file:line 的断言——再用同一个任务重跑,观察改动面有没有从「大改」收敛到「一处」。不要同时换模型、换提示词、换测试,否则你无法知道是哪一项真正起了作用。
完成标准:五行都能指向一个具体文件、命令或判定;补强信号前后各保存一次同一验收命令的结果;补强后你能只凭退出码而不是感觉判断是否修好。如果仍要靠「我觉得好像好了」,这次审计就还没完成。
来源与延伸阅读
标题为“来源与延伸阅读”的章节- Claude Code 官方文档:Best practices(给可验证信号与反馈回路)
- Anthropic:Effective harnesses for long-running agents
- Anthropic:Demystifying evals for AI agents
- Harness Engineering 橙皮书
- 本篇研究包与可运行 Showcase
前三条为官方一手资料,支撑本文所有当前产品行为,核验日期 2026-07-11;其中 Stop hook「连续 8 次阻塞后结束回合」为当前实现,可能随版本变化。橙皮书作为中文主题地图(二手来源)帮助确认反馈层在 harness 叙事中的位置,其仓库为教育性分享并要求署名,本文在此保留链接与署名,未复制其任何图片或成段文字。反馈四属性、最快验收放最内圈与信号连续不变即升级,是 LearnPrompt 对官方建议的操作化综合,不是行业统一标准;文中 Showcase 已重新组织并复核。
