1. 为什么 Agent 一到生产环境就“失控”——黑盒问题的本质今年上半年我接连接手了几个 AI Agent 项目有做代码生成的有做客服自动化的还有一个是做供应链异常归因分析的。Demo 阶段都跑得很漂亮产品经理看了直点头领导也觉得“这次 AI Native 是真的落下来了”。结果一到联调画风突变Agent 时不时答非所问工具调用偶尔传错参数本来该结束的任务莫名多绕了好几轮。更要命的是谁也不知道问题出在哪一步。传统后端开发里我们早就习惯了“日志 监控 链路追踪”三板斧接口慢了看 APM报错了看堆栈数据不对了翻数据库日志。但这些经验搬到 Agent 上几乎全部失效。你打开日志只能看到“调用 LLM 返回了 content”和“调用了 search_web 工具”这种零散记录根本拼不出 Agent 当时的完整思考链。模型为什么决定调用这个工具中间哪一步产生了错误信息导致后续判断跑偏是哪一次 LLM 返回让 Agent 开始绕圈子的日志里全是断点没法串成线。后来我翻了很多讨论也跟做 LLM 基建的朋友聊了几轮慢慢想明白一件事传统可观测性观测的是“确定性的系统”而 Agent 的系统里塞进了一个概率性的 LLM 核心。模型不是按固定路径执行代码而是按“概率分布”选择下一步动作以至于同一个 prompt 多次执行路径却可能完全不同。这对可观测性体系是釜底抽薪式的冲击我们不能只观测“发生了什么”还得观测“为什么走到这里”“它思考过哪些分支”“每一步的置信度有多高”。这就是我在标题里写“从黑盒到全链路”的原因。生产级 Agent 如果没有一整套可观测性体系撑腰本质上就是在一个复杂分布式系统里裸奔出问题只能靠猜。这篇文章我会把这一年来搭建 Agent 可观测性体系踩过的坑、验证过的方法和沉淀下来的框架一次性讲清楚适合正在做 Agent 开发落地、或者已经在生产环境被“幽灵 Bug”折腾过的工程师参考。2. Agent 可观测性到底该观测什么——四个核心维度在做具体技术选型之前先得把口径对齐Agent 的可观测性到底观测哪些东西一开始我也以为无非就是多记点日志但随着问题越来越多我意识到要在四个维度上建立完整视图缺一个都不行。2.1 运行轨迹观测Agent 每一步“踩在哪里”运行轨迹是最基础的一层说白了就是把 Agent 从接收到用户请求到最终返回结果的完整执行路径还原出来。传统分布式追踪记录的是服务间调用链Agent 的链路则是“LLM 调用 → 工具调用 → 结果返回 → 再次调用 LLM”这样的循环每个环节可能是循环几轮甚至几十轮。这一步我强烈建议做三件事给每次 Agent 运行分配全局唯一的 trace_id把 LLM 调用、工具执行、内部代码逻辑全部挂在这个 trace_id 下记录每一步的时间戳和耗时并且区分“LLM 思考耗时”和“工具执行耗时”记录每轮循环开始时的 Agent 状态摘要比如当前目标任务、已收集到的关键信息、剩余计划步骤。有了这套轨迹出问题时至少能快速看到“这个 Agent 总共跑了 17 轮其中有 8 轮在反复调用 search_web 但参数都不一样明显是陷入了检索循环”。这句话在以前几乎是不可能快速确认的。2.2 内部状态观测模型脑子里到底在想什么这一层比轨迹观测深一步难度也大不少。Agent 跟传统程序一个很大的不同是它有内部状态而且这些状态并不是传统意义上结构化的变量而是半结构化的“上下文记忆”。包括系统提示词、历史对话摘要、当前待办计划、工具返回结果在上下文中的位置等。传统日志记录很难覆盖这部分因为 LLM 的上下文是动态拼接的而且长度有限你不可能每轮都把完整上下文打出来。我的做法是分层记录在全量日志里记录每一轮发往 LLM 的 prompt 的“摘要版本”例如截取最后 N 个 token、保留关键系统指令在关键转折点工具选择变化、判断结论变化记录该时刻完整上下文快照放进对象存储方便回溯分析。这里要提醒一句LLM 的上下文是很贵的全量记录也不现实。可观测性系统的设计原则不是“什么都存”而是“在需要还原行为时能够重建现场”。后续我会讲到怎么按层级设计存储策略。2.3 数据血缘观测答案是怎么一步步算出来的这个维度我在项目早期完全没意识到它的重要性后来被坑惨了才补上的。Agent 生产环境中经常出现这种情况用户问“帮我分析一下上季度华东区销售额下降的原因”Agent 先去数据库查询了订单表又调用了内部 BI 工具拿到了日报数据还搜索了外部新闻最后综合这些信息给出结论。如果结论有误你怎么定位是 SQL 写错了还是报表数据没更新还是外部信息误导了模型数据血缘观测解决的就是这个问题。它记录每一次工具调用的输入参数、返回结果摘要以及这些结果最终被用于哪一轮 LLM 推理。换句话说就是建立起“结论 → 依据 → 数据源”的反向追溯链。技术实现上可以给每个工具返回值附加一个 data_source_id然后在 LLM 调用的 prompt 中保留这些 ID当最终答案生成时通过分析引用关系映射回数据源。这块做扎实之后业务方来投诉“这个数算错了”的时候你不用再拍脑袋直接打开链路图告诉他这一步用的是 6 月 30 日的快照数据而 6 月 30 日的数据当天凌晨有延迟源头在数据管道不在 Agent 逻辑。2.4 质量与成本观测跑得准不准烧钱多不多这个维度最容易被技术团队忽略但偏偏是老板最关心的。Agent 每跑一轮都在消耗 token复杂的 Agent 任务跑几十轮一次交互烧掉几块钱甚至几十块钱都是正常的。没有观测月底账单出来才发现成本爆炸那就被动了。我的做法是给每一轮 LLM 调用单独记录 token 消耗输入、输出、缓存命中分别是多少、模型名称、温度和 top_p 等参数然后按 trace 维度做聚合。这样就能回答几个关键问题平均每个任务要跑多少轮哪些任务类型 token 消耗特别高有没有因为 Agent 陷入循环而导致 token 消耗异常的任务质量维度则要自定义一套评分体系。规则类的指标包括任务是否正常结束没有超时或达到最大迭代次数、工具调用是否有异常报错、最终答案是否为空语义类的指标包括最终答案与用户问题的相关性打分、答案是否引用了工具返回的真实数据。规则类指标代码里直接可以统计语义类指标要接一个可选的“评判模型”来做二次评估这部分后面详聊。3. 全链路追踪的设计与落地——OpenTelemetry 体系怎么扩展到 Agent维度想清楚了接下来就是技术实现。我实测下来完全从零搭一套 Agent 可观测性体系其实是没必要的因为 OpenTelemetry简称 OTel这套标准已经能覆盖绝大部分需求关键是做“语义约定”的扩展和定制。3.1 用 Trace 语义还原 Agent 的思考链条OTel 里的核心概念是 Trace、Span 和 Attribute。常规后端服务里一个 Span 通常代表一次 HTTP 请求或一次数据库查询。在 Agent 场景我把 Span 设计了四类AgentRunSpan代表一次完整的 Agent 处理流程是最外层、LLMSpan代表一次 LLM 调用记录模型名、prompt 摘要、response 摘要、token 用量、ToolSpan代表一次工具调用记录工具名、入参、返回值摘要、执行耗时、AgentStepSpan代表 Actor/Cot 的一步推理过程。很多人第一次设计会把 ToolSpan 挂成 LLMSpan 的子 Span因为工具调用确实是模型决定的。但我在实际使用中发现更好的做法是把 ToolSpan 和 LLMSpan 都挂成 AgentStepSpan 的子节点然后 AgentStepSpan 再挂在 AgentRunSpan 下。原因是一个完整的“Agent 推理步骤”应该是“思考LLM行动工具观察结果”的组合三者在时间上有先后但逻辑上属于同一个决策单元。这样在查看跨度时间线时每一步的完整闭环都一目了然。示例伪代码如下with tracer.start_as_current_span(agent_run) as run_span: run_span.set_attribute(agent.type, customer_service) run_span.set_attribute(session.id, session_id) while not task_finished: with tracer.start_as_current_span(agent_step) as step_span: step_span.set_attribute(step.index, step_count) step_span.set_attribute(step.input, summarize(step_input)) with tracer.start_as_current_span(llm_call) as llm_span: llm_span.set_attribute(llm.model, model_name) llm_span.set_attribute(llm.prompt_tokens, prompt_tokens) llm_span.set_attribute(llm.completion_tokens, completion_tokens) llm_response llm_call(prompt) llm_span.set_attribute(llm.response, summarize(llm_response)) # 模型决定调用工具 if llm_response.tool_call: with tracer.start_as_current_span(tool_exec) as tool_span: tool_span.set_attribute(tool.name, tool_name) tool_span.set_attribute(tool.input, json.dumps(tool_args)) result execute_tool(tool_name, tool_args) tool_span.set_attribute(tool.result, summarize(result))3.2 用 Session 语义串联多轮交互只做单次 Run 的 Trace 还不够。生产环境里的 Agent 几乎都是多轮对话形态用户会连续追问、修正、补充信息Agent 需要基于之前的对话历史继续作答。这种情况下每一次用户输入都会触发一次新的 Agent Run但它在逻辑上是同一段会话的延续。我的方案是通过 OTel 的 Baggage 机制传递 session_id让所有子 Span 都能自动继承会话上下文。然后在存储层用 session_id 做索引查询时能把一个用户会话内的所有 Agent Run 拉出来按时间顺序排列形成完整的“会话级时间线”。这样排查多轮对话中的状态串扰问题比如用户上一轮让 Agent 记住某个偏好这一轮 Agent 突然忘了就非常方便。3.3 存储与分析ClickHouse 还是 ElasticsearchTrace 数据采集了接下来就是怎么存、怎么查。Agent 场景的数据量跟传统后端不太一样Span 数量大一次 Agent Run 可能产生几十个 Span单个 Span 的文本内容长prompt 摘要、工具入出参摘要都是文本而且要支持按 trace_id 精确定位和按属性过滤聚合。我先后试过两种方案。第一种是 Elasticsearch用标准 APM 索引模板存 Span好处是生态成熟、Kibana 里能看到现成的 Trace 瀑布图坏处是文本字段一多索引膨胀很快而且聚合查询性能一般。第二种是 ClickHouse把 Span 拍平成宽表存储利用 ClickHouse 的列式存储和向量化查询对“按 session_id 拉全部 Span”“按属性过滤计算平均 token 消耗”这类分析型查询非常友好。最终我选择了 ClickHouse 为主存储配合一个轻量的 Trace 查询 API 和自研前端界面Elasticsearch 只用于全文检索比如按 prompt 关键词搜历史记录。如果你团队规模不大不想自研 UI初期可以直接用 Grafana Tempo 或 Jaeger它们都支持 OTel 协议接入先解决“看得见”的问题等后面对“怎么查得爽”有更高要求了再考虑自研存储层。4. 从 Trace 到 Diagnosis——可回放、可评估、可诊断的闭环体系Trace 只是原始数据真正值钱的是怎么把它变成诊断能力和评估能力。我见过不少团队把 OTel 接入之后觉得“这下可观测性稳了”结果就是每天盯着五彩斑斓的瀑布图问题来了还是不知道怎么查。可观测性体系的最终形态应该是“诊断闭环”不只是看还能查、能试、能判。4.1 会话回放像看录像一样复盘 Agent 行为我们团队内部做了一个“会话回放”功能不算复杂但效果极好。核心思路是把每一轮 AgentStepSpan 里的 LLM 入参、LLM 返回值、工具调用的参数和结果完整记录到对象存储里然后前端按时间拉出来渲染成类似聊天记录的样式——左边显示 Agent 当时的思考和计划右边并列显示它调了哪个工具、拿到了什么结果中间用时间线串起来。比起看瀑布图这个回放视图对非技术同事产品、运营、客服质检特别友好。他们不用理解什么是 Span直接回放一遍就知道 Agent 当时做了什么、怎么做的。有一次客服团队反馈“Agent 回答态度生硬”我们回放发现是模型在某一步突然把客户称呼从“您”换成了“你”再追溯发现是工具返回的一段历史对话里有不规范的称呼污染了上下文。这种问题在传统日志里几乎不可能定位但回放视图十分钟就看明白了。实现回放的关键点在于不能只存结构化字段LLM 调用和工具调用的原始文本必须完整落盘。我们踩过这个坑——刚开始只做了摘要结果回放到一半发现上下文不连贯后来改成“结构化摘要 原文快照”双写摘要用于快速浏览原文用于深度分析。4.2 离线评估把生产数据变成回归测试集可观测性不只是被动响应问题更重要的价值是沉淀数据、反哺质量。我们的做法是从生产链路里定期采样 Trace把真实用户请求和 Agent 的完整执行历史整理成“回放数据集”然后用这批数据做回归测试。具体流程是这样每周从生产环境随机抽取 200~300 个已完成的 Agent Run把这些 Run 的用户输入作为测试集在发版前用新版本的 Agent 代码重新跑一遍测试集采集新的 Trace把新旧 Trace 对比看三件事任务完成率有没有下降、平均轮数有没有变多、工具调用失败率有没有上升。这里面有一定技术难点LLM 的非确定性导致同一个请求多次执行结果不同所以对比不是简单判断“结果是否一致”而是看“分布是否漂移”。比如旧版本平均 3.2 轮完成新版本变成 5.8 轮即使最终答案看起来都对也说明新版本推理效率明显劣化了。这种回归能力靠人工测试是补不出来的只能靠可观测性体系攒数据。4.3 质量评分与告警从“看日志”到“看指标”诊断闭环的最后一环是告警。告警策略不能只盯技术指标如 P95 延迟、错误率得设计一套“Agent 质量指标”。我们线上重点盯这几个无效循环率同一工具连续调用超过 5 次且参数相似的任务占比这个指标直接暴露 Agent 的“转圈”问题、工具失败率工具执行抛错或超时的次数占比、语义完成度用评判模型对最终回答打分的均值低于阈值才算真正失败、token 成本异常率单次 Run token 消耗超过该类型任务 P99 的任务占比。这套指标上线一周就把我们之前潜伏的两个问题炸出来了一个是某个 Agent 在特定 intents 下会反复调用数据库查询工具平均每个任务多消耗 40% token另一个是某个工具在下午高峰期持续出现 30% 的失败率但我们原有的基础设施监控根本没有覆盖到它。这说明 Agent 可观测性体系不能只“观”模型还得“测”外围依赖。5. 实践中的典型问题与避坑指南5.1 采样策略全量记录会爆不记又没数据Agent 可观测性的数据量远高于传统后端。一次复杂的 Agent Run 可能产生 20~50 个 Span每个 Span 都带文本摘要一天几十万次调用存储成本非常可观。全量记录不现实但采样率太低又没法排查偶发问题。我们最终采用的是“分级采样”策略健康任务成功结束、耗时正常、工具无异常按 10% 采样异常任务报错、超时、达到最大轮数全量采集低质量任务语义评分低于阈值全量采集。同时所有任务都保留最小化链路元数据trace_id、时间戳、耗时、token 总量但只对采样任务记录详细 Span。这样在存储成本可控的前提下既保证大多数问题能被抓到又不放过低频的异常样本。这个设计让我踩过一个大坑最初我以为“全量采集过期删除”就够了结果 3 天就写满了 2TB 磁盘而且查询性能迅速劣化。后来想了很久才调整成“元数据全量 明细采样”的双轨模式成本降到原来的 1/10问题的可发现率反而因为加了“异常全采”而提升了。5.2 LLM 文本记录摘要程度怎么把握记录 LLM 的 prompt 和 response 是个容易走极端的事情。记得太细存储和隐私都扛不住记得太粗回放和排查时就找不到关键信息。我的经验是分三层完整原文只保存到“需要点位分析时才触发”比如异常任务或带敏感标记的任务中间层是“结构性摘要”对 prompt 保留系统指令全文 历史对话最后 3 轮 用户当前输入全文 工具返回结果的关键字段最外层是“一句话摘要”用一个小模型或规则从 prompt/response 里提取主题和关键实体用于搜索和聚类。做这个设计时我参考了“近细远粗”的数据分层思想在绝大多数场景下你能靠“中间层”解决 95% 的排查需求完整原文反而很少用上。5.3 隐私与合规日志也是数据别在可观测性里裸奔Agent 处理的大多是真实业务数据prompt 里可能包含用户姓名、手机号、订单信息甚至病历。这些内容一旦进入可观测性系统就形成了新的“数据副本”合规风险立刻上升。我强烈建议在采集端做脱敏通过正则和 NER 模型识别敏感实体在写入存储前替换为占位符同时存储层做字段级加密和访问审计谁查了这些数据都要留痕。有一次我们差点因为日志里存了用户身份证号被安全团队通报从那时起“脱敏先行”成了接线规。这不是可观测性体系的功能负担而是生产级系统的底线要求。5.4 工具调用上下文丢失问题这类问题在 Agent 生产环境中非常典型比如第一步搜索返回了 5 条结果第二步工具调用又要用其中第 3 条结果的信息但工具只接收了第 3 条的 ID执行时 ID 对应的记录已经过期或不存在了。传统日志里你只能看到“工具返回异常”但看不到“Agent 为什么只传了 ID 没传完整信息”。要排查这类问题必须在 ToolSpan 里记录完整的工具入参和返回摘要。我在 ToolSpan 里加了一个tool.context_summary属性用来记录“Agent 是在什么背景下决定这样调用工具的”本质上是对 LLM 推理过程的压缩快照。有了它当工具调用失败时你能快速判断是 Agent 上下文记忆丢失还是工具侧数据变更还是入参构造逻辑本身有 Bug——三者处理方向完全不同。6. 未来演进从可观测到可治理可观测性体系搭到一定程度后我明显感觉到它正在从“事后排查工具”变成“事前治理手段”。顺着这个方向Agent 的可观测性未来会有三个延伸第一个是主动质量护栏。现在我们已经默认给高成本任务加一个“预警机制”当 trace 中检测到无效循环、token 消耗超过预期、工具连续失败系统会自动终止当前 Agent Run转入人工处理队列不再让它继续烧钱绕圈。本质是把可观测性从“记录”变成“控制回路”。第二个是成本治理。Agent 的 token 消耗越来越像云资源费用需要有预算分配、成本分摊和异常计费检测。可观测性平台正在演变成“FinOps 工具”管理层视角下的每一次 Agent 决策都要有对应的成本账单。我们已在上个月上线了“按业务线分摊 token 成本”的报表这在未来一定会变成刚需。第三个是数据管道化。压缩后的 Trace 摘要会成为数据仓库的常驻数据直接喂给后续的运营分析和模型微调。我在内部已经把“高质量 Agent Run”整理成了训练数据集用来做特定业务场景的 SFT效果比纯人工构造数据要自然得多。可观测性不仅是运维系统的眼睛还会成为模型迭代的数据源头。写在最后的一点体会从我自己的实践来看Agent 可观测性体系的难点从来不是技术栈的选择而是思维方式的转变。传统可观测性假设系统行为是可预期的而 Agent 的行为在本质上是一个概率分布——同一个输入今天走这条路明天可能走另一条路。这让所有“根据历史经验猜测问题”的排查手段失效。唯一可靠的办法就是把 Agent 的每一步思考、每次行动、每个依据都变成可见的数据让黑盒里的不确定性可以被审视、比较和验证。如果你正在从零搭这套体系我的建议是先别追求大而全。从 trace 开始把链路串起来然后加会话回放让非技术同事也能参与排查再多一个离线评估用生产数据反哺模型迭代。一步一步来你会发现“生产级 Agent”的重点从来不在“Agent”而在“生产级”这三个字——它们背后站着的就是一套扎实的可观测性体系。