Agent 工作流可观测性:Trace、日志与回放

0阅读7分钟

为什么只看最终结果不够

我在做 Agent 系统的前几个月,遇到一个典型问题:用户说「帮我查一下上海飞东京下周最便宜的航班」,Agent 最终返回了一个结果,看起来格式正确、有航班号、有价格。但用户截图反馈说价格差了将近一倍。

我打开日志,只看到一行 task_finished: status=success。没有中间步骤,没有工具调用参数,没有模型为什么选了这个航班号的推理过程。这行日志告诉我任务完成了,但没有告诉我为什么完成错了。

传统后端服务的日志逻辑到这里不太够用了。一个 HTTP 接口返回 200,问题通常出在请求参数或者数据库查询。但 Agent 的「200」背后,可能经过了五六次 LLM 调用、三次工具调用、一次上下文裁剪,其中任何一步出了偏差,最终结果就会错——而每一步的输入和输出如果不显式记录下来,根本无从回溯。

这篇文章讨论的是如何在 Agent 工作流中建立结构化的可观测性:记录什么、怎么记录、如何在出了问题的时候回放整个过程。

理论基础:从 OpenTelemetry 到 Agent Trace

OpenTelemetry GenAI 语义约定

OpenTelemetry 社区在 2024 年底启动了 GenAI 语义约定项目,到 2026 年中已经进入开发阶段,定义了 gen_ai.* 命名空间下的标准化属性。这套约定的核心思路是把一次 Agent 执行拆成三层 span:

  • gen_ai.chat / gen_ai.embeddings:底层模型调用,记录模型名、token 用量、延迟、finish_reason
  • agent.tool_call:工具调用,记录工具名、参数摘要、执行结果、耗时
  • agent.run:顶层任务,串联整个执行流程

MLflow 和 Portkey 等平台已经实现了这套约定,Langfuse、Arize Phoenix 等工具通过 OpenInference 提供了兼容的 span 结构。这套标准化的好处是,换模型提供商或者换 Agent 框架时,trace 数据不用重写。

Braintrust 的四类 Span 分类

Braintrust 在 2026 年的 Agent Observability 完整指南中,将 Agent trace 的 span 分为四类:

  1. Tool-call spans:工具名、参数、原始输出、重试次数、错误状态
  2. Reasoning spans:模型的计划、选择的行动、观察结果、下一步决策
  3. State transition spans:每步执行前后的状态快照、上下文编辑
  4. Memory operation spans:对长期存储的读写,含查询、返回条目、相关性评分

这个分类比单纯记录 LLM 调用要完整得多。它把「Agent 为什么这么做」也纳入了 trace 范围。

LangChain 的三层数据结构

LangSmith 用 Runs → Traces → Threads 三层结构组织数据:

  • Run:Agent 的单步操作(一次 LLM 调用、一次工具调用)
  • Trace:Agent 单次完整执行,由嵌套的 Runs 树组成
  • Thread:用户与应用的完整对话集合

这三层对应了不同的调试场景:单次调用异常看 Run,任务逻辑错误看 Trace,跨会话行为问题看 Thread。

Trace 事件设计

一个 Agent 任务的完整 trace 应该包含以下事件节点:

事件类型记录内容调试用途
task_created用户目标、入口、权限范围还原任务起点
plan_generated计划步骤、置信度、需确认动作检查规划是否合理
context_built上下文来源、检索材料、裁剪策略定位上下文问题
context_compressed压缩前/后 token 数、压缩策略诊断上下文饱和
tool_called工具名、参数摘要、耗时、状态、错误定位工具故障和循环
tool_blocked阻断原因(循环/限流/权限)检查循环检测是否误杀
handoff_sent交接载荷、子任务数、格式定位多 Agent 协作问题
state_updated任务进度、产物、下一步追踪执行路径
task_finished结果、失败类型、人工接管/回滚信息任务级复盘
eval_scored评分、评分器、是否通过质量门控连接评估循环

OpenTelemetry GenAI 约定定义了 gen_ai.usage.input_tokensgen_ai.usage.output_tokens 等标准属性,可以跨平台使用。在这个基础上加上 Agent 特有的事件类型(tool_calledhandoff_sent 等),就构成了一套可用的 trace schema。

指标设计:不要只看调用次数

Agent 指标容易陷入一个误区:只统计 LLM 调用次数和 token 总量。这些是成本指标,不是质量指标。更有价值的指标分为三个层次:

任务层指标

指标定义为什么重要
任务成功率用户目标是否达成最直接的质量信号
人工接管率需要人工介入完成任务的比例反映 Agent 自主能力
平均步骤数完成任务的平均工具调用次数过多说明效率低,过少可能有遗漏
用户采纳率用户是否接受 Agent 的输出最终衡量标准

工具层指标

指标定义对应问题
工具失败率按工具名分组的失败比例定位不可靠的工具
工具重试率同一工具连续调用比例暗示循环或参数问题
平均工具延迟P50/P95/P99 延迟分布识别性能瓶颈

失败分类(对应不同修复方向)

失败类型典型表现修复方向
上下文不足工具调用参数缺失或错误改进 RAG 检索或上下文组装
工具失败外部服务超时、权限不足工具侧修复,添加重试和降级
模型误判选错工具、幻觉参数改进工具描述、添加参数验证
规划循环反复调用同一工具添加循环检测和硬限制
用户目标不清任务范围模糊导致反复确认改进入口交互,明确任务边界

可观测性落地路径

不需要第一天就搭建完整的观测平台。一个务实的分阶段路径:

阶段一:Day 1 — 结构化日志

最小起步:给每个任务一个 trace_id,用结构化 JSON 输出关键事件。不需要引入 OTel SDK,标准的 structlogpino 就够了。

{
  "trace_id": "abc-123",
  "event": "tool_called",
  "tool": "search_flights",
  "params": {"origin": "SHA", "destination": "TYO"},
  "status": "success",
  "duration_ms": 320,
  "tokens": {"input": 512, "output": 128}
}

关键要求:字段稳定,能按 trace_id 串联,能按用户、任务、工具筛选。

阶段二:Week 1 — Trace 可视化

接入 Langfuse(自托管)或 LangSmith(SaaS),让 trace 数据可视化。这时候可以开始做:

  • 按 trace_id 查看完整的执行路径
  • 按工具名过滤,找到失败率最高的工具
  • 按用户分组,找到体验最差的使用场景

阶段三:Month 1 — 在线评估

在 trace 样本上添加在线评分器。可以用 LLM-as-a-judge(用一个独立模型给 Agent 输出打分),也可以用规则评分(检查输出格式、关键信息是否完整)。设置质量回归告警。

阶段四:Quarter 1 — CI 集成

把生产环境发现的失败案例收集为 eval 数据集。在 CI 中跑相同的评估器。质量低于阈值时阻断 PR 合并。这一步把一次性调试变成了持续的质量循环。

阶段时间核心动作所需工具
结构化日志Day 1trace_id + JSON 输出structlog / pino
Trace 可视化Week 1接入 Langfuse/LangSmithLangfuse / LangSmith
在线评估Month 1评分器 + 质量告警Braintrust / Langfuse evals
CI 集成Quarter 1eval 数据集 + PR 门控GitHub Actions + eval 框架

检查清单

设计阶段

  • 确定 trace_id 生成策略,确保能按用户、任务、会话三个维度查询
  • 定义事件类型枚举(task_created / plan_generated / tool_called / state_updated / task_finished 等),写入团队共享的 schema 文档
  • 确定哪些字段是必填(trace_id、event、timestamp、status),哪些是可选(params、result、error_detail)
  • 决定 prompt/completion 内容的记录策略:全量记录、截断记录、还是只记录 hash(涉及隐私和成本)
  • 为每个工具定义成功/失败的返回格式规范,包含明确的 SUCCESS/FAILED 标记

实现阶段

  • 每次 LLM 调用记录 model 名、input_tokens、output_tokens、finish_reason、延迟
  • 每次工具调用记录工具名、参数摘要、执行结果、耗时、重试次数
  • 上下文窗口利用率在每轮结束时计算并记录,接近 70% 时标记
  • 多 Agent 交接时,交接载荷结构化记录,每个子任务独立 span
  • 添加循环检测 hook:同一工具+参数在滑动窗口内出现 N 次时阻断
  • 添加工具调用硬限制:单次任务每个工具最多调用 M 次

运维阶段

  • 任务成功率、人工接管率、工具失败率按日/周聚合,建立基线
  • 上下文 token 数增速设置告警(单轮增长超过阈值)
  • 工具延迟 P95 设置告警
  • 生产环境失败案例定期(每周)收集为 eval 数据集
  • CI 中运行 eval 套件,质量低于阈值时阻断合并
  • 定期校准 LLM-as-judge 评分器与人工标注的一致性

边界与局限

有些问题可观测性本身解决不了。

Trace 数据量大。一个复杂 Agent 任务可能产生几十个 span,每个 span 包含 prompt 和工具调用详情。全量记录的存储成本不低,需要采样策略。对错误 trace 做 100% 采样,对成功 trace 做 10-20% 采样是比较常见的做法。

Prompt 内容涉及用户隐私。OTel GenAI 约定明确警告了属性中可能包含敏感信息,建议 opt-in 记录并配合过滤和截断。在合规要求严格的场景(医疗、金融),可能需要完全放弃记录 prompt 原文,只保留结构化元数据。

LLM-as-judge 评分器本身有不确定性。用它做在线评估时,评分结果有方差,不能像传统断言一样做精确比对。需要足够大的样本量才能得到有意义的趋势信号。

可观测性是调试的基础设施,不是银弹。它让你能在出问题后回溯原因,但不能自动修复问题。修复还是要回到 prompt 工程、工具设计和 Agent 逻辑本身。

参考资料

  1. OpenTelemetry GenAI Semantic Conventions — OTel 官方 GenAI 语义约定,定义了 gen_ai.* 命名空间下的标准化属性
  2. Agent Observability: The Complete Guide for 2026 — Braintrust — 四类 span 分类、最小可行 trace schema、分阶段落地路径
  3. AI Agent Observability with Langfuse — Langfuse 的 Agent 可观测性策略、评估方法和跨框架集成
  4. Debugging Deep Agents with LangSmith — LangChain — Deep Agent 的 trace 分析、Polly AI 助手辅助调试、Fetch CLI 工具
  5. How to Prevent AI Agent Reasoning Loops — AWS/Dev.to — DebounceHook、明确 SUCCESS 状态、硬限制三层防线
  6. The AI Agent Debugging Playbook — 8 种失败模式分类、Replay 方法、三层评估体系
  7. OpenTelemetry for AI Systems: LLM and Agent Observability — Uptrace — OTel GenAI 约定的代码示例、自动/手动 instrumentation、告警规则设计
  8. Semantic Conventions for Generative AI Agentic Systems — GitHub — Agent 系统语义约定的正式提案,定义了 agentic 系统的专用属性

评论 0

0 / 1000