Agent 系统的可观测性:你不能控制你看不见的东西
一个 Agent 在生产环境里执行了一系列操作,最终结果是错的。你怎么知道问题出在哪一步?是模型的推理出了问题,还是工具返回了错误的数据,还是 Orchestrator 给 Executor 的上下文打包有误?
在传统服务里,这个问题有成熟的答案:日志、链路追踪、指标。三位一体的可观测性体系经过十几年的工程积累,已经相当完善。
在 Agent 系统里,这套东西从零开始。
为什么 Agent 的可观测性比服务可观测性难
不是难在技术上,而是难在性质上。
传统服务的行为本质上是确定性的:给定相同的输入,走相同的代码路径,产生相同的输出。日志记录的是事实:request_id=xxx, endpoint=/api/v1/orders, status=200, latency=43ms。你看到日志,就能还原发生了什么。
Agent 的行为是半确定性的。工具调用层面是确定的——调用了哪个工具、参数是什么、返回了什么,这些是事实。但工具调用之间的推理过程不是确定性的:模型为什么选择先调用 A 而不是 B,为什么在步骤三放弃了路径 X 转向路径 Y,这些决策的”原因”嵌在模型内部,不是代码逻辑的显式展开。
这带来了可观测性的核心困难:你能记录 Agent 做了什么,但很难记录 Agent 为什么这样做。
第二个难点是链路长度。一个普通的 API 请求,链路涉及几个服务,几十毫秒。一个 Agent 任务,可能涉及几十次工具调用、几轮模型推理、多个子 Agent 的并行执行,时间跨度从几分钟到几小时。传统追踪系统在这个时间尺度下的设计假设开始失效。
第三个难点是多模型、多 Agent 的因果链。当 Orchestrator 把任务委派给 Executor,Executor 调用了一个工具出错,这个错误通过状态更新反馈给 Orchestrator,Orchestrator 决定重新委派……这条因果链横跨多个 Agent 实例,每个实例可能在不同的进程、不同的服务甚至不同的机器上运行。把这条链串起来,是一个非平凡的工程问题。
三个层次的可观测性
我把 Agent 系统的可观测性需求分成三层,从下到上:
第一层:工具执行层(Tool-level Traces)
这是最容易实现的一层,也是最基础的一层。
记录每一次工具调用的完整生命周期:调用时间、工具名称、输入参数、返回结果、执行耗时、成功或失败。这层的数据是纯粹的事实,不涉及推理,结构化程度高,用传统的分布式追踪系统(OpenTelemetry span)就能覆盖。
这层能回答的问题:Agent 做了哪些操作?操作的顺序是什么?哪一步失败了?
工程要点: 工具调用的 span 应该挂在 Agent 任务的 trace 树下,而不是独立记录。否则你有大量零散的工具调用日志,但无法还原单个任务的完整执行路径。每个 Agent 实例启动时生成一个 task_id,每次工具调用继承这个 task_id 作为父 span。
第二层:推理层(Reasoning Traces)
这层是 Agent 可观测性的核心难点,也是和传统服务最大的区别所在。
工具调用之间,模型在推理:分析当前状态、制定下一步计划、选择下一个工具。这个推理过程的输出,就是模型生成的文本——通常包括对当前进展的分析、对下一步行动的说明,以及(有时候)对不确定性的表达。
推理层的可观测性,本质上是把模型的内部独白结构化。
现代 LLM 的工具使用模式(tool use / function calling)给了我们一个切入点:每次工具调用之前,模型会生成一段文本说明它为什么要调用这个工具。这段文本不是无用的”废话”,它是模型推理意图的显式表达。把它提取出来、关联到对应的工具调用、按时间序列组织,你就得到了一个粗粒度的推理轨迹。
这层能回答的问题:Agent 在某个节点的决策意图是什么?为什么选择了这个工具而不是其他工具?决策过程中有没有出现明显的推理偏差?
工程要点: 推理文本是非结构化的自然语言,不适合直接用传统日志系统存储和查询。更好的做法是把它存到专门的结构化存储里,和工具调用 span 通过 task_id 关联,按需查询时能同时拿到”做了什么”和”为什么这样做”。
第三层:任务层(Task-level Audit)
这层的视角从单次执行提升到任务全貌。
一个任务可能经历多轮执行——失败重试、Orchestrator 重新委派、用户中途修改目标。任务层的可观测性关注的是:这个任务从开始到结束经历了什么?有哪些关键决策点?人工干预了几次、在哪些节点介入?最终结果是否符合预期?
这层是 Agent 系统从”能用”到”可管理”的关键跨越。没有任务层的可观测性,你对 Agent 的管理停留在”事后看结果”;有了任务层,你可以做事中监控、模式识别和持续改进。
在开源项目治理场景里,我们在这层增加了一个人工决策记录维度:每次维护者审批或拒绝 Agent 的操作请求,决策结果和决策原因都记录进任务审计日志。这让我们可以在几周后回顾:哪类操作请求的拒绝率最高?拒绝的原因是什么?这些拒绝是 Agent 能力的局限,还是权限配置过于保守?
当前工具链的现状
现有的工具生态已经开始响应这个需求,但还处于早期。
LangSmith、Phoenix、Arize 这类 LLM 可观测性平台做了工具调用追踪和推理文本存储,对单 Agent 场景基本够用。问题在于:它们的数据模型是以”LLM 调用”为中心的,不是以”Agent 任务”为中心的。在多 Agent 协作场景下,任务被拆分到多个 Agent,每个 Agent 独立调用 LLM,这些平台很难把它们关联成一条完整的任务因果链。
OpenTelemetry 提供了基础设施,但 Agent 语境下的 span 设计规范还在讨论阶段(参见 OpenTelemetry GenAI 工作组)。社区里有不同的 span 语义约定,工具之间的互操作性有限。
自研方案 是很多团队的现实选择。基本的做法是:在 Harness 层的钩子里注入可观测性逻辑,每个工具调用前后各有一个 hook,把执行数据写到中心化存储(PostgreSQL 或者 ClickHouse 都可以,取决于查询需求),用一个独立的 UI 做任务视图展示。这不优雅,但完全可控。
最小可行可观测性:从哪里开始
如果现在要在一个 Agent 系统里从零建可观测性,我会按这个顺序推进:
第一步,先解决工具调用层。 在 Harness 的工具调用钩子里加日志,记录工具名、参数、返回值、耗时、task_id。这一步的工作量最小,但收益最大——你立刻就能回答”Agent 做了哪些操作”这个最基本的问题。用结构化日志(JSON),确保 task_id 在所有工具调用中一致。
第二步,提取推理意图。 在每次 LLM 调用的响应里,把工具调用之前的文本内容单独存储,关联到对应的工具调用记录。这不需要任何 NLP 处理,只是把模型已经生成的文本保留下来,而不是丢弃。
第三步,建任务视图。 用 task_id 把所有工具调用和推理文本串成一条时间线,提供一个简单的查询接口:给定 task_id,返回完整的执行时间线。不需要复杂的 UI,一个 API 端点加一个 JSON 输出就够用。
第四步,按需扩展。 根据实际遇到的问题,决定接下来优先解决哪个层次。常见的演进路径是:先做告警(哪些任务失败了?失败的模式是什么),再做性能分析(哪些步骤最耗时,哪些工具调用频率最高),最后做质量评估(任务完成率、人工干预率的趋势)。
一个容易忽视的设计原则
可观测性数据是一种特殊的数据:它记录的是 Agent 的行为,但它本身也在影响系统的行为——通过它所触发的告警、审计、决策。
这意味着可观测性系统需要一个独立于 Agent 主流程的设计。它不能是 Agent 执行路径上的同步依赖——如果可观测性系统出问题,Agent 的主流程不应该受影响。把可观测性数据的写入设计成异步的、可降级的,是一个重要的架构决策。
另一个容易忽视的点:可观测性数据的访问控制。工具调用记录和推理轨迹里可能包含敏感信息——用户数据、代码库内容、API 密钥(如果 Agent 曾经处理过这些)。这些数据的访问权限应该和生产数据库的权限同等对待,而不是当作”只是日志”随意开放。
可观测性不是 Agent 系统”成熟后再做”的优化项。它是让 Agent 系统能够在生产环境里被真正信任和使用的前提条件。
你不能控制你看不见的东西。在 Agent 能力快速扩展的当下,建立行为可见性是比任何能力升级都更紧迫的工程任务。