Running Untrusted Agent Code Without a Sandbox在没有沙盒的情况下运行不受信任的智能体代码

Hunter Lovell
June 30, 2026
min
Go back to blog

We recently introduced dynamic subagents in Deep Agents: instead of dispatching subagents one tool call at a time, we let the agent write a short script that orchestrates them. That script runs in a code interpreter, where agents write and execute code.我们最近在 Deep Agents 中引入了动态子智能体:智能体不再一次只调用一个工具,而是编写一个简短的脚本来编排它们。该脚本在代码解释器中运行,智能体可以在其中编写和执行代码。

It's a powerful pattern, but it rests on something deceptively hard:这是一种强大的模式,但它建立在一个看似简单实则困难的问题之上:

It's hard to run untrusted code securely and reliably.安全且可靠地运行不受信任的代码非常困难。

Running untrusted code is a well-studied problem. Running code written by an agent influenced by untrusted input isn't. Since prompt injection remains unsolved, we have to assume agent-written code will eventually do something it shouldn't be allowed to do. Instead of trusting the agent to behave, we constrain what it can do. To make a trustworthy agent this way that comes down to three design requirements:运行不受信任的代码是一个经过充分研究的问题。但运行由受不受信任输入影响的智能体所编写的代码则不然。由于提示词注入(prompt injection)问题尚未解决,我们必须假设智能体编写的代码最终会执行一些它本不该执行的操作。我们不再信任智能体的行为,而是限制它的能力。要以这种方式构建一个值得信赖的智能体,归结为三个设计要求:

  • Execution isolation: agent-written code can't compromise the host it runs on.执行隔离:智能体编写的代码不能危及运行它的宿主环境。
  • Capability isolation: the agent can only touch the data and actions we deliberately hand it.能力隔离:智能体只能接触我们刻意交给它的数据和操作。
  • Durable pauses: execution can stop for human input and resume later without losing its place.持久化暂停:执行过程可以为了等待人工输入而停止,并在稍后恢复,且不会丢失当前进度。

At Interrupt 2026, we announced two offerings built around those requirements.在 Interrupt 2026 大会上,我们宣布了围绕这些要求构建的两项产品。

  • LangSmith Sandboxes give an agent a full remote container: roughly the same freedom as a local coding agent, but isolated on a different machine.LangSmith Sandboxes 为智能体提供了一个完整的远程容器:它拥有与本地编码智能体大致相同的自由度,但被隔离在不同的机器上。
  • Code Interpreters for Deep Agents take the opposite tack: a smaller runtime where the agent can write and run programs, but only inside the harness we provide.Deep Agents 的代码解释器则采取了相反的策略:它是一个更小的运行时环境,智能体可以在其中编写和运行程序,但仅限于我们提供的框架内。

We've already written about the first kind of sandbox. This post is about the second: why orchestration workflows don’t necessarily need a sandboxed computer, and how we keep a smaller surface without giving up the isolation that makes sandboxes appealing in the first place.我们已经介绍过第一种沙盒。本文将探讨第二种:为什么编排工作流不一定需要沙盒计算机,以及我们如何在不放弃沙盒所带来的隔离性的前提下,保持更小的攻击面。

Execution isolation执行隔离

Everyone who runs untrusted code reaches the same conclusion: you need a hard boundary between it and everything else. An interpreter needs that boundary too without leaving the process, which is what we use WebAssembly for.每个运行不受信任代码的人都会得出同样的结论:你需要在它与其他一切事物之间建立一道坚实的边界。解释器也需要这种边界,且不能离开进程,这就是我们使用 WebAssembly 的原因。

WebAssemblyWebAssembly

WebAssembly (WASM) is a compact binary format that executes inside a sandboxed, in-process VM with its own memory, and can only interact with the outside world through host-provided capabilities. That separate linear memory is the crux of the boundary: code running inside WASM can't dereference pointers into the host process, so it can't read or corrupt memory it wasn't handed. WASM runtimes make hard memory and execution limits straightforward to enforce, and because it runs alongside the harness, we can instrument it without standing up a separate machine.WebAssembly (WASM) 是一种紧凑的二进制格式,它在沙盒化的进程内虚拟机中运行,拥有自己的内存,并且只能通过宿主提供的能力与外界交互。这种独立的线性内存是边界的关键:在 WASM 内部运行的代码无法解引用指向宿主进程的指针,因此它无法读取或破坏未交给它的内存。WASM 运行时使得强制执行严格的内存和执行限制变得简单,而且因为它与框架并行运行,我们无需建立独立的机器即可对其进行检测。

AWS, Shopify, and Figma all reach for WASM to run untrusted code on their platforms, and it's the same isolation model behind tools like WebContainers and wasmtime.AWS、Shopify 和 Figma 都选择使用 WASM 在其平台上运行不受信任的代码,这也是 WebContainers 和 wasmtime 等工具背后的隔离模型。

QuickJSQuickJS

WASM gives us the sandbox; we still need something to run code inside it. That's what QuickJS is for: a small, fast, ECMA-compliant JavaScript engine written in plain C. It's small, which keeps the trusted surface inside the boundary small, and it compiles cleanly to WASM, so the engine itself sits behind the boundary rather than beside it. JavaScript is also a good fit for the work: it's expressive enough to write orchestration logic without a compile step, which is exactly the shape of the short programs agents produce.WASM 为我们提供了沙盒;我们仍然需要某种东西在其中运行代码。这就是 QuickJS 的用途:一个用纯 C 语言编写的小型、快速、符合 ECMA 标准的 JavaScript 引擎。它体积小,这使得边界内的受信任面很小,而且它可以干净地编译为 WASM,因此引擎本身位于边界之内,而不是旁边。JavaScript 也非常适合这项工作:它具有足够的表达能力来编写编排逻辑而无需编译步骤,这正是智能体生成的短程序的形式。

Capability isolation能力隔离

The execution boundary stops the agent from compromising the host, but says nothing about what it's allowed to do. An agent is only as useful, and only as dangerous, as the capabilities we give it.执行边界阻止了智能体危及宿主,但并未说明它被允许做什么。智能体的有用程度和危险程度,完全取决于我们赋予它的能力。

Picture an agent planning a wedding. To be useful it has to read sensitive data from everywhere (contracts, RSVPs, the family group chat) and act on it externally (email vendors, approve a deposit). Each capability is reasonable on its own; combine them in one autonomous loop and a single hostile RSVP can read the private budget and email a vendor "approved" changes.想象一个正在筹划婚礼的智能体。为了有用,它必须从各处读取敏感数据(合同、回复、家庭群聊)并进行外部操作(给供应商发邮件、批准押金)。每项能力单独来看都是合理的;但如果将它们组合在一个自主循环中,一个恶意的回复就可能读取私人预算并向供应商发送“已批准”的变更。

Meta's rule of two captures this constraint: until prompt injection is solved, an agent should be able to do no more than two of the following:Meta 的“二元规则”捕捉到了这一约束:在提示词注入问题解决之前,智能体最多只能执行以下三项中的两项:

  • access sensitive data访问敏感数据
  • be exposed to untrusted content接触不受信任的内容
  • change state or communicate externally更改状态或进行外部通信

This is where interpreters and traditional sandboxes diverge most. A sandbox starts computer-shaped (filesystem, dependencies, a shell), so its security work is subtractive: you begin with broad capability and claw it back. A code interpreter starts with nothing. Out of the box it can't read a file, make a network request, or install a dependency. All it has is the language: variables, functions, objects, loops, conditionals, etc. Everything more powerful is bridged in deliberately through the harness.这正是解释器与传统沙盒分歧最大的地方。沙盒的初始形态像一台计算机(文件系统、依赖项、shell),因此其安全工作是减法:你从广泛的能力开始,然后将其收回。而代码解释器从零开始。开箱即用时,它无法读取文件、发起网络请求或安装依赖项。它拥有的只有语言本身:变量、函数、对象、循环、条件语句等。所有更强大的功能都是通过框架刻意桥接进来的。

The clearest example of a bridged capability is calling subagents in code. Instead of a process manager or network stack, the agent gets a function with a narrow contract, and the harness handles the execution. Because we own that bridge, we also set its limits: how many subagents can run at once, and how many a single call can spawn.桥接能力最明显的例子是在代码中调用子智能体。智能体不再拥有进程管理器或网络栈,而是获得了一个具有严格契约的函数,由框架处理执行。因为我们拥有这个桥梁,所以我们也设定了它的限制:一次可以运行多少个子智能体,以及单次调用可以生成多少个子智能体。

Durable pauses持久化暂停

Execution isolation and capability limits keep a running program safe; the last requirement is keeping it alive. A production-ready agent has to stop and wait for a human before doing something risky, and that approval can come back in seconds, hours, or days, often long after the agent has been evicted from the process. So how do you pause a half-finished program for that long and pick up exactly where it left off?执行隔离和能力限制保证了运行程序的安全;最后一个要求是保持其存活。生产就绪的智能体必须在执行风险操作前停止并等待人工确认,而这种确认可能在几秒、几小时甚至几天后才返回,通常那时智能体早已从进程中被移除了。那么,如何将一个未完成的程序暂停那么久,并在稍后从中断处准确恢复呢?

Because QuickJS runs inside WASM, we can pause the program itself instead of rebuilding it. We serialize the interpreter's linear memory to LangGraph state, and on resume the harness restores the snapshot and feeds the result back into the call that was waiting on it. The program sees only an async call that took a while to return.因为 QuickJS 在 WASM 内部运行,我们可以直接暂停程序本身,而无需重建它。我们将解释器的线性内存序列化到 LangGraph 状态中,恢复时,框架会还原快照并将结果反馈给正在等待的调用。程序只会看到一个耗时较长的异步调用。

Try it试用一下

Two of the packages behind this are now public, both experimental:支持此功能的两个包现已公开,均为实验性质:

  • quickjs-rs — the runtime and Python bindings for running QuickJS through WASM.quickjs-rs — 用于通过 WASM 运行 QuickJS 的运行时和 Python 绑定。
  • langchain-quickjs — a Deep Agents middleware built on quickjs-rs.langchain-quickjs — 基于 quickjs-rs 构建的 Deep Agents 中间件。

We're working with a few close partners to bring them into production and tightening the runtime as we learn from those deployments. If you want to see what the interpreter actually unlocks, read our post on Dynamic Subagents, or just go and try it for yourself!我们正在与几家密切合作伙伴合作,将其投入生产,并根据这些部署的经验不断优化运行时。如果你想了解解释器到底解锁了什么,请阅读我们关于动态子智能体的文章,或者直接亲自试用!

uv add deepagents langchain-quickjs
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware
 
agent = create_deep_agent(
	model="baseten:zai-org/GLM-5.2",
	middleware=[CodeInterpreterMiddleware()]
)

S
e
e
w
h
a
t
y
o
u
r
a
g
e
n
t
i
s
r
e
a
l
l
y
d
o
i
n
g

LangSmith, our agent engineering platform, helps developers debug every agent decision, eval changes, and deploy in one click.