更新于

AI Agent 可观测性设计


传统应用的可观测性问了三个问题:请求慢在哪、错误发生在哪、资源用了多少。Agent 系统多问一个更难的问题:它为什么做了这个决定。一个 Agent 可能自主规划出 10 步任务、动态选择调用哪个工具、根据上一步结果改变下一步计划——出了问题之后,日志里一堆文本,却很难重建”它当时到底在想什么”。本文讲清 Agent 可观测性该怎么设计,而不是介绍某一个具体工具。

一、Agent 可观测性比传统监控多出的维度

普通 Web 服务监控关心延迟、错误率、吞吐量三件事。Agent 系统在这基础上,还要观测:

  • 决策路径:Agent 每一步选择了哪个工具、为什么选它(是模型自己”想”的,不是代码里写死的分支)
  • 中间状态:多轮规划中,Agent 对任务的理解是否随着执行发生了偏移
  • 输出质量:不像传统系统”有输出就算成功”,Agent 的输出可能格式正确但内容是错的(幻觉、工具参数编造),质量判断需要额外一层
  • 成本归因:一次用户请求可能触发几十次模型调用,需要知道钱花在了流程的哪一步

关于成本这一维度的具体实现,已经有专门的深度文章,看 AI Agent 单会话成本监控实现;关于错误定位的具体方案,看 AI 流水线的错误追踪方案。本文聚焦这几个维度之上的整体架构设计:怎么把它们串成一套统一的可观测性系统。

二、核心数据模型:Trace / Span / Event 三层结构

照搬分布式追踪的经验,但要针对 Agent 的特点做调整:

Trace(一次完整的用户请求)
 └─ Span(Agent 的一次"思考-行动"循环)
     ├─ Event: 模型调用(Prompt、输出、token 数、耗时)
     ├─ Event: 工具调用(工具名、入参、返回值、是否报错)
     └─ Span(子任务,如果 Agent 做了任务拆分)

关键设计点:

  • Span 要绑定”决策依据”:不只是记录”调用了搜索工具”,还要记录模型在这一步收到的完整上下文和输出的原始文本,否则事后无法判断”为什么模型选了这个工具”
  • Trace ID 要贯穿到底层模型调用:包括重试、fallback 到备用模型的情况,一次用户请求无论内部转了几圈,都要能用一个 ID 串起来
  • 区分”计划内分支”和”计划外分支”:Agent 走了预期路径 vs 因为工具报错临时改变计划,这两种情况在排障时的意义完全不同,最好用不同的 Event 类型区分

三、最小可用埋点怎么写

不需要一开始就上重型平台,一个装饰器就能跑起来基础版本:

import time
import uuid
import json

def traced_step(span_name):
    def decorator(func):
        def wrapper(*args, trace_id=None, **kwargs):
            span_id = str(uuid.uuid4())
            start = time.time()
            try:
                result = func(*args, trace_id=trace_id, **kwargs)
                status = "success"
                return result
            except Exception as e:
                status = "error"
                result = {"error": str(e)}
                raise
            finally:
                log_span({
                    "trace_id": trace_id,
                    "span_id": span_id,
                    "span_name": span_name,
                    "duration_ms": int((time.time() - start) * 1000),
                    "status": status,
                    "output_preview": json.dumps(result)[:500],
                })
        return wrapper
    return decorator

@traced_step("tool_call:web_search")
def call_search_tool(query, trace_id=None):
    ...

这个最小版本先落地三件事:每一步有唯一 span_id、每一步归属到同一个 trace_id、每一步记录耗时和状态。等规模上来了,再考虑接入专门的可观测性平台(比如自部署 Langfuse,具体部署方式看 LLM 可观测性实战:Langfuse 自部署完整指南),把这些字段结构化写入即可,架构不需要推倒重来。

四、告警该设在哪几条线上

Agent 系统的告警不能只靠”错误率超过阈值”这一种规则,几个 Agent 特有的告警场景:

告警场景触发条件示例为什么重要
循环未收敛单个 Trace 内 Span 数量超过预设上限(如 20)Agent 可能陷入”规划-失败-重新规划”死循环,不加限制会持续烧钱
工具调用参数异常率上升同一工具连续 N 次调用参数校验失败提示模型对工具 Schema 理解出现偏差,或者上游 Prompt 被改动影响了工具选择
单 Trace 成本突增单次请求成本超过历史 P95 的 3 倍比全局成本告警更早发现单个异常任务,配合预算控制看 AI API 预算上限设计
输出格式合规率下降结构化输出解析失败率环比上升往往是模型版本切换或者上游 Prompt 微调引入的回归

告警阈值不要一开始就定死,先跑一到两周收集基线数据(P50/P95 的 Span 数、成本、耗时),再基于基线设置相对阈值,比拍脑袋定绝对值更稳。

五、Dashboard 该展示什么,而不是塞满图表

排障时真正有用的视图通常只有三类:

  1. 单条 Trace 的完整时间线:点开一次用户请求,能看到从第一次模型调用到最终输出的完整决策链路,逐步展开每个 Span 的输入输出
  2. 按工具/按步骤聚合的失败率:哪个工具、哪个环节的失败率最高,比”整体错误率 5%“这种笼统数字有用得多
  3. 成本/延迟的分布而不是均值:均值会被极端值掩盖,P95、P99 更能反映真实用户体验,尤其是 Agent 这种耗时波动大的系统

不需要一开始就做大而全的 Dashboard,先把”点开一条 Trace 能看清全过程”这一条做扎实,其他视图可以逐步补。

六、自建还是用现成工具

方案适合场景代价
自建最小埋点(日志 + 结构化字段)早期项目、Agent 逻辑简单、团队小开发量小,但排查复杂问题时效率较低
自部署 Langfuse 等开源平台有一定规模、要求数据不出境、团队有运维能力部署和维护成本,换来完整的 Trace UI 和用量分析
商业 SaaS 可观测性平台追求快速上线、不介意数据出境按量付费,通常有免费额度,接入最快

国内团队如果数据合规是硬性要求,自部署开源方案通常是更稳妥的选择,具体合规考虑看 国内大模型数据合规指南。不管选哪种方案,核心原则不变:先把 Trace/Span/Event 的数据模型设计对,工具只是把这套模型可视化,模型设计错了,换什么工具都白搭。

七、相关阅读

搭建 Agent 可观测性系统离不开稳定、可追踪调用记录的模型接口,YoTradeApi 提供完整的调用日志和用量明细,方便和自建的 Trace 系统对账。