AI 智能体的可观测性:如何弄清智能体做了什么,以及应该记录什么
AI 智能体的可观测性,是指能够还原智能体做了什么、为何这样做以及有何证据支持的能力。它由两层构成:运行时(操作日志、追踪、检查输出)和过程(任务约定、验收标准、附带证据链接的报告)。缺少任何一层,智能体说的“完成”都毫无意义。
什么是 AI 智能体的可观测性
AI 智能体的可观测性,是指能够依据工件而非记忆,也不依据智能体的言辞,回答三个问题:它做了什么,为什么这样做,以及凭什么认为结果是正确的。如果智能体工作二十分钟后说“完成了,但有两个测试失败,可能是时序问题”,而被问到“涉及了哪些路径”时只回答“请让我看看代码”,那就谈不上可观测性。智能体并不笨,只是没有东西可以拿出来证明。
这个问题是系统性的。同一个智能体一周之内可能十次诚实地说“完成了”,而每次的含义都不同:有时是跑完了所有测试,有时只跑了一个,有时什么也没跑。项目存在得越久,每次新会话的代价就越高,因为每次都要从挖掘开始。
两层结构:运行时与过程
人们常把可观测性与日志混为一谈,但日志只是其中一半。
| 层 | 回答的问题 | 工件 |
|---|---|---|
| 运行时 | 发生了什么 | 智能体操作日志、追踪、测试和构建的输出、提交记录 |
| 过程 | 想要什么,为何这样做 | 任务约定(范围、验收标准)、评分细则、智能体报告 |
只有运行时:能看到什么失败了,却不知道目标是什么。只有过程:知道想要什么,却看不到为何没有成功。两者缺一不可。
为什么智能体自己做不到这一点
对智能体说“记录得详细些”是行不通的,原因有三点。
- 它不知道自己不知道什么。智能体不会记录它没有意识到重要的信号。
- 格式不固定。同一个智能体在一次会话中写得很详细,在另一次只写一行。无法比较各次会话,也无法统计汇总。
- 过程无法靠日志来覆盖。“为什么这样做”的答案存在于工作开始前创建的文档中,而不在命令的输出里。
因此,可观测性应当写入 harness——即智能体的工作环境:模板、hooks、脚本,以及强制的报告格式。
应当记录什么:最小集合
好的日志不是对一切的逐字记录,而是一组事实,依靠它们可以还原工作的过程,也可以统计数据。
- 标识符:会话 ID、任务或功能的 ID、智能体所用的模型。
- 有副作用的操作:运行了哪些命令,修改了哪些文件。读取文件通常不必记录。
- 检查结果:使用的检查命令、返回码、通过和失败的测试数量。
- 重试:某一步花了多少次尝试,每次尝试有何不同。
- 消耗:如果工具提供,记录 token 数量和耗时。
- 结论:任务是被接受、被拒绝,还是被放弃,以及原因。
不要记录:密钥、访问令牌、命令输出中的个人数据。日志可能会被公开共享,或者再次进入模型的上下文。
通过 hooks 记录 Claude Code 的操作日志
在 Claude Code 中,操作日志最适合用 hooks 来记录——这些命令由工具本身运行,而不是由模型运行,因此总能触发。hook 从 stdin 接收描述事件的 JSON,其中包括会话 ID、工具名称及其参数。下面是在 .claude/settings.json 中的配置示例:每次调用 Bash、Edit 或 Write 之后,都会向日志中追加一行:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Bash|Edit|Write",
"hooks": [
{ "type": "command", "command": ".claude/hooks/log-action.sh" }
]
}
]
}
}
#!/usr/bin/env bash
# .claude/hooks/log-action.sh — 每个操作一行 JSON
mkdir -p .agent/logs
jq -c '{ts: (now | todate), session: .session_id, tool: .tool_name,
cmd: .tool_input.command, file: .tool_input.file_path}' \
>> .agent/logs/actions.jsonl || true
JSONL 格式(每行一条 JSON 记录)的优点是便于用 jq 过滤,也便于导入任何分析系统。此外,Claude Code 自身会在 ~/.claude/projects/ 目录中本地保存完整的会话历史。如果需要查看完整的推理过程和输出,可以打开它;但要统计数据,自己维护的精简日志更方便。
追踪:每个会话一条 trace,每个任务一个 span
当智能体数量多,或者它们按计划运行时,文本日志就不够用了,需要汇总数据。这时可以使用常见的分布式追踪工具和 OpenTelemetry 标准。
- Trace——一次智能体会话。
- Span——会话中的一项任务。
- 嵌套的 span——验证步骤:构建、测试、端到端测试。
每个 span 的最少属性:功能 ID 和会话 ID、模型、检查结果、token 数、重试次数。这些数据可以在 Jaeger、Grafana Tempo、Honeycomb 中查看,并回答单个会话无法回答的问题:哪类任务最常需要重试,智能体在哪里消耗的 token 最多,哪次对 harness 的修改之后被接受的任务比例下降了。Claude Code 内置了通过 OpenTelemetry 导出指标和事件的功能,通过环境变量开启,具体细节请参阅该工具的文档。
用提交作为日志
最便宜、最可靠的日志您早已拥有——那就是 git。只需要让它可读。
- 一次提交,对应一项任务。这样
git log就与已完成的工作清单一致,撤销一项任务只需一次git revert。 - 提交信息中写明做了什么、如何验证。不要写“fix”,而要写“改了什么、为什么改、用什么命令验证”。
- 只有在检查通过之后才提交。这样历史中的每个节点都是可用的状态,
git bisect才能找到问题的源头。
fix(payments): 折扣舍入到分
折扣在 float 中计算,金额 999.99 时丢失了一分钱。
改为以整数分为单位进行计算。
已验证:make test(142 passed),make e2e checkout — 正常。
范围之外:报表中的舍入,已记入 BACKLOG.md。
智能体报告:用证据代替印象
如果智能体的最终消息具有固定格式,它本身也是可观测性的工件。把模板写入 AGENTS.md 或 CLAUDE.md:
## 任务结束时的报告
- 完成了什么:修改清单(文件 — 内容)
- 如何验证:确切的命令及其结果
- 未验证的内容及原因
- 与任务约定的偏差
- 需要人工回答的开放问题
核心规则:每一项论断都要有证据支撑——文件和行号、测试输出、日志中的记录。“看起来正常”和“我觉得没问题”不算报告。“未验证的内容”这一项尤其有价值,它诚实地展示了信心的边界。
工作前的约定,工作后的评分细则
过程层从工作开始之前就已存在。简短的任务约定(sprint contract)需要写明范围、验收标准、“范围之外”一节,以及由人决定的开放问题。“范围之外”这一节能避免一半关于结果的争论。
工作完成之后,依据评分细则评估结果,而不是凭印象。否则,两位经验丰富的评审者可能给出截然不同的结论,评估就变成了抽签。
| 维度 | 问题 | 证据 |
|---|---|---|
| 正确性 | 验收标准是否满足? | 测试输出、端到端测试 |
| 架构 | 模块边界是否得到遵守? | 文件:行号 |
| 测试 | 新行为是否被覆盖? | 新增测试的清单 |
| 错误处理 | 故障和边界情况是否得到处理? | 测试或文件:行号 |
| 可观测性 | 能否从日志中看出正在发生什么? | 日志片段 |
每个维度按 A/B/C/D 评分,没有证据链接的评分无效。简单的决策规则是:一个 D 或两个 C,就拒绝并列出需要改进的内容。同样的评分细则也可以交由独立的检查智能体来执行。
如何梳理智能体在一次会话中做了什么
如果没有报告,或者不信任报告,请按以下顺序检查工件。
- 工作区状态:
git status——哪些内容未提交,有没有垃圾文件。 - 历史:从会话开始的
git log --stat——有哪些提交和文件。 - 改动规模:
git diff --stat main...HEAD,然后逐一查看重要文件的完整 diff。 - 操作日志:运行过哪些命令,检查返回了什么。
- 重新验证:自己运行构建和测试。相信命令的输出,而不是对它的转述。
# 智能体在最近一次会话中运行了哪些命令
jq -r 'select(.tool=="Bash") | .cmd' .agent/logs/actions.jsonl | tail -n 30
# 哪些文件被修改得最频繁
jq -r '.file // empty' .agent/logs/actions.jsonl | sort | uniq -c | sort -rn | head
常见错误
- 什么都记录。吉字节的逐字记录没有人会读。只记录有副作用的操作和检查结果。
- 格式自由。如果日志的格式由智能体自行决定,就无法汇总。格式应由 harness 来规定。
- 相信没有命令输出的报告。“测试都通过了”而没有输出,只是一种意见。
- 盲目重试。不知道失败原因,智能体就会去修改随机的地方。应先要求给出带有日志链接的诊断,再允许修改。
- “以后再清理”。调试文件和未提交的改动会误导下一个会话:它无法分辨哪些是临时的,哪些是有意为之的。
小结
AI 智能体的可观测性由两层构成:运行时(通过 hooks 记录的操作日志、追踪、带有验证说明的提交)和过程(工作前的任务约定、工作后的评分细则,以及附带证据的报告)。这样,“完成”在每个会话中都意味着同一件事,而分析智能体的工作只需几分钟。我们在 harness engineering 课程中,结合一个端到端的支付后端项目,对这一主题进行了实践讲解。
常见问题
AI 智能体的可观测性与普通的监控有什么不同?
监控回答的是系统现在是否在运行。智能体的可观测性还必须回答智能体为何作出某个决定,以及结果有何证据支持,因此在日志之外还要加入任务约定、评分细则和报告。
Claude Code 把会话历史保存在哪里?
本地,位于 ~/.claude/projects/ 目录中,以 JSONL 文件的形式按会话保存。要做统计,通过 hooks 维护自己的精简日志更方便。
能相信智能体关于工作的报告吗?
只有当每一项论断都有可验证的证据支持时才能相信:命令的输出、文件:行号的引用、日志记录。关键的检查最好由您自己重新运行。
对于单个开发者,需要 Jaeger 或 Grafana 吗?
通常不需要:JSONL 日志、规范的提交和报告模板就足够了。当智能体数量增多或者它们定期无人值守地运行时,追踪系统才会物有所值。
如果智能体不按模板写报告怎么办?
让模板简短,并放在指令文件的开头;同时用机制来保证遵守,例如在结束时运行的 hook,或者一个独立的检查智能体,它会把没有报告的工作退回。