代理生成代码、交互文件系统和运行 shell 命令。因为我们无法预测代理可能做什么,所以重要的是其环境是隔离的,这样它就不能访问凭据、文件或网络。沙盒通过在代理的执行环境和我们主机系统之间创建边界来提供这种隔离。
在 Deep Agents 中,沙盒是后端,定义了代理操作的环境。与其他仅暴露文件操作的后端(State、Filesystem、Store)不同,沙盒后端还给代理一个 execute 工具来运行 shell 命令。当您配置沙盒后端时,代理会获得:
- 所有标准文件系统工具(
ls、read_file、write_file、edit_file、glob、grep)
execute 工具,用于在沙盒中运行任意 shell 命令
- 保护您主机系统的安全边界
为什么使用沙盒?
沙盒用于安全目的。
它们让代理执行任意代码、访问文件和使用网络,而不会损害您的凭据、本地文件或主机系统。
当代理自主运行时,这种隔离是必不可少的。
沙盒对以下情况特别有用:
- 编码代理:自主运行的代理可以使用 shell、git、克隆仓库(许多提供商提供原生 git API,例如 Daytona 的 git 操作),并运行 Docker-in-Docker 进行构建和测试管道
- 数据分析代理——在安全、隔离的环境中加载文件、安装数据分析库(pandas、numpy 等)、运行统计计算并创建 PowerPoint 演示文稿等输出
使用 Deep Agents CLI? CLI 通过 --sandbox 标志内置沙盒支持。请参阅使用远程沙盒了解 CLI 特定的设置、标志(--sandbox-id、--sandbox-setup)和示例。
基本用法
这些示例假设您已经使用提供商的 SDK 创建了沙盒/devbox,并设置了凭据。关于注册、认证和提供商特定的生命周期详情,请参阅可用的提供商。
Modal
Runloop
Daytona
LangSmith
pip install langchain-modal
import modal
from deepagents import create_deep_agent
from langchain_anthropic import ChatAnthropic
from langchain_modal import ModalSandbox
app = modal.App.lookup("your-app")
modal_sandbox = modal.Sandbox.create(app=app)
backend = ModalSandbox(sandbox=modal_sandbox)
agent = create_deep_agent(
model=ChatAnthropic(model="claude-sonnet-4-6"),
system_prompt="You are a Python coding assistant with sandbox access.",
backend=backend,
)
try:
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Create a small Python package and run pytest",
}
]
}
)
finally:
modal_sandbox.terminate()
pip install langchain-runloop
import os
from deepagents import create_deep_agent
from langchain_anthropic import ChatAnthropic
from langchain_runloop import RunloopSandbox
from runloop_api_client import RunloopSDK
client = RunloopSDK(bearer_token=os.environ["RUNLOOP_API_KEY"])
devbox = client.devbox.create()
backend = RunloopSandbox(devbox=devbox)
agent = create_deep_agent(
model=ChatAnthropic(model="claude-sonnet-4-6"),
system_prompt="You are a Python coding assistant with sandbox access.",
backend=backend,
)
try:
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Create a small Python package and run pytest",
}
]
}
)
finally:
devbox.shutdown()
pip install langchain-daytona
from daytona import Daytona
from deepagents import create_deep_agent
from langchain_anthropic import ChatAnthropic
from langchain_daytona import DaytonaSandbox
sandbox = Daytona().create()
backend = DaytonaSandbox(sandbox=sandbox)
agent = create_deep_agent(
model=ChatAnthropic(model="claude-sonnet-4-6"),
system_prompt="You are a Python coding assistant with sandbox access.",
backend=backend,
)
try:
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Create a small Python package and run pytest",
}
]
}
)
finally:
sandbox.stop()
LangSmith sandboxes are currently in private beta.
pip install "langsmith[sandbox]"
from deepagents import create_deep_agent
from deepagents.backends import LangSmithSandbox
from langchain_anthropic import ChatAnthropic
from langsmith.sandbox import SandboxClient
client = SandboxClient()
ls_sandbox = client.create_sandbox(template_name="my-template")
backend = LangSmithSandbox(sandbox=ls_sandbox)
agent = create_deep_agent(
model=ChatAnthropic(model="claude-sonnet-4-6"),
system_prompt="You are a Python coding assistant with sandbox access.",
backend=backend,
)
try:
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "Create a small Python package and run pytest",
}
]
}
)
finally:
client.delete_sandbox(ls_sandbox.name)
可用的提供商
关于提供商特定的设置、认证和生命周期详情,请参阅沙盒集成。
没看到您的提供商?您可以实现自己的沙盒后端。请参阅贡献沙盒集成。
生命周期和范围
沙盒消耗资源并产生成本,直到它们被关闭。您如何管理它们的生命周期取决于您的应用程序。
选择沙盒生命周期如何映射到您的应用程序资源。请参阅投入生产了解更多关于此决策的信息。
线程范围(默认)
每个对话获得自己的沙盒。沙盒在第一次运行开始时创建,并在同一线程的后续消息中重用。当线程被清理(或沙盒 TTL 过期)时,沙盒被销毁。这对大多数代理是正确的默认值。
示例:一个数据分析机器人,每个对话从干净的环境开始。
助手范围
给定助手的所有线程共享一个沙盒。沙盒 ID 存储在助手的配置中,因此每个对话返回到相同的环境。文件、安装的包和克隆的仓库在对话之间持久化。当代理维护长时间运行的工作区时使用此模式。
示例:一个维护克隆仓库和跨对话安装依赖项的编码助手。
助手范围的沙盒会随时间积累文件、安装的包和其他沙盒内状态。使用沙盒提供商的 TTL 配置,定期使用快照重置,或实施清理逻辑,以防止沙盒的磁盘和内存无限增长。线程范围的沙盒通过每次对话全新开始来避免这个问题。
基本生命周期
AgentCore
Modal
Runloop
Daytona
LangSmith
from bedrock_agentcore.tools.code_interpreter_client import CodeInterpreter
from langchain_agentcore_codeinterpreter import AgentCoreSandbox
interpreter = CodeInterpreter(region="us-west-2")
interpreter.start()
backend = AgentCoreSandbox(interpreter=interpreter)
result = backend.execute("echo hello")
# ... 使用沙盒
interpreter.stop()
import modal
from langchain_modal import ModalSandbox
app = modal.App.lookup("your-app")
modal_sandbox = modal.Sandbox.create(app=app)
backend = ModalSandbox(sandbox=modal_sandbox)
result = backend.execute("echo hello")
# ... 使用沙盒
modal_sandbox.terminate()
from runloop_api_client import RunloopSDK
from langchain_runloop import RunloopSandbox
client = RunloopSDK(bearer_token="...")
devbox = client.devbox.create()
backend = RunloopSandbox(devbox=devbox)
result = backend.execute("echo hello")
# ... 使用沙盒
devbox.shutdown()
from daytona import Daytona
from langchain_daytona import DaytonaSandbox
sandbox = Daytona().create()
backend = DaytonaSandbox(sandbox=sandbox)
result = backend.execute("echo hello")
# ... 使用沙盒
sandbox.stop()
from langsmith.sandbox import SandboxClient
from deepagents.backends.langsmith import LangSmithSandbox
client = SandboxClient()
ls_sandbox = client.create_sandbox(template_name="deepagents-deploy")
backend = LangSmithSandbox(sandbox=ls_sandbox)
result = backend.execute("echo hello")
# ... 使用沙盒
client.delete_sandbox(backend.id)
每个对话的生命周期
在聊天应用程序中,对话通常由 thread_id 表示。
一般来说,每个 thread_id 应该使用自己的唯一沙盒。
在您的应用程序中存储沙盒 ID 和 thread_id 之间的映射,或者如果沙盒提供商允许将元数据附加到沙盒,则使用沙盒存储。
**聊天应用程序的 TTL。**当用户可以在空闲时间后重新参与时,您通常不知道他们何时或是否返回。在沙盒上配置生存时间(TTL)——例如,TTL 归档或 TTL 删除——这样提供商自动清理空闲沙盒。许多沙盒提供商支持此功能。
以下示例展示了使用 Daytona 的获取或创建模式。
关于其他提供商,请参阅沙盒提供商 API 了解等效的标签、元数据和 TTL 选项:
from langchain_core.utils.uuid import uuid7
from daytona import CreateSandboxFromSnapshotParams, Daytona
from deepagents import create_deep_agent
from langchain_daytona import DaytonaSandbox
client = Daytona()
thread_id = str(uuid7())
# 通过 thread_id 获取或创建沙盒
try:
sandbox = client.find_one(labels={"thread_id": thread_id})
except Exception:
params = CreateSandboxFromSnapshotParams(
labels={"thread_id": thread_id},
# 添加 TTL,以便空闲时清理沙盒
auto_delete_interval=3600,
)
sandbox = client.create(params)
backend = DaytonaSandbox(sandbox=sandbox)
agent = create_deep_agent(
backend=backend,
system_prompt="您是一个具有沙盒访问权限的编码助手。您可以在沙盒中创建和运行代码。",
)
try:
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "创建一个 hello world Python 脚本并运行它",
}
]
},
config={
"configurable": {
"thread_id": thread_id,
}
},
)
print(result["messages"][-1].content)
except Exception:
# 可选:在异常时主动删除沙盒
client.delete(sandbox)
raise
集成模式
根据代理运行的位置,有两种将代理与沙盒集成的架构模式。
代理在沙盒中模式
代理在沙盒内运行,您通过网络与它通信。您构建一个预安装了代理框架的 Docker 或 VM 镜像,在沙盒内运行它,并从外部连接发送消息。
优点:
- ✅ 与本地开发密切对应。
- ✅ 代理和环境紧密耦合。
权衡:
- 🔴 API 密钥必须放在沙盒内(安全风险)。
- 🔴 更新需要重建镜像。
- 🔴 需要通信基础设施(WebSocket 或 HTTP 层)。
要在沙盒中运行代理,请构建镜像并安装 deepagents。
FROM python:3.11
RUN pip install deepagents-cli
然后在沙盒内运行代理。
要在沙盒内使用代理,您必须添加额外的基础设施来处理您的应用程序与沙盒内代理之间的通信。
沙盒作为工具模式
代理在您的机器或服务器上运行。当它需要执行代码时,它调用沙盒工具(如 execute、read_file 或 write_file),这些工具调用提供商的 API 在远程沙盒中运行操作。
优点:
- ✅ 无需重建镜像即可立即更新代理代码。
- ✅ 代理状态和执行之间更清晰的分隔。
- API 密钥保持在沙盒外。
- 沙盒故障不会丢失代理状态。
- 可以选择在多个沙盒中并行运行任务。
- ✅ 只为执行时间付费。
权衡:
from daytona import Daytona
from deepagents import create_deep_agent
from dotenv import load_dotenv
from langchain_daytona import DaytonaSandbox
load_dotenv()
# 也可以使用 AgentCore、E2B、Runloop、Modal
sandbox = Daytona().create()
backend = DaytonaSandbox(sandbox=sandbox)
agent = create_deep_agent(
backend=backend,
system_prompt="您是一个具有沙盒访问权限的编码助手。您可以在沙盒中创建和运行代码。",
)
try:
result = agent.invoke(
{
"messages": [
{
"role": "user",
"content": "创建一个 hello world Python 脚本并运行它",
}
]
}
)
print(result["messages"][-1].content)
except Exception:
# 可选:在异常时主动删除沙盒
sandbox.stop()
raise
本文档中的示例使用沙盒作为工具模式。
当提供商的 SDK 处理通信层且您希望生产环境镜像本地开发时,选择代理在沙盒中模式。
当您需要快速迭代代理逻辑、将 API 密钥保持在沙盒外或偏好更清晰的分隔时,选择沙盒作为工具模式。
沙盒如何工作
隔离边界
所有沙盒提供商保护您的主机系统免受代理的文件系统和 shell 操作的影响。代理无法读取您的本地文件、访问您机器上的环境变量或干扰其他进程。但是,仅靠沙盒不能防止:
- 上下文注入:控制代理部分输入的攻击者可以指示它在沙盒内运行任意命令。沙盒是隔离的,但代理在其中有完全控制权。
- 网络泄露:除非阻止网络访问,否则上下文注入的代理可以通过 HTTP 或 DNS 将数据发送出沙盒。一些提供商支持阻止网络访问(例如,Modal 上的
blockNetwork: true)。
请参阅安全考虑了解如何处理秘密和缓解这些风险。
execute 方法
沙盒后端有一个简单的架构:提供商必须实现的唯一方法是 execute(),它运行 shell 命令并返回其输出。每个其他文件系统操作(read、write、edit、ls、glob、grep)都由 BaseSandbox 基类构建在 execute() 之上,它构造脚本并通过 execute() 在沙盒内运行它们。
这种设计意味着:
- **添加新提供商很简单。**实现
execute()——基类处理其他一切。
- **
execute 工具是有条件可用的。**在每次模型调用时,测试框架检查后端是否实现了 SandboxBackendProtocol。如果没有,工具被过滤掉,代理永远不会看到它。
当代理调用 execute 工具时,它提供一个 command 字符串并返回组合的 stdout/stderr、退出代码,以及如果输出太大则返回截断通知。
您也可以直接在应用程序代码中调用后端 execute() 方法。
文件传输
沙盒提供商支持文件操作,使您能够将文件和目录复制到沙盒中以及从沙盒中复制出来。请参阅提供商特定的文档了解详情。
一些提供商支持:
- 上传/下载文件:将本地文件传输到沙盒,或将沙盒中的文件下载到本地
- 预置模板:使用预安装依赖项的模板来加快启动时间
- 快照:保存沙盒状态的快照,以便以后恢复
这些功能在提供商特定的集成文档中有详细说明。
安全考虑
处理秘密
最佳实践:
- 将 API 密钥保持在沙盒外:使用沙盒作为工具模式,这样凭据保留在您的主机或服务器上
- 使用环境变量:许多沙盒提供商支持安全地设置环境变量,而无需将它们写入磁盘
- 配置网络限制:如果您的提供商支持,阻止沙盒的出站网络访问以防止数据泄露
上下文注入缓解
沙盒隔离代理的操作,但不隔离代理的指令。如果攻击者可以控制代理的输入,他们可以指示代理在沙盒内执行任意命令。
缓解措施:
- 验证用户输入:在将用户输入传递给代理之前清理和验证它
- 使用人工介入:对敏感操作使用人工介入审批
- 网络限制:如果提供商支持,阻止沙盒的出站网络访问
了解更多
- 后端:可插拔存储后端,包括 StateBackend、FilesystemBackend、StoreBackend 和沙盒
- 权限:文件系统权限规则
- 投入生产:沙盒生命周期管理和部署最佳实践