Skip to content

第17章:可观测性(Observability)—— 你的 Agent 在第 6 步到底干了什么

2026 年 2 月,一篇关于 Agent 故障特征的研究论文分析了 385 个真实 Agent 故障案例,归纳出 37 类故障模式 [1]。其中一个核心发现:Agent 的故障与传统软件有本质区别——Agent 需要配置一个基于 LLM 的认知核心、维护跨多轮迭代的长期状态、并在动态环境中调用外部工具。这三件事的交叉,让"出了 bug → 找到根因 → 修复"这条路变得异常曲折。

上一章讲了怎么让 Agent 在生产环境中可靠运行——持久化执行、沙箱、容错。但"可靠运行"只是最低要求。更难的问题是:当一个 Agent 在第 6 步突然跑偏了,你怎么知道是模型幻觉、工具返回了脏数据、还是上下文里塞了太多无关信息?

如果你的系统回答不了这个问题,你还没有真正的可观测性。

17.1 为什么 Agent 可观测性不同于传统 LLM 监控

核心直觉

传统 LLM 监控关心的是"这次调用怎么样"——延迟多少、用了多少 token、返回了什么。Agent 可观测性关心的是"这 15 步加起来怎么回事"——为什么它在第 3 步选了错误的工具,为什么第 7 步的工具返回结果被第 10 步的推理误解了,为什么最终输出和第 1 步的用户意图南辕北辙。

从单次调用到因果链

传统 LLM 应用(比如一个翻译 API)的可观测性很简单:

用户输入 → LLM 调用 → 输出

你需要监控的就是:延迟、token 用量、输出质量。一个 Prometheus + Grafana 仪表盘就能搞定。

Agent 的执行路径长这样:

用户输入 → LLM 推理 → 选工具 A → 执行工具 A → 观察结果 →
LLM 推理 → 选工具 B → 执行工具 B → 失败 → 重试 →
LLM 推理 → 改用工具 C → 执行工具 C → 观察结果 →
LLM 推理 → 生成最终输出

这条链里,任何一个环节的问题都可能在后续环节被放大。工具 A 返回了一个含有误导性信息的结果,LLM 基于这个结果做出了错误推理,选择了不该选的工具 B,工具 B 失败了,重试后切到工具 C,工具 C 返回了正确结果但 LLM 已经被之前的错误推理带偏了——最终输出看起来格式正确、语言流畅,但答案是错的。

这就是 级联故障(cascading failure):根因在第 1 步,症状在第 10 步。如果你只监控最后一步的输出质量,你永远找不到根因。

传统监控 vs Agent 可观测性

维度传统 LLM 监控Agent 可观测性
关注对象单次 API 调用多步执行链
核心指标延迟、token、错误率因果链追踪、工具选择准确率、状态变迁
故障定位"这次调用报错了""第 3 步的工具返回导致第 7 步推理跑偏"
时间跨度毫秒到秒分钟到小时
数据模型指标(Metrics)+ 日志(Logs)追踪(Traces)+ 指标 + 日志 + 评估事件
调试方式看日志、看仪表盘重放执行轨迹、逐步检查状态变化

Galileo 的一篇关于多 Agent 调试的博客文章 [2] 指出了一个核心挑战:在多 Agent 系统中,一个单点的根因错误会沿着委托链传播到后续所有决策。如果没有逐 Agent、逐步骤的追踪,找到根因就像在一团乱麻里找线头。

17.2 核心概念:Trace 与 Span

核心直觉

Trace 是一次完整任务执行的时间线——从用户提交任务到 Agent 返回结果的全过程。Span 是这条时间线上的一个操作——一次 LLM 调用、一次工具执行、一次状态更新。Trace 由多个嵌套的 Span 组成,就像一棵树:根节点是整个任务,叶子节点是最小的操作单元。

Trace:一次执行的完整快照

一个 Trace 记录了 Agent 从接到任务到完成(或失败)的全过程。它不是简单的日志列表——它有结构。每个操作知道自己的父操作是谁、子操作有哪些、耗时多少、产生了什么结果。

一个典型的 Agent Trace 长这样:

agent.run "帮我查一下最近的 Jira 工单并总结" (总耗时 12.3s)
├── gen_ai.chat (推理:决定先搜索 Jira) ──── 1.2s
│   └── [输入: 用户请求 + 系统提示]
│   └── [输出: 调用 search_jira 工具]
├── tool.search_jira (执行工具) ──────────── 3.1s
│   └── [参数: project=PROJ, max_results=10]
│   └── [返回: 8 条工单]
├── gen_ai.chat (推理:分析工单内容) ──────── 2.8s
│   └── [输入: 8 条工单 + "请总结"]
│   └── [输出: 调用 get_issue_details 工具,获取前 3 条详情]
├── tool.get_issue_details (执行工具) ──────── 1.5s
│   └── [参数: issues=["PROJ-101","PROJ-102","PROJ-103"]]
│   └── [返回: 3 条工单详情]
├── gen_ai.chat (推理:生成总结) ──────────── 3.2s
│   └── [输入: 3 条工单详情]
│   └── [输出: 最终总结文本]
└── [状态: completed, 总 token: 4,230]

这棵树就是一个 Trace。你可以从任意节点往上追溯到根节点,也可以从根节点往下展开到任意叶子。当最终输出有问题时,你沿着树往上找:是最后一步的推理出错了?还是工具返回的数据有问题?还是更早的搜索就漏掉了关键工单?

Span:一个操作的详细记录

每个 Span 记录了一个操作的全部信息:

  • 操作类型:LLM 调用、工具执行、状态更新、Agent 间通信
  • 时间信息:开始时间、结束时间、耗时
  • 输入输出:发给 LLM 的 prompt、LLM 返回的内容、工具的参数和结果
  • 元数据:模型名称、temperature、token 用量、错误信息
  • 父子关系:这个操作属于哪个更大的操作

为什么不用日志

日志是时间顺序的文本流——"14:32:01 调用了 search_jira"、"14:32:04 返回了 8 条结果"。当你有 50 个 Agent 并发执行、每个跑 20 步时,日志就是一锅乱炖。你无法从日志中高效地回答"Agent-42 的第 3 步到底做了什么"。

Trace 是结构化的。它天然支持按任务维度聚合(给我 Agent-42 的所有操作)、按时间维度排序(这些操作的先后顺序是什么)、按因果维度追溯(这个错误的上游操作是什么)。

日志不是没用——它记录了 Span 内部的细节(比如一次 HTTP 请求的具体 header)。但日志是 Trace 的补充,不是替代。三者的关系:

  • Trace:回答"整个任务发生了什么"
  • Span:回答"某个操作发生了什么"
  • Log:回答"某个操作内部的细节"
  • Metric:回答"系统整体的统计趋势"

17.3 OpenTelemetry —— 一次埋点,任意后端

核心直觉

你不想在代码里写死 langsmith.log() 然后发现要换成 Langfuse 时得改几百个调用点。OpenTelemetry(OTel)是一套开放标准——你按它的规范埋点,数据可以发到 Jaeger、Grafana、Datadog、Langfuse 或任何兼容的后端。一次投入,终身不锁定。

OTel 是什么

OpenTelemetry 是 CNCF(云原生计算基金会)的开放可观测性标准,定义了 Trace、Metric、Log 三种信号的采集和传输规范。它不是一个产品,而是一套协议 + SDK + 采集器。你用 OTel SDK 在代码里创建 Span,Span 通过 OTel Collector 路由到你选择的后端——换后端只改 Collector 配置,代码不动。

这套标准在微服务领域已经是事实标准。现在,它正在扩展到 AI 和 Agent 领域。

GenAI 语义约定:Agent 可观测性的公共语言

OTel 的 GenAI SIG(Special Interest Group)正在定义一套专门用于 AI 系统的语义约定(Semantic Conventions)[3]。这套约定规定了:当你追踪一次 LLM 调用时,应该记录哪些属性、用什么名字。

截至 2026 年 4 月,GenAI 语义约定的状态:

约定类别状态说明
gen_ai.* 属性(模型名、token 用量等)Development(实验性)属性名可能变化
gen_ai.agent.* 属性(Agent ID、名称等)DevelopmentAgent 特有属性
GenAI 指标(token 用量直方图、延迟等)Development尚未稳定
GenAI 事件(推理详情、评估结果)Development包含 gen_ai.evaluation.result
核心 OTel 属性(error.typeserver.addressStable继承自 OTel 核心

关键事实:截至写作时,所有 gen_ai.* 属性都处于 Development 状态,没有一个被标记为 Stable [3]。这意味着属性名可能在未来版本中变化。OTel 提供了一个兼容机制:通过设置环境变量 OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental 来启用最新实验性约定。

GenAI 语义约定定义的核心属性包括 [3]:

请求属性(必需或推荐):

  • gen_ai.operation.name:操作类型(chatembeddingsexecute_toolcreate_agentinvoke_agent
  • gen_ai.provider.name:供应商(openaianthropicaws_bedrock
  • gen_ai.request.model:请求的模型名
  • gen_ai.request.temperaturegen_ai.request.max_tokens:推理参数

响应属性(推荐):

  • gen_ai.response.model:实际使用的模型
  • gen_ai.usage.input_tokensgen_ai.usage.output_tokens:token 消耗
  • gen_ai.response.finish_reasons:结束原因
  • gen_ai.response.time_to_first_chunk:流式场景的首 token 延迟

Agent 特有属性 [4]:

  • gen_ai.agent.id:Agent 唯一标识
  • gen_ai.agent.name:Agent 可读名称
  • gen_ai.agent.descriptiongen_ai.agent.version

敏感内容(默认关闭,需显式启用):

  • gen_ai.input.messages:完整聊天历史
  • gen_ai.output.messages:模型完整输出
  • gen_ai.system_instructions:系统提示词
  • gen_ai.tool.call.argumentsgen_ai.tool.call.result:工具调用详情

敏感内容默认关闭是正确的设计——生产环境的 Trace 可能包含用户 PII、API Key 或业务机密。你需要在可观测性和数据安全之间找到平衡:开发环境全开,预发布环境采样,生产环境默认关闭或脱敏后记录。

Agent Span 的两个操作

OTel 为 Agent 定义了两个 Span 操作 [4]:

  • create_agent:创建 Agent 实例。Span Kind 固定为 CLIENT
  • invoke_agent:调用 Agent 执行任务。如果是远程调用(如 OpenAI Assistants API、AWS Bedrock Agents),Span Kind 为 CLIENT;如果是进程内调用(如 LangChain、CrewAI),Span Kind 为 INTERNAL

这个区分很重要:当你的 Multi-Agent 系统中一个 Agent 通过 A2A 协议调用另一个 Agent 时,调用方的 invoke_agent Span 成为被调用方的父 Span。两个 Agent 的 Trace 通过 W3C traceparent 头连接成一条完整链。

跨服务追踪:MCP 和 Multi-Agent 的难点

OTel 的分布式追踪依赖 Context Propagation(上下文传播):每个出站请求携带 traceparent 头,下游服务读取后创建子 Span。在标准的 HTTP/gRPC 调用中,这是自动的。

但在 Agent 场景下有两个难点:

MCP 追踪断裂:MCP Client 和 MCP Server 是独立进程,通过 JSON-RPC 通信。MCP 协议目前没有为 trace context 定义标准传输位置——你需要手动把 traceparent 注入到 MCP 请求的元数据中 [5]。如果不做这一步,MCP Client 和 MCP Server 的 Trace 就是断开的——你看到两段不连续的轨迹,无法知道 Server 端的延迟是由哪个 Client 请求触发的。

Multi-Agent 追踪协调:当 Agent A 通过 A2A 协议调用 Agent B 时,两边都需要兼容的 OTel 埋点才能形成连续 Trace。实际中,不同团队用不同框架构建 Agent——LangGraph 的 Agent 调用 CrewAI 的 Agent——追踪的连续性完全取决于两边是否都正确传播了 trace context。截至写作时,这个问题没有自动化的解决方案。

一个最小的 OTel 埋点示例

python
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

# 初始化(应用启动时执行一次)
provider = TracerProvider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
trace.set_tracer_provider(provider)

tracer = trace.get_tracer("my-agent", "1.0.0")

def run_agent(task: str) -> str:
    with tracer.start_as_current_span("agent.run") as root_span:
        root_span.set_attribute("gen_ai.agent.name", "research-agent")

        # 第一步:LLM 推理
        with tracer.start_as_current_span("gen_ai.chat") as llm_span:
            llm_span.set_attribute("gen_ai.operation.name", "chat")
            llm_span.set_attribute("gen_ai.request.model", "claude-sonnet-4-20250514")
            llm_span.set_attribute("gen_ai.request.temperature", 0.0)

            response = call_llm(task)

            llm_span.set_attribute("gen_ai.usage.input_tokens", response.usage.input_tokens)
            llm_span.set_attribute("gen_ai.usage.output_tokens", response.usage.output_tokens)
            llm_span.set_attribute("gen_ai.response.finish_reasons", [response.stop_reason])

        # 第二步:工具调用
        if response.tool_calls:
            for tool_call in response.tool_calls:
                with tracer.start_as_current_span("gen_ai.execute_tool") as tool_span:
                    tool_span.set_attribute("gen_ai.operation.name", "execute_tool")
                    tool_span.set_attribute("gen_ai.tool.name", tool_call.name)
                    # 敏感内容——生产环境考虑脱敏或关闭
                    # tool_span.set_attribute("gen_ai.tool.call.arguments", str(tool_call.args))

                    result = execute_tool(tool_call)

        # 第三步:最终推理
        with tracer.start_as_current_span("gen_ai.chat") as final_span:
            final_span.set_attribute("gen_ai.operation.name", "chat")
            final_output = call_llm_with_results(response, result)

        return final_output

这段代码产生的 Trace 可以发到 Jaeger、Grafana Tempo、Langfuse、Datadog——任何支持 OTLP 协议的后端。如果你明天决定从 Langfuse 换到 Grafana,改 OTLPSpanExporter 的目标地址就行,Agent 代码一行不动。

OTel 不是银弹

社区对 OTel 在 Agent 领域的应用有一些值得注意的批评 [6]:

"浅层兼容"问题:多个工具都声称"OpenTelemetry 兼容",但实际的 Span 属性名、结构、粒度各不相同。由于 GenAI 语义约定还在 Development 状态,没有强制执行的标准——结果是同一个 Agent 的 Trace 发到不同后端,展示效果可能完全不同。

"指标没有意义"问题:HN 上有开发者质疑,追踪 token 消耗和工具调用次数是否真的比传统应用监控(请求延迟、错误率、队列深度)更有价值。这是一个合理的怀疑——如果你的 Agent 只是一个简单的 RAG 管线,传统 APM 可能就够了。OTel 的 Agent 语义约定更适合多步骤、多工具、多 Agent 的复杂场景。

"Wide Events"思路:一些开发者和可观测性厂商(受 Honeycomb 的"Observability 2.0"影响)认为,Agent 调试真正需要的不是 12 个轻量 Span 组成的瀑布图,而是一个单一的、维度丰富的结构化事件——把一次 Agent 调用的所有上下文塞进一个宽事件里。这个思路与 OTel 的 Span 层级模型有本质分歧,但反映了实际调试需求:当你在排查一个失败时,你想一眼看到所有相关信息,而不是点开 12 个 Span 逐个检查。

判断用哪种方案的简单标准:如果你的 Agent 系统跨多个服务和团队,OTel 的标准化价值无可替代。如果你是一个小团队在单体应用里跑 Agent,宽事件可能更适合快速调试。

未来方向

OTel 社区有一个开放提案(GitHub Issue #2664)[7],计划把 TasksActionsTeamsArtifactsMemory 作为一等公民纳入 Agent 可观测性语义约定。截至写作时,这个提案尚未合并到正式规范。如果它最终落地,意味着 OTel 将不仅能追踪"Agent 调了什么 API",还能追踪"Agent 的记忆发生了什么变化"、"团队中哪个 Agent 处理了哪个子任务"。

17.4 Trace → Eval → Fix 闭环

核心直觉

生产环境的 Agent 出了错,你不应该只修 bug 然后祈祷下次不再犯。正确的做法是:把出错的执行轨迹变成一个测试用例,加进你的评估套件里,让它永远守住这条防线。这样每次改 prompt、换模型、调工具时,CI 会自动检查"那个曾经出过的错是不是又犯了"。

这个闭环长什么样

这不是理论流程。Anthropic 在其评估指南中明确建议从 20-50 个真实失败案例开始构建评估套件,而不是等完美的测试集 [8]。评估套件是活的——每一次生产故障都应该成为新的测试用例。

两个评估维度

Anthropic 区分了两种评估目标 [8],这个区分对 Agent 可观测性至关重要:

Outcome 评估:最终结果对不对?文件创建了吗?数据库更新了吗?UI 上显示的值正确吗?这是检查 Agent 对环境产生的实际影响,而不是它声称做了什么。

Transcript 评估:过程合不合理?工具选择序列对吗?轮次是不是太多了?token 消耗是不是异常高?有没有不必要的重试?这是检查 Agent 的行为路径

一个 Agent 可能产出了正确的结果但过程很差(绕了 15 步才完成一个 3 步就够的任务),也可能过程看起来合理但结果是错的(每一步的推理都说得通,但最终输出有事实错误)。两个维度都需要评估。

Agent Development Lifecycle(ADLC)

ADLC 这个概念在 2024-2025 年间由多个组织独立提出——Arthur AI、Sierra、Salesforce、EPAM 各有自己的版本 [9] [10]。它们的核心共识是:Agent 开发不能套用传统的 SDLC(软件开发生命周期),因为三个根本差异:

  1. 非确定性:同样的输入,Agent 可能走出不同的路径。传统单元测试的 pass/fail 不够用,你需要统计性的评估指标(pass@k、pass^k)。
  2. 行为可以不改代码就变化:模型升级、数据漂移、工具 API 变更——都可能改变 Agent 行为,但你的代码一行没动。这要求持续监控,而不是"部署完就不管了"。
  3. 评估套件替代单元测试:传统软件的质量门禁是"所有测试通过";Agent 的质量门禁是"评估分数在可接受范围内"。

Arthur AI 把 ADLC 的核心描述为一个飞轮 [9]:真实使用(或模拟使用)→ 发现失败模式 → 增强评估套件 → 实验并度量效果 → 重复。评估套件是飞轮的轴心——Arthur AI 称之为"保证成功的最大单一杠杆"。

Sierra 的实现更具体 [10]:他们的 Experience Manager 让非技术团队(客户体验经理)每天审阅 Agent 对话,标注哪些决策正确、哪些有问题。每一条被标注的对话自动成为回归测试用例,在模拟 API 环境中并行运行。这是截至写作时,关于"人工标注 → 自动回归测试"闭环最详细的公开描述。

工程落地:从 Trace 到测试用例

把这个闭环落地到工程中,需要三个能力:

1. 失败 Trace 一键入库

当一条生产 Trace 被评估为失败(自动评估或人工审阅),它应该能一键加入评估数据集。Braintrust 和 Langfuse 都支持这个工作流。关键是数据集中保存的不只是输入和期望输出——还有完整的 Trace(工具调用序列、中间状态),因为你需要验证 Agent 不仅给出正确答案,而且走的路径也是合理的。

2. CI/CD 质量门禁

每次 PR 修改了 prompt、工具定义、模型版本或 Agent 配置时,CI 自动运行评估套件。如果质量分数低于阈值,阻止合并。这和代码的单元测试门禁逻辑一样——但执行方式不同,因为 Agent 评估涉及 LLM 调用,成本更高、速度更慢。

实际操作中,团队通常分两层:

  • 快速回归:用代码评分器(字符串匹配、JSON Schema 验证)跑全量数据集,几分钟完成
  • 深度评估:用模型评分器(LLM-as-judge)跑关键子集,可能需要十几分钟

3. 持续阅读 Transcript

Anthropic 的一个建议值得重复 [8]:定期阅读 Transcript,验证评分器是否真的在测你想测的东西。当评估分数长期稳定时,可能不是 Agent 变好了——可能是评估变容易了(数据集没有跟上新的失败模式),或者评分器的标准在漂移。

社区的真实声音

HN 上关于 Agent 评估的讨论(2026 年 2 月)[11] 揭示了一个现实:大多数团队的评估实践仍然是初期状态。评论者倾向于程序化工具(promptfoo、deepeval、pydantic-ai-evals)而非 UI 驱动的方案。原帖本身表达了对"现实中大家到底怎么做"的困惑——行业尚未收敛到标准做法。

另一个 HN 讨论(2025 年 7 月)[12] 提出了更尖锐的批评:用 LLM 评判 LLM 输出是"最大化根本失败概率的方法",因为评判者和被评判者共享相同的盲区。一个具体的例子:评估器把 "45 + 8 = 63" 判为正确。这不是说 LLM-as-judge 不能用——而是说它必须配合代码评分器做交叉验证,不能独立作为唯一评估手段。

17.5 开发阶段的日志与 Debug 实践

核心直觉

生产可观测性关心系统健康——"今天的失败率多少、平均延迟多少"。开发阶段的 Debug 关心可复现和可解释——"这个 Agent 为什么在这条输入上选了错误的工具,我怎么精确复现它"。两者需要不同的工具和方法。

本地重放(Deterministic Replay)

Agent 调试最大的痛点:你没法稳定复现问题。同样的输入、同样的 prompt,Agent 可能走出不同的路径——因为 LLM 调用是非确定性的。

本地重放的核心思路:录制一切,重放时用录制的结果替代真实调用 [13]。

python
# 录制模式:真实 LLM 和工具,同时写入 Trace 文件
agent = Agent(
    llm_client=RealLLMClient(),
    tool_client=RealToolClient(),
    trace_writer=TraceWriter("trace_2026-04-25_task42.jsonl")
)
result = agent.run("帮我查 Jira 工单并总结")

# 重放模式:用 Trace 文件中记录的结果替代真实调用
events = load_trace("trace_2026-04-25_task42.jsonl")
agent = Agent(
    llm_client=ReplayLLMClient(events),   # 返回录制的 LLM 响应
    tool_client=ReplayToolClient(events),  # 返回录制的工具结果
    trace_writer=TraceWriter("replay.jsonl")
)
replay_result = agent.run("帮我查 Jira 工单并总结")

# 如果 Agent 代码没改,replay_result 应该和 result 完全一致
# 如果不一致,说明有控制流 bug 或未被追踪的外部依赖
assert result == replay_result

重放的最小录制集合:

  • 每次 LLM 请求的完整 prompt 和完整响应
  • 每次工具调用的输入参数和返回结果
  • 每次 Agent 间消息
  • 每个交接点的状态快照
  • 模型标识、解码参数(temperature、top_p、max_tokens)、模型版本

如果录制和重放中任何一项不同,Agent 的决策路径可能分叉。

架构要求:所有 Agent 活动必须流经一个"工具网关"——一个记录所有工具请求/响应到追加日志的集中点 [13]。如果工具可以从代码的任意位置被调用,你就无法可靠地录制和重放。

LangGraph 提供了一个叫"Time Travel"的变体:不是在 LLM 调用层录制/重放,而是在图的每个节点持久化状态。你可以回到任意检查点,修改输入,然后从那个点重新执行。这把 Agent 开发从"基于 prompt 的实验"变成了"基于状态的工程"——LLM 仍然是概率性的,但工作流变得确定、可调试、可审计 [14]。

逐步调试(Step Debugger)

传统调试器的 breakpoint、step、continue、inspect 语义可以适配到 Agent 场景。AgentStepper [15] 是这方面的一个研究原型,它的做法是:

  • 把 Agent 的执行轨迹表示为 LLM、Agent 程序和工具之间的结构化对话
  • 开发者在 LLM 调用前/后和工具调用前/后设置断点
  • 断点触发时,可以实时编辑 prompt、LLM 响应、工具参数和工具返回值
  • 每步的代码变化被记录为 git commit,可以看到代码库是如何逐步演变的

AgentStepper 的用户研究(n=12)显示:使用逐步调试器后,bug 识别成功率从 17% 提升到 60%,挫败感评分从 5.4/7.0 降到 2.4/7.0 [15]。样本量小,但方向清晰——Agent 调试需要专用工具,而不是 print() 和看日志。

Prompt Diff / Config Diff

当 Agent 行为发生变化时,你需要回答一个问题:变化来自哪里? 是 prompt 改了?模型换了?工具定义变了?还是上下文数据漂移了?

Prompt Diff 的核心做法:

  1. 给每个 prompt 版本一个不可变 ID(通常是内容的哈希值)——同样的 prompt 永远产生同样的 ID
  2. 并排对比:在相同输入上,用当前版本和修改版本分别生成输出,对比差异
  3. 回归测试:每次 prompt 变更前,在现有评估数据集上跑一遍,确认分数没有下降
  4. CI/CD 集成:prompt 变更和代码变更一样走 PR 审核和自动测试

非确定性是这里的额外挑战:一次 prompt 变更的效果是概率性的。你不能像 diff 代码那样做二值判断——你需要在多次运行上做统计比较。一个实践案例:某团队将 prompt 版本化到 Git 中,每次 PR 运行 15 条回归测试用例,6 个月内 0 次回滚,任务成功率稳定在 89% [16]。

Langfuse 社区有一个关于 prompt diff 视图的功能请求(GitHub Discussion #3131),说明这个需求是真实的但工具支持还在追赶。Humanloop 提供了交互式并排 prompt 对比功能,在触发系统评估之前先做快速视觉检查。

17.6 工具链

选择可观测性工具的核心问题

在选工具之前,先想清楚三个问题:

  1. 你需要解决的首要问题是什么? 是"Agent 在生产中出错了,我要快速定位根因"(侧重 Trace),还是"我要确保每次 prompt 变更不会退化"(侧重 Eval),还是"我要控制 token 成本"(侧重 Metrics)?
  2. 你能接受多大的供应商锁定? MIT/ELv2/source-available 的自托管方案可控性更强,闭源 SaaS 锁定更深但运维成本更低。
  3. 你的团队规模和技术栈是什么? 小团队用 Langfuse 自托管可能比企业级方案更务实。

主要工具对比

截至 2026 年 4 月:

工具许可证自托管核心定位OTel 支持关键优势关键限制
LangfuseMIT(+ EE 功能已开放)免费追踪 + Prompt 管理 + 评估原生(v3 起)社区最大(26K+ GitHub stars)、Grafana/Jaeger 兼容生产自托管需要 Kubernetes
Braintrust闭源Enterprise 混合方案评估优先 + CI/CD 集成接收 OTel Span评估 → CI/CD 闭环最紧密闭源锁定、自动埋点集成少
Arize PhoenixElastic License 2.0可以追踪 + 评估原生自动埋点覆盖广,基于 OpenTelemetry / OpenInference非 OSI 许可证,商业再托管受限
LangSmith闭源 SaaSEnterpriseLangChain 生态深度集成支持 OTel 相关集成LangChain/LangGraph 无缝集成用量计费与数据保留需治理
Microsoft Foundry商业云服务企业级端到端方案OTel 基础与 Azure Monitor、Entra、私网和治理集成Azure 锁定、按消费计费

重要说明:大部分公开的工具对比文章来自工具厂商自己——Braintrust 写的对比文章自然突出自己的优势,Langfuse 也一样。上表综合了多个来源,但仍然建议你在实际选型时亲自试用。

各工具详解

Langfuse

Langfuse 是目前社区采用最广的开源 Agent 可观测性平台 [17]。截至 2025 年 6 月的官方数据:每月 8,000+ 自托管实例、SDK 月安装量 700 万+、Docker 拉取量 550 万+。Langflow(116K GitHub stars)和 Open WebUI(109K stars)都用 Langfuse 作为可观测性层。

核心能力分三个支柱:

  • 追踪:捕获 LLM 调用、检索操作、API 交互、多轮对话。v3 起基于 OTel 构建,原生兼容 Grafana、Jaeger、Datadog。
  • Prompt 管理:版本化 prompt,跨环境部署,交互式 Playground 测试。
  • 评估:LLM-as-judge、用户反馈、人工标注、自定义指标——同时适用于开发数据集和生产 Trace。

2025 年 6 月,Langfuse 把企业功能(EE)开源 [17],移除了商业壁垒。这在市场上是一个少见的举动。

生产自托管的代价:Langfuse 的维护者明确建议不要用 Docker Compose 跑生产环境——"缺乏高可用性、扩展能力和备份功能"。生产部署需要 Kubernetes(Helm Chart),技术栈包含 Postgres + ClickHouse + Redis/Valkey + S3/Blob Store + Worker。对小团队来说这是不小的运维负担。

HN 上 AINews 团队的评价:"extremely reliable" [18]。

Braintrust

Braintrust 的核心设计理念是"评估优先"——你先定义质量标准,生产 Trace 管线反馈到评估数据集中 [19]。

关键组件:

  • Brainstore:为可观测性工作负载优化的自研数据库。Braintrust 自称全文搜索快 86 倍、读写 Span 快 2 倍(自报数据,未经独立验证)。
  • CI/CD 集成:GitHub Actions 在每个 PR 上运行评估,以评论形式展示哪些用例改进了、哪些退化了。质量低于阈值时阻止合并。
  • Loop Agent:AI 助手,从观察到的失败中自动生成更好的 prompt、评分器和数据集。

Braintrust 引用的客户案例(厂商自报数据):Notion 从每天修复 3 个问题提升到 30 个(10x);Zapier 在 2-3 个月内从 50% 以下准确率提升到 90%+。

限制:闭源,供应商锁定风险。与 Phoenix、Langfuse 这类开放栈相比,可替换性取决于你是否在应用层保留 OTel 或自有追踪抽象。免费层限制 1M Span/月、14 天数据保留。

Arize Phoenix

Phoenix 由 Arize AI 构建,核心优势是自动埋点覆盖广,并且底层基于 OpenTelemetry 和 OpenInference。官方文档列出的集成覆盖 LlamaIndex、LangChain、DSPy、Mastra、Vercel AI SDK、OpenAI、Bedrock、Anthropic,以及 Python/TypeScript/Java 等语言生态 [20]。

许可证注意事项:Phoenix 使用 Elastic License 2.0(ELv2)[26]。Arize 文档称其"fully open-source free to self-host",并明确自托管免费、无功能门槛;但 ELv2 不是 OSI 认定的开放源代码许可证,和 MIT/Apache 2.0 的再分发/再托管自由度不同。

LangSmith

LangSmith 的核心优势是与 LangChain/LangGraph 生态的深度集成——追踪和评估感觉像框架的自然延伸。官方文档称它覆盖 observability、evals 和 prompt engineering,并具备 HIPAA、SOC 2 Type 2、GDPR 合规能力 [21]。

社区反馈中的警告信号

2025 年 9 月,LangChain 论坛上有用户发帖称 "LangSmith is down for 17 hours?",并描述本地开发和用户问题调试都受影响;LangChain 联合创始人在回复中确认当时存在由上游供应商引发的复杂生产故障 [22]。这里的"17 小时"来自社区帖标题和用户反馈,不应当被当作独立 SLA 统计。

这暴露了一个结构性问题:如果你的可观测性工具本身是 SaaS,它的宕机意味着你的调试能力也宕机了。这是选择自托管方案(Langfuse、Phoenix)的一个理由。

另一个常见担忧是成本可预测性:Trace 保留、反馈、标注和长期调试数据会让用量型计费更难估算。选择闭源 SaaS 时,最好把数据保留策略、采样率、导出能力和超额计费写进上线前检查清单。

Microsoft Foundry

Microsoft 在 2026 年 3 月 16 日宣布 Foundry Agent Service、Foundry Portal,以及 Foundry Control Plane 中的评估、追踪、监控能力进入 GA 或接近 GA 的生产可用阶段 [23] [27]。它的独特性不在于"唯一",而在于把 Agent 可观测性放进 Azure Monitor、Entra、私网、RBAC、审计和治理这套企业操作平面里。

核心能力:

  • 评估:内置通用质量评估器(连贯性、流畅性)、RAG 专用指标(忠实度、相关性)、安全评估器、Agent 专用指标(工具调用准确率、任务完成率)。
  • 监控:集成 Azure Monitor / Application Insights。实时仪表盘覆盖 token 消耗、延迟、错误率、质量分数。
  • 追踪:基于 OTel 的分布式追踪。Microsoft 文档称其可跟踪模型调用、工具调用、检索步骤、编排逻辑和跨 Agent handoff,并支持 Microsoft Agent Framework、Semantic Kernel、LangChain、LangGraph、OpenAI Agents SDK 等框架 [27]。
  • AI Red Teaming Agent:使用微软 PyRIT 框架模拟复杂攻击,支持部署后定期红队测试。

限制:Azure 原生,对不在 Azure 上的组织意味着深度锁定。评估按消费计费——Playground 中的评估默认启用并计费(必须手动关闭)。

选型决策树

你已经在用 LangChain/LangGraph?
├── 是,且不介意锁定 → LangSmith(最少摩擦)
├── 是,但想保持灵活 → Langfuse + OTel(开放标准)
└── 否
    ├── 你的组织在 Azure 上? → Microsoft Foundry
    ├── 你是小团队,想自托管?
    │   ├── 有 K8s 能力 → Langfuse(MIT、社区最大)
    │   └── 不想运维 → Braintrust Cloud 或 Langfuse Cloud
    ├── 你的首要问题是 CI/CD 评估门禁? → Braintrust
    └── 你需要最广的自动埋点覆盖? → Arize Phoenix

这个决策树不是标准答案——每个团队的约束不同。但有一条通用建议:不管用哪个工具,尽量让你的埋点层保留 OTel 或自有抽象。如果业务代码深度依赖某个厂商的专有 SDK,未来迁移会更痛;如果用 OTel SDK 或一层薄适配器,后端可以更从容地替换。

17.7 Shadow Agent 发现与治理

核心直觉

你的组织里可能有人——某个开发者、某个运营团队——已经悄悄部署了一个 AI Agent,用员工自己的 API Key 连着生产数据库在跑。没人知道它的存在,没人监控它的行为,没人审计它的数据访问。这就是 Shadow Agent——影子 Agent。

从 Shadow IT 到 Shadow Agent

Shadow IT 不是新问题——员工用个人 Dropbox 存公司文件、用未经批准的 SaaS 工具处理客户数据,IT 部门头疼了十年。Shadow Agent 是这个问题的升级版,因为 Agent 不只是存取数据——它自主决策和执行操作

AvePoint CPO John Hodges 的说法 [24]:"以前我们叫它 Shadow IT,现在是 Shadow Agent 在被创建,而且我们看不到这种趋势会放缓。"

几个数字(注意:这些多来自安全厂商或媒体对 Gartner/IBM 等报告的二手转述,适合作为风险信号,不适合作为精确市场统计):

  • Gartner(2025 年 11 月调查,n=302 网络安全负责人):到 2030 年,超过 40% 的企业会经历与未经授权的 Shadow AI 相关的安全或合规事件 [28]。
  • IBM 相关 Shadow AI 报告的二手转述:只有 37% 的组织有检测或管理 Shadow AI 的策略 [28]。
  • Gartner 相关预测的二手转述:到 2026 年底,40% 的企业应用将包含任务特定的 AI Agent,而 2025 年这个比例不到 5% [29]。

为什么 Shadow Agent 特别难发现

一个 Shadow Agent 通常使用员工自己的凭据,通过员工已经被授权访问的 API 来操作。从网络监控和身份管理的角度看,它和员工正常工作没有区别——同样的 IP、同样的账户、同样的 API 调用模式。

标准的 DLP(数据防泄漏)和 IAM(身份访问管理)工具对 Agent 的临时身份是盲的。CSPM(云安全态势管理)工具看到的是一个合法服务器运行着合法进程——而不是那个没经过审查的 AI 逻辑正在通过硬编码的 API Key 调用第三方服务。

OWASP 的 Agentic Applications Top-10(2026)把 Agent 目标劫持、工具误用、身份与权限滥用等列为 Agentic AI 系统的关键风险 [25]。这说明 Shadow Agent 不是单纯的资产盘点问题,而是身份、权限、工具边界和审计链路共同失控的问题。

Shadow Agent 的典型风险

  1. 绕过企业控制:一个善意的开发者给 Agent 授予了 AWS AdministratorAccess 或全范围的 GitHub PAT,创造了一个跑在云函数里、拿着过大权限的非确定性自主实体。
  2. 数据泄露:Agent 查询生产数据库获取上下文,把包含 PII 的数据发送到第三方 LLM API。没有人审计这个数据流动。
  3. 运营脆弱性:未被追踪的 Agent 嵌入到业务流程中,成为不可见的依赖。当它停止运行时,没人知道哪个流程受了影响。
  4. 对抗攻击面:未经加固的 Agent 更容易被 prompt 注入和工具滥用攻击利用。

检测和治理

可观测性系统在 Shadow Agent 治理中扮演关键角色——你不能治理你看不到的东西。

集中式 Agent 注册表:组织应该维护一个 Agent 的单一真相来源——所有已注册和已发现的 Agent 都在这个列表上。每个 Agent 需要:唯一标识、所有者、权限范围、审计日志、定期审查周期。把 Agent 当作受管身份,而不是一段没人管的脚本。

异常检测:监控以下信号可以帮助发现未注册的 Agent:

  • API 调用模式异常(非工作时间的持续调用、异常高的请求频率)
  • 新出现的 LLM API 调用(之前没有的 OpenAI/Anthropic API 流量)
  • 大量数据外传到未知的外部端点
  • 异常的凭据使用模式(一个人类账户在同时从多个 IP 调用 API)

提供正规通道:如果组织提供了便捷的、受治理的 Agent 开发环境,员工自建 Shadow Agent 的动机就会减弱。堵不如疏——与其事后追查,不如事先提供一个"合法途径"。

这个话题和第 20 章(安全、治理与合规)有交叉。本章从可观测性的视角覆盖"怎么发现 Shadow Agent",第 20 章从治理的视角覆盖"发现后怎么管"。

面试高频题

问题:如何定位一个 Multi-Agent 系统中间步骤的失败根因?你会怎么设计 Agent 的可观测性架构?

参考答案框架:

  1. 先讲 Trace 的层级结构:每个 Agent 的执行是一个 Trace,由多个 Span 组成(LLM 调用 Span、工具执行 Span、状态更新 Span)。Multi-Agent 系统中,Agent A 调用 Agent B 时,A 的 invoke_agent Span 是 B 的根 Span 的父节点,通过 W3C trace context 传播连接。

  2. 定位根因的流程:从最终失败的 Span 往上回溯父 Span → 检查每个 Span 的输入输出 → 找到第一个输出异常的 Span → 该 Span 的输入就是根因线索。关键是区分"这一步本身出错了"和"这一步的输入就是坏的"。

  3. 可观测性架构设计:

    • 埋点层用 OTel 标准,避免供应商锁定
    • 三种信号分开:Trace(因果链)、Metrics(统计趋势)、Logs(操作细节)
    • 敏感内容控制:开发环境全量记录,生产环境默认脱敏
    • 跨 Agent 的 trace context 传播必须显式处理(尤其是 MCP 场景)

加分点:

  • 提到 Trace → Eval → Fix 闭环:失败的 Trace 应该变成评估数据集中的回归测试用例
  • 提到 Outcome 评估和 Transcript 评估的区别:不仅看结果对不对,还看路径是否合理
  • 提到 Shadow Agent 的发现:可观测性不仅是为了调试已知 Agent,也是为了发现未知 Agent
  • 提到 OTel GenAI 语义约定仍在 Development 状态,实际使用中需要关注版本兼容性

参考资料

[1] Zhang et al., "Characterizing Faults in Agentic AI", arXiv:2603.06847, 2026. https://arxiv.org/html/2603.06847v1

[2] Galileo, "7 Multi-Agent Debugging Challenges". https://galileo.ai/blog/debug-multi-agent-ai-systems

[3] OpenTelemetry, "Semantic Conventions for Generative AI Systems". https://opentelemetry.io/docs/specs/semconv/gen-ai/

[4] OpenTelemetry, "Semantic Conventions for GenAI Agent and Framework Spans". https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-agent-spans/

[5] MintMCP, "OpenTelemetry for AI Agents: Implementing Observability in MCP Workflows". https://www.mintmcp.com/blog/opentelemetry-ai-agents

[6] Hacker News, "LLM Observability in the Wild" 讨论, 2025. https://news.ycombinator.com/item?id=45398467

[7] OpenTelemetry Semantic Conventions, GitHub Issue #2664, "Semantic Conventions for Generative AI Agentic Systems". https://github.com/open-telemetry/semantic-conventions/issues/2664

[8] Anthropic, "Demystifying Evals for AI Agents", Anthropic Engineering Blog. https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents

[9] Arthur AI, "Introducing ADLC (Agentic Development Lifecycle)". https://www.arthur.ai/blog/introducing-adlc

[10] Sierra AI, "Agent Development Life Cycle". https://sierra.ai/blog/agent-development-life-cycle

[11] Hacker News, "Evaluations for Testing Agentic AI" 讨论, 2026 年 2 月. https://news.ycombinator.com/item?id=46825259

[12] Hacker News, "AI Agent Benchmarks Are Broken" 讨论, 2025 年 7 月. https://news.ycombinator.com/item?id=44531697

[13] sakurasky.com, "Missing Primitives for Trustworthy AI: Deterministic Replay". https://www.sakurasky.com/blog/missing-primitives-for-trustworthy-ai-part-8/

[14] DEV.to, "Debugging Non-Deterministic LLM Agents with LangGraph Time Travel". https://dev.to/sreeni5018/debugging-non-deterministic-llm-agents-implementing-checkpoint-based-state-replay-with-langgraph-5171

[15] AgentStepper, arXiv:2602.06593, 2026. https://arxiv.org/html/2602.06593v1

[16] PromptBuilder, "Prompt Testing in CI/CD 2025". https://promptbuilder.cc/blog/prompt-testing-versioning-ci-cd-2025

[17] Langfuse, "Open Sourcing Langfuse Product", 2025 年 6 月. https://langfuse.com/blog/2025-06-04-open-sourcing-langfuse-product

[18] Hacker News, Langfuse Launch 讨论. https://news.ycombinator.com/item?id=42441258

[19] Braintrust, "How to Eval". https://www.braintrust.dev/articles/how-to-eval

[20] Arize AI, "Phoenix Documentation". https://arize.com/docs/phoenix

[21] LangChain Docs, "LangSmith docs". https://docs.langchain.com/langsmith

[22] LangChain Forum, "LangSmith Is Down for 17 Hours", 2025 年 9 月. https://forum.langchain.com/t/langsmith-is-down-for-17-hours/1484

[23] Microsoft Foundry Blog, "Building Production-Ready, Secure, Observable, AI Agents with Real-Time Voice with Microsoft Foundry", 2026 年 3 月 16 日. https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/building-production-ready-secure-observable-ai-agents-with-real-time-voice-with-/4501074

[24] CIO, "Shadow AI Morphs into Shadow Operations". https://www.cio.com/article/4162664/shadow-ai-morphs-into-shadow-operations.html

[25] OWASP GenAI Security Project, "OWASP Top 10 for Agentic Applications for 2026". https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/

[26] Arize Phoenix, "License". https://arize.com/docs/phoenix/self-hosting/license

[27] Microsoft Foundry Blog, "Generally Available: Evaluations, Monitoring, and Tracing in Microsoft Foundry", 2026 年 3 月. https://techcommunity.microsoft.com/blog/azure-ai-foundry-blog/generally-available-evaluations-monitoring-and-tracing-in-microsoft-foundry/4502760

[28] Noma Security, "Shadow AI Agents: Enterprise Risk". https://noma.security/resources/shadow-ai-agents-enterprise-risk/

[29] Aona AI, "Shadow Agents: Enterprise Agentic AI 2026". https://aona.ai/blog/shadow-agents-enterprise-agentic-ai-2026/