跳转至

05 · 喂事实,不喂叮嘱

agent 出错时,多数团队的第一反应是往 prompt 里加话:"请注意 XXX""务必先确认 YYY"。dsh 的第一反应是另一个问题:它缺什么事实? 然后把这个事实做成结构化、可靠、防篡改的供给。这一章看两个样本。

样本一:一次真实事故的复盘(postmortem 0003)

docs/postmortem/ 下有 4 篇事故复盘,编号和格式与大厂 SRE 事故报告一致:摘要、影响、时间线、根因、护栏。(截至 09-04 仍是 4 篇,原因见第 09 章。)0003 讲的是一次 AI 自己犯的错:

事情经过:一个 Web agent 改了 GUI 源码,但它不知道自己的会话跑在哪个 URL、哪个进程上。于是它先把验收甩回给用户;然后自己起了个裸 Vite 服务,看到 HTTP 200 就宣布成功(实际是白屏——关键的启动清单只有完整宿主才会注入);最后又在另一个端口起了全新的服务器去"验证",而用户原来那个页面早就自动更新好了。用户连续指出三次错误它才反应过来。

他们怎么处理的,三点都反常规:

  1. 时间线精确到会话日志的事件序号("31865 号事件启动了裸 Vite"),不靠事后回忆重构意图——这依赖产品本身的 append-only 会话日志,一切模型可见的东西都有据可查
  2. 根因不怪模型笨,怪环境没给信息:当前 GUI 的 URL 和运行模式对模型不可见,它只能猜;HTTP 200 看起来像成功是因为"传输就绪"和"应用就绪"没有区分的信号
  3. 修复全是系统性的:把 URL 和模式注入 prompt 和环境变量、让裸 Vite 启动模式直接报错拒绝、加真实路径的回归测试。甚至发现第一版回归测试本身是假阳性(超时杀进程恰好满足非零退出断言),也写进了复盘。("模式"这一项其实在 08-11 就被移除了,只保留 URL 注入,本章初稿写作时它已不在源码里;复盘文档里的护栏描述滞后到 08-25 才随一次文档标准重写改掉。postmortem 不冻结历史,只描述现状,但也不是实时的。)

配套还有一份 defensive-patterns.md,33 行,开头一句定性:"每一条都是在这里真实发生过或差点发生的缺陷类别,写成防止复发的规则"。一个 bug 从发生到变成体系免疫有四层去处:postmortem 记单例 → defensive-patterns 归纳成类别 → review skill 要求对照 → 能机械化的沉到 lint。

样本二:change-scope——给 agent 造一个"事实仪表"

问题:agent 在 push 前和 review 时需要知道"这次到底改了哪些文件",据此选测试、定 review 重点。但 diff 需要比较基准(base),agent 自己算经常错——新分支还没 push 时 origin/分支名 不存在;堆叠 PR(分支 B 基于未合入的分支 A)时和 master 比会混进 A 的全部改动。基准错 → 范围错 → 漏测。

解法:一个仓库根命令 change-scope --base <ref>,输出一份版本化 JSON 事实报告:

  • base、head、merge-base 各自解析成哪个 commit,白纸黑字
  • 四份文件清单:已提交 / 已暂存 / 改了没暂存 / 未跟踪——普通 diff 只看第一类,agent 常忘了工作区还有脏改动

防污染的细节(每条都是防"事实不完整导致误判"):

  • 禁用 rename 检测——改名显示成"删 a、加 b"两行,啰嗦但一个路径都不漏
  • 禁外部 diff 驱动——本机 git 配置不能悄悄让某类改动从报告里消失
  • 严格 UTF-8,非法值直接中止——宁可整个命令报错,也不悄悄把乱码替换成占位符(替换后两个不同文件名可能变得一样,报告就撒谎了)

最关键的一刀:命令拒绝自己猜 base。 可以做成"聪明"的(自动查 upstream、问 GitHub PR 目标),他们拒绝了——没有任何自动推断在所有分支拓扑下都对。于是分工明确:

判断 base 是谁 = 判断题,归 agent(skill 指导它先确认分支和 PR 状态); 给定 base 后改了什么 = 事实题,归工具(保证绝对准确,宁失败不失真)。

工具不越界做判断,agent 不亲手算事实。

这一章的通用原则

  1. agent 出错,先问它缺什么事实,再考虑改 prompt
  2. 事实供给要工具化:结构化输出、防污染、fail loud(宁可报错不输出看似正确但残缺的结果)
  3. 判断和事实分离:工具只陈述世界,判断留给 agent(并用 skill 指导判断)
  4. 让一切模型可见的东西可追溯(他们的会话日志把这条做成了产品级不变式:"model-visible ⟺ logged"),事故复盘才可能精确到事件而不是靠回忆

← 上一章 · 返回目录 · 下一章:测试哲学 →