第五课:可观测性与验证 harness
让 Agent 出示证据,而不是宣称成功

≈20 分钟 · AI 时代战略型工程师 · 课程 0005 · 2026-07-06
前置:第四课·上下文漂移 · 第三课·编排大重构 · 参考:术语表 · 能力速查表

这一课治的是你流水线最后的短板

你的 KMP 重构 Workflow 全自动跑通了:10 个单元 MERGED、编译绿。但你自己说过那句关键的话——编译通过 ≠ 功能没坏。点赞计数、评论面板参数、JustWatched 浮窗、分页 generation,这些运行时回归只有真机能验。于是整条 ⑤ 级流水线,唯一还卡在 ① 级纯手工的环节就是:你抱着那 1 台设备逐 Tab 点

官方点名的失败模式就叫 trust-then-verify gap:agent 宣称"完成了",你信了,回归在一周后爆出来。Claude Code 官方 best-practices 现在有专章讲这件事,核心口径两句话——"Have Claude show evidence rather than asserting success"(让它出示证据,不是宣称成功)、"If you can't verify it, don't ship it"

这一课把第四课埋的伏笔收回来:trace 是诊断工具——现在我们把它建起来,再接上"判定",组成完整的验证 harness。目标不是全自动,是把你从"逐 Tab 点"降级到"只审一份分析报告"。

先分清两件事:看见 ≠ 判定

可观测性(observability):能还原 agent 实际干了什么——trace、journal、日志。回答"发生了什么"。
验证(verification):有一道门禁判定产出对不对——断言、测试、门禁 hook、独立 verifier。回答"它对吗"。
两者缺一不可:只有验证没有 trace,挂了不知道为什么;只有 trace 没有验证,看得见但拦不住。

可观测性三件套(给你的流水线补上)

确定性 trace:hook 落盘,agent 想忘都忘不掉。第四课讲过 hook 是确定性的——事件发生必然执行,不靠模型自觉。用 PostToolUse 把每次工具调用写成一行 JSONL(stdin 本身就是结构化 JSON,直接落盘):
{ "hooks": { "PostToolUse": [{ "matcher": "*", "hooks": [
  { "type": "command", "command":
    "jq -c '{ts:now, tool:.tool_name, input:.tool_input, session:.session_id, prompt:.prompt_id}' >> ~/.claude/trace.jsonl" }
]}]}}
官方语义保证:多个 hook 并行执行,日志 hook 和拦截 hook 可叠加——trace 和第四课的护栏不冲突。另外别忘了白嫖:会话本身就是全量 JSONL transcript~/.claude/projects/<项目>/<session-id>.jsonl),每行带 timestamp/parentUuid/token 用量,一条 jq 就能抽出所有工具调用:
jq -c 'select(.type=="assistant") | .message.content[] | select(.type=="tool_use")' <session>.jsonl
结构化 run journal:agent 自己记"意图层"。trace 是机器记的"实际动作",但它没有"agent 当时想干什么、判定是否成功"。把这条不变量钉进编排(第四课的钉磁盘手法):每步追加一行 步骤号 | 意图 | 动作 | 观察 | 判定;所有截图文件名带步骤号(step07-savior-digg.png),让图和 trace 能对上。journal 与 trace 是两条独立证据链——对照它们,才能发现"判定说成功、证据说失败"的那一步。
trace 的消费也委派。跑完别自己啃几百行日志——丢给一个分析 agent,prompt 模板:
读这份 run journal 和 trace(JSONL),找出第一个"判定与证据矛盾"的步骤:
判定写成功但观察/日志/截图不支持,或跳过了 VERIFY.md 里要求的断言。
输出:步骤号、矛盾内容、涉及的 Tab、建议复查动作。按严重度排序。
一个额外的好消息:v2.1.199 修掉了"子 agent 把 API 错误当成功结果返回"的 bug——独立 verifier 模式过去最阴的坑已被官方填了。

个人 vs 团队的选型:官方还有 OpenTelemetry 导出(CLAUDE_CODE_ENABLE_TELEMETRY=1,metrics/logs/traces 三件套,成本可按 skill/子 agent 归因,beta 的分布式 tracing 连 agent 树都能还原)。但那是团队级基建——个人做 trace,hooks + transcript 这条轻路线就够。

验证梯度:官方给了四级,对号入座

2026 年中 best-practices 的新框架,按门禁强度递增——这张表值得贴在墙上:

层级机制一句话适合
1. 单条 promptprompt 里写明"跑完测试再结束"零配置,靠自觉(会漂,见第四课)小改动
2. 会话级/goal(v2.1.139+)每轮结束由独立小模型评估条件是否达成,未达成自动续跑;条件要写成"可度量终态 + 检查方式",可加 or stop after 20 turns 限流长任务收敛
3. 确定性门禁Stop hook 跑脚本测试/lint 不过就 {"decision":"block"},回合无法结束。护栏:连续 block 8 次强制放行;脚本要查 stop_hook_active 防自锁硬标准(编译/测试/lint)
4. 第二意见独立 verifier 子 agent / workflow干活的模型不给自己打分——fresh context 的 agent 只看 diff 和标准做审查(第六课的对抗验证就是它的舰队版)正确性无法脚本化时

官方 verifier prompt 模板(注意最后一句,防 reviewer 过度报问题):

Use a subagent to review the diff against PLAN.md.
Check that every requirement is implemented.
Report gaps that affect correctness, not style preferences.

还有两个新门禁值得知道:TaskCompleted hook(exit 2 可阻止任务被标记完成——给内置任务系统装验收钩);内置 /verify skill(v2.1.145+)——通过真实界面端到端驱动改动,官方原话:"If users click buttons, test by clicking buttons, not by curling the API underneath"。

客户端特化:把真机验证做成半自动

你的场景比纯后端难:1 台设备 = 验证天然串行,UI 回归靠肉眼。但你手里的验证设施其实已经齐了,缺的只是把它们接进 agent 流水线

VERIFY.md
每 Tab 的断言清单(你写)
device-control 驱动
agent 逐 Tab 操作真机
logcat 断言 + 截图
日志优先,截图兜底
run journal
步骤|意图|观察|判定
分析 agent 出报告
你只审这份
1
断言优先用日志,截图只兜底(你自己的 test-coverage-completeness 规范,这里是它的 agent 化)。日志能 grep、能机器断言、能进 trace;截图脆、要人眼。你重构时补的分层日志 tag 不是文档洁癖——它就是验证 harness 的地基:adb logcat -s "Savior:*" 一条命令断言"点赞后计数++ 且无重复请求"。
2
Native-vs-KMP 截图对比接进流水线:你的 .a2k/ 截图测试体系是现成的 UI 回归网——让驱动 agent 在关键步骤触发它,diff 超阈值就在 journal 里记"判定=可疑",留给分析 agent 升级为复查项。
3
1 台设备的正确用法:串行瓶颈没法并行化,那就把它放到流水线最后、且只跑增量——哪些 Tab 的代码这轮真的变了,只验它们;全量回归留给周末挂机跑。设备时间是你最稀缺的资源,按第六课的算法给它记单价。

两个工具怎么各自落地

Claude Code
trace:PostToolUse JSONL hook + 会话 transcript 白嫖;hook 事件 2026 年已扩到 30+(含 PostToolUseFailure 工具失败、InstructionsLoaded 规则加载——第四课诊断"规则到底加载没"用的就是它)。
门禁:Stop hook 跑测试不过就 block;/goal 会话级收敛;TaskCompleted 挡任务假完成。
CI 断言claude -p --output-format stream-json 拿 NDJSON 事件流;--json-schema 强制结构化输出后直接 jq '.structured_output' 机器断言;--bare 保证每台机器行为一致。
Codex(2026-07 本机实测 codex-cli 0.142.5)
tracecodex exec --json 输出 NDJSON 事件流,实测事件链:thread.started → turn.started → item.started/completed → turn.completed;item.type 有 agent_message / command_execution(含 exit_code)/ file_change / reasoning / mcp_tool_call 等;turn.completed.usagereasoning_output_tokens 都有(文档还没写,实测为准)。-o 把最终结论单独落盘给脚本消费。解析注意:warning 也走 item.type:"error",别一律当失败。
门禁:hooks 2026-05-14 GA(10 个事件),Stop 返回 {"decision":"block"} 或 exit 2 → 带你的反馈自动续跑;/goal 已 stable——目标持久化进 SQLite,每轮续行时重新注入。
CIopenai/codex-action@v1,官方模式:只读 job 跑 codex 产出 patch 工件 → 独立有写权限的 job 应用。
调研现场的活教材:agent 的自述不是证据。备课时我们两次问 codex"你自己支持哪些能力"——放开工具时它跑去克隆自家源码逐行核对,直到上下文耗尽(12 分钟无产出);只凭记忆答时它把自家 best-of-N 的 flag 猜成 --best-of(实际是 --attempts)、还"不确定 hooks 是否已支持"(它自己每一轮都在触发 hooks)。这正是本课主旨的镜像:对 agent 的能力和产出,--help、feature flags、trace、源码才是事实源;它的自我报告只能当线索

落地任务:给下一次真机验证装上 harness(约 60 分钟)

就用你欠着的那次 KMP 真机验证做

写 VERIFY.md(15 分钟,只有你能写):5 个 Tab × 每个 3-5 条断言。每条必须是可检查的形式:动作 → 预期日志(tag+关键字)→ 兜底截图点。例:「Savior Tab 点赞 → logcat 出现 Savior:Digg count=+1 且 60s 内无重复请求日志 → step 截图」。写不出断言的项,说明你自己也不知道"对"长什么样——先补规格(第二课)。
装 trace + 让 agent 驱动真机(30 分钟):把上面的 PostToolUse JSONL hook 装上;然后派 agent:「按 VERIFY.md 逐 Tab 执行,用 device-control 操作、logcat 断言,每步在 RUN_JOURNAL.md 追加 步骤号|意图|动作|观察|判定,截图文件名带步骤号,任何断言失败继续跑完但判定记 FAIL」。
委派分析,你只审报告(15 分钟):跑完把 journal + trace 丢给分析 agent(用上面的模板),拿到"矛盾步骤清单"后,你只对清单上的项亲手复查。对比指标:这轮人工触屏次数 vs 上一次全人肉逐 Tab 点的次数——把两个数发回给我,这是本课是否成立的证据。

自测(先回忆,再点选)

1. agent 报告"重构完成、编译通过",此时正确的验收姿势是?

官方点名的失败模式 trust-then-verify gap:编译绿 ≠ 功能没坏,"问它是否确认"得到的还是宣称。正确姿势是官方那句 "Have Claude show evidence rather than asserting success"——日志断言、测试输出、真机 journal 这些证据,而不是它的自我评估。

2. Stop hook 门禁和 /goal 的本质区别是什么?

Stop hook 真的跑你的脚本(测试/lint),不过就 block,是确定性门禁;/goal 是每轮结束后由独立小模型看对话中已呈现的证据来评估条件是否达成——它不跑命令。所以硬标准(编译/测试)用 Stop hook,方向性收敛用 /goal,两者可叠加。

3. 真机验证的断言,日志和截图的正确优先级是?

日志能 grep、能机器断言、能进 trace 自动化;截图脆(分辨率/动画/时序都会误报)、必须人眼看。所以断言优先建在分层日志 tag 上,截图作为 UI 回归的兜底证据链——这也是你重构时"补分层日志 tag"真正的回报所在。

4. run journal 和 trace 的关系,正确的是?

trace 是机器记的"实际动作"(hook/NDJSON,agent 想瞒都瞒不掉),journal 是 agent 自己记的"意图与判定"(步骤号|意图|动作|观察|判定)。单看任何一条都不够——把两条对照,才能找到"判定说成功、证据说失败"的那一步,这正是分析 agent 的工作。

首选阅读(一篇就够)

Claude Code 官方 Best Practices — "Give Claude a way to verify its work" 章节(约 10 分钟)。四级验证梯度、verifier prompt 模板、"show evidence rather than asserting success" 的一手出处——2026 年官方把"验证"从社区经验升格成了产品方法论,这一章就是分水岭。

引用文献

🧠背诵区

点卡片翻面。记的是能用的判断核心,不是定义。

闪卡 1 / 核心姿势
agent 说"完成了",验收的核心口径是什么?官方的验证梯度有哪四级?
翻面
让它出示证据,而不是宣称成功(show evidence, not assert success);不能验证就不算完成。四级梯度:① prompt 里要求自验(靠自觉,会漂)② /goal(小模型评估)③ Stop hook 确定性门禁(脚本不过不许停)④ 独立 verifier(fresh context 只看 diff 和标准)。硬标准用 ③,脚本化不了的正确性用 ④。
闪卡 2 / 三件套
可观测性三件套是什么?trace 和 run journal 为什么必须两条都有?
翻面
确定性 trace(PostToolUse hook 落 JSONL / transcript 白嫖 / codex exec --json)② run journal(agent 每步记 步骤号|意图|动作|观察|判定,截图名带步骤号)③ 分析也委派(agent 找"判定与证据矛盾"的步骤)。trace 是机器记的动作、journal 是 agent 记的意图——对照两条独立证据链才能抓到"说成功、实失败"
闪卡 3 / 客户端特化
1 台真机的验证怎么从"逐 Tab 点"降到"只审报告"?三个要素。
翻面
VERIFY.md 断言清单(每 Tab:动作→预期日志→兜底截图;写不出断言=规格还没想清)② agent 用 device-control 驱动 + logcat 断言优先、截图兜底(分层日志 tag 是地基)③ journal+trace 丢给分析 agent 出矛盾清单,人只复查清单项。设备串行没法并行,就放流水线最后、只跑增量。
⏱ 间隔复习:明天扫一遍,3 天后再来。交织:翻一张第四课「上下文漂移」的旧卡——那课的 hook 是"拦截违规",这课的 hook 是"落盘证据",同一个机制的两副面孔。
🗣复述区

能讲清楚,才是真懂——比能回忆高一层。

复述任务
用一段话讲清楚:为什么"编译绿"不能作为 KMP 重构的验收标准?验证 harness 的三件套怎么把你从"逐 Tab 手点"里解放出来、而你仍然是最后拍板的人?
参考表述
编译只证明类型和依赖成立,证明不了运行时行为——点赞计数、面板参数、浮窗时序这些回归只在真机上暴露,所以"编译绿就算完成"正是官方点名的 trust-then-verify gap:把 agent 的宣称当成了证据。解法是装一套验证 harness:先用确定性 hook 把每次工具调用落成 JSONL trace(机器记的动作,agent 瞒不掉);再让 agent 按我写的 VERIFY.md 用 device-control 驱动真机,断言优先建在分层日志上、截图兜底,每步在 run journal 里记"意图|动作|观察|判定";最后把 trace 和 journal 丢给独立的分析 agent,对照两条证据链找出"判定与证据矛盾"的步骤。我从逐 Tab 手点降级为只审矛盾清单——动手的环节被委派了,但断言清单是我写的、可疑项是我复查的、发不发版是我拍板的:验证的执行可以自动化,验收的责任不能。

讲不顺的地方就是还没真懂的地方 —— 发给我,我帮你补上。