代理控制程序应置于沙箱之外作者Andrea Luzzardi发布时间2026 年 4 月 10 日阅读时长7 分钟代理控制程序是驱动大语言模型LLM的循环机制。它发送提示、获取响应、执行模型请求的工具调用、反馈执行结果然后重复这一过程直到模型表示任务完成。每个生产环境中的代理都有这样一个控制程序。问题来了它应该在哪里运行呢答案有俩。不同的运行位置有着不同的安全特性、不同的故障模式对代理的功能也有不同影响。这些权衡在构建单用户代理比如在笔记本电脑上工作的一名工程师使用的代理和多用户代理同一组织内数十名工程师共享的代理时也不一样。我们属于多用户代理阵营这会带来单用户代理构建者不会遇到的问题。两种架构控制程序置于沙箱内控制循环和它所操作的代码处于同一个容器中。LLM 调用从容器内部发起。工具调用如 bash 命令、文件读写在本地执行。控制程序跟踪的技能、记忆等信息都以文件形式存储在容器的文件系统中。这就跟你在笔记本电脑上运行 claude或者在远程容器中启动 Claude Code 时的情况类似。如果你正在构建单用户代理可以使用 Claude Code SDK 来实现可用的功能。控制程序置于沙箱外控制循环在后端运行。当需要执行工具时它通过 API 调用沙箱。沙箱执行工具并返回结果而控制循环本身不会进入沙箱。权衡比较将控制程序置于沙箱内有一些优点。执行模型很简单一个容器、一个进程树、一个文件系统生命周期统一。可以直接复用现有的控制程序。技能和记忆功能无需修改因为它们假定使用本地文件系统而沙箱内正好提供了这样的环境。将控制程序置于沙箱外则能实现一些置于沙箱内无法做到的事情。凭证不会进入沙箱。控制循环持有 LLM API 密钥、用户令牌和数据库访问权限。沙箱中仅包含代理完成工作所需的环境。这样代理就没有可逃逸的目标也就无需实施权限模型也不用担心凭证泄露的问题。可以在代理不使用沙箱时将其挂起。代理的很多操作根本不需要沙箱比如思考、调用 API、总结信息、等待持续集成CI结果等。有些会话甚至根本不会涉及沙箱。当控制程序在沙箱外时只有在代理需要运行命令时才会分配沙箱空闲时则将其挂起。而当控制程序在沙箱内时就无法这样操作因为不能挂起控制循环所在的容器。沙箱可以像“牛群”一样管理。如果在会话期间某个沙箱出现故障控制循环可以分配一个新的沙箱并继续运行。而当控制程序在沙箱内时沙箱就是会话本身沙箱故障意味着会话丢失。而且多用户使用时不再是分布式文件系统的问题。同一组织内的多名工程师使用同一个代理他们共享技能和记忆有时还会并行处理同一事件。当控制程序在沙箱外时这些共享信息可以存储在共享数据库中。而当控制程序在沙箱内时就会面临分布式文件系统的问题后面会详细讨论。不过一旦将控制循环移到沙箱外现有的本地控制程序就无法使用了因为它们都假定使用本地文件系统。持久化执行也成了一个问题因为代理会话可能会持续数小时并且需要在部署更新时保持运行。而且当控制程序和沙箱位于不同的机器上时“文件系统”就不再是一个直观的概念了。我们选择了将控制程序置于沙箱外的架构。接下来将介绍为了实现这一架构需要解决的三个问题。持久化执行代理循环是一个长时间运行的函数最短也要运行几分钟在我们的场景中可能会持续数小时。它必须能够在滚动部署、规模调整和实例故障等情况下保持运行。如果将循环保存在 API 服务器的内存中那么在发布新版本时就会中断。我们已经在 [Inngest](https://www.inngest.com) 上运行 CI 数据摄取管道在之前的一篇文章 [《大语言模型擅长处理 SQL》](/blog/llms-are-good-at-sql) 中介绍过。出于同样的原因我们决定将其扩展到代理循环良好的开发者体验DX、无需自行管理集群而且我们不需要像 Temporal 那样强大的通用性。代理循环是一个 Inngest 函数每一轮交互都是一个步骤Inngest 会对每个步骤进行检查点记录。如果服务器重启循环会从上次中断的地方继续运行。沙箱生命周期管理控制循环大部分时间处于挂起状态比如在进行 LLM 调用、工具调用之间或者等待像 CI 这样的长时间工作流时。我们希望沙箱也能在这些时候挂起只在代理运行命令时处于活跃状态。但问题是冷启动。一个冷启动的沙箱需要几秒钟才能启动在交互式操作中这可是很长的时间。我们使用 [Blaxel](https://blaxel.ai) 来解决这个问题。Blaxel 可以让沙箱从待机状态恢复只需 25 毫秒。我们在代理不运行命令时挂起沙箱一旦需要就立即恢复。25 毫秒的恢复时间足够短代理几乎感觉不到沙箱曾经被挂起过。文件系统问题现代的代理控制程序不仅仅是 bash 命令和 LLM 的组合。它们还包含技能代理按需读取的提示片段、记忆代理为自己或用户记录的笔记、子代理、计划、待办事项列表等。所有这些都假定使用本地文件系统。例如技能是 .claude/skills/foo.md 文件记忆是 .claude/memory/MEMORY.md 文件。控制程序使用与处理源代码相同的 read 和 write 工具来读写这些文件。这种方式在笔记本电脑上可行但当控制程序位于沙箱外时就会出现问题。沙箱是可丢弃的。我们将其视为临时资源可以挂起、恢复、终止和重新创建。如果沙箱出现故障并重新创建代理写入 .claude/memory/MEMORY.md 的内容就会丢失。你可以为每个会话保留一个长期运行的沙箱来保存状态但这样就又回到了为每个会话单独管理沙箱的问题而且会失去其他期望的特性。另一个问题是多用户使用。用户的笔记本电脑上运行的代理是为一个人服务的而我们的代理要为同一组织内的数十名工程师服务。技能是组织层面的团队中的每个人都共享相同的故障排查手册记忆也是如此。如果代理在周一了解到 X 团队总是从发布分支进行部署那么周二同一团队的另一名工程师使用代理时也应该知道这一点。你可以假装沙箱有本地文件系统将数据写入其中然后在会话结束时将所有内容同步到数据库。这种方法在单用户场景下可行但在多用户场景下就相当于构建了一个分布式文件系统。两个同时运行的会话可能会写入同一个记忆文件这时就需要进行冲突解决。三名工程师针对同一事件触发代理在会话结束之前他们可能都会看到过时的状态。这就涉及到冲突解决、最终一致性和缓存失效等问题。一个更简洁的解决方案是不再假装使用本地文件系统而是将记忆和技能存储在数据库中。控制程序在代理请求时从数据库中读取这些信息并在代理更新时将其写回数据库。但我们仍然希望代理能够以文件的方式进行操作。统一接口双后端实现控制程序对文件系统访问进行虚拟化。代理有一个 read 工具、一个 write 工具和一个 edit 工具。当代理调用这些工具时控制程序会根据路径来判断调用的目标。工作区路径下的操作会像以往一样发送到沙箱。技能和记忆命名空间下的路径则会指向数据库。对记忆路径的写入操作会作为一个数据库事务执行并且作用于整个组织范围。对记忆路径的读取操作也从数据库获取这样同一组织内的两个并行会话在写入记忆后能立即看到相同的内容。代理并不知道其中的区别。在它看来就是存在一个文件系统可以进行文件的读写操作。有些文件存储在 Postgres 数据库中有些则存储在千里之外的沙箱中。为何不直接添加工具一个明显的替代方案是为代理添加 memory_read 和 memory_write 工具与 read 和 write 工具并存。这种方法可行而且很多人也是这么做的。在实现虚拟化层之前我们也是这样做的。但问题是工具越多代理的性能就越差。每个工具都会分散模型对其他工具的注意力使提示变长并且增加了模型在每一轮交互中需要做出的决策。read 和 memory_read 这两个功能相近的工具尤其糟糕因为模型需要从上下文中区分它们有时还会选错。另一个更重要的原因是Anthropic 等训练前沿模型的机构很可能是在类似 Claude Code 的控制程序上进行强化学习的。这种训练使模型擅长特定的 API 接口如 read(path)、write(path, content)、edit(path, old, new)。如果你发明了 memory_read就偏离了模型训练的路径只能得到模型通用的能力而失去了它在特定训练规范上学习到的能力。虚拟化接口保留了模型训练时的 API 接口同时将数据库语义隐藏在后端。仍待解决的难题技术发展日新月异。每隔几周Claude Code 或其他类似工具就会引入新的模式如子代理、计划、后台任务而这些模式几乎都假定使用本地文件系统。我们可以拦截大部分操作但新功能发布和我们的虚拟化层正确处理之间总会存在时间差。不使用原生的 Claude Code 确实会带来一些成本。我们选择了与 Claude Code 本地布局相似的路径前缀/skills/、/memory/这可能会带来问题。Claude Code 的布局仍在变化一旦约定改变我们可能需要迁移所有数据。也许正确的做法是提供一个完全不同的接口但正如前面所说我们的初衷是保持接口与模型训练时的一致。Bash 命令是一个漏洞。控制程序可以拦截 read(/skills/foo.md) 这样的结构化工具调用但代理还有一个 bash 工具它可以在 bash 会话中运行 grep -r foo /skills/ 命令。Bash 命令会绕过虚拟化层直接访问沙箱的真实文件系统而 /skills/ 路径在那里并不存在。我们采取了两种尽力而为的防护措施在系统提示中告知代理不要在虚拟化命名空间中使用 bash 命令并使用 tree-sitter 解析 bash 命令以捕获访问这些路径的调用。但这两种方法都不是万无一失的目前只能勉强应对。一致性问题是我们还没有解决的难题。当同一组织内的两个会话同时更新记忆时它们应该看到什么样的内容呢严格的串行化似乎很有吸引力但可能并不正确因为代理不是数据库让一个会话等待另一个会话的写入操作可能会导致死锁而我们目前还没有解决方案。我们目前采用的是每个键最后写入者优先的策略这在我们遇到的场景中还能正常工作但几乎可以肯定在某些情况下会出现问题。聘请你的 AI 运维团队安装 GitHub 应用程序Mendral 即可开始对故障进行分类并自动创建包含修复方案的拉取请求PR。安装过程约需 5 分钟。开始使用Mendral你的 AI 运维团队。由专业代理组成的团队能够诊断 CI 故障、修复不稳定的测试、加快构建速度并提交拉取请求。系统状态正常产品工作原理集成方式安全保障系统状态博客公司关于我们定价策略招聘信息联系我们社交Twitter / XLinkedIn法律服务条款隐私政策© 2026 Mendral, Inc.SOC 2 Type II