无共享智能体:为水平可扩展性设计 AI 智能体
你的负载均衡器将一个传入的智能体请求分配给副本 3。但用户的对话历史存储在副本 7 的内存中。副本 3 完全不知道过去六轮发生了什么,于是它从头开始,让用户一头雾水,你的值班工程师在凌晨 2 点被叫醒。你启用了会话粘滞。现在该用户的所有请求永远路由到副本 7。你用一个正确性问题换来了一个可扩展性天花板。
就在这一刻,团队意识到:AI 智能体的"水平扩展"和 Web 服务器的水平扩展根本不是同一个问题。修复方式不同,而那些看似直接的路径会以可预见的方式失败。
根本原因是隐式状态。大多数智能体实现将上下文积累在进程内部——对话历史、临时文件、中间工具输出、检查点数据——从未考虑当该进程消失或第二个进程尝试处理同一用户时会发生什么。这对原型来说没问题,在生产环境中却会严重失效。
解决方案来自分布式系统,而非 AI 研究:无共享架构原则。将每个智能体副本设计为对先前工作没有任何记忆。通过外部系统引入所有所需上下文,并将所有输出写回这些系统。让每个操作都可以安全重试。坚持这样做,你就能实现真正的水平可扩展性——添加副本、分发负载、优雅处理故障。本文将介绍如何实现这一目标。
为什么智能体会积累状态(以及为什么这很重要)
LLM 在设计上是无状态的。每次推理调用接收一个完整的提示并生成响应,模型内部没有隐藏的会话对象。这就是 LLM 提供商能够服务数百万用户的原因:每个请求都是独立的。
智能体打破了这一特性。智能体框架用控制循环包裹 LLM 调用,维护以下内容:
- 对话历史:提供上下文所需的轮次序列
- 工具输出:用于指导当前推理的先前调用结果
- 工作文件:执行过程中写入的文档、日志、数据文件
- 检查点数据:循环中断时恢复所需的足够状态
当这些数据存活在进程内存或本地文件系统中,你就创建了一个有状态的进程。添加第二个副本没有帮助——它无法访问第一个副本的内存。你需要会话粘滞,这意味着一个副本处理来自特定用户或会话的所有请求。你以运维麻烦换来了毫无意义的扩展。
随着智能体能力增强,问题会进一步恶化。一个处理 20 步研究任务的智能体可能积累数百 KB 的中间上下文。运行了 90 分钟的智能体积累了工具输出、部分结果和推理步骤,这些东西只存在于运行它的那台机器上。当那台机器重 启,一切都丢失了。
无共享原则在智能体上的应用
无共享是一种分布式系统架构,其中每个节点在不假设共享状态的情况下运行。节点不共享内存,不共享本地磁盘,只通过显式消息传递——API、队列、数据库——进行协调。经典实现是 Web 请求:请求到达池中的任意服务器,该服务器从数据库获取所需内容,处理请求,写回结果,然后返回。下一个请求可以去任意服务器。
对于智能体,等价原则是:每次智能体调用必须能在任何可用副本上运行。这需要三件事:
所有上下文必须存在于进程之外。 对话历史、工具结果和检查点数据存入外部存储——Redis 用于快速读取,数据库用于持久性,对象存储用于大型工件。智能体在每轮开始时获取所需内容,在结束时写回结果。
上下文重建必须可从元数据实现。 你不应该需要重放整个历史来从中间任务恢复。存储每个步骤的元数据——发生了什么、做出了什么决定、指向存储工件的指针——这样新副本可以在毫秒内重建足够的上下文。
所有工具调用必须可以安全重试。 当工具调用中途失败,或当智能体从检查点重启时,它可能会再次调用相同的工具。非幂等的工具在重试时会损坏状态。
这三个要求是独立的,每个都可以单独解决。但你需要全部三个才能实现完整的水平可扩展性。
