← 对照卡索引
模块 C第 8 周 sandbox-effect-cap

硬沙箱:把'写文件'这一 effect 限制在能力边界内

🎯 prompt 约束挡不住模型(实测它把 5 次 WriteFile 全写到 F 之外、还跑偏续跑别人的研究); 必须工具层硬拦。只拦写不拦读;Bash 靠 CWD + dangerousCommandBlock(shell 无法工具层硬沙箱)。

📖 教科书概念

智能体一旦能写文件、跑命令,就具备了改变环境的副作用(side effect)。仅靠 prompt 约束("请只在 F 目录内操作")是不可靠的——模型会越界。工程上必须在 **工具层**做硬性能力限制(capability confinement):无论模型怎么想,越界的写 操作在执行边界被直接拒绝。 这对应操作系统里的沙箱/权限模型:能力(capability)显式授予且不可逾越, realpath 解析符号链接以防 `..` 或 symlink 逃逸。

💻 平台源码落点

lambdagent/src/lambdagent/builtin_tools/_sandbox.py · check_write_allowed · L38-56
38def check_write_allowed(target_path: str) -> Tuple[bool, str]:
39 """目标路径是否允许写。未设 root → 总允许;设了 → 必须落在 root 内。
40
41 返回 (ok, error_msg)。realpath 解析符号链接,防 symlink 逃逸。
42 """
43 root = get_sandbox_root()
44 if not root:
45 return True, ""
46 try:
47 rp = os.path.realpath(target_path)
48 except Exception:
49 rp = os.path.abspath(target_path)
50 if rp == root or rp.startswith(root + os.sep):
51 return True, ""
52 return False, (
53 f"[SANDBOX_DENIED] 拒绝写到工作文件夹之外:{target_path}\n"
54 f"本次会话只允许在工作文件夹内读写:{root}\n"
55 f"请把目标路径改到该文件夹内(用其下的绝对路径)后重试。"
56 )
模块级 root + 锁。未设 root → 总允许;设了 → realpath 后必须 == root 或落在 root/ 之下,否则返回 [SANDBOX_DENIED]。realpath 防 symlink/.. 逃逸。
lambdagent/src/lambdagent/builtin_tools/file_tools.py · _resolve · L21-35
21def _resolve(path: str) -> str:
22 """把(可能相对的)路径解析为绝对路径,基准 = Bash 会话当前目录(_session_cwd),
23 与 Bash/沙箱一致。原实现用 os.path.abspath()/os.getcwd() 即**服务进程 CWD**
24 (=启动目录 lambdagentpaas),导致工作区对话里 ListFiles/相对路径落到错目录——
25 agent 用绝对路径读到了对的论文,却 ListFiles 列出 lambdagentpaas 源码、自我迷惑、
26 放弃落盘(commit 见此修复)。绝对路径原样规整。"""
27 try:
28 from lambdagent.builtin_tools.shell_tools import _get_cwd
29 base = _get_cwd()
30 except Exception:
31 base = os.getcwd()
32 if not path:
33 return base
34 if os.path.isabs(path):
35 return os.path.abspath(path)
相对路径必须按会话 CWD(shell_tools._get_cwd)解析,而非服务进程 CWD。 配套坑:加任何新文件工具都要走 _resolve,否则读侧目录会和写侧对不上。

∑ 形式化对象

Guard(WriteFile, check_write_allowed) ; root = realpath(F)
effect: State[fs] ↾ Cap(root) (effect 被限制到能力 root 内)

沙箱在形式上是一个 effect 限制算子:把 WriteFile 的 State[fs] effect 投影 (restrict, ↾)到能力集 Cap(root) 之内。越界写不是"运行期再补救",而是被 Guard 直接拒绝并把 [SANDBOX_DENIED] 反馈给模型纠正——这正是"形式化提前拒绝" 在安全维度的兑现,和范式③的能力授权(Cap)是同一套语言。

🎞 Live Demo

回归测试 test_sandbox_blocks_writes_outside_root 验证越界写被拒。 教学演示:在工作区模式下令 agent 尝试写 F 之外,观察 trace 里的 [SANDBOX_DENIED] 与模型随后的自我纠正。

学习目标 / Takeaway 学生应能把三件事连起来: 1. 副作用(effect)是危险的来源,所以需要边界; 2. 边界必须在工具层强制(prompt 不够),代码落点是 check_write_allowed + _resolve; 3. 形式上这是 effect 到能力集的限制(↾ Cap),与范式③的外包能力授权是同一抽象。 这张卡把"安全"从一句口号变成一个可指认的 effect 限制算子。

相关对照卡: 工具调用范式③:claude-code 原生车道(把工具调用外包给运行时) 工具调用范式②:原生 function-calling(结构化 tool_calls)