跳到主要内容

238 篇博文 含有标签「reliability」

查看所有标签

演示到生产的悬崖:为什么准确率 90% 的智能体发布率为 0%

· 阅读需 11 分钟
Tian Pan
Software Engineer

有一种特定的会议,通常发生在令人印象深刻的智能体(Agent)演示大约六周后。原型演示了订机票、重构模块、核对发票——现场演示,一次成功,就在利益相关者面前。大家都认为它可以上线了。接着有人调取了生产数据,发现那个“好用”的智能体每完成 40 个任务就会产生一张工单,每几百次就会产生一笔退款,还留下了一堆没人能解释的半成品状态。项目没被砍掉,但它卡住了。而且到现在还卡在那儿。

这就是从演示到生产的悬崖,也是智能体项目失败最常见的方式。悬崖并非由糟糕的模型或懈怠的团队造成的。它源于一个度量错误:将 90% 的成功率视为完成了 90% 的上线工作。事实并非如此。一个 90% 准确率的智能体是一场成功的演示,但对于大多数真实工作流来说,它是一个无法上线的产品。2025 年登上头条的 MIT NANDA 报告指出,95% 的企业生成式 AI 试点项目没有产生可衡量的损益(P&L)影响——这就是在大规模范围内统计出的悬崖现状。

Eval 测试集是滞后指标:你的绿色仪表盘只反映上季度的失败

· 阅读需 9 分钟
Tian Pan
Software Engineer

每一个成熟的 AI 团队构建其评估套件的方式都如出一辙,而且几乎没有人会公开说出那个潜台词。生产环境中出现了一个故障。有人写了一份复盘报告。一名工程师将该事故提炼为一个测试用例,将其添加到评估套件中,于是仪表盘再次变绿。重复这个循环一年,你就会拥有几百个案例、一个令人满意的通过率,以及一个足以让你在演示幻灯片上感到无比安心的数字。

潜台词是:那个评估套件其实是一个博物馆。每一件展品都是团队已经挺过来的故障类别。98% 的通过率证明了你的系统可以抵御过去 —— 抵御那些已经发生过的特定破坏方式 —— 而对于模型迁移、提示词编辑或用户行为转变即将引入的新型故障模式,它几乎给不出任何参考。评估集是一个披着先行指标外衣的滞后指标。

那个你从未进行过负载测试的备用模型

· 阅读需 10 分钟
Tian Pan
Software Engineer

每一个具有韧性的 LLM 设计在配置中都有一行代码指定了一个备选模型。它之所以存在,是因为有人在设计评审中提出了正确的问题——“如果主模型宕机了怎么办?”——而另一个人用一个 fallback: 键回答了这个问题。大家都点头表示赞同。架构图上多了一个带有虚线箭头的第二个框。合规文档中也增加了一句关于优雅降级的描述。

然后,再也没有人碰过它。

备选模型是大多数生产环境 AI 系统中被断言得最自信、但演练最少的组件。它被命名、被记录、被画在图中——而在它真正承载流量的那一天,也是它第一次遇到真实请求的一天。你并没有建立安全网。你只是构建了第二个断裂强度未知的模型,而你将在最糟糕的时刻发现那个极限。

你的备用路径是生产环境中唯一未经测试的代码

· 阅读需 10 分钟
Tian Pan
Software Engineer

每个成熟的 AI 系统都会配备回退方案(fallback)。当主模型被限流时,路由到更廉价的模型。当服务商返回 5xx 错误时,提供缓存的答案。当置信度低于阈值时,回退到手写的启发式规则(heuristic)。架构图中有一个清晰的小分支,标注为“降级模式”(degraded mode),每个人都因此感到更安全。

令人不安的部分在于:那个分支是系统中几乎从未运行过的唯一代码。主路径每天执行数百万次,并经过大量流量的调试、性能分析和实战测试。回退路径几乎从不执行——直到某天,它在负载下、在事故期间、在三名工程师看着仪表盘变红时,突然为所有人同时执行。

一个你不练习的回退方案并不是冗余。它是一个不受监控的第二系统,其“首秀”在统计学上注定会发生在最糟糕的时刻。

Happy Path 是你的 Agent 评估测试过的唯一路径

· 阅读需 11 分钟
Tian Pan
Software Engineer

看看大多数智能体(Agent)评测集是从哪里来的。有人构建了智能体,向团队演示,演示成功了,于是演示脚本就变成了评测套件。那些通过评审的案例,正是有人已经亲眼看到它们运行成功的案例。评测集在构建之初,几乎就是“快乐路径”(Happy path)的录音——即在截屏当天成功运行的那一段工具调用序列。

所以,当仪表盘显示智能体得分为 94% 时,它实际上是在说:它通过了我们能想象到的案例。它完全没有提及搜索 API 在多步计划中途返回 429 错误的情况,或者用户推翻了两轮前设定的约束的情况,亦或是检索结果为空,智能体必须在胡乱猜测和承认不知道之间做出选择的情况。这些情况并非没有通过你的评测。它们压根就没在评测里。

这就是黄金路径偏见(Golden-path bias),除非你刻意对抗,否则它就是智能体评测套件的默认形态。解决方法不是增加案例数量,而是增加不同种类的案例——这些案例应根据失败模式(Failure mode)来选择,从生产环境中收集,并针对刻意引入的故障进行压力测试。

你的智能体从未发送的幂等键

· 阅读需 12 分钟
Tian Pan
Software Engineer

一位客户曾因一次退货收到了三次退款。这不是因为模型幻觉出了某种政策,也不是因为人类填错了表单——而是因为退款工具超时了两次,智能体随之重试了两次,而每次重试都发起了一个全新的请求,导致支付处理器根本无法识别这其实是它处理过的工作。三个干净的 HTTP 200。三次真实的资金转移。智能体完全按照指令行事:当调用失败时,重试。

这个 Bug 不在模型中。Bug 出在一个从未发送的 Header(标头)中。

重试是智能体最自然不过的本能。工具调用返回错误,或者更糟,什么都不返回,而循环系统的直觉——无论是编码在框架、提示词还是模型自身的训练中——就是重新尝试该动作。这种直觉对于“读”操作是正确的,而对于“写”操作则是灾难性的。一个韧性十足的智能体与一个会向客户重复收费的智能体之间的区别,不在于智力高低,而在于每一次改变状态的工具调用是否携带了幂等键(Idempotency Key),以及另一端的系统是否真正履行了它。

Prompt Caching 的隐形代价:当缓存命中提供错误的用户上下文时

· 阅读需 13 分钟
Tian Pan
Software Engineer

Prompt 缓存被宣传为一种稳赚不赔的方案。缓存长期的共享前缀——你的系统提示词、工具定义、检索到的上下文——只需为变化的短尾部分支付全额费用,然后看着账单下降。数字是真实的:缓存读取的成本大约是新鲜输入 Token 的十分之一,因此具有大量稳定前缀的工作负载,其输入成本可以降低 80% 或更多。团队因此采用它,因此调整它,并用单一指标来汇报:缓存命中率,且趋势向好。

这种表述掩盖了一个事实:你刚刚划定的边界——缓存前缀与非缓存尾部之间的界线——并不是一个计费旋钮。它是一个正确性边界。缓存断点之上的所有内容都是系统认为可以在请求之间互换的内容。如果你为了最大化命中率而划定这条线,你就是在让财务指标来决定你的 Prompt 中哪些事实可以在用户之间、租户之间以及跨时间共享。这是一个隔离决策,理应有目的地做出。

这种失效模式是隐蔽的,因为它永远不会报错。如果缓存命中提供了一个由另一个用户概况塑造的上下文,它会返回一个格式完全正确的响应。如果缓存命中提供了一个在缓存预热时为真、但在重用时已失效的个性化信息,它会返回一个自信、连贯但错误的答案。你的延迟图表或错误率不会有任何波动。唯一的信号是看起来 非常棒 的命中率——因为 Key 太粗颗粒度了。

演变成产品决策的速率限制

· 阅读需 11 分钟
Tian Pan
Software Engineer

频率限制(Rate limit)过去曾是一个基础设施细节。当你遇到 429 错误,你会使用退避算法(backoff)重试,将溢出的请求排队,而 On-call 频道之外的人甚至根本不知道这回事。用户看到的响应只是比平时慢了几百毫秒。这就是故事的全部。

对于智能体(agentic)功能,这个故事不再适用。当一个智能体在执行多步计划的过程中,中途触及了供应商的每分钟 Token 数(TPM)上限时,失败并不会停留在基础设施层。它会表现为一个半成品的答案、一个在最后一次调用前卡住的工具循环,或者让用户盯着一个永远无法解决的加载动画。配额不再仅仅是后端容量数字,而变成了一个产品必须围绕其进行设计的约束条件 —— 就像产品围绕结账流程或空状态进行设计一样。

自信地返回错误答案的语义缓存

· 阅读需 11 分钟
Tian Pan
Software Engineer

两个支持用户在短短一分钟内向你的智能体提出了几乎相同的问题。第一个用户问:“我们针对 EU 订单的退款期限是多久?”第二个用户问:“我们针对 US 订单的退款期限是多久?”这两个句子的嵌入向量(embeddings)仅有一丝之差——长度相同,结构相同,只有一个两字母 token 的区别。你的语义缓存经过了相似度阈值的微调(在演示中看起来非常合理),将它们判定为匹配。于是,第二个用户得到了第一个用户的答案。EU 的 14 天冷静期被当作事实,以流利的文字呈现给了一位 US 客户,且没有任何补充说明。

没有人会因此收到报警。缓存返回了 200。延迟表现优异。成本看板显示了一次命中,这正是每个人想要的结果。唯一能说明出了问题的信号是客户根据一项不适用于他们的政策行事——而这个信号在几天后才会通过退款纠纷传来,而不是通过你的监控系统。

正是这种故障模式,让语义缓存与你之前构建过的任何缓存都截然不同。精确匹配缓存可能会过期,但它永远不会 错误——key 要么匹配,要么不匹配。语义缓存则是有意放弃了这种保证。它被设计为能针对从未见过的 key 返回答案,而这种延迟优势的代价是正确性风险,大多数团队从未对这一风险进行过量化。

原本运行良好的工具,直到两个智能体同时调用它

· 阅读需 11 分钟
Tian Pan
Software Engineer

一个工具通过了测试。你从一个智能体(agent)调用它,看着它读取记录、转换、写回并返回一个清晰的结果。几周以来,它每次都表现完美。然后你将智能体集群从一个 worker 扩展到十二个,结果一个客户报告说他们的订阅在同一分钟内被升级了两次。工具没有变,只是调用它的并发量变了。

这是单智能体测试无法捕获的失败模式,因为单智能体测试永远不会产生触发该模式的条件。从结构上看,单个调用者是一个串行工作负载。你的工具默默依赖的所有并发假设——读取时没有其他人在写入、自增的计数器是属于它自己的、保存时正在编辑的草稿依然存在——在只有一个调用者时都是理所当然成立的。工具并非正确,只是未经过测试。这两者是不同的,而在第二个智能体出现之前,这种差异是不可见的。

当两个 Agent 共享一个工具:多 Agent 系统中的并发 Bug

· 阅读需 11 分钟
Tian Pan
Software Engineer

当你输入“启动另一个智能体来并行处理”的那一刻,你就已经成为了一名分布式系统工程师。你可能没有注意到。框架让它变成了一行代码的改动,演示运行良好,延迟也降低了。但在底层,你刚刚引入了两个在没有协调的情况下读写共享状态的进程 —— 而困扰了数据库领域五十年的每一个竞态条件 (race condition)、更新丢失 (lost update) 和脏读 (dirty read),现在都潜伏在你的智能体堆栈中,伺机而动。

这种情况之所以棘手,是因为故障看起来不像并发 Bug,而像是某个智能体出错了。输出在语法上是有效的,流水线显示绿色,没有抛出异常 —— 然而,客户被收取了两次费用,或者文件丢失了一半预期的内容,又或者一个智能体自信地根据另一个智能体已经覆盖的数字采取了行动。你去调试那个“愚蠢的智能体”,发现它的提示词 (prompt) 没有任何问题,因为提示词根本就不是问题的症结所在。

停止并非一种状态:为什么智能体需要类型化的终端原因协议

· 阅读需 11 分钟
Tian Pan
Software Engineer

打开一个 Agent 集群(fleet)的仪表板,你会看到一个干净的数字:完成率,94%。在它下方是一系列运行记录,每条都标记着两种状态之一 —— 正在运行(running)或未在运行(not running)。那 6% “未在运行”的记录看起来完全一样。其中一些完美地完成了任务。一些在离完成还差两步时达到了步骤限制。一些捕获到了工具错误并放弃了。一些正确地判定任务是不可能的。还有一些则干脆断了思路,停止输出 token。

你的监控无法区分这些情况。它只知道流程不再运行了。它不知道 为什么,而“为什么”正是你在决定是否要呼叫(page)值班人员时唯一关心的事情。