跳过正文

NixOS(四):用AI维护配置——维护者技能

·4425 字·9 分钟
锦李本鲤
作者
锦李本鲤
光锥之内,皆为命运。
NixOS系列 - 这篇文章属于一个选集。
§ 4: 本文

缘起
#

第一篇里说过,我们回到NixOS的核心原因是分工变了:人负责决定"我要什么",AI负责把它翻译成正确的Nix代码并验证它。第二、三篇讲了NixOS的机制和我们仓库的组织方式。这一篇讲"维护"本身:AI接手一次配置变更时,具体怎么做、按什么顺序做、哪里绝不能越界。

答案是一份写下来的工作流文档:/home/lijin/nixos-config/.codex/skills/nixos-config-maintainer/SKILL.md。它是一份"技能(skill)"——AI代理的提示词文件,当任务涉及修改NixOS模块、主机硬件、内核、服务、包、flake输入或README与代次记录时,代理就按它执行。它的作用是把第三篇里那套"日常变更流程"变成一份可执行、可检查、有明确禁令的契约:不是"大概这样做比较好",而是"第一步做什么、验证用哪两条命令、什么情况下不算完成"。

这份文档不长(一百多行),但每一条都对应一个真实场景。下面逐段讲它的内容和设计理由,引用均来自该文件。

前提
#

技能文件的定位
#

技能文件的开头是它的使用条件,这段文字本身就是路由规则:

Maintain this repository’s multi-machine NixOS flake and its operational documentation. Use when changing NixOS modules, host hardware, kernels, services, packages, flake inputs, README instructions, generation notes, or when validating, committing, and syncing configuration changes.

注意它把"改配置"和"改文档"放在同一个技能里。这不是偷懒:在我们的仓库里,README和代次记录(build notes)与配置代码同等重要——一篇文档写错了,AI下次接手时就会基于错误上下文行动。所以"维护文档"必须和"维护配置"用同一套验证与提交纪律。

仓库模型:先把作用域说死
#

工作流的第一节是"仓库模型",把第三篇讲过的三个作用域重新写成硬性规则。这里只挑技能文件新增的几条:

  • 主机只能选择自己的硬件模块:“A host must not inherit another host’s hardware module.“当前Surface主机的名字是surface-pro-6,它使用nixos-hardware.nixosModules.microsoft-surface-pro-intel。这条规则防的是AI在加新机器时"顺手"复用Surface模块的事故。
  • 保留nixos兼容别名,除非用户明确要求删除。
  • 每台机器用自己的flake target重建:Surf上是.#surface-pro-6,Halo上是.#halo。AI不允许"在一台机器上改完顺手重建另一台”。
  • flake.lock是被跟踪的配置:更新它是刻意行为,必须审查diff,而不是构建失败的副作用。

标准工作流:八步
#

这是技能文件的核心,原文按顺序编号,我按内容分三组讲。

动手前:检查与分类(第1–3步)
#

第1步是只读检查。在编辑任何文件之前,先:

  • git status(有没有别人未提交的改动要保留);
  • 读相关的主机/模块文件;
  • 看最近的Git历史;
  • 看当前的NixOS代次profile。

最后一点值得展开:/nix/var/nix/profiles/system-*-link记录了这台机器实际激活过的每个代次。AI改配置前先看一眼"现在跑在机器上的是什么”,才能区分"仓库里的配置"和"机器上的配置"——这两者可能差了好几个提交,尤其在构建失败、代次没成功的时候。

第2步是分类。把这次要改的每一条设置归入三类之一:共享(modules/common.nix)、主机专属(hosts/<主机>/configuration.nix)、生成硬件数据(hosts/<主机>/hardware-configuration.nix),“Place it in the narrowest appropriate scope."——放到能放的最窄作用域。这是AI最容易犯错的步骤:把Surface专属的策略写进common.nix,或者把共享策略重复写进每台机器。把规则写成一句话,比写三段解释更有效。

第3步是最小变更。“Make the smallest declarative change.“如果是新机器,固定动作是:建主机目录和硬件文件,加一个独立的nixosConfigurations.<name>入口,导入Surface专属模块。

改动后:文档、验证、提交(第4–7步)
#

第4步是同步README。只要命令、主机名、目录结构、激活步骤、验证方法、回滚方法或硬件行为变了,README就要跟着改,并且"Include instructions useful to a newcomer”——写给没参与这次改动的人(包括三个月后的AI)能看懂。

第5步是代次记录。这一步的规则最具体,因为它对应第三篇讲过的build notes约定。技能文件要求记录每个真实代次,格式固定:

1
2
3
4
5
6
## Generation N

- **Time:** YYYY-MM-DD HH:MM
- **Git commit:** `short-id`
- **Main changes:**
  - ...

信息来源有两个,且要求两者都看:

  • Git历史:git log --reverse --date=iso-local --format='%h|%ad|%s'
  • 代次链接:/nix/var/nix/profiles/system-*-link,包括链接时间戳和内核target。如果nix-env --list-generations因需要root锁而跑不了,就用profile链接推断,并明确说明这是推断

然后是三条禁令,全部围绕"不许编”:

  • 绝不虚构一个代次;
  • 绝不声称一个未激活的配置正在运行;
  • 只改文档或工具的提交,放在Git-only follow-up changes一节,不占代次编号。

第6步是验证,命令是固定的两条:

1
2
git diff --check
nix flake check --no-build --show-trace

git diff --check抓空白错误和冲突标记这种低级问题;nix flake check要求每一个打算构建的nixosConfigurations.<name>都能评估通过。技能文件还要求:涉及内核、bootloader、文件系统或硬件的变更,要向用户说明"第一次真实构建可能很久,需要本地激活/重启”,并且在验证完成之前不许说运行系统已经变了。这一条直接对应第一篇里的洞察——NixOS"声明式+可验证"的价值,就是在AI手里变成了一条可执行的规则。

第7步是提交纪律

  • 审查完整diff,包括改名和删除;
  • git add属于本次任务的文件;
  • 提交信息简短;
  • 沿用仓库现有的Git作者身份,缺失时问用户,而不是自己编一个;
  • 如果build notes需要新commit ID,先提交配置,再用一个后续提交补代次记录(顺序不能反,否则笔记里会引用一个不存在的哈希)。

同步:第8步
#

第8步是推送:只在用户要求同步、或任务明确包含推送时才推。使用已认证的gh/Git环境;验证分支干净且跟踪origin。这条主要是给沙箱环境用的提示——某些代理环境里推送需要认证凭据,技能文件明确"never ask the user to paste a token and never print token contents"。

激活交接:AI的边界在哪里
#

技能文件单独一节讲"Activation handoff"。核心原则是:AI准备好命令,人执行激活。对当前主机,AI给出对应flake target:

1
2
3
4
cd /home/lijin/nixos-config
sudo nixos-rebuild switch --flake .#surface-pro-6   # on Surf
sudo nixos-rebuild switch --flake .#halo            # on Halo
sudo reboot                                         # only when required

重启后的验证也按机器定制。Surface的IPTS触摸板驱动是模板单元,要这样查:

1
2
uname -r
systemctl list-units --all 'iptsd@*' --no-pager

Halo上基于容器的服务(比如ROCm推理容器q38rocm):

1
2
uname -r
systemctl status docker-q38rocm.service

这里还有一个反直觉的细节:技能文件明确提醒不要要求内核名包含surface——配置好的Surface内核可能只报告版本号,比如6.19.8。这是验证脚本里最容易写错的断言。

激活失败时的回滚指引也是标准动作的一部分:

1
sudo nixos-rebuild switch --rollback

失败与安全规则:四道护栏
#

技能文件最后一节列出的"Failure and safety rules",全部是不允许AI做的事:

规则防的是什么
除非用户明确要求,不跑git reset等破坏性命令AI"顺手"丢弃用户未提交的工作
本仓库是选定flake源时,不改/etc/nixos两处配置漂移(第三篇讲过我们是有意分离的)
不随手编辑生成的硬件文件,只为对应主机重新生成并审查nixos-generate-config改的是本机硬件描述
评估失败就修好再验证、再提交;构建太贵跑不完时,如实报告"评估通过、真实构建待完成"把"能编译"和"能跑"混为一谈

最后一条特别符合AI协作的场景:Surface本地编译内核动辄数小时、数十GiB磁盘,AI没有耐心等也不该等——正确的做法是区分并如实说出评估(秒级,必须过)和构建(小时级,可能待完成)两个状态。

为什么写成技能文件而不是文档
#

回头看这套设计,它和"写一篇维护手册"的区别在于:

  1. 它是给AI读的,且以AI最常犯的错为中心。作用域分类、“不虚构代次”、“不声称未激活的配置在运行”——每一条都对应AI在维护任务里真实的失误模式,而不是泛泛的最佳实践。
  2. 它把验证做成硬门槛git diff --check + nix flake check是"提交前必须过"的关卡,这正好用上了第一篇说的NixOS机器可验证性。
  3. 它划清了人机边界。评估、修改、提交、记录是AI的活;sudo nixos-rebuild switch、重启、最终验收是人的活;推送只在被要求时发生。
  4. 它和仓库文档互为校验。代次记录、README、Git历史、profile链接四个来源互相印证,任何一处失真都会被其他三处戳穿——“绝不虚构"才有真正的约束力。

一句话总结:AI擅长写Nix,NixOS擅长兜底,技能文件负责把两者接起来。第二篇讲的机制在这一篇里全部变成了可执行步骤,下一篇开始,就轮到具体机器的迁移记录了。

相关文章
#

NixOS系列 - 这篇文章属于一个选集。
§ 4: 本文