跳到主要内容

智能体灾难恢复:当工作记忆随区域一同失效时

· 阅读需 14 分钟
Tian Pan
Software Engineer

你团队每季度演练的灾备 (DR) 操作手册是为了一套你已经不再完全运行的技术栈编写的。手册上写着:提升从库、重新指向 DNS、清空队列。它假设状态存储在数据库、队列和对象存储中 —— 这些是 SRE 团队已经管理、命名并测试了十年的地方。接着在上个季度,你上线了一个智能体 (agent)。现在,工作内存存在于推理提供商的会话缓存中、工作节点本地磁盘上的草稿文件里、尚未回写的在途工具调用结果中,以及仅存在于单次模型调用提示词历史中的部分“计划-执行”轨迹 (trace) 里。这些都不在资产登记簿上,也不在操作手册里。

当区域宕机时,智能体并不会干净利落地失败,而是处于一种“半完成”的状态。用户看到工作流已经开始,但故障转移后的区域无法恢复进度;客户收到了两次账单,或者根本没收到,因为幂等键存在于已经失效的工作节点上;值班工程师读着 Slack 频道里的讨论,开头是“编排器已启动,但是...”,六小时后以处理信用卡拒付队列告终。

这就是没人点破的鸿沟:智能体特性拥有现有灾备计划未曾描述的状态模型。如果团队还没有记录下这些状态表面,那么只需一次区域性停机,他们就能深刻体会到操作手册的缺失所带来的代价。

你的操作手册未涵盖的状态表面

按类别梳理智能体的状态究竟存在于何处,并检查你的 DR 操作手册中哪些条目涵盖了它们。

第一个表面是推理提供商中的工作内存。现代提供商会缓存提示词前缀、工具模式 (schemas) 和会话状态,以降低输入 Token 的成本。长期运行的智能体依赖于这些缓存;一些提供商甚至提供会话亲和性 (session affinity) 标头,以便具有相同标识符的请求路由到同一个模型实例。当智能体绑定的区域失效时,缓存状态就会丢失。故障转移区域虽然可以提供服务,但它是“冷启动”的 —— 任何假设对话具有连续性的智能体逻辑,现在面对的都是一个“陌生人”。

第二个表面是工作节点的本地磁盘。智能体会写入草稿文件来记录“计划-执行”轨迹、放不下上下文的中间工具输出,以及刚下载准备上传到别处的产物。本地磁盘比持久化存储快,且比将每个产物都存入 S3 便宜。但它也会在工作节点失效的瞬间消失,故障转移区域无法读取它。

第三个表面是在途的工具结果。智能体在三分钟前调用了一个支付 API,工具返回了结果,结果传给了模型,模型决定了下一步操作 —— 但那个决定以及它所依赖的工具结果仅存在于一个开启的推理调用中。模型返回了,但接收返回结果的工作节点已经失效。结果从未被持久化,故障转移后的工作节点根本不知道支付已经执行过。

第四个表面是部分“计划-执行”轨迹。智能体已经执行了计划中的七个步骤中的三步。计划以文本形式存在于提示词历史中,随着轮次逐一累积。目前还没有“已完成、下一步、已决定”的结构化表示。如果另一个工作节点尝试恢复,它必须从头开始推演计划 —— 即使输入相同,不同的模型调用也可能做出与第一次不同的决定。

这些表面中的每一个对 SRE 团队来说都是不可见的,因为每一个都被视为智能体运行时的实现细节,而不是独立的状态服务。灾备计划无法对它无法命名的东西进行故障转移。

任务级幂等,而非请求级幂等

大多数服务框架提供请求级幂等:一个带有 Key 的 HTTP 请求进入,处理器运行,结果被缓存,重试时返回相同的响应。这种原语无法延伸到智能体,因为智能体的“任务”不是一个请求。它是一系列模型调用和工具调用,可能持续数分钟或数小时,跨越多次重试,并在过程中产生多个步骤的副作用。

必须建立的规范是:幂等键在任务创建时生成,而不是在智能体内部生成。当用户提交“订机票并发送行程单”时,编排器在智能体执行任何操作之前就生成一个任务 ID。智能体发出的每个工具调用都会继承该任务 ID 和一个步骤序号:task=abc, step=1, action=search_flights。支付 API 和邮件服务被配置为基于 (task_id, step_ordinal) 进行去重。如果智能体在第 3 步后失效,故障转移区域在第 3 步接手任务,去重键是相同的 —— 第二次尝试要么不执行操作,要么返回缓存的结果。

这听起来很简单,原则上也确实如此。陷阱在于,开发者上线的智能体其幂等键是在智能体的“计划-执行”循环内部生成的 —— 可能是从模型输出中采样、从当前提示词哈希得来,或者是根据调用的墙钟时间衍生。这三种方式在故障转移时都会失效,因为恢复的任务会产生不同的 Key。团队只有在客户被收了两次费时才会发现这个问题。

修复方法是将任务 ID 和步骤序号视为编排器拥有的原语,智能体必须使用但不能自行生成。编排器在调用之前为每个工具位 (slot) 分配一个 Key。如果智能体在调用中途崩溃,恢复者会重复使用相同的 Key。下游 API 强制执行去重。客户只会被收一次费。

在每次工具调用前进行 Checkpoint,而非会话结束时

朴素的 Checkpoint 节奏是“在会话结束时保存状态”。这是聊天类产品使用的节奏,它对聊天有效,因为状态只是没有外部副作用的对话记录。但它对 Agent(智能体)无效,因为 Agent 在会话中期会产生不可逆的副作用——支付、发送邮件、数据库写入、开启工单——而两个副作用之间的状态才是你真正需要恢复的状态。

正确的节奏是在每次工具调用之前编写持久化 Checkpoint。在 Agent 调用工具之前,编排器(orchestrator)会持久化:当前计划、提示词前缀、工具名称、参数、幂等键(idempotency key)和步骤序号(step ordinal)。如果 Worker 在任何时间点宕机——调用前、调用中、调用后但在处理结果前——故障转移(failover)后的 Worker 就有足够的状态来恢复运行;或者,如果副作用的状态未知,可以通过使用幂等键查询下游 API 来进行对账。

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates