交互模式下人推着循环走:它问你答,看到不对就打断。无人值守模式下没人可问。 所以「请求确认」在流水线里必须翻译成「拒绝并说明」——实测同一段脚本,交互回三行、 无人值守回一行 JSON。
前六章讲的是「模型怎么工作」,这一章讲「它工作在没人看着的地方」。 这一节先把这个转变说清楚:变的不是能力,是「谁来收结果」。
一、两种模式的分界线
交互模式:人发起、人看输出、人决定下一步。循环的驱动力在人身上。 无人值守:一条命令跑完,输出交给下一个程序。驱动力在参数和退出码上。 分界线不在「有没有界面」,在「谁来收结果」。
一旦结果是给程序看的,「好看」就完全不重要了——排版、颜色、分节标题,全是噪声。 这也是为什么同一件事,两种模式的输出长得完全不像。
二、真跑:同一个脚本两种入口
演示里同一个 agent 脚本:带 -p 一次性跑完,不带 -p 进交互循环。 交互模式喂一行进去,它回三行——其中一行是「要不要继续」。 无人值守模式回一行 JSON,退出码零。
那行「要不要继续」就是分界线:无人值守的时候,没有人回答它。 同样一件事,输出行数差三倍,而这个差不是「精简」,是「少了一个环节」。
三、需要人拍板的事只能拒绝
演示第二条指令是「删除所有未提交的改动」:退出码 3,标准输出是空的。 原因只写在标准错误里。这就是无人值守的规矩:拿不准就停,并且告诉外面「我停了」。 如果它在那里等着人回答,CI 会一直挂到超时——那是最难查的一类失败。
因为「超时」这个现象,既可能是网络、也可能是死锁、还可能是它真的在等。 所以「请求确认」这个动作,在 headless 里必须翻译成「拒绝,并说明为什么需要人」。
四、三条铁律
第一条:输出必须机器可读。给人看的排版,在流水线里全是噪声。 第二条:失败必须靠退出码表达。日志里写「出错了」但退出码是零,等于没报错。 第三条:不许等输入。任何「请确认」都要提前变成参数,或者直接拒绝。
三条都不难,难的是它们在本地开发时全都无所谓——所以最容易在第一次上 CI 时才被发现。
五、这个转变的代价与收益
代价:人失去了中途纠偏的机会。参数写错了,它就照着错的跑完,你事后才知道。 收益:能进流水线。而进了流水线,「每次提交都检查一遍」才成为可能。 所以它不是「更先进的用法」,是「把判断从过程中提前到开始之前」。
提前判断的代价是你要想得更清楚;收益是它能在你睡觉的时候跑。
六、动手 Demo:同一个任务,交互跑和无人值守跑差在哪
交互模式下,人推着循环走:它问你答,你看到不对就打断。 无人值守模式下没人可问。所以「提问」必须变成「默认动作」或者「退出码」。
if "-p" in sys.argv: # 无人值守:一次跑完
print(json.dumps({"ok": True, "result": u"改好了 2 个文件"}))
sys.exit(0)
for line in sys.stdin: # 交互:人推着走
print(u"> 要不要继续?(y/n)")
实跑输出:
--------------------------------------------------------------------
同一个任务,交互跑和无人值守跑差在哪
--------------------------------------------------------------------
交互模式下,人推着循环走:它问你答,你看到不对就打断。
无人值守模式下没人可问。所以「提问」必须变成「默认动作」或者「退出码」。
① 同一个脚本,两种入口
--------------------------------------------------------------------
-p 模式:一条命令跑完,退出码 0,输出 1 行。
{"ok": true, "result": "改好了 2 个文件"}
② 交互模式:谁在推循环
--------------------------------------------------------------------
交互模式:喂一行进去,它回了 3 行,退出码 0。
> 收到:把 README 改短
> 要不要继续?(y/n)
> 会话结束,共 1 轮
| 模式 | 输出行数 | 机器可读 | 退出码 | 结果给谁 |
|---|---|---|:---:|---|
| 交互 | 3 行 | 没有 | 0 | 你来收 |
| 无人值守 | 1 行 | 是 | 0 | 下一个程序收 |
交互那几行里有一行是在问你「要不要继续」——无人值守的时候,这个问题没有人回答。
所以它不是「少了个人」,是「少了一次判断的机会」。判断必须提前写进参数或提示词。
③ 需要人拍板的事,无人值守只能拒绝
--------------------------------------------------------------------
| 指令 | 退出码 | 看到什么 |
|---|---|:---:|
| 把 README 改短 | 0 | 一行 JSON,含结果 |
| 删除所有未提交的改动 | 3 | 需要确认:这条指令会改变工作区,无人值守下不许执行 |
第二条退出码 3,标准输出是空的,原因只写在标准错误里。
这就是无人值守的规矩:拿不准就停,并且用一个非零的码告诉外面「我停了、别继续」。
如果它在这里等着人回答,CI 会一直挂到超时——那是最难查的一类失败。
④ 三条铁律
--------------------------------------------------------------------
- 输出必须机器可读。给人看的排版,在流水线里全是噪声。
- 失败必须靠退出码表达。日志里写「出错了」但退出码是零,等于没报错。
- 不许等输入。任何「请确认」都必须提前变成参数,或者直接拒绝。
⑤ 为什么这一章单独成章
--------------------------------------------------------------------
前六章讲的是「模型怎么工作」,这一章讲「它工作在没人看着的地方」。
没人看着的时候,前面那些「它大概会做对」的假设全都要换成「做错了谁来兜」。
! 这一段的 agent 是替身,真实 Claude Code 用 -p 走非交互模式,语义一致。
①同一个脚本两种入口:带 -p 一次跑完回一行 JSON,不带 -p 进交互循环回三行。 差的那两行里有一行是在问你要不要继续。 ②分界线不在「有没有界面」,在「谁来收结果」。一旦结果给程序看,排版、颜色、标题全是噪声。 ③第二条指令要删未提交的改动,退出码 3,标准输出空,原因只写在标准错误里。 这就是无人值守的规矩:拿不准就停。 如果它在那里等人回答,CI 会一直挂到超时——而超时这个现象,既可能是网络、也可能是死锁、 还可能是它真的在等。 ④三条铁律:输出机器可读、失败靠退出码、不许等输入。 三条在本地开发时全都无所谓,所以最容易在第一次上 CI 时才被发现。
30 秒实验
- 把「要不要继续」那行改成「默认继续」再跑一遍。风险从「卡住」变成什么?这两种失败哪种更难发现?
- 给交互模式也加一个退出码约定(比如 4 表示「用户中断」),重跑一遍。为什么交互模式也需要退出码?
- 把 -p 模式的输出从 JSON 改成一句中文,再想一下:如果下游是 jq,会发生什么?
常见误区
- 在 headless 里保留「请确认」的提问。没人回答,它会挂到超时。
- 失败只写在日志里,退出码还是零。流水线会显示绿色,问题留到线上。
- 输出用人读的格式,下游拿 jq 去解析。第一次改需求就得回头改参数。
- 以为无人值守就是「少了个人」。它少的是一次判断的机会,判断必须提前写进参数。
- 把交互模式的做法照搬到流水线。人推循环的前提是有人在,而这个前提已经没了。
自测三问(点开对答案)
1. 交互模式和无人值守的分界线在哪? —— 在「谁来收结果」。结果给人看时,人可以在中途纠偏; 结果给程序看时,判断必须提前写进参数和退出码。 2. 无人值守下遇到需要人拍板的事,该怎么办? —— 拒绝,并说明原因。用一个非零退出码停下,绝不能等着人回答。 3. 三条铁律是什么? —— 输出必须机器可读、失败必须靠退出码表达、不许等输入。模式变了,参数就成了唯一的约束手段。下一节把这四组参数拆开看。
- ☐ (本篇完,下一篇 7.2 核心参数体系:4 个维度的控制)
相关笔记
- 处理器类型:四种处理器和成本的阶梯
- 核心参数体系:参数体系
- Claude Code 橙皮书 · 索引
- 🐎 Claude Code 实战 · 索引