第 12 章 执行环境:Agent 在哪动手、怎么动手
工具是手,环境是战场
前两章讲完了工具怎么设计、怎么发现。但还有个被低估的问题:Agent 到底在哪执行? 在本机跑命令、在沙箱跑代码、还是直接操作一台虚拟机?执行环境的可靠性,直接决定 Agent 能不能把活干完。
真实生产的坑是这样的:一条命令可能因为缺个伪终端(TTY)就挂了,跨平台行为不一致,长任务没人跟踪、没人清理,输出爆炸把内存打满。更基础的问题是,Agent 和用户用两套不同的交互方式,一套给模型一套给人,维护两遍。
这一章讲四个模式,从"单条命令怎么跑"到"整个机器怎么管":
- Intelligent Bash Tool Execution(智能 Bash 执行):多模式执行 + 自适应回退 + 后台进程管理。
- Shell Command Contextualization(Shell 输出上下文化):命令和输出自动进入上下文。
- CLI-First Skill Design(CLI 优先的技能设计):一套接口,人和 Agent 通用。
- Virtual Machine Operator Agent(虚拟机操作 Agent):给 Agent 一整台机器当工作台。
模式一:智能 Bash 执行(Intelligent Bash Tool Execution)
问题
让 Agent 安全、可靠地执行命令,比看起来难得多:
- PTY 依赖:需要终端 TTY 的 CLI(编码 Agent、终端 UI)直接用
exec跑会失败。 - 平台差异:Linux 和 macOS 对分离进程(detached process)、信号处理的默认行为不一样。
- 安全:任意命令执行必须有审批流程,还要检测"提权模式"。
- 后台管理:长任务需要跟踪、输出聚合、进程清理。
一个简单的 spawn 解决不了这些问题,Agent 需要一种多模式执行策略:根据命令需求自适应,同时守住安全和可靠。
方案
多模式执行 + 自适应回退:直接执行(direct exec)→ PTY,按命令需求和运行时能力自动选择。PTY 启动失败要自动降级,后台进程要管理,安全审批要到位。
核心要点:
- 结构化工具接口:Bash 命令通过结构化工具(MCP、OpenAI Function Calling)调用,参数经过校验。
- PTY-first:检测到命令需要伪终端(编码 Agent、交互式 CLI),用
node-pty起一个 PTY。 - 自动回退:PTY 启动失败(模块缺失、平台不支持),回退到直接执行并给出警告。
- 平台处理:macOS 需要分离进程才能正确传播信号;Linux 两种模式都支持。
- 安全感知模式:提权模式检测 + 审批流程(deny / allowlist / full 三档)。
- 后台进程注册表:长任务用 session ID 跟踪、输出 tail、退出通知。
- 输出截断:强制
maxOutput上限,防止啰嗦进程耗尽内存。 - 正确的信号传播:超时或中止时,把 SIGTERM/SIGKILL 正确发给子进程。
function runExecProcess(opts):
# opts: command, workdir, env, usePty, timeoutSec, runInBackground, maxOutput
if opts.usePty:
try:
pty = spawn_pty(shell, [opts.command], cwd, env, cols=120, rows=30)
except:
warnings.push("PTY 启动失败,回退到直接执行")
child = spawn_with_fallback(argv, opts)
else:
child = spawn_with_fallback(argv, opts) # 平台差异走 fallback
session = {id: 生成ID, command, pid, aggregated: "", tail: "", exited: false}
注册到进程注册表
if opts.timeoutSec > 0:
超时后: SIGTERM → 1秒宽限 → SIGKILL # 优雅关闭再强杀
return session后台进程管理:进程注册表用 session ID 跟踪长任务,聚合 stdout/stderr,维护 tail 供用户通知,进程退出后清理注册表条目,防止僵尸进程。
证据
- 来自 Clawdbot 生产实现(
validated-in-production,生产验证过)。 - Claude Code 的工具化 bash 执行(含
run_in_background)也验证了这条路。
怎么用
- 检测 TTY 需求:命令是需要 TTY 的 CLI 吗?是就
usePty: true。 - PTY 启动包 try-catch,失败回退直接执行并警告。
- 配安全档位(deny / allowlist / full)和审批行为(off / on-miss / always)。
- 后台进程进注册表跟踪、轮询、清理;信号先 SIGTERM 再 SIGKILL。
- 输出设
maxOutput上限,超了截断中间部分。
取舍
- 好处:PTY 模式让原本跑不了的 TTY 工具也能跑;正常降级;三档安全策略灵活;进程注册表管住长任务;处理好 macOS/Linux 的信号差异。
- 代价:依赖
node-pty原生模块,有的环境编译不过;多模式增加代码复杂度和测试面;全量聚合输出可能吃爆内存;macOS 分离进程收不到信号,要绕。
模式二:Shell 输出上下文化(Shell Command Contextualization)
问题
Agent 在本机干活时,经常要跑 shell 命令(跑 linter、查 git status、列文件),然后用输出作为后续推理的上下文。手动把命令输出复制粘贴进提示词,又繁琐又容易出错。
方案
在 Agent 的界面里提供一个专门机制(比如 ! 前缀或某个命令模式),让用户直接发一条 shell 命令在本机执行。关键是:命令本身和完整输出(stdout + stderr)自动被捕获,注入 Agent 当前的对话/工作上下文。
! 前缀语法起源于 IPython(2001 年),现在已经成为各大 AI 编程平台的默认约定:Claude Code、Cursor、GitHub Copilot、Continue.dev、Aider、Replit Agent 都用。
用户输入: !ls -la
本地执行: ls -la
捕获输出: (stdout + stderr + 退出码)
注入上下文: 命令 + 完整输出 → Agent 可见
Agent: 基于 shell 上下文继续推理在 Claude Code 里输入 !ls -la,本地执行,命令和输出都进 Claude 的上下文。其它平台各有实现:Cursor 用 UI 触发执行、Continue.dev 读终端、Aider 直接集成终端、OpenAI Code Interpreter 跑 Python cell。
证据
- 来自 Boris Cherny 的 Claude Code 指南(
established,成熟),!语法是跨平台事实标准。 - 学术根基:ToolFormer(Schick 等人,2023)、ReAct(Yao 等人,ICLR 2023)、RAG 都验证了"让模型基于真实工具输出推理"的价值。
怎么用
- 用 PTY-aware 执行 + 非交互命令的自动回退(就是模式一)。
- 执行前校验命令(allowlist 或危险模式检测)。
- 捕获完整输出(stdout、stderr、退出码),上下文才完整。
- 给工具调用加可观测性:延迟、失败、回退路径。
取舍
- 好处:消灭手动复制粘贴;上下文自动注入;跨平台有成熟实现;学术根基扎实。
- 代价:有集成耦合和环境维护成本;要安全考量(校验、沙箱);输出太大影响 token 成本。
模式三:CLI 优先的技能设计(CLI-First Skill Design)
问题
设计 Agent 技能(可复用能力)时,有个常见撕裂:
- API-first:把技能写成函数/类,程序化使用很顺,但人没法手动调试。
- GUI-first:做成可视化工具,人用着爽,但 Agent 没法调用。
结果团队要么维护两套接口,要么只讨好其中一方。
方案
先把所有技能设计成 CLI 工具。 一个设计良好的 CLI 天然是双用的:人从终端调,Agent 通过 shell 命令调。
一条技能一条脚本,人人都能跑、Agent 也能跑、脚本能自动化、cron 能定时。一套接口吃所有场景。
核心原则:
- 一条脚本一个技能:每个能力是一个独立可执行文件。
- 子命令对应操作:
skill.sh list、skill.sh get <id>、skill.sh create。 - 结构化输出:程序化使用给 JSON,人看给可读文本(用
isatty()自动检测)。 - 退出码:0 成功、1 错误、2 用法错误、127 找不到。
- 环境配置:凭据走环境变量,不硬编码。
- 默认非交互:避免交互提示,用
--yes/--force参数代替。
# Trello 技能做成 CLI
trello.sh boards # 列出所有看板
trello.sh cards # 列出看板上的卡片
trello.sh create "标题" # 建卡片
trello.sh move # 移动卡片
# 人用
$ trello.sh boards
{"id": "abc123", "name": "Personal", "url": "..."}
# Agent 用(通过 Bash 工具)
Bash: trello.sh cards abc123 | jq '.[0].name' 技能结构:
~/.claude/skills/
├── trello/
│ └── scripts/trello.sh # 主 CLI 入口
├── asana/
│ └── scripts/asana.sh
└── priority-report/
└── scripts/priority-report.sh # 组合其它技能组合示例:一个 priority-report.sh 可以拼装多个技能 CLI 的输出(gh pr list + trello.sh cards + asana.sh tasks),这就是 Unix 哲学的组合性。
怎么用
- 独立可执行文件带 shebang(
#!/bin/bash);无参数或--help出帮助文本。 - CRUD 操作用子命令;输出走 JSON(或 TTY 自动检测)。
- 凭据从
~/.envrc或环境变量来;stderr 放错误、stdout 放数据。 - 非交互模式配
--yes/--force。
什么时候别用 CLI:
- 高频调用(每秒 >100 次):用进程内函数。
- 复杂对象图:用结构化 API。
- 实时流式:用 WebSocket/SSE。
取舍
- 好处:默认双用,人和 Agent 同一套接口;能手动跑、能检查输出;能和 Unix 工具管道组合;任何 shell 都能跑,无运行时依赖;Agent 的工具调用就是可见的 shell 命令,透明;容易写集成测试。
- 代价:bash 处理复杂数据结构别扭;错误处理不如异常结构清晰;进程启动有开销;调用间无持久状态;Windows 要 WSL 或 Git Bash。
模式四:虚拟机操作 Agent(Virtual Machine Operator Agent)
问题
很多任务超出"生成代码、处理文本"的范畴。Agent 需要操作一整个计算机环境:执行代码、管理系统资源、装软件、操作各种应用。只给 Agent 一个函数库,它干不了这些事。
方案
给 Agent 一台专门的虚拟机(VM)当工作台。 Agent 学会在 VM 里操作,把这台机器当自己的直接工作空间:执行任意代码和脚本、装软件包、读写文件系统、用 VM 里的命令行工具和应用。
这个模式把 Agent 从"专用工具"变成"通用数字操作员"。Amjad Masad 的比喻很准:别把 computer use 想成"操作员",更像是"你给模型一台虚拟机,它知道怎么在上面执行代码、装包、写脚本、用应用,尽量多做事"。
常见的实现层级(从重到轻):
- 完整虚拟机(EC2、GCP):隔离最强,开销最高。
- MicroVM(Firecracker、Modal、E2B):隔离和启动速度平衡。
- 容器隔离(Docker、Kubernetes):启动快,共享内核有风险。
- 工具介导执行:开销最小,能力按需划定。
用户: 复杂任务请求
↓
Agent: 决定要在 VM 里做什么
├─ 执行代码/脚本
├─ 安装软件包
├─ 文件系统操作
└─ 用 CLI 工具/应用
↓
VM: 返回执行结果
↓
Agent: 处理并分析结果 → 汇报完成证据
- 来自 Amjad Masad 对高级 computer use Agent 的描述(
established,成熟)。 - 学术根基:Beurer-Kellner 等人(2025)的安全执行框架、ReAct(Yao 等人)的推理-行动范式。
怎么用
- Agent 的成败依赖可靠工具调用和环境搭建时,考虑给一台 VM。
- 从窄工具面开始,参数校验严格。
- 加可观测性:工具延迟、失败、回退路径。
- 自动清理:空闲超时 + 硬执行上限。
- 状态隔离:每个会话独立的文件系统,不共享网络命名空间。
取舍
- 好处:执行成功率和工具调用可靠性提升;Agent 能做"通用操作"而非单一任务。
- 代价:集成耦合、环境维护成本、冷启动延迟(按隔离级别 1-120 秒)。
四个模式怎么选
| 场景 | 推荐模式 |
|---|---|
| 本机要安全可靠地跑命令 | 智能 Bash 执行 |
| 命令输出要自动变成 Agent 上下文 | Shell 输出上下文化 |
| 技能想一套接口人和 Agent 通用 | CLI 优先技能设计 |
| Agent 要操作一整个环境,装包跑应用 | 虚拟机操作 Agent |
四个模式是执行环境的四层:Shell 上下文化解决"人和 Agent 的交互",智能 Bash解决"单条命令怎么跑得稳",CLI 优先解决"技能怎么设计得通用",虚拟机把执行环境推到完整机器的规模。本机够用就从 Bash 开始,要隔离、要装乱七八糟的软件,再上虚拟机。
实践清单
- 命令执行做多模式:PTY 优先,失败回退直接执行
- 配安全档位和审批流程(deny / allowlist / full)
- 后台进程注册表跟踪 + 输出聚合 + 退出清理,防僵尸进程
- 输出设 maxOutput 上限,防内存打爆
- 超时先 SIGTERM 再 SIGKILL,正确处理信号
- Shell 命令输出自动注入上下文(
!前缀),别让人复制粘贴 - 技能先做成 CLI:一条脚本一个技能、子命令、JSON 输出、环境变量凭据
- 需要完整环境时给 Agent 一台 VM/MicroVM,配空闲超时和隔离
- 加可观测性:延迟、失败率、回退路径
本章小结
- 智能 Bash:多模式执行 + 自适应回退 + 后台进程注册表,命令跑得稳、安全。
- Shell 上下文化:命令和输出自动进上下文,消灭复制粘贴,
!是跨平台标准。 - CLI 优先:一条脚本一个技能,人和 Agent 一套接口,还能和 Unix 工具组合。
- 虚拟机操作:给 Agent 一台机器当工作台,从专用工具变成通用操作员。
- 执行环境是"战场":工具设计得再好,跑不稳也白搭。
下一章讲代码执行与沙箱:把"写代码"和"跑代码"分开。先写码后执行、动态代码注入、程序化控制 SDK。