
/goal 是给盲眼裁判判题:一个隐喻打通三件实践
你写了第一个 /goal:
/goal 修复登录 bug
按下回车,agent 开始干活。半小时后回来一看,对话已经跑了 20 多轮,每一轮都在说「再确认一下」「再加一段保护」。它没在循环,它在飘。
第二次换个写法:
/goal 部署到 staging 成功
这次 agent 三轮就停了,说部署完成。点开 staging,404。
这两个失败有同一个根:你写的不是给 Claude 看的指令,而是给一个根本看不见现场的裁判判的题目。
/goal 不是循环机制,是判题机制
主流叙事是这样的:/goal 让 Claude 跨轮工作直到条件满足。
这描述没错,但完全错过了它的结构。/goal 的真实形态是:
每轮主 turn 结束,一个独立的小模型(默认 Haiku)被调起来,拿到你写的 condition 和当前对话历史,判 yes/no。yes 就停,no 就把它的理由当成下一轮的 directive 甩给主模型继续干。
这个小模型有个核心特征:它不能调用任何工具。
不能跑 bash。不能 read file。不能 git status。不能 curl。它就是个纯文本判断器,输入是对话流,输出是 yes/no 加一句理由。
把它叫做盲眼裁判 —— 它有判决权,但什么都看不见。
一旦这个隐喻立起来,三件你听过但分散的实践,全部归一到「让裁判看得见」这一个问题上。
裁判能看到什么、看不到什么
| 能看到 | 看不到 |
|---|---|
| 你之前发的所有 prompt | 任何文件的真实内容(即使 Claude 改过) |
| Claude 的所有文字回复 | shell 命令的真实输出(除非 Claude 把它 echo 出来) |
| Claude 工具调用的返回值 | git 当前状态 |
| Claude 主动写的验证报告 | 数据库、网络服务、CI、文件系统 |
这不是 bug,是物理约束。evaluator 跑在另一个调用里,没有工作目录权限,也不应该有 —— 否则每轮都跑一遍测试,成本和延迟会爆炸。
「修复登录 bug」失败是因为:完成的标志是 bug 真的不见,但裁判读到的只是「Claude 说 bug 修好了」之类的字符串。信不信全凭它对对话流的解读,没有可信信号。
「部署到 staging 成功」失败是因为:部署成功是个外部状态,裁判完全看不到 staging。它只能根据 Claude 自己说的「部署完成」判断。Claude 跑了 kubectl apply,stdout 说 created,它就当成功了。但 created 不等于 rolled out,pod 可能还在 CrashLoopBackOff。
推论一:condition 用状态写,不要用动作写
动作和状态的差距不是文风差距,是可判性差距。
| 动作(难判) | 状态(好判) |
|---|---|
| 修复登录 bug | tests/auth.test.ts 全部通过且 git status clean |
| 重构这个模块 | src/payment/ 下每个文件 ≤300 行且 lint clean 且原有测试全过 |
| 让性能更好 | bench/login.bench.ts 跑出 p95 ≤ 200ms(baseline 320ms) |
| 整理文档 | docs/ 下每个 .md 首行有 H1 且最后修改时间 ≥ 今天 |
| 优化这个 SQL | EXPLAIN ANALYZE 的 Total Cost ≤ 5000(原值 23000) |
右列每一条都满足同一个模板:
主体 + 动词的完成体 + 量化谓词
主体(哪些文件 / 哪些 metric)告诉裁判去对话里找什么。完成体(已通过 / 已满足 / 处于 X 状态)让「做完了」二值化。量化谓词(≤ 阈值 / 包含字段 / 等于值)杜绝主观判断。
为什么状态比动作好用:
- 离散。yes/no 二分不需要「判断质量」
- 自带验证手段。写「p95 ≤ 200ms」的同时你已经被迫想清楚怎么测
- 抗 forward-motion bias。动作描述让 agent 觉得「我做了几件事 = 完成」;状态描述要求那几件事真的产出了那个结果
日文社区有句话总结得很准:「『修正されていること』の方が判定しやすいんだわ」—— 被修正过的状态,比「去修正」更容易判定。
推论二:所有证据必须落进对话流
condition 写成状态了,但裁判还要看到证据,否则只能信 Claude 的总结。
/goal config.yaml 里 enable_v2 字段为 true
Claude 改了文件,回复「已修改」。裁判看到字符串「已修改」,判 yes,结束。但配置可能根本没改对,YAML 里可能还有第二处 enable_v2: false。
修法:
/goal config.yaml 里 enable_v2 字段为 true,
完成后必须运行 `grep enable_v2 config.yaml` 并贴出输出
现在 enable_v2: true 这行以 stdout 形式出现在对话里,裁判读到的是原文,不是 Claude 的措辞。
测试通过的 condition 一样:
/goal 所有测试通过
—— 改成:
/goal `npm test` 退出码 0 且 stdout 末尾包含 "Tests: X passed, 0 failed",
不要压缩测试输出
部署成功更典型:
/goal 部署到 staging 成功
—— 改成:
/goal 跑 `kubectl rollout status deployment/api -n staging`,
stdout 包含 "successfully rolled out" 且退出码 0
每个 condition 都要补上「必须把验证命令的原始输出贴回」这一步。检验窍门只有一个:
只给一个没有运行权限的人看这段对话,他能判定吗?
不能就回去补证据通道。
推论三:长任务需要外部存档
condition 写好了,证据通道也开了,下一个问题是任务跑长了。跑到 30 轮,对话被 compaction,前面的状态全压成摘要,agent 不记得自己干到哪。或者 session 重启了,需要 --resume,但 turn 计数和 token baseline 全部归零。
这就是 PLANS.md 存在的理由。
社区流传的「三件套」(PLAN.md / EXPERIMENTS.md / NOTES.md)其实是简化版误传 —— canonical source 是 OpenAI cookbook 的单文件 PLANS.md + 4 个固定 section 方法论:
Progress:
- [x] (2026-05-12 14:30Z) auth/login.ts 迁移到 v2 API
- [ ] auth/logout.ts 迁移(完成: 删旧调用; 剩: token 刷新)
- [ ] auth/refresh.ts 迁移
每个停顿点更新,时间戳 + checkbox。做了一半就停了的项,拆成两项写。
Surprises & Discoveries:
- Observation: v2 API 的 token 字段从 access_token 改成了 accessToken
Evidence: /v2/refresh 返回 {"accessToken": "..."}, 旧测试期望 access_token
写「观察 + 证据」对,不写主观判断。证据要可粘贴。
Decision Log:
- Decision: 保留旧 v1 endpoint 作为 fallback 6 个月
Rationale: 移动端有 12% 流量未升级,发布节奏对不上
Date/Author: 2026-05-12 / claude session abc123
Outcomes & Retrospective:阶段性或完成时写一次,对比原始目标 vs 实际产出,记 lessons learned。
PLANS.md 不是文档卫生。它是裁判的外接记忆。 Claude 每轮 cat PLANS.md 把 Progress 输出到对话里,裁判就能读到当前进度,即使前 25 轮已经被 compaction 抹掉。这同时也解决了 --resume 后所有计数归零的问题 —— 计数会丢,但 PLANS.md 不会。
4 个 section 不是工整摆设
为什么是这 4 个 section 而不是 3 个或 5 个?因为它们构成一个完整的认知闭环:
| Section | 干什么 | 缺这一节会发生什么 |
|---|---|---|
| Progress | 做事 | 失忆 —— 不知道做到哪了 |
| Surprises & Discoveries | 学习 | 重复踩坑 —— 同一个 API 坑掉进两次 |
| Decision Log | 决策 | 反悔无据 —— 后面走错路,但不知道当初为什么选这条 |
| Outcomes & Retrospective | 复盘 | 经验不沉淀 —— 下次类似任务从零开始 |
做事 → 学习 → 决策 → 复盘,是 PDCA 在 agent 协作里的最小实例。少一节不是少了点信息,是少了一类完全不同的信息。
标准 condition 模板
把三个推论合在一起,一个能跑的 /goal 长这样:
/goal 按 PLANS.md 的 Progress 清单跑到所有项 checkbox 变 [x],
期间不修改 src/legacy/ 下的文件,
每完成一项更新 Progress 时间戳,
append 任何意外到 Surprises & Discoveries,
npm test 退出 0 视为单项通过,
最后 append 一节 Outcomes & Retrospective,
或 30 turns 上限
拆开看:
按 PLANS.md ... 变 [x]—— 状态化谓词不修改 src/legacy/—— 安全护栏每完成一项更新...append 到 Surprises—— 强制证据落进文件,文件被 cat 进对话流npm test 退出 0 视为单项通过—— 证据通道append Outcomes & Retrospective—— 收尾节点30 turns 上限—— 防暴走兜底
一句话检验
下次写 /goal 之前,把 condition 念一遍,问自己:
盲眼裁判靠这段对话能判吗?
能 —— 跑。 不能 —— 这条 condition 还没写完,回去给它补眼睛。
评论 (0)
加载中...