首页
/ openai-agents-python 沙箱 Workspace Entries 完全指南:从 Dir/File 到多云 Mount 的清单化工作区构建

openai-agents-python 沙箱 Workspace Entries 完全指南:从 Dir/File 到多云 Mount 的清单化工作区构建

2026-09-09 21:51:54作者:庞眉杨Will

agents.sandbox.entries 是 openai-agents-python 沙箱体系中定义"工作区内容"的核心模块:一个 Manifest 通过一组 entry 声明沙箱启动时应当拥有的文件、目录、Git 仓库与远程存储挂载。本文将基于该模块的 API 参考文档与仓库源码,逐一剖析 DirFileGitRepoLocalDirLocalFileMountAzureBlobMountGCSMountR2MountS3MountS3FilesMountBoxMount 等全部成员的设计意图、字段语义与底层实现,帮助你精确构造可移植、可复现、安全可控的沙箱工作区。

Workspace Entries 在沙箱体系中的定位

沙箱 Agent 的核心价值在于"让模型在真实文件系统上工作"。在 沙箱指南 中,Manifest 被定义为"全新沙箱会话的工作区契约"——它声明了工作区 root、文件与目录、需要克隆的 Git 仓库、远程存储挂载、环境变量、用户与组,以及工作区之外的绝对路径访问授权。

这些声明逐条落在 Manifest.entries 上,而 entries 的值全部来自 agents.sandbox.entries 模块。参考文档 docs/ref/sandbox/entries.md 列出的 12 个公开成员正是该模块的全部"内容类型":

类别 Entry 用途
合成内容 DirFile 在沙箱内直接声明目录与二进制文件内容
本地来源 LocalDirLocalFile 把 SDK 宿主机器上的目录/文件实体化进沙箱
代码来源 GitRepo 按 tag / branch / commit 拉取远程仓库
远程存储 MountAzureBlobMountGCSMountR2MountS3MountS3FilesMountBoxMount 把对象存储/文件存储挂载进工作区

entry 的路径语义有一条铁律:Manifest 条目路径是工作区相对的,既不允许绝对路径,也不允许用 .. 逃逸出工作区(见 guide.md 的 Manifest 一节),这使得同一份清单可以在本地、Docker 与托管客户端之间保持可移植。

一切条目的基类:BaseEntry 与公共字段

所有 entry 都继承自 BaseEntry(定义于 src/agents/sandbox/entries/base.py),这是一个基于 pydantic 的抽象模型,其公共字段决定了每个条目在清单中的通用行为:

  • type: str:条目类型标识。每个子类都必须在 type 字段上给出非空字符串默认值,模块通过"子类注册表"(_subclass_registry)把字符串与类绑定;BaseEntry.parse() 在反序列化时按 type 字段分派到正确的子类,遇到未知类型会抛出包含全部已注册类型的 ValueError(见 base.py)。
  • description: str | None:条目的可选说明,默认 None
  • ephemeral: bool:是否视作临时内容。Mount 固定为 True——挂载是运行时附加的外部文件系统,不属于持久化工作区状态,因此快照与 persist_workspace() 都不会包含挂载内容(见 mounts/base.py 的注释说明)。
  • group: Group | User | None:把条目归属到某个清单用户或组,用于配合 run_as 实现文件级共享规则。
  • is_dir: bool:是否在沙箱文件系统中作为目录对待。DirLocalDirGitRepoMount 及其子类都覆盖为 True
  • permissions: Permissions:条目在沙箱内的文件权限。默认值是 owner 全部权限(FileMode.ALL),group 与 other 为 READ | EXEC(见 base.py),即"属主可读写执行、组与其他可读可执行"。

这些字段最终在 _apply_metadata() 中落盘:如果设置了 group,会先执行 chgrp,随后用八进制模式执行 chmod(见 base.py)。

Permissions 本身存储 owner / group / other 三组位标志以及是否为目录,支持直接构造、Permissions.from_str(...) 从模式字符串解析、Permissions.from_mode(...) 从 OS 模式派生。沙箱指南给出了一个"私有笔记"示例——owner 可读写、组和其他完全无权限:

from agents.sandbox import FileMode, Permissions
from agents.sandbox.entries import File

private_notes = File(
    content=b"internal notes",
    permissions=Permissions(
        owner=FileMode.READ | FileMode.WRITE,
        group=FileMode.NONE,
        other=FileMode.NONE,
    ),
)

合成内容:Dir 与 File

Dirartifacts.py)声明一个沙箱目录,type 固定为 "dir"。它的关键字段是 children: dict[str | Path, BaseEntry]——一个"路径片段 → 条目"的映射,可以在声明目录的同时内联其全部子内容。应用时先 mkdir(parents=True),再以批次方式逐个应用子条目,因此 Dir 天然支持嵌套结构,例如构建一个带任务说明与输出位置的目录树:

from agents.sandbox import Manifest
from agents.sandbox.entries import Dir, File

manifest = Manifest(
    entries={
        "repo": Dir(
            children={
                "task.md": File(content=b"# Task spec\nSummarize this workspace."),
                "src": Dir(children={"main.py": File(content=b"print('hello')")}),
            }
        ),
        "output": Dir(),
    }
)

Fileartifacts.py)声明一个内容直接内联的沙箱文件,type 固定为 "file",字段为 content: bytes。应用时通过会话的 write() 写入字节流,再套用权限元数据。适合小体积的合成输入、辅助脚本与说明文件;沙箱指南建议把长任务说明放进 repo/task.md 这类工作区文件,保持清单"窄而精"。

本地来源:LocalFile 与 LocalDir

当内容已经存在于 SDK 宿主机上时,使用 LocalFileartifacts.py)与 LocalDirartifacts.py)。

  • LocalFile.src: Path:宿主文件路径,实体化时以 1 MiB 分块流式计算 SHA-256 校验和,返回 MaterializedFile(path=dest, sha256=checksum) 作为实体化"回执"。
  • LocalDir.src: Path | None:宿主目录路径,src 为空时仅创建一个空目录。非空时递归列出目录下所有常规文件并拷贝进沙箱,每个文件同样附带 SHA-256 校验;多文件拷贝通过 gather_in_order(..., max_concurrency=session._max_local_dir_file_concurrency) 受控并发执行(artifacts.py),并发上限可由 SandboxConcurrencyLimits(local_dir_files=...) 调节。

src 的解析边界与 extra_path_grants

LocalFile.srcLocalDir.src 默认相对于 SDK 进程的工作目录解析,源路径必须位于该基准目录之内,除非被 extra_path_grants 显式授权。授权场景例如 /tmp 的临时工具输出、/opt/toolchain 的只读运行时、或需要实体化的技能目录:

from agents.sandbox import Manifest, SandboxPathGrant

manifest = Manifest(
    extra_path_grants=(
        SandboxPathGrant(path="/tmp"),
        SandboxPathGrant(path="/opt/toolchain", read_only=True),
    ),
)

注意:extra_path_grants 属于"受信任配置",不应从模型输出等不可信载荷加载;且被授权的路径只是运行时访问权,快照与 persist_workspace() 仍然只包含工作区根目录(guide.md 的 Manifest 一节)。

符号链接与 TOCTOU 防护

LocalDir 的实现值得关注其安全设计:它拒绝符号链接。遍历目录时若 lstat 发现符号链接,直接抛出 LocalDirReadError,并携带 "symlink_not_supported" 原因(artifacts.py)。

在支持 dir_fd 的平台上,它还采用"目录描述符固定"策略:以 os.O_RDONLY | O_DIRECTORY | O_NOFOLLOW 打开目录并逐级下钻,所有子路径操作都基于 fd 完成,从而把"路径在拷贝期间被替换"("path_changed_during_copy")与符号链接攻击的窗口压缩到最小(见 _open_local_dir_src_root_fd_list_local_dir_files_from_dir_fdartifacts.py)。在不支持 dir_fd 的平台上则回退到基于 lstat 的校验路径(_open_local_dir_file_for_copy_fallback)。

一个把本地数据室目录只读地暴露给分析员用户、同时提供可写输出目录的完整示例见 guide.md 的 Permissions 小节,其核心片段为:

from agents.sandbox import FileMode, Manifest, Permissions, SandboxAgent, SandboxRunConfig, User
from agents.sandbox.entries import Dir, LocalDir
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient
from agents.run import RunConfig
from agents import Runner

analyst = User(name="analyst")

agent = SandboxAgent(
    name="Dataroom analyst",
    instructions="Review the files in `dataroom/` and write findings to `output/`.",
    default_manifest=Manifest(
        users=[analyst],
        entries={
            "dataroom": LocalDir(
                src="./dataroom",
                group=analyst,
                permissions=Permissions(
                    owner=FileMode.READ | FileMode.EXEC,
                    group=FileMode.READ | FileMode.EXEC,
                    other=FileMode.NONE,
                ),
            ),
            "output": Dir(
                group=analyst,
                permissions=Permissions(
                    owner=FileMode.ALL,
                    group=FileMode.ALL,
                    other=FileMode.NONE,
                ),
            ),
        },
    ),
    run_as=analyst,
)

代码来源:GitRepo

GitRepoartifacts.py)把远程仓库拉取进工作区,字段包括:

  • host: str = "github.com":Git 托管主机,可覆盖为任意主机。
  • repo: str:形如 "owner/name" 的仓库路径(或主机特定路径)。
  • ref: str:tag、branch 或 commit SHA。
  • subpath: str | None:只取仓库中的子目录。

ref 的两种拉取路径

实现会先检查 ref 是否匹配 7–40 位十六进制(_COMMIT_REF_RE,见 artifacts.py):

  • 若是 commit ref:先 git init + remote add origin + git fetch --depth 1 --no-tags origin <sha> + git checkout --detach FETCH_HEAD;若该流程失败(例如服务端不支持按 SHA fetch),回退到普通命名 ref 克隆。
  • 若是 tag/branch:直接 git clone --depth 1 --no-tags --branch <ref> <url> <tmp>

克隆统一在临时目录 /tmp/sandbox-git-<session_id>-<uuid> 进行,随后用 cp -R 把(可选的)子路径内容复制到目标位置,最后清理临时目录。拉取失败抛 GitCloneError,复制失败抛 GitCopyError,而沙箱镜像缺少 git 时抛 GitMissingInImageError(此时错误上下文会附带镜像名,便于排查镜像选择问题)。

subpath 校验

subpath 不是简单字符串拼接,_validate_subpath()artifacts.py)会拒绝:空字符串(去空白后)、绝对路径(POSIX 或 Windows 盘符)、包含反斜杠的 Windows 风格路径、以及含 .. 的父目录穿越,全部以 GitSubpathError 报出。仓库内示例 examples/sandbox/docs/coding_task.py 展示了 GitRepoLocalDir 混用的真实清单。

远程存储:Mount 体系

Mountmounts/base.py)是"把外部存储暴露进沙箱工作区"的抽象基类。它的设计哲学是**"挂载什么"与"如何挂载"解耦**:

  • Mount 及其子类描述要挂载的存储元数据(桶名、账号、凭据、前缀等);
  • mount_strategy 描述沙箱后端如何把它挂上去。

mount_strategy:两种挂载策略

策略基类 MountStrategyBase 同样采用 type 分派注册机制("in_container" / "docker_volume"),并提供完整的生命周期钩子:validate_mountactivatedeactivateteardown_for_snapshotrestore_after_snapshot,以及 Docker 卷驱动的配置生成(mounts/base.py)。

  • InContainerMountStrategytype="in_container"):挂载由沙箱容器内部的命令完成,通过 pattern: MountPattern 指定具体技术。激活前会经过 validate_mount_activation_credential_boundary 的凭据边界校验(mounts/base.py)。
  • DockerVolumeMountStrategytype="docker_volume",字段 driverdriver_options):卷由宿主容器运行时在会话启动前附加,activate/deactivate 对不支持该能力的后端直接抛 MountConfigError,支持时则为空操作(因为挂载在会话开始前已完成)(mounts/base.py)。

每种 Mount 子类通过 supported_in_container_patterns()supported_docker_volume_drivers() 声明自己接受哪些模式/驱动;两者皆空时构造直接抛错,且 mount_strategy 与挂载类型不匹配会在校验阶段被拒绝。

Mount 的公共字段

  • mount_path: Path | None:挂载点在沙箱内的显式路径。绝对路径原样使用;相对路径按"工作区根目录内"解释,从而保证清单在不同后端(根前缀不同)之间可移植(见 _resolve_mount_pathmounts/base.py)。
  • read_only: bool = True:默认只读;读写挂载需显式置 False
  • ephemeral: bool = True:固定为临时,快照不包含挂载内容。
  • permissions:对 Mount 无效——若显式设置非默认权限会触发 UserWarning("Mount permissions are not enforced")并回退到默认值,访问控制应交给云厂商侧的权限配置(mounts/base.py)。

四种容器内挂载模式

MountPattern 的四种实现定义在 patterns.py

  1. FuseMountPatterntype="fuse"):基于 blobfuse2,用于 Azure Blob。字段包括 cache_typeblock_cache/file_cache,默认 block_cache)、cache_size_mbblock_cache_block_size_mb(默认 16)、block_cache_disk_timeout_sec(默认 3600)、file_cache_timeout_sec(默认 120)、attr/entry/negative-entry 缓存超时等。激活时在 .sandbox-blobfuse-cache/<session_id>/... 下放置缓存、在 .sandbox-blobfuse-config/ 下生成带 0600 权限的 YAML 配置(cache 目录须在挂载点之外),然后执行 blobfuse2 mountpatterns.py)。
  2. MountpointMountPatterntype="mountpoint"):基于 AWS 的 mount-s3。支持 --no-sign-request(无凭据时)、--read-only--region--endpoint-url--prefix;GCS 挂载会附加 --upload-checksums off(GCS XML API 不兼容默认校验和流程)。凭据通过写入 .sandbox-mountpoint-env/<session_id>/<hash>.envexport 脚本注入,错误信息中的敏感值会被替换为 REDACTEDpatterns.py)。
  3. RcloneMountPatterntype="rclone"):基于 rclone,支持 mode="fuse"rclone mount --daemon)与 mode="nfs"(先 rclone serve nfs 再内核 mount -t nfs,默认挂载选项 vers=4.1,tcp,port=2049,soft,timeo=50,retrans=1)。remote_name 未指定时按 sandbox_<kind>_<session_id> 生成确定性名称,保证多挂载互不污染;配置写入 .sandbox-rclone-config/<session_id>/ 下的独立文件(patterns.py)。
  4. S3FilesMountPatterntype="s3files"):基于 mount.s3files(S3 Express One Zone 文件系统),支持 mounttargetipaccesspointregion 等挂载选项,只读时附加 ropatterns.py)。

六大云存储 Provider

Provider 子类把云侧字段翻译成上述模式所需的运行时配置:

  • S3Mountproviders/s3.py):bucket(必填)、access_key_idsecret_access_keysession_tokenprefixregionendpoint_urls3_provider(默认 "AWS")。支持容器内 rclone/mountpoint 与 Docker 卷驱动 rclone/mountpoint。
  • AzureBlobMountproviders/azure_blob.py):account(必填)、container(必填)、endpointidentity_client_id(对应 AZURE_CLIENT_ID)、account_key(对应 AZURE_STORAGE_ACCOUNT_KEY)。支持 rclone 与 fuse(blobfuse2)模式。
  • R2Mountproviders/r2.py):Cloudflare R2 桶,容器内走 rclone。
  • GCSMount:Google Cloud Storage 桶,容器内支持 rclone 与 mountpoint 模式。
  • S3FilesMount:S3 Express One Zone 文件系统挂载,使用 s3files 模式。
  • BoxMount:Box 云盘挂载。

后端还可通过扩展挂载策略(如 examples/sandbox/extensions/cloudflare_runner.py 中的 CloudflareBucketMountStrategy、Modal 的 ModalCloudBucketMountStrategy)为 R2Mount/S3Mount/GCSMount 提供"原生云桶挂载",见 examples/sandbox/extensions/README.md

仓库中的读写挂载冒烟示例展示了真实参数组合,例如 S3(s3_mount_read_write.py):

from agents.sandbox.entries import DockerVolumeMountStrategy, S3Mount

mount = S3Mount(
    bucket=bucket,
    access_key_id=os.getenv("AWS_ACCESS_KEY_ID"),
    secret_access_key=os.getenv("AWS_SECRET_ACCESS_KEY"),
    session_token=os.getenv("AWS_SESSION_TOKEN"),
    prefix=os.getenv("S3_MOUNT_PREFIX"),
    region=os.getenv("AWS_REGION") or os.getenv("AWS_DEFAULT_REGION"),
    endpoint_url=os.getenv("S3_ENDPOINT_URL"),
    mount_strategy=DockerVolumeMountStrategy(driver="rclone"),
    read_only=False,
)

以及 Azure Blob(azure_mount_read_write.py):

from agents.sandbox.entries import AzureBlobMount, DockerVolumeMountStrategy

mount = AzureBlobMount(
    account=account,
    container=container,
    endpoint=endpoint,
    identity_client_id=identity_client_id,
    account_key=account_key,
    mount_strategy=DockerVolumeMountStrategy(driver="rclone"),
    read_only=False,
)

路径安全与校验:resolve_workspace_path

所有条目路径在实体化前都要经过 resolve_workspace_path()base.py)的强校验,这是工作区可移植性与安全性的底层保障:

  • Windows 绝对路径(如 C:\...)直接拒绝,原因标记为 "absolute"
  • 绝对路径默认拒绝;allow_absolute_within_root=True 时允许"工作区根内的绝对路径",但仍要求规范化后落在根内,且宿主侧若存在该路径还需通过 resolve() 相对性检查(防符号链接逃逸);
  • 相对路径中含 .. 片段一律拒绝("escape_root"),确保任何条目都无法逃出工作区边界;
  • 校验失败统一抛 InvalidManifestPathError(定义于 errors.py),错误信息携带具体的拒绝原因。

Manifest 组装(manifest.py)在解析 entries 时即调用该函数,同时为远程挂载维护默认命令白名单(DEFAULT_REMOTE_MOUNT_COMMAND_ALLOWLIST,含 lsfindcatgrepcp 等只读/常规命令,见 manifest.py)。

与 Manifest、快照和生命周期的联动

理解 entries 还需看到它上下游的配合:

  • 快照语义Mount 恒为 ephemeral=TrueLocalDir/GitRepo 等实体化内容则属于工作区根内的持久状态,会被快照与 persist_workspace() 收录;挂载点与挂载生成的 .sandbox-* 内部目录通过 register_persist_workspace_skip_path() 排除在外(见 patterns.py 中各模式的实现)。
  • 资源控制:大清单或大目录拷贝可通过 SandboxConcurrencyLimits(manifest_entries=..., local_dir_files=...) 收紧实体化并行度,任一值设为 None 即禁用对应限制(guide.md 的 SandboxRunConfig 一节)。
  • 能力注入:沙箱 capabilities(如 LocalDirLazySkillSource(source=LocalDir(src=...)))在运行前把技能目录实体化进工作区——此时 LocalDir.src 指向 SDK 宿主上的目录,而 skills_path 才是沙箱内的目标位置(guide.md 的 Capabilities 一节)。

参考资源

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
docsdocs
暂无描述
Markdown
899
5.83 K
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.04 K
525