
Agent 的代码执行沙箱怎么设计拆解 OpenRouter shell 工具与 Files API原文OpenRouter Blog - 《Shell server tool and Files API: a hosted sandboxed shell for any model》https://openrouter.ai/blog/announcements/shell-tool一、Agent 执行代码难的不是跑是在哪跑做 Agent 开发的人大多撞过同一堵墙模型明明能写出正确的脚本但你没地方让它跑。常见的绕法有三种。本地执行等于把宿主机交出去自己搭 Docker 沙箱隔离、网络、文件回收、成本归因全要自己写干脆不给终端那模型只能在文本里想象结果。三种都不算好答案。OpenRouter 在 9 月 8 日给出的答案是把它做成平台能力一个叫openrouter:shell的服务端工具加一套 Files API。任何在 OpenRouter 上、支持工具调用的模型都能在托管的 Linux 容器里执行命令并且能上传输入文件、取回产物。两者目前都在 beta 阶段。这篇不聊用起来爽不爽只拆机制工具怎么声明、容器怎么隔离、文件怎么进出、钱怎么算。这几件事才是接进生产前真正要判断的。二、三块拼图shell、container、Files API这个能力不是一个工具而是三个部件咬在一起。部件作用对应接口shell / bash在容器内执行命令回传标准输出、标准错误和退出码openrouter:shellResponses、Messagesopenrouter:bashMessagescontainer命令真正运行的隔离 Linux 环境承载网络策略与文件捕获通过工具的 environment 字段配置Files API工作区文件存储负责把输入送进去、把产物取出来/api/v1/files有一个设计细节值得单独拎出来说模型调用 shell 时会一次发出一批命令每条命令在容器里独立执行然后把标准输出、标准错误和退出码一起还给模型。这就是 Agent 能自我修正的物理基础——脚本解析 CSV 失败时它看得到报错能在回答你之前把脚本改好而不是硬着头皮输出一个错结果。三、最小可用调用把 shell 挂进 tools官方给的第一个例子很短curlhttps://openrouter.ai/api/v1/responses\-HAuthorization: Bearer$OPENROUTER_API_KEY\-HContent-Type: application/json\-d{ model: deepseek/deepseek-v4-pro-0813, input: Check the Python version, then write a script that prints the first 20 primes and run it., tools: [ { type: openrouter:shell, parameters: { engine: openrouter } } ] }参数逐个看model任意支持工具调用的模型。官方示例用的是 deepseek/deepseek-v4-pro-0813说明这条路不绑定某一家模型。input自然语言任务你不需要自己写命令。tools[].type工具类型这里就是 openrouter:shell。tools[].parameters.engine设为 openrouter表示强制在服务端沙箱执行。调用链是请求发出 → 模型判断需要终端并输出工具调用 → 命令在容器里执行 → 结果连同退出码回灌给模型 → 模型继续推理或再次调用 → 返回最终答案。OpenRouter 的日志页会把这个请求画成时间线模型轮次和沙箱运行各占一行各自带耗时和费用。排查到底是模型慢还是沙箱慢时这个视图很省事。还有两个容易踩的兼容性差异整理成表工具对齐的规范可用 API默认在哪执行openrouter:shellOpenAI 的 shell 工具Responses、MessagesOpenRouter 沙箱openrouter:bashAnthropic 的 bash 工具Messages你的应用本地也就是说bash 工具默认会让你自己的应用去跑命令想改成服务端执行必须显式把 engine 设成 openrouter。接 Anthropic Messages 规范的项目尤其要注意这个默认值。四、容器网络、文件、复用与寿命容器是隔离的 Linux 环境按工作区划分。四个可配置点决定了它能干什么、不能干什么。第一网络默认关闭。要装依赖就得开白名单{network_policy:{type:allowlist,allowed_domains:[pypi.org,files.pythonhosted.org]}}白名单里的主机只能走 80 和 443 端口不在白名单的域名会返回 HTTP 520而不是连接错误。这个细节很重要它让你能区分被策略拦了和网络真的不通。想完全放开就把 allowed_domains 写成星号通配。需要记住的是策略在容器启动后不能修改所以上线前要一次想清楚。第二文件捕获范围是固定的只有 /workspace/home 目录下的文件会被收集。每条 shell 结果还会返回本次命令新建或修改过的文件 id 列表前缀是 cfile_。第三容器默认不复用。一次请求一个干净容器如果请求里带了 session_id或者上一轮 shell 结果里有可识别的容器就会复用同一个。想手动指定可以在工具的 environment 字段里传 container_reference 加 container_id。第四寿命是硬编码的容器空闲 5 分钟后休眠不可配置。五、Files API文件怎么进、怎么出、怎么留Files API 是容器的配套存储解决输入从哪来、产物往哪去。上传输入文件curlhttps://openrouter.ai/api/v1/files\-HAuthorization: Bearer$OPENROUTER_API_KEY\-Ffiledata/sales.csv返回值里会有一个以 or_file_ 开头的文件 id。拿到 id 后在工具的 environment 里挂载它{type:openrouter:shell,parameters:{engine:openrouter,environment:{type:container_auto,file_ids:[or_file_011CNha8iCJcU1wXNR6q4V8w]}}}挂载后的行为有几个点必须知道挂进去的是可写副本每个容器最多挂 20 个文件。文件名是文件 id 的最后 8 个字符加原文件名。所以 data/sales.csv 用上面的 id 挂进去会变成 ~/NR6q4V8w-sales.csv。写脚本时不要硬编码原文件名这一点很容易踩。容器内的修改不会影响工作区里的原始文件。容器启动时只有你挂进去的文件不会有别的。把产物取回来用容器文件内容端点curlhttps://openrouter.ai/api/v1/containers/$CONTAINER_ID/files/$FILE_ID/content\-HAuthorization: Bearer$OPENROUTER_API_KEY\-ooutput.txt容器文件保留 30 天。要长期留存就把它提升进工作区curl-XPOSThttps://openrouter.ai/api/v1/containers/$CONTAINER_ID/files/$FILE_ID/promote\-HAuthorization: Bearer$OPENROUTER_API_KEY提升操作会把容器文件复制到工作区并返回一个新的 or_file_ id下次可以像普通上传一样挂载。这里有个反直觉的规则直接上传的文件不能下载从容器提升出来的文件才能下载。如果你需要把产物交付给用户路径只能是先提升、再取。六、多工具协作容器断网也能查资料server tool 是可以叠加的。下面这个例子让模型先联网搜索再把结果写进容器里的文件curlhttps://openrouter.ai/api/v1/responses\-HAuthorization: Bearer$OPENROUTER_API_KEY\-HContent-Type: application/json\-d{ model: deepseek/deepseek-v4-pro-0813, input: Look up the three biggest open-source AI releases this week, then write ~/out/releases.md with one paragraph each and a source link., tools: [ { type: openrouter:web_search }, { type: openrouter:shell, parameters: { engine: openrouter } } ] }生成的 ~/out/releases.md 会出现在 shell 结果的文件列表里再按上一节的方式下载即可。这个组合真正的价值在安全边界web search 在容器外运行所以容器可以一直保持没有网络的默认策略模型照样能把网上内容带进命令里。对需要严格管控出网的场景这比放开 egress 干净得多。七、计费与治理计费口径不复杂但要算对沙箱按活跃秒计费每秒 0.0001 美元从请求内第一条沙箱命令开始到最后一条命令结束。请求结束后的空闲时间不计费。请求启动冷容器时最低按 30 秒计费。连续多个请求共用同一容器时只有第一个付这个 30 秒。单个请求的成本等于 token 成本加沙箱时长日志时间线里各占一行便于做单请求归因。Files API 不单独收费但总存储上限是 10 GiB。治理侧server tool 默认全部开启。工作区管理员可以在 Server Tools 页面把某个工具切成 Blocked这个设置对该工作区的所有调用生效不管是 API key、chatroom 还是 preset。八、给 Agent 开发者的落地清单先定要不要出网再写 network_policy。它启动后不能改白名单比全放开安全得多。把模型能看见报错当成一等需求标准错误和退出码要原样回灌只喂标准输出会让 Agent 失去自我修正能力。会话内尽量复用容器session_id 或 container_id避免每轮冷启动都吃 30 秒最低计费。产物一律写在 /workspace/home 下否则不会被捕获。需要交付给用户的文件走提升流程不要指望 30 天的容器文件。目前是 beta接口可能变动正式接入前以官方文档为准。小结OpenRouter 这套设计值得学的不是给模型一个终端而是它把终端拆成了三层工具负责声明意图容器负责隔离与网络策略Files API 负责数据进出。三者边界清楚所以你能单独替换其中一层也能在断网容器里叠加联网工具。对 Agent 开发者来说判断一个沙箱能力能不能上生产看的从来不是它能不能跑 Python而是这四件事出网策略是否可控、报错是否回灌、产物是否可回收、成本是否可归因。这四条都能答上再谈接入。