ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

openai-agents-python 沙箱运行时边界:所有权、会话来源、信任边界与清理语义全解析

openai-agents-python 沙箱运行时边界:所有权、会话来源、信任边界与清理语义全解析 openai-agents-python 沙箱运行时边界所有权、会话来源、信任边界与清理语义全解析【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python导读本文以 openai-agents-python 仓库中沙箱Sandbox运行时边界设计文档为骨架系统拆解SandboxAgent与沙箱会话之间的职责划分谁拥有沙箱会话的生命周期、会话从何处来、代理如何被准备、路径与凭据的信任边界如何划定、错误与挂载如何收敛。读完本文你将掌握沙箱运行时的内部契约对应 .agents/references/sandbox-runtime-boundary.md能够安全地在自己的应用中注入、恢复、快照或并发使用沙箱会话并能基于 tests/sandbox/test_runtime.py 等测试用例验证行为是否符合预期。一、运行时所有权外层 Runner 与沙箱会话的分层契约沙箱运行时边界的第一条原则是分层所有权不要把某一层的生命周期挪到另一层除非你同时为两层都定义好 resume恢复与 cleanup清理行为。从源码看这个分层被实现在两个核心类中外层Runner负责 agent 回合turns、审批approvals、handoff、tracing、会话历史与RunState沙箱会话BaseSandboxSession负责执行环境、工作区、进程、挂载与 provider 相关的连接状态。对应实现位于 src/agents/sandbox/runtime.py 与 src/agents/sandbox/runtime_session_manager.py# runtime.py节选 class SandboxRuntime(Generic[TContext]): def __init__(self, *, starting_agent, run_config, rollout_idNone, run_stateNone) - None: self._session_manager SandboxRuntimeSessionManager( starting_agentstarting_agent, sandbox_configself._sandbox_config, run_staterun_state, )SandboxRuntime只做代理准备与内存结果入队而会话的创建、恢复、清理全部委托给SandboxRuntimeSessionManager。这正是文档所言沙箱会话拥有命令、文件变化与环境隔离外层运行时拥有审批、追踪与恢复所需状态这一核心模型的落地。1.1 注入会话是调用方拥有的当调用方通过SandboxRunConfig(session...)注入一个已经创建的 live 会话时Runner 可以配置并使用它但不得删除或完全拆除它。在_create_resources中可以看到注入路径的显式处理# runtime_session_manager.py节选 if sandbox_config.session is not None: self._configure_session(sandbox_config.session, ...) ... return _SandboxSessionResources( sessionsandbox_config.session, clientNone, owns_sessionFalse, # 调用方拥有Runner 不清理 )owns_sessionFalse意味着_SandboxSessionResources.cleanup()会直接短路返回if not self._owns_session: return因此async with sandbox:退出时由调用方自己执行aclose()完成全量清理。1.2 Runner 创建的会话由 Runner 拥有当会话由SandboxRunConfig.client创建或恢复时它是 Runner 拥有的。_SandboxSessionResources.cleanup()的完整顺序见 runtime_session_manager.py为运行 pre-stop hooksrun_pre_stop_hooks()调用stop()持久化 snapshot 支撑的工作区状态shutdown()关闭沙箱通过client.delete(session)删除 provider 资源当 client 存在且会话类型为SandboxSession时_aclose_dependencies()关闭会话级依赖。关键语义清理必须幂等self._cleaned标志保证同一资源只清理一次且全程由asyncio.Lock串行化避免并发aclose()竞态清理失败也必须释放并发守卫cleanup()中的finally块总会执行self._release_agents()即使 pre-stop hook、stop 或 persistence 失败也会把SandboxAgent的active_runs计数复位guard.active_runs max(0, guard.active_runs - 1)见 tests/sandbox/test_runtime.py 中的test_runner_owned_cleanup_redacts_pre_stop_hook_failureL592等用例。1.3SandboxAgent不能跨 run 并发复用一个SandboxAgent实例在同一时刻只能绑定一个 live run因为准备好的 capability 工具与会话状态都与该 run 绑定。acquire_agent()用实例上的_sandbox_concurrency_guard实现互斥# runtime_session_manager.py节选 guard getattr(agent, _sandbox_concurrency_guard, None) if guard is None: guard _SandboxConcurrencyGuard() agent._sandbox_concurrency_guard guard with guard.lock: if guard.active_runs 0: raise RuntimeError( fSandboxAgent {agent.name!r} cannot be reused concurrently across runs ) guard.active_runs 1因此并发工作应通过agent.clone()或直接构造新的SandboxAgent实例来完成。二、会话来源与保存状态解析顺序与语义区分2.1 四步解析顺序会话来源按固定优先级解析对应 docs/sandbox/guide.md 中的SandboxRunConfig说明与_create_resources实现注入的 live 会话run_config.sandbox.session直接复用RunState携带的可恢复沙箱状态_resume_state_payload_for_agent()从run_state._sandbox中按 resume key 取回序列化状态显式SandboxRunConfig.session_stateclient.deserialize_session_state(explicit_state)后client.resume(...)新建会话以run_config.sandbox.manifest或agent.default_manifest为输入调用client.create(...)。Manifest 与 snapshot 输入只用于播种全新会话不会覆盖注入或恢复的工作区——这正是文档强调Manifest 是 fresh-session 的工作区契约而不是每个 live 沙箱的完整真相来源的原因。2.2 三类状态不可混用状态载体含义用途RunState的 sandbox payloadRunner 管理的序列化沙箱状态含backend_id、current_agent_key、session_state、sessions_by_agent跨 run 自动续接 Runner 管理的流程SandboxRunConfig.session_state显式序列化的 provider 连接/会话状态在RunState之外自己持久化状态时直接恢复snapshot/SnapshotSpec保存的工作区内容播种全新沙箱会话的文件与工件snapshot 代表保存的工作区内容与 provider 的会话状态不可互换。serialize_resume_state()runtime_session_manager.py只在 stop-time 持久化完成之后才序列化 Runner 拥有的会话这样后续 resume 时后端存活就直接重连reattach后端不存活则从保存的 snapshot 重建工作区。2.3 重复 agent 名称下的稳定 resume 身份Handoff 图允许出现重名 agent而对象身份id()是进程局部的序列化状态需要稳定 key 与显式的 current-agent 选择。SandboxRuntimeSessionManager用_stable_resume_keys_by_agent_id_allocate_unique_agent_identity为每个 agent 分配不冲突的 resume key并用current_agent_key记录当前活动 agent。相关测试覆盖了这些边界test_runner_serializes_unique_sandbox_resume_keys_for_duplicate_agent_namestest_runtime.pytest_runner_restores_duplicate_name_sandbox_sessions_after_json_roundtriptest_runtime.pytest_session_manager_reserves_current_duplicate_resume_key_for_current_agenttest_runtime.py三、Agent 准备从克隆到绑定的五步流程3.1 每次 run 克隆 capability 实例Capability 对象是可变的持有self.session、采样设置等跨 run 复用会泄漏工具、采样设置或会话引用。因此每次准备都执行clone_capabilities()runtime_agent_preparation.pydef clone_capabilities(capabilities: Sequence[Capability]) - list[Capability]: return [capability.clone() for capability in capabilities]随后在 runtime.py 中把克隆绑定到 live 会话for capability in prepared_capabilities: capability.bind(session) capability.bind_workspace_scope(self._workspace_scope) _bind_capability_run_as(prepared_capabilities, run_as)绑定发生在上下文处理context processing之前这样 capability 在转换输入时可以安全地检查self.session。3.2 先验证依赖再暴露工具prepare_sandbox_agent()runtime_agent_preparation.py在构造工具列表前先校验 capability 依赖available_capability_types {capability.type for capability in capabilities} for capability in capabilities: required_capability_types capability.required_capability_types() missing_capability_types required_capability_types - available_capability_types if missing_capability_types: raise UserError(f{type(capability).__name__} requires missing capabilities: {missing})这样确保 capability 工具构造、指令片段、输入处理与采样调整使用的是同一套有效 capability 集。例如内置Memorycapability 要求ShellFilesystem提供apply_patch与view_image详见 docs/sandbox/guide.md 的 capabilities 表格。3.3 指令的固定拼接顺序最终指令按文档化顺序构建build_sandbox_instructions()runtime_agent_preparation.py的实现顺序与文档完全一致SDK 默认沙箱 base promptagents.sandbox.instructions.prompt.md或base_instructions显式替换instructions作为 Agent instructions 小节追加各 capability 的指令片段Sandbox capability instructionsremote-mount 策略文本build_remote_mount_policy_instructions(manifest)对应 Sandbox remote mount policy渲染后的文件系统树# Filesystem 小节render_manifest_description深度为 3。注意resolve_instructions同时支持字符串与 callable异步/同步均可且动态指令与 hooks 通过get_public_agent(current_agent)观察公开的SandboxAgent而不是内部克隆——prepare_sandbox_agent末尾的set_public_agent(prepared_agent, agent)建立了这条从克隆回指公开 agent 的链接。3.4 Handoff 与嵌套 run 的边界Handoff 停留在外层 run loop切换的是下一个回合由哪个 agent 执行并为该沙箱 agent 选择另一个 agent 绑定的沙箱会话不会产生嵌套 runAgent.as_tool()嵌套 run拥有自己的嵌套 runner 与沙箱生命周期、自己的max_turns与审批流从外层视角只算一次工具调用。四、文件系统信任边界POSIX 路径、宿主转换与归档防护4.1 沙箱内一律按 POSIX 路径对待无论宿主操作系统是 Windows 还是 macOS/Linux沙箱内可见的每个路径都必须视为 POSIX 路径。严禁用str(Path(...))或str(PurePath(...))来生成、校验、比较或序列化沙箱路径——这些调用在 Windows 上会输出反斜杠。应使用PurePath.as_posix()或 workspace_paths.py 中的规范辅助函数如coerce_posix_path()def coerce_posix_path(path: str | PurePath) - PurePosixPath: Return a POSIX-flavored path for sandbox filesystem paths. if isinstance(path, PurePath): path path.as_posix() else: path path.replace(\\, /) return PurePosixPath(path)4.2 类型化路径对象与原始字符串的信任区分信任边界必须区分两类输入类型化的Path/PurePath包括原生的 WindowsPath/PureWindowsPath可转换为 POSIX 沙箱表示windows_absolute_path()用于识别 Windows 绝对路径语法workspace_paths.py含反斜杠的原始字符串当公共契约要求显式 POSIX 语法时可能仍需要被拒绝。不要用静默规范化所有字符串来掩盖输入校验失败。例如normalize_sandbox_cwd()对字符串中的\直接抛错sandbox.cwd must use POSIX path separators而SandboxWorkspaceScope承载的是模型可见的相对路径基准cwd为空表示工作区根。从 docs/sandbox/guide.md 可知SandboxRunConfig.cwd只改变exec_command、view_image、apply_patch等内置工具的相对路径解析不改变Manifest.root或会话底层工作区边界。4.3 宿主文件系统转换只在显式边界进行解析 manifest、挂载目标、归档排除、snapshot、grant 或 provider 路径的代码不得让宿主的Path实现改变沙箱路径的身份。WorkspacePathPolicyworkspace_paths.py 起集中了这一职责absolute_workspace_path()/normalize_path()/normalize_sandbox_path()校验路径落在工作区根或 extra grant 之下越界抛InvalidManifestPathError_raise_if_read_only_grant()对只读 grant 的写操作抛WorkspaceArchiveWriteErrorresolve_symlinks仅在沙箱工作区是真实本地宿主目录如UnixLocalSandboxSession时启用Docker/远程会话则走 POSIX 校验路径。4.4LocalFile/LocalDir信任基目录 使用期校验LocalFile与LocalDir是宿主侧输入。其约束为默认基于 SDK 进程工作目录解析srcsrc必须留在该基目录内除非被extra_path_grants覆盖基目录之外的访问要求应用显式控制的extra_path_grants拒绝试图授权自身宿主访问的不可信 manifest。并且必须在使用期materialization 时而非仅解析 manifest 时校验防御符号链接源、父目录被替换、平台路径别名、以及校验与解包之间成员含义发生变化的归档。对应实现位于 src/agents/sandbox/materialization.py测试见 tests/sandbox/test_entries.py如test_local_file_rejects_symlinked_source_ancestorsL388与 tests/sandbox/test_docker.py如test_docker_workspace_file_ops_reject_symlink_escapeL1646。4.5 归档解包防护归档解包src/agents/sandbox/session/archive_extraction.py在写入前必须拒绝路径穿越traversal如..逃逸测试test_apply_patch_rejects_escape_root_path不安全的链接symlink escape不支持的成员类型并强制成员数、字节数与解包后大小限制且不物化无界成员列表。资源阈值由SandboxRunConfig.archive_limitsSandboxArchiveLimits(max_input_bytes..., max_extracted_bytes..., max_members...)控制见 src/agents/sandbox/runtime_session_manager.py 与 docs/sandbox/guide.md 的 Materialization controls 一节。4.6 路径授权与凭据不落入持久化extra_path_grants是运行时访问不是持久化工作区内容snapshot 与persist_workspace()只包含工作区根不包含任意授予路径挂载或 provider 的凭据必须留在所属 adapter 内不得出现在生成的 shell 命令、模型可见的错误、日志或序列化沙箱状态中。SandboxPathGrantworkspace_paths.py对path与host_path都有严格校验拒绝文件系统根_raise_if_filesystem_root、拒绝 UNC/设备路径、拒绝含..的 host_path、host_path配置时path必须为 POSIX 绝对路径。Docker 支持host_path把宿主路径映射为容器内不同的 POSIX 路径而UnixLocalSandboxClient只支持同路径 grant详见 docs/sandbox/clients.md。五、Provider 与错误边界归一化、可重试性与部分启动失败清理5.1 错误归一化但保留诊断细节后端失败应归一化为沙箱错误但不得丢弃诊断所需的 provider 细节可重试性retryability应在错误产生时显式保留而不是事后靠匹配错误消息字符串推断。redact_mount_error_data装饰器runtime_session_manager.py在向上抛出前对挂载错误数据做脱敏同时保留错误结构。5.2 可移植路径与宿主路径分离可移植的沙箱路径、宿主文件系统路径、provider 标识符三者必须分离。转换只属于后端或 materialization 边界不进入面向 agent 的工具。WorkspacePathPolicy的这一设计使同一套SandboxAgent定义可以无缝切换UnixLocalSandboxClient、DockerSandboxClient或托管 providerdocs/sandbox/clients.md 的决策表。5.3 部分启动失败也要清理临时克隆、挂载、sink 与依赖资源不仅要在正常关闭时清理在部分启动失败时也要清理。cleanup()的 finally 块保证资源映射清空、current-agent 复位、并发守卫释放多个资源逐个清理时首个异常被记录但其余资源仍会继续清理最终统一抛出首个错误见_SandboxSessionResources.cleanup与SandboxRuntimeSessionManager.cleanup的错误聚合逻辑测试test_runner_owned_cleanup_redacts_client_delete_failure覆盖了 delete 失败路径。5.4 有界输出与私有运行时元数据Capability 工具应上报有界输出并保留 provider 的退出状态或结构化错误数据但不向模型暴露私有运行时元数据如宿主路径、凭据、内部连接信息。六、远程挂载的简洁性边界默认单一生命周期6.1 默认只支持一种窄生命周期远程挂载默认收敛到一种窄生命周期在沙箱创建时声明挂载挂载内容保持在工作区持久化之外snapshot / persist 流程会 detach 或跳过挂载路径关闭时卸载挂载。当 tar 持久化或 hydration 需要 detach 挂载时操作完成后必须立即恢复。挂载凭据必须始终是受信任的 live 配置不得从序列化会话状态重建。6.2 动态挂载变更属于 opt-in provider 能力动态挂载变更、native-snapshot 支撑的挂载、可恢复挂载都应是 opt-in 的 provider 能力而非默认需求。如果特权挂载转换变得模糊不清正确做法是停止沙箱而不是引入协调/恢复状态机。除非 provider 暴露了可信原语、且变更得到聚焦的 provider 证据支持否则不要添加凭据解析器刷新循环refresh loops持久化挂载注册表动态挂载 API。6.3 Vercel S3 adapter 的边界声明Provider adapter 可以有意识地支持更窄的生命周期但应在实施该策略的 adapter 状态旁明确记录边界避免后续维护者把有意的排除误认为未完成功能。Vercel S3 adaptersrc/agents/extensions/sandbox/vercel/mounts.py 与 src/agents/extensions/sandbox/vercel/sandbox.py遵循 create-time-only 形式受信任的挂载配置仅存在于 live 会话中含挂载的会话不能恢复resume挂载拓扑创建后不能改变。这在 docs/sandbox/clients.md 的托管平台挂载表中有明确对应VercelSandboxClient仅支持 create-time-only 的 S3/S3-compatible 挂载内联凭据需要allow_s3_credential_exposureTrue。6.4 凭据暴露的显式确认机制对于需要在模型可控的沙箱容器内运行挂载助手的场景SDK 要求受信任的应用代码显式确认凭据暴露且确认是运行时专用、不序列化的# 挂载级值如内联访问密钥 manifest manifest.with_in_container_mount_credential_exposure_acknowledged(data) # 更宽泛的权限如托管/工作负载身份与外部凭据文件 manifest manifest.with_in_container_mount_broad_credential_exposure_acknowledged(data)FuseMountPatternblobfuse2发现环境 Azure 权限与S3FilesMountPatternmount.s3files使用环境 IAM 权限都需要 broad 确认。恢复含挂载的会话时SDK 只在当前受信任 manifest 与持久化状态拥有完全相同的无凭据挂载拓扑时恢复凭据缺失或不匹配会导致 resume 在沙箱启动前失败——序列化状态本身永远不授予权限。七、回归审查清单可执行的验证步骤文档给出的 Review Checklist 本身就是一份可操作的安全与正确性清单结合源码可映射为以下验证动作命名每个资源的 owner每个 live 会话、provider client、挂载、进程、capability、临时资源都必须有明确 owner注入会话 → 调用方client 创建/恢复的会话 → Runner。分别测试五条会话路径注入injected、恢复resumed、显式状态explicitsession_state、快照播种snapshot-seeded、全新会话fresh。SandboxRuntimeSessionManager._create_resources的分支结构runtime_session_manager.py就是这五条路径的直接映射。验证会话映射在复杂场景下保持正确handoff、重复 agent 名称、中断恢复、清理失败。对应测试如test_runner_resumed_handoff_materializes_manifest_for_new_sandbox_agenttest_runtime.py、test_runner_restores_duplicate_name_sandbox_sessions_after_json_roundtriptest_runtime.py。在适用平台测试路径与归档边界宿主路径、符号链接、穿越、归档限制、凭据脱敏。归档相关测试见 tests/sandbox/test_extract.py路径安全测试见 tests/sandbox/test_entries.py、tests/sandbox/test_apply_patch.py 与 tests/sandbox/test_docker.py。走公开Runner路径做端到端验证让 agent 准备、capability 绑定、持久化与清理一起执行而不是只测内部组件。对应测试如test_runner_persists_workspace_and_tool_choice_state_across_sandbox_resumetest_runtime.py、test_unix_local_runner_cleanup_preserves_resumed_caller_owned_workspace_roottest_runtime.py。每条沙箱路径校验/规范化/比较/序列化都要测PureWindowsPath输入在每个宿主上测试并确认原始反斜杠字符串保留其预期的校验行为。源码证据coerce_posix_path、windows_absolute_path与normalize_sandbox_cwd对\的拒绝逻辑workspace_paths.py测试证据test_exec_command_tool_normalizes_raw_backslashes_before_workspace_scopetests/sandbox/capabilities/test_shell_capability.py、test_apply_patch_normalizes_backslashes_in_string_pathtests/sandbox/test_apply_patch.py。八、延伸阅读沙箱代理完整指南SandboxAgent、Manifest、capabilities、生命周期与常见模式沙箱客户端选择本地、Docker、托管平台与挂载策略沙箱运行时实现代理准备、capability 绑定、清理编排会话管理器实现所有权、resume key、并发守卫、清理语义路径策略实现POSIX 转换、grant 校验、工作区边界运行时测试 与 会话状态往返测试所有权、重复名称、恢复与清理的回归验证。【免费下载链接】openai-agents-pythonA lightweight, powerful framework for multi-agent workflows项目地址: https://gitcode.com/GitHub_Trending/op/openai-agents-python创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表