传统微服务 APM 在 Agent 时代的失效
传统的 APM 工具只关注 HTTP 响应码和 RPC 耗时。但对于一个 Agent 而言,即便所有工具调用都返回了 HTTP 200,它依然可能输出一段完全错误的死循环代码。
我们需要记录的是智能体的决策语义链条。
语义约定与 Span 组织
利用 OpenTelemetry,我们将一个复杂任务建模为树状 Span 结构:
- Root Span (Task): 记录原始用户指令与最终完成状态;
- Child Span (LLM Inference): 记录输入 Prompt 摘要、使用的模型参数、TTFT 与 Token 消耗;
- Child Span (Tool Invocation): 记录 MCP 工具名称、入参 JSON、执行耗时与返回数据大小;
- Child Span (Evaluation / Critic): 记录质量门禁或单元测试是否通过。
这套可观测体系让团队能够像调试传统微服务一样,精准复盘每一个不确定性推理过程。
落地示例:先把属性约定定下来
字段命名一旦混乱,链路就只能看个形状。建议用统一前缀自定义属性,并在团队文档里固化下来:
agent.task.id 任务标识,跨重试保持一致
agent.step.index 当前步骤序号
agent.model.name 模型标识
agent.tokens.input 输入 Token 数
agent.tokens.output 输出 Token 数
agent.tool.name 被调用的 MCP 工具名
agent.tool.result.size 工具返回载荷字节数
agent.decision.outcome 成功 / 失败 / 需人工介入
上面的键名是本方案的内部约定,接入时替换成你们自己的前缀即可;OpenTelemetry 各语言 SDK 的属性字段写法差异较大,以所用 SDK 文档为准。
另外三条经验:提示词原文不要整段塞进 Span,改存摘要加外部存储引用,否则后端会被撑爆;重试要作为子 Span 而不是覆盖父 Span 状态,否则看不出真实尝试次数;工具返回的大结果只记录大小和截断样本。
记录粒度与成本取舍
| 采集级别 | 内容 | 存储成本 | 适用阶段 |
|---|---|---|---|
| 仅结构与指标 | 层级、耗时、Token 数 | 低 | 全量常开 |
| 加输入输出摘要 | 哈希与首尾片段 | 中 | 长期保留 |
| 全量提示与返回 | 完整原文 | 高 | 采样或排障期 |
通常的做法是全量开第一二级,第三级按任务采样,并把采样比例做成可动态调整的配置,事故时临时放大。
验证方法
用一个已知会失败的任务验收:任务在第三次工具调用后进入死循环,如果链路视图里能一眼看出"同一工具同参数被反复调用且结果一致",说明结构有效;看不出来就缺步骤序号或参数摘要。再加两项硬指标核对:Span 里的 Token 合计与供应商账单统计的差异是否可解释;任意一次线上事故能否在几分钟内定位到具体步骤。
常见故障速查
| 现象 | 原因 | 处理 |
|---|---|---|
| 链路里看不到工具调用 | 上下文未随异步调用传播 | 显式传递父级上下文再创建子 Span |
| 一个任务被拆成多条断链 | 任务经消息队列异步续跑 | 用任务标识做关联键串接 |
| 后端存储暴涨 | 完整提示词进了属性 | 改存摘要与外部引用 |
| Token 统计与账单差很多 | 未计工具回传与重试 | 在推理 Span 中记录实际请求量 |
| 只有耗时看不出对错 | 缺少结果语义字段 | 补 outcome 与评测 Span |
小结
可观测性做没做好,只有一个判据:随机挑一次线上失败,能否在链路里指出"哪一步、基于什么输入、做了什么决定、为什么错"。四问答不上任何一条,就说明缺了语义字段而不是缺工具。评估体系如何与链路数据打通,可参考 如何科学评估一个 Agent 的好坏?从 MMLU 到 GAIA 与 AgentBench 评测标准的演进。