第 9 章:什么时候该停,什么时候该问人
本章目标:讲清停机条件、失败边界与 human-in-the-loop 为什么不是保守设计,而是成熟 Agent 系统的必要部分。
本章对应总纲:docs/ebook-outline.md中“第 9 章正文写作提纲(2026-03-31 归档)”。
9.1 为什么“全自动到底”是个很幼稚的幻想
很多人对 Agent 的想象,天然带着一种错觉:
- 最厉害的系统应该一路自动做完
- 中间最好别打扰人
- 如果还要问人,说明系统不够强
这套想法听起来很爽,工程上却经常很蠢。
因为现实任务里,经常存在这些情况:
- 信息不够
- 边界不清
- 多种方案都合理
- 某些动作不可逆
- 某些动作会影响共享状态
- 某些动作本来就需要授权
在这些地方,系统如果还硬往前冲,不是强,是危险。
所以一个成熟 Agent 不只是要会动,还得会停。
9.2 停机条件不是失败补丁,而是边界治理的一部分
这一章和第 3 章不一样。第 3 章讲的是闭环怎么跑;这一章讲的是:
闭环跑到什么地方必须停,什么地方必须问,什么地方可以继续。
很多人把停机条件理解成“做不下去时的兜底逻辑”。
这理解太浅了。
在真正的系统里,停机条件本来就是边界治理的一部分。因为系统每轮都必须回答一个问题:
现在应该继续,还是应该结束、回退、求助?
再说得更工程一点:这类“什么时候继续、什么时候确认、什么时候直接停下”的判断,本身就是运行时控制面的一部分。第 12 章会把这条线集中收束成配置与策略问题;这一章先把它放回闭环出口来讲。
如果没有这层判断,闭环就会退化成机械重复。
所以停机不是例外,而是循环的合法出口。
9.3 一个 Agent 至少应该有哪些停机出口
工程上最少可以把停机出口分成四类:
9.3.1 成功停机
最理想的情况:
- 目标已经满足
- 验证已经通过
- 没有新的未解决问题
这时系统应该停止,而不是继续多做一堆“顺手优化”。
9.3.2 失败停机
有些路径已经明确失败,例如:
- 关键依赖不存在
- 当前权限不够
- 外部系统不可用
- 验证连续失败且没有新线索
这时继续试并不聪明,只是在浪费动作预算。
9.3.3 授权停机
当下一步动作涉及风险边界时,系统应该停下来等待授权,例如:
- 删除文件
- 推送远端
- 修改共享资源
- 覆盖已有工作
- 访问敏感外部系统
这不是“求助”,而是把控制权交还给真正该决策的人。
9.3.4 信息停机
有时系统不是不能做,而是不该在信息不足时乱做。
例如:
- 需求本身含糊
- 多种实现路径都合理
- 缺少关键上下文
- 用户偏好会显著影响方案
这时最合理的动作不是继续猜,而是停下来问清楚。
9.4 为什么 human-in-the-loop 不是降级,而是成熟设计
9.4.1 人类介入不是因为系统“菜”
很多任务天然就不该全自动完成。
因为系统再强,也不该替人做这些决定:
- 业务优先级判断
- 风险接受判断
- 最终发布授权
- 有歧义需求下的方向选择
- 对已有工作覆盖与否的拍板
这些不是推理问题,而是责任归属问题。
9.4.2 人类介入本质上是在补“合法决策权”
从系统视角看,human-in-the-loop 的作用不是给 Agent 补智商,而是给它补:
- 授权
- 业务判断
- 外部信息
- 价值偏好
所以真正成熟的系统不会把“问人”当成丢脸,而会把它设计成标准路径之一。
9.5 什么情况下必须问人,而不是继续猜
这个问题必须说死一点。至少有几类场景,继续猜通常都是坏品味。
9.5.1 需求存在真实歧义
比如用户说“顺手优化一下”“帮我改得更合理”“把这个整理好”。
这种话如果边界没补清,系统很容易越改越多。
9.5.2 多种方案都有明显 trade-off
例如:
- 性能优先还是可读性优先
- 向后兼容还是彻底重构
- 本地最小修补还是结构性调整
这类问题通常不该由系统擅自拍板。
9.5.3 即将跨越风险边界
例如:
- 删除、覆盖、回滚
- 远端推送
- 修改共享基础设施
- 动到可能影响别人的状态
这些动作不确认就做,属于欠收拾。
9.5.4 当前信息缺口足以改变决策
如果缺失的信息会直接影响路径选择,那继续执行就只是把错误做得更快。
可以把这里压成一个三分法:
| 当前局面 | 系统该做什么 |
|---|---|
| 信息够、风险低、边界清楚 | 继续 |
| 信息缺口会改变路径 | 问人 |
| 风险越权或无合理收益 | 停下 |
9.6 什么时候不该问人
反过来也要说清楚。
有些系统一遇到点不确定就停下来问人,结果把自己做成了低效菜单机。
这同样很烂。
9.6.1 当前问题可以通过读环境自行补足
比如:
- 文件路径在哪
- 定义在哪
- 当前错误是什么
- 当前分支和状态如何
这些本来就应该优先自己查,不该先去烦用户。
9.6.2 风险边界并未被触发
如果动作局部、可逆、低风险,就不该动不动停下来请示。
9.6.3 用户已经明确授权了当前范围
用户如果已经给了足够清楚的边界,系统就应该在这个边界内自己完成。
所以好系统不是“尽量多问”,也不是“尽量不问”,而是:
只在真的需要人类决策权或外部信息时才问。
9.7 失败边界:不是所有问题都该无限重试
成熟 Agent 还有一层经常被忽视:失败边界。
9.7.1 为什么必须定义失败边界
如果系统没有失败边界,就会出现这些典型烂行为:
- 同一个错误来回试
- 路径已经证伪还继续加补丁
- 只是因为“还能再试一次”就不停拖长上下文
- 明明该停,却不断自我消耗
9.7.2 失败边界和授权边界不是一回事
这里必须分开:
- 失败边界:继续行动已经没有合理收益
- 授权边界:继续行动前需要用户拍板
前者解决“还值不值得继续”,后者解决“轮不轮得到系统自己继续”。
这两者如果不分清,系统就会一会儿瞎冲,一会儿乱停。
9.7.3 失败边界通常长什么样
常见形式包括:
- 连续若干轮没有新信息
- 同类验证失败重复出现
- 当前权限不足且短期内无法补足
- 关键依赖缺失
- 已触达用户明确禁止的边界
失败边界不是悲观主义,而是系统在承认:
继续行动已经没有合理收益。
这是一种成熟。
9.8 停机条件会反过来塑造规划质量
一个很有意思的事实是:
你怎么定义停机,反过来决定了你会怎么规划。
如果系统根本没定义清楚:
- 什么算完成
- 什么算失败
- 什么必须确认
- 什么必须补信息
那它前面的规划也会跟着发虚。
因为所有步骤都缺少出口条件。
所以“规划”和“停机”不是两块分离模块,而是一前一后的同一套结构:
- 规划决定怎么走
- 停机决定什么时候别再走
9.9 用 Claude Code 看一个现实中的人类介入模型
Claude Code 这类系统特别适合说明这件事,因为它并不是盲目追求全自动。
最直接的证据就在 examples/settings/settings-strict.json:1:
ask: ["Bash"]deny: ["WebSearch", "WebFetch"]
这两个字段已经把三种完全不同的出口分开了:
- 允许继续的路径
- 必须先问人的路径
- 当前根本禁止的路径
这很重要。因为一个成熟系统如果只会“继续”,那它根本不算有边界;如果只会“遇到问题就问”,那它又只是低效菜单机。Claude Code 这种设计的价值,就在于它把“继续 / 询问 / 禁止”都做成显式运行分支,而不是让模型临场发挥。
再看 plugins/README.md:21 和 plugins/README.md:26。hookify、security-guidance 这类插件都说明:生命周期拦截和安全提醒不是附属建议,而是专门存在的一层。这背后其实就是同一个设计思路:该停的时候,要有独立机制把系统拦住,而不是指望模型自己突然变谨慎。
所以从这个现实样本里,至少能抽出几条非常硬的规则:
- 授权边界必须显式建模,别靠模型猜什么时候该问人
- 禁止边界必须比提醒更硬,不该做的就直接别让它走到那一步
- 人类介入是合法出口,不是系统失败后的羞耻补丁
- 停机、确认、拒绝都要属于闭环结构本身,而不是事后附加说明
这说明它追求的不是“自动化最大化”,而是在正确边界内尽可能自动,在不该越权的地方老实停下。这才是真正能落地的 agentic 系统思路。
9.10 真正好的停机,不会让用户觉得系统在偷懒
这一点很关键。
用户真正讨厌的,不是系统停下来;用户讨厌的是系统:
- 该自己查的没查
- 该自己做的没做
- 只因为一点点不确定就把活又甩回来
所以一个好的停机或提问,必须满足两个条件:
- 系统已经尽到了自己该做的探索义务
- 现在确实需要用户的授权、判断或补充信息
做到这两点,停机就不会像推责,而像专业协作。
9.11 本章小结
这一章真正想讲清的是:会停、会问人、会承认失败,不是 Agent 变弱了,而是它终于从玩具变成了成熟系统。
你现在应该记住六件事:
- 全自动到底并不是成熟 Agent 的必要条件。
- 停机条件是闭环的一部分,不是失败补丁。
- 成功停机、失败停机、授权停机、信息停机,都是合法出口。
- human-in-the-loop 的价值在于补足授权、业务判断和外部信息。
- 失败边界能防止系统在错误路径上无限重试。
- 好的系统既不会该问不问,也不会不该问时一直烦用户。
下一章我们继续进入更工程化的一层:多 Agent。不是把多个模型凑在一起那么简单,而是看什么时候真的值得拆成多个执行单元,什么时候又只是徒增复杂度。