1. 无状态是 Serverless 的立业之本,却恰恰是 Agent 的命门所在
先说一个我在生产环境里反复撞见的矛盾:Serverless 平台天然要求函数无状态——请求进来、代码执行、结果返回、实例销毁,整个生命周期里不允许你“记住”任何东西;而 AI Agent 恰恰每一步都在“记住”:对话上下文、工具调用的中间产物、工作目录里的临时文件、会话级的许可证或 Token。这两件事放在一起,几乎是从底层逻辑上互相打架的。
大多数刚接触 Agent 工程化的朋友,第一反应是“那我把上下文塞进 Redis 不就行了”。这个思路对了一半:外部状态可以外置,但 Agent 运行时的环境状态是外置不了的。举个例子,Agent 在沙箱里执行一个 Python 脚本,这个脚本写入了 /workspace/cache/model.bin,下一次工具调用又要读这个文件。如果 Serverless 平台把你的函数实例回收了,这个文件就没了,外置 Redis 也救不回来——因为写入发生在沙箱文件系统内部,而不是你的业务代码里。真实项目里,类似的问题还出现在子进程残留、环境变量修改、临时端口占用、SSH 密钥缓存等各个层面。这也是为什么现在社区里讨论 Agent 沙箱时,总有人提到“Codex 沙箱启动的时候读文件失败,错误一直是 apply deny-read ACL”——这类问题的根源,就是沙箱生命周期和 Agent 会话生命周期没有对齐。
AgentRun 这个项目解决的核心问题,正是这个错位。它不是去改 Serverless 的本质——函数依然是无状态的,而是加了一个专门承载 Agent 运行态的调度层,让“无状态的计算资源”和“有状态的 Agent 会话”之间有一层可以谈判、可以持久化、可以恢复的中间层。说得直白一点:Serverless 说“我记不住任何东西”,Agent 说“我必须记住所有东西”,AgentRun 说“行,我来帮你记,而且在你忘记之前先把现场保存下来”。
所以说,这篇内容适合的人不是“想学 Agent API 怎么调用”的新手,而是已经跑通了一个 Agent Demo、现在想把它部署到生产环境、却发现 Serverless 平台各种别扭的开发者。你不需要重新设计算法,也不需要研究模型推理,你需要的是搞清楚:Agent 的工作目录归谁管、子进程死了算谁的、Session 断线以后现场怎么恢复、沙箱逃逸怎么防。这些才是 Agent 工程化真正的深水区。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Agent 和普通后端服务的本质差异:为什么传统的弹性伸缩模型失效了
2.1 普通 Web 服务是“一次性计算”,Agent 是“一个连续操作的时间轴”
一个普通的 Serverless 函数,它的生命周期是从事件触发到响应返回,通常几百毫秒到几秒。它的状态极简:入参、出参、日志。即使平台把实例回收,也不影响业务连续性,因为下一次请求可以由一个全新的、干净的实例来处理。这种模型优雅、可靠、易扩展,是整个云原生体系的基石。
但 Agent 不一样。一个 Agent 执行一次任务,往往要经历这样的循环:接收用户指令 → 拆解子任务 → 调用工具 → 观察结果 → 调整计划 → 再次调用工具。这个过程可能持续几分钟、几十分钟,甚至跨小时。任务过程中,Agent 的“大脑”里有一个逐步演进的计划(plan),手里有一堆工具调用的结果缓存,工作目录里散落着下载的附件、生成的中间文件、缓存的认证凭据。这些内容组合在一起,就是 Agent 的“现场”。
如果这个“现场”因为平台缩容被强制回收,会出现什么结果?轻则任务中断、用户需要从头再来;重则 Agent 已经执行了一部分副作用操作——比如已经写入了数据库、已经调用了外部 API——但任务状态丢失,用户不知道哪些操作生效了哪些没生效,这是比“任务失败”严重得多的问题。普通服务的“无状态弹性”在这套逻辑下不仅不解决问题,反而成了隐患制造机。
2.2 为什么简单延长超时时间解决不了问题
一些人会想:那把 Serverless 函数的超时时间调到最大、内存调到最大,总行了吧?在实际项目中,这条路走不通有三个原因。
第一,Serverless 平台出于资源调度效率考虑,对单实例最长运行时间有硬性上限,常见的在 15 分钟到 1 小时之间。但真实的 Agent 任务里,超过 1 小时的并不少见——尤其是涉及多轮网页操作、长文档生成、多步数据处理的场景。你的任务刚干到一半,平台强制回收,前功尽弃。
第二,单实例的内存和 CPU 配额是固定的。Agent 任务中某个阶段会密集调用推理接口、某个阶段会大量解析 PDF、某个阶段又可能只是长时间等待外部服务响应。固定的配额要么在高峰期不够用,要么在低谷期白白浪费。这和“弹性”的目标背道而驰。
第三,也是最容易被忽略的:Serverless 平台为了安全隔离,通常在实例之间做了严格的网络和文件系统隔离。多个 Agent 任务并发时,你没法让它们共享一个工作目录,也没法让它们访问同一个本地服务。每个实例都是信息孤岛,而这恰恰是 Agent 协同推理、共享工具缓存这类能力的天敌。
AgentRun 看到的正是这个缺口。它没有试图让函数“变得有状态”,而是把 Agent 会话本身变成一个一等公民——一个可以被调度、迁移、恢复的独立实体。函数仍然是函数,但它的生命周期不再等同于 Agent 的生命周期。
2.3 Agent 运行时到底需要保存哪些状态
在具体设计 AgentRun 之前,先把“状态”论清楚。一个 Agent 会话里,按状态的生命周期和重要性,大致可以分成四类:
| 状态类别 | 内容示例 | 生命周期 | 丢失后果 |
|---|---|---|---|
| 会话上下文 | 用户对话历史、意图分析、任务计划 | 整个任务周期 | 任务无法继续 |
| 工作区文件 | 下载的附件、生成的中间文件、临时脚本 | 整个任务周期 | 已执行工作丢失 |
| 进程与句柄 | 子进程、临时端口、套接字连接、锁文件 | 分钟级 | 可能引发资源泄漏 |
| 凭据与缓存 | API Token、会话密钥、模型推理缓存 | 小时到天级 | 需重新认证 |
其中最麻烦的是第三类“进程与句柄”。普通状态存到 Redis 就能解决,但子进程和端口不是“数据”,它们是操作系统级别的资源。你在沙箱里起了一个长期运行的 Python HTTP 服务,如果沙箱被回收,这个服务就没了;用户拿到的是一个 502。要恢复这个状态,不只是“重新拉一份数据”,而是要“重新拉起一个环境”,这就回到了沙箱工程化的核心。
3. AgentRun 的破局方案:一个承载完整 Agent 运行时的调度层
3.1 AgentRun 的设计目标与架构定位
AgentRun 的定位不是一个 Age
