🐎 第7章 无为而治:Headless 模式与 CI/CD 集成
📄 一句话概括

「本地能跑、CI 不能跑」十次里有八次是环境变量。它们不会一起报错,而是一个一个让行为变形。 演示七项里三项必填。 最隐蔽的是「无人值守」那个标记:不设它,程序会以为有人坐在那儿,然后等一个不会来的回答。

📝 本章和前面几章的关系

上一节讲上下文怎么传,这一节讲环境怎么配。 这一节的东西可以直接当清单用——贴进仓库的 README,比记在某个人的脑子里有用。

一、七个变量的分工

必填三项:认证的密钥、标记无人值守的开关、工作目录。 可选一项:往 PR 写评论的令牌。不设也能跑,只是审查结果贴不上去。 建议三项:成本护栏、关彩色输出、时区。不设不会失败,但会让日志难读、账单难算。

这份清单的价值不在「全」,在于它把「必须」和「建议」分开了——不分的话,人会把七项都当成必填。

二、真跑:什么都没设会怎样

演示里第一份环境什么都没设,检查器退出码 1,报三项缺失、两项建议。 第二份按清单补齐必填,退出码 0,只剩两条建议。 要注意的是:检查器只报了 missing,而真实的后果比「缺一个变量」隐蔽得多。

比如少了无人值守那个标记,程序会以为有人坐在那儿,于是等一个永远不会来的回答。

三、性能调优的三类参数

并发:同时跑几个。设太大反而慢——抢 CPU、撞文件、互相等锁。 超时:多久算失败。设太长失败发现得晚,设太短大仓库永远超时。 重试:失败几次再算失败。对幂等步骤有用,对「改了代码」的步骤有害。

第三条最容易被忽略:重试一个已经改过文件的步骤,会把改动做两遍。

四、MCP 在 CI 里的两条规矩

第一,只连只读的。CI 里没人看着,写权限的服务器一旦用错,后果直接进仓库。 第二,密钥从环境变量来。这和第 6.4 节那条是同一条规矩,只是换了个执行环境。 还有一条隐含的:CI 里的 MCP 服务器要能「无状态启动」——不能依赖上一次的会话。

因为每一次流水线都是新机器,任何「本地缓存」都不存在。

五、清单的用法

贴进仓库 README,或者做成一个 .env.example,和代码放一起。 这样新人的第一个 PR 就会照它配,而不是来问你「为什么我这儿跑不起来」。 更好的做法是让检查器进流水线:缺必填就红,别等跑到一半才失败。

这条和 5.11 节那个第一判据是一路的:能自动查的,别靠人记。

六、动手 Demo:七个环境变量,缺哪个会让流水线悄悄跑歪

「本地能跑、CI 不能跑」十次里有八次是环境变量。 这里真跑两份环境:一份什么都没设,一份按清单补齐必填。

MUST = [u"API_KEY", u"CI", u"WORKDIR"]
WARN = [u"MAX_TURNS", u"NO_COLOR"]

miss = [k for k in MUST if not os.environ.get(k)]
sys.exit(1 if miss else 0)

实跑输出:

--------------------------------------------------------------------
七个环境变量,缺哪个会让流水线悄悄跑歪
--------------------------------------------------------------------

  「本地能跑、CI 不能跑」十次里有八次是环境变量。
  它们不会一起报错,而是一个一个地让行为变形。

  ① 七个变量的分工
  --------------------------------------------------------------------
| 变量 | 干什么 | 必不必填 | 不设会怎样 |
|---|---|---|---|
| API_KEY | 认证 | 必填 | 起不来,报鉴权失败 |
| CI | 标记无人值守 | 必填 | 它会等交互输入,一直挂到超时 |
| WORKDIR | 工作目录 | 必填 | 相对路径全飘,找不到配置 |
| GH_TOKEN | 往 PR 写评论 | 可选 | 只读不写,审查结果贴不上去 |
| MAX_TURNS | 成本护栏 | 建议 | 跑到超时才停 |
| NO_COLOR | 关彩色 | 建议 | 日志里全是转义符,grep 不到关键字 |
| TZ | 时区 | 建议 | 日志时间和别处对不上 |


  ② 真跑:两份环境,一份缺三项
  --------------------------------------------------------------------
  什么都没设:退出码 1
    missing: API_KEY

    missing: CI

    missing: WORKDIR

    recommend: MAX_TURNS

    recommend: NO_COLOR

    result: 3 missing, 2 recommended
  按清单补齐必填:退出码 0
    recommend: MAX_TURNS

    recommend: NO_COLOR

    result: 0 missing, 2 recommended
  第一份漏了 CI 这个变量,检查器只报了 missing,但真实后果更隐蔽:
  程序会以为有人坐在那儿,于是等一个永远不会来的回答。

  ③ 性能调优的三类参数
  --------------------------------------------------------------------
| 类别 | 调什么 | 注意什么 |
|---|---|---|
| 并发 | 同时跑几个 | 设太大反而慢:抢 CPU、撞文件 |
| 超时 | 多久算失败 | 设太长,失败发现得晚;设太短,大仓库永远超时 |
| 重试 | 失败几次再算失败 | 对幂等步骤有用,对「改了代码」的步骤有害 |

  第三条最容易被忽略:重试一个已经改过文件的步骤,会把改动做两遍。

  ④ MCP 在 CI 里的两条规矩
  --------------------------------------------------------------------
  第一,只连只读的。CI 里没人看着,写权限的服务器一旦被用错,后果直接进仓库。
  第二,密钥从环境变量来。这和第 6.4 节那条是同一条规矩,只是换了个执行环境。

  ⑤ 清单的用法
  --------------------------------------------------------------------
  把这张表贴进仓库的 README,比写在某个人的笔记里有用。
  新人的第一个 PR 就会照它配,而不是来问你「为什么我这儿跑不起来」。
  ! 变量名是示意,实际以各版本文档为准;这里讲的是每一类变量为什么必须有。

①七项分三档:必填三项(密钥、无人值守标记、工作目录)、可选一项(写 PR 评论的令牌)、 建议三项(护栏、关彩色、时区)。 ②什么都没设时退出码 1,报三项缺失、两项建议;补齐必填后退出码 0,只剩两条建议。 ③最隐蔽的是少了「无人值守」那个标记:检查器只报 missing,但真实后果是程序以为有人坐在那儿, 于是等一个永远不会来的回答。 ④调优三类:并发(设太大反而慢、撞文件)、超时(太长发现得晚、太短永远超时)、 重试(对幂等步骤有用,对「改了代码」的步骤有害)。 重试那条最容易被忽略:重试一个已经改过文件的步骤,会把改动做两遍。

30 秒实验

  1. 把「建议」那三项也提到必填,重跑一遍。第一份环境的退出码变了吗?把建议当必填的代价是什么?
  2. 给检查器加一条「工作目录必须存在」,重跑一遍。这条检查和「变量有没有设」差在哪一类问题上?
  3. 把重试加上「最多两次」,然后想一下:哪一类步骤重试是安全的,哪一类不是?

常见误区

  1. 不设无人值守标记。程序以为有人在,等着一个不会来的回答,一直挂到超时。
  2. 把七项全当必填。人会挑着填,最后连真的必填项也漏了。
  3. 用相对路径当工作目录。钩子和脚本的工作目录未必是项目根,相对路径会飘。
  4. 给「改了代码」的步骤配重试。重试会把改动做两遍,第二次可能直接冲突。
  5. 以为环境变量是运维的事。它决定了模型在 CI 里的行为,是配置的一部分。
自测三问(点开对答案) 1. 哪三项是必填的? —— 认证密钥、标记无人值守的开关、工作目录。 前者不设起不来,中者不设会挂到超时,后者不设相对路径全飘。 2. 为什么「无人值守」这个标记最隐蔽? —— 因为它不设的时候不报错。程序会以为有人坐在那儿,然后等一个永远不会来的回答,表现为「超时」。 3. 哪一类步骤不能配重试? —— 会改文件的步骤。重试会把改动做两遍,第二次可能直接冲突或者覆盖。

清单有了,下一节换成另外两个平台:同一套命令,壳要重包一遍。

相关笔记