跳转到内容

概念

现代智能体在能够操作文件系统中的真实文件时效果最佳。沙盒智能体可以使用专用工具和 shell 命令搜索和处理大型文档集、编辑文件、生成产物并运行命令。沙盒为模型提供一个持久化工作区,智能体可在其中代您完成工作。Agents SDK 中的沙盒智能体可帮助您运行与沙盒环境配对的智能体,让您能够轻松地将所需文件放入文件系统,并大规模编排沙盒以启动、停止和恢复任务。

您可以围绕智能体所需的数据定义工作区。工作区可以来自 GitHub 仓库、本地文件和目录、合成任务文件、S3 或 Azure Blob Storage 等远程文件系统,以及您提供的其他沙盒输入。

带计算环境的沙盒智能体框架

SandboxAgent 扩展了 Agent,因此它仍然是一个 Agent。它保留常规的智能体接口,例如 instructionstoolshandoffsmcpServersmodelSettings、输出类型、护栏和钩子,并且仍通过常规的 run()Runner API 运行。变化之处在于执行边界:

  • SandboxAgent 定义智能体本身:包括常规智能体配置,以及 defaultManifestbaseInstructionsrunAs 等沙盒专用默认设置,还有文件系统工具、shell 访问、技能、记忆或压缩等功能。
  • Manifest 声明全新沙盒工作区所需的初始内容和布局,包括文件、仓库、挂载和环境。
  • 沙盒会话是命令运行和文件发生变化的实时执行环境。
  • sandbox 运行选项决定本次运行如何获得该沙盒会话,例如直接注入会话、从序列化的沙盒会话状态重新连接,或通过沙盒客户端创建全新的沙盒会话。
  • 保存的沙盒状态和快照可让后续运行重新连接到先前的工作,或根据保存的内容初始化全新的沙盒会话。

Manifest 定义新沙盒工作区的初始内容。它不描述每个实时沙盒中的当前文件,因为复用的会话、序列化的会话状态和快照都可以在运行时提供或更改工作区。

在本页中,“沙盒会话”是指由沙盒客户端管理的实时执行环境。具体边界取决于客户端:Unix 本地会话在主机上的本地工作区中运行,而 Docker 和托管客户端可提供更强的环境隔离。这与会话中介绍的 SDK 对话式 Session 接口不同。

外层运行时仍负责审批、追踪、交接和恢复记录管理。沙盒会话负责命令、文件更改和环境隔离。这种职责划分是该模型的核心部分。

一次沙盒运行会将智能体定义与单次运行的沙盒配置相结合。运行器会准备智能体,将其绑定到实时沙盒会话,并可保存状态供后续运行使用。

SandboxAgent智能体及沙盒默认设置
Runner准备 instructions 并绑定功能工具
沙盒会话命令运行和文件发生变化的工作区
已保存状态稍后恢复或初始化全新工作区

沙盒专用默认设置保留在 SandboxAgent 上。每次运行的沙盒会话选择则保留在 sandbox 运行选项中。

可以将生命周期分为三个阶段:

  1. 使用 SandboxAgentManifest 和各项功能定义智能体与初始工作区内容。
  2. run()Runner 提供 sandbox 运行选项,通过注入、恢复或创建沙盒会话来执行运行。
  3. 稍后从运行器管理的 RunState、显式沙盒 sessionState 或保存的工作区快照继续运行。

如果只偶尔需要将 shell 访问作为一个工具,请先使用工具指南中的托管 shell。当工作区隔离、沙盒客户端选择或沙盒会话恢复行为属于整体设计的一部分时,请使用沙盒智能体。

沙盒智能体非常适合以工作区为中心的工作流,例如:

  • 编码和调试:针对 GitHub 仓库中的问题报告编排自动修复,并运行有针对性的测试。
  • 文档处理和编辑:从用户的财务文档中提取信息,并生成填妥的税表草稿。
  • 基于文件的审查或分析:在回答之前检查入职资料包、生成的报告或产物包。
  • 隔离式多智能体模式:为每个审查智能体或编码子智能体提供独立工作区。
  • 多步骤工作区任务:在一次运行中修复错误,稍后再添加回归测试,或从快照或沙盒会话状态恢复。

如果不需要访问文件或持续存在的文件系统,请继续使用 Agent。如果 shell 访问只是偶尔需要的一项功能,请添加托管 shell;如果工作区边界本身就是功能的一部分,请使用沙盒智能体。

本地开发请从 UnixLocalSandboxClient 开始。当需要容器隔离或镜像一致性时,请改用 DockerSandboxClient。需要由提供商管理执行时,请使用托管提供商。

大多数情况下,SandboxAgent 定义保持不变,只需在 sandbox 运行选项中更改沙盒客户端及其选项。有关本地、Docker、托管和远程挂载选项,请参阅沙盒客户端

层级主要 SDK 组件回答的问题
智能体定义SandboxAgentManifest、功能将运行哪个智能体?其全新会话工作区应遵循什么初始约定?
沙盒执行sandbox 运行选项、沙盒客户端和实时沙盒会话本次运行如何获得实时沙盒会话?工作在哪里执行?
已保存的沙盒状态RunState 沙盒载荷、sessionState 和快照此工作流如何重新连接到先前的沙盒工作,或根据保存的内容初始化全新的沙盒会话?

主要 SDK 组件与这些层级的对应关系如下:

组件负责的内容应提出的问题
SandboxAgent智能体定义此智能体应该做什么?哪些默认设置应该随它一起使用?
Manifest全新会话的工作区文件和文件夹运行开始时,文件系统中应存在哪些文件和文件夹?
Capability沙盒原生行为应为此智能体附加哪些工具、instructions 片段或运行时行为?
sandbox 运行选项每次运行的沙盒客户端和沙盒会话来源本次运行应注入、恢复还是创建沙盒会话?
RunState由运行器管理的已保存沙盒状态我是否正在恢复由运行器管理的先前工作流,并自动延续其沙盒状态?
sandbox.sessionState显式序列化的沙盒会话状态我是否希望从已在 RunState 外部序列化的沙盒状态恢复?
sandbox.snapshot用于全新沙盒会话的已保存工作区内容新沙盒会话是否应从保存的文件和产物开始?

推荐的实际设计顺序如下:

  1. 使用 Manifest 或清单初始化对象定义全新会话的工作区约定。
  2. 使用 SandboxAgent 定义智能体。
  3. 添加内置或自定义功能。
  4. 决定每次运行应如何在 run(agent, input, { sandbox: ... })new Runner({ sandbox: ... }) 中获取沙盒会话。

运行时,运行器会将该定义转换为具体的沙盒支持运行:

  1. sandbox 运行选项解析沙盒会话。
  2. 确定本次运行实际使用的工作区输入。
  3. 让各项功能处理生成的清单。
  4. 按固定顺序构建最终 instructions:SDK 的默认沙盒提示;如果您显式覆盖,则改用 baseInstructions;然后是 instructions、功能的 instructions 片段、任何远程挂载策略文本,最后是渲染后的文件系统树。
  5. 将功能工具绑定到实时沙盒会话,并通过常规的 run()Runner API 运行准备好的智能体。

沙盒机制不会改变”回合”的含义。一个回合仍是一次模型步骤,而不是单条 shell 命令或一次沙盒操作。沙盒侧操作与回合之间不存在固定的 1:1 映射。实际使用中,只有在沙盒工作发生后,智能体运行时需要模型再次响应时,才会消耗另一个回合。

以下是常规 Agent 字段之外的沙盒专用选项:

选项最佳用途
defaultManifest运行器创建的全新沙盒会话所使用的默认工作区。
instructions附加在 SDK 沙盒提示之后的角色、工作流和成功标准。
baseInstructions用于替换 SDK 沙盒提示的高级后备选项。
capabilities应随此智能体一起使用的沙盒原生工具和行为。
runAs面向模型的沙盒工具所使用的用户身份,例如 shell 命令、文件读取和补丁操作。

沙盒客户端选择、沙盒会话复用、清单覆盖和快照选择应放在 sandbox 运行选项中,而不是智能体上。

defaultManifest 是运行器为此智能体创建全新沙盒会话时使用的默认工作区。可以传入 Manifest 实例,也可以传入与 new 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。在 SandboxAgent 中,这些 instructions 会附加到 SDK 的沙盒基础提示之后,因此您可以保留内置沙盒指南,同时添加自己的角色、工作流和成功标准。

只有在需要替换 SDK 沙盒基础提示时,才应使用 baseInstructions。大多数智能体都不应设置它。

放置位置用途示例
instructions智能体的稳定角色、工作流规则和成功标准。“检查入职文档,然后进行交接。""将最终文件写入 output/。“
baseInstructions完整替换 SDK 沙盒基础提示。自定义底层沙盒包装提示。
用户提示本次运行的一次性请求。“总结此工作区。“
清单中的工作区文件较长的任务规范、仓库本地 instructions 或限定范围的参考材料。repo/task.md、文档包、示例资料包。

请避免将用户的一次性任务复制到 instructions 中、嵌入本应放入清单的长篇参考材料、重复内置功能已经注入的工具文档,或混入模型在运行时不需要的本地安装说明。

功能可为 SandboxAgent 附加沙盒原生行为。它们可以在运行开始前调整工作区、附加沙盒专用 instructions、公开绑定到实时沙盒会话的工具,并调整该智能体的模型行为或输入处理方式。

内置功能包括:

功能添加时机说明
shell()智能体需要 shell 访问。添加 exec_command;当沙盒客户端支持 PTY 交互时,还会添加 write_stdin
filesystem()智能体需要编辑文件或检查本地图像。添加 apply_patchview_image;补丁路径相对于工作区根目录。
skills()希望在沙盒中发现并物化技能。对于沙盒本地的 SKILL.md 技能,优先使用此功能,而不是手动挂载 .agents.agents/skills
memory()后续运行应读取或生成记忆产物。需要 shell();实时更新还需要 filesystem()
compaction()长时间运行的流程需要在压缩项出现后裁剪上下文。调整模型采样和输入处理。

默认情况下,SandboxAgent.capabilities 使用 Capabilities.default(),其中包括 filesystem()shell()compaction()。如果传入 capabilities: [...],该列表会替换默认值,因此请加入仍希望保留的所有默认功能。

Manifest 描述全新沙盒会话的工作区。它可以设置工作区 root、声明文件和目录、复制本地文件、克隆 Git 仓库、附加远程存储挂载、设置环境变量、定义用户或组,并授权访问工作区外的特定绝对路径。

默认情况下,清单中的环境值会持久保存。对于 API 密钥、访问令牌或其他不应随沙盒状态保存的短期凭据,请使用 { value: "...", ephemeral: true } 等临时条目。

清单条目路径相对于工作区。它们不能是绝对路径,也不能使用 .. 逃离工作区,从而确保工作区约定可在本地、Docker 和托管客户端之间移植。

对于智能体开始工作前所需的材料,请使用清单条目:

清单条目用途
file()dir()小型合成输入、辅助文件或输出目录。
localFile()localDir()应物化到沙盒中的主机文件或目录。
gitRepo()应提取到工作区中的仓库。
s3Mount()gcsMount()r2Mount()azureBlobMount()s3FilesMount() 等挂载应显示在沙盒内的外部存储。

对于本地物化,localFile()localDir() 的源路径必须位于本地源基础目录内。默认基础目录是 Node 进程的当前工作目录,本地沙盒客户端在物化条目时也可能提供客户端专用的基础目录。如果源文件必须来自其他主机绝对目录,请添加范围尽可能小的 Manifest.extraPathGrants 条目。

本地延迟技能发现也会使用 extraPathGrants。如果 localDirLazySkillSource() 指向源基础目录之外的位置,而清单未授予对该目录的访问权限,则会忽略该来源。对于共享技能、数据集和参考仓库等输入包,建议设置 readOnly: true

授权访问共享本地来源
import { Manifest, localDir, skills } from '@openai/agents/sandbox';
import { localDirLazySkillSource } from '@openai/agents/sandbox/local';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
const appRoot = dirname(fileURLToPath(import.meta.url));
const repoDir = join(appRoot, 'repo');
const sharedSkillsDir = '/opt/company/agent-skills';
const manifest = new Manifest({
extraPathGrants: [
{
path: sharedSkillsDir,
readOnly: true,
description: 'Shared skill bundle.',
},
],
entries: {
repo: localDir({ src: repoDir }),
},
});
const skillCapability = skills({
lazyFrom: localDirLazySkillSource({
src: sharedSkillsDir,
}),
});

挂载条目描述要公开的存储,挂载策略则描述沙盒后端如何附加该存储。有关挂载选项和提供商支持,请参阅沙盒客户端

Permissions 控制清单条目的文件系统权限。它针对沙盒物化的文件,而非模型权限、审批策略或 API 凭据。

用户是可以在沙盒中执行工作的身份。如果希望某个身份存在于沙盒中,请将该用户添加到清单;当 shell 命令、文件读取和补丁等面向模型的沙盒工具应以该用户身份运行时,再设置 SandboxAgent.runAs

如果还需要文件级共享规则,请将用户与清单组以及条目的 group 元数据结合使用。runAs 用户控制由谁执行沙盒原生操作;Permissions 则控制沙盒物化工作区后,该用户可以读取、写入或执行哪些文件。

SnapshotSpec 指定全新沙盒会话应从何处恢复已保存的工作区内容,以及将内容持久保存回何处。它是沙盒工作区的快照策略,而 sessionState 是用于恢复特定沙盒后端的序列化连接状态。

本地持久快照请使用本地快照;当应用提供远程快照客户端时,请使用远程快照。挂载路径和临时路径不会作为持久工作区内容复制到快照中。

生命周期有两种模式:SDK 管理开发者管理

SDK 管理运行器管理实时沙盒。
  1. 传入 sandbox.client

  2. 运行器创建或恢复沙盒会话。
  3. 智能体运行,并可持久保存由快照支持的工作区状态。

  4. 运行器关闭其管理的资源。
开发者管理您的应用管理实时沙盒。
  1. 创建 session

  2. sandbox.session 传入运行。

  3. 智能体使用现有工作区。
  4. 检查并复用会话,然后自行将其关闭。

当沙盒只需在一次运行期间存在时,请使用 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.6-sol',
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.6-sol',
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 运行选项包含每次运行的设置,用于决定沙盒会话的来源,以及全新会话的初始化方式。

以下选项决定运行器应复用、恢复还是创建沙盒会话:

选项使用时机说明
client希望运行器为您创建、恢复和清理沙盒会话。除非提供实时沙盒 session,否则必填。
session已自行创建实时沙盒会话。调用方负责生命周期;运行器复用该实时沙盒会话。
sessionState拥有序列化的沙盒会话状态,但没有实时沙盒会话对象。需要 client;运行器从该显式状态恢复,并负责会话生命周期。

以下选项仅在运行器创建全新沙盒会话时生效:

选项使用时机说明
manifest希望为全新会话提供一次性工作区覆盖设置。接受 Manifest 或清单初始化对象。省略时回退到 agent.defaultManifest
snapshot全新沙盒会话应从快照初始化。适用于类似恢复的流程或远程快照客户端。
options沙盒客户端需要创建时选项。常用于 Docker 镜像、提供商超时和类似的客户端专用设置。

concurrencyLimits 控制可并行执行的沙盒物化工作量。当大型清单或本地目录复制需要更严格的资源控制时,请使用 manifestEntrieslocalDirFiles

物化控制有意按每次运行设置。请将其放在 sandbox 运行选项附近,这样同一个 SandboxAgent 就可以对大型本地目录复制使用保守限制,对小型清单使用较宽松的限制。

当清单包含许多相互独立的条目(例如文件、目录、仓库和挂载)时,请使用 concurrencyLimits.manifestEntries。当 localDir() 条目包含大量文件,需要限制本地复制压力时,请使用 concurrencyLimits.localDirFiles

以下编码风格示例是一个合适的默认起点:

沙盒编码任务
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.6-sol',
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({
src: 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 不变,只更改沙盒客户端、沙盒会话来源或工作区来源。

保持智能体定义不变,只更改运行配置。需要容器隔离或镜像一致性时使用 Docker;需要由提供商管理执行时使用托管提供商。有关示例和提供商选项,请参阅沙盒客户端

保持智能体定义不变,仅通过 sandbox: { client, manifest } 替换全新会话的清单。当同一个智能体角色需要针对不同仓库、资料包或任务包运行,而无需重新构建智能体时,请使用此模式。

当需要显式控制生命周期、运行后检查或复制输出时,请注入实时沙盒会话。为该次运行使用 sandbox: { session },并在应用代码中关闭会话。

如果已经在 RunState 外部序列化了沙盒状态,可以通过 sandbox: { client, sessionState } 让运行器从该状态重新连接。当沙盒状态存储在您自己的存储或作业系统中,并希望 Runner 直接从中恢复时,请使用此模式。

使用 sandbox: { client, snapshot } 根据保存的文件和产物初始化新沙盒。当全新运行应从已保存的工作区内容开始,而不是仅使用 agent.defaultManifest 时,请使用此模式。

使用 skills({ from: gitRepo(...) }) 将本地技能来源替换为仓库支持的来源。当技能包有自己的发布周期,或需要在多个沙盒间共享时,请使用此模式。

工具智能体既可以拥有自己的沙盒边界,也可以复用父级运行中的实时沙盒。对于快速的只读探索智能体,复用非常有用:它可以检查父级正在使用的同一工作区,而无需付出创建、填充或快照另一个沙盒的成本。

如果工具智能体确实需要隔离,请通过 sandboxAgent.asTool(...) 为其提供自己的 runConfig。当工具智能体需要自由修改内容、运行不受信任的命令,或使用不同的后端或镜像时,请使用独立沙盒。

在保留沙盒工作区的同时,仍可在同一个智能体上使用常规工具。沙盒功能可以与 toolsmcpServers、交接、模型设置和输出配置共存。

当未来的沙盒智能体运行应从先前运行中学习时,请使用 memory() 功能。记忆与 SDK 的对话式 Session 记忆彼此独立:它会将经验提炼为沙盒工作区内的文件,供后续运行读取。

有关设置、读取与生成行为、多轮对话和布局隔离,请参阅智能体记忆

理解单智能体模式后,下一个设计问题就是在更大的系统中应将沙盒边界放在哪里。

沙盒智能体仍可与 SDK 的其他部分组合:

  • 交接:将文档密集型工作从非沙盒接收智能体交接给沙盒审查智能体。
  • Agents as tools:将多个沙盒智能体公开为工具,通常是在每次调用 asTool(...) 时传入沙盒运行配置,让每个工具拥有自己的沙盒边界。
  • MCP 集成和常规函数工具:沙盒功能可以与 mcpServers 和常规工具共存。
  • 运行智能体:沙盒运行仍使用常规的 run()Runner API。

使用交接时,仍然只有一次顶层运行和一个顶层回合循环。活跃智能体会发生变化,但该运行不会变成嵌套运行。

使用 asTool(...) 时,两者关系不同。外层编排智能体使用一个外层回合来决定调用工具,而该工具调用会为沙盒智能体启动一次嵌套运行。嵌套运行拥有自己的回合循环、maxTurns、审批,通常也有自己的沙盒运行配置。从外层编排智能体的角度看,所有这些工作仍封装在一次工具调用之后,因此嵌套回合不会增加外层运行的回合计数。