跳转到内容

Agent 可观测性:Trace、事件、成本与故障定位

Eval 告诉你“退款切片退步了”,却不会说明是哪一步坏了:模型选错工具、检索拿到旧政策、参数被截断、审批超时,还是工具早已成功、只是响应丢了?

普通应用也有日志、指标和 Trace。Agent 更难在于路径会动态变化:模型调用、工具、副作用和多 Agent 交接可能在一次运行里交织,最终答案又往往把中间错误盖住。可观测性不是把 Prompt 全量存下来。它要为一次运行留下可追踪的因果链:哪一步读了什么、哪一步产生副作用、最后由什么证据确认结果,同时控制隐私和存储成本。

先记住最小单位: 一条 Trace 要能回答“目标是什么、最后一个成功步骤是什么、外部世界有没有已发生的副作用、当前版本是什么”。存更多对话不等于更容易定位问题。

最后会把 agent.plantool.searchagent.answer 三段运行聚成一条 Trace,核对耗时、Token 和工具错误。

只有最终答案,根本找不到错误在哪一层

Section titled “只有最终答案,根本找不到错误在哪一层”

考虑一个研究 Agent 的失败回答。只看输入输出,你只能知道结论错了。真实原因可能是 Router 把任务发给了错误专业 Agent、搜索工具命中了旧页面、网页抓取返回 403 模型没有显式报错、上下文裁剪丢了日期限定、工具参数中的年份被模型写错、验证器未运行、达到预算后系统提前终止,或者正确证据被最终摘要误写。

这些故障分别属于编排、工具、上下文、模型、验证和预算层。没有结构化事件,只能从一大段对话猜测。

例如 tool_call_deniedcheckpoint_savedhandoff_started。日志适合离散事件和错误详情,但单条日志不天然表达父子关系。

成功率、P95 延迟、Token、费用、工具错误率、人工接管率和循环次数。指标适合告警与趋势,不能单独解释某一次失败。

Trace 包含多个 Span。每个 Span 代表一段工作,例如模型生成、工具调用、检索、Guardrail 或 Handoff,并记录父子关系、耗时、状态和属性。

代码 Diff、报告、截图、测试结果、工具原始响应和检查点。大工件不应全塞进 Span,可以保存引用、哈希和访问策略。

四者组合起来,才既能发现系统异常,又能解释单次运行。把所有东西都塞进日志会让后续检索更难;把 Trace、指标和工件各放在适合的位置,诊断链才不会断。

一个 Trace 至少需要:

trace_id 一次端到端运行
span_id 当前步骤
parent_span_id 父步骤
name agent.run / model.generate / tool.call ...
start/end 时间与耗时
status ok / error / cancelled
attributes 模型、工具、Token、版本、重试等
events 审批、异常、状态变化
links 与异步任务或其他 Trace 的关系

OpenAI Agents SDK 的 Tracing 覆盖 Agent、Generation、Tool、Guardrail 和 Handoff 等事件。[1] OpenTelemetry 的 Generative AI Semantic Conventions 则尝试为模型、Agent、工具和评测定义跨平台语义。[2]

统一语义很重要:如果每个框架都把工具名称、Token 和错误写在不同字段,跨服务检索与仪表盘会不断重做。

不是每一行代码都需要 Span。优先覆盖会改变决策、成本或外部状态的边界:agent.run(顶层任务)、model.generate(模型与参数版本)、context.build(检索、裁剪与输入预算)、tool.call(工具、参数摘要、结果状态)、policy.check(允许、拒绝和规则版本)、handoff(来源、目标和交接包)、checkpoint(状态版本和存储结果)、eval.score(Judge 与分项分数)、human.approval(请求、决定和等待时长)。

Span 命名应描述稳定的操作类型,不要把用户 ID 或完整问题拼进名称,否则指标维度爆炸。

记录可验证的动作,不记录不可验证的独白

Section titled “记录可验证的动作,不记录不可验证的独白”

调试系统需要的是输入来源、动作、参数、状态和结果,不需要存储模型不可验证的内在推理独白。

更有价值的记录是:模型收到哪些经过裁剪的消息类型、选择了什么工具、参数通过了哪版 Schema、工具返回什么状态码和结果摘要、验证器依据什么环境证据作出判定、最终状态如何迁移。这样既能诊断,也减少保存敏感推理和无边界文本的风险。

Trace 要跟着状态和副作用一起走

Section titled “Trace 要跟着状态和副作用一起走”

第 10、11 讲把 Harness 拆成上下文、工具、控制、状态、验证和运行环境。Trace 应沿这些边界记录。

例如一次状态迁移:

RUNNING
-> tool.call(send_email)
-> response_timeout
-> checkpoint(UNKNOWN_EXTERNAL_STATE)
-> tool.call(query_by_idempotency_key)
-> verifier(email_exists)
-> SUCCEEDED

只记录“模型重试了两次”无法判断是否重复发送;记录外部状态查询和幂等键,才能重建真实因果。对长时任务,Trace 还要与持久化 run_id、检查点版本和业务对象 ID 关联。进程重启后可以产生新的 Trace Segment,但仍属于同一领域运行。

Manager 启动 Worker、A2A 委派远程 Agent、消息进入队列时,父子调用可能跨进程甚至跨组织。此时简单的同步父子 Span 不够。

常用方法包括传递 Trace Context、用 link 连接异步任务、记录 task_idagent_idattempthandoff_id、把消息生产与消费分别建 Span、保留 Worker 工件哈希而不复制全部内容。同时要明确租户边界。跨组织 A2A 不应无条件传递内部 Trace、Prompt 或用户数据;只传必要的关联 ID 和经过批准的上下文。

指标要回答用户任务,不只回答模型调用

Section titled “指标要回答用户任务,不只回答模型调用”

业务终态成功率、验证通过率、人工纠正与回退率、按任务类别的 Eval 分数。

工具错误与超时率、重试、重复副作用和卡死率、检查点恢复成功率、各状态停留时间。

端到端 P50/P95/P99、模型、检索、工具、审批各段耗时、输入/输出 Token、每个成功任务成本而不是每次调用成本、并行峰值和队列等待。

策略拒绝次数、高风险工具批准率、未知 Server 或能力变化、Prompt Injection 告警、敏感字段脱敏失败。

“每个成功任务成本”特别重要。一个便宜模型如果需要更多重试和人工接管,总成本可能更高。

Langfuse 提供 Trace、Prompt、Evaluation、Dataset 等 LLM 工程能力,适合把线上运行与离线评测连接起来。[3]

Langfuse GitHub 页面

图 1:Langfuse 是开源 Agent/LLM 可观测项目之一。GitHub 热度代表关注度,不代表适合所有部署。来源见文末。

Phoenix 关注 AI Observability 与 Evaluation,支持 Trace、实验和 RAG/Agent 分析。[4]

Phoenix GitHub 页面

图 2:Phoenix 将 AI Observability 与 Evaluation 放在同一产品面。来源见文末。

OpenTelemetry 更接近跨语言、跨服务的遥测标准与生态,不是完整的 Agent 产品控制台。工程上可以用 OpenTelemetry 采集和传输,再选择后端存储、分析与评测。选择平台时要看:是否支持自托管、数据保留与脱敏、Trace 标准、Dataset/Eval 连接、成本、访问控制和导出能力。

Trace 越有用,越要先设计数据边界

Section titled “Trace 越有用,越要先设计数据边界”

Prompt、Tool 参数和返回值可能包含姓名、订单、代码、医疗数据和 Secret。全量记录虽然方便调试,却可能制造一个新的敏感数据仓库。

应在采集前设计:字段白名单与结构化脱敏、Prompt/Response 只存哈希、摘要或受控引用、Secret 永不进入遥测、按租户和角色控制访问、设定保留期与删除流程、调试临时提级有审批和时效、导出和第三方平台符合组织政策。不要依赖事后在日志平台里搜索并删除。敏感信息一旦复制到多个后端,治理成本会迅速扩大。

采样:不是所有成功 Trace 都要永久保存

Section titled “采样:不是所有成功 Trace 都要永久保存”

可以组合三类采样:头部采样在运行开始时按比例决定,成本低,但可能漏掉稀有错误;尾部采样在运行结束后保留错误、慢请求和高风险任务,更有诊断价值;规则采样对付款、删除、策略拒绝、人工接管等事件强制保留。

指标可以全量聚合,详细内容按风险采样。错误 Trace 的保留期不必无限,修复后应沉淀成脱敏 Eval Case。

动手跑一次:把三段运行聚合成一条 Trace

Section titled “动手跑一次:把三段运行聚合成一条 Trace”

从本讲目录执行:

Terminal window
python3 experiments/trace_budget.py

trace_budget.py 构造 agent.plantool.searchagent.answer 三个 Span,再把它们聚合为端到端预算。数据是固定教学 Fixture,重点在字段怎样关联,不是拿来比较真实模型速度。

运行结果:总耗时 1040ms,总 Token 1030,工具错误为 0,终态 success

{
"trace_id": "trace-demo-001",
"total_latency_ms": 1040,
"total_tokens": 1030,
"tool_error_count": 0
}

完整结果见 experiment-result.json。运行后核对三项:total_latency_ms1040total_tokens1030tool_error_count0;三个局部事实都能回到同一个 trace_id。这组数据是教学 Fixture,不是线上测量。它展示的是聚合原则:每个 Span 保存局部事实,Trace 提供端到端预算和因果关系。

在实验中加入一个失败的 tool.fetch Span,设置两次重试。分别计算首次尝试错误率、最终工具失败率、重试带来的额外延迟、每个成功任务 Token、最慢 Span。

再给 Span 增加 model_versionprompt_versiontool_version,模拟某次发布后 P95 变高,验证能否按版本切片定位。最后加入一个看似真实的邮箱字段,在写文件前做脱敏,并写测试确认结果中不包含原始值。

遇到失败时,按顺序问:用户目标对应的领域终态是什么、运行在哪个状态停止、最后一个成功 Span 是什么、错误是模型、工具、政策、传输还是环境、是否产生了未知副作用、当前 Trace 与哪个版本、Commit 和 Fixture 绑定、这是单例还是同一切片的系统性变化、修复后能否变成一个自动回归 Case。

这比从几千行字符串日志里搜索“error”更接近工程诊断。

Agent 可观测性的核心不是“存更多对话”,而是建立从目标、状态、模型、工具到环境终态的因果链。

Trace 解释一次运行,Metrics 发现整体变化,Artifacts 保存证据,Eval 判断质量。四者通过版本和任务 ID 连接,团队才不必靠回忆复盘一次失败。

下一讲进入可靠性工程:超时之后究竟能不能重试,怎样用错误分类、幂等、检查点和补偿控制副作用。


[1] OpenAI Agents SDK, Tracing. https://openai.github.io/openai-agents-python/tracing/

[2] OpenTelemetry, Generative AI Semantic Conventions. https://opentelemetry.io/docs/specs/semconv/gen-ai/

[3] Langfuse. https://github.com/langfuse/langfuse

[4] Arize Phoenix. https://github.com/Arize-ai/phoenix

[5] Google Cloud, A developer’s guide to production-ready AI agents. https://cloud.google.com/blog/products/ai-machine-learning/a-devs-guide-to-production-ready-ai-agents/