AI Agent 做 Demo 很容易,在生产环境里调试才是真正的难题

Demo 从来不会出问题
我从来没见过一个 AI Agent 在演示时出过问题。真的,一次都没有。
打开 notebook,运行 cell,Agent 调用了一个工具,拿到了响应,推理了一下,又调用了另一个工具,最后生成了一个漂亮的答案。房间里所有人都在点头。有人说"这太厉害了"。有人问,这能直接上线吗?
你当时说可以,因为那一刻你自己也真的这么以为。
然后过一周,你第一次在生产环境里看到一个奇怪结果,心里会突然往下一沉。不是因为模型错了一次,而是因为你突然发现,自己根本解释不了它为什么会错。
我也这么以为过。我见过太多支持类 Agent 都会走上同一条路:先分类工单,再拉取客户历史,判断是升级给人工还是自动回复,最后起草回复。Demo 里这一套流程看起来完美无缺。可一到生产环境,它就开始做出你解释不了的事。以前 2 秒就能返回的响应变成了 8 秒。流量明明没怎么变,成本却在往上爬。还有一些用户收到了明显不对劲的回复,可 Agent 自己却把它标成了“高置信度”。
最糟的不是故障本身,而是那种无力感。我们根本看不到,错误到底是从链条的哪个环节开始偏掉的。
为什么 Agent 比普通 AI 功能更难调试
单个 LLM 调用的调试很直接。你发了一个 prompt,拿到了一个响应,你能直接看到发生了什么。如果不对,改 prompt 或者换模型就行。
但 Agent 不是单次调用。它是一条链。有时是一张图。有时是一个循环。
当这条链的某个环节出了问题,症状出现在链尾,但原因藏在链中间。Agent 在第二步选错了工具。或者工具在第三步返回了一个意外的格式。或者因为第四步的上下文太杂,第二次 LLM 调用直接产生了幻觉。又或者,什么都没有"坏" — Agent 只是走了一条和平时稍微不同的路径,结果的质量比预期微妙地变差了。
传统的调试工具根本不是为这类场景设计的。你的应用日志只显示最终响应。Provider 的 dashboard 只显示 token 数量。两者都不会告诉你导致输出的那条决策链是什么。
AI 开发者实际在用的调试工具
如果你今天在做 Agent 开发,你大概已经拼凑出了这样一套工具链:
-
Print 和日志。 你在 Agent 代码里到处撒
print()或logger.debug()。Agent 只有两三步时还好用,等到六步的时候,你的终端就是 200 行无结构的输出。到最后你是在调试你自己的调试日志。 -
LangSmith traces。 如果你用的是 LangChain 或 LangGraph,trace 确实有用。你能看到每个节点、每次 LLM 调用、每次工具调用。但局限是它和 LangChain 生态高度耦合。如果你的 Agent 用了原生 SDK 调用,或者混合了多个框架,或者在 LangChain 抽象之外做了任何事情,那那些步骤在 trace 里就会消失。
-
Provider dashboard。 OpenAI 的 usage 页面能告诉你用了多少 token。但它不会告诉你是 Agent 的哪一步消耗的,也不会告诉你为什么第三步比平时慢了四倍,更不会告诉你是不是因为主模型被限速了而自动使用了备用模型。
-
Weights & Biases / Weave。 做实验追踪和 prompt 版本管理不错。但对于实时生产调试 — 你需要弄清楚凌晨 3 点的某个特定请求为什么走了一条奇怪路径时 — 它就不太能帮到你了。
这些工具都能解决一部分问题。但没有任何一个能给你在生产环境里 Agent 出问题时最关键的那个东西:让你看清 Agent 实际发出的 LLM 请求、看清发生了哪些 provider 尝试,以及请求究竟是在什么时刻开始走偏。如果你还想看到应用运行时内部的工具级 trace,那仍然需要你在应用层或框架层自己做埋点。
会出问题的那 2%
以下是我在生产环境里观察 Agent 故障之后总结出的经验。故障通常落入以下五种模式,而且几乎没有一种会在开发阶段出现。
Provider 不稳定
你的 Agent 每个请求要发三次 LLM 调用。如果每次调用的成功率是 99%,那你的 Agent 成功率就是 97%。3% 的失败率听起来很小,但你的 Slack 一亮你就知道了:一千个用户里今天就有三十个碰到了坏掉的体验。而且如果失败发生在四步链条的第二步,Agent 通常不会干净利落地崩溃 — 它往往会带着垃圾上下文继续往下走,然后生成一个听起来合理、但实际上完全错误的回复。
最直观的解决方案是加重试逻辑。但对 Agent 链条的第二步做重试,和对一个无状态 API 调用做重试完全不是一回事。上下文已经构建好了,对话状态已经存在了,你需要决定的是:只重试那个失败的调用,还是整条链重新跑一遍。大多数团队至少会在这里踩一次坑。
延迟叠加
单次 LLM 调用 1-3 秒。一个有四次 LLM 调用的 Agent 就是 4-12 秒。再加上一个打到外部 API 的工具调用,变成 15 秒。再因为第一次超时而触发了重试,变成 20 秒。你的用户盯着一个转圈圈的图标。你的前端超时了。你的客服收件箱满了。
延迟叠加最阴险的地方在于,它不会出现在你的平均延迟指标里。你的 p50 看起来挺好。你的 p99 是场噩梦。而 p99 的用户往往是那些查询最复杂的用户 — 恰恰是你最不能失去的核心用户。
工具选择漂移
你的 Agent 能用五个工具。在测试时,它可靠地选对了工具。在生产环境中,面对更混乱的输入和更广泛的查询类型,Agent 开始选择那些"技术上能匹配但结果更差"的工具。该用数据库查询时用了搜索工具。该用领域专用提取器时用了通用摘要工具。
你不会马上注意到,因为 Agent 仍然"能用"。它仍然会产出答案。只是答案的质量微妙地变差了,而这个信号淹没在噪音里。等你意识到准确率已经漂移的时候,你可能已经提供了好几周的降级结果。
Token 爆炸
一个本该两轮就结束的 Agent 循环 — 先分类、再摘要 — 偶尔会因为分类结果模糊而决定多跑四五轮。每轮迭代都会往上下文窗口里塞东西。到第四轮的时候,上下文已经太大了,LLM 调用变得又贵又慢,而且模型开始找不到原始意图,因为相关信息已经被淹没在一大堆中间推理过程里了。
我见过一个失控的 Agent 请求单次烧掉 $2 的 token。这不算什么大钱,但如果每天有 50 次因为某类用户输入触发了循环而发生这种情况,那它很快就变成一个实实在在的预算问题。
输出漂移
这是最微妙的失败模式。你的 Agent 产出的结果在技术上是正确的,但随着生产流量模式的变化,它的语气、格式或具体程度在悄悄发生偏移。Prompt 没变,模型也没变。但输入的分布变了,Agent 的行为以一种难以检测的方式在持续漂移 — 除非你一直在看实际的请求体和响应体。
你真正需要的是什么
在多个项目里和这些问题搏斗之后,我开始相信,Agent 的核心调试问题归根结底有两件事:一是单个请求级别的生命周期可见性,二是能把同一次 Agent 运行里相关请求串起来的关联方式。
不是只有 trace。不是只有日志。也不是只有 dashboard。而是每个请求都有一条时间线,再加上一种把这些请求连起来的方法。
对于经过 ModelRiver 的每一个请求,你需要能看到:
- 是哪个 provider 和 model 处理了这个请求
- 每次 provider 尝试分别花了多久
- 是否触发了故障转移
- 在启用请求体日志时,这个请求及失败尝试对应的完整请求体和响应体
- Token 使用量和预估成本
- 这个具体请求是在哪里出错,或者在哪里走上了意料之外的路径
如果你的 Agent 会发起多次 LLM 调用,你还应该能通过 workflow 名称、customer_data 或其他稳定的运行标识,把这些相关请求关联起来。不是去翻日志,不是同时打开三个 dashboard,而是每个请求都能看清,并且能把它们串起来。
一个例子:8 秒 Agent
让我用一个更贴近真实生产环境的例子来说明。
假设你有一个处理客户反馈的 Agent。正常流程大约 2 秒:分类反馈、提取关键主题、生成摘要。应用代码里一共三次 LLM 调用,每次都通过 ModelRiver 里不同的 workflow 发出去,这样你就能分别检查。
某天早上,用户开始抱怨响应慢了。平均延迟从 2 秒跳到了 8 秒。代码没有任何改动。流量也正常。这种 bug 最折磨人的地方,是它会让你开始怀疑是不是自己记错了什么。
没有按请求维度的可见性时,调试过程是这样的:
- 查应用日志。没有报错。所有请求都返回 200。
- 查 provider dashboard。Token 用量上升了,但那可能是任何原因。
- 多加一些日志。重新部署。等它再次发生。
- 又发生了。新日志显示第二次 LLM 调用耗时 5 秒而不是 0.5 秒。
- 为什么?是 provider 的问题?是 prompt 的问题?是上下文太大了?
- 再加日志。再部署。再等。
这种循环,很容易就吃掉你两天时间。
如果你有请求时间线,再加上写进 customer_data 的运行标识,调试过程会变成这样:
- 打开 Request Logs,按受影响的运行标识或用户 ID 过滤。
- 先看到
classify这个 workflow 的请求只花了 400ms,一切正常。 - 再打开
extract这个 workflow 的请求,看它的时间线:主 provider 先卡了 3 秒,然后失败;接着故障转移到备用模型,又花了 4.5 秒。 - 再看
summarize这个 workflow 的请求,只花了 800ms,正常。 - 5 分钟定位根因:主 provider 在抛出明确 API 错误之前,就已经在模型层面开始降级这类请求。落到应用里的表现更像是不完整的响应,而不是一个明显的
429,所以故障转移往往是在请求已经明显变慢之后才触发。而一旦触发,它又把流量切到了一个更慢的备用模型。
总调试时间:5 分钟,而不是 2 天。更重要的是,你还能发现一个光靠日志几乎看不到的问题:那些还没有完全触发故障转移阈值的请求,正在被一种“不完整响应”的模式悄悄拉低质量。
把这些关联起来之后,整个调试过程就清楚了。不需要去考古日志,不需要靠猜,更多的是一种终于松了一口气的感觉。
这意味着你应该怎样构建
思维模式的转变很简单,而且和我们十年前在数据库和 API 领域经历的那次转变是一样的。
不要再把 AI 调用当成特例。 把它当成基础设施。
当你的应用调用数据库时,你理所当然地认为自己能追踪那条查询、看到它的执行时间、理解它用了哪个索引、用查询计划来诊断慢查询。没有人会接受一个数据库告诉你"你的查询比较慢但我没法告诉你为什么"。
当你的应用调用外部 API — Stripe、Twilio、任何第三方服务 — 你理所当然地认为每一次调用都有一条请求日志,记录着精确的请求体、响应体、延迟和状态码。这是基线级别的基础设施可观测性。
AI 调用理应得到相同的对待。尤其是 Agent 调用,因为它的决策链已经复杂到你不可能仅仅靠最终结果倒推出中间到底发生了什么。
如果你现在还在比较框架本身该怎么选,更适合先看这篇:2026 年最佳 LLM 框架对比(附使用场景)。
这不是某个框架特有的问题 —— 不管你用的是 LangGraph、CrewAI 还是 Agents SDK,缺口都是一样的。框架给了你构建 Agent 的工具,但没有给你在凌晨 3 点生产环境里调试 Agent 的工具。
一个务实的起步方式
如果你想在自己的 Agent 工作流里构建这种可见性,以下三个步骤不依赖于你使用的框架。
第一步:把 Agent 的 LLM 调用路由到一个你自己控制的层。 不要直接调用 provider,而是通过一个能记录每次请求和响应的中间层。这是最大的杠杆。一旦你有了原始数据,后面的事情自然就跟上了。
如果你在用 OpenAI SDK、LangChain、LlamaIndex,或者任何兼容 OpenAI 的客户端,ModelRiver 开箱即用 —— 把 base_url 指向 https://api.modelriver.com/v1,每一次经过 ModelRiver 的调用都会记录延迟、token 使用量和故障转移链。请求体和响应体默认也会在项目级别记录,但如果你不想存这些内容,可以关闭。你也可以自己用代理实现,但日志、时间线可视化和故障转移处理加起来其实很耗时间。
第二步:为 Agent 的每一步命名。 对于链条里比较稳定的步骤,直接使用不同的 workflows —— 比如“分类”“提取”“摘要”;或者至少给每次调用带上稳定的 customer_data,这样相关请求才能被一起过滤出来。出问题时你能立刻看到是哪一步出了问题,而不是面对一堆没有归属的 LLM 调用记录。
第三步:看请求时间线,而不只是看指标。 平均值会掩盖问题。你的 Agent 现在最痛苦的地方在 p99。挑一个慢的或失败的请求,打开时间线,再顺手看看同一轮运行里的其他相关请求。每周做一次,你就能在聚合指标真正显现问题之前,提前几周发现漂移趋势。
什么情况下你不需要这些
不是每个 AI 应用都需要请求级别的生命周期可见性。如果你在做的是:
- 单次调用的 AI 功能 — 一个 prompt 进去,一个响应出来,没有链条 — 那标准日志加上 provider 的 dashboard 大概率够用了。
- 原型或周末项目 — 保持简单,等流量上来了再考虑可观测性。
- 离线批处理管道 — 延迟无所谓,你可以慢慢检查输出。
- 研究或实验 — 你是在 notebook 里优化 prompt,不是在调试生产事故。
这个模式真正重要的场景是:你有真实用户、有多步骤的 Agent、而且你需要在凌晨 3 点出状况时能搞清楚发生了什么,而不是把整个团队都叫起来。
这个变化已经在发生
AI 行业在 2024 和 2025 年一直在构建越来越复杂的 Agent — 更多步骤、更多工具、更多自主性。这个趋势没有放慢。但调试基础设施并没有跟上复杂度的增长。
我们正处于和后端开发在微服务成为主流时相同的拐点。那些在单体架构下好用的工具 — printf、grep 加上祈祷 — 在分布式系统里行不通了。行业随后建设了分布式追踪、结构化日志和可观测性平台。不是因为旧工具不好,而是因为架构的复杂度要求一种不同层次的可见性。
AI Agent 正在走同样的弧线。架构已经复杂到调试工具需要追上来。那些提早在技术栈里建设这种可见性的团队,将花更少的时间救火、花更多的时间构建。
如果你想试试这个模式,ModelRiver 的快速上手指南 几分钟就能搭好,你的第一条请求日志就能告诉你的信息,比一周的 print 调试加起来都多。
