概念
现代智能体在能够对文件系统中的真实文件进行操作时效果最佳。Sandbox Agents 可以使用专门的工具和 shell 命令,对大型文档集进行搜索与操作、编辑文件、生成产物并运行命令。沙盒为模型提供了一个持久化工作区,智能体可以利用它代表您完成工作。Agents SDK 中的 Sandbox Agents 可帮助您运行与沙盒环境配对的智能体,让文件以合适的方式进入文件系统,并能够大规模编排沙盒以启动、停止和恢复任务。
您可以围绕智能体所需的数据来定义工作区。它可以从 GitHub 仓库、本地文件和目录、合成任务文件、远程文件系统(如 S3 或 Azure Blob Storage)以及您提供的其他沙盒输入开始。

SandboxAgent 扩展自 Agent,因此它仍然是一个 Agent。它保留了常见的智能体接口,例如 instructions、tools、handoffs、mcpServers、modelSettings、输出类型、护栏和 hooks,并且仍通过常规的 run() 和 Runner API 运行。变化之处在于执行边界:
SandboxAgent定义智能体本身:除了常规智能体配置外,还包括沙盒特有的默认值,如defaultManifest、baseInstructions、runAs,以及文件系统工具、shell 访问、技能、记忆或压缩等能力。Manifest声明全新沙盒工作区期望的初始内容和布局,包括文件、仓库、挂载和环境。- 沙盒会话是命令运行和文件发生变化的实时执行环境。
sandbox运行选项决定本次运行如何获取该沙盒会话,例如直接注入一个会话、从序列化的沙盒会话状态重新连接,或通过沙盒客户端创建一个新的沙盒会话。- 已保存的沙盒状态和快照允许后续运行重新连接到先前的工作,或使用已保存的内容为新的沙盒会话提供初始数据。
Manifest 定义新沙盒工作区的初始内容。它并不描述每个实时沙盒中的当前文件,因为复用的会话、序列化的会话状态和快照都可能在运行时提供或更改工作区。
在本页中,“沙盒会话”指由沙盒客户端管理的实时执行环境。具体边界取决于客户端:Unix 本地会话在主机上的本地工作区中运行,而 Docker 和托管客户端提供更强的环境隔离。这与 会话 中描述的 SDK 对话式 Session 接口不同。
外层运行时仍负责审批、追踪、交接和恢复相关记录。沙盒会话负责命令、文件变更和环境隔离。这种分工是该模型的核心部分。
各部分的协作方式
Section titled “各部分的协作方式”一次沙盒运行会将智能体定义与每次运行的沙盒配置结合起来。运行器会准备智能体,将其绑定到一个实时沙盒会话,并可以为后续运行保存状态。
沙盒特有的默认值保留在 SandboxAgent 上。每次运行的沙盒会话选择保留在 sandbox 运行选项中。
可以将生命周期理解为三个阶段:
- 使用
SandboxAgent、Manifest和能力定义智能体及工作区初始内容。 - 通过为
run()或Runner提供sandbox运行选项来执行一次运行,该选项可注入、恢复或创建沙盒会话。 - 之后可基于运行器管理的
RunState、显式的沙盒sessionState或已保存的工作区快照继续。
如果 shell 访问只是偶尔使用的一种工具,请先从工具指南中的托管 shell 开始。当工作区隔离、沙盒客户端选择或沙盒会话恢复行为本身就是设计的一部分时,再使用沙盒智能体。
沙盒智能体非常适合以工作区为中心的工作流,例如:
- 编码与调试:为 GitHub 仓库中的问题报告编排自动修复,并运行有针对性的测试。
- 文档处理与编辑:从用户的财务文档中提取信息,并创建填好的税表草稿。
- 基于文件的审查或分析:在回答前检查入职材料、生成的报告或产物包。
- 隔离的多智能体模式:为每个审查者或编码子智能体提供各自独立的工作区。
- 多步骤工作区任务:在一次运行中修复 bug,稍后再添加回归测试,或从快照或沙盒会话状态恢复。
如果您不需要访问文件或活动的文件系统,请继续使用 Agent。如果 shell 访问只是偶尔需要的一项能力,可添加托管 shell;如果工作区边界本身是功能的一部分,则应使用沙盒智能体。
选择沙盒客户端
Section titled “选择沙盒客户端”本地开发时,先使用 UnixLocalSandboxClient。当您需要容器隔离或镜像一致性时,切换到 DockerSandboxClient。当您需要由提供方管理执行环境时,切换到托管提供方。
在大多数情况下,SandboxAgent 定义保持不变,只需在 sandbox 运行选项中更改沙盒客户端及其选项。有关本地、Docker、托管和远程挂载选项,请参阅沙盒客户端。
| 层 | 主要 SDK 组件 | 它回答的问题 |
|---|---|---|
| 智能体定义 | SandboxAgent、Manifest、capabilities | 将运行什么智能体,以及它应从什么样的全新会话工作区契约开始? |
| 沙盒执行 | sandbox 运行选项、沙盒客户端和实时沙盒会话 | 这次运行如何获得一个实时沙盒会话,工作又在哪里执行? |
| 已保存的沙盒状态 | RunState 沙盒负载、sessionState 和快照 | 该工作流如何重新连接到先前的沙盒工作,或从已保存内容为全新沙盒会话提供初始数据? |
主要 SDK 组件与这些层的对应关系如下:
| 组件 | 它负责什么 | 请问自己这个问题 |
|---|---|---|
SandboxAgent | 智能体定义 | 这个智能体应该做什么,哪些默认值应该随它一起携带? |
Manifest | 全新会话工作区中的文件和文件夹 | 运行开始时,文件系统中应该有哪些文件和文件夹? |
Capability | 沙盒原生行为 | 应为这个智能体附加哪些工具、指令片段或运行时行为? |
sandbox 运行选项 | 每次运行的沙盒客户端和沙盒会话来源 | 这次运行应注入、恢复还是创建一个沙盒会话? |
RunState | 由运行器管理的已保存沙盒状态 | 我是否在恢复先前由运行器管理的工作流,并自动延续其沙盒状态? |
sandbox.sessionState | 显式序列化的沙盒会话状态 | 我是否想从已在 RunState 外部序列化好的沙盒状态恢复? |
sandbox.snapshot | 用于全新沙盒会话的已保存工作区内容 | 新的沙盒会话是否应从已保存的文件和产物开始? |
一种实用的设计顺序是:
- 使用
Manifest定义全新会话工作区契约。 - 使用
SandboxAgent定义智能体。 - 添加内置或自定义能力。
- 在
run(agent, input, { sandbox: ... })或new Runner({ sandbox: ... })中决定每次运行应如何获取其沙盒会话。
沙盒运行的准备方式
Section titled “沙盒运行的准备方式”在运行时,运行器会将上述定义转化为一个具体的、由沙盒支持的运行:
- 它从
sandbox运行选项中解析沙盒会话。 - 它确定本次运行生效的工作区输入。
- 它让 capabilities 处理最终生成的 manifest。
- 它按固定顺序构建最终 instructions:SDK 默认的沙盒提示词,或在您显式覆盖时使用
baseInstructions,然后是instructions,接着是 capability 的指令片段,再接着是任何远程挂载策略文本,最后是渲染后的文件系统树。 - 它将 capability 工具绑定到实时沙盒会话,并通过常规的
run()和RunnerAPI 运行准备好的智能体。
沙盒化不会改变一次 turn 的含义。turn 仍然是模型的一步,而不是单个 shell 命令或沙盒操作。沙盒侧操作与 turn 之间并不存在固定的 1:1 映射。实际规则是,只有当沙盒工作完成后,智能体运行时还需要模型给出新的响应时,才会消耗另一个 turn。
SandboxAgent 选项
Section titled “SandboxAgent 选项”这些是在常规 Agent 字段之上的沙盒专属选项:
| 选项 | 最佳用途 |
|---|---|
defaultManifest | 由运行器创建的全新沙盒会话的默认工作区。 |
instructions | 追加在 SDK 沙盒提示词之后的额外角色、工作流和成功标准。 |
baseInstructions | 用于替换 SDK 沙盒提示词的高级兜底选项。 |
capabilities | 应随该智能体一起携带的沙盒原生工具和行为。 |
runAs | 面向模型的沙盒工具(如 shell 命令、文件读取和补丁)所使用的用户身份。 |
沙盒客户端的选择、沙盒会话复用、manifest 覆盖和快照选择都应放在 sandbox 运行选项中,而不是放在智能体上。
defaultManifest
Section titled “defaultManifest”defaultManifest 是当运行器为该智能体创建全新沙盒会话时所使用的默认 Manifest。可用它定义智能体通常应从哪些文件、仓库、辅助材料、输出目录和挂载开始。
这只是默认值。运行可以通过 sandbox.manifest 覆盖它,而复用或恢复的沙盒会话会保留其现有工作区状态。
import { file, gitRepo, Manifest } from '@openai/agents/sandbox';
const manifest = new Manifest({ root: '/workspace', entries: { 'task.md': file({ content: 'Fix the failing test and summarize the change.', }), repo: gitRepo({ repo: 'openai/openai-agents-js', ref: 'main', }), }, environment: { NODE_ENV: 'test', },});instructions 和 baseInstructions
Section titled “instructions 和 baseInstructions”使用 instructions 来放置应跨不同提示词保留的简短规则。在 SandboxAgent 中,这些 instructions 会附加在 SDK 的沙盒基础提示词之后,因此您可以保留内置的沙盒指导,同时添加自己的角色、工作流和成功标准。
只有在您想替换 SDK 沙盒基础提示词时,才使用 baseInstructions。大多数智能体都不应设置它。
| 放在…中 | 用途 | 示例 |
|---|---|---|
instructions | 智能体的稳定角色、工作流规则和成功标准。 | “检查入职文档,然后交接。”、“将最终文件写入 output/。“ |
baseInstructions | 完整替换 SDK 沙盒基础提示词。 | 自定义底层沙盒包装提示词。 |
| 用户提示词 | 本次运行的一次性请求。 | “总结这个工作区。“ |
| manifest 中的工作区文件 | 更长的任务说明、仓库本地 instructions 或有限的参考材料。 | repo/task.md、文档包、示例材料。 |
避免将用户的一次性任务复制到 instructions 中,避免嵌入本应放在 manifest 中的长参考资料,避免重复描述内置 capability 已经注入的工具文档,也避免混入模型在运行时并不需要的本地安装说明。
capabilities
Section titled “capabilities”Capabilities 会将沙盒原生行为附加到 SandboxAgent 上。它们可以在运行开始前塑造工作区、追加沙盒专属 instructions、暴露绑定到实时沙盒会话的工具,并为该智能体调整模型行为或输入处理方式。
内置 capabilities 包括:
| Capability | 在何时添加 | 说明 |
|---|---|---|
shell() | 智能体需要 shell 访问时。 | 添加 exec_command,以及当沙盒客户端支持 PTY 交互时添加 write_stdin。 |
filesystem() | 智能体需要编辑文件或查看本地图像时。 | 添加 apply_patch 和 view_image;补丁路径相对于工作区根目录。 |
skills() | 您希望在沙盒中进行技能发现和实例化时。 | 对于沙盒本地 SKILL.md 技能,优先使用此方式,而不是手动挂载 .agents 或 .agents/skills。 |
memory() | 后续运行应读取或生成记忆产物时。 | 需要 shell();实时更新还需要 filesystem()。 |
compaction() | 长时间运行的流程在压缩项后需要裁剪上下文时。 | 会调整模型采样和输入处理。 |
默认情况下,SandboxAgent.capabilities 使用 Capabilities.default(),其中包括 filesystem()、shell() 和 compaction()。如果您传入 capabilities: [...],该列表会替换默认值,因此请将您仍希望保留的默认 capability 一并包含进去。
Manifest
Section titled “Manifest”Manifest 描述全新沙盒会话的工作区。它可以设置工作区 root,声明文件和目录,复制本地文件,克隆 Git 仓库,附加远程存储挂载,设置环境变量,定义用户或组,并授予对工作区外特定绝对路径的访问权限。
Manifest 中的环境值默认会被持久化。对于 API 密钥、访问令牌或其他不应随沙盒状态一起保存的短期凭证,请使用如 { value: "...", ephemeral: true } 这样的临时条目。
Manifest 条目的路径是相对于工作区的。它们不能是绝对路径,也不能通过 .. 跳出工作区,这使工作区契约可以在本地、Docker 和托管客户端之间保持可移植性。
对于智能体在开始工作前所需的材料,请使用 manifest 条目:
| Manifest 条目 | 用途 |
|---|---|
file()、dir() | 小型合成输入、辅助文件或输出目录。 |
localFile()、localDir() | 应在沙盒中实例化的主机文件或目录。 |
gitRepo() | 应拉取到工作区中的仓库。 |
挂载,如 s3Mount()、gcsMount()、r2Mount()、azureBlobMount()、s3FilesMount() | 应显示在沙盒中的外部存储。 |
挂载条目描述要暴露哪些存储;挂载策略描述沙盒后端如何附加这些存储。有关挂载选项和提供方支持,请参阅沙盒客户端。
Permissions 控制 manifest 条目的文件系统权限。它针对的是沙盒实例化出的文件,而不是模型权限、审批策略或 API 凭证。
用户是可以在沙盒中执行工作的身份。当您希望某个身份存在于沙盒中时,可将用户添加到 manifest 中;然后在模型侧的沙盒工具(如 shell 命令、文件读取和补丁)应以该用户身份运行时,设置 SandboxAgent.runAs。
如果您还需要文件级共享规则,可将用户与 manifest 中的组以及条目的 group 元数据结合使用。runAs 用户控制谁来执行沙盒原生操作;Permissions 控制在沙盒实例化工作区后,该用户可以读取、写入或执行哪些文件。
SnapshotSpec
Section titled “SnapshotSpec”SnapshotSpec 告诉全新沙盒会话应从哪里恢复已保存的工作区内容,并持久化回哪里。它是沙盒工作区的快照策略,而 sessionState 则是用于恢复特定沙盒后端的序列化连接状态。
在需要本地持久化快照时使用本地快照;当您的应用提供远程快照客户端时使用远程快照。已挂载路径和临时路径不会作为持久工作区内容被复制到快照中。
沙盒生命周期
Section titled “沙盒生命周期”有两种生命周期模式:SDK 持有 和 开发者持有。
传入
sandbox.client。运行器创建或恢复一个沙盒会话。
智能体运行,并且基于快照的工作区状态可以持久化。
- 运行器关闭由其持有的资源。
创建一个
session。将
sandbox.session传入运行。- 智能体使用现有工作区。
- 自行检查、复用并关闭该会话。
当沙盒只需要存活一次运行时,使用 SDK 持有的生命周期。传入 client、可选的 manifest、可选的 snapshot 以及客户端 options;运行器会创建或恢复沙盒、运行智能体、持久化基于快照的工作区状态,并让客户端清理由运行器持有的资源。
import { run } from '@openai/agents';import { SandboxAgent } from '@openai/agents/sandbox';import { UnixLocalSandboxClient } from '@openai/agents/sandbox/local';
const agent = new SandboxAgent({ name: 'Workspace reviewer', model: 'gpt-5.5', instructions: 'Inspect the sandbox workspace before answering.',});
const result = await run(agent, 'Inspect the workspace.', { sandbox: { client: new UnixLocalSandboxClient(), },});
console.log(result.finalOutput);当您希望提前创建一个沙盒、在多次运行中复用同一个实时沙盒、在运行后检查文件、对您自己创建的沙盒进行流式传输,或精确决定何时清理时,使用开发者持有的生命周期。传入 session 会告诉运行器使用该实时沙盒,但不会替您关闭它。
import { run } from '@openai/agents';import { Manifest, SandboxAgent } from '@openai/agents/sandbox';import { UnixLocalSandboxClient } from '@openai/agents/sandbox/local';
const manifest = new Manifest();const agent = new SandboxAgent({ name: 'Workspace reviewer', model: 'gpt-5.5', instructions: 'Inspect the sandbox workspace before answering.',});
const client = new UnixLocalSandboxClient();const session = await client.create({ manifest });
try { await run(agent, 'First task.', { sandbox: { session } }); await run(agent, 'Follow-up task.', { sandbox: { session } });} finally { await session.close?.();}sandbox 运行选项
Section titled “sandbox 运行选项”sandbox 运行选项包含每次运行的配置,用于决定沙盒会话从哪里来,以及如何初始化全新会话。
这些选项决定运行器应复用、恢复还是创建沙盒会话:
| 选项 | 适用时机 | 说明 |
|---|---|---|
client | 您希望运行器为您创建、恢复并清理沙盒会话时。 | 除非您提供了一个实时沙盒 session,否则这是必需的。 |
session | 您已经自行创建了一个实时沙盒会话。 | 生命周期由调用方持有;运行器会复用该实时沙盒会话。 |
sessionState | 您有序列化的沙盒会话状态,但没有实时沙盒会话对象。 | 需要 client;运行器会从该显式状态恢复,并将其作为持有型会话。 |
全新会话输入
Section titled “全新会话输入”这些选项仅在运行器创建全新沙盒会话时才有意义:
| 选项 | 适用时机 | 说明 |
|---|---|---|
manifest | 您想要一次性的全新会话工作区覆盖。 | 省略时会回退到 agent.defaultManifest。 |
snapshot | 全新沙盒会话应从快照提供初始数据。 | 适用于类似恢复的流程或远程快照客户端。 |
options | 沙盒客户端在创建时需要额外选项。 | 常用于 Docker 镜像、提供方超时及类似的客户端特定设置。 |
concurrencyLimits 控制沙盒实例化工作可并行执行的程度。当大型 manifest 或本地目录复制需要更严格的资源控制时,可使用 manifestEntries 和 localDirFiles。
实例化控制被有意设计为每次运行配置。请将它们放在 sandbox 运行选项附近,这样同一个 SandboxAgent 就可以在复制大型本地目录时使用保守限制,而在处理小型 manifest 时使用更宽松的限制。
当 manifest 有许多独立条目(如文件、目录、仓库和挂载)时,使用 concurrencyLimits.manifestEntries。当 localDir() 条目包含许多文件且需要限制本地复制压力时,使用 concurrencyLimits.localDirFiles。
完整示例:编码任务
Section titled “完整示例:编码任务”这个编码风格的示例是一个很好的默认起点:
import { run } from '@openai/agents';import { Capabilities, Manifest, SandboxAgent, localDir, skills,} from '@openai/agents/sandbox';import { UnixLocalSandboxClient, localDirLazySkillSource,} from '@openai/agents/sandbox/local';import { dirname, join } from 'node:path';import { fileURLToPath } from 'node:url';
const exampleDir = dirname(fileURLToPath(import.meta.url));const hostRepoDir = join(exampleDir, 'repo');const hostSkillsDir = join(exampleDir, 'skills');
const manifest = new Manifest({ entries: { repo: localDir({ src: hostRepoDir }), },});
const agent = new SandboxAgent({ name: 'Sandbox engineer', model: 'gpt-5.5', instructions: 'Read `repo/task.md` before editing files. Load the `$invoice-total-fixer` skill before changing code. Stay grounded in the repository, preserve existing behavior, and mention the exact verification command you ran. If you edit files with apply_patch, paths are relative to the sandbox workspace root.', defaultManifest: manifest, capabilities: [ ...Capabilities.default(), skills({ lazyFrom: localDirLazySkillSource(hostSkillsDir), }), ],});
const result = await run( agent, 'Open `repo/task.md`, fix the issue, run the targeted test, and summarize the change.', { sandbox: { client: new UnixLocalSandboxClient(), }, },);
console.log(result.finalOutput);请从上面的完整示例开始。在很多情况下,可以保持同一个 SandboxAgent 不变,仅更改沙盒客户端、沙盒会话来源或工作区来源。
切换沙盒客户端
Section titled “切换沙盒客户端”保持智能体定义不变,只更改运行配置。当您需要容器隔离或镜像一致性时使用 Docker;当您需要由提供方管理执行环境时使用托管提供方。有关示例和提供方选项,请参阅沙盒客户端。
保持智能体定义不变,仅通过 sandbox: { client, manifest } 替换全新会话 manifest。当同一个智能体角色需要针对不同仓库、材料包或任务包运行,而无需重建智能体时,这种方式很适合。
注入沙盒会话
Section titled “注入沙盒会话”当您需要显式生命周期控制、运行后检查或复制输出时,可注入一个实时沙盒会话。为该次运行使用 sandbox: { session },并在您的应用代码中关闭该会话。
从会话状态恢复
Section titled “从会话状态恢复”如果您已经在 RunState 外部序列化了沙盒状态,可通过 sandbox: { client, sessionState } 让运行器从该状态重新连接。当沙盒状态存储在您自己的存储或任务系统中,并且您希望 Runner 直接从中恢复时,可使用此方式。
使用 sandbox: { client, snapshot } 让新沙盒从已保存的文件和产物中提供初始数据。当一次全新运行应从已保存的工作区内容开始,而不只是从 agent.defaultManifest 开始时,可使用此方式。
从 Git 加载技能
Section titled “从 Git 加载技能”通过 skills({ from: gitRepo(...) }) 将本地技能来源替换为基于仓库的来源。当技能包有自己的发布节奏,或应在多个沙盒之间共享时,可使用此方式。
作为工具暴露
Section titled “作为工具暴露”工具智能体既可以拥有自己的沙盒边界,也可以复用父运行中的实时沙盒。复用对于快速的只读探索型智能体很有用:它可以检查父级正在使用的精确工作区,而无需付出创建、填充或快照另一个沙盒的成本。
当工具智能体确实需要隔离时,请通过 sandboxAgent.asTool(...) 为它提供自己的 runConfig。如果工具智能体应自由修改、运行不受信任的命令,或使用不同的后端或镜像,请使用独立沙盒。
与本地工具和 MCP 结合
Section titled “与本地工具和 MCP 结合”在保留沙盒工作区的同时,仍可在同一个智能体上使用普通工具。沙盒 capabilities 可以与 tools、mcpServers、handoffs、模型设置和输出配置共存。
当未来的沙盒智能体运行应从先前运行中学习时,请使用 memory() capability。记忆不同于 SDK 对话式 Session 记忆:它会将经验提炼成沙盒工作区中的文件,之后的运行可以读取这些文件。
有关设置、读取/生成行为、多轮对话和布局隔离,请参阅智能体记忆。
当单智能体模式已经清晰后,下一个设计问题就是在更大的系统中,沙盒边界应该放在哪里。
沙盒智能体仍然可以与 SDK 的其余部分组合:
- 交接:将文档密集型工作从非沙盒接入智能体交接给沙盒审查智能体。
- Agents as tools:将多个沙盒智能体作为工具暴露,通常是在每次
asTool(...)调用中传入一个沙盒运行配置,以便每个工具都有自己的沙盒边界。 - MCP 集成 和普通函数工具:沙盒 capabilities 可以与
mcpServers和普通工具共存。 - 运行智能体:沙盒运行仍然使用常规的
run()和RunnerAPI。
对于交接,仍然只有一个顶层运行和一个顶层 turn 循环。活跃智能体会变化,但运行不会变成嵌套结构。
而使用 asTool(...) 时,关系就不同了。外层编排器会在一个外层 turn 中决定调用该工具,而该工具调用会为沙盒智能体启动一个嵌套运行。这个嵌套运行有自己的 turn 循环、maxTurns、审批,并且通常也有自己的沙盒运行配置。从外层编排器的视角看,所有这些工作仍然都隐藏在一次工具调用之后,因此嵌套的 turns 不会增加外层运行的 turn 计数。