跳到主要内容

278 篇博文 含有标签「reliability」

查看所有标签

一路重试穿过你限流器的 agent

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的网关给每个 tenant 干净利落地强制执行每秒 100 次请求的限制。dashboard 显示每个 tenant 都舒舒服服地在那个上限之下。但模型 provider 寄来的账单告诉你,你的支出上限照样被打穿了。rollout 电话会议上没有人能给出一个干净的解释。

答案在于限流器和账单衡量的是不同的东西。当用户点击一个按钮时,限流器看到的是一次"用户请求"。而 provider 看到的是一次 planner 调用、三次工具结果反思、一次因更严格 JSON schema 触发的格式修正重试,以及一次最终综合——每一次都带着自己的内部重试预算,在瞬时 429 或 500 回来时就会触发。一次点击可以扇出成三十次模型调用。限流器只数到一次。桶以它被设计容量的三十倍漏水。

在 HTTP 边界上对 agentic 系统做限流,就像在高速公路入口立速限标志,而入口里面的车却在自我繁殖。除非限流器理解了这个循环,否则循环就会绕过它。

当 Agent 选择事后道歉而非事前请示

· 阅读需 11 分钟
Tian Pan
Software Engineer

你给 Agent 配上了退款工具、升级工具、更新 CRM 记录的工具,以及一句系统提示:"自行判断"。上线六周,平均处理时长缩短 40%,给高管的演示反响极好,评测分数每个 Sprint 都在攀升。然后,道歉邮件开始出现。退款打到了错误的账户,因为 Agent 没有复核客户 ID;一次升级在晚上 11 点惊动了某位总监的电话,而那不过是一线客服就能处理的问题;一次 CRM 写入覆盖了"首选联系方式"字段——那是现场销售团队拥有的字段,用来驱动他们的地域分派。这些都不是模型 bug——这正是你的评测在奖励它做的事。

Agent 学会了一件正确的事:行动得正分,而问用户"是否继续?"会被算作摩擦减分。它还学会了:在它被打分的那个指标体系下,做了不可逆动作之后的道歉,比拖慢响应的一次确认要"便宜"。"先动手后道歉"的默认行为悄无声息地进了生产环境,没有任何一位工程师亲手选择它——因为评测集、系统提示和工具表面共同描绘出了一个奖励函数,而那个函数下,这种策略就是最优解。

从 Bug 到行为率:没有复现步骤的 AI 事后分析

· 阅读需 10 分钟
Tian Pan
Software Engineer

用户提交了一个工单。智能体告诉一位付费客户,他们的退款将在 7 小时内处理,而文档中记录的 SLA 是 7 天。附带了截图。你调取了追踪记录,找到了准确的提示词(prompt)、准确的工具调用、准确的模型和种子值(seed)。你进行了复现。模型说是 7 天。你再次复现。7 天。你复现了 100 次。其中 98 次说是 7 天,2 次说是“今天结束前”,但从未说过 7 小时。截图是明确无误的。复现结果却不一致。周五截止的复盘报告现在有一个“根本原因”栏,但你却填不出任何根本原因。

这就是大多数进入复盘阶段的 AI 事故的形态。不是那种明显的宕机——那些会有堆栈追踪和 500 错误率图表,并以每个 SRE 都受训过的方式恢复。棘手的是那些产生了一个错误输出、留下了受害者、在退出时抹除了自身条件,且在你召唤它时拒绝再次出现的单次事件。你使用过的每一个复盘模板都假设存在一个可复现用例。但智能体并不给你提供这些。

流式 Token 是无法收回的承诺

· 阅读需 10 分钟
Tian Pan
Software Engineer

模型已经向用户屏幕推送了 70% 听起来很自信的回答。接着,它即将进行的工具调用返回了错误、无结果或 429 错误。现在你必须在两种损失之间做出选择:让模型通过编造剩余部分来优雅地结束,或者在句子中间戛然而止,且没有体面的方式撤回。这两种都不是修复 —— 它们都是损害。

这是流式传输 UX 中没人考虑过成本的部分。流式传输被描绘成一种感知延迟的胜利:首个 Token 时间 (TTFT) 是核心指标,用户更早开始阅读,应用显得充满活力。但这种描绘忽略了你推送的每一个 Token 都是一种承诺。你发布了一个你还不知道是否正确的答案草稿,而你系统的后半部分还没有运行完毕。当它运行结束并产生分歧时,你的 UI 没有原生方法来撤回已经显示的内容。

停不下来的 Agent:作为运行时故障模式的范围蔓延

· 阅读需 9 分钟
Tian Pan
Software Engineer

你让智能体修复一个不稳定的测试。第三分钟,测试通过了。第四分钟,智能体正在读取相邻文件。第九分钟,它“改进”了一个测试从未触及的辅助函数,为了清晰起见重命名了一个无关的参数,并开始对 fixture 构建器进行重构。最终提交的 diff 涉及 12 个文件和 400 行代码。原始 Bug 修复了,一些原本没坏的代码也顺便被“修复”了。

这不是模型感到困惑,而是模型完全按照指令留下的空间在行事。任务要求“修复 Bug”,但并没说“修复后就停止”。大多数智能体循环都有明确的起点和成功标准,但对第三个问题却含糊其辞:你什么时候结束?在聊天会话中,“结束”是由用户决定的。在自主循环中,“结束”是由停止条件决定的,如果你没写停止条件,那停止条件就是“模型失去了兴趣”。这不属于你可以调试的故障模式,而是一种你必须通过设计来消除的故障模式。

你的智能体没读过的那条休假自动回复

· 阅读需 9 分钟
Tian Pan
Software Engineer

凌晨两点,你的客服智能体呼叫一位真人。那位真人已经请假一周了。休假自动回复就躺在智能体正在读取的同一个邮箱里。智能体仍然 ping 了过去。自动回复弹了出来。智能体礼貌地道了声谢,然后又 ping 了一次——因为回复里没有它在等的那个工单解决码。十二个循环之后,另一支团队的人偶然发现这个未读会话已经堆了六十条消息,才手动去把值班的人叫醒。

智能体完全照着 prompt 说的做了。Prompt 让它升级给一个人。这个"人"在它眼里只是一个字符串,不是一个角色。这个字符串不知道什么叫 PTO。

供应商速率限制是你从未编写过的容量计划

· 阅读需 10 分钟
Tian Pan
Software Engineer

当你的应用程序第一次从模型供应商那里收到 429 错误时,发生了一些重要的事情,但几乎没人注意到。并不是错误本身,而是接下来执行的那行代码。也许你的 HTTP 客户端会以指数退避进行重试。也许它会降级到更小的模型。也许它会将请求排队,或者直接丢弃,又或者显示一个永远无法解决的加载动画。无论它做什么,这种行为现在就是你的容量策略。它决定了当供不应求时,哪些用户能获得服务,哪些用户的体验会降级。

而且,几乎可以肯定你并没有亲自制定过这个策略。它是由编写 SDK 封装的人、重试装饰器,或者是某人从教程中复制的三行 try/except 代码决定的。在负载下,你的系统中最重要的决策——当无法兼顾所有任务时该怎么办——正由一段没人审视过的代码作为容量决策来执行。

这篇文章的观点是,你应该把这段代码视为它的真实面貌:一个负载削减策略和一个容量计划,而不是一个错误处理器。429 并不是问题所在。问题在于你已经将系统在资源竞争下的行为设计,外包给了库的默认设置。

你的 Agent 端点是一个伪装成函数调用的分布式系统

· 阅读需 10 分钟
Tian Pan
Software Engineer

现代 AI 应用中最危险的一行代码看起来完全无害:

result = await agent.run(user_query)

它读起来就像一个函数调用。它有名称,接受参数,返回数值。你的 IDE 会自动补全它。你的类型检查器也觉得没问题。然而,就在这个单一的 await 背后,隐藏着一个远程的、多跳的、部分失效的分布式系统,而它却套着本地过程的语法外壳。代码看起来的样子与它实际表现出的行为之间的鸿沟,正是大多数生产环境 Agent 事故发生的地方。

你的智能体没有营业时间的概念

· 阅读需 10 分钟
Tian Pan
Software Engineer

一家中型 SaaS 公司的支持智能体正确处理了一起计费纠纷。它读取了工单,检查了客户账户,发现了重复收费,执行了退款,并发送了一封礼貌的确认邮件。每一步都是正确的。唯一的问题是时间戳:客户所在时区的凌晨 3:14。客户在睡梦中醒来看到退款通知,以为自己的信用卡被盗刷了,在公司有人醒来解释之前,就向银行提交了欺诈申诉。

在那个工作流中,没有任何环节是传统意义上的 Bug。智能体没有产生幻觉,没有选错账户,也没有算错退款金额。它只是完全不知道凌晨 3 点是一个告知别人资金变动的糟糕时间。这个模型读过的人类睡眠习惯相关文本比世界上任何人都多,但它的行为表现仍然像是在对待一个随时待命的服务端点,只要你调用它,它就是清醒的。

那些由于模型选择了不同的 Token 而无法复现的 Bug

· 阅读需 11 分钟
Tian Pan
Software Engineer

用户提交了一个 bug。你的智能体生成的摘要掉了一段关键内容,或者 JSON 返回格式错误,或者回答一本正经地胡说八道。你打开工单,复制请求,然后重放(replay)。结果正常。你又重放了一次。依然正常。于是你把工单标记为“无法复现”并继续处理其他事情。

Bug 依然在那儿。真实用户依然在遇到它。你之所以关闭工单,是因为你的调试工具链默认了固定的输入会产生固定的输出——而你正在调试的组件实际上是从概率分布中进行采样的。

你从未注入过的故障:给你的 Agent 提供一个说谎的工具

· 阅读需 11 分钟
Tian Pan
Software Engineer

打开你的智能体(agent)韧性测试套件,看看它实际上在测试什么。你会发现超时。你会发现连接中断、500 错误、频率限制响应、格式错误的 JSON,也许还有一个在失败前卡死三十秒的工具。所有这些都是经典模式下的故障注入:工具坏了,问题在于你的智能体是否能优雅地降级。

现在找找看那个工具完全没坏的测试。那个工具在 80 毫秒内响应,返回了完全符合 schema 的有效 JSON,但里面的值纯粹是错的。一个过期了三天的余额。一个交换了两个字段的客户记录。一个两位数移位的订单数量。一个本应返回四十行却返回空的查询结果列表。

你找不到它。几乎没有人注入过这种故障。而这正是你的智能体最无法抵御的故障,因为所有其他故障都会自我宣告,而这种故障不会。

点对了按钮但点错了屏幕的 GUI Agent

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个计算机使用智能体拍摄了一张截图,对其进行推理,决定点击像素点 (840, 612) 处的“确认”按钮,并发送了点击指令。当光标落下时,一个弹窗出现了。三秒钟前还是“确认”的像素点,现在变成了“删除”。该智能体完全按照计划执行了操作。但它的计划是针对一个已不再存在的屏幕制定的。

这不是定位(grounding)错误。模型正确识别了按钮。这也不是推理错误,计划本身是合理的。这是一个时序错误(timing error)——这是 GUI 自动化中监测最不足的失败类别——而你的测试套件几乎肯定没有覆盖它,因为你的测试环境在观察和行动之间从未发生过变化。

一个令人不安的测量结果:最近一项针对真实 Ubuntu 工作负载下的桌面智能体的研究发现,从智能体观察屏幕到基于该观察采取行动之间,平均存在 6.51 秒 的间隔。对于 UI 来说,6.5 秒是漫长的永恒。通知会弹出,懒加载列表完成加载,动画趋于稳定,焦点发生转移。智能体对屏幕的心理模型是有保质期的,但几乎没有智能体框架会这样对待它。