跳到主要内容

为什么在 AI Agent 出错时,你现有的可观测性栈无法救场

· 阅读需 14 分钟
Tian Pan
Software Engineer

你的 Datadog 仪表板显示零错误。延迟正常。所有服务都返回 HTTP 200。与此同时,你的 AI agent 刚刚在错误的时区预订了一个会议,幻觉了一个客户的订单历史,并为此烧掉了 4 美元的 token。

这正是让 agent 可观测性变得异常困难的原因:你现有的指标几乎无法告诉你 agent 是否真的在正常工作。

传统的分布式追踪建立在关于软件如何失效的一系列假设之上。LLM agent 违反了所有这些假设,而“我的基础设施是健康的”与“我的 agent 做出了正确的事情”之间的差距,正是大多数调试痛苦的根源。

为什么现有的追踪方式对 Agent 失效了

微服务的分布式追踪假设请求路径是确定性的且无状态的。请求从边缘进入,遵循一个可预测的调用图,并以一组已知的结果退出。你可以设置基准延迟阈值,定义错误代码,并对偏差发出警报,因为路径是可枚举的。

Agent 打破了每一个假设。

同样的用户输入在每次调用时都可能触发不同的工具调用序列、不同的检索结果和不同的推理链。没有一个规范的路径可以用来进行对比(diff)。一个从内存中检索上下文的 agent 可能在周一执行三个步骤,在周五执行七个步骤 —— 两者都是正确的,且都返回 HTTP 200。

第二个不同点在于,agent 有两种截然不同的失败模式,而传统系统只有一种。基础设施故障 —— 超时、API 错误、连接重置 —— 的表现与普通的分布式系统故障一致,你现有的工具可以很好地处理它们。但 agent 还有 认知失败(cognitive failures):幻觉、错误的工具选择、误解的检索结果、错误的步进规划。这些永远不会体现为错误。Agent 带着 2xx 状态码自信地返回响应,你的仪表板保持绿色,但输出却是错误的。

基础设施指标因缺失而具有误导性。一个陷入推理循环的 agent,用略有不同的参数重试相同的工具调用,在毫无进展的同时烧掉你的 token 预算,这在延迟图表上看起来与健康的 agent 毫无区别。

在多 agent 系统中,情况会进一步恶化。故障发生在移交边界 —— 当不完整的上下文、陈旧的记忆或模糊的中间结果在 agent 之间传递时。没有一个单一的服务对故障负责。它是交互过程中产生的涌现属性。

核心重构是:agent 追踪是关于调试决策,而非请求路由。 可观察的人造物是 agent 做出的选择序列,而不是其调用图的形状。

一个 Agent 追踪实际上长什么样

有效的原语是执行树:一个层级结构,其中每个节点都是一个具名的、有时间记录的操作。在 agent 上下文中,这意味着 LLM API 调用、工具调用、向量数据库查询、内存读取以及 agent 间的移交 —— 所有的父子关系都反映了每一个步骤是如何触发下一步的。

一个典型的调研 agent 追踪可能是这样的:根跨度(span)覆盖整个任务(比如 1.8 秒)。在它之下,一个嵌入(embedding)调用紧接着一个向量搜索来检索上下文。然后第一个 LLM 调用决定使用两个工具 —— 网页搜索和计算器 —— 每个都有自己的子跨度。第二个 LLM 调用将所有内容合成最终答案。

这种结构能告诉你延迟直方图无法提供的信息:哪一步增加了延迟,agent 是否使用了它应该使用的工具,以及它在推理与获取数据上分别花了多少时间。当追踪结果出错时 —— 比如检索跨度返回了六个月前的文件 —— 你可以清楚地看到现实是从哪里开始偏离意图的。

OTel 社区已经就 LLM 跨度的 gen_ai.* 属性命名空间达成了共识(截至 2026 年初仍处于实验阶段,未来可能会有 schema 变更)。关键属性包括提供商和模型、操作类型、拆分为输入和输出的 token 计数以及结束原因。跨度事件 —— 用户消息、助手响应、工具结果 —— 被附加到跨度上,而不是作为属性存储,因为内容可能很大,而且你通常希望控制是否导出这些内容。OTel 的建议是仅在受控环境中捕获消息内容,并设有显式的启用开关。

在多 agent 系统中,这种层级结构变得更有趣。单个用户请求可能会产生一个横跨请求管理服务、agent 编排器、LLM 推理路由器以及一个或多个 MCP 工具服务器的追踪。每一次跳跃都是它自己的一组跨度。关键的工程挑战是上下文传播:每个出站调用都需要携带父追踪上下文(通过 W3C traceparent 头部),以便跨服务的跨度可以组装成一棵树。像 LangGraph、LlamaIndex 和 OpenAI Agents SDK 等主流 LLM 框架现在默认发出符合 OTel 标准的追踪,但 MCP 服务器和自定义工具实现通常需要手动打点。

究竟应该衡量什么

鉴于你正在调试的是决策过程而非请求路由,真正重要的信号类别与传统服务有所不同。

任务完成质量是最关键且最难收集的指标。这不仅仅是“HTTP 请求是否成功”,而是“Agent 是否实现了用户的意图”。这需要人工审查、LLM 作为评审 (LLM-as-judge) 打分,或者在你的评估流水线中内置特定任务的成功标准。工具调用成功率是一个替代指标,但它包含噪声 —— 工具可能会返回数据,但 Agent 随后可能会误解这些数据。

按输出 Token 归一化的延迟是思考速度的正确方式。如果模型产生了 2,000 个输出 Token,那么 10 秒的响应时间是可以接受的;但如果只产生了 50 个,那就是出问题的信号。静态延迟阈值对于 LLM 工作负载具有误导性。首个 Token 时间 (Time-to-first-token) 对于流式应用来说是一个独立且通常更重要的指标,因为它决定了感知的响应速度。

每个成功任务的 Token 成本比单次请求的成本更有意义。一个因为错误的工具路由而需要重试三次的任务,其成本是应有成本的三倍,但它在账单上可能仍显示为一个计费结果。如果你只在请求级别跟踪成本,你就会忽略 Agent 循环带来的放大效应。

每个任务的步骤数是一个非常有用的异常信号。如果你的 Agent 通常在 4 个步骤内完成任务,而突然平均达到 8 个步骤,那么说明某些地方退化了 —— 可能是指令、工具、检索或模型出了问题。步骤数分布为你提供了一个纯延迟和错误指标会遗漏的预警信号 (Canary)。

记忆和检索质量通常不被衡量,但往往是失败的源头。检索到的上下文是否真的出现在 Agent 的推理中,记忆读取是否返回了陈旧内容,以及 Agent 的最终答案是否基于检索到的内容 —— 这些都是需要特定检测手段的独立失败模式。

Agent 实际失败的九种方式

Microsoft Research 最近通过研究三个基准测试中的 115 个带标注的 Agent 失败轨迹,发布了一个系统性的失败分类法。这些类别值得内化,因为它们对应着不同的调试策略:

  1. 计划遵循失败 (Plan adherence failure) —— Agent 偏离了实际上正确的计划
  2. 虚构信息 (Invented information) —— 幻觉出的事实、工具输出或追踪记录中不存在的上下文
  3. 无效调用 (Invalid invocation) —— 调用工具时参数错误或处于错误的状态
  4. 误解工具输出 (Misinterpreted tool output) —— 工具返回了正确数据,但 Agent 理解错了
  5. 意图与计划不一致 (Intent-plan misalignment) —— 计划在语法上有效,但无法实现用户的目标
  6. 意图说明不足 (Under-specified intent) —— 模糊的输入导致下游产生复合错误
  7. 意图不支持 (Intent not supported) —— Agent 尝试执行超出其能力范围的任务
  8. 护栏触发 (Guardrail trigger) —— 策略执行中断了运行(可能是正确的,也可能是误报)
  9. 系统故障 (System failure) —— 执行过程中的基础设施故障

第 2 到第 5 类故障对于基础设施监控是不可见的。它们需要查看 Agent 的推理内容,而不是基础设施的运行状况。微软的 AgentRX 框架针对这些情况进行自动化的故障定位,结果显示比基准提示词方法在故障定位准确度上提升了 23.6%。

了解失败属于哪个类别还能告诉你应该关注哪里:类别 1 和 4 通常指向提示词工程或工具描述问题;类别 2 指向接地性 (Grounding) 或检索质量不足;类别 3 指向工具 Schema 或能力边界问题;类别 8 可能表示护栏过于激进或需要改进。

选择适合你场景的工具

LLM 可观测性市场正在迅速扩张,根据你的具体情况,权衡利弊会有很大不同。

对于希望完全掌控数据且不希望被供应商锁定的团队,Langfuse (MIT 开源协议,可自托管) 和 Arize Phoenix (开源,本地运行) 是自然的选择。Phoenix 在开发阶段特别有用 —— 它可以在 Jupyter notebook 中运行,无需外部依赖,并专注于 RAG 和检索调试。Langfuse 在生产环境下具有更强大的多轮会话跟踪功能。

对于已经在使用 LangChain 或 LangGraph 的团队,LangSmith 是阻力最小、原生集成最好的路径,尽管它带有 LangChain 生态锁定的特性,且其按席位计费的价格在大规模使用时可能会变得昂贵。

Braintrust 在复杂 Agent 的生产追踪方面脱颖而出 —— 它具有时间线回放 (Timeline replay) 功能(跨多个步骤的每个跨度开始和结束的可视化瀑布图),以及一个“评估到追踪”的循环,允许你将生产环境中的失败转化为评估数据集。这是使非确定性失败可复现的关键工作流。

如果你已经在 Datadog 或类似的 APM 平台上进行大规模运营,通过 OTel (OpenTelemetry) 将 LLM span 集成到现有基础设施中通常是正确的选择。gen_ai.* 指标会直接发送到 Prometheus/Grafana 或 Datadog 可以摄取的标准计数器和直方图。这不会为你提供提示词/补全回放等 LLM 特有功能,但它能让你在同一个地方获取成本和延迟数据。

无论选择哪个平台,最值得优先考虑的功能是:时间线回放。能够看到带有时间戳的完整执行树,而不仅仅是日志或摘要,是将真正的有效调试与对多步 Agent 的瞎猜区分开来的关键。

调试生产环境故障:工作流程

考虑一个具体的场景:你的智能体(Agent)在错误的时间预订了会议。延迟正常,没有报错,任务标记为已完成。

调试流程的第一步是提取该会话的完整追踪(Trace)—— 不仅仅是最后一轮,而是跨越所有步骤的完整执行树。最后一轮可能看起来是正确的:日历工具被调用,时间段看起来合理,工具返回了成功响应。但三步之前的追踪显示,内存检索的 Span 返回了一个包含之前会话中过期时区偏好的日历条目。智能体正确读取了当前请求,但它进行推理的上下文是错误的。

如果没有关联的执行树,你在每一层都只能靠假设。有了它,你可以清楚地看到哪个 Span 引入了错误数据,智能体从中推断出了什么,以及在哪个时间点该决定复合成了错误的行动。

针对非确定性故障的第二步是将生产环境的追踪转换为可复现的测试用例。获取完整的追踪 —— 所有的输入、上下文状态、中间结果 —— 并在预发布环境中以相同的输入重新运行。这就是你如何将“它在生产环境中失败了,但我无法在本地复现”从一个慢性的调试负担转变为一个可解决的问题。

第三步是采样策略。在生产环境中捕获每一个追踪是非常昂贵的,而且通常没有必要。实际的做法是对成功的追踪进行低频率采样(5-10%),对错误进行 100% 采样,对可能指示失控循环的高 Token 数请求进行 100% 采样。如果 Token 速率超过基准线的 2 倍,就是一个非常有用的警报阈值,可以在智能体循环跑完之前检测到它们。

这对生产级智能体系统意味着什么

正在构建智能体系统的工程师们发现,标准的 DevOps 实践需要扩展,而不是取代。基础设施层仍然很重要 —— 你需要知道 LLM 供应商何时宕机、工具何时返回错误、数据库查询何时变慢。但那一层现在是必要但不充分的。

额外的一层是决策可观测性:理解你的智能体选择了做什么、为什么要做出这种选择、它是基于什么上下文进行推理的,以及结果是否符合意图。这需要不同的数据(执行树 vs. 延迟直方图)、不同的工具(追踪回放 vs. 仪表盘)以及不同的调试姿态(检查推理链,而不是调用图)。

在生产环境中成功实现这一点的团队通常会收敛于以下几种实践:在每次智能体运行中强制使用追踪 ID(Trace ID)、跨智能体边界显式传递上下文、将步骤计数监控作为回归金丝雀(Canary),以及将生产追踪转换为评估数据集的工作流程。这些在技术上都不复杂。难点在于从一开始就将它们视为工程需求,而不是在发生第一次需要三天时间调试的事故后再去进行补救(Retrofitting)。


智能体系统的可观测性尚未成为一个已解决的问题 —— OTel 的 gen_ai.* 规范仍处于实验阶段,工具生态尚不成熟,故障分类也仍在开发中。但需要进行仪器化(Instrumented)监控的轮廓已经很清晰了。现在投资于执行级追踪的团队,相较于那些当智能体悄无声息地做出错误决策时仍在盯着延迟仪表盘的团队,将拥有显著的调试优势。

References:Let's stay in touch and Follow me for more thoughts and updates