跳到主要内容

238 篇博文 含有标签「reliability」

查看所有标签

那个基于已被你的上下文剪枝器丢弃的事实进行分支的智能体计划

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个运行时间较长的 Agent 在第 3 步生成了一个计划。计划的内容大致是:“如果第 1 步中 get_order 返回的订单状态为 shipped,则向客户发送一封物流追踪邮件;否则开启退款工单。”Agent 自信地选择了邮件分支。但客户从未收到追踪号码,因为订单实际上处于 pending 状态。你查看 Trace,期望能发现幻觉。但你发现的情况更糟:第 1 步的工具结果已经不在上下文中了。Pruner 在第 2 步和第 3 步之间将其剔除了——因为它在最近性排名中较低,而且为了给 12KB 的对话记录腾出空间。计划仍在运行。分支仍被选中。现在的决策指向了一个根本不存在的证据。

这在通常意义上并不是模型失败。模型生成了语法正确的计划,按顺序执行,并做出了分支决策。分支是基于一个曾经在上下文中但现在已不在其中的事实做出的。思维链编码了条件(if status == "shipped");而实际的状态在传递到需要它的步骤时被丢弃了。计划看起来是确定性的,但它已经被悄悄地从证据中剥离了。

那个在行动已发出后才生效的预算上限

· 阅读需 10 分钟
Tian Pan
Software Engineer

一个超级用户在第三天早上 9 点就耗尽了你的月度 token 预算。熔断机制(kill-switch)准确触发——网关返回 429,模型停止调用,账单停止增长。与此同时,智能体(agent)已经订好了机票,发送了确认邮件,并将支持工单标记为已解决。仪表盘显示“支出已停止”。用户却问:“为什么要为我从未要求的行程扣费?”两者都没错。预算上限阻止了模型思考,但它没能阻止世界的改变。

这是几乎每个智能体预算护栏都会携带的失效模式:上限信号位于 支出 平面,但损害发生在 行动 平面,而这两个平面在连接时并没有共享的事务边界。告诉模型停止并不等同于告诉世界撤销模型刚刚所做的一切。

确定性种子:为什么供应商将其视为“提示”而非“契约”

· 阅读需 12 分钟
Tian Pan
Software Engineer

CI 测试只有一个断言:相同的模型、相同的温度、相同的提示词、相同的种子(seed)、相同的输出字符串。它在每个开发者的笔记本电脑上都通过了,在前一百次 CI 运行中也通过了,但在三周内每五十次运行就会出现一次随机失败(flake),直到最后有人承认这种模式是真实存在的。第一个假设是显而易见的——测试工具中某处存在非确定性依赖——三天的调查却一无所获。实际原因隐藏在供应商 API 参考文档的一个脚注中:“seed 提供尽力而为的确定性。”团队读到了参数名称,并将其视为一种契约。而供应商记录的只是一个提示。

这是托管推理的一种特定失败模式,它困扰着那些围绕单一心智模型设计测试基础设施的团队:模型是其输入的纯函数,而 seed 是使函数具有可重现性的关键。在生产环境中,这个模型的这两个部分都是错误的。API 表面与底层物理原理之间的差距如此之大,以至于团队在供应商明确否认的假设之上,构建了整个评估和回归测试栈。

重新路由回智能体的升级路径

· 阅读需 11 分钟
Tian Pan
Software Engineer

升级工具曾是最后一道安全网。当智能体的置信度降至阈值以下时,它会调用 escalate_to_human,随后请求滑入工单队列,并向用户礼貌地回复“专家稍后会跟进”。工程团队在发布清单上完成了闭环确认。值班表上也列出了负责接收的人员。

六个月后,一次审计对该路径进行了回溯。升级工具打开了一个 Zendesk 工单。Zendesk 队列由客服团队为了维持 SLA 响应时间而设立的预审智能体进行分拣。预审智能体由于找不到可以直接解决的政策匹配,调用了自己的 delegate_to_specialist 工具——该工具将案例路由给了一个专家智能体。而专家智能体在不确定时,又调用了 escalate_to_human。追踪轨迹形成了一个闭路。审计抽样调查的升级案例中,没有任何人类接触过。发布文档中描述的人机协作(human-in-the-loop)实际上并不存在。

升级接口并没有失败。它的每一次跳转都得到了执行。失败的是“接收系统是自然人”这一假设。

通过了 Schema 验证的虚假工具参数

· 阅读需 9 分钟
Tian Pan
Software Engineer

Agent 调用 fetch_order,参数为 order_id: "ORD-739241"。Schema 接受了它 —— 三个字母、一个连字符、六位数字,完全符合模式。工具返回了 404。Agent 开始含糊其辞,生成了 "ORD-739242" 并再次调用,又得到一个 404,接着又生成了 "ORD-739243"。你的仪表盘记录了三次成功的工具调用和三次干净的 Schema 校验。客户在等待。在追踪记录的某个地方,安全栈的每一层都报告为绿色,而模型正在全速虚构标识符。

团队认为 Schema 拦截了错误。Schema 确实拦截了它能拦截的东西:形状(shape)。它检查了参数是否为字符串,是否匹配正则表达式,以及必填字段是否存在。Schema 无法检查 ORD-739241 是否对应数据库中的真实订单,因为 Schema 根本不知道数据库的存在。这种差距 —— 句法上的合理性与语义上的正确性之间 —— 正是大多数生产环境工具调用 bug 的所在地,而且这种失败非常隐蔽,唯一的信号就是客户的困惑。

带有延迟预算的紧急开关:你的故障处理从未达到的标准

· 阅读需 13 分钟
Tian Pan
Software Engineer

运维手册上写着“禁用代理”。值班人员照做了。43 分钟后,当紧急开关终于通过配置服务传播开来时,该代理已经提交了 1,200 张错误的工单,调用了 8,000 次计费 API,并向根本没有订阅任何服务的客户发送了邮件。运维手册是正确的,但它也是徒劳的,因为没有人衡量过当代理每秒钟都在造成破坏时,“禁用代理”实际上需要多长时间。

!["https://opengraph-image.blockeden.xyz/api/og-tianpan-co?title=%E5%B8%A6%E6%9C%89%E5%BB%B6%E8%BF%9F%E9%A2%84%E7%AE%97%E7%9A%84%E7%B4%A7%E6%80%82%E5%BC%80%E5%85%B3%EF%BC%9A%E4%BD%A0%E7%9A%84%E6%95%85%E9%9A%9C%E5%A4%84%E7%90%86%E4%BB%8E%E6%9C%AA%E8%BE%BE%E6%A0%87"]

大多数 AI 功能都配有紧急开关,就像大多数建筑都配有灭火器一样:有人签字确认它的存在,却没人计时到达它需要多久。合规审查会问“是否有紧急开关?”,答案是肯定的。而故障现场会问“止血有多快?”,答案则取决于底层管道恰好需要的时间——团队中从未有人针对该功能造成破坏的速度测量过这个数字。

这种不匹配正是问题的核心。一个遏制时间长于其破坏扩散时间的功能,交付的只是“遏制剧场”(Containment Theater)。

你的智能体平台忘了配置的值班轮换

· 阅读需 12 分钟
Tian Pan
Software Engineer

AI 平台团队有四名工程师。他们七个月前发布的内部 Agent 现在每天为 200 名员工回答问题。在第一个月,创始工程师亲自处理了每一个 Slack 消息——周二晚上 11 点,周日上午,甚至公司团建的晚上。随后,她因推动采用率方面的贡献而晋升为资深工程师(Staff Engineer),三周后,她在下午 6 点后就不再查看频道了,因为那是资深工程师该做的。原本打算取代她的值班轮换(on-call rotation)从未正式化,因为运营模式总是打算在“试点之后”再解决。

当 Agent 针对四分之一的用户静默降级的那天——一个悄然落后的检索索引,或者改变了拒绝行为的模型版本切换,或者是架构轮换后现在返回空数组的工具——投诉并不会降临到平台团队的传呼机(pager)上。它们落在帮助台队列中,由那些无法访问 Agent 追踪(traces)、不知道什么是系统提示词(system prompt)、并且被 IT 部门告知该 Agent “由 AI 团队负责”的人员处理。从第一个用户投诉到第一位工程师查看 trace,中间过去了 16 个小时。平台团队没有人玩忽职守;只是根本就没有人在执勤。

提供商故障转移:在对话中途替换了你安全策略的隐忧

· 阅读需 12 分钟
Tian Pan
Software Engineer

用户正与你的助手进行一场关于受控物质处方模式的谨慎对话,已经进行了十二轮。模型表现得很有分寸,提出澄清性问题,引用指南,并拒绝进行文献之外的推演。在第十三轮,用户提出了一个后续问题,按理说应该得到与前十二轮相同的回应。然而,他们得到的却是一个生硬的拒绝:“我无法提供相关帮助。”对话结束了。他们怒气冲冲地给支持团队写信——他们并没有问任何不同的内容,助手刚才还在帮助他们,到底发生了什么变化。

你的日志解释了变化的原因。在第十三轮进行到一半时,你的主供应商在流式传输过程中返回了 503 错误。你的网关按照配置执行了操作:在请求的剩余部分故障转移(failover)到了备用供应商。备用供应商对该类查询的拒绝阈值校准得比主供应商更保守。用户并没有问任何不同的问题——他们在同一个品牌下对不同的模型提出了相同的问题,而新模型说了“不”。

智能体学会针对重试预算进行规划

· 阅读需 11 分钟
Tian Pan
Software Engineer

在生产环境中运行智能体(agent)得出的最令人不安的教训不是它们会失败——而是它们会学习。并不是指任何深度意义上的学习;权重并没有改变。但在一个会话(session)中,在一个轨迹(trajectory)中,模型所隐含的策略会根据其运行的底层环境(substrate)进行调整。如果你的底层环境代表智能体悄悄吸收了失败,智能体最终会察觉到这一点,并开始将其视为免费的算力进行规划。

最明显的例子就是重试层(retry layer)。你添加它是为了可靠性——在报错之前,SDK 会对失败的工具调用进行三次重试;你的中间件为每一步包装了指数退避(exponential backoff);你的循环捕获了格式错误的 JSON 并重新提示模型进行修复。这些都没错。但每一个机制都是智能体可以观察、概括并利用的副作用。一旦它这样做了,你的可靠性层就不再是安全网,而成了规划原语(planning primitive)。

流式中止后遗留的计费副作用

· 阅读需 12 分钟
Tian Pan
Software Engineer

用户正在查看你的智能体(Agent)流式传输回复。在 200 毫秒时,他们点击了停止。UI 清除了气泡,加载图标消失,产品的表现就像请求从未发生过一样。

但它确实发生了。Agent 已经调用了 send_invoice_email。供应商的邮件转发服务器返回了 250 OK。客户收到了一份用户从未批准的草案发票。你的计费系统向用户收取了中止前流式传输的 token 费用。但它无法撤回发送邮件产生的费用。

这是每个使用流式工具(streaming tool use)的团队至少都会遇到一次的失败模式,而且大多数团队甚至从未察觉。流层(stream layer)报告 cancelled。工具层(tool layer)报告 succeeded。你的面向客户的日志会根据最后刷新的子系统从中挑选一个,于是同一个请求的两个部分现在对于该请求是否发生产生了分歧。

两个模型对同一结构化输出 Schema 的不同理解

· 阅读需 10 分钟
Tian Pan
Software Engineer

当你的备用路由(fallback route)第一次在生产环境中触发时,绝不是发现两个供应商对你的 schema 定义存在分歧的好时机。在两个客户端配置中,JSON Schema 看起来完全一样。验证器对两个输出都通过了。下游代码按名称读取字段并获取一个值。接着,账单总额以数字字符串而非整数的形式出现,或者长度为一的列表以纯对象而非单元素数组的形式到达,一段已经正常运行了六个月的代码路径会静默地返回错误答案。

结构化输出引人入胜之处在于它消除了一类错误——无法解析的 JSON、幻觉字段、缺失的键——因此让人感觉它彻底解决了解析问题。实际上它所做的是将解析问题向上移动了一层,从词法分析器(lexer)移到了类型系统,在那里问题变得更难被察觉。两个供应商可以都遵循 JSON Schema,但仍然产生不可互换的输出,因为在这个生态系统的角落里,“遵循”至少有四种不同的含义,而你的 schema 并没有指明你想要哪一种。

第四方风险:当供应商的供应商掌控了你客户的故障

· 阅读需 13 分钟
Tian Pan
Software Engineer

你与模型提供商签订了合同。你的运行手册(runbook)处理了该提供商降级的情况。当他们的仪表板变黄时,你的状态页订阅会向你发送告警。你觉得万无一失。然后,在某个周三下午,你提供商运行的基础云区域开始出现局部降级,你提供商的故障转移区域也受到了影响,因为他们为了控制单位经济效益而整合了容量。由于签署合同时上游两层的供应商决策,你的产品在 90 分钟内处于半瘫痪状态。

第二天早上,客户的事后分析(postmortem)请求出现在你的收件箱里。他们想要找到根本原因。根本原因存在于你的状态页无法看到的层级,也是你的合同无法约束的层级。这一层级正是所谓的第四方风险——它不是一个采购复选框,而是一个无形的依赖层,它会向上层传导故障,只会衰减而不会被吸收。