上一篇讨论了 NixOS 为什么适合与 AI 协作,以及怎样把主机地址、SSH 安全和 Test 金丝雀 发布变成机器可验证的约束。这一篇进入更接近真实运维的部分:权限、secrets 、存储、告警 和测试中的失败案例,以及如何向 AI 描述任务、如何判断验证证据。
本文涉及的公开配置与实现:LokiSharp/nix-config。 真实凭据保存在独立私有仓库中,不包含在公开配置里。
七、案例四:健康检查不应该等于给 AI 一个 root shell
为了让 AI 能部署后验证节点,最简单的办法是允许它 SSH 到 root ,然后运行任意命令。
我认为这不是一个好边界。
现在健康检查通过专用普通用户连接。这个用户:
- 不属于
wheel; - 不是 Nix trusted user ;
- 不负责部署;
- 不能运行任意 sudo 命令。
只有少数确实需要权限的检查通过一个 Nix 生成的固定 helper:
deployment-health-root
它只接受有限动作:
- 读取 audit 状态;
- 读取当前启动中的高优先级 journal ;
- 查询 BIRD 控制套接字;
- 查询 ZeroTier 状态;
- 以 postgres 用户执行只读的
SELECT 1。
调用使用 sudo -n,不会询问密码,也不能把参数变成任意 shell 命令。
这套设计并不是为了防止 AI“作恶”,而是为了减少错误的作用面。AI 和人类都会输错命令, 最可靠的权限模型不是相信操作者永远正确,而是让错误命令没有足够权限造成更大事故。
八、案例五:AI 很擅长整理重复模块,但必须防止它删掉“有意无用”的东西
仓库引入 Nix formatter 、Statix 和 Deadnix 后,AI 做了一轮全仓库清理:
- 格式化 Nix 文件;
- 修复
inherit等静态分析建议; - 删除未使用的参数和声明;
- 把检查加入 CI 和 Git hooks 。
大多数修改都很好,但有一次它删除了 secrets 目录中的未使用模板。
从 Deadnix 的角度看,那些声明确实没有消费者。
从我的角度看,它们是故意保留的权限模板,用来提醒未来新增 secret 时选择正确的 owner 、 group 和 mode 。
我问:
secrets 里没有使用的模板为什么要删?
最后我们恢复了这些模板,并明确了不同安全级别:
- 普通服务 secret ;
- 仅服务账户可读;
- root-only ;
- 模板文件与单值 secret 的不同权限;
- 哪些预留参数不能因为“当前没有使用”而删除。
后来我又继续追问:
NOTE: the args not used in this file CAN NOT be removed! 这些修改是?
这件事非常能代表 AI 协作的边界。
静态分析知道变量是否被引用,却不知道某个例子是不是组织知识。AI 如果只追求“零 warning”, 会把代码整理得更干净,同时把未来维护者需要的语义一起擦掉。
解决办法不是停用 Deadnix ,而是:
- 明确哪些文件承担模板或接口职责;
- 使用工具支持的忽略方式;
- 在注释中说明为什么保留;
- 把“不得删除的占位符”视为仓库契约;
- 清理后检查 Git 历史中是否还有类似误删。
工具负责发现可疑点,人负责定义什么叫“无用”。
九、案例六:从 Nushell 的 any 到可维护的部署 API
仓库的部署和健康检查逻辑主要写在 utils.nu,目前超过一千行。
Nushell 相比传统 shell 的优势,是管道中传递的不只是文本,还可以是 list 、record 、table 和带类型的值。对于 AI 来说,这一点非常有帮助,因为函数契约可以直接写进代码:
def check-host [
host: record
since_minutes: int
]: nothing -> int {
# ...
}
我曾经要求 AI:
可以帮我把 nu 脚本的 any 类型都补全么?
第一轮完成后,我又让它继续检查是否还有其他 any。
这个过程不是单纯追求类型洁癖。部署脚本里的类型可以提前暴露:
- 期望主机列表却得到字符串;
exit_code可能不存在;- 网络地址可能是 null ;
- closure 的输入输出约定不一致;
- 从 JSON 读取的数据形状发生变化;
- 函数原本返回 nothing ,却在某个分支返回 record 。
传统 shell 中,这些错误经常在某个远端命令执行到一半才出现。Nushell 至少能把一部分错误 提前到本地解析和测试阶段。
不过,类型也不能乱补。为了消灭 any 而写一个不真实的精确类型,反而会迫使代码用大量
转换绕过类型系统。正确做法是先观察真实数据形状,再收紧边界。
十、案例七:让存储维护从“我记得要做”变成系统能力
我后来把关注点从网络转向存储:
接下来做 Btrfs 定期维护和磁盘健康检查吧。
一开始的问题是“哪些节点需要加”,最后形成的是一组自动派生能力:
- 只要最终配置中存在 Btrfs 文件系统,就启用定期 scrub ;
- 同一设备上的多个子卷只 scrub 一次;
- 每日检查 Btrfs device error counters ;
/persistent每小时创建本地快照;- Server 的
/data/apps额外创建应用快照; - 快照只有来源变化时才创建;
- 按小时、天、周、月分层保留;
- 快照成功时间和数量导出为 Prometheus 指标;
- 快照目录未挂载、快照缺失、任务长时间未成功都会告警;
- 物理盘节点启用 smartd 和 smartctl exporter ;
- OVH 的 mdraid1 额外启用 mdadm collector 和降级告警。
这里有几个很适合 AI 的工作:
第一,扫描所有 fileSystems,找出哪些挂载点其实位于同一个 Btrfs 设备,避免重复 scrub 。
第二,从主机元数据筛选需要物理盘监控的节点,自动生成 VictoriaMetrics targets 。
第三,为跨模块关系补测试。例如:
- 启用
diskHealth的节点必须同时启用 smartd 和 exporter ; - 监控中心的 scrape targets 必须覆盖所有启用节点;
- Btrfs 来源和快照目录必须在同一设备;
- timer 必须进入部署健康检查清单;
- OVH 必须显式启用 mdadm collector 。
第四,把最终策略写成文档。现在仓库中有一份完整的存储、快照和磁盘健康说明,记录执行 周期、保留策略、空间代价、手动检查命令和单文件恢复方法。
这件事也让我更清楚地看到,AI 不只是代码生成器。它很适合把“实现、测试、运维命令和 文档”同时保持一致。
但仍然有一个必须由人强调的事实:
同盘 Btrfs 快照不是备份。
快照能帮助恢复误删和错误配置,不能抵抗整盘损坏、机器丢失或攻击者同时删除来源与快照。 如果不明确说出威胁模型,AI 很容易在“已经有快照”以后写出过度乐观的结论。
十一、案例八:从一封烦人的告警邮件追到 Nix 自己的崩溃
监控系统上线以后,我收到过不少告警:
- journald coredump ;
- CPU iowait ;
- 磁盘写延迟;
- systemd oneshot 失败;
- Btrfs 快照异常;
- node-exporter 不可达。
其中 Lycheen-US-SLC 最麻烦。它的供应商虚拟磁盘会周期性出现高延迟,小型同步写入也可能
阻塞几秒。结果是:
- iowait 升高;
- journald 写日志受阻;
- 服务错误产生更多日志;
- journald 自己崩溃;
- coredump 和磁盘延迟同时告警。
如果只处理表面现象,可以不断提高阈值、删除 coredump 或禁用规则。但我们最后做的是:
- 普通 journal 改为 volatile ;
- 限制 RuntimeMaxUse 和单文件大小;
- audit 和外部 coredump 继续持久化;
- 对高噪声代理日志降低级别;
- journald 告警只针对最新 15 分钟的真实崩溃;
- coredump 指标改为扫描外部文件,避免监控脚本反过来读取并加重 journal ;
- iowait 必须持续超过阈值 5 分钟才 firing 。
最近又出现了另一个例子:VM-NixOS 上有两次 nix-daemon 2.34.7 coredump 。
AI 读取 Alertmanager 后发现,这是当时唯一 firing 的告警。继续查看 coredump ,确认:
- 两次都是
SIGABRT; - 异常为
nix::Interrupted: interrupted by the user; - 堆栈位于
TunnelLogger::enqueueMsg、ignoreExceptionExceptInterrupt和线程池; - 主 daemon 后续仍然接受连接;
- 这不是机器持续离线,而是两个 worker 进程在客户端中断路径触发了 Nix bug 。
这时又发现一个告警设计问题:旧规则只要 24 小时内存在任何普通应用 coredump ,就持续 firing ,并每 4 小时重复发信。
于是规则被拆成两层数据:
- 24 小时计数继续用于 Grafana 和历史分析;
- 最新应用 coredump 时间戳用于实时告警;
- 只有最新崩溃发生后的 15 分钟内 firing ;
- 超过时间窗口自然 resolved ,不删除证据。
这个例子很好地说明了监控中的一个原则:
保留事件历史,与持续呼叫人处理,是两件不同的事。
AI 很适合追踪指标、规则、采集脚本和邮件之间的链路;人需要决定什么值得在凌晨持续打扰。
十二、案例九:测试不是越多越好
AI 特别喜欢补测试,因为测试看起来是“提高质量”最直接的方式。
但我的仓库曾经出现过几类没有意义的测试:
1. 恒真测试
表达式只是把实现中的值重新组合一遍,再和自己比较。它永远不会因为真实回归失败。
2. 不可达分支测试
上游类型已经排除了某种状态,下游还在断言“不会出现该状态”。这增加阅读成本,却没有新增 保障。
3. 原样快照测试
把某台主机的整份元数据抄进 expected 。任何合理修改都会让测试失败,但失败并不说明系统 不安全,只说明文本变了。
4. 历史幽灵测试
检查一个已经废弃的地址段、路由或兼容字段不存在。新维护者会误以为它仍然是重要需求。
后来我们逐个讨论这些测试:
这个检查有什么意义?
有必要改成可以执行的测试?
删除吧。
最终保留的测试更偏向不变量:
- 地址和 ID 全局唯一;
- 字段之间依赖完整;
- 安全选项最终值正确;
- 防火墙 forward chain 语义正确;
- SSH 端口在各消费者之间一致;
- 部署顺序不可绕过;
- 必检 unit 真实存在;
- 监控 targets 覆盖元数据声明;
- 规则的时间窗口和严重级别正确。
下一步我还准备引入 promtool test rules,用模拟时间序列验证:
- node-exporter 连续离线五分钟才告警;
- 短暂失败不会告警;
- iowait 必须持续超过阈值;
- coredump 在第十四分钟告警、第十五分钟恢复。
现在已有的 Promtool 检查只证明 YAML 和 PromQL 语法正确;行为测试才证明规则按我们理解的 时间线工作。
十三、它和 Ansible 、脚本、容器编排有什么不同
写到这里,可能会有一个自然的问题:
这些事情用 Ansible 、Shell 、Terraform 或容器不也能做吗?
当然能。NixOS 并没有垄断声明式运维,AI 也可以操作任何一种工具。这里真正值得比较的, 不是哪一种语法更漂亮,而是 AI 所面对的“系统边界”有多完整。
1. 和 Shell 脚本相比
Shell 的优势是普及、直接,几乎每台 Linux 都能运行。遇到一次性问题时,让 AI 写几条 诊断命令通常是最快的。
问题在于,Shell 默认描述的是操作过程:
apt install nginx
cp nginx.conf /etc/nginx/nginx.conf
systemctl restart nginx
这段脚本没有自然回答:
- nginx 原来是什么版本;
- 配置文件之前由谁管理;
- 第三步失败后前两步是否需要撤销;
- 重复执行是否得到相同结果;
- 谁保证另一段脚本不会覆盖同一个文件;
- 半年后怎样从机器状态还原当时的意图。
AI 很容易继续在这个过程上叠加命令。第一次是三行,出错后补一个 sed,再出错补一个
chmod,最终形成一段只有当前聊天上下文才能解释的操作历史。
NixOS 更倾向于描述结果:
services.nginx = {
enable = true;
recommendedProxySettings = true;
};
它没有消灭底层操作,但把“如何从旧状态切到新状态”交给模块系统和 activation 处理。 AI 的主要修改对象因此是期望状态,而不是一串只执行一次的补丁。
我仍然大量使用命令行脚本,尤其是部署编排、探测和故障调查。区别是,这些脚本不负责偷偷 创造系统事实。长期状态尽量回到 Nix 模块中,脚本负责验证它。
2. 和 Ansible 相比
Ansible 同样可以把运维写入 Git ,也有幂等 module 、inventory 、check mode 和丰富生态。 如果团队已经在使用 Ansible ,没有必要为了 AI 立刻迁移。
我感受到的区别主要有三点。
第一,Ansible playbook 通常描述从当前状态走向目标状态的步骤; NixOS 模块先合并成一份 最终配置,再构建切换结果。AI 修改 Nix 时,可以对最终 option 值做求值,不必只推测任务 执行后的状态。
第二,Nix 包依赖、服务配置和系统配置共享同一套表达式与 store 。Ansible 经常还要依赖 目标机软件源、Python 环境和外部模板。两者都能固定版本,但 Nix 的输入锁定更接近默认 工作方式。
第三,NixOS generation 是系统级结果。Ansible 可以自己实现回滚,但每个 role 是否留下 可恢复状态,要由作者设计。
反过来说,Ansible 对现有异构 Linux 、网络设备和命令式 API 的覆盖明显更广。NixOS 最舒服 的场景,是你真的愿意让目标主机成为 NixOS ,并把大部分状态纳入声明。
3. 和 Docker 、Kubernetes 相比
容器解决的是应用交付边界,不等于主机操作系统管理。
我可以用容器运行 Grafana 、数据库或 Web 应用,但仍然要处理:
- 内核和启动参数;
- 文件系统、磁盘、RAID 和 SMART ;
- SSH 、防火墙和路由;
- systemd 与宿主机 timer ;
- 用户、权限和 secrets ;
- node-exporter 与宿主机日志;
- 容器运行时本身。
NixOS 可以负责宿主机,也可以声明容器。两者不是替代关系。
Kubernetes 的控制回路和声明式 API 同样很适合 AI ,因为资源有 schema 、有状态、有事件。 但对我这类十台左右、包含路由和存储职责的个人基础设施,引入一套集群控制面未必比直接 管理 NixOS 更简单。
一个朴素的选择标准是:
- 如果主要问题是跨大量异构设备执行任务,优先考虑 Ansible ;
- 如果主要问题是编排大规模容器工作负载,优先考虑 Kubernetes ;
- 如果希望把主机从 bootloader 到服务都作为一个可求值结果管理,NixOS 很有吸引力;
- 如果只是修一台临时机器,Shell 可能已经足够。
AI 不会改变这些工具的适用范围,只会放大它们原有的结构。边界清楚的系统,AI 更容易得到 可靠反馈;边界模糊的系统,AI 只会更快地产生更多变化。
十四、一次完整修改到底怎样发生
前面的案例按主题拆开讲,可能仍然显得抽象。下面用一次典型的监控修改,还原我和 AI 的 实际协作节奏。
1. 告警到达
我把 Alertmanager 邮件贴进对话:
alertname = HostCpuHighIowait
instance = 198.18.0.5:9100
severity = warning
VALUE = 11.45
一开始我只知道它经常报警,不知道是磁盘真的濒临损坏、供应商虚拟化噪声,还是阈值太敏感。
一个不够谨慎的 AI 很可能直接建议把阈值从 10% 改成 20%,或者在 Alertmanager 中 silence 这台机器。这样邮件确实少了,但没有回答实际问题。
2. 先建立时间线
我要求继续监控并检查具体原因。AI 依次收集:
- 当前 iowait ;
- 一段时间内的峰值和持续时间;
sda的写延迟;- SMART 是否报告介质错误;
- 内核是否有 I/O reset ;
- journald coredump 的发生时间;
- 同时段有哪些 timer 、snapshot 或更新任务;
- 节点的虚拟化和磁盘类型;
- 最近配置切换时间。
这些信息的价值不在单个数值,而在顺序:
磁盘写延迟升高
-> iowait 短时超过 10%
-> 日志写入阻塞
-> journald worker 崩溃
-> coredump 与 iowait 告警一起出现
如果时间顺序相反,结论可能完全不同。例如某个服务先疯狂刷日志,才把磁盘打满,那么应该 处理服务日志,而不是先怪供应商磁盘。
3. 把“事实”和“推断”分开
AI 在调查中需要明确区分:
- 事实:某时刻的指标、journal 行、unit 状态、coredump 堆栈;
- 推断:高延迟可能导致 journald 阻塞;
- 尚未证明:磁盘硬件已经损坏;
- 反证:SMART 没有介质错误,内核没有 reset ,延迟会自行恢复。
这个区分非常重要。AI 的自然语言很流畅,很容易把“符合现象的解释”写成“已经确认的根因”。
在我的节点上,最后更合理的描述是:供应商提供的虚拟磁盘存在周期性高延迟,触发短时 iowait ;没有足够证据证明物理盘即将损坏,但 journald 的写入模式确实放大了影响。
4. 先处理放大器,再调整告警
我们没有先关告警,而是减少故障放大:
- 将普通 journal 放在内存;
- 限制 journal 总量和单文件大小;
- 保留 audit 与外部 coredump 的持久证据;
- 减少高频代理日志;
- 避免监控采集再次扫描重型 journal 路径。
完成这些以后,再把 iowait 规则改成必须连续超过阈值五分钟。
这不是简单的“提高容忍度”。瞬时峰值在这台虚拟机上没有明确行动价值,而持续五分钟的高 iowait 仍然值得处理。告警定义应当对应可执行动作。
5. 本地验证
修改后不是立即部署。AI 先检查:
just fmt
statix check .
deadnix --fail .
just test
nix flake check --all-systems --no-build --show-trace
如果涉及 Prometheus 规则,还运行 Promtool 语法检查。
这里要警惕一种很常见的假成功:命令通过,但目标文件没有进入 Flake source ;或者测试只
检查规则能解析,没有检查 for: 5m 的行为。退出码是证据之一,不是全部证据。
6. 审查差异与提交
我会让 AI 解释:
- 哪些文件改变;
- 每个变化如何对应告警链路;
- 是否影响所有节点还是单个节点;
- 是否有阈值调整;
- 是否删除了采集证据;
- 如何回滚。
然后把不同语义拆成独立提交。这样如果日志策略有效、告警窗口不合适,可以只回退后一项。
提交信息不追求花哨,重点是让半年后的自己可以搜索:
fix(logging): reduce journald pressure on slow disks
fix(monitoring): require sustained high iowait
7. Test 与目标节点
公共模块发生改变时,先部署 Test 。Test 能确认模块求值、activation 、unit 和基本健康。
但 Test 没有同一种慢磁盘,所以它不能证明 Lycheen 的问题已经消失。随后仍需部署真实目标, 观察:
- journald 是否稳定;
- timer 是否成功;
- 指标是否继续采集;
- iowait 峰值是否缩短;
- Alertmanager 是否自然 resolved ;
- 新规则会不会漏掉真正的持续故障。
这也说明 Test 节点的边界:它是发布闸门,不是生产环境的完美复制品。
8. 把结论写回系统
一次调查如果只停留在聊天里,下次会从头再来。
因此最终成果至少应该进入以下一种载体:
- 模块注释记录节点例外的原因;
- 测试记录不能破坏的约束;
- runbook 记录调查命令;
- commit 记录决策;
- 监控规则记录时间窗口;
- 文档记录已知代价。
AI 在这里最有价值的不是记住对话,而是帮助把对话压缩成仓库可以长期保存的事实。
十五、AI 最适合承担什么角色
经过这些实践,我认为 AI 在 NixOS 仓库中最适合五种角色。
1. 仓库考古
它可以快速回答:
- 这个 option 在哪些节点被覆盖;
- 某个地址由哪些字段派生;
- 一个 service 的 secret 从哪里进入;
- 某个 systemd unit 为什么出现在健康检查中;
- 哪些测试依赖某个旧字段;
- 最近几个 commit 是否误删过类似占位符。
这类工作人也能做,但在几百个文件中反复搜索很消耗注意力。
2. 约束翻译器
人说:
所有公网节点的 SSH 端口必须一致地传到防火墙、Colmena 和健康检查。
AI 可以把它翻译成:
- 数据模型;
- 模块配置;
- 求值断言;
- expected fixture ;
- 文档。
3. 机械重构者
例如:
- 把主机元数据迁移到统一结构;
- 为 Nushell 函数补类型;
- 拆分大型模块;
- 批量修复格式和 Statix 建议;
- 把重复 required units 改成模块自声明。
只要边界明确,AI 做机械变更的效率很高。
4. 故障调查助手
它可以把 Alertmanager 标签、PromQL 、systemd 、journal 、coredump 、磁盘指标和最近部署串成 一条时间线。
但要注意,“调查”不等于“立刻修改”。我通常会先让它解释原因,再决定是否授权修复。
5. 文档维护者
系统改完以后,AI 很适合根据真实代码更新:
- 根 README ;
- 新主机流程;
- VPS 安装说明;
- 金丝雀发布说明;
- 安全基线;
- 快照与磁盘健康手册;
- 告警处理 runbook 。
这比让文档长期依赖人的记忆更现实。
十六、我怎样给 AI 下达运维任务
同一个模型,用不同方式提出任务,结果差距很大。
最危险的指令通常很短:
优化一下。
它没有说明优化目标、风险边界、验证方式和是否允许部署。AI 只能自行填空,而它填入的目标 往往是“代码更整洁”“warning 更少”“配置更统一”,未必是“线上更安全”。
我现在更倾向于分阶段给任务。
1. 只调查,不修改
检查这个告警的具体原因。
先读取当前规则、指标来源、节点日志和最近提交。
区分已确认事实、推断和未知项。
不要修改配置,不要清空告警,不要重启服务。
最后列出按风险排序的处理方案。
这种写法适合线上故障。它明确禁止 AI 为了快速得到“绿色”而改变状态。
2. 讨论一个设计
分析当前 SSH 端口检查覆盖了哪些层次。
目标是避免部署后把管理员锁在门外。
分别说明求值测试、Test 节点验证和外部网络探测能发现什么。
先讨论,不修改。
要求它解释覆盖范围,比直接说“多加几个测试”更容易得到有意义的设计。
3. 实施一个已经确认的方案
按刚才确定的方案修改。
只改 SSH 安全检查,不重构无关模块。
保留 secrets 中的预留模板和未使用接口参数。
运行格式化、Statix 、Deadnix 、仓库测试和 Flake 求值。
确认新增测试实际出现在报告中。
按 Conventional Commits 提交,但先不要部署。
这里把“不要做什么”写清楚,通常比重复强调“仔细一点”更有效。
4. 金丝雀部署
将刚才的 commit 先部署到 Test-NixOS 。
检查 SSH 、failed units 、内核、网络、BIRD 、DNS 和高优先级日志。
如果有异常就停止,不要继续其他节点。
如果全部通过,再部署其余受影响节点并执行最终健康检查。
报告 activation 成功和健康检查成功时要分开描述。
这条指令授予了明确范围内的部署权限,也规定了停止条件。
5. 审查 AI 自己的修改
重新查看最近几个提交。
寻找以下问题:
1. 为通过静态检查而删除了有意保留的占位符;
2. 新测试恒真、重复实现或检查已废弃路径;
3. 文档与最终配置不一致;
4. 新模块没有被任何主机导入;
5. 测试文件没有进入 Flake source ;
6. commit 混入不相关修改。
只报告有证据的问题。
让 AI 第二次审查自己非常有用。第一次工作时,它的注意力集中在完成方案;第二次可以换成 审查者视角。
6. 一些我会刻意避免的表达
我尽量不说:
- “自动修好所有问题”;
- “你看着办”;
- “没问题就全部部署”,但没有定义什么叫没问题;
- “清空告警”,但没有区分 silence 、resolved 和删除历史;
- “把无用代码都删了”,但没有说明预留接口;
- “所有节点都一样配置”,但没有检查硬件差异。
这些话对人类同事也很模糊,只是人类可能会根据长期共同经验主动追问。AI 往往会选择一个 最可能的解释并继续执行。
提示词不是魔法咒语。真正有效的是把工程权限、终止条件和可验证结果写清楚。
十七、怎样判断 AI 给出的结果是否可信
我不会根据回答语气判断可信度。AI 说“已经全面验证”没有意义,必须看它提供的证据落在哪 一层。
第一层:文本证据
- 找到了相关实现;
- 引用了准确文件和 option ;
- 差异与需求一致;
- 没有修改无关文件。
这一层只能证明代码看起来合理。
第二层:静态证据
- formatter 没有差异;
- Statix 和 Deadnix 通过;
- Nushell 能解析;
- YAML 、JSON 、PromQL 能解析;
- Git diff 没有空白错误。
静态工具擅长发现局部问题,不理解运行语义。
第三层:求值证据
- Nix 模块成功合并;
- option 类型正确;
- assertions 成立;
- 所有 host 输出可以求值;
- 新测试确实被 Flake 纳入。
这一层仍然不会启动服务。
第四层:构建证据
- derivation 可以实际构建;
- 引用的 package 存在;
- 生成文件符合预期;
- CI 代表性构建覆盖 Server 、VPS 和桌面。
构建成功也不能证明真实 secret 、网络和磁盘可用。
第五层:金丝雀证据
- Test activation 成功;
- SSH 没有中断;
- 必需 unit 正常;
- 实际探针通过;
- 新 generation 可启动;
- 高优先级日志没有新异常。
这证明配置能在一台真实机器上工作。
第六层:目标环境证据
- 受影响节点 activation 成功;
- 硬件或供应商特有路径有效;
- overlay 与公网路径连通;
- 数据库和应用真实响应;
- 没有新增 firing 。
这一层才接近“上线成功”。
第七层:时间证据
- timer 按预定周期再次成功;
- 快照保留策略经过轮转;
- 告警能 firing 也能 resolved ;
- 磁盘趋势没有继续恶化;
- 服务经过重启和下一次升级仍正常。
很多运维结论需要时间才能成立。刚部署五分钟就说“问题彻底解决”,通常证据不足。
我希望 AI 最终报告类似:
本地静态检查和全仓库求值通过;
Test-NixOS activation 与健康检查通过;
Lycheen-US-SLC 已成功切换,当前无 failed unit ;
新的 iowait 规则已加载,但是否消除短时误报仍需观察一个告警周期。
而不是一句:
全部修复完成,一切正常。
前一种说法保留了证据边界,也告诉我下一步该观察什么。
中篇小结
从健康检查账户到告警规则,这一篇处理的其实是同一个问题:AI 看到的是代码和输出,但人类 必须告诉它哪些语义不能从“有没有被引用”“命令是否成功”或“告警是否消失”中直接推导。
最小权限限制错误的作用面,快照和监控提供运行反馈,有意义的测试把经验固化成约束;清楚的 任务边界与分层证据,则决定了什么时候可以继续部署。它们共同组成的不是无人值守系统,而是 一套允许 AI 参与、又能及时拒绝错误的工程环境。
下一篇会集中讨论剩余风险:仓库为什么不等于现实,哪些副作用无法随 generation 回滚, 为什么 CI 不应该默认触碰节点,以及个人维护者应当如何控制复杂度。
系列下一篇:《我让 AI 参与维护 9 台 NixOS (下):协作流程、证据边界与风险》