第 21 章 排错与调试实战

0 阅读9分钟

第 21 章 排错与调试实战

本章要解决的问题

线上 Agent 出问题了,只知道「答得不对」——怎么一步步定位到是提示词、工具、还是模型的问题?

章节大纲

  • 21.1 Agent 全链路追踪(Trace 到每步思考/工具调用)
  • 21.2 常见故障分类:幻觉、循环、上下文溢出、工具错配、成本爆炸
  • 21.3 线上问题定位 SOP
  • 🛠 解决方案:Agent 故障排查手册(40+ 真实问题对照表)

21.1 Agent 全链路追踪

21.1.1 为什么 Agent 排错比传统软件难 10 倍

传统软件的 bug 是可复现的(同一输入 → 同一错误),Agent 的故障是概率性的:这次答错,下次可能对;换个人问可能就正常。没有追踪,你甚至不知道"错的那次"发生了什么。

第 6 章强调过"每步留痕"——本章把它升级为生产级的全链路追踪

21.1.2 追踪的最小数据模型

每个 Agent 任务(trace)包含一组事件(span),每个事件记录一次调用:

图 1:Trace 数据模型

图 1:Trace 数据模型

TRACE = {
    "trace_id": "t-20260807-001",
    "task": "查询订单 A123 物流",
    "user_id": "u-1001",
    "started_at": "...", "ended_at": "...",
    "total_cost": 0.0123, "total_tokens": 3200,
    "spans": [
        {
            "span_id": "s1",
            "type": "llm",                # 模型调用
            "model": "deepseek-chat",
            "input": "系统提示词 + 历史…",    # prompt 摘要
            "output": "调用工具 query_logistics",
            "tool_calls": [{"name": "query_logistics",
                            "args": {"order_id": "A123"}}],
            "tokens": 1200, "cost": 0.004, "latency_ms": 850,
        },
        {
            "span_id": "s2",
            "type": "tool",               # 工具执行
            "tool": "query_logistics",
            "args": {"order_id": "A123"},
            "result": {"status": "shipped", "eta": "明天"},  # 成功
            "latency_ms": 120,
        },
        {
            "span_id": "s3",
            "type": "llm",               # 最终回答
            "input": "...工具结果回填...",
            "output": "您的订单 A123 已发货,预计明天送达。",
            "latency_ms": 600,
        },
    ],
}

排错的本质 = 顺着 trace 找到"哪个 span 出了问题"——是模型决策错(s1)、工具执行失败(s2)、还是最终生成错(s3)。

21.1.3 追踪工具的落地

层级工具说明
快速起步自研 JSON 日志每 span 一条日志,零依赖
专业方案Langfuse(推荐)LLM 专用:trace/评测/成本面板
生态绑定LangSmithLangChain 生态
统一监控OpenTelemetry与现有监控体系统一

落地顺序:先自研日志跑起来 → 数据积累后接 Langfuse 可视化——别一上来就上重工具。

21.2 常见故障分类

21.2.1 五大故障族

生产 Agent 的故障可以归为五类,先分类再定位

图 2:五大故障族

图 2:五大故障族

故障族表现高频根因
幻觉编造事实/数据/参数知识缺失硬答、温度高、无约束(呼应 13/17 章)
循环反复调用、无进展无停止条件、无进展检测(呼应 6 章)
上下文溢出答非所问、爆窗历史无压缩、结果全量回填(呼应 3 章)
工具错配调错工具/参数乱填Schema 描述弱、温度高(呼应 5/14 章)
成本爆炸单任务费用异常无限重试、无预算封顶(呼应 23/24 章)

21.2.2 每种故障的 trace 特征

看 trace 就能初步判断故障族

故障trace 特征
幻觉无工具调用 span,模型直接回答;或答案无法对应任何工具结果
循环多个相同的 tool_calls span 重复出现;result 相同
上下文溢出单个 llm span 的 input 特别长(接近窗口上限)
工具错配tool_calls 里的工具名/参数与需求不符
成本爆炸spans 数量异常多;cost 累计超限

排错口诀:先看 trace,再猜原因——trace 会告诉你"哪里错了",别对着线上输出瞎猜。

21.3 线上问题定位 SOP

21.3.1 五步定位流程

[1 收集现场] 找到出问题的 trace_id(用户报错时间 / 日志检索)
   ↓
[2 看全貌]   整个 trace 有多少 span?哪一步开始不对劲?
   ↓
[3 定位环节] 问题在 模型决策 / 工具执行 / 上下文组织 / 最终生成?
   ↓
[4 复现验证] 用同一输入 + 同版本参数跑本地,确认可复现率
   ↓
[5 修复回归] 修复后跑评测集回归(第 20 章),确认无退化

图 3:线上定位五步 SOP

图 3:线上定位五步 SOP

21.3.2 定位到具体环节后的处理

定位结果下一步
模型决策错(该调工具没调)检查工具描述/Schema(第 5、14 章)
工具执行失败检查工具代码、权限、上游系统
上下文组织问题检查历史压缩/按需投喂(第 3 章)
最终生成错检查提示词约束、温度(第 4、24 章)
成本异常检查重试/循环/缓存(第 23、24 章)

21.3.3 可复现率:概率故障的验证方法

Agent 故障往往不能 100% 复现,用可复现率判断:

图 4:可复现率判断

图 4:可复现率判断

同一输入跑 N 次(N≥10):
  失败次数/N = 可复现率
  ├─ 高复现率(>70%)→ 确定性 bug,对照 trace 排查
  └─ 低复现率(<30%)→ 概率性故障,检查温度/随机性/竞态

21.4 真实排错案例复盘(3 个完整案例)

把前面所有方法串起来,用 3 个真实场景演示"从现象到根因到修复"的完整路径。

案例一:客服 Agent"答非所问"(高复现率)

现象:某客服 Agent 对"物流到哪了"的回答经常驴唇不对马嘴,复现率 80%。

排查过程(五步 SOP)

[1 收集现场] 找到 trace_id,看该任务全部 span
[2 看全貌]   发现只有 2span1 次模型调用 → 直接回答(没调工具)
[3 定位环节] 问题在"模型决策"——该调物流工具但没调
[4 复现验证] 同一问题跑 10 次,8 次没调工具(复现率 80%[5 修复]     检查工具 Schema 描述

根因query_logistics 工具的 description 写的是"查询订单",模型不知道"物流进度"应该用它。

修复:描述改为"查询订单物流状态。当用户询问发货/配送/物流/到哪了时使用。"——重跑评测,工具调用率从 20% 升到 95%。

教训:高复现率"该调没调",先看工具描述是否覆盖用户问法(第 5/14 章:描述是工具的灵魂)。

案例二:Agent 偶发死循环(低复现率)

现象:某个 Agent 偶尔(约 15% 概率)陷入循环,反复调用同一个工具,直到超时。

排查过程

[1 收集现场] trace 显示 8+ 个相同 tool_calls span,结果相同
[2 看全貌]   循环特征明显:同一工具、同一参数、结果不变
[3 定位环节] 问题在"循环控制"——无进展检测缺失
[4 复现验证] 低复现率(15%),与温度/seed 相关
[5 修复]     加无进展检测 + 调用去重

根因:工具返回"查询中"这类中间状态,模型收到后不知道该不该继续,偶尔"原地打转"。

修复

# 1. 相同调用去重(第6章)
def dedup(tool_calls, history):
    sig = (call.name, json.dumps(call.args, sort_keys=True))
    return sig not in history   # 相同签名不重复执行

# 2. 无进展检测(第6章)
def no_progress(history, threshold=3):
    recent = [h["result"] for h in history[-threshold:]]
    return len(set(recent)) == 1   # 连续结果相同 → 终止

教训:低复现率 + 相同调用重复 = 典型的"无进展循环",用无进展检测根治(第 6 章 6.3.2)。

案例三:RAG 问答"查得到但答不对"(高复现率)

现象:知识库问答,检索命中率 90%,但答案经常漏掉关键信息,复现率接近 100%。

排查过程

[1 收集现场] trace 显示检索命中了正确文档(命中率 OK)
[2 看全貌]   上下文里有 8 个 chunk,关键信息在第 6[3 定位环节] 问题在"上下文组织"——Top-K 太大,关键信息被淹没
[4 复现验证] 稳定复现(确定性高)
[5 修复]     缩小 Top-K + 加重排

根因:Top-8 全塞进上下文,模型注意力被无关 chunk 稀释(第 3 章注意力稀释)。

修复:混合检索召回 Top-20 → Cross-Encoder 重排取 Top-3(第 8 章)——只喂精排后的 3 个块,答案完整率显著提升。

教训:检索命中率高但答不对 → 问题在"上下文组织"而非"检索"——用第 8 章定位口诀"先检索、后生成"判断。

案例复盘总结

案例复现率故障族关键排查点修复
答非所问80%工具错配trace 发现"该调没调"改工具描述
偶发循环15%循环相同调用重复无进展检测
查得到答不对100%上下文命中率高但组织差重排取 Top-3

三个案例的共同点:都是"先看 trace 定位环节 → 再用对症工具修复"——没有 trace 全靠猜,有了 trace 全是套路。

🛠 解决方案:Agent 故障排查手册(40+ 真实问题对照表)

幻觉类(10 例典型)

现象根因修复
编造订单号无工具/参数幻觉强制走工具 + 参数校验(5 章)
编造政策条款知识缺失硬答接 RAG + "没有就说没有"(8 章)
编造数据来源无约束加"仅基于材料"约束 + 温度调低
反思修不好幻觉知识型错误反思修疏忽,幻觉用工具/检索(13 章)

循环类(8 例典型)

现象根因修复
反复调同一工具无进展检测相同签名去重 + 无进展终止(6 章)
计划来回改重规划无上限重规划上限 + 历史去重(15 章)
反思不停无停止条件三停止条件(13 章)
步数不够就超时任务复杂调 max_iterations + 查任务拆解

上下文类(8 例典型)

现象根因修复
聊几轮失忆无压缩摘要 + 滑动窗口(3 章)
答非所问注意力稀释指令前置 + 裁剪
爆窗 400 错误超窗口压缩 + 精简回填
答案不完整输出预留不足预留 20% + max_tokens(24 章)

工具类(8 例典型)

现象根因修复
调错工具描述不清重写描述(14 章)
参数乱填温度高/Schema 弱低温度 + 严格 Schema(5 章)
一直失败无熔断重试上限 + 熔断(24 章 B1)
注入调危险工具无输入过滤注入检测 + 敏感确认(17/22 章)

成本类(6 例典型)

现象根因修复
单任务成本飙升无限循环停止条件 + 无进展检测(6 章)
重试成本爆炸无上限重试 2~3 + 熔断(5 章)
缓存失效成本高未开缓存Prompt 缓存 + 结果缓存(23 章)

实战提示

  1. 排错先看 trace:顺着 span 找"哪里开始不对劲",别对着输出猜。
  2. 故障先分类:幻觉/循环/上下文/工具/成本五大类,先归类再定位。
  3. 可复现率说话:概率故障跑 10 次算复现率,别被单次复现误导。
  4. 修复必回归:改完跑评测集(第 20 章),防止修好 A 弄坏 B。
  5. 排查手册当团队资产:每个新故障沉淀进手册,越用越全。