沙箱环境变量指南
说明: 本页出现的
abx命令由cli仓库中的 Node.js CLI 提供(npm install -g @agent-sandbox/cli)。CLI 专属文档(CLI 设计、教程、参考)也在该仓库;本仓库记录 Python SDK(agent-sandbox)与容器侧运行时(agent-sandbox-server)。
本指南说明环境变量在 Agent Sandbox 中的工作方式,涵盖两种独立的进程作用域、注入方法、直接执行(direct-exec)语义以及推荐实践。
设计决策: SDK 不提供
sandbox.set_env()API。envd 进程树和 SandboxServer 的os.environ是两个独立且不可统一的作用域,提供单一的"设置环境变量"抽象会造成误导。请使用下文描述的作用域特定注入方法。
两种进程作用域
每个沙箱容器内部有两棵独立的进程树,各自拥有独立的环境变量:
┌─────────────────────────────────────────────────────┐
│ 沙箱容器 │
│ │
│ ┌────────────────────┐ ┌───────────────────────┐ │
│ │ envd(PID 1) │ │ SandboxServer │ │
│ │ ├─ commands.run() │ │ (可选,端口 9000) │ │
│ │ ├─ run_code() │ │ ├─ GET/POST /env │ │
│ │ └─ 终端 PTY │ │ ├─ POST /commands/* │ │
│ │ │ │ └─ 子进程 │ │
│ └────────────────────┘ └───────────────────────┘ │
└─────────────────────────────────────────────────────┘
| 作用域 | 进程所有者 | 环境变量传播方式 |
|---|---|---|
| envd 作用域 | envd 守护进程(PID 1) |
变量在沙箱创建时设置或按调用传递;通过 commands.run()、run_code() 或 PTY 终端启动的所有进程均从 envd 继承。 |
| Server 作用域 | SandboxServer Python 进程 |
POST /env 修改 Server 进程的 os.environ;仅由 Server 派生的子进程(如 POST /commands/{name} 处理器)继承这些变更。 |
关键边界: SandboxServer 上的 POST /env 不会影响通过 SDK 的 commands.run() 或 run_code() 启动的进程——这些进程经由 envd 启动,拥有独立的环境。两个作用域完全隔离。
envd 作用域 — SDK 与 CLI 注入
创建时注入
创建时传入的变量会注入到 envd 根进程,被所有后续命令继承:
SDK:
sandbox = await Sandbox.create(
template="base",
envs={"DATABASE_URL": "postgres://...", "APP_ENV": "staging"},
)
CLI:
abx create --template base --env DATABASE_URL=postgres://... --env APP_ENV=staging
@sandbox 装饰器:
@sandbox(template="base", envs={"DATABASE_URL": "postgres://..."})
def my_task():
import os
return os.environ["DATABASE_URL"]
按调用覆盖
每次执行调用可传入额外的环境变量,仅对该次调用生效:
# commands.run — 使用 `env=`(单数,dict)
result = await sandbox.commands.run(
"printenv MY_VAR",
env={"MY_VAR": "hello"},
)
# run_code — 使用 `envs=`(复数,dict)
result = await sandbox.run_code(
"import os; print(os.environ['MY_VAR'])",
envs={"MY_VAR": "hello"},
)
注意参数命名差异:
commands.run()使用env=(单数);run_code()使用envs=(复数)。这沿用 E2B 的命名约定。
直接执行语义
通过 envd 发送的命令默认以 直接 exec 方式执行。但 commands.run()、commands.stream() 和 commands.start() 会自动检测未加引号的 shell 运算符(|、;、&&、||、>、<、(...)、$(...))并透明地将命令包裹在 sh -c 中。
自动 Shell 运算符包裹
管道、重定向、分号和逻辑运算符现在无需手动使用 sh -c 即可工作:
# ✅ 可用 — 管道被自动检测并包裹
result = await sandbox.commands.run("ls /tmp | grep log")
# ✅ 可用 — 重定向被自动检测并包裹
result = await sandbox.commands.run("echo hello > /tmp/hello.txt")
# ✅ 可用 — && 被自动检测并包裹
result = await sandbox.commands.run("cd /app && python main.py")
# ✅ 可用 — 分号被自动检测并包裹
result = await sandbox.commands.run("mkdir -p /tmp/out; cp file.txt /tmp/out/")
变量展开仍需手动包裹
Shell 变量展开($VAR、${VAR})不会被自动检测,因为 $ 不是 shell 运算符字符。需要显式使用 sh -c:
# ❌ 错误 — $HOME 作为字面字符串传递(不会触发自动包裹)
result = await sandbox.commands.run("echo $HOME")
# stdout: "$HOME"(字面值)
# ✅ 正确 — 使用 sh -c 进行变量展开
result = await sandbox.commands.run("sh -c 'echo $HOME'")
# stdout: "/root"
# ✅ 正确 — 使用 printenv 读取单个变量
result = await sandbox.commands.run("printenv HOME")
# stdout: "/root"
通配符展开仍需手动包裹
# ❌ 错误 — * 作为字面字符传递(不会触发自动包裹)
result = await sandbox.commands.run("ls *.py")
# ✅ 正确
result = await sandbox.commands.run("sh -c 'ls *.py'")
经验法则
自动包裹(无需手动使用 sh -c):
- 管道(|)
- 重定向(>、>>、<)
- 逻辑运算符(&&、||)
- 命令分隔符(;)
- 子 shell((...))
- 命令替换($(...))
仍需显式使用 sh -c '...':
- 变量展开($VAR、${VAR})
- 反引号替换(`cmd`)
- 通配符(*、?、[...])
如果只需读取单个环境变量的值,推荐使用 printenv VAR——更简单且避免 shell 引号问题。
Server 作用域 — GET/POST /env
当沙箱内运行了 SandboxServer(通常监听端口 9000)时,/env 端点管理的是 Server 进程的 os.environ:
读取变量
import httpx
url = sandbox.network.get_url(9000)
headers = sandbox.network.get_access_headers()
resp = httpx.get(f"{url}/env", headers=headers)
print(resp.json()["variables"])
# 仅返回非敏感变量(名称中包含 TOKEN、SECRET、KEY、
# PASSWORD、CREDENTIAL 的变量会被隐藏)
设置变量
resp = httpx.post(
f"{url}/env",
headers={**headers, "Content-Type": "application/json"},
json={"vars": {"MY_CONFIG": "new_value"}},
)
print(resp.json()["updated"]) # ["MY_CONFIG"]
Server 作用域边界
POST /env仅修改 SandboxServer 进程的os.environ。- 由 Server 派生的子进程(如通过
POST /commands/{name}执行的自定义命令处理器)会继承这些变更。 - 通过
commands.run()、run_code()或 PTY 终端启动的进程不受影响——它们在 envd 下运行,拥有独立的环境。
受保护变量
Server 拒绝覆写以下变量:
- 系统变量:
PATH、HOME、USER、SHELL - SDK 内部变量:任何以
ABX_开头的变量(包括ABX_SERVER_TOKEN)
推荐模式
模式 1:创建时注入(最常用)
适用于需要在整个沙箱生命周期内可用的变量:
sandbox = await Sandbox.create(
template="base",
envs={
"DATABASE_URL": db_url,
"API_KEY": api_key,
"APP_ENV": "production",
},
)
# 后续所有命令都能看到这些变量
result = await sandbox.commands.run("printenv DATABASE_URL")
模式 2:按调用传 env Dict(局部覆盖)
适用于每次调用不同的变量:
env = {"BATCH_ID": "batch-001", "DRY_RUN": "true"}
result = await sandbox.commands.run("sh -c 'python process.py'", env=env)
模式 3:复用 env Dict 对象
维护一个共享 dict 并传给每次调用:
shared_env = {"DATABASE_URL": db_url, "LOG_LEVEL": "DEBUG"}
await sandbox.commands.run("sh -c 'python migrate.py'", env=shared_env)
await sandbox.commands.run("sh -c 'python seed.py'", env=shared_env)
await sandbox.run_code("import os; print(os.environ['LOG_LEVEL'])", envs=shared_env)
模式 4:Server 作用域配置(仅 SandboxServer)
使用 SandboxServer 的自定义命令时,通过 POST /env 设置仅 Server 需要的运行时配置:
import httpx
url = sandbox.network.get_url(9000)
headers = sandbox.network.get_access_headers()
httpx.post(
f"{url}/env",
headers={**headers, "Content-Type": "application/json"},
json={"vars": {"FEATURE_FLAG_X": "enabled"}},
)
# 该变量现在对 Server 命令处理器可见:
result = await sandbox.run("my_handler")
客户端配置变量不是沙箱变量
在你的机器上配置 SDK 和 CLI 的变量(ABX_SANDBOX_API_KEY、ABX_PROVIDER、ALIBABA_CLOUD_REGION_ID 等)由客户端读取,永远不会被复制进沙箱。要让某个值在沙箱内可见,请用 envs= / --env 显式传入。每项客户端设置只有一个变量。名字和优先级见配置参考。
由此有两点:
- 在 Server 内部,所有以
ABX_开头的名字都受保护(见上文)。不要把ABX_*用作你自己的沙箱变量。 - 提供方插件同样不继承你的 shell 环境。插件进程以清理过的环境启动,它需要的配置值通过
initialize请求发送给它。
安全注意事项
-
envs 中的密钥:通过
envs=在创建时传入的环境变量会传输到平台 API。确保传输使用 HTTPS(默认值)。避免在日志中输出包含密钥的 env dict。 -
GET /env脱敏:Server 的GET /env端点自动隐藏名称中包含TOKEN、SECRET、KEY、PASSWORD或CREDENTIAL的变量。 -
受保护的 Server 变量:
POST /env禁止写入PATH、HOME、USER、SHELL及所有ABX_*变量,防止意外破坏。 -
无跨作用域泄漏:通过
POST /env在 Server 上设置的变量对 envd 进程不可见,反之亦然。这是安全特性,而非缺陷。 -
推荐
printenv而非echo $VAR:由于 envd 使用直接执行,echo $VAR会输出字面字符串$VAR。请使用printenv VAR或sh -c 'echo $VAR'。
速查表
| 方法 | 参数 | 作用域 | 生命周期 |
|---|---|---|---|
Sandbox.create(envs=) |
envs |
envd | 沙箱生命周期 |
commands.run(env=) |
env |
envd | 单次命令 |
run_code(envs=) |
envs |
envd | 单次执行 |
@sandbox(envs=) |
envs |
envd | 装饰器调用 |
CLI --env KEY=VAL |
--env |
envd | 沙箱生命周期 |
POST /env |
vars |
Server | Server 进程生命周期 |