外观
可观测性与调试
传统的可观测性回答「服务慢不慢、挂没挂」;Agent 的可观测性要回答一个更难受的问题:系统没报错,但输出是错的,为什么? 一次 Agent 执行里没有任何 exception,HTTP 状态码全是 200,但任务失败了——规划跑偏、工具参数填错、检索回来的上下文驴唇不对马嘴。这类「安静的失败」只能靠 trace 级别的可观测性来抓。
这一页讲清楚三件事:Agent 系统的可观测性数据长什么样(三支柱与 trace 数据模型)、用什么工具接(LangSmith / Langfuse / Helicone / OpenTelemetry GenAI)、以及拿到轨迹之后怎么调试、怎么转成生产监控指标。
一、三支柱:trace、log、metric 在 Agent 语境下的重新定义
经典三支柱(trace / log / metric)在 Agent 系统里都还在,但重心完全变了:
| 支柱 | 传统后端 | Agent 系统 |
|---|---|---|
| Trace | 一次 HTTP 请求穿过哪些微服务 | 一次任务执行中 Agent 的每一步「思考-行动」:LLM 调用、工具调用、检索、子 Agent |
| Log | 结构化业务日志、错误堆栈 | prompt 全文、模型原始输出、工具入参出参——本质是「决策现场」的完整录像 |
| Metric | QPS、延迟、错误率 | 任务成功率、token 成本、工具失败率、人工接管率——很多指标无法从日志聚合,需要评估器打分 |
两个关键差异值得记住:
- Trace 是主支柱,log 和 metric 是派生物。 在微服务世界里 metric 是监控主力,trace 用来排查;在 Agent 世界里反过来——几乎所有重要指标(成功率、成本归因、工具失败率)都得从 trace 里算出来。选型时先问「这个工具的 trace 模型能不能表达我的 Agent 结构」,其他都是次要的。
- 非确定性意味着「同样的输入,trace 每次都不一样」。 传统 trace 的 diff 主要发生在代码变更后;Agent 的 trace 连分支数量都不固定(Agent Loop 迭代几次是运行时决定的)。这直接决定了后文调试方法论的形状——你不能用「重放请求」来复现问题,只能靠完整记录当时现场的输入输出。
一个判断
如果你的「监控」只是给 LLM API 调用包了一层耗时和状态码统计,那不叫 Agent 可观测性,那叫 HTTP 监控。Agent 可观测性的最小单元是一次任务执行(run)的完整决策轨迹,包含每一步的 prompt、模型输出、工具调用和中间状态。达不到这个粒度,线上出问题只能靠猜。
二、Trace 的数据模型:run、span 与事件
各家的术语略有差异(LangSmith 叫 run,OTel 叫 span,Langfuse 叫 observation),但数据模型已经高度收敛,本质是三层:
Trace(一次端到端任务执行,例如「处理这个用户工单」)
└── Span(有开始/结束时间的操作,可嵌套,构成树)
├── agent span 一次 Agent 运行(可能内含多轮 LLM 调用)
├── generation span 一次 LLM 调用(prompt、completion、token 用量)
├── tool span 一次工具执行(入参、出参、错误)
└── retrieval span 一次检索(query、命中文档)
└── Event(挂在 span 上的时间点记录,无时长)
├── 输入/输出消息(prompt messages、模型回复)
├── 评估结果(score、judge 的 explanation)
└── exception(堆栈、错误类型)几个工程上有实际含义的细节:
- parent_id 构成的树比时间序更重要。 多步 Agent 的 span 按时间平铺出来是一锅粥,按父子关系渲染成树才能看清「这个工具调用是哪次规划的产物」。多 Agent 场景下还要靠 trace context 跨进程传播(见 多智能体架构),否则子 Agent 的轨迹会变成孤立的 trace。
- group_id / session_id 是独立的一维。 OpenAI Agents SDK 的 trace 有
group_id字段,用来把同一会话线程的多次 trace 串起来(比如一个对话线程 ID)。Langfuse 的 Session、Helicone 的 Session 是同一思路:trace 管「一次任务」,session 管「一段连续交互」。 - 评估结果是一等公民。 OTel GenAI 语义约定在 v1.38.0(2025 年 10 月)专门加了
gen_ai.evaluation.result事件,携带evaluation.name、数值型score.value或分类型score.label、以及explanation,并建议带gen_ai.response.id做关联——这让「给某次生成打分」可以直接挂回被评分的那个 span,而不是散落在另一个系统里。
一次真实执行的最小 trace 示例
下面是一个客服 Agent 处理「帮我退掉上周的订单」的 trace 树(缩进表示父子关系,冒号后是时长):
trace: "customer-service" trace_id=trace_9f3c... 总耗时 11.4s
├─ agent_span: support-agent 11.2s
│ ├─ generation_span: gpt-5 (turn 1) 2.1s
│ │ ├─ event: input.messages [system, user: "帮我退掉上周的订单"]
│ │ └─ event: output.messages tool_call: lookup_order(email=?)
│ │ ↑ 注意这里:模型忘了问邮箱就调工具
│ ├─ function_span: lookup_order(email="") 0.3s status=ERROR
│ │ └─ event: exception ValidationError: email required
│ ├─ generation_span: gpt-5 (turn 2) 1.8s
│ │ └─ output: "请问您的注册邮箱是?" ← Agent 选择反问
│ │ ...(用户回复后 trace 继续)
│ ├─ function_span: lookup_order(email="a@b.com") 0.4s
│ ├─ function_span: refund(order_id=88213) 1.9s status=OK
│ └─ generation_span: gpt-5 (turn 4) 1.6s
│ └─ output: "已为您退款..."
└─ event: gen_ai.evaluation.result name=task_success score=1一条这样的 trace 几乎包含了调试所需的一切:哪一步出的错、模型当时看到了什么、工具返回了什么、整个任务花了多少 token。缺任何一环,定位成本都指数上升——这就是「trace 是主支柱」的具体含义。
三、工具链:平台、网关与开放标准
2026 年的工具格局可以分成三类:全家桶平台(LangSmith、Langfuse)、网关/代理(Helicone)、开放标准(OpenTelemetry GenAI)。它们不互斥,常见组合是「SDK 埋点 + OTel 导出 + 平台展示」。
LangSmith:LangGraph 生态的默认选项
LangChain 官方的闭源平台。和 LangChain/LangGraph 集成零配置(设两个环境变量就有全量 trace),评估和数据集工具链最成熟。代价是:闭源、只有 Enterprise 版才能私有化部署,且计费模式是「席位费 + trace 条数」——截至 2026 年中,公开的定价结构大致是 Developer 免费档(1 席位、每月 5k 条基础 trace)、Plus 约 $39/席位/月(含 1 万条),超量部分按每千条 trace 额外计费且长保留期(400 天)的单价约为短保留期(14 天)的两倍(价格以官网为准,引用前请核对)。
trace 计费有一个隐蔽的坑:你怎么埋点直接决定账单。 一个迭代 20 轮的 Agent,如果每轮拆成独立 run 上报,和合并成一条嵌套 trace,费用能差好几倍。上量之前先算清楚自己的 trace 粒度。
Langfuse:开源、可自托管的首选
MIT 协议开源(核心功能),self-host 优先,框架无关——这是它和 LangSmith 最根本的分歧点。数据模型是 trace / observation(span、generation、event)/ score 三层,SDK 之外还支持直接吃 OTLP(OpenTelemetry 协议)数据。托管版按「unit」(任何摄入的事件:trace、observation、score 都算)计费,截至 2026 年中有免费 Hobby 档(每月 5 万 units)和 $29/月起步的 Core 档;自托管则不受此限。
给 trace 挂评分的典型用法(线上打分的入口,第六节会用到):
python
from langfuse import Langfuse
langfuse = Langfuse()
# 对某条已上报的 trace 追加评分:可以来自规则、人工标注或 LLM-as-judge
langfuse.score(
trace_id="trace-abc123",
name="task_success", # 指标名,会出现在仪表盘
value=1, # 也支持 0.0–1.0 连续值或分类值
comment="退款流程完整走完",
)Helicone:一行代码的代理模式
思路完全不同:不改代码埋点,把 OpenAI/Anthropic 客户端的 base URL 指向 Helicone 的代理(加一行、一个 Helicone-Auth header),请求/响应、延迟、成本就自动进仪表盘。开源、可自托管,还附带网关能力(缓存、限流、重试)。官方文档把两种接入方式分得很清楚:proxy 模式接入快、能用网关功能,但流量过它的服务器;async 日志模式把上报挪出关键路径,网络故障不影响主链路。
代理模式的天花板也很明显:它只能看到「进出 LLM 的报文」,看不到你的 Agent 内部结构——规划步骤、工具调用之间的关系、为什么这次检索返回了空。对多步 Agent 来说,网关指标(成本、延迟、错误率)够用,但调试还得靠 SDK 级 trace。它更适合作为成本监控和兜底日志,而不是主力调试工具。
OpenTelemetry GenAI 语义约定:一个仍在施工中的标准
这是 2025–2026 年最重要的结构性变化:LLM/Agent 遥测正在向 OTel 收敛,但截至 2026 年 8 月,这个标准还没稳定,具体状态(依据 2026 年 7 月的实测梳理):
- GenAI 语义约定已从主仓库迁出,独立到
open-telemetry/semantic-conventions-genai仓库;主仓库 v1.42.0(2026 年 6 月 12 日)把全部gen_ai.*内容标记为废弃并迁走,v1.43.0 起主仓库不再携带 GenAI 内容。 - 所有 GenAI 专属的 span、event、metric、attribute 截至 2026 年 7 月仍是 Development 状态,没有任何一个被标记为 Stable;独立仓库甚至还没有打过版本 tag。被引用的通用属性(
error.type、server.address等)是 Stable 的,gen_ai.*命名空间不是。 - 近几年命名持续变动,生产环境必然混着好几代属性:
gen_ai.system→gen_ai.provider.name(v1.37.0,2025 年 8 月)、prompt_tokens/completion_tokens→input_tokens/output_tokens(更早的 v1.27.0)、每条消息一个 event →gen_ai.input.messages等结构化属性(v1.37.0)。核心 span 名已收敛为invoke_agent、chat、execute_tool这一组。
别把 Development 状态的 schema 当数据库契约
实务建议四条:① 框架、instrumentation 包、OTel SDK 全部锁版本——一次 minor 升级就可能改掉属性名;② 查询时对新旧两代字段做 COALESCE 归并(注意绝不能求和,兼容期框架会双写同一份数据);③ 内部维护一份自己的版本化数据模型,导出时才映射到当前约定;④ 自定义属性别放进 gen_ai.* 保留命名空间。部分 instrumentation 支持用 OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental 切到最新实验约定,但各框架行为不一致,以实际导出的 span 为准。
各家 SDK 自带的 tracing
主流 Agent 框架都内置了 tracing,但要注意它们和 OTel 的关系各不相同:
- OpenAI Agents SDK(详见 OpenAI Agents SDK):tracing 默认开启,自动把
Runner.run()包进一个 trace,内部生成agent_span/generation_span/function_span/guardrail_span/handoff_span等(语音场景还有transcription_span、speech_span)。默认经BatchTraceProcessor批量上报到 OpenAI 后台,可用add_trace_processor()追加第三方后端(Langfuse、Arize Phoenix、Braintrust、Datadog 等官方列出的生态集成有二十多家),或set_trace_processors()整体替换。注意它原生不发射 OTel GenAI 语义约定,走 OTel 要靠社区 instrumentation 或自定义 processor。ZDR(零数据保留)政策的组织不可用此 tracing。 - Claude Agent SDK / Vercel AI SDK 等:普遍走「直接支持 OTel」路线。Vercel AI SDK 的遥测已拆到独立的
@ai-sdk/otel包,发射gen_ai.provider.name等新代属性,旧ai.*命名空间保留为兼容路径。 - LangGraph:首选 LangSmith,但也可通过 OpenLLMetry 等 instrumentation 走 OTel。
一个横向对比:
| 工具 | 定位 | 开源/自托管 | 计费口径(2026 年中,以官网为准) | 适合的团队 |
|---|---|---|---|---|
| LangSmith | 全家桶平台 | 闭源;仅 Enterprise 可私有化 | 席位 + trace 条数,保留期分档 | 全家桶 LangChain/LangGraph |
| Langfuse | 全家桶平台 | MIT,self-host 优先 | 托管版按 unit(事件条数);自托管免费 | 需要数据自控、框架无关 |
| Helicone | 网关/代理 | 开源 | 请求量 | 快速获得成本/延迟监控,或做网关 |
| Arize Phoenix | 平台 | 全开源,OTel 原生 | 自托管免费 | 想要 OTel 原生且零成本自托管 |
| OTel GenAI | 标准/协议 | — | — | 不想被单一平台锁定的底层约定 |
四、调试方法论:从失败轨迹倒推根因
拿到完整 trace 之后,调试的核心动作是沿着树从后往前走:先看最终失败点,再逐级追问「这一步的输入是什么、输出为什么是这样」。绝大多数 Agent 失败能归进四类根因,每一类在 trace 里有特征性的「指纹」:
1. 规划错(planning failure)
指纹:工具都执行成功了,但调用的工具序列本身就不对——漏了关键步骤、顺序颠倒、在错误的时间点终止循环,或者陷入重复调用同一工具的打转模式(连续几个 function_span 参数几乎相同)。判断依据是对照「要完成任务本该做什么」。修复方向通常是改 instruction、加规划约束,或引入显式的 规划模块,而不是动工具。
2. 工具错(tool failure)
指纹:function_span 上挂着 exception,或工具返回了错误/空结果。再往下分两种:模型填错了参数(比如上节示例里没拿到邮箱就调 lookup_order,这是生成问题的外显)和工具/环境本身坏了(API 超时、权限不足、返回 schema 变了)。前者修 prompt 或工具描述,后者修工具。trace 里必须同时存入参、出参和错误原文,否则这两种无法区分。
3. 上下文错(context failure)
最隐蔽的一类。模型行为「合理」但基于错误信息:检索回来的文档不相关、长对话里关键信息被截断、记忆系统注入了过期事实。指纹是 generation_span 的 input.messages 里有问题——所以一定要把发给模型的完整 prompt 存下来,只看最终输出永远发现不了这类错误。这类问题的修复在 上下文工程 和 RAG 侧,不在模型侧。
4. 模型错(model failure)
兜底类:输入上下文完整正确、工具正常、规划合理,但模型就是推理错了、幻觉了、或没遵循格式。指纹是排除掉前三类之后剩下的。修复手段是换模型、调 temperature、拆任务,或者给这一步加结构化输出约束和重试。
实际排查顺序建议反过来:先查工具错(最容易定位),再查上下文错,然后规划错,最后才归因模型。 经验上「模型不行」是最后才成立的结论——大多数时候是上下文或工具的问题被甩锅给了模型。更多反模式见 常见陷阱。
轨迹回放与 diff
对修复做验证时,两个手段最实用:
- 轨迹回放(replay):把失败 trace 里的
input.messages原样取出来,在修改后的 prompt/模型/工具上重跑,对比新轨迹。因为模型非确定,单次回放不能下结论,要把失败案例沉淀成数据集批量重放——这就滑向了 评估 的范畴,见第六节。 - Trace diff:对比「失败 trace 集」和「成功 trace 集」的结构差异——工具调用次数分布、检索命中率、循环轮数。结构差异往往比单条 trace 的细节更快指向系统性问题。比如失败 trace 的平均工具调用次数是成功 trace 的 3 倍,大概率是规划在打转,而不是某个工具有 bug。
五、生产监控:Agent 系统该盯哪些指标
传统 RED(Rate / Errors / Duration)只覆盖了 Agent 的健康度的一小半。一个及格的生产监控面板至少要有这五组指标,而且全部应该能按 prompt 版本、模型版本、用户分群切片——aggregate 的数字在 Agent 系统里几乎没有诊断价值:
| 指标组 | 具体指标 | 告警信号 | 数据来源 |
|---|---|---|---|
| 任务成功率 | 端到端完成率(需定义「完成」)、放弃/超时率 | 按版本对比下跌 > 5% | trace + 评估打分 |
| 成本 | 每次任务的 token 数与美元成本、按步骤归因 | 单次任务成本 P95 突增 | generation span 的 usage 属性 |
| 延迟 | 端到端 P50/P95/P99、每轮迭代耗时、工具耗时占比 | P99 超 SLA;某工具耗时占比异常 | span 时长 |
| 工具健康度 | 各工具失败率、参数校验失败率、重试率 | 单工具失败率 > 阈值 | function span 状态 |
| 人工接管率 | HITL 触发率、接管后修改率、升级人工客服率 | 持续上升 = Agent 能力退化 | 业务事件 |
三个容易被忽略的点:
- 成本要看分布,不要看均值。 Agent 的成本是重尾的——大多数任务几千 token,少数失控循环能烧掉几十倍。盯 P95/P99 和「单 trace 成本 Top N」,并给单次执行设 token 预算上限(详见 成本优化)。OTel GenAI 约定里 token 用量在
gen_ai.usage.input_tokens/output_tokens属性上(旧代是prompt_tokens/completion_tokens),聚合查询记得归并两代字段。 - 「任务成功率」无法从日志算出来。 HTTP 200 不代表任务成功。它必须来自评估器:规则校验(如「退款单是否真的创建」)、LLM-as-judge、或人工抽检。这是 Agent 监控和传统 APM 最大的区别,也是下一节的主题。
- 人工接管率是最诚实的能力指标。 用户点了「转人工」、修改了 Agent 的草稿、或者直接放弃会话,都是免费的失败标注。这些信号应该回写成 trace 上的 score,而不是只留在业务数据库里。
TIP
把每次发布(prompt 改动、模型切换、工具变更)当作一次实验:在 trace 的 metadata 里打上版本标签,监控面板按版本切片。Agent 系统的退化经常不是「变坏了」而是「分布变了」——某个版本让 P50 更好但 P99 更糟,不切片永远看不见。
六、线上评估与离线评估的闭环
可观测性和评估不是两个系统,是一个闭环。OTel GenAI 约定把 gen_ai.evaluation.result 做成标准事件,正是这个闭环在规范层面的体现。完整循环长这样:
离线评估(发布前) 线上评估(运行中)
───────────── ─────────────
失败案例数据集 ────重放────▶ 新 prompt/模型批量跑分
▲ │
│ ▼
│ 采样 trace 打分(judge/规则)
│ │
└──── 低分 trace 自动入数据集 ◀──┘
│
▼
调试(第四节)→ 修复 → 回到离线评估实操上的几条经验:
- 线上打分必须采样,不可能全量。 LLM-as-judge 本身有成本和延迟。常见做法是分层采样:全部失败信号(人工接管、工具异常)必采 + 成功案例随机抽 1–5%。
- judge 的 explanation 比 score 值钱。 score 进仪表盘,explanation 进调试工作流。低分 trace 连同 judge 理由自动进入一个待办队列,是团队最高效的晨会材料。
- 离线数据集靠线上「养」。 冷启动的数据集是编的,覆盖不了真实分布;持续把线上低分 trace 和边界案例入册,数据集才会跟着流量进化。这也是 Evals 实战 里强调的核心循环。
- 回归门禁放在发布前。 任何 prompt/模型变更,先在累计的失败案例集上重放打分,通过率不达标就不发。没有这一步,「修了一个 case、搞坏了十个」是常态。
七、隐私与数据驻留
Agent 的 trace 是全量数据里最敏感的一种:完整 prompt 里可能含用户 PII、业务机密,工具出参里可能有数据库行。几个必须做的决定:
- 敏感数据开关要用起来。 OpenAI Agents SDK 默认
trace_include_sensitive_data=True(记录 generation 和 function 的完整输入输出),可用RunConfig.trace_include_sensitive_data或环境变量OPENAI_AGENTS_TRACE_INCLUDE_SENSITIVE_DATA关掉;代价是调试能力大幅下降。更细的方案是在上报前做 PII 脱敏(自研 processor 或平台的 masking 功能),保留结构、抹掉敏感字段。注意该 SDK 的 tracing 对 ZDR(零数据保留)政策的组织直接不可用。 - 数据驻留决定选型。 金融、医疗、政务等场景,trace 不能出域——这直接把闭源 SaaS 排除,指向 Langfuse / Phoenix 自托管,或自建 OTel collector + 后端(Jaeger、Grafana、ClickHouse)。这也是「先想清楚合规约束再选平台」的原因,迁移 trace 存储的成本很高。
- 保留期是合规问题,不只是成本问题。 LangSmith 按保留期分档计价(14 天与 400 天单价差一倍),但更要先回答「我们被允许存多久的用户对话原文」。很多司法辖区对含 PII 的遥测数据有明确的最长保留要求,长保留档可能根本不可选。
- 网关模式意味着流量过第三方。 Helicone 这类托管代理会经手全部 LLM 报文;对敏感场景应选自托管网关或 async 日志模式,把「过路」降级为「抄送」。
更广义的安全面(prompt injection 的检测与审计、工具的权限边界)见 安全与防护——trace 同样是安全事件的事后取证依据,这要求 trace 本身不可篡改、访问有审计。
参考资料
- The state of the OpenTelemetry GenAI semantic conventions (July 2026) —— 2026 年 7 月对 OTel GenAI 约定迁移、稳定性状态与各框架实际发射情况的实测梳理,本文 OTel 部分的主要事实来源。
- open-telemetry/semantic-conventions-genai —— GenAI 语义约定 2026 年迁出后的独立仓库(尚无版本发布)。
- Tracing - OpenAI Agents SDK —— SDK 内置 tracing 的 span 类型、processor 架构与敏感数据开关的官方文档。
- Trace the OpenAI Agents SDK with Langfuse —— Langfuse 通过 OTLP/Logfire 接入 OpenAI Agents SDK trace 的官方集成指南。
- Proxy vs Async Integration - Helicone —— Helicone 两种接入模式的官方取舍说明。
- Langfuse vs LangSmith vs OpenObserve: LLM Observability Compared (2026) —— 2026 年 7 月对三家计费口径(unit / trace 条数 / 流量 GB)的横评。
- The Complete Guide to LLM Observability Platforms - Helicone —— 厂商视角的平台选型对比,注意甄别其中偏向性。