本篇导读

前十五篇已经覆盖了 SDK 的文本、多 Agent、工具、MCP、Realtime 和 Voice 能力。

这一篇进入 Sandbox Agent。

Sandbox Agent 的目标不是让模型“知道更多文件名”,而是给模型一个可以工作的文件系统和执行环境:

用户任务
    -> Runner
    -> SandboxAgent
    -> sandbox session
    -> 文件读取、补丁、命令执行、产物生成
    -> 最终回答

它适合这类任务:

  1. 检查一个代码仓库并修复问题。
  2. 在隔离 workspace 中运行测试。
  3. 读取大量本地文件后生成报告。
  4. 处理文档包、数据包和输出产物。
  5. 让长任务跨 run 保留 workspace 状态。

本篇重点回答八个问题:

  1. SandboxAgent 和普通 Agent 有什么区别。
  2. Manifest 如何声明 fresh sandbox workspace。
  3. FileDirLocalFileLocalDirGitRepo 分别适合什么场景。
  4. SandboxRunConfig 如何决定 session 来源。
  5. Runner 如何准备 sandbox agent、绑定 capabilities 和构造提示词。
  6. UnixLocalSandboxClientDockerSandboxClient 的差异是什么。
  7. 什么时候让 Runner 托管生命周期,什么时候自己创建 live session。
  8. 入门阶段需要避开的安全边界和路径边界是什么。

第十六篇关注的源码入口

这一篇主要看这些文件:

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

最关键的是:

  1. sandbox_agent.py:定义 SandboxAgent
  2. manifest.py:定义 workspace contract。
  3. entries/artifacts.py:定义 file、dir、local dir、git repo 等 entry。
  4. runtime.py:Runner 侧 sandbox runtime 接入点。
  5. runtime_session_manager.py:决定 session 注入、恢复或创建。
  6. runtime_agent_preparation.py:准备 instructions、tools 和 capabilities。
  7. unix_local.py:本地 Unix sandbox client。
  8. 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 的能力:

  1. instructions
  2. tools
  3. handoffs
  4. mcp_servers
  5. model_settings
  6. output_type
  7. guardrails。
  8. 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 会:

  1. 通过 client 创建 sandbox session。
  2. 启动 session。
  3. 应用 manifest。
  4. 准备 agent。
  5. 绑定 sandbox tools。
  6. 执行 run。
  7. 持久化 snapshot。
  8. 停止并清理 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()

适合:

  1. 一次性任务。
  2. 不需要 run 后手动检查 workspace。
  3. 不需要复用同一个 live session。
  4. 希望 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。

适合:

  1. 一个 sandbox 连续跑多个 Runner.run()
  2. run 后读取 workspace 文件做校验。
  3. 需要手动复制输出产物。
  4. 需要把同一个 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 则会:

  1. 获取并 clone capabilities。
  2. ensure sandbox session。
  3. 将 capabilities 绑定到 live session。
  4. 绑定 run_as 用户。
  5. 处理输入上下文。
  6. 构造 execution agent。
  7. 把 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,
)

最终顺序是:

  1. SDK 默认 sandbox base prompt,或 base_instructions
  2. SandboxAgent.instructions
  3. capability instructions。
  4. remote mount policy instructions。
  5. 渲染后的 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()]

默认会提供:

  1. Filesystem()apply_patchview_image
  2. Shell()exec_command,如果支持 PTY 则还有 write_stdin
  3. 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。

它的特点是:

  1. 只支持 macOS / Linux。
  2. 不需要 Docker。
  3. Runner 创建 fresh session 时会使用临时目录作为 workspace root。
  4. 命令在主机上执行,但被限制在 workspace 相关路径策略内。
  5. 不支持 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 创建容器时会:

  1. 确认 image 是否存在,不存在则 pull。
  2. 创建长期保持运行的容器。
  3. 注入 manifest environment。
  4. 配置 exposed ports。
  5. 根据 mount entry 增加 Docker mounts 或 capability。

适合:

  1. 需要容器隔离。
  2. 需要固定 Python/系统依赖版本。
  3. 本地环境和生产环境需要一致。
  4. 任务可能运行不可信命令。

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 再从这些文件恢复。

不要把二者混为一谈:

  1. session_state 更像“怎么连回原来的执行环境”。
  2. snapshot 更像“用哪些文件重新铺一个 workspace”。

第十七篇再展开 resume、memory 和 snapshot。

错误边界和 cleanup

Sandbox runtime cleanup 需要释放:

  1. session。
  2. provider client 资源。
  3. Docker container 或本地临时目录。
  4. pre-stop hooks。
  5. snapshot persistence。
  6. session dependencies。
  7. 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 检查等限制,但它不是容器化隔离边界。

如果你要运行风险更高的命令,或者需要更强隔离,应使用:

  1. DockerSandboxClient
  2. hosted sandbox provider。
  3. 应用层额外权限控制和审计。

这不是实现细节,而是产品安全边界。

推荐入门调试顺序

当 Sandbox Agent 行为不符合预期时,可以按这个顺序查:

  1. RunConfig 是否包含 SandboxRunConfig
  2. SandboxRunConfigclient 还是 session
  3. 是否 fresh session。
  4. manifest 是否真的被应用。
  5. entry 目标路径是否 workspace-relative。
  6. LocalDir.src 是否是 host 上存在的路径。
  7. capabilities 是否意外替换了默认能力。
  8. shell tool 是否能看到文件。
  9. apply_patch 路径是否带上 workspace 下的 repo 前缀。
  10. 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 时,必须同时考虑:

  1. 对话状态。
  2. workspace 状态。
  3. sandbox session 生命周期。

本篇小结

本篇从入门角度拆解了 Sandbox Agent 的核心链路。

SandboxAgent 仍然是普通 Agent,但新增了 default_manifestcapabilitiesrun_as 等 sandbox-specific 配置。

Manifest 声明 fresh session 的 workspace contract,entry 目标路径必须是 workspace-relative,LocalDirLocalFile 则是 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。

最后要记住三条边界:

  1. Manifest 不等于 live workspace 的全量真相,它主要服务 fresh session。
  2. LocalDir.src 是 host-side source,不是 sandbox 内路径。
  3. UnixLocalSandboxClient 是快速本地开发路径,不是强隔离安全边界。

下一篇将继续进入 Sandbox Agent 进阶,重点看 capabilities、memory、远程 sandbox provider 和更细的安全边界。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐