Skip to content

专题 05 调试、观测与链路追踪

本专题解决的是 AI Agent 工程里一个非常真实的问题:为什么系统一旦出错,就会进入“好像哪里都可能有问题,但又不知道先查哪里”的状态。

传统前端问题通常还能靠浏览器控制台、接口日志、埋点数据快速缩小范围;而 Agent 系统的问题往往跨越了 Prompt、检索、工具调用、模型输出、工作流状态和前端展示多个层次。如果没有一套清晰的观测与调试方法,系统越复杂,定位故障就越慢。

1. 为什么 Agent 系统更需要可观测性

前端系统里,一次异常往往发生在比较明确的层次:

  • 页面渲染错了。
  • 某个请求失败了。
  • 某个状态更新丢了。

但 Agent 系统的问题链路更长:

  • 用户输入本身可能有歧义。
  • Prompt 组织方式可能引导错了方向。
  • 检索没召回到对的证据。
  • 工具调用可能超时、失败或返回脏数据。
  • 模型可能误选工具或输出格式漂移。
  • 前端可能把中间状态展示得过于模糊,导致用户感知为“卡住了”。

这意味着你不能只看最终输出,而要能够还原“这次回答是怎么一步一步形成的”。

2. 可观测性到底要观测什么

建议把观测拆成四层:

  • 输入层:用户输入、上下文槽位、检索证据、工具参数。
  • 决策层:模型选择了什么路径、为什么走这条路径。
  • 执行层:工具调用结果、工作流节点状态、重试与回退。
  • 输出层:最终回答、结构化字段、引用、前端展示状态、用户反馈。

只有四层都可见,问题才能被定位,而不是靠猜。

3. 用前端视角理解 tracing

前端工程师通常已经熟悉以下调试方式:

  • network 面板看请求链路。
  • error monitoring 看异常栈。
  • 埋点分析看用户路径。
  • source map 回溯前端代码位置。

在 Agent 系统里,tracing 可以类比成“把一次用户请求从输入到输出的整个执行过程串起来”。 一个完整 trace 最好能回答:

  • 这次请求的唯一 ID 是什么。
  • 触发了哪些模型调用。
  • 调用了哪些工具。
  • 每一步花了多长时间。
  • 哪一步失败了。
  • 最终返回给用户的是什么。

4. 最应该先观测的 8 类信息

4.1 请求标识

每次用户请求都应该有唯一请求 ID 或 trace ID。 没有它,跨服务排查几乎不可能高效进行。

4.2 Prompt 版本

不要只记录“调用了模型”,还要记录:

  • 系统指令版本。
  • 模板版本。
  • 关键参数。
  • 是否开启了某些策略开关。

否则你很难知道问题是代码逻辑导致,还是 Prompt 版本回退导致。

4.3 检索结果摘要

不要把整个大上下文原样堆到日志里,但至少要记录:

  • 召回了哪些文档。
  • top-k 是多少。
  • 最终被送进模型的片段有哪些。
  • 是否经过重排和压缩。

4.4 工具调用日志

至少记录:

  • 工具名。
  • 参数摘要。
  • 是否成功。
  • 耗时。
  • 错误类型。
  • 是否重试。

4.5 工作流节点状态

如果系统用了工作流编排,建议每个节点都有明确状态:

  • pending
  • running
  • succeeded
  • failed
  • cancelled
  • waiting_human

4.6 输出结构校验结果

模型说“我输出了 JSON”并不代表它真的可用。 建议记录:

  • 是否解析成功。
  • 哪个字段缺失。
  • 是否触发了 fallback。
  • 是否转成了人工复核。

4.7 前端感知状态

从用户体验角度,前端状态同样重要:

  • 是否正在流式输出。
  • 是否处于等待确认。
  • 是否显示了引用与依据。
  • 用户是否主动取消了流程。

4.8 用户反馈

很多问题不是日志里直接能看出的,而是从用户反馈暴露的:

  • “回答不准确”
  • “引用不匹配”
  • “流程卡住了”
  • “明明已经执行成功,但页面还在转圈”

这些反馈应该能和 trace 串起来。

5. 一套推荐的调试顺序

第一步:先判断问题属于哪一层

建议先粗分为:

  • 输入问题。
  • 检索问题。
  • 工具问题。
  • 模型决策问题。
  • 输出解析问题。
  • 前端展示问题。

如果一开始就陷入所有层都看,很容易越查越乱。

第二步:固定复现场景

调试前先固定:

  • 用户输入。
  • Prompt 版本。
  • 模型版本。
  • 检索配置。
  • 工具版本。

否则你今天复现的是 A 问题,明天环境变了又变成 B 问题。

第三步:查看完整 trace

排查顺序建议是:

  1. 用户问题进入后被如何标准化。
  2. 检索返回了什么。
  3. 模型拿到了哪些上下文。
  4. 是否发生了工具调用。
  5. 工具结果是什么。
  6. 输出是否通过结构校验。
  7. 前端最终展示了什么。

第四步:对失败样本归类

不要每个问题都单独处理,建议做失败归类,例如:

  • 检索召回为空。
  • 检索召回正确但生成错误。
  • 工具成功但结果解释错误。
  • 输出格式漂移。
  • 前端状态未同步。

归类后才更容易集中修一类问题。

6. 前端工程师最能发挥价值的部分

6.1 设计前后端一致的状态模型

前端最怕的是:

  • 后端说流程完成了,但前端还在 loading。
  • 后端说等待确认,前端却没有展示确认入口。
  • 后端已 fallback,前端还当正常结果展示。

所以需要设计统一状态枚举和清晰的 UI 映射关系。

6.2 把黑盒变成半透明

不是所有内部推理都要展示给用户,但至少应该展示关键状态和关键依据。 前端可以负责把:

  • 当前步骤
  • 关键引用
  • 工具执行摘要
  • 风险提示 合理地展示出来。

6.3 用埋点补足“技术成功但体验失败”的问题

有些系统日志看起来都正常,但用户体验还是差。 比如:

  • 平均 15 秒才有第一段可见反馈。
  • 用户在等待确认时不知道要点击什么。
  • 用户频繁在某一步取消。

这类问题很适合通过前端行为埋点发现。

7. 一个推荐的日志字段清单

可以先从最小集合开始:

  • trace_id
  • session_id
  • user_id 或匿名标识
  • prompt_version
  • model_name
  • retrieval_docs
  • tool_name
  • tool_status
  • node_status
  • latency_ms
  • fallback_triggered
  • need_human_review
  • ui_status
  • user_feedback

日志字段命名一旦统一,后续排查效率会大幅提高。

8. 一个实战排查案例

假设你有一个企业知识问答 Agent,用户反馈“明明引用了制度文档,答案还是错的”。 推荐排查顺序如下:

  1. 看 trace 确认检索是否真的召回了正确文档。
  2. 看最终注入模型的上下文里是否还保留了关键条款。
  3. 看是否有无关证据挤掉了关键证据。
  4. 看模型输出是否误解了条款。
  5. 看前端展示的引用是否和后端返回一致。

这样可以快速区分:

  • 是检索问题。
  • 是上下文压缩问题。
  • 是生成解释问题。
  • 还是前端展示错位问题。

9. 常见失败模式

9.1 只记录错误,不记录成功路径

如果没有成功样本做对照,你就很难知道失败样本到底偏离了哪里。

9.2 日志太碎,没有 trace 串联

每个服务各打一段日志,但没有统一 trace_id,最终还是很难排查。

9.3 只看后端,不看前端状态

很多“系统卡住”的问题其实不是模型卡,而是前端状态没更新或事件没收敛。

9.4 记录太多原始敏感内容

观测不是无上限收集。 企业环境下要特别注意脱敏、最小化记录和权限控制。

9.5 没有失败分类体系

团队每次都靠“临时口头描述问题”,问题就很难被系统性修复。

10. 验证方式

你可以用以下问题验证自己的观测体系是否成型:

  • 一次失败请求能否在 5 分钟内定位到问题层级。
  • 是否能从 trace 中完整还原一次请求的执行路径。
  • 是否能区分检索失败、工具失败、生成失败和展示失败。
  • 发布后质量回退时,是否能快速定位是哪次变更引入。
  • 用户投诉是否能绑定到具体 trace 做复盘。

11. 可复用模板

11.1 失败排查记录模板

text
trace_id:
问题现象:
影响范围:
所属层级:输入 / 检索 / 工具 / 模型 / 输出 / 前端
复现条件:
根因分析:
修复动作:
是否加入回归集:是 / 否

11.2 Trace 事件模板

text
时间:
trace_id:
节点:
动作:
输入摘要:
输出摘要:
耗时:
状态:成功 / 失败 / 回退 / 等待确认

11.3 前端体验埋点模板

text
事件名:agent_step_visible
触发时机:某一步状态首次对用户可见
关键字段:trace_id / step_name / duration_ms / user_action

12. 面试表达

你可以这样讲:

“我在做 Agent 工程时,很早就把可观测性当成系统设计的一部分,而不是上线后补日志。我的做法是把一次请求拆成输入、决策、执行、输出四层,并用 trace_id 串起检索、工具调用、工作流节点和前端状态。这样出了问题时,我能很快定位到底是检索召回、模型决策、工具异常,还是 UI 状态同步出了问题。”