Skip to content

Agent 出错时怎么定位?Trace、日志与指标设计 ​

内容摘要:排查 Agent 问题、算成本、做评测,全都依赖可观测性。本章讲 Agent 需要记录的完整信号(模型输入输出、工具调用与结果、重试、Token、延迟、错误),以及结构化日志与脱敏,并给出一个最小 Trace 记录器。 难度:进阶 |前置知识:06 工具调用、08 Agent Loop

常见误区

记录用户问题和最终答案就足够排查故障,模型的中间调用与工具参数既复杂又没有必要保存。

先给结论 ​

Agent 故障跨越模型、编排和工具多层,没有 Trace(追踪)就无法复盘。每次运行应关联模型与 Prompt 版本、状态转换、工具请求与结果、重试、Token、延迟、费用和终止原因,并对敏感数据脱敏。

带着这三个问题阅读:

  1. Log、Metric、Trace 与 Agent 轨迹分别回答什么问题?
  2. 一次运行中哪些 Span 和字段必须可关联?
  3. 怎样在可诊断性与隐私、存储成本之间取舍?

一、为什么 Agent 更需要可观测性 ​

Agent 是"模型 + 多次工具调用 + 循环 + 非确定性",出问题时若没有完整轨迹,几乎无法定位根因(22)。三支柱 + Agent 专属信号:

二、一次 Agent 运行该记录什么 ​

信号内容用途
模型输入/输出messages、decision(脱敏)复现、评测
工具轨迹工具名、参数、结果摘要、耗时、成功/失败排障、成功率
重试与错误错误类型、重试次数可靠性
Token输入/输出 Token、按步累计成本(18)
延迟每 Span、端到端 P50/P95性能
停止原因ok/max_steps/circuit_open/...质量诊断

三、最小 Trace 记录器(可运行) ​

python
"""trace.py —— 最小 Trace/Span 记录器(可离线运行)。"""
from __future__ import annotations
import time, json, re
from contextlib import contextmanager
from dataclasses import dataclass, field

SECRET = re.compile(r"sk-[A-Za-z0-9]{6,}")
def _redact(v):
    return SECRET.sub("[REDACTED]", v) if isinstance(v, str) else v

@dataclass
class Trace:
    trace_id: str
    spans: list[dict] = field(default_factory=list)
    tokens_in: int = 0
    tokens_out: int = 0

    @contextmanager
    def span(self, name: str, **attrs):
        start = time.monotonic()
        rec = {"name": name, "attrs": {k: _redact(v) for k, v in attrs.items()}}
        try:
            yield rec
            rec["status"] = "ok"
        except Exception as e:
            rec["status"] = "error"; rec["error"] = str(e)
            raise
        finally:
            rec["elapsed_ms"] = round((time.monotonic() - start) * 1000, 1)
            self.spans.append(rec)

    def add_usage(self, tin: int, tout: int):
        self.tokens_in += tin; self.tokens_out += tout

    def summary(self) -> dict:
        return {"trace_id": self.trace_id, "spans": len(self.spans),
                "tokens_in": self.tokens_in, "tokens_out": self.tokens_out,
                "total_ms": round(sum(s["elapsed_ms"] for s in self.spans), 1)}

if __name__ == "__main__":
    tr = Trace("run-1")
    with tr.span("model.decide", prompt="key sk-abcdef123"):  # 密钥会被脱敏
        tr.add_usage(120, 40)
    with tr.span("tool.get_slow_queries", instance_id="mysql-test-01"):
        pass
    print(json.dumps(tr.summary(), ensure_ascii=False))
    print(tr.spans[0]["attrs"]["prompt"])   # 预期:key [REDACTED]

预期输出:summary 含 spans: 2、Token 计数;首个 span 的 prompt 中密钥被替换为 [REDACTED]。

四、结构化日志与脱敏 ​

  • 结构化:日志用 JSON,带 trace_id、step、tool、status,便于检索聚合。
  • 脱敏:密钥、Token、PII、内部地址一律脱敏(14)。Trace 是常见的密钥泄露点。
  • 关联:同一 trace_id 串起模型、工具、重试,便于端到端排障。

五、指标与告警 ​

关键指标:任务完成率、工具成功率、平均步数、P95 延迟、每任务 Token/成本、错误率、注入拦截数。设告警阈值(成本突增、成功率下跌、步数打满率上升)。

六、失败路径 ​

  • 只记最终答案:出错无法定位是模型、工具还是校验的问题。
  • Trace 记明文密钥:安全事故。
  • 无 trace_id 关联:多次工具调用无法拼成链路。

七、测试与验证 ​

  • 断言异常时 span 记录了 status=error 与错误;断言密钥被脱敏(见上例可直接测)。
  • 断言 Token/耗时被正确累计。

八、安全与权限 ​

  • Trace/日志的访问要鉴权;含敏感字段的 Trace 加密与保留期管理。

九、性能与成本 ​

  • 采样:全量 Trace 成本高,可对正常流量采样、对错误全量。Token 统计是成本核算基础(18)。

十、框架差异 ​

  • OpenTelemetry 有 GenAI 语义约定(gen_ai.*);LangSmith 等提供托管 Trace。原则一致:结构化、可关联、脱敏。

十一、关联章节 ​

面试复述 ​

Agent 可观测性要能回答“哪个版本在什么状态下,基于什么证据调用了什么工具,结果如何,为什么停止”。日志、指标与 Trace 必须统一 trace_id,并默认脱敏。

复述时先给结论,再补机制、适用边界和一个可验证指标;不要只罗列框架名称或产品功能。

迁移练习 ​

为一次包含两次模型调用和一次工具调用的任务设计 Trace 树,列出每个 Span 的必填字段、敏感字段和聚合指标。

检索关键词 ​

Agent 可观测性 Trace Span Token 消耗统计 工具轨迹 结构化日志 脱敏 OpenTelemetry GenAI

官方来源 ​

  • OpenTelemetry GenAI 语义约定;LangSmith/各观测平台文档(verified_at: 2026-09-07)。

ICP 备案号待补充 · Gitee 主页