第 12 章 AI AgentAgentic Patterns执行环境

第 12 章 执行环境:Agent 在哪动手、怎么动手

工具是手,环境是战场

前两章讲完了工具怎么设计、怎么发现。但还有个被低估的问题:Agent 到底在哪执行? 在本机跑命令、在沙箱跑代码、还是直接操作一台虚拟机?执行环境的可靠性,直接决定 Agent 能不能把活干完。

真实生产的坑是这样的:一条命令可能因为缺个伪终端(TTY)就挂了,跨平台行为不一致,长任务没人跟踪、没人清理,输出爆炸把内存打满。更基础的问题是,Agent 和用户用两套不同的交互方式,一套给模型一套给人,维护两遍。

这一章讲四个模式,从"单条命令怎么跑"到"整个机器怎么管":

  1. Intelligent Bash Tool Execution(智能 Bash 执行):多模式执行 + 自适应回退 + 后台进程管理。
  2. Shell Command Contextualization(Shell 输出上下文化):命令和输出自动进入上下文。
  3. CLI-First Skill Design(CLI 优先的技能设计):一套接口,人和 Agent 通用。
  4. 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 能定时。一套接口吃所有场景。

核心原则:

  1. 一条脚本一个技能:每个能力是一个独立可执行文件。
  2. 子命令对应操作skill.sh listskill.sh get <id>skill.sh create
  3. 结构化输出:程序化使用给 JSON,人看给可读文本(用 isatty() 自动检测)。
  4. 退出码:0 成功、1 错误、2 用法错误、127 找不到。
  5. 环境配置:凭据走环境变量,不硬编码。
  6. 默认非交互:避免交互提示,用 --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。

📑 Agent 模式实战:生产级 AI Agent 的工程模式

1 第 1 章 什么是 Agent 模式 2 第 2 章 规划-执行-观察:先想清楚,再动手 3 第 3 章 反思闭环:让 Agent 学会检查自己的作业 4 第 4 章 委派:让主 Agent 学会把活分出去 5 第 5 章 上下文预算治理:把 token 当成钱来管 6 第 6 章 上下文压缩与精选:装不下怎么办 7 第 7 章 上下文最小化:别让脏东西留在脑子里 8 第 8 章 记忆体系:让 Agent 记得住过去 9 第 9 章 学习沉淀:让 Agent 和团队一起变聪明 10 第 10 章 工具接口哲学:让 Agent 能用、好用、用得起 11 第 11 章 工具发现:让 Agent 在几百个工具里找到对的 12 第 12 章 执行环境:Agent 在哪动手、怎么动手 13 第 13 章 代码执行与沙箱:先写码,再跑码 14 第 14 章 结构化输出与契约:让 Agent 的输出能接住 15 第 15 章 验证循环:Agent 怎么检查自己的作业 16 第 16 章 评测基建:怎么系统地检验 Agent 17 第 17 章 可观测性:看见 Agent 在想什么、在干嘛 18 第 18 章 韧性工程:扛得住部分失效 19 第 19 章 威胁模型:先看风险长什么样,再谈防御 20 第 20 章 控制流隔离:把"谁做决定"和"谁执行"分开 21 第 21 章 权限与审批:谁有权干什么、谁点头 22 第 22 章 凭据与出口:Agent 手里的钥匙和门 23 第 23 章 多智能体信任:多个 Agent 之间怎么互信、怎么审计 24 第 24 章 反馈信号设计:给 Agent 的是信号,不是更大的提示词 25 第 25 章 评测驱动的改进:让 Agent 在真实使用和对抗测试里变强 26 第 26 章 强化学习:把反馈变成训练信号 27 第 27 章 复合式进化:让 Agent 系统越用越值钱 28 第 28 章 多智能体协调:让一群 Agent 一起干活不掉链子 29 第 29 章 模型路由:谁用哪个模型,怎么用得起 30 第 30 章 推理搜索结构:让 Agent 多想想,而不是一条道走到黑 31 第 31 章 控制谱系:从自动补全到完全自主的滑动条 32 第 32 章 团队与产品:把 Agent 变成团队资产,而不是个人玩具
← 返回本书大纲