为什么 AI 代理需要不同的可观测性模型
普通 API 请求通常有清晰的开始、处理程序和响应。AI 代理增加了循环。它选择模型,决定是否调用工具,等待远程系统,读取结果,然后可能再次调用模型。因此,一个用户请求可能包含多次模型调用、检索查询、工具执行、重试和策略检查。只写一条“请求失败”的日志,无法说明哪个分支消耗了时间或预算。
可观测性通过互相关联的 traces、metrics 和 logs 让这条路径可见。OpenTelemetry 将 trace 描述为请求经过系统的路径,将 span 描述为其中的一项操作。为用户可见的代理运行创建一个根 span,再为模型推理、检索、工具、安全检查和序列化创建子 span。模型名和工具名应保持低基数。请求专用的 ID 放入 trace context 或结构化日志,不要放入指标标签。这样,值班人员可以分别回答三个问题:这次运行发生了什么,行为出现得多频繁,哪些工作流受到影响。
本文使用与供应商无关的 schema。具体的 usage 字段和计费规则仍以供应商文档为准。OpenTelemetry GenAI span 约定和GenAI 指标约定为不断变化的集成提供共同词汇。
能够承受代理循环的 trace schema
应用接受请求时就创建根 span,而不是等到第一次模型调用。可以使用 invoke_agent 这样的 operation,并记录 service、deployment、environment、workflow version 以及不含敏感信息的租户类别。只有在已有会话 ID 且保留策略允许时才记录它。不要把用户消息、完整 prompt 或工具参数放进指标标签。
每个子 span 都应回答一个运维问题。最低集合可以是:
| Span | 应记录的内容 |
|---|---|
| agent.run | 工作流名称和版本、结果、尝试次数、持续时间 |
| gen_ai.inference | 供应商、请求模型和响应模型、操作、流式标志、结束原因、令牌 |
| gen_ai.retrieval | 索引或数据源类别、查询模式、结果数、缓存命中 |
| gen_ai.tool | 工具名称和类型、授权决定、超时、结果状态 |
| guardrail.check | 策略版本、决定、原因代码、持续时间 |
操作失败时设置 span status 和 error.type。重试事件包含尝试编号、退避时间和原因代码。重试不是第二个根运行,而是同一逻辑操作的另一种尝试。网络请求可以为每次尝试创建 client span,避免仪表盘重复用户请求并显示供应商尝试。
下面的 JSON 只是导出记录的形状,不代表某个供应商的固定载荷。它只包含计数和代码,不包含内容。
{
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"span_id": "00f067aa0ba902b7",
"parent_span_id": "b7ad6b7169203331",
"name": "chat model",
"kind": "CLIENT",
"status": "OK",
"attributes": {
"gen_ai.operation.name": "chat",
"gen_ai.provider.name": "provider.example",
"gen_ai.request.model": "model.example",
"gen_ai.response.model": "model.example-2026-01",
"gen_ai.usage.input_tokens": 820,
"gen_ai.usage.output_tokens": 146,
"gen_ai.response.finish_reasons": ["stop"],
"app.agent.attempt": 1
}
}
span 名称应描述操作类别,而不是 ID 或用户文本。把语义约定版本保存在 instrumentation 元数据中。分别报告的计费令牌和处理令牌都应保存在内部账本,成本视图采用计费数量。不要无层次地区分地相加 client 和 server instrumentation,否则会重复计数。
Correlation ID 和上下文传播
trace ID 将多个服务连接起来,span ID 标识一项操作。应用自己的 request ID 便于客服支持,但不能替代 trace context。通过 API gateway、代理服务、检索服务和工具适配器传播 W3C traceparent 头。OpenTelemetry 上下文传播指南说明了接收方如何提取远程上下文并创建子 span。还可以将相同的上下文写入结构化日志,从日志跳转到 trace。
支持流程需要短 ID 时创建随机 request ID,并把它与 trace ID 的映射放在日志里。对于队列,将上下文注入消息元数据,并在消费者处理消息时创建 consumer span。没有单一父级的并行任务使用 span links。公共边界须验证 tracing headers,内部 baggage 不得转发给外部供应商,因为它可能含凭据或个人数据。
工具 spans 揭示代理的真实行为
需要时分开记录模型的决定和应用的执行。模型 span 说明模型请求了某个工具,工具 span 说明应用实际执行了什么。工具 span 可包含稳定的 tool.name、function、extension 或 datastore 等类型、策略决定和外部操作。只有在不会泄露秘密时才添加请求方法或查询类别。不要保存访问令牌、原始 SQL 参数、文档内容或带 query 的完整 URL。
每次工具调用记录开始和结束时间、超时设置、结果类别、重试数以及有上限的结果大小。超时、业务拒绝、策略拒绝和上游 5xx 应使用不同的原因代码。如果工具调用另一个服务,传播当前上下文。如果工具执行本地命令,只记录命令族和退出类别,不记录任意用户输入。
也要显示非工具等待。为队列等待、速率限制休眠、熔断器开启和响应流创建 spans,否则模型 span 可能隐藏并发槽位等待。多代理工作流为委派代理命名并链接父 trace,不要为内部状态创建新 trace。
令牌与成本核算
供应商响应提供 usage 时应优先使用它。输入、输出、缓存输入、推理输出、批处理、图像和工具单位可能有不同价格。OpenAI 令牌指南说明,令牌化随模型和语言变化,已完成请求的 usage 对象是核算依据。本地 tokenizer 可以在请求前估计预算,但不能替代供应商 usage 来对账。
为每次模型尝试保存 provider、requested model、response model、token category、count、currency、pricing-table version 和 cost center。收到响应后计算成本:
cost = input_billable_tokens * input_price
+ cached_input_tokens * cached_input_price
+ output_billable_tokens * output_price
+ provider_units * unit_price
价格应是带生效日期的配置,而不是写死在 trace exporter 中。保留原始计数和金额以审计价目表修正。把所有尝试相加,因为失败调用也可能消耗令牌。估算或延迟费用标为临时值,并与供应商报告核对。
成本维度必须是负责人能够改变的内容,例如工作流、模型系列、环境、租户类别和结果。不要把用户 ID、prompt 或任意工具参数做成指标维度。每日成本仪表盘应显示总金额、每次完成运行的成本、每次运行的令牌、重试比例以及昂贵模型的占比。输出令牌突然增加而延迟正常,可能意味着响应没有边界、出现循环或 prompt 发生变化。
延迟分布:p50、p95 和 p99
平均值会隐藏队列等待或慢工具造成的尾部延迟。为根运行和重要 span 记录以秒为单位的 histogram。记录 time to first token、流式块之间的时间以及完整响应时间。供应商提供相关字段时,区分服务器、队列和网络耗时。
p50 描述典型情况,p95 描述较慢的一批用户,p99 显示少见但严重的尾部。它们是分布的分位数,不是三个平均值。按照实际 SLO 选择从亚秒到数分钟的 bucket,并保持单位一致。Prometheus 的 histogram_quantile 根据 histogram bucket 估算分位数。经典 histogram 需要先按 le 聚合。
histogram_quantile(
0.95,
sum by (le, workflow) (
rate(agent_run_duration_seconds_bucket[10m])
)
)
不要把 trace ID 放在指标标签里。只按少量受控维度拆分,例如工作流、模型系列、区域和结果。trace 样本可以解释 p95 为什么变化。并发工具的子 span 时间会重叠,不要简单相加,应从 trace 中找关键路径。
错误、重试和速率限制
先定义错误分类,再建立告警。至少区分客户端校验、策略拒绝、认证、速率限制、超时、上游服务器错误、模型输出格式错误、工具失败和取消。将供应商代码映射到这些稳定类别,同时保留基数受控的原始代码。把用户主动取消与服务器失败分开。
每次重试都记录原因、尝试编号、退避和最终结果。只有在供应商契约允许时才使用带 jitter 的有界指数退避。不要重试校验、授权、策略决定或确定性的 schema 错误。为完整代理运行和每次尝试设置 deadline。根 span 报告 retry_count、attempt_count 和最终结果,每次尝试保留自己的 status。成功率正常时,过多重试仍可能增加成本。
把备用模型的选择记录为事件或 span。仪表盘要区分速率限制、延迟、安全策略和能力检查导致的 fallback。分别监控错误的工具参数和 schema 修复循环。如果允许修复,限制迭代次数,达到上限时发出 loop_limit。
隐私、脱敏和采样
prompt 和工具数据可能包含个人、机密或安全敏感信息。安全的默认做法是收集元数据、令牌数、指纹和原因代码,把内容排除在 telemetry 之外。如果调试必须查看例子,应使用独立的受控存储,配合明确同意、短期保留、加密、访问日志和字段级脱敏。在发送到后端之前完成脱敏。
使用属性 allowlist。删除授权头、cookie、API key、联系信息、账号、带 query 的 URL 和文档文本。哈希并不会自动使数据匿名。将 prompt 指纹与 prompt 本身分开,并记录谁可以关联它们。用含有真实格式秘密和多语言个人数据的 fixtures 测试脱敏。
采样可以减少数据量,但不能隐藏事故。使用 parent-based sampling 保持 trace 一致。在 Collector 中使用 tail sampling 保留错误、超时、高成本和慢 trace,同时降低普通成功运行的采样率。OpenTelemetry sampling 规范区分 recording 与 export,所以本地 sampler 也可避免构造昂贵属性。指标保持不采样,trace 用于 exemplars 和调查。
OpenTelemetry、Grafana 与 Sentry 的组合
使用 OpenTelemetry API 和语义约定对代理做 instrumentation,向 OpenTelemetry Collector 发送 OTLP,再让 Collector 负责 batching、内存限制、脱敏、采样和路由。将 traces、metrics、logs 发送到对应 backend,并统一 service、version、environment、region 和 deployment 属性。在开启生产采样前,先在 staging 中验证一条完整 trace。
Grafana 适合共享运维视图。仪表盘可以包含请求量、成功率、根延迟的 p50/p95/p99、首令牌时间、令牌、估算成本、重试率、工具耗时和供应商状态。为面板添加 trace 搜索和 runbook 链接。Grafana 告警规则文档介绍查询、条件、评估周期和通知行为。Sentry 可以补充问题分组、错误上下文和 trace 检查。Sentry Trace API提供单条 trace 包含的 spans 和 errors。无论使用哪种错误后端,都只发送已脱敏数据,并明确配置采样。
可操作的 SLO 和告警
SLO 应表达用户可见的承诺,而不是 Collector 的健康状态。可以把可用性定义为没有分类的服务器、供应商或工具错误的完成运行比例。可以把延迟定义为低于指定阈值的根运行比例。如果成本是产品约束,单独跟踪 budget SLI。
根据测量基线和产品需求选择目标,对期望不同的工作流分别设定 SLO。在长窗口报告 error budget,并为部署保留短视图。Grafana SLO 文档介绍 SLI、预算消耗以及 fast-burn 和 slow-burn 告警。值班人员有明确行动时才发送 page,慢速趋势创建工单。
有用的告警包括根运行失败持续上升、error budget 快速消耗、p95 超过合同、供应商速率限制激增、重试比例上升、usage 记录缺失、意外成本速率和停滞队列。告警中包含工作流、区域、部署、当前值、阈值、trace 搜索链接、负责人和 runbook。配置 pending period,避免单个样本触发通知。按服务和严重性分组。没有立即行动时,用仪表盘而非告警。
从症状定位原因
从 SLO 或用户报告开始,选择一条代表性 trace。检查根 span 是否包含完整子 span,并检查 gateway、queue 和工具边界处的上下文。如果 spans 缺失,先检查 exporter 健康、采样决定和上下文注入,再修改业务代码。
对于慢运行,比较队列等待、首令牌时间、输出时长、检索和工具关键路径。p99 高而 p50 正常,通常指向尾部依赖、并发限制或重试。p50 整体移动则可能由部署、模型、prompt 或区域变化引起。成本上升时,按实际响应模型、令牌类别、工作流版本和尝试次数分组,并与供应商报告对账。
对于错误,从第一条失败 span 开始,不要从最后的包装异常开始。检查 status、error.type、供应商代码、超时预算和重试事件。区分工具拒绝、供应商中断、模型格式错误和应用解析器错误。确认脱敏没有删除唯一安全的诊断代码。把预期的策略阻断作为产品结果统计,只有比例意外变化时才告警。
保留使用非敏感、确定性 fixtures 的合成请求。修改 instrumentation、模型、prompt 或路由后,确认 traces、metrics、令牌记录和错误使用同一个 correlation ID。架构背景可参考 production AI agent architecture、AI agent evaluations、prompt injection and MCP security 和 hybrid RAG with pgvector。这些主题影响应创建哪些 spans 以及 SLO 如何定义,而可观测性仍是中立的证据层。