为什么你的智能体在开发中表现完美,在生产中却状况百出
Agent 演示总是能成功。数据库里有三个客户,一个匹配记录,向量索引中有 12 篇文档,一个带有无限空档的空日历。Agent 选对行,检索到正确的文档,预订好正确的会议。上线吧。
接着,生产环境交给了同一个 Agent 一千万个客户,其中在同一个城市有三个 “John Smith”;一个返回了四千行的过滤器,因为 Agent 本想表达 status = 'active' 时却自信地写成了 status != 'closed';一个向量查询返回了七篇看似合理的文档,而 Agent 从未被要求在这几篇文档之间做选择;以及一个每个空档都需要协商的日历。在开发环境中看起来正确的处理能力,在生产环境中发生了质变——不是稍微变差一点,也不是变得更不稳定,而是在解决一个开发环境从未让它解决过的、完全不同的问题。
这就是“在本地运行正常”所掩盖的鸿沟。对于确定性代码,这句话在处理边缘案例时已经算是个谎言。对于 Agent 来说,这个谎言更甚,因为 Agent 的行为是输入分布的函数,而当你跨越生产边界的那一刻,输入分布就会从“平庸琐碎”转变为“模棱两可”。
稀疏的开发数据掩盖了唯一重要的测试
标准的开发设置是为快速迭代而设计的。你预置三行数据,因为三行足以渲染 UI 并运行 SQL。你在向量索引中放入 12 篇文档,因为 README 示例就是这么写的。你创建了一个只有三个会议的假日历,因为团队中没人想等真实的日历。
每一个决定对工程效率来说都是正确的,但对 Agent 评估来说都是错误的。Agent 的推理空间是其输入的基数(cardinality)和歧义性(ambiguity),而你刚好把这两者都隐藏了。在只有一个 John Smith 的测试数据中要求 Agent “寻找名为 John Smith 的客户”,并不是在测试消歧能力——而是在测试 Agent 是否能调用该工具。在涉及三个不同主题的 12 篇文档中要求它“寻找相关的政策文档”,并不是在测试检索推理——而是在测试 Agent 是否能构建出任何查询。
你看着 Agent 成功运行,并得出该能力已构建完成的结论。但实际上你构建的能力,仅适用于测试数据所编码的规则之下。在开发环境中,每一次匹配都是唯一的,每一个过滤器都是狭窄的,每一次检索都是明确的。在生产环境中,这些假设都不成立,Agent 会遇到一种它从未被要求解决过的问题形态。
这种不对称性使其难以被察觉。确定性代码在处理一行数据和一百万行数据时,失败的方式是一样的;“差一错误”(off-by-one)始终是“差一错误”。而 Agent 在处理一行数 据和一百万行数据时的行为可能会有质的区别——在面对小数据集时自信地正确,而在面对大数据集时自信地错误——因为两者之间改变的不是代码路径,而是当输入不再明确时,Agent 必须执行的推理过程。
生产环境的密度是一个不同级别的问题
让我们梳理一下典型的 Agent 工具在生产规模下的实际表现。
一个“查找客户”工具在开发环境中返回一行,但在生产环境中返回了 47 行,因为有三个人同名,还有 12 个人匹配到了 Agent 决定使用的模糊子字符串。Agent 没有处理“如果结果集比我预想的大该怎么办”的测试经验,因为在开发环境中结果集永远正好是一个。它要么随意挑选第一个,要么自信地把 47 个结果都呈现给用户,仿佛所有结果都是正确的,要么在越来越窄的过滤器中循环,直到排除掉正确答案。
一个向量搜索工具在开发环境中返回三篇高度相关的文档,现在返回的是更密集分布中的 Top-K,而前三名与前七名的相似度差异小到 Agent 无法解读。在开发环境中没有什么需要解读的——相关的文档肉眼可见就是相关的。在生产环境中,Agent 必须推理在七篇看似合理的文档中该信任哪一篇,而这种推理在测试数据中从未被演练过。
一个“寻找空档”工具在面对空日历时能轻松成功,现在则呈现出一百个冲突的空档,每个空档都有不同的利益相关者、不同的优先级和不同的重新调度成本。在开发环境中,“寻找空档”只是查表 。在生产环境中,它是一场协商。Agent 的提示词是为查表而优化的。
一个“按状态过滤记录”工具在开发环境中返回 30 行,其中每个状态都是相关的。在生产环境中它返回 40,000 行,Agent 会因为上下文预算爆炸而悄悄地将其截断为第一页,它计算出的答案现在取决于 Agent 根本不知道存在的、数据库随机的排序规则。
这些都不是模型本身的 Bug。它们是模型遇到了一种你的测试数据曾承诺过永远不会出现的输入分布。Agent 的能力边界是基数和歧义性的函数,而不是抽象任务名称的函数,而开发环境系统地掩盖了这两者。
“在我本地没问题”对智能体而言,从结构上讲是一个更严重的谎言
对于确定性代码,“在我本地没问题”通常意味着“我们漏掉了一个边缘案例”。修复方法是针对该边缘案例编写单元测试,而且无论输入规模如何,代码逻辑都是一样的。一旦你记得编写测试,可靠性差距就会消失。
对于智能体(Agent)来说,“在我本地没问题”有着更深层的含义。它意味着模型的推理能力尚未在生产环境实际挑战它的维度上经受考验。修复方法不是补上一个缺失的测试用例,而是建立一套缺失的测试体系。你无法通过增加一个额外的固定数据(fixture)来弥补开发环境的稀疏性,因为失效模式不是针对特定输入,而是跨所有输入的分布偏移(distribution shift)。
必然的结论是,智能体通过开发测试套件并不能有力地证明它在生产环境中也能正常工作——相反,这是一种具有误导性的证据,因为该套件选择了智能体总能成功的环境。在稀疏的固定数据上获得的绿色测试信号意味着“智能体可以处理简单的情况”,而这一点你无需运行任何测试就能预料到。你真正需要的信息是智能体在生产环境密度下的行为表现,而测试套件的设计初衷并不是为了产生这些信息。
这也是为什么“我们以后再添加生产测试”是错误的顺序。智能体在生产环境密度下的行为在性质上是完全不同的,因此,从实际意义上讲,你发布的智能体并不是你测试过的那个。正确的框架是:在针对其将要服务的输入分布进行演练之前,该智能体作为可部署的产物并不存在。在此之前的任何东西都只是原型。
生产环境状态(Production-shaped state)究竟是什么样的
缩小差距并不是靠增加一个更密集的固定数据文件,而是要将生产分布视为评估的一等输入。具体来说:
- 生产影子数据集 (Production-shadow datasets):从生产状态的脱敏快照中获取测试环境的种子数据,而不是使用合成的固定数据。客户表要具备真实环境的基数(cardinality)、重名率和脏数据尾部。向量索引的大小应与真实环境一致。日历应具有真实环境的冲突密度。智能体 的工具返回的应该是它们实际会返回的内容。
- 评估中的密度轴 (A density axis in eval):在多个基数(如 10、1,000、100,000 行)下对相同的智能体能力进行评分,并绘制跨轴的行为图表。在开发环境固定数据规模下不可见的性能退化,在你绘制曲线的那一刻就会变得清晰。保持稳定的能力是可证明的稳健;而在超过某个阈值后崩塌的能力,则能准确地告诉你智能体的推理在何处停止泛化。
- 注入歧义的固定数据 (Ambiguity-injection fixtures):构建固定数据集,使歧义成为默认情况而非特殊情况。多个匹配项、近乎重复的文档、冲突的时段、部分相关的行。智能体在每次测试中都必须进行消歧,因为在每次生产调用中它都必须消歧。一个具有唯一正确答案的固定数据是无法测试出任何东西的。
- 将影子流量作为发布门禁 (Shadow traffic as a release gate):在不执行副作用的情况下,针对实时生产状态运行智能体。将它的预期操作与实际操作进行对比。这个差距就是你的发布信号;智能体选择发生分歧的案例就是你下一次迭代的评估集。这是目前几个生产团队在放行智能体执行不可逆操作之前所采用的准则,也是衡量其在关键分布下行为的唯一方法。
- 智能体就绪门禁,而非智能体正确性门禁:不要根据“测试套件通过”来发布。而要根据“已在模拟目标环境形态、目标环境基数的状态下观察过智能体,且其失效模式已具备特征并处于可控范围”来发布。门禁的重点在于你是否进行了生产环境的观察,而不在于开发测试是否显示绿色。
这一切背后的模式是一致的:停止测试那些方便测试的东西,开始测试生产环境实际会遇到的情况。实现这一目标的手段是固定数据密度和影子流量,而不是更多的单元测试。
决定是否在生产环境的规制下发布,而非开发环境
这一切的基础是架构层面的认知:智能体的行为是它所见输入分布的函数。开发环境是从智能体永远不会服务的分布中提取的受控样本。生产环境才是真正的分布。评估套件需要存在于第二个世界,而非第一个世界。
这意味着要将固定数据视为生产环境的代表,它们有自己的偏移(drift)、新鲜度要求和衰减。一套使用了六个月的固定数据并不能代表智能体现今在生产环境中面临的问题,因为生产分布已在其底层发生了偏移。固定数据语料库需要与智能体相同的运维(Ops)准则:标注日期、版本化、从当前状态重新推导,并在陈旧时停用。
这也意味着,反对生产级测试的团队效率论点是错误的方向。在稀疏固定数据上运行智能体的成本并非为零——其成本是生产环境第一周发生的事故,因为智能体在真实用户面前第一次遇到了这种输入分布。这种“开发快、生产崩”的循环并不是效率的提升;它是借来的时间,最终要通过事故复盘和客户信任的损耗来偿还。
实践标准很简单:在智能体上线之前,你应该能够指出它被观察时的规制(regime),并说:“这就是生产环境的样子,智能体在这里的行为是可以接受的。”如果你唯一能指出的规制是开发环境的固定数据,那么你还没有评估这个智能体——你评估的是另一个针对不同问题运行的智能体。发布你真正测试过的那个,或者测试你真正计划发布的那个。除此之外没有第三种选择,否则只会以恐慌性的回滚告终。
- https://www.codeant.ai/blogs/llm-shadow-traffic-ab-testing
- https://www.anthropic.com/engineering/demystifying-evals-for-ai-agents
- https://towardsdatascience.com/most-ai-agents-fail-in-production-because-theyre-built-backwards/
- https://www.zenml.io/blog/what-1200-production-deployments-reveal-about-llmops-in-2025
- https://www.getmaxim.ai/articles/exploring-effective-testing-frameworks-for-ai-agents-in-real-world-scenarios/
- https://www.confident-ai.com/blog/definitive-ai-agent-evaluation-guide
- https://achan2013.medium.com/ai-agent-anti-patterns-part-2-tooling-observability-and-scale-traps-in-enterprise-agents-42a451ea84ec
- https://www.glukhov.org/observability/observability-for-llm-systems/
