跳到主要内容

238 篇博文 含有标签「reliability」

查看所有标签

无真值情况下的智能体 SLO:为无法实时评分的输出建立错误预算

· 阅读需 13 分钟
Tian Pan
Software Engineer

你的智能体平台连续一年每季度都达到了 99.9% 的“响应成功率”SLO。但工单量增加了 40%。受智能体引导的用户群体的留存率却在下降。轮值运维感到无聊,产品经理在恐慌,而管理层评审一直在问,为什么仪表盘显示一切正常,而支持队列却显示情况一团糟。仪表盘没有撒谎。它只是衡量了错误的东西 —— 因为编写 SLO 的 SRE 将成功定义为“模型 API 返回了 200”,而这正是遥测系统最初唯一能表达的成功定义。

这是智能体可靠性工程的核心问题:成功的信号不是状态码。它是一种关于智能体是否针对特定任务做了正确事情的判断,而这种判断在请求时是无法获得的,通常在会话时也无法获得,有时只有在几天后,当用户提交工单、修改输出或悄无声息地流失时,才能揭晓。你无法在一个尚不存在的列上标记“200 对比 500”的布尔值。

常见的反应是等待获得基准真相(ground truth)后再宣布 SLO。这是错误的。可靠性工作不会在你构建标注流水线时暂停。正确的做法是针对你明知不完美的代理指标(proxies)编写错误预算,将它们命名为代理指标,设定团队在指标触发时的响应策略,并在产生基准真相后将其回填到计算中。这篇文章将探讨如何在不自欺欺人的情况下做到这一点。

取消安全的智能体:你的“停止”按钮背后已经产生的副作用

· 阅读需 12 分钟
Tian Pan
Software Engineer

用户点击“停止”,因为智能体(agent)误解了请求。UI 界面闪烁着“已停止”。在加载图标消失时,智能体已经发送了两封邮件,在你的日历上安排了周二的会议,针对错误的分支开启了一个草稿拉取请求(pull request),并排队发送了一条正在通过工具层追赶取消信号的 Slack 消息。模型已经听话地停止了生成 token。但外部世界并未停止对它三十秒前生成的 token 做出反应。

这是智能体演示中没人提及的失败模式。同步代码中的取消操作本身就是一个难题,背后有一整代协作式取消理论的支持:Go contexts、Python 的 asyncio.cancel、带有任务组的结构化并发,以及“礼貌请求、谨慎升级、不留资源”的整套语法。智能体在这个本就困难的问题上又增加了一层复杂性:规划器不知道用户在第 4 步和第 5 步之间撤回了授权,而它在第 4 步启动的工具在第 5 步被取消时也不会收到通知。“停止”只是一个 UI 交互功能。其背后的系统必须经过专门设计。

当需求是悬崖而非曲线时,如何进行 GPU 产能规划

· 阅读需 13 分钟
Tian Pan
Software Engineer

当 Agent 平台第一次崩溃时,事后分析报告(Postmortem)通常包含这样一句话:“周五我们还有八周的冗余容量。到了周一下午,我们已经达到了已配置容量的 140%。”没有人撒谎。容量模型本身是正确的,只是被应用到了一个它从未被设计用来应对的工作负载上。传统的容量规划假设需求沿着一条平滑曲线增长,周季节性是主导信号,最坏的情况是可以提前六个月规划的“黑色星期五”。Agent 工作负载彻底打破了这一假设。

Agent 需求的形态不是曲线,而是悬崖。有三件事造成了这种悬崖效应,并且它们会产生复合影响。一个企业级客户的入驻,就能根据你已经签署的合同通知,在通宵之间将基线移动 10 倍。一个 Agent 循环可以将微小的用户活动增长放大为扇出倍增的浪潮,对推理端的冲击比面向用户的图表显示的要高出 30 倍。单个产品变更——例如启用工具调用、延长上下文、切换到更大的模型——可以在用户数量不变的情况下,将单个任务的 Token 消耗提高一个数量级。

如果你的容量规划以 QPS 为单位,且你的冗余预算是“75% 的利用率是健康的”,那么你不是在规划。你是在赌这三个“悬崖”不会在同一个星期降临。

JSON 模式是一种方言,而非标准:你备选路径中的隐形崩溃

· 阅读需 13 分钟
Tian Pan
Software Engineer

我第一次看到备用路由引发的事故比它试图缓解的停机故障还要严重时,复盘文档的标题是这样写的:“主服务降级 11 分钟。备用服务导致我们的解析器降级了 6 天。” 没人写错代码。没人跳过 Schema 评审。18 个月前连接备用服务时的二级供应商集成测试还是通过的。其间发生的事情是,两个供应商之一悄悄收紧了其枚举强制转换(enum coercion)策略,而我们下游解析器所遵循的契约——我们认为“或多或少就是 JSON Schema”的契约——已经从共享标准漂移成了两个略微不兼容的方言。

这是我不断看到的失败模式,而且它总能让那些本该更明白的团队感到惊讶。“JSON 模式”听起来像是一个你开启的功能。其实不然。它是一个你需要维护的契约——针对你可能路由到的每一个供应商分别维护——而且随着供应商演进其结构化输出技术栈,这个契约每季度都会发生漂移。你签署合同时供应商文档中所暗示的“无缝替换”,在生产环境中其实是一个需要维护的转换层。如果没有这个层,你的备用路径就会变成一个纸面上的合规产物:存在于架构图中,但在你真正需要它的那天却是坏的。

负载降级是为人类设计的,而 Agent 会放大你正在抵御的风暴

· 阅读需 13 分钟
Tian Pan
Software Engineer

对人类来说,503 意味着一个“稍后再试”的页面和一段咖啡休息时间。对 Agent 来说,503 只是在七次重试中的第一次尝试前那 250 毫秒的挫折,而且规划器(planner)已经开始询问 LLM 是否有其他工具可以绕过这个失效的依赖项。第一种行为为过载的服务提供了恢复空间。第二种行为则是过载服务的噩梦:数以千计的关联重试,每一次都比人类的操作更廉价、更快速,其中一半还会扩散(fan out)到下一个依赖项,因为规划器认为那是一个富有创意的变通方案。

负载脱落(Load shedding)—— 即通过丢弃低优先级任务来维持高优先级路径可用的准则 —— 是在流量发送主体主要是键盘前的人类,或者是具有手动调优重试策略且行为良好的服务的时代设计的。当 Agent 集群出现时,这两个假设都会瞬间崩塌。Agent 重试速度更快,能同时从更多地方发起重试,绕过故障重新规划,并把你返回的 503 视为负载均衡的暗示,而不是你本意中希望达成的协作式背压(back-pressure)信号。

本文将探讨为什么标准的负载脱落策略在面对 Agent 客户端时会失效,上游服务需要什么样的原语才能真正卸载 Agent 流量,以及 Agent 本身在工具层和规划层必须做些什么,才能不再成为别人事故报告中的恶意流量。

重新规划而非重试:为什么大多数智能体错误并非瞬时性的

· 阅读需 12 分钟
Tian Pan
Software Engineer

一次日历写入返回了 409 Conflict。框架默认的错误处理器开始介入:退避 200ms,重试。同样的冲突。退避 400ms,重试。同样的冲突。退避 800ms,重试。等到智能体放弃并告诉用户“我无法预订会议”时,它已经浪费了三秒钟的延迟预算,去证明第一条响应就已经告诉它的事实:该时段已被占用。世界没有改变。它也不会在 800 毫秒内改变。重试永远不会奏效,因为这个错误中没有任何瞬时性的成分。

这是智能体系统中最为常见的错误处理 bug,而且它就隐藏在当今几乎每一个发布的框架之中。带有指数退避的重试模式是从无状态 HTTP 客户端中照搬过来的——在那里这种模式完全正确——但被引入到有状态的规划循环中时,它就完全错误了。对于智能体中的工具错误,正确的默认处理方式不是重试,而是重新规划。

并行工具扇出的结构化并发:谁来负责部分失败?

· 阅读需 13 分钟
Tian Pan
Software Engineer

当你的智能体(Agent)扇出五个并行工具调用——跨三个索引进行搜索、查询两个数据库、调用一个外部 API——的那一刻,你已经跨越了一道无形的界限。你不再是在编写“提示-响应”(prompt-and-response)代码,而是在编写一个并发程序。大多数智能体框架都假装你没有在这样做,而账单会在凌晨 2 点准时送达。

这种假象是令人愉悦的。规划器(Planner)发出一个工具调用列表,运行时环境(Runtime)启动它们,收集返回的任何结果,最后由规划器消费这些汇总数据。从万英尺的高空俯瞰,这就像一个扇出 / 扇入(fan-out / fan-in)流水线,大多数团队在生产环境给他们上课之前,也确实是这样对待它的。问题在于,二十年的并发编程研究——部分失败语义(partial-failure semantics)、结构化取消(structured cancellation)、背压(backpressure)、确定性错误归因(deterministic error attribution)——已经解决了你即将重新发现的那些失败模式。而你的智能体框架在默认情况下,没有引入其中的任何一项。

供应商 99.9% 的 SLA 对你的 Agent 来说衡量边界错了

· 阅读需 14 分钟
Tian Pan
Software Engineer

一个模型提供商发布了 99.9% 的可用性 SLA。采购团队将其理解为“三个九,每年四个小时的停机时间,对于非 0 级(非核心)工作负载是可以接受的”。六个月后,智能体(Agent)功能上线,值班仪表板显示用户感知的任务成功率约为 98% —— 这个数字没有写进任何合同,在提供商的状态页面上也找不到,而且没有人为此负责。提供商满足了他们的 SLA,而产品却没达到其 SLO。两者同时成立,而这种差距并不是一个 bug —— 这是一个算术问题。

大多数团队都忽略了算术这部分。提供商的 99.9% 是针对同步请求工作负载进行衡量的 —— 一个用户,一个提示词,一个响应,一个计费事件。而智能体并不会产生这种工作负载。一个用户感知的任务会扇出(fan out)为 8 到 20 次推理调用,它会对瞬时错误进行重试,对慢速调用进行对冲(hedge),并聚合部分输出。每一次调用都是对提供商故障分布的一次独立抽样,如果任何关键调用失败,任务就会失败。SLA 覆盖的边界和用户感受到的边界并不是同一个边界。

95% 可靠性幻觉:为什么你的 10 步 Agent 在 40% 的情况下会失败

· 阅读需 13 分钟
Tian Pan
Software Engineer

在几乎每一个智能体(agent)项目评审中,都有一个会让谈话戛然而止的时刻。有人画了一张小图表:y 轴是端到端任务成功率,x 轴是工具使用的步骤数。曲线急剧下降。全场陷入沉默,因为屋子里的每个人之前都在争论提示词(prompt)、模型和检索策略——而这张图表在告诉大家,所有的这些争论,都抵不过一个简单的事实:这条链条上的环节太多了。

这一数学原理是可靠性工程中最古老的结论之一,如今被移植到了一个自以为是的新领域。如果流水线中的每一步都以概率 p 独立成功,那么 n 个串联步骤的成功概率就是 p 的 n 次方。代入一些在进度报告中听起来还不错的数字:单步可靠性 95%,十个步骤,端到端成功率就只有 60%。二十步降至 36%。三十步则降至 21%。那个“95% 的时间都能正常工作”的智能体,实际上在三分之一的真实用户请求中都会失败,因为真实的用户请求绝非只有单个步骤。

人格漂移:当你的智能体忘记自己的身份时

· 阅读需 12 分钟
Tian Pan
Software Engineer

系统提示词写着:“你是一名金融分析师——保持保守,永远不要给出具体的买入/卖出建议,始终披露不确定性。”在最初的二十轮对话中,智能体的表现确实像一名金融分析师。到了第五十轮,它开始推荐具体的股票,模仿用户随意的语气,且比起第三轮时更少做风险对冲。没有人修改过系统提示词。没有人注入任何恶意指令。角色只是在对话的重压下被侵蚀了,就像河岸在没有任何东西越过“攻击”阈值、但流水从未停止移动时所发生的那样。

这就是人格漂移(Persona Drift),也是你的评估套件未能捕获的退化。能力评估衡量模型是否能完成任务。而身份评估——即模型是否仍在按照系统提示词要求的方式执行任务——在研究论文之外几乎不存在。其结果是产生了一类生产环境下的失败:它们在逐轮查看时显得正确,只有当你从头到尾阅读完整记录时才会发现问题。

智能体能力悬崖:为什么你的模型升级让简单的 95% 变得完美,却让困难的 5% 成了你最糟糕的季度

· 阅读需 13 分钟
Tian Pan
Software Engineer

你上线了新模型。综合评估通过率从 91% 提升到了 96%。产品团队在全体员工大会上宣布这是一次重大胜利。六周后,可靠性团队却迎来了有史以来最糟糕的一个季度——并不是因为故障变多了,而是因为现在每一个故障都需要三名工程师花上两天时间才能解决。

这就是智能体能力悬崖 (agent capability cliff),它是生产环境 AI 中最反直觉的失败模式之一。模型升级并不会均匀地提升所有任务的表现。它们将增益集中在大部分流量上——即那些旧模型原本就能在大部分时间内处理正确的简单和中等案例——而长尾中真正困难的输入却只看到了微乎其微的改进。你的失败面缩小了,但剩下的每一次失败都是能力边界案例,这些案例旧模型也处理不了,而且简单的提示词工程 (prompt engineering) 也无法修复。

这个“悬崖”并不是新模型的缺陷。它是我们衡量模型改进的方式(混合难度评估集的平均通过率)与值班排班中实际遇到的问题(最难流量的残差集,现在已经没有了以前占据主导地位的简单故障的缓冲)之间的不匹配。

智能体幂等性是一项编排契约,而非工具属性

· 阅读需 12 分钟
Tian Pan
Software Engineer

客服工单在上午 9:41 送达:“我被扣了三次费。”链路追踪看起来无异常。一条用户消息,一次规划器轮转,三次对 charge_card 的调用 —— 每次都有唯一的工具调用 ID,每次都返回 200 OK,每次都写入了不同的 Stripe 扣款。工具本身有幂等键,后端有去重表,支付处理器也遵循 Idempotency-Key。每一层都是幂等的,但客户依然支付了三次。

如果你构建 Agent 的时间足够长,这类 Bug 迟早会出现在你的桌上。它不是任何工具的 Bug,而是 Agent 循环与工具之间契约的 Bug,而这种契约几乎总是只存在于资深工程师的脑海中。