用户反馈 Agent 在昨天下午“做了一些奇怪的事情”。你打开日志,看到了以下内容:
INFO agent run started
INFO calling tool: updateOrder
INFO tool returned 200
INFO agent run completed
Agent 调用了 updateOrder。但你不知道它使用了什么参数、针对的是哪笔订单、为什么要选择该工具,以及返回了什么内容。从你记录的所有指标来看,这次运行都是成功的,但你却无法重建它所做出的任何一个决策。
Agent 系统的失败方式往往只有在事后才能理解,这意味着日志就是产品。本指南介绍了在每次工具调用时需要记录哪些内容、如何将模型决策与所生成的 HTTP 请求进行关联、需要脱敏哪些数据,以及如何将追踪转化为测试。我们关于 API 可观测性的文章介绍了服务端的情况,而本文则涵盖了运行在服务之上的 Agent 层。
一旦你拥有了追踪记录,Apifox 就会非常有用,因为理解一次微调或失败调用的最快方法,就是针对同一个接口重新运行它,并观察会发生什么。
AI Coding 交流群
如果你也在用 AI 写代码,或者正在研究 Cursor、Claude Code 这些工具,欢迎加入以下交流群。群里平时会聊一些 AI 编程的实际用法、开发工作流,还有各种新工具和新玩法。
三个层级,一个追踪
Agent 会在三个层级产生事件,但大多数团队只记录了中间的那一层。
推理层是模型进行决策的地方。包括上下文里有什么、提供了哪些工具、它选择了哪一个,以及使用了什么参数。
工具层是你的执行器。它负责验证参数、应用策略、将调用映射为 HTTP 请求并处理结果。
HTTP 层是传输线路。包括 Method、URL、headers、body、状态码和延迟。
调试工作几乎总是会跨越这些层级。“Agent 发送了错误的客户 ID”是一个推理问题,但只能在 HTTP 层看到。“API 返回了 200 状态码但 body 为空”是一个 HTTP 问题,但会在三步之后表现为奇怪的推理。如果这三个层级没有通过一个共享的标识符绑定在一起,你就只能通过时间戳来进行关联,一旦有两个运行实例重叠,这种方法就会失效。
因此,第一条规则是:每次 Agent 运行对应一个 trace ID,每次工具调用对应一个 span ID,并且在每个层级的每条记录上都要打上这两个标签。OpenTelemetry traces 已经完全对这种结构进行了建模,并且有一套不断完善的 GenAI 语义约定来命名属性,从而保证你的数据具有可移植性。
每次工具调用需要记录什么
一个能够解决实际问题的记录大致长这样:
{
"trace_id": "run_01J8ZK3M2Q",
"span_id": "call_004",
"parent_span_id": "call_003",
"timestamp": "2026-08-26T14:03:11.482Z",
"agent": "billing",
"step": 4,
"tool_name": "refundOrder",
"tool_args": { "orderId": "ord_92", "amount": 1200, "reason": "duplicate" },
"tools_available": ["getOrder", "listOrders", "refundOrder", "voidInvoice"],
"http": {
"method": "POST",
"url": "/v1/orders/ord_92/refund",
"request_body_hash": "sha256:1f4c...",
"status": 200,
"duration_ms": 412,
"retry_count": 1,
"idempotency_key": "9f2b7c14-6d3a-4b18"
},
"outcome": "success",
"tokens": { "prompt": 8420, "completion": 96 },
"policy": { "approval_required": true, "approved_by": "user_31", "dry_run": false }
}
有五个字段发挥了极其关键的作用。
tool_args 是最常缺失的字段,但它正是你最需要的。在执行器对模型生成的参数进行标准化之前,记录下这些参数。当智能体发送了错误的 ID 时,就可以在这里直观地看到问题所在。
tools_available 解释了选择的原因。如果模型选择了一个奇怪的工具,首先要问的是它还有哪些其他可选项。这个字段仅占用几个字节,却能瞬间解答这个问题。
retry_count 区分了“API 响应慢”和“API 失败两次后成功”这两种情况。如果没有它,三次尝试看起来就像是一次调用。
outcome 应该是一个明确的枚举,而不是通过状态码推断出来的。例如:success、failed、timed_out、blocked_by_policy、rejected_by_human。最后两个非常重要,因为被策略阻断的调用代表护栏在正常工作,而不是发生了错误,将它们混为一谈会污染你的失败率数据。
policy 是你的审计轨迹。当有人询问某项破坏性操作是否获得批准时,这就是答案。它与我们在关于 AI 智能体护栏的博文中所描述的执行机制相辅相成。
记录决策,而不仅仅是行动
智能体最难解决的 Bug 往往出在选择上,因此要记录足够的信息来重建决策过程。
保留运行中所使用的工具定义,或它们的哈希值。 当选择的准确性发生漂移时,首先怀疑的应该是有人修改了描述,而哈希值能让你立刻知道在正常运行和异常运行之间工具集是否发生了变化。我们关于工具数据模型设计的博文介绍了为什么这些文本对行为的影响如此之大。
记录模型及其设置。 运行记录中应该包含 Model ID、temperature 和 prompt 版本。模型版本不同,其行为也会发生变化,如果没有这个字段,你可能会花上一整天的时间去排查自己的代码。
记录模型看到的内容,或者至少记录其大小。 存储完整的 prompt 转储成本很高,且通常包含敏感信息。Token 数量加上哈希值就能提供大部分诊断价值:如果一次运行的 prompt 大小是平时的两倍,就说明其中附加了不应该存在的内容。
在精简之前记录原始的工具结果。 如果你的执行器在将响应传递给模型之前对其进行了裁剪(如我们关于防止工具响应占用上下文窗口的博文中所述),请在 Trace 中保存完整的 payload。否则,你将无法判断是数据本身缺失了,还是被你过滤掉了。
在存储前进行脱敏
智能体的 Trace 异常危险,因为它们既包含请求,也包含围绕请求的推理过程,而且 prompt 很容易收集到个人数据。
以下四条规则可以使这一问题处于可控范围内。
切勿存储凭证。清除 Authorization、API 密钥、cookies 和任何已签名的 URL。记录凭证的标识符(例如密钥 ID),而不是其具体值。我们关于 Agent 最小特权 API 密钥的博文介绍了为什么需要该标识符:它能告诉你具体是哪个 Agent 执行了操作。
在边界处进行脱敏,而不是在查询时。读取时过滤意味着敏感信息已经被写入磁盘、复制并备份。在日志中间件中、当记录离开进程之前就对其进行脱敏。
对无法存储的 body 进行哈希处理。即使不保留 payload,request body 的哈希值仍能让你证明两次调用是完全相同的,这已能满足排查重复请求的大部分需求。
根据敏感度设置保留期限。完整追踪信息保留一周,脱敏后的摘要保留一年。大多数调试工作在几天内就会进行;而大多数审计问题则在几个月内才会提出。
将追踪信息转化为测试
良好的链路追踪带来的收益不仅是更快的调试速度,它还源源不断地提供真实的测试用例。
每次失败的运行都是一个测试场景。获取异常追踪中的工具调用(tool calls),在你的 API 上进行重放,就能复现问题。修复方案上线后,将重放保留为回归测试。在 Apifox 中,你可以将失败的请求重建为已保存的用例,断言修正后的行为,并在 CI 中运行它,这就是将一次性事件转化为永久测试覆盖率的方法。
追踪信息还能告诉你应该 mock 什么。Agent 调用最频繁的接口,以及它实际遇到的失败状态,都可以直接从数据中获取,而无需凭空猜测。参考我们关于“针对 mock 运行 Agent 而非针对生产环境运行”的博文,围绕这些接口构建 mock。
它们还能暴露你平时可能会忽略的缓慢偏差。每周追踪以下几个指标:工具选择分布、每个接口的重试率、完成单个任务的调用次数,以及因策略而被拦截的运行比例。其中任何一项指标的变化都是潜在故障的先兆。正如我们的 API 契约测试指南中所述,契约级校验可以捕获通常导致此类问题的上游变更。
链路追踪必须经受住的三种排查场景
“Agent 扣错了客户的钱。” 你需要模型生成的参数、解析后的 URL 以及它的前一步操作。十有八九,这个 ID 来自于更早前的某个工具执行结果,该结果返回了多个匹配项,而模型直接选择了第一个。追踪信息会显示之前的那个结果、歧义点以及模型做出的选择。如果没有 tool_args,你只会得到一个 200 状态码和一位非常不满的客户。
“周二开始它就不能用了。” 逐个字段对比正常运行和异常运行的记录。模型 ID、工具集哈希、prompt 版本、平均响应大小。某些东西发生了改变,而这四个指标之一通常就能指明原因。这就是为什么运行记录不仅要包含事件,还要包含配置信息:只有在双方都记录了相同字段的情况下,进行 diff 对比才成为可能。
“有人批准这个吗?” 策略块(policy block)就是完整的答案,而且它必须在做出决定的那一刻被记录下来,而不是事后重建。approval_required、approved_by 和时间戳能将一场紧张的对话简化为一次简单的查询。
注意这些内容的共同点。它们没有一个可以通过“工具返回了 200”来回答。这三个问题都是通过写入成本几乎为零、但在事后却根本无法恢复的字段来回答的。
采样,以及绝不能采样什么
当运行规模很大时,对每一次运行都进行全保真追踪会变得非常昂贵,因此团队会进行采样。采样需要非常谨慎,因为 Agent 的流量并不是均匀的。
务必保留每一次失败的运行、每一次触发策略块的运行,以及每一次包含写入操作的运行。这些才是人们最关心的运行记录。对于成功的只读运行,可以进行采样,因为它们占据了流量的大部分,且单次运行的分析价值最低,不过你仍然需要保留足够的数据来计算基线。
谷歌的 SRE 书中关于监控的章节 依然最清晰地阐述了为什么要针对信号而非流量大小进行采样,这一逻辑同样适用于这里。
即使丢弃了 payload,也要保留运行记录。包含工具名称、结果和持续时间的骨架追踪(skeleton trace)占用空间很小,且仍然能够支持上述四项指标。成本高昂的部分是 body 和 prompt,这些是你可以首先丢弃的部分。
关于尾部采样(tail sampling)的一个警告:如果你是在运行结束后才决定保留哪些数据,请确保该决定是在已知运行结果之后做出的。一个在第三步看起来很正常、但在第九步失败的运行必须被完整保留,这意味着你需要进行缓冲,而不是一边运行一边丢弃。
追踪数据应该存放在哪里
以上所有内容都假设你拥有存储的所有权。当 Agent 是你自己的服务在调用你自己的 API 时,这个假设是成立的。但当 Agent 是开发者机器上的编码运行时,这就很不合适了,因为此时的追踪数据只会保存在运行它的那个终端里。
HiFox 采用了另一种方法:将执行追踪与分配给 Agent 的任务(Task)绑定。运行历史、执行日志和结果会与目标、状态以及人工评审工作的评论线索放在一起。实际的区别在于检索。“Agent 为什么会这样做”变成了一个通过打开任务即可回答的问题,而不需要去寻找特定的机器、会话和终端滚动历史。

它不能取代这里所说的追踪,也不能取代运行时;实际的工作仍然由 Claude Code 和 Codex 完成。它所改变的,是当 Agent 不是由你部署的服务时,记录最终存放的位置。
关注四个指标
只有当有人查看时,追踪数据才有用。这四个指标值得在仪表盘上占有一席之地。
单次完成任务的调用次数。 最直观的效率衡量指标。如果该指标上升,说明智能体正在进行更多探索,这通常是因为描述信息变差或某个接口开始出现故障。
按接口统计的重试率。 对你最不可靠的依赖项进行排序,并在其性能下降时进行展示。我们关于智能体错误恢复的文章介绍了如何处理该列表顶部的内容。
策略阻断率。 应当保持在较低且稳定的水平。该指标激增意味着要么是智能体在尝试不该做的事情,要么是策略限制过紧,成为了当前的瓶颈。
首次工具调用时间。 启动缓慢通常意味着 prompt 过于臃肿,而 prompt 的大小往往会在无人主动扩增的情况下悄然膨胀。
检查清单
- 每次运行一个 trace ID,每次工具调用一个 span ID,并在所有三个层级进行标记。
- 在标准化之前记录模型参数。
- 每次调用时记录可用的工具列表。
- 将执行结果记录为明确的枚举值,包括策略阻断。
- 重试次数与调用次数分开统计。
- 在运行记录中包含模型、temperature、prompt 版本和工具集哈希。
- 存储原始的工具执行结果,而不仅仅是传递给模型裁剪后的版本。
- 在中间件中剥离凭证,对于无法存储的 body 进行哈希处理。
- 根据敏感度进行分层的数据留存。
- 失败的 trace 可以转换为可复现的测试用例。
目标显而易见:当有人问起为什么智能体会做出某种行为时,你可以根据记录来回答,而不是凭空猜测。下载 Apifox 来复现 trace 中的调用,并将这些复现过程保留为测试。
常见问题解答
我应该使用 OpenTelemetry 还是专用的智能体观测工具? 建议在传输和 trace 模型上使用 OpenTelemetry,因为它已经处理了关联性,而且你的基础设施很可能已经支持它。专用于智能体的工具在此基础上添加了有用的视图,但底层的数据仍应当是可迁移的。
存储完整的 trace 链条需要花费多少成本? 如果你进行分层存储,成本会比预期的要低。保留几天完整的 payload,并在更长时间内仅保留不带 body 的结构化记录,可以减少大部分的存储量。Prompt 转储是成本较高的部分,因此默认情况下应对其进行哈希处理并记录大小,而不是直接存储。
我需要记录模型的推理文本吗? 通常不需要。它选择的工具、生成的参数以及可用的选项已经可以解释大部分决策。在服务商提供推理内容的情况下,仅针对失败的运行进行存储,并将其视为敏感数据。
如何跨多个智能体进行追踪? 为整个任务保持同一个 trace ID,并为每个智能体分配其专属的 span,将交接记录为一个事件。我们关于多智能体交接的文章详细介绍了交接记录中应该包含哪些内容。
如果智能体运行在客户的机器上怎么办? 在本地记录日志,进行严格的脱敏处理,并且除非用户主动同意,否则仅发送聚合指标。工具名称、执行结果和持续时间通常足以进行设备群级别的监控,而无需让任何 payload 离开设备。
请求 body hash 真的有用吗? 是的,对于最常见的问题确实有用。它证明了两次调用是完全相同的,这可以在不保留 payload 本身的情况下,解决大部分关于重复写入的排查工作。请将其与本应防止重复的 idempotency keys 配合使用。
开发必备:API 全流程管理神器 Apifox
介绍完上文的内容,我想额外介绍一个对开发者同样重要的效率工具 —— Apifox。作为一个集 API 文档、调试、设计、测试、Mock、自动化测试于一体的工具,Apifox 是目前提升研发效率的首选。
如果你正在开发项目,不妨试试其极其友好的界面设计,它完全兼容 Postman 和 Swagger 数据格式,导入数据非常方便,,即使是新手也能很快上手,点击这里即可注册使用。

值得一提的是,除了个人和常规团队使用,针对有高安全合规要求、或需要在内网环境协作的企业,Apifox 还提供了深度定制的私有化部署方案。
获取专属报价与部署方案
详细的私有化部署系统架构与安全白皮书
针对您公司规模的专属报价单
免费的 1v1 专属产品演示 (Demo) 机会