openai-agents-python-sdk 源码解析 | 第十六篇:Sandbox Agent 入门:让 Agent 在工作区中执行长任务
本篇导读
前十五篇已经覆盖了 SDK 的文本、多 Agent、工具、MCP、Realtime 和 Voice 能力。
这一篇进入 Sandbox Agent。
Sandbox Agent 的目标不是让模型“知道更多文件名”,而是给模型一个可以工作的文件系统和执行环境:
用户任务
-> Runner
-> SandboxAgent
-> sandbox session
-> 文件读取、补丁、命令执行、产物生成
-> 最终回答
它适合这类任务:
- 检查一个代码仓库并修复问题。
- 在隔离 workspace 中运行测试。
- 读取大量本地文件后生成报告。
- 处理文档包、数据包和输出产物。
- 让长任务跨 run 保留 workspace 状态。
本篇重点回答八个问题:
SandboxAgent和普通Agent有什么区别。Manifest如何声明 fresh sandbox workspace。File、Dir、LocalFile、LocalDir、GitRepo分别适合什么场景。SandboxRunConfig如何决定 session 来源。- Runner 如何准备 sandbox agent、绑定 capabilities 和构造提示词。
UnixLocalSandboxClient与DockerSandboxClient的差异是什么。- 什么时候让 Runner 托管生命周期,什么时候自己创建 live session。
- 入门阶段需要避开的安全边界和路径边界是什么。
第十六篇关注的源码入口
这一篇主要看这些文件:
src/agents/sandbox/sandbox_agent.py
src/agents/sandbox/manifest.py
src/agents/sandbox/entries/base.py
src/agents/sandbox/entries/artifacts.py
src/agents/sandbox/runtime.py
src/agents/sandbox/runtime_agent_preparation.py
src/agents/sandbox/runtime_session_manager.py
src/agents/sandbox/session/base_sandbox_session.py
src/agents/sandbox/session/sandbox_client.py
src/agents/sandbox/sandboxes/unix_local.py
src/agents/sandbox/sandboxes/docker.py
docs/sandbox_agents.md
docs/sandbox/guide.md
docs/sandbox/clients.md
examples/sandbox
最关键的是:
sandbox_agent.py:定义SandboxAgent。manifest.py:定义 workspace contract。entries/artifacts.py:定义 file、dir、local dir、git repo 等 entry。runtime.py:Runner 侧 sandbox runtime 接入点。runtime_session_manager.py:决定 session 注入、恢复或创建。runtime_agent_preparation.py:准备 instructions、tools 和 capabilities。unix_local.py:本地 Unix sandbox client。docker.py:Docker sandbox client。
先给结论:SandboxAgent 仍然是 Agent
SandboxAgent 继承自普通 Agent:
@dataclass
class SandboxAgent(Agent[TContext]):
default_manifest: Manifest | None = None
base_instructions: str | Callable[..., ...] | None = None
capabilities: Sequence[Capability] = field(
default_factory=Capabilities.default
)
run_as: User | str | None = None
它仍然支持普通 Agent 的能力:
instructions。tools。handoffs。mcp_servers。model_settings。output_type。- guardrails。
- hooks。
新增的是 sandbox-specific 配置:
| 字段 | 作用 |
|---|---|
default_manifest |
fresh session 默认 workspace |
base_instructions |
替换 SDK 默认 sandbox prompt 的高级入口 |
capabilities |
绑定 sandbox-native 工具和行为 |
run_as |
model-facing sandbox actions 的用户身份 |
所以要避免一个误解:
SandboxAgent 不是新的 Runner。
它是普通 Agent 加上 sandbox workspace 和 execution boundary。
普通 Agent 与 SandboxAgent 的差异
普通 Agent 更像是纯对话执行:
输入 -> 模型 -> 工具调用 -> 输出
Sandbox Agent 增加了一个 live execution environment:
输入 -> 模型 -> sandbox tools -> sandbox session -> 文件和进程状态 -> 输出
差异可以总结为:
| 维度 | 普通 Agent |
SandboxAgent |
|---|---|---|
| 文件系统 | 需要工具自行提供 | 有 sandbox workspace |
| 命令执行 | 通常依赖自定义工具 | 通过 shell capability 暴露 |
| 文件编辑 | 通常依赖自定义工具 | 通过 filesystem capability 暴露 |
| 工作区来源 | 无统一协议 | Manifest |
| 执行环境 | 无统一生命周期 | SandboxSession |
| 运行配置 | RunConfig |
RunConfig(sandbox=SandboxRunConfig(...)) |
| 恢复能力 | 依赖普通 run/session | 可携带 sandbox state 或 snapshot |
这也是为什么文档说:
如果只是偶尔需要一个 shell tool,可以先用 hosted shell。
如果 workspace boundary 本身是产品设计的一部分,再使用 SandboxAgent。
三个核心对象
入门先记住三个对象:
SandboxAgent
Manifest
SandboxRunConfig
它们回答的问题不同:
| 对象 | 回答的问题 |
|---|---|
SandboxAgent |
这个 agent 要做什么,默认 workspace 是什么 |
Manifest |
fresh session 启动时 workspace 里有什么 |
SandboxRunConfig |
这次 run 从哪里拿 live sandbox session |
真正执行命令、读写文件、挂载资源的是:
SandboxSession
但是大多数入门代码不会直接操作 session。
你只需要把 sandbox client 放进 run config,让 Runner 创建和清理。
最小 SDK-owned 示例
最简单的本地 Unix 示例是:
from agents import Runner
from agents.run import RunConfig
from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig
from agents.sandbox.entries import File
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient
构造一个 workspace:
manifest = Manifest(
entries={
"README.md": File(content=b"# Demo\n\nSandbox project."),
"src/app.py": File(content=b'print("hello")\n'),
}
)
构造 agent:
agent = SandboxAgent(
name="Workspace reviewer",
instructions="Inspect the workspace before answering.",
default_manifest=manifest,
)
运行:
result = await Runner.run(
agent,
"Summarize this project in two bullets.",
run_config=RunConfig(
sandbox=SandboxRunConfig(client=UnixLocalSandboxClient()),
),
)
这个路径叫 SDK-owned lifecycle。
Runner 会:
- 通过 client 创建 sandbox session。
- 启动 session。
- 应用 manifest。
- 准备 agent。
- 绑定 sandbox tools。
- 执行 run。
- 持久化 snapshot。
- 停止并清理 runner-owned session。
Manifest 是 fresh workspace contract
Manifest 的定义是:
class Manifest(BaseModel):
version: Literal[1] = 1
root: str = "/workspace"
entries: dict[str | Path, BaseEntry] = Field(default_factory=dict)
environment: Environment = Field(default_factory=Environment)
users: list[User] = Field(default_factory=list)
groups: list[Group] = Field(default_factory=list)
还有两个重要字段:
extra_path_grants: tuple[SandboxPathGrant, ...] = ()
remote_mount_command_allowlist: list[str] = ...
它描述的是:
fresh sandbox session 启动时应该有哪些文件、目录、环境变量、用户和挂载。
注意这里的限定词:
fresh session。
如果你传入已经运行的 session,或者从 session_state / RunState 恢复,已有 workspace 状态会优先。
Manifest 不会无条件覆盖 live sandbox 的所有状态。
entries 路径必须是 workspace-relative
Manifest entry key 是 workspace 内路径。
源码会验证:
if rel_path.is_absolute():
raise InvalidManifestPathError(...)
if ".." in rel_path.parts:
raise InvalidManifestPathError(...)
这意味着:
Manifest(entries={"repo": LocalDir(src="./repo")})
是合法的。
但下面两种目标路径不合法:
Manifest(entries={"/repo": LocalDir(src="./repo")})
Manifest(entries={"../repo": LocalDir(src="./repo")})
这个限制让 manifest 在 Unix local、Docker 和 hosted provider 之间更可移植,也避免让不可信 manifest 把文件写到 workspace 外部。
常见 entry 类型
入门阶段常用 entry 有五类:
| Entry | 来源 | 用途 |
|---|---|---|
File |
代码内 bytes | 小型任务文件、说明文件 |
Dir |
代码内 children | 创建输出目录或合成目录树 |
LocalFile |
SDK 主机文件 | 把一个本地文件复制进 sandbox |
LocalDir |
SDK 主机目录 | 把一个本地目录递归复制进 sandbox |
GitRepo |
Git 仓库 | 克隆指定 repo/ref/subpath |
例如:
from agents.sandbox.entries import File, LocalDir
manifest = Manifest(
entries={
"task.md": File(content=b"Review the repo."),
"repo": LocalDir(src="./my-project"),
}
)
这里 "repo" 是 sandbox workspace 里的路径。
src="./my-project" 是 SDK 进程所在主机上的路径。
这两个路径不在同一个命名空间里。
LocalDir 的安全边界
LocalDir 是 host-side source。
它从 SDK 进程所在机器读取文件,再复制进 sandbox。
源码会把 src 解析到 base dir 下:
src_input = _absolute_without_symlink_resolution(base_dir / self.src)
如果 source 不在 base dir 下,则必须匹配 extra_path_grants:
matching_grant = self._matching_source_grant(
src_input,
source_grants,
)
否则抛出 LocalDirReadError。
它还会拒绝 symlink:
if stat.S_ISLNK(current_stat.st_mode):
raise LocalDirReadError(...)
所以不要把 extra_path_grants 当成“给模型自己申请 host 权限”的入口。
它应该由可信应用代码配置。
Dir 和 LocalDir 的区别
这两个名字很像,但语义完全不同。
Dir 创建 sandbox 内目录:
from agents.sandbox.entries import Dir
Manifest(entries={"output": Dir()})
它不会读取主机目录。
LocalDir 复制主机目录:
from agents.sandbox.entries import LocalDir
Manifest(entries={"repo": LocalDir(src="./repo")})
它会从 SDK 进程所在机器读取 ./repo。
入门时可以这样记:
Dir 是 sandbox 里的目录。
LocalDir 是 host 到 sandbox 的目录拷贝。
GitRepo entry
GitRepo 用于把仓库放进 workspace:
from agents.sandbox.entries import GitRepo
manifest = Manifest(
entries={
"repo": GitRepo(
repo="openai/openai-agents-python",
ref="main",
)
}
)
源码里会检查 sandbox 环境是否有 git:
git_check = await session.exec("command -v git >/dev/null 2>&1")
然后执行 clone/copy。
所以 Docker 镜像或 hosted backend 需要包含必要工具。
如果你用 python:3.14-slim 这类极简镜像,别默认假设里面有所有开发工具。
SandboxRunConfig
SandboxRunConfig 定义在 src/agents/run_config.py:
@dataclass
class SandboxRunConfig:
client: BaseSandboxClient[Any] | None = None
options: Any | None = None
session: BaseSandboxSession | None = None
session_state: SandboxSessionState | None = None
manifest: Manifest | None = None
snapshot: SnapshotSpec | SnapshotBase | None = None
还包括 materialization 限制:
concurrency_limits: SandboxConcurrencyLimits = ...
archive_limits: SandboxArchiveLimits | None = None
这里最重要的是:
SandboxRunConfig 是 per-run 配置,不是 agent 定义。
同一个 SandboxAgent 可以在不同 run 中使用不同 sandbox client、不同 manifest override、不同 snapshot 或不同 live session。
session 来源解析顺序
SandboxRuntimeSessionManager 的逻辑可以概括为:
1. 如果传入 session,直接复用 live session。
2. 否则,如果 RunState 带 sandbox state,尝试恢复。
3. 否则,如果传入 session_state,显式恢复。
4. 否则,通过 client 创建 fresh session。
fresh session 时才会选择 manifest:
run_config.sandbox.manifest
-> agent.default_manifest
这解释了一个常见困惑:
为什么我改了 default_manifest,但复用 session 后没看到 workspace 重置?
因为复用 live session 时,已有 workspace 状态优先。
Manifest 不是每次 run 都重铺全量文件系统。
SDK-owned lifecycle
SDK-owned lifecycle 用法是:
run_config = RunConfig(
sandbox=SandboxRunConfig(
client=UnixLocalSandboxClient(),
)
)
result = await Runner.run(agent, prompt, run_config=run_config)
此时 Runner owns session。
cleanup 路径在源码里包括:
await self._session.run_pre_stop_hooks()
await self._session.stop()
await self._session.shutdown()
await self._client.delete(self._session)
await self._session._aclose_dependencies()
适合:
- 一次性任务。
- 不需要 run 后手动检查 workspace。
- 不需要复用同一个 live session。
- 希望 Runner 自动创建和清理资源。
入门建议从这个模式开始。
developer-owned lifecycle
如果你需要自己控制 session 生命周期,可以先创建 session:
client = UnixLocalSandboxClient()
sandbox = await client.create(manifest=agent.default_manifest)
然后注入:
async with sandbox:
result = await Runner.run(
agent,
prompt,
run_config=RunConfig(
sandbox=SandboxRunConfig(session=sandbox),
),
)
此时 Runner 不会删除这个 live session。
适合:
- 一个 sandbox 连续跑多个
Runner.run()。 - run 后读取 workspace 文件做校验。
- 需要手动复制输出产物。
- 需要把同一个 workspace 暴露给多个 agent。
但这也意味着:
你要负责 session cleanup。
Runner 如何准备 SandboxAgent
SandboxRuntime.prepare_agent() 是 Runner 接入 sandbox 的关键入口。
普通 Agent 直接绑定 public agent:
if not isinstance(current_agent, SandboxAgent):
return bind_public_agent(current_agent)
SandboxAgent 则会:
- 获取并 clone capabilities。
- ensure sandbox session。
- 将 capabilities 绑定到 live session。
- 绑定
run_as用户。 - 处理输入上下文。
- 构造 execution agent。
- 把 public agent 和 execution agent 建立绑定。
源码片段:
prepared_capabilities = clone_capabilities(current_agent.capabilities)
session = await self._session_manager.ensure_session(...)
for capability in prepared_capabilities:
capability.bind(session)
clone 很重要。
Capability 会绑定 live session,不能跨并发 run 共享同一个可变对象。
SandboxAgent 不能并发复用
SandboxRuntimeSessionManager.acquire_agent() 有并发 guard:
if guard.active_runs > 0:
raise RuntimeError(
f"SandboxAgent {agent.name!r} cannot be reused concurrently across runs"
)
原因是:
prepared capability tools 和 session state 会绑定到一个 live run。
如果需要并发执行同一类任务,应构造多个 agent 实例,或者 clone agent,而不是把同一个 SandboxAgent 对象同时交给多个 run。
这是和普通无状态 Agent 使用体验不同的地方。
instructions 构造顺序
prepare_sandbox_agent() 会把 SandboxAgent clone 成 execution agent。
核心是构造新的 instructions:
instructions=build_sandbox_instructions(
base_instructions=agent.base_instructions,
additional_instructions=agent.instructions,
capabilities=capabilities,
manifest=manifest,
)
最终顺序是:
- SDK 默认 sandbox base prompt,或
base_instructions。 SandboxAgent.instructions。- capability instructions。
- remote mount policy instructions。
- 渲染后的 filesystem tree。
这解释了为什么通常不要轻易设置 base_instructions。
你一般只需要写 instructions,保留 SDK 默认 sandbox prompt。
filesystem tree 如何进入提示词
runtime_agent_preparation.py 会渲染 manifest:
tree = render_manifest_description(
root=manifest.root,
entries=manifest.validated_entries(),
depth=3,
)
然后拼成:
# Filesystem
You have access to a container with a filesystem. The filesystem layout is:
/workspace
├── repo/
└── task.md
这个文件树不是装饰。
它会让模型知道 workspace 初始布局,减少无意义的 ls 探索。
但模型仍然应该通过 shell 或 filesystem tools 读取真实文件内容,而不是只凭 tree 猜测。
默认 capabilities
Capabilities.default() 是:
class Capabilities:
@classmethod
def default(cls) -> list[Capability]:
return [Filesystem(), Shell(), Compaction()]
默认会提供:
Filesystem():apply_patch、view_image。Shell():exec_command,如果支持 PTY 则还有write_stdin。Compaction():长任务上下文压缩相关能力。
如果你显式传:
SandboxAgent(capabilities=[MyCapability()])
这会替换默认列表。
如果你想在默认能力上追加:
from agents.sandbox.capabilities import Capabilities
SandboxAgent(
capabilities=Capabilities.default() + [MyCapability()],
)
Shell capability
Shell 会暴露 shell 工具:
toolset = ShellToolSet(
exec_command=ExecCommandTool(session=self.session, user=self.run_as),
write_stdin=WriteStdinTool(session=self.session)
if self.session.supports_pty()
else None,
)
它还会注入说明:
Use exec_command for shell execution.
If available, use write_stdin to interact with running sessions.
Prefer rg and rg --files for text/file discovery when available.
所以 Sandbox Agent 的 shell tool 不是普通 Python 函数。
它是绑定到当前 SandboxSession 的工具。
Filesystem capability
Filesystem 会暴露:
view_image=ViewImageTool(session=self.session, user=self.run_as)
apply_patch=SandboxApplyPatchTool(session=self.session, user=self.run_as)
这两个工具也绑定 live session。
特别注意 apply_patch 的路径语义:
patch path 是相对 sandbox workspace root 的路径。
如果 repo 被放在 workspace 的 repo/ 下,那么补丁路径应该是:
repo/src/app.py
不是 shell 当前目录下的:
src/app.py
官方 coding example 也在 instructions 里专门提醒了这一点。
UnixLocalSandboxClient
UnixLocalSandboxClient 是本地开发最容易启动的 client。
它的特点是:
- 只支持 macOS / Linux。
- 不需要 Docker。
- Runner 创建 fresh session 时会使用临时目录作为 workspace root。
- 命令在主机上执行,但被限制在 workspace 相关路径策略内。
- 不支持 manifest users/groups,因为那会改动主机用户系统。
源码里如果 manifest root 是默认 /workspace,会替换成临时目录:
workspace_dir = tempfile.mkdtemp(prefix="sandbox-local-")
manifest = manifest.model_copy(
update={"root": workspace_dir},
deep=True,
)
delete 时,如果 workspace root 是 client 创建的,会 best-effort 删除临时目录。
入门时可以先用它验证 sandbox agent 逻辑。
但不要把它理解成强隔离安全容器。
DockerSandboxClient
Docker client 使用容器作为 backend。
创建时需要 options:
from docker import from_env as docker_from_env
from agents.sandbox.sandboxes.docker import (
DockerSandboxClient,
DockerSandboxClientOptions,
)
client = DockerSandboxClient(docker_from_env())
options = DockerSandboxClientOptions(image="python:3.14-slim")
放入 run config:
run_config = RunConfig(
sandbox=SandboxRunConfig(
client=client,
options=options,
)
)
Docker client 创建容器时会:
- 确认 image 是否存在,不存在则 pull。
- 创建长期保持运行的容器。
- 注入 manifest environment。
- 配置 exposed ports。
- 根据 mount entry 增加 Docker mounts 或 capability。
适合:
- 需要容器隔离。
- 需要固定 Python/系统依赖版本。
- 本地环境和生产环境需要一致。
- 任务可能运行不可信命令。
UnixLocal 与 Docker 的选择
入门可以这样选:
| 目标 | 推荐 client |
|---|---|
| 快速本地验证 | UnixLocalSandboxClient |
| 不想安装 Docker 依赖 | UnixLocalSandboxClient |
| 需要指定镜像 | DockerSandboxClient |
| 需要容器隔离 | DockerSandboxClient |
| 需要更接近生产环境 | Docker 或 hosted provider |
文档也明确建议:
Unix-local is the easiest way to start developing against a local filesystem.
Move to Docker or a hosted provider when you need stronger environment isolation.
所以第一个 demo 用 Unix local 是合理的。
真正承载生产任务时,应该按安全、依赖、成本和运维能力重新选择 backend。
用 LocalDir 检查当前仓库
假设你要让 Sandbox Agent 读取当前仓库并生成摘要:
from pathlib import Path
repo_dir = Path.cwd()
agent = SandboxAgent(
name="Repo reviewer",
instructions=(
"Inspect `repo/README.md` and source layout before answering. "
"Use shell commands to verify file names."
),
default_manifest=Manifest(
entries={
"repo": LocalDir(src=repo_dir),
}
),
)
运行:
result = await Runner.run(
agent,
"Summarize this repository and list three important directories.",
run_config=RunConfig(
sandbox=SandboxRunConfig(client=UnixLocalSandboxClient()),
),
)
模型看到的是 sandbox 内的:
repo/
不是主机上的真实绝对路径。
这能让 prompts 和工具调用更可移植。
让 Agent 执行只读命令
如果只希望 agent 做只读检查,可以在 instructions 里约束:
agent = SandboxAgent(
name="Read-only repo reviewer",
instructions=(
"Inspect files using read-only commands such as `rg`, `ls`, "
"`sed`, `head`, and `wc`. Do not edit files."
),
default_manifest=Manifest(
entries={"repo": LocalDir(src=repo_dir)}
),
)
但要注意:
instructions 是模型约束,不是强权限策略。
如果要强限制写入能力,不应只靠文字说明。
应进一步控制 capabilities、permissions、backend policy 或自定义工具。
第十七篇会继续展开 capabilities 和安全边界。
run_as 与 Permissions
run_as 控制 model-facing sandbox actions 的执行用户。
Manifest entries 可以带 Permissions。
文档示例里会创建用户:
analyst = User(name="analyst")
agent = SandboxAgent(
run_as=analyst,
default_manifest=Manifest(users=[analyst], entries={...}),
)
如果 run_as 指定的用户不在 manifest 中,runtime 会把它加入 effective manifest:
return manifest.model_copy(
update={"users": [*manifest.users, user]},
deep=True,
)
不过 UnixLocalSandboxSession 不支持 manifest users/groups。
源码会拒绝:
if self.state.manifest.users or self.state.manifest.groups:
raise ValueError(...)
所以要测试 users/groups 和文件权限行为,应优先使用 Docker 或 hosted backend。
snapshot 和 session_state 的区别
入门阶段只要先记住:
session_state 是后端连接状态。
snapshot 是 workspace 内容。
session_state 用于恢复某个 backend session。
例如 Docker 的 state 里有:
class DockerSandboxSessionState(SandboxSessionState):
image: str
container_id: str
snapshot 则用于把 workspace 内容保存下来,fresh session 再从这些文件恢复。
不要把二者混为一谈:
session_state更像“怎么连回原来的执行环境”。snapshot更像“用哪些文件重新铺一个 workspace”。
第十七篇再展开 resume、memory 和 snapshot。
错误边界和 cleanup
Sandbox runtime cleanup 需要释放:
- session。
- provider client 资源。
- Docker container 或本地临时目录。
- pre-stop hooks。
- snapshot persistence。
- session dependencies。
- SandboxAgent concurrency guard。
源码里 cleanup 即使失败,也会尽量释放 agent guard:
finally:
self._resources_by_agent.clear()
self._current_agent_id = None
self._release_agents()
这对长任务很重要。
否则一次失败可能导致 agent 对象永久处于 active 状态,后续 run 无法复用。
常见问题一:忘记传 RunConfig(sandbox=…)
如果直接运行:
await Runner.run(sandbox_agent, "do work")
会触发:
if isinstance(agent, SandboxAgent) and self._sandbox_config is None:
raise UserError(
"SandboxAgent execution requires `RunConfig(sandbox=...)`"
)
所以只要使用 SandboxAgent,就必须传:
RunConfig(sandbox=SandboxRunConfig(...))
这是最常见的入门错误。
常见问题二:以为 default_manifest 每次都会重置 workspace
default_manifest 只用于 fresh session。
如果你传的是:
SandboxRunConfig(session=sandbox)
Runner 会复用 live session。
已有 workspace 状态不会被 default_manifest 全量覆盖。
如果要强制从 manifest 开始,创建 fresh session,不要复用旧 session。
常见问题三:LocalDir 指向了错误路径
LocalDir(src=...) 是 SDK 主机路径。
它不是 sandbox 内路径。
例如:
Manifest(entries={"repo": LocalDir(src="./repo")})
含义是:
从 SDK 进程当前工作目录下的 ./repo 读取文件,
复制到 sandbox workspace 的 repo/ 下。
如果你把 sandbox 内路径误写到 src,materialization 会找不到主机路径。
常见问题四:apply_patch 路径少了 repo/
如果 manifest 是:
Manifest(entries={"repo": LocalDir(src="./repo")})
那么 workspace root 下有:
repo/
Filesystem capability 的 apply_patch 路径是 workspace-root-relative。
所以补丁应该指向:
repo/src/app.py
而不是:
src/app.py
这也是官方 coding example 在 instructions 中反复强调的点。
常见问题五:把 UnixLocal 当强隔离沙箱
UnixLocalSandboxClient 是本地开发工具。
它在主机临时 workspace 中运行命令。
虽然 SDK 做了 workspace path policy、extra path grants、symlink 检查等限制,但它不是容器化隔离边界。
如果你要运行风险更高的命令,或者需要更强隔离,应使用:
DockerSandboxClient。- hosted sandbox provider。
- 应用层额外权限控制和审计。
这不是实现细节,而是产品安全边界。
推荐入门调试顺序
当 Sandbox Agent 行为不符合预期时,可以按这个顺序查:
RunConfig是否包含SandboxRunConfig。SandboxRunConfig是client还是session。- 是否 fresh session。
- manifest 是否真的被应用。
- entry 目标路径是否 workspace-relative。
LocalDir.src是否是 host 上存在的路径。- capabilities 是否意外替换了默认能力。
- shell tool 是否能看到文件。
apply_patch路径是否带上 workspace 下的 repo 前缀。- cleanup 是否由 Runner 或应用明确负责。
这比直接猜模型行为更有效。
Sandbox Agent 的很多问题不是模型不聪明,而是 workspace contract 或 session source 写错。
和前几篇内容的连接
Sandbox Agent 复用了前面讲过的 Runner 架构。
| 前文主题 | Sandbox 中的体现 |
|---|---|
| Agent 定义 | SandboxAgent 继承 Agent |
| Runner runtime | 普通 Runner.run() 仍是入口 |
| Tools | capabilities 把 sandbox tools 注入 agent |
| Streaming | Runner.run_streamed() 可照常使用 |
| Handoff | outer runtime 仍负责 handoff |
| Tracing | sandbox 操作会产生 sandbox spans/events |
| RunState | 可携带 sandbox resume payload |
| ModelSettings | capability 可调整 sampling params |
关键差异在于:
普通 Agent 的状态主要在 run history。
SandboxAgent 还多了 live workspace 和执行环境状态。
因此设计 Sandbox Agent 时,必须同时考虑:
- 对话状态。
- workspace 状态。
- sandbox session 生命周期。
本篇小结
本篇从入门角度拆解了 Sandbox Agent 的核心链路。
SandboxAgent 仍然是普通 Agent,但新增了 default_manifest、capabilities、run_as 等 sandbox-specific 配置。
Manifest 声明 fresh session 的 workspace contract,entry 目标路径必须是 workspace-relative,LocalDir 和 LocalFile 则是 SDK 主机上的 source。
SandboxRunConfig 决定本次 run 从哪里拿 live sandbox session:注入 session、从 RunState 恢复、从显式 session_state 恢复,或用 client 创建 fresh session。
Runner 会在运行时 clone capabilities、绑定 live session、构造 sandbox instructions、注入 shell/filesystem tools,然后仍然通过普通 Runner.run() 推进。
入门建议先用 UnixLocalSandboxClient 跑通 workflow,再根据隔离、依赖和生产要求切换到 Docker 或 hosted sandbox provider。
最后要记住三条边界:
Manifest不等于 live workspace 的全量真相,它主要服务 fresh session。LocalDir.src是 host-side source,不是 sandbox 内路径。UnixLocalSandboxClient是快速本地开发路径,不是强隔离安全边界。
下一篇将继续进入 Sandbox Agent 进阶,重点看 capabilities、memory、远程 sandbox provider 和更细的安全边界。
更多推荐

所有评论(0)