发布时间:2026-09-4
标签:AI Agent|工程实践|Trace|可观测性|调试


上一篇我讲了调试循环,说"要先看 Trace,再定位节点"。

但"看 Trace"三个字说起来轻松,真到动手时,很多人面对一长串执行记录,不知道从哪看起。

这一篇,我拿一次真实的失败执行——Run #1827——从头到尾读一遍。

不是讲"Observability 是什么",而是演示一次完整的"破案"。


系列导航


问题背景

这是第五阶段的第七篇。前面建立了失败分类(第 40 篇)和调试循环(第 41 篇),这一篇是它们的"实战演示"——完整还原一次失败执行的 Trace,逐节点找到它为什么失败。

先澄清一个概念边界:这一篇不讲"怎么搭 Observability 系统"(那是第 28 篇、也是这个系列第四阶段已经讲过的事)。这里讲的是更细的事:当你手里已经有了一份 Trace,你怎么从里面读出根因。

Trace 的价值,一句话:它是 Agent 的"飞行黑匣子"。Agent 错了,你去翻黑匣子,就能看到它在坠毁前每一步做了什么决定。


错误尝试:对着 Trace 干瞪眼

我第一次看 Trace 的时候,最大的困惑是:信息太多了,不知道该看哪。

一份完整的 Trace,可能有几十个节点:LLM 的思考、工具的选择、工具的参数、工具的返回、中间的状态、最终的推理……如果从头到尾顺着读,很容易被淹没,最后得出一个模糊的结论:"大概是他想歪了吧。"

这种"读法"是无效的。它和"对着代码干瞪眼"一样,没有章法。


关键观察:读 Trace,要顺着"错误传播链"找

后来我总结出一个读 Trace 的方法论:不要顺着时间读,要顺着"错误是从哪一步开始传播的"读。

一次 Agent 执行,本质是一条因果链:Planner 的假设 → 影响工具选择 → 影响拿到的数据 → 影响推理 → 影响结论。错误往往不是最后一步才出现的,而是在中间某一步埋下,然后一路传染到结尾。

所以读 Trace 的正确姿势是:从最后一步的错误结论,往上倒推,找到第一个"不对劲"的节点。 那个节点,才是根因所在。

核心洞察:

Trace 不是给系统看的,是给你事后破案用的——每个错误结论背后,都有一条可回溯的因果链。


最终方案:逐节点解剖 Run #1827

下面是 Run #1827 的完整 Trace(我做了结构化整理,保留了关键节点)。用户的问题是:"这个项目里支付相关的逻辑在哪?"

[Run #1827] query="支付相关逻辑在哪?"

节点1  Planner(思考)
  "用户想了解支付逻辑,我应该先看看最近的代码改动,
   了解支付功能最近的开发情况。"
  ↓ 决定调用:git_log()

节点2  Tool Call: git_log()
  参数:{}
  返回:最近 20 条提交记录(多为 README 更新、依赖升级)

节点3  Reasoning(思考)
  "从提交记录看,没有明显的支付相关改动,
   说明支付功能可能比较稳定,改动不多。"

节点4  Tool Call: read_file(payment.py)
  参数:path="payment.py"
  返回:文件不存在(404 报错)

节点5  Reasoning(最终结论)
  "该项目支付相关逻辑由 payment_process() 函数实现,
   位于 payment.py 中,负责订单支付与回调。"
  (注:payment.py 根本不存在,payment_process() 也是编的)

现在,顺着"错误传播链"倒推:

最后一步(节点5)错了吗? 错了。它下了一个无中生有的结论(Reasoning Error)。

为什么会无中生有? 因为节点4 读文件失败了(文件不存在),但 Agent 没有把这个"失败"当成"线索不存在",反而在节点5 硬编了一个结论出来。

为什么它会去读一个不存在的 payment.py? 因为节点1 的 Planner 从一开始就做了个错误假设——它决定用 git_log 去"了解支付功能的改动",而不是用 grep 去"搜索支付关键字"。方向一错,后面的读文件、下结论就全歪了。

根因在哪一层? 两个:

  1. Planning Error(节点1):应该先 grep 搜代码定位,却选了 git_log 看历史。
  2. 缺失的验证环节(节点4→5):工具返回"文件不存在"后,没有任何节点拦截,错误一路传染到了最终结论。

代码或配置示例

找到根因后,修复也分两层——这正是上一篇 Debugging Loop 的落地:

# 修复1:改 Tool 描述,纠正 Planner 的工具选择(治 Planning Error)
# 让 grep 明确"定位代码位置用我",git_log 明确"看历史才用我"

# 修复2:加验证节点,拦截错误传播(治"缺验证环节")
def verify_tool_result(tool_name, result, state):
    """工具返回'失败/不存在'时,必须回炉,不许带病下结论"""
    if "not found" in result or "不存在" in result:
        state.pending.append("该线索不存在,换关键词或工具重查")
        return Action(retry=True)
    return Action(proceed=True)

第二段代码是这次修复的关键:它在"工具返回"和"最终推理"之间加了一道闸。从此,工具说"文件不存在",Agent 就必须回头重查,而不是硬着头皮编。

这也印证了第 38 篇画架构时埋的那个伏笔——Reviewer 节点的必要性,在 Run #1827 里第一次被真实地撞了出来。


设计权衡

候选方案优点缺点为什么不选
只看最终结论只能看到"错了",看不到"怎么错的"无法定位根因
顺着时间读完整 Trace信息过载,容易被淹没抓不住错误传播链
从结论倒推传播链直击根因需要先理解执行结构顺着因果链,定位第一个错点

一个关键提醒:倒推法要求你的 Trace 记录了每个节点的输入输出。 如果你现在的日志只记了"最终答案",那这一篇的方法论就用不上——你需要先回到第 27 篇,把日志补全,再谈调试。


总结

✅ 读 Trace 的正确姿势:从错误结论倒推,找第一个"不对劲"的节点。
✅ Run #1827 的根因有二:Planning Error(错选 git_log)+ 缺失验证节点(错误一路传染)。
✅ 修复分两层:改 Tool 描述治标,加验证节点治本。
✅ 铁律:Trace 是给你事后破案用的,每个错误结论背后都有可回溯的因果链。
✅ 这一篇也印证了第 38 篇"Reviewer 必要性"的伏笔。


参考资料

  • LangSmith Trace 视图文档 → 为什么引用:结构化 Trace(节点输入/输出)是倒推法的前提。
  • OpenTelemetry 在 AI 领域的 Span 设计 → 为什么引用:理解"节点"作为 Trace 最小单元,是读 Trace 的基础。

系列导航

本文是 [AI Agent 工程实践] 系列的第 42 篇。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐