第 13 章 代码执行与沙箱:先写码,再跑码
跑代码,比看起来危险
上一章讲了命令怎么跑。这一章聚焦一个更精确的环节:代码执行。Agent 不只是跑现成命令,它经常要自己写代码、自己跑。而"让模型写代码再跑"这件事,有两个绕不开的问题。
第一个是审计。Agent 的"想清楚再动手"(plan-and-act)循环,关键决策都藏在自然语言的推理里,外人根本没法验证它有没有把不可信数据流进危险操作(发外部邮件、付款、执行破坏性命令)。文本计划太弱,做不了形式化校验。
第二个是注入。会话中途,Agent 经常要查看本来没载入上下文的文件。手动把整个文件复制粘贴进提示词,又繁琐又费 token,还打断节奏。
这一章讲三个模式:
- Code-Then-Execute(先写码后执行):让模型先输出一段沙箱程序,静态检查通过再执行。
- Dynamic Code Injection(动态代码注入):按需把文件内容注入上下文,别整篇复制。
- Agent SDK for Programmatic Control(程序化控制 SDK):把 Agent 的核心能力暴露成 SDK,供程序调用。
模式一:先写码后执行(Code-Then-Execute)
问题
安全敏感的工作流里,团队需要可验证的保证:被污染的输入(tainted inputs)不能流进危险出口(外部消息、支付、破坏性命令)。但自由形式的 plan-and-act 循环做不到这一点,因为控制决策都在自然语言推理里,没法形式化验证。
方案
让 LLM 输出一段"沙箱程序"或 DSL 脚本,把"推理动作"变成"编译动作"。动作一旦变成代码,策略引擎和静态分析器就能在执行前强制数据流规则。
三步走:
- LLM 写代码:写一段调用工具和不可信数据处理器的代码。
- 静态检查 / 污点分析:验证数据流,比如"没有脏变量流入
send_email.recipient"。 - 沙箱解释执行:代码在锁定的沙箱里跑。
核心转变:从"推理动作"转向"把动作编译成可检查的产物"。
x = calendar.read(today) # 读日历
y = QuarantineLLM.format(x) # 用 LLM 格式化(不可信输出)
email.write(to="john@acme.com", body=y) # 检查: y 是否干净? 能进邮件吗?这段 DSL 里,静态检查器可以确认:从 QuarantineLLM.format 出来的 y 属于"不可信"来源,如果要流进 email.write 的 body,必须先过隔离/净化逻辑,否则直接拒绝。
这个模式还有个附带价值:token 优化。工具调用在沙箱内执行,不经过特殊 token,只有精简后的结果回流 LLM 上下文。生产部署中,数据重的工作流 token 能省 75%-99%。
证据
- 来自 DeepMind CaMeL(Beurer-Kellner 等人,2025,
emerging),提出了"代码增强语言模型"做安全工具使用。 - Anthropic Engineering 的 Code Execution with MCP(2024)验证了"沙箱内执行 + 结果回流"的 token 收益。
怎么用
- 适合多步骤、审计很重要的 Agent:SQL 助手、软件工程机器人、工作流自动化。
- 从小的 DSL 和明确的禁止流开始,随着检查逻辑成熟再扩展语言特性。
- 别一上来就做大而全的 DSL,先覆盖最小安全边界。
取舍
- 好处:形式化可验证;可重放日志;数据重工作流 token 成本下降。
- 代价:要设计 DSL 和静态分析基础设施;沙箱执行有额外开销。
模式二:动态代码注入(Dynamic Code Injection)
问题
交互式编码会话里,用户或 Agent 经常要查看一开始没载入主上下文的文件。手动把整个文件复制粘贴进提示词:
- 繁琐、容易出错。
- 浪费 token 在样板内容上(比如大型配置文件)。
- 打断节奏:编辑器 / 聊天之间来回切换,工作流就断了。
方案
用特殊语法(@filename 或 /load file)支持按需文件注入,自动完成三步:
- 取文件:从磁盘或版本控制拉取请求的文件。
- 摘要/提取:文件大就只提取相关部分(函数体、AST 解析的定义、指定行范围)。
- 注入:把片段注入 Agent 当前上下文,直接扩展它"对任务的记忆"。
具体来说:
用户输入: /load src/components/Button.js:10-50
或: @src/setup/db.js
预处理: 拦截命令 → 读文件(或行范围)→ 用文件内容替换命令
其余提示词不变 → Agent 无需重启会话继续推理语法约定:
@path/to/file.ext:整个文件载入(小于 2,000 token),否则跑启发式摘要。/load path/to/file.ext:10-50:只载入第 10-50 行。/summarize path/to/test_spec.py:跑摘要例程(提取 docstring + 测试名)。
实现步骤:
- 在聊天前端或 CLI 里建一个监听器,识别
@和/load标记。 - 把标记映射到文件路径;校验权限,解析符号链接(防止越出项目根目录)。
- 读文件文本,需要时跑行范围解析器或 AST 片段提取器(tree-sitter 支持多语言)。
- 在发出的提示词里,把标记替换成
/// BEGIN <filename> …内容… /// END <filename>。 - 把增强后的提示词发给 LLM 推理。
常见坑:
- 路径穿越:必须校验并拒绝
@../../../etc/passwd、项目外的绝对路径、恶意符号链接。 - 大文件:超过 4,096 token 自动跑摘要子例程,只提取函数/方法定义。
- 敏感文件:
.env、*.key这类要拦。
证据
- 来自 Anthropic 的常见工作流(Claude Code 的 at-mention),
established(成熟)。 - 各大 AI IDE 插件都在用:GitHub Copilot Workspace、Cursor;Aider 的
/add、/drop命令配 tree-sitter AST 解析。
怎么用
- 适合任何编码类 Agent 和 AI IDE 插件。
- 前端/代理要有本地文件系统访问权限。
- 安全三件套不可妥协:路径校验、敏感文件拦截、沙箱。
取舍
- 好处:不出聊天环境就能探索代码;消灭手动复制粘贴;确保最相关的代码直接可见,Agent 更准;token 省 10-100 倍(相对全量载入),有记录显示开发效率提升 3 倍以上。
- 代价:界面/代理要能访问本地文件系统;安全校验是硬要求;摘要启发式可能漏掉微妙上下文(比如私有辅助函数)。
模式三:程序化控制 SDK(Agent SDK for Programmatic Control)
问题
交互式终端和聊天界面适合很多 Agent 任务,但不是所有。把 Agent 能力集成进自动化工作流(CI/CD 流水线、定时任务、批处理),或者在核心 Agent 功能之上构建更复杂的应用,需要一个程序化接口。你不能在 CI 里开个聊天窗口。
方案
提供一个 SDK,把 Agent 的核心功能暴露成程序化访问。 开发者可以:
- 从代码里调用 Agent 动作(处理提示词、用工具、访问记忆),用 Python、TypeScript。
- 以非交互方式配置 Agent 行为和工具访问。
- 把 Agent 逻辑集成进更大的软件系统。
- 自动化涉及 Agent 的重复任务。
- 构建由 Agent 后端驱动的自定义 UI 或应用。
- 控制资源限制(token 预算、执行时间、成本上限)。
- 实现细粒度权限管理和授权范围。
SDK 通常包含库、用于脚本化的 CLI、以及无头(headless)/嵌入使用的文档。
# Claude Code SDK 风格的 CLI 示例
$ claude -p "我这周干了啥?" \
--allowedTools Bash(git log:*) \
--output-format json程序化调用,非交互,输出 JSON,权限提前限定好。
怎么用
什么时候用:
- CI/CD 流水线集成和自动化工作流。
- 跨多个文件或项目的批处理。
- 构建由 Agent 后端驱动的自定义应用或 UI。
- 性能敏感场景(缓存和低开销重要)。
- 外部开发者集成和标准化需求。
什么时候别用:
- 微服务架构(用 REST/gRPC API 更合适)。
- 语言和框架独立性是硬要求。
- 高频调用(每秒 >100 次)或实时流式。
实施建议: 从窄工具面开始,参数校验严格;加可观测性(延迟、失败、回退路径);代码执行做沙箱隔离。
取舍
- 好处:能自动化、能接 CI/CD;权限、资源、可观测性都能细粒度控制;支持批处理和自定义 UI。
- 代价:集成耦合和环境维护成本;失去对话式交互和澄清能力;要用代码处理错误,重试/回退逻辑要写扎实。
三个模式怎么选
| 场景 | 推荐模式 |
|---|---|
| 安全敏感,要验证数据不流进危险操作 | 先写码后执行(DSL + 污点分析) |
| 会话中要按需查看大文件 | 动态代码注入 |
| 要把 Agent 接进 CI/CD 或构建应用 | 程序化控制 SDK |
三个模式回答"代码执行"的三个问题:先写码后执行管"怎么安全地跑",动态代码注入管"代码上下文从哪来",程序化 SDK 管"怎么让程序来驱动"。它们和上一章的虚拟机、智能 Bash 组合起来,就是完整的执行层。
这里也呼应一下第 10 章:Code-Over-API 说的是"让代码去调工具省 token",先写码后执行说的是"让代码可审计"。同一个"代码优先"思路的两个侧面,一个为省钱,一个为安全。
实践清单
- 安全敏感工作流:让模型输出 DSL/沙箱程序,静态检查通过再执行
- DSL 从小做起,先明确"禁止流",再扩展语言特性
- 代码执行前跑污点分析:脏数据不能流进邮件/支付/破坏性命令
- 会话里按需注入文件:
@file//load file:10-50,大文件自动摘要 - 文件注入必须校验路径:拒绝穿越、项目外绝对路径、恶意符号链接
- 敏感文件(.env、*.key)拦截,超 4K token 自动摘要
- 要自动化:暴露 Agent SDK / CLI,非交互、JSON 输出、权限前置
- SDK 配资源限制(token 预算、执行时间、成本上限)和沙箱隔离
- 加可观测性:延迟、失败率、回退路径
本章小结
- 先写码后执行:动作变成可检查的代码,静态分析先于执行,token 还省 75%-99%。
- 动态代码注入:
@//load按需注入文件片段,省 token、保节奏、Agent 更准。 - 程序化 SDK:把 Agent 核心能力暴露成代码可调,接 CI/CD、做应用、控资源权限。
- 代码执行三件事:安全地跑、按需取上下文、程序化驱动。
- 到这里,第三部分"工具与环境"就讲完了:接口设计(10)→ 工具发现(11)→ 执行环境(12)→ 代码执行与沙箱(13),一条"让 Agent 动手"的完整链路。
下一部分进入"让它可靠":评测与观测,让 Agent 稳定可用。先从第 14 章结构化输出与契约开始。