跳转到内容

概念

现代智能体在能够操作文件系统中的真实文件时效果最佳。沙盒智能体可以使用专用工具和 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 接口,后者在会话中介绍。

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

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

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

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

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

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

如果 shell 访问只是偶尔使用的一个工具,请从工具中的托管 shell 开始。当工作区隔离、沙盒客户端选择或沙盒会话恢复行为是设计的一部分时,再使用沙盒智能体。

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

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

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

本地开发时从 UnixLocalSandboxClient 开始。当您需要容器隔离或镜像一致性时,切换到 DockerSandboxClient。当您需要由提供商托管的执行环境时,切换到托管提供商。

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

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

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

组件负责内容需要提出的问题
SandboxAgent智能体定义这个智能体应做什么?哪些默认值应随它一起传递?
Manifest全新会话的工作区文件和文件夹运行开始时,文件系统上应有哪些文件和文件夹?
Capability沙盒原生行为哪些工具、instruction 片段或运行时行为应附加到此智能体?
sandbox 运行选项每次运行的沙盒客户端和沙盒会话来源本次运行应注入、恢复还是创建沙盒会话?
RunStaterunner 管理的已保存沙盒状态我是否在恢复之前由 runner 管理的工作流,并自动向前携带其沙盒状态?
sandbox.sessionState显式序列化的沙盒会话状态我是否希望从已经在 RunState 之外序列化的沙盒状态恢复?
sandbox.snapshot用于全新沙盒会话的已保存工作区内容新沙盒会话是否应从已保存的文件和产物开始?

一个实用的设计顺序是:

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

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

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

沙盒化不会改变一轮对话的含义。一轮仍然是一个模型步骤,而不是单个 shell 命令或沙盒操作。沙盒侧操作与轮次之间没有固定的 1:1 映射。作为实用规则,只有当智能体运行时在沙盒工作发生后需要另一个模型响应时,才会消耗另一轮。

这些是在常规 Agent 字段之上的沙盒专用选项:

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

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

defaultManifest 是 runner 为此智能体创建全新沙盒会话时使用的默认工作区。传入一个 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/。“
baseInstructionsSDK 沙盒基础提示的完整替代。自定义低层沙盒包装提示。
用户提示本次运行的一次性请求。“总结此工作区。“
清单中的工作区文件更长的任务规格、仓库本地 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 拥有Runner 拥有实时沙盒。
  1. 传入 sandbox.client

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

  4. Runner 关闭由 runner 拥有的资源。

开发者拥有您的应用拥有实时沙盒。
  1. 创建一个 session

  2. sandbox.session 传入运行。

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

当沙盒只需要在一次运行中存活时,使用 SDK 拥有的生命周期。传入 client、可选的 manifest、可选的 snapshot 和客户端 options;runner 会创建或恢复沙盒,运行智能体,持久化由快照支持的工作区状态,并让客户端清理由 runner 拥有的资源。

让 runner 管理沙盒会话
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 会告诉 runner 使用该实时沙盒,但不会替您关闭它。

自行管理沙盒会话
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 运行选项保存每次运行的选项,用于决定沙盒会话来自哪里,以及全新会话应如何初始化。

这些选项决定 runner 应复用、恢复还是创建沙盒会话:

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

这些选项只在 runner 创建全新沙盒会话时才有意义:

选项使用时机说明
manifest您希望为全新会话的工作区提供一次性覆盖。接受 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.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({
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 之外序列化了沙盒状态,可以让 runner 使用 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、审批,并且通常有自己的沙盒运行配置。从外层编排器的角度看,所有这些工作仍然位于一次工具调用之后,因此嵌套轮次不会增加外层运行的轮次计数器。