Agent 架构

控制机制:从蒸汽机到 Agent 控制工程

Agent 控制工程与蒸汽机离心调速器的类比——上下文填充率、步执行速率等关键控制切面

05 - 控制机制

从蒸汽机到控制工程

18 世纪的工厂里,蒸汽机频繁出事。飞轮转速过快导致轴承过热,锅炉压力失控引发爆炸,传动皮带断裂抽伤工人。这些机器将化学能转化为机械能的能力令人惊叹,却也因无法驾驭这种能量而危险重重。

工程师们开始针对失败模式设计应对策略:

  • 离心调速器(Watt, 1788):利用飞球的离心力自动调节蒸汽阀门,转速越高,阀门开度越小,形成负反馈闭环。这是历史上第一个自动控制系统。
  • 急停按钮:在危险即将发生时,人工介入切断动力源。它不是自动的,但提供了最后的安全保障。
  • 防呆设计(Poka-yoke):让错误的操作在物理上无法进行。插座的三脚设计确保地线先接通,磁带的不对称缺口防止反向插入。
  • 本质安全:不是让机器在故障时安全停止,而是让故障本身不可能造成严重后果。化工厂的低压反应釜即使破裂,释放的能量也不足以引发连锁爆炸。

这些机制有一个共同点:它们不依赖于操作者的完美无缺,而是嵌入在系统本身,持续监视、自动响应、在必要时强制干预。

Agent 作为热机

Agent 与现代软件系统有本质不同。它更像一台热机:将推理努力(计算能量)转化为有用的交付物(工作产出)。

这种转化过程是狂暴的。Agent 的每一次行动都会产生大量信息,与环境的每一次互动都可能触发意想不到的副作用:

  • 一次文件搜索返回上千个结果,淹没上下文窗口
  • 一个工具调用触发级联操作,修改了未预期的资源
  • 一个循环条件判断失误,导致数百次无效 API 调用
  • 一个幻觉累积,使后续所有推理建立在错误前提上

目前市面上的大多数 Agent 系统,本质上是没有离心调速器的蒸汽机。它们可以演示,可以玩具级地使用,但无法在无人看管的情况下持续运行。

识别关键物理量

控制工程的核心是识别关键物理量,并围绕它们构建反馈回路。

对于蒸汽机,关键物理量是转速和压力。对于 Agent,我们需要问:什么是我们的"转速"?什么是可能"爆炸"的指标?

候选的关键量包括:

  • 上下文填充率:窗口使用了多少?剩余空间是否足以容纳关键信息?
  • Step 执行速率:最近 N 步中,多少步产生了有效进展?多少步在原地打转?
  • 副作用范围:当前 Session 触及了多少文件?修改了多少行代码?
  • 置信度衰减:模型对自身输出的确定性是否在下降?

卓越的工程师不会试图监控所有指标——那会制造噪音,淹没真正的信号。他们会选择少数几个与失败模式直接相关的物理量,构建简单、可管理、可靠的控制回路。

控制回路的核心模式

一个成功的控制机制通常遵循以下模式:

┌─────────────────────────────────────────────────────┐
│ Agent Loop                                          │
│                                                     │
│   Step → [控制机制挂载点] → 控制机制评估 → 控制指令       │
│     ↑                                     │         │
│     └─────────────────────────────────────┘         │
│                                                     │
└─────────────────────────────────────────────────────┘

控制机制被挂载到 Agent Loop 的恰当位置(如 pre-tool-callpost-steppre-turn-stop),重复被激活。每次工作时:

  1. 读取环境状态:当前文件系统状态、已执行的工具列表、资源使用情况
  2. 读取 Agent 状态:上下文内容、已生成的推理轨迹、计划步骤
  3. 根据内部策略评估:是否违反规则?是否接近阈值?是否需要干预?
  4. 返回控制指令:允许继续、提示警告、强制暂停、回滚操作

Agent 框架响应控制指令,可能将信息注入提示词("注意:你正在修改一个未读取的文件"),或直接拦截操作、触发回滚。

一个具体案例

Claude Code 的一个控制机制可以作为范例:

阻断对文件的修改,除非它在 5 步以内被读取过。

这个规则简单到可以用一句话描述,却解决了大量实际问题:

  • 防止基于过时理解的盲目修改
  • 强制 Agent 在编辑前重新确认文件内容
  • 限制副作用范围,使错误更容易定位

实现上,这个控制机制维护一个最近读取文件的集合,在每次 pre-tool-call 钩子中检查:如果调用是文件写入操作,目标文件是否在集合中?如果不在,拦截调用,向 Agent 返回提示:"你需要先读取这个文件的内容,才能安全地修改它。"

这个设计之所以有效,是因为它:

  • 明确:规则清晰,无歧义
  • 可本地验证:不需要全局理解,只检查局部状态
  • 可恢复:被阻断后,Agent 可以通过正常路径(先读取)继续
  • 副作用最小:不修改环境,只影响控制流

这正是 Claude Code 区别于原型级 Agent 的关键之一。不是模型的能力更强,而是控制机制让这种能力能够在生产环境中可靠地释放。

控制机制的层次

控制机制可以按照干预强度分层:

| 层次 | 机制 | 示例 | | ---- | ----------------------- | -------------------------------- | | 通知 | 仅记录,不干预 | 发送遥测,记录日志 | | 提示 | 注入信息,由 Agent 决定 | "注意:你已执行了 20 步仍未完成" | | 阻断 | 阻止特定操作 | 拒绝未读取文件的写入请求 | | 回滚 | 撤销已执行的操作 | 检测到测试失败后恢复文件 | | 熔断 | 完全停止 Session | 成本超限或检测到危险操作模式 |

高层次机制依赖低层次机制提供的信息。熔断器需要通知系统记录触发原因,回滚需要阻断机制先阻止进一步损害。

设计时应该优先使用低强度机制。提示比阻断更灵活,阻断比回滚更简单。只有当低强度机制被证明不足时,才引入更高强度的干预。

从玩具到工具

一个 Agent 系统是否具备生产级别的可靠性,不取决于它使用了什么模型,不取决于它的工具列表有多丰富,而取决于它的控制机制是否完备。

没有控制机制的 Agent 是玩具——可以演示有趣的行为,但不能托付重要任务。有了合适的控制机制,Agent 才能从演示品转化为工具,从玩具转化为基础设施。

下一章将讨论一种特定的控制机制实现:状态总线。它提供了跨 Session 持久化状态、在控制机制间共享信息的基础设施。

Agent Hooks 是一种运行时无关的智能体钩子定义规范倡议,定义了在 Agent 生命周期事件中挂载控制逻辑的标准接口。详见:docs/agent-hooks.md