Agent 出问题时,日志里只有一句「回答不对」。你需要知道:它看到了什么、选了哪个工具、传了什么参数、模型为什么这么决定。
Trace 与 Span
一次用户请求是一条 Trace,其中的每个操作是一个 Span:
trace: task_8f2c (总耗时 12.4s, 18,930 token)
├─ span: input_guard 12ms
├─ span: retrieve 340ms
│ ├─ span: embed 80ms
│ └─ span: vector_search 210ms
├─ span: llm_call_1 2.1s (model=..., 4,200 in / 180 out)
├─ span: tool:search_docs 620ms (ok, 3 results)
├─ span: llm_call_2 1.8s
├─ span: tool:create_refund 410ms (ok, refund_id=...)
├─ span: llm_call_3 3.2s
└─ span: output_guard 9ms没有这种树状结构,你只能看到「总耗时 12 秒」,无法知道是检索慢、模型慢,还是重试了三次。
每个 Span 要记录什么
| 字段 | 说明 |
|---|---|
trace_id / span_id / parent_id | 关联关系 |
name | 操作名,如 llm.chat、tool.search |
start_time / end_time | 耗时 |
status | ok / error / cancelled |
model / provider | 模型版本 |
prompt_version | 提示词版本号 |
input_tokens / output_tokens | 成本 |
temperature / max_tokens | 参数 |
tool_name / arguments / result_summary | 工具调用 |
error_code / error_message | 错误 |
user_id / tenant_id | 归因(脱敏) |
session_id / task_id | 多轮与任务关联 |
不要记录完整提示词和完整工具返回(体积大、含敏感信息)。记录哈希、长度、摘要,需要时再按 ID 取原文。
提示词版本化
提示词必须能对应到一次具体运行:
{
"prompt_id": "support_agent",
"prompt_version": "v14",
"prompt_hash": "sha256:9f2c...",
"template_vars": { "locale": "zh-CN", "plan": "pro" }
}没有版本号,你无法回答「上周通过率下降是不是因为改了提示」。提示词应该和代码一起提交、一起发布。
工具调用要记录成败与参数
工具是最容易出问题的环节:
- 模型传错参数;
- 权限不足;
- 超时;
- 返回空结果;
- 部分失败。
记录:
{
"tool": "create_refund",
"arguments": { "order_id": "A1024", "amount": 299.0 },
"status": "error",
"error_code": "AMOUNT_EXCEEDS_PAID",
"retryable": false,
"duration_ms": 210
}参数可能含 PII,入库前脱敏;但保留结构,便于统计「哪个字段最容易错」。
指标面板
至少四组:
质量
- 任务成功率;
- 人工接管率;
- 用户重试/重新生成率;
- 评估集通过率(按版本)。
过程
- 平均步数 / P95 步数;
- 工具调用次数与错误率;
- 死循环检测次数;
- 重试率;
- 拒答率。
性能
- TTFT;
- 端到端 P50/P95/P99;
- 各 Span 耗时占比;
- 队列等待时间。
成本
- 每任务 token;
- 每任务金额;
- 缓存命中率;
- 按模型/工具/租户拆分。
把这四组放在一个面板上,能快速判断「变慢了」是模型、工具还是流量。
调试工作流
- 定位 Trace:按 task_id、用户、时间、错误码找到具体请求;
- 看时间线:哪一步最慢?哪一步开始偏离?
- 看上下文:模型当时看到了什么?检索结果相关吗?工具返回完整吗?
- 看决策:为什么选这个工具?参数从哪来?
- 复现:用相同的模型版本、提示版本、工具 mock 重跑;
- 修复:改提示/工具/流程;
- 回归:加入评估集,防止再犯。
可回放(replay)是 Agent 可观测性的核心能力。理想情况下,能回到任意一步,修改状态后从那里继续。
采样与成本
全量记录成本高。策略:
- 错误全量:所有失败请求完整记录;
- 慢请求全量:P95 以上全量;
- 正常请求采样:1%–10%;
- 高风险动作全量:退款、发送、删除;
- 保留期分层:原始 trace 7–30 天,聚合指标长期保留。
常见工具
| 工具 | 特点 |
|---|---|
| OpenTelemetry | 标准协议,可对接任意后端;GenAI 语义约定在完善 |
| Langfuse | 开源,trace、评估、提示管理一体 |
| LangSmith | 与 LangChain 生态结合紧密 |
| Arize Phoenix | 开源,偏评估与可观测 |
| Helicone | 网关层,易接入,成本分析 |
| Braintrust | 评估与实验管理 |
自建也不难:OpenTelemetry + ClickHouse/BigQuery + Grafana。关键是字段设计一致。
告警
不要只告警「错误率」。Agent 特有的信号:
- 平均步数突然上升(可能陷入循环);
- 工具错误率上升(下游 API 变了);
- 拒答率上升(检索/提示坏了);
- 单任务成本突增;
- P95 延迟突增;
- 某租户错误率异常(隔离问题);
- 评估集通过率下降(发版回归)。
告警要带 trace 链接和最近一次失败样本,否则值班的人还得自己找。
隐私与合规
- 日志脱敏:手机号、邮箱、身份证、地址、token;
- 提示词里不放大段 PII;
- 工具参数按字段脱敏,而不是整条丢弃;
- 支持按用户删除 trace;
- 跨境数据存储要合规;
- 访问日志本身要审计。
反模式
出问题无法定位。至少记录工具调用、模型调用、错误。
体积大、含敏感信息、检索困难。记录版本 + 哈希 + 摘要。
用户投诉时找不到对应请求。task_id、session_id、user_id 必须贯穿。
无法归因。提示词必须版本化。
平均值掩盖长尾。要看 P95/P99 和失败样本。
检查清单
- 每次请求有 trace_id,贯穿所有调用
- LLM、工具、检索、审批都有 span
- 记录模型、提示版本、token、耗时、错误
- 工具参数与结果有摘要,PII 脱敏
- 有质量、过程、性能、成本四组面板
- 错误与慢请求全量保留,正常采样
- 能按 trace 回放与复现
- 关键异常有告警与样本链接
小结
- Agent 调试依赖树状 trace,而不是一行日志。
- 模型、提示版本、工具参数、token、错误,一个都不能少。
- 指标要覆盖质量、过程、性能、成本。
- 错误全量、正常采样,兼顾成本与可追溯。
- 可回放是从「猜」到「定位」的关键。
下一篇讲失败时怎么兜住:可靠性工程。