{SITE.title} logo 云萱笔记
可观测性 · · 5 分钟阅读

AI Agent 可观测性实战:从日志、追踪到指标的全链路体系设计

前言

在传统软件系统中,可观测性(Observability)已经是一个非常成熟的领域——日志、指标、追踪三大支柱,配合 ELK、Prometheus、Jaeger 等工具链,形成了完整的方法论。

但在 AI Agent 系统中,可观测性面临全新的挑战:

  • LLM 调用是非确定性的,同样的输入可能产生不同的输出
  • Agent 的决策路径是动态的,每一步调用什么工具、走什么分支,事前无法预知
  • 错误不再是”非对即错”,而是质量退化——回答可能没错,但质量下降了
  • 性能问题不仅是”慢”,还有 Token 消耗、首字延迟、吞吐等新维度

本文将基于云萱 AI 原生工程平台的实践,详细拆解一套适用于 AI Agent 系统的可观测性体系,包括:

  • Tracer 的多后端架构设计
  • 4 ID 关联体系
  • 12 种 Span 类型覆盖全场景
  • Metrics 指标收集
  • 如何用可观测性工具定位 Agent 缺陷

一、AI Agent 可观测性的特殊性

1.1 传统可观测性 vs AI 可观测性

维度传统系统AI Agent 系统
执行路径固定的代码路径动态决策路径
输出确定性100% 确定概率性输出
错误定义非对即错质量退化 continuum
性能指标延迟、QPSTTFT、TPS、Token 消耗
调试难度可复现难以复现
成本计算资源Token 费用

1.2 AI Agent 需要观察什么?

在 AI Agent 系统中,除了传统的错误和延迟,你还需要回答这些问题:

  1. 功能正确性

    • Agent 调用了正确的工具吗?
    • 工具返回的结果被正确使用了吗?
    • 最终回答和问题相关吗?
  2. 效率问题

    • 完成一个任务需要多少步?
    • 有没有重复调用、无效工具调用?
    • LLM 调用花了多少钱?
  3. 质量退化

    • 最近 Agent 的回答质量下降了吗?
    • 是哪个环节导致的?(模型版本?Prompt 变更?工具变更?)
  4. 用户体验

    • 用户需要等多久才能看到第一个字?
    • 整个任务完成需要多长时间?

这就要求我们的可观测性系统不仅要”记录发生了什么”,还要能帮助理解为什么会这样。


二、整体架构:Tracer + Metrics + Logging

2.1 三层架构

我们的可观测性系统分为三层:

┌───────────────────────────────────────────────────────┐
│                  可观测性统一接口层                      │
│  ┌──────────┐  ┌──────────────┐  ┌────────────────┐  │
│  │  Tracer  │  │   Metrics    │  │  Logging       │  │
│  │  (追踪)   │  │   (指标)     │  │  (日志)        │  │
│  └────┬─────┘  └──────┬───────┘  └───────┬────────┘  │
└───────┼───────────────┼───────────────────┼───────────┘
        │               │                   │
┌───────▼───────────────▼───────────────────▼───────────┐
│                    多后端适配层                         │
│  ┌─────────┐  ┌──────────┐  ┌────────┐  ┌──────────┐  │
│  │ Console │  │LangSmith │  │Langfuse│  │   OTel   │  │
│  │ (本地)   │  │ (官方)   │  │(开源)  │  │(标准协议) │  │
│  └─────────┘  └──────────┘  └────────┘  └──────────┘  │
└───────────────────────────────────────────────────────┘

三层各司其职:

  • Tracer:记录 Agent 的完整执行轨迹(每一步调用、每一次工具使用、每一个决策)
  • Metrics:统计聚合指标(调用次数、成功率、耗时分布、Token 消耗)
  • Logging:结构化日志,用于详细排查

2.2 统一 ID 体系

传统分布式追踪只有一个 trace_id,但在 AI Agent 系统中,我们需要4 个层级的 ID 来关联不同维度的数据:

ID作用生命周期
trace_id全局追踪 ID,贯穿一次完整的 Agent 执行一次任务
workflow_id工作流 ID,关联一次端到端的工作流执行一个工作流
session_id用户会话 ID,关联同一个用户的多次交互一次会话
user_id用户 ID,用于按用户维度统计分析长期

这种设计让我们可以从不同维度查询数据:

  • 按 trace_id 查单次执行的完整轨迹
  • 按 workflow_id 查某个工作流的所有执行
  • 按 session_id 查一次会话中的所有交互
  • 按 user_id 统计某个用户的使用情况

三、Tracer 设计详解

3.1 12 种 Span 类型

我们定义了 12 种 Span 类型,覆盖 AI Agent 的全场景:

Span 类型用途触发时机
llm_callLLM 调用每次调用大模型
tool_call工具调用(开始)Agent 决定调用工具时
tool_result工具返回(结束)工具执行完成时
agent_stepAgent 步骤每个 Agent 推理步骤
rag_queryRAG 查询每次向量检索
approval审批操作人工审批节点
security_event安全事件检测到安全风险时
stage_transition阶段流转Multi-Agent 阶段切换
checkpoint检查点到达人工检查点
evaluation评估执行运行评测时
error错误发生异常时
custom自定义用户自定义追踪

每种类型都映射到对应的 LangSmith / Langfuse run_type:

_SPAN_TYPE_TO_RUN_TYPE: dict[str, str] = {
    "llm_call": "llm",
    "tool_call": "tool",
    "tool_result": "tool",
    "agent_step": "chain",
    "rag_query": "retriever",
    # ...
}

这样,我们自己的 Tracer 定义了一套标准语义,然后自动映射到各个后端的概念上。

3.2 多后端架构

Tracer 支持 5 种后端,通过策略模式统一管理:

            ┌──────────────────┐
            │     Tracer       │
            │  (统一接口)       │
            └─────────┬────────┘
                      │
          ┌───────────┼───────────┐
          │           │           │
    ┌─────▼────┐ ┌───▼────┐ ┌───▼─────┐
    │ _Base    │ │Console │ │ Noop    │
    │ Backend  │ │Backend │ │ Backend │
    └─────┬────┘ └────────┘ └─────────┘
          │
    ┌─────▼─────────┬──────────────┐
    │               │              │
┌───▼──────┐ ┌──────▼─────┐ ┌─────▼────┐
│ LangSmith│ │  Langfuse  │ │   OTel   │
│ Backend  │ │  Backend   │ │ Backend  │
│(生产级)   │ │ (开源)     │ │(标准协议) │
└──────────┘ └────────────┘ └──────────┘

基类定义了统一的接口:

class _BaseBackend:
    """追踪后端基类。"""
    def start_span(self, span: TraceSpan) -> None: pass
    def end_span(self, span: TraceSpan) -> None: pass
    def flush(self) -> None: pass

每个后端实现这三个方法即可。新增后端时,只需要加一个子类,不需要改 Tracer 本身。

后端选择策略

  • 本地开发 → console 后端,日志直接输出到终端
  • 生产环境(LangChain 生态) → langsmith 后端,功能最完善
  • 生产环境(自托管) → langfuse 后端,开源可私有化
  • 企业级(标准协议) → otel 后端,对接现有 APM 系统(待实现)
  • 不需要追踪 → noop 后端,零开销

自动降级机制:如果配置的后端初始化失败,自动降级为 console 后端,保证主流程不受影响。

elif backend == "langsmith":
    ls = _LangSmithBackend()
    if ls.available:
        return ls
    logger.warning("[Tracer] LangSmith 不可用,降级为 console 后端")
    return _ConsoleBackend()

3.3 Span 的数据模型

每个 Span 包含这些关键字段:

class TraceSpan(PlatformBaseModel):
    """追踪 span。"""
    span_id: str = ""          # Span 唯一 ID
    trace_id: str = ""         # 全局追踪 ID
    parent_id: str = ""        # 父 Span ID(构建树结构)
    workflow_id: str = ""      # 工作流 ID
    session_id: str = ""       # 会话 ID
    user_id: str = ""          # 用户 ID

    span_type: str = "custom"  # 12 种类型之一
    name: str = ""             # Span 名称
    status: str = "success"    # success / failed / blocked / pending

    start_time_ms: float = 0.0  # 开始时间
    end_time_ms: float = 0.0    # 结束时间
    duration_ms: float = 0.0    # 持续时间

    input_snapshot: str = ""    # 输入快照(用于回放和调试)
    output_snapshot: str = ""   # 输出快照
    metadata: dict[str, Any] = {}  # 附加元数据

输入输出快照是 AI Agent 可观测性的关键——你不仅需要知道”调用了一次 LLM”,还需要知道”当时的输入是什么、输出是什么”,才能调试非确定性问题。

当然,生产环境中可以通过配置控制是否开启快照,避免敏感数据泄露和存储成本过高。

3.4 上下文管理器 API

为了让业务代码用起来最简洁,我们提供了上下文管理器形式的 API:

from app.observability.tracer import get_tracer

tracer = get_tracer()
tracer.new_trace()

# 最简单的用法
with tracer.span("处理用户请求", span_type="agent_step"):
    # 业务逻辑
    result = do_something()

# 带输入输出快照
with tracer.span(
    "调用知识库检索",
    span_type="rag_query",
    input_snapshot=query,
) as span:
    results = vector_store.search(query)
    span.output_snapshot = str(results)  # 手动设置输出

Span 的父子关系自动维护——栈式管理,进入一个 span 就 push,退出就 pop,嵌套的 span 自动建立父子关系。

也支持手动创建和结束:

span = tracer.start_span("工具调用", span_type="tool_call")
try:
    result = tool.execute()
    tracer.end_span(span, status="success", output_snapshot=str(result))
except Exception as e:
    tracer.end_span(span, status="failed", metadata={"error": str(e)})
    raise

四、Metrics 指标收集

4.1 四种指标类型

MetricsCollector 支持四种标准的指标类型:

指标类型用途示例
Counter计数器(只增不减)LLM 调用总次数、Token 总消耗
Gauge仪表值(可升可降)当前活跃会话数、队列长度
Histogram直方图(统计分布)延迟分布、Token 数分布
Timer计时器(自动记录到直方图)LLM 调用耗时、工具执行时间
from app.observability.metrics import MetricsCollector

metrics = MetricsCollector()

# Counter
metrics.increment("llm_call_total", 1)
metrics.increment("token_used", 1500)

# Gauge
metrics.set_gauge("active_sessions", 42)

# Histogram
metrics.observe("llm_latency_ms", 230.5)

# Timer
metrics.start_timer("tool_execution")
# ... do work ...
metrics.stop_timer("tool_execution")

4.2 统计分析

Histogram 自动计算 P50、P95、P99 等统计值:

stats = metrics.histogram_stats("llm_latency_ms")
# 返回: {
#   "count": 100,
#   "min": 50.0,
#   "max": 500.0,
#   "avg": 150.0,
#   "p50": 120.0,
#   "p95": 350.0,
#   "p99": 480.0,
# }

这对于性能退化检测非常有用——当 P95 延迟突然升高时,可能是模型版本变更、API 限流或网络问题。


五、用可观测性定位 Agent 缺陷

有了完整的可观测性数据,我们是怎么定位 Agent 问题的呢?

5.1 典型缺陷定位流程

假设用户反馈”Agent 回答质量下降了”,我们的排查路径:

第一步:看 Metrics 宏观指标

- LLM 调用成功率 → 正常
- 平均响应时间 → 正常
- 工具调用成功率 → 正常
- Token 消耗 → 比上周增加了 30%

Token 消耗增加但成功率没变 → 说明 Agent 可能在”绕弯路”——调用更多步骤才能完成任务。

第二步:按 trace_id 抽样查看

找出最近几个耗时最长、Token 消耗最多的 trace,查看详细轨迹。

第三步:分析 Trajectory

发现某个 trace 中:

agent_step: 分析用户问题
  → tool_call: search_knowledge_base (第一次查询)
  → tool_result: 返回 5 个结果
  → agent_step: 阅读检索结果
  → tool_call: search_knowledge_base (第二次查询,换了关键词)
  → tool_result: 返回 5 个结果
  → agent_step: 综合回答
  → tool_call: search_knowledge_base (第三次查询...)

Agent 进行了 3 次重复的知识库检索——说明 RAG 检索质量有问题,第一次没找到答案,Agent 只好反复尝试。

第四步:定位根因

查看每次 rag_query 的 input_snapshot 和 output_snapshot,发现:

  • 第一次查询关键词太宽泛
  • 返回的结果虽然相关但不直接
  • Agent 只好换关键词再查

根因:知识库的 Chunk 策略需要优化,或者检索算法需要调整。

5.2 可观测性能帮我们发现的问题

问题类型怎么发现看什么数据
工具调用错误率高tool_call 的 failed status 增多tool_result 的错误信息
Agent 决策变慢agent_step 的 duration 变长LLM 调用耗时 + Token 数
回答质量下降evaluation 得分降低人工抽检 + LLM-as-Judge
安全风险增加security_event 数量突增注入检测得分、攻击类型分布
成本失控token 消耗 counter 异常增长每次调用的 Token 数分布
RAG 质量差重复 rag_query 次数增多查询内容 + 返回结果

六、实践经验与踩坑

6.1 不要一开始就上复杂方案

很多团队一开始就想上全套 LangSmith + Langfuse + OTel,结果:

  • 接入成本太高,迟迟用不起来
  • 数据量爆炸,存储成本居高不下
  • 团队还没建立”看数据”的习惯

我们的建议:从 console 后端开始,先把追踪点打好,养成看数据的习惯,再逐步升级后端。

6.2 输入输出快照要谨慎

快照是调试 AI 问题的神器,但也是”双刃剑”:

  • ⚠️ 敏感数据泄露:用户输入可能包含隐私信息
  • ⚠️ 存储成本高:一次 Agent 执行可能有几十次 LLM 调用,每次都存完整对话会很占空间
  • ⚠️ 合规风险:某些行业有数据留存的法规要求

建议做法:

  1. 开发环境全开
  2. 测试环境采样(比如 10%)
  3. 生产环境默认关闭,需要时按 trace_id 动态开启

6.3 指标不要贪多

不是所有东西都需要做成 Metric。Metric 的价值在于趋势和告警——如果一个指标你不会设置告警,也不会经常看,那就不要加。

我们的经验是:先加 5 个最核心的指标,用起来了再慢慢加。

核心指标推荐:

  1. LLM 调用次数 / 失败率
  2. LLM 平均延迟(P50/P95)
  3. Token 总消耗(成本)
  4. 工具调用成功率
  5. 任务完成率(完整走完全流程的比例)

6.4 Trace 是给人看的,不是给机器看的

设计 Span 的 name 和 metadata 时,要想着”一个工程师凌晨 3 点被叫醒排查问题,他能不能快速看懂”。

好的 Span 命名:

  • ✅ search_knowledge_base(清楚地知道在做什么)
  • ✅ generate_test_cases[TesterAgent](知道哪个 Agent 在做什么)
  • ❌ step_3(完全不知道在干嘛)
  • ❌ call_llm(太泛了,哪个模型?什么用途?)

七、未来演进方向

我们的可观测性体系还在持续演进,接下来的方向:

  1. OpenTelemetry 后端:对接企业现有 APM 系统,统一可观测性栈
  2. 智能异常检测:基于历史数据自动检测异常(延迟突增、质量下降、成本异常)
  3. 根因分析(RCA)辅助:自动关联多个 Span,给出”最可能的问题原因”
  4. Agent Trajectory 质量评分:自动评估 Agent 的决策路径质量(有没有走弯路、是否最优)
  5. Prompt 版本追踪:把 Prompt 版本也纳入 trace,方便分析”哪个 Prompt 版本效果最好”

结语

AI Agent 的可观测性不是”锦上添花”,而是生产环境的刚需。

没有可观测性的 Agent 系统,就像一辆没有仪表盘的汽车——你能开,但不知道速度多少、油还剩多少、发动机有没有过热。等出了问题,只能瞎猜。

希望这篇文章的实践经验,能帮你搭建起自己的 AI Agent 可观测性体系。


相关阅读:AI 工程质量保障体系:确定性评估优先的设计哲学