提出用「故意破坏服务→检查脚本是否捕获失败」来验证 AI 生成的 SRE 健康检查代码,而非仅做人工 review 或 happy path 测试。
你应该根据 AI 生成的健康检查能捕获哪些故障来评判它,而不是根据它读起来有多像一份 SRE 运维手册。脚本在服务正常时返回零几乎什么都告诉不了你;只有在隐藏故障存在时非零退出、且能说出具体哪个不变量被破坏的脚本,才是唯一值得接入告警规则的版本。大多数团队验证生成式运维代码的方式和验证手写代码一样——读一遍、lint 一下、跑一次愉快路径——所以他们永远不会做那个测试。
下面这个工作流把一个免费模型端点和一台免费服务器当作同一间实验室的两面:模型起草一份健康检查,服务器在一个你刻意破坏的服务上运行它。声明:本文是 MonkeyCode 产品推广的一部分。运营方提供的前提是 MonkeyCode 同时提供免费模型访问和免费服务器;不要依赖任何特定模型名、配额或可用性,因为这些细节变化很快,对测试设计没有价值。如果你有任何可用的免费模型端点和任何可处置的远程主机,同样的方法完全可以迁移过去。
AI 生成健康检查的问题不在语法。模型可以生成一个完全合法的 Python 或 Bash 脚本——请求一个端点、验证状态码、当 body 包含已知字符串时 exit 零。这个脚本会通过 lint、通过基本的冒烟测试,但依然制造了一种虚假的安全感。失败是语义层面的:真实服务会以 prompt 从未描述过的方式降级。它在负载下变慢、开始为一个分片返回 503、丢弃某个追踪 header、或者返回的数据技术上是合法的但是过期的。如果生成的检查在验证过程中从未见过这些情况,这个差距会在凌晨三点暴露出来——要么是告警往错误方向触发,要么是根本没有任何告警。
更好的问题不是"这个检查正确吗",而是"它能把哪些特定类别的故障可靠地转化为非零退出?"要回答这个问题,你需要一个小型的故障注入工具,而不是又一轮代码审查。工具启动一个最小化服务,每次注入一种故障行为,然后对生成的检查运行测试并记录退出码和第一行输出。这个产物故意朴实无华,因为它的唯一工作就是暴露缺口。
下面是一个可以适配到你技术栈的精简版本:
# fault_probe.py
import os
import subprocess
import sys
# Each entry injects one failure into the target service.
# These are not the hidden holdout cases; reserve a few faults for later.
FAULTS = {
"slow_response": {"SERVICE_SLEEP": "2.0"},
"internal_error": {"FORCE_STATUS": "500"},
"missing_header": {"STRIP_HEADER": "x-request-id"},
"stale_payload": {"DATA_AGE_SECONDS": "900"},
}
for name, extra_env in FAULTS.items():
env = {**os.environ, **extra_env, "BASE_URL": "http://127.0.0.1:8000"}
result = subprocess.run(
[sys.executable, "healthcheck.py"],
env=env,
capture_output=True,
text=True,
)
print(f"{name:>15} -> exit {result.returncode}: {result.stdout.strip()[:120]}")
这个工具假设你已经有了一个能根据环境变量切换故障的小型服务。这个服务不需要是生产系统;它只需要镜像你关心的四个属性。一个好的生成式检查应该能把 slow_response 映射到超时或延迟阈值,把 internal_error 映射到非零退出,把 missing_header 视为某个损坏的代理或中间件层的症状,把 stale_payload 视为 freshness 问题——即使 HTTP 状态是 200。如果检查对其中任何一个返回了零,你就发现了一个具体的语义 bug,而不是代码风格问题。这只是一个起点,不是已经执行的基准测试;在你信任结果之前,先根据自己的环境调整服务和路径。
现在加上免费服务器这一步,因为你的笔记本会说谎。它有缓存的依赖、快速的 loopback 接口、特定的时区,以及积累的开发环境怪癖——这些都可能让一个有问题的健康检查意外通过。当你在一个干净的远程机器上运行同样的故障矩阵时——无论是通过 MonkeyCode 的免费服务器选项还是任何你能访问的可处置主机——这些伪装就被去除了。第一次干净运行往往会揭示:生成的脚本依赖了一个未声明的库、假设了只存在于你本地机器上的文件布局,或者使用了一个为 localhost 调优而非为网络一跳调优的超时值。这些失败都不是模型的道德缺陷,但它们都是让检查远离告警规则的理由——直到故障矩阵在两个环境中表现一致。
如果你计划与模型迭代,还有一个第二层值得加入:把你的故障分成可见集和隐藏集。给模型看可见集当你给出反馈时,但在生成阶段永远不要暴露隐藏集。等检查通过可见故障后,在不改变 prompt 的情况下对它运行隐藏故障。这可以保护你免于一个常见循环:模型学会了写一个过拟合你展示的五个例子的脚本。隐藏故障可能是重定向的端点、带 200 状态的空 body、或者慢的 DNS 查询而非慢的响应体。如果生成的检查漏掉了这些,你就知道验证在做真正的工作,而不是变成了一场模型学会表演的 choreography。
诚实地说清局限性和方法本身一样重要。有限的故障矩阵只能证明检查能捕获你想到要注入的故障;它无法说明关联故障、影响单个分片的局部中断、或者以微妙指标漂移而非端点级症状出现的故障。它也无法证明否命题。生成的检查对你尝试的每个可见和隐藏故障都 exit 零,并不等于保证它就是正确的,因为可能故障的空间实际上是无限的。把矩阵当作进入预发环境的最低门槛,而不是正确性证书。如果你的团队运行着受监管的服务、高合规要求的环境,或者任何在远程主机上运行生成代码会引发数据驻留或安全审查问题的场景,在那些审查完成之前这个方法不适用。当健康检查需要接触凭证、内网负载均衡器或可能有状态系统时,你也应该跳过它——因为故障注入可能产生真实的生产副作用。
健康检查更接近烟雾报警器而非数学证明:你不是通过欣赏布线来验证它,而是通过点燃一个小型的受控火源、看看警报是否尖叫来验证它。这就是整个工作流的一句话总结。用免费模型起草检查、通过小型工具逐个注入故障、在干净的免费服务器上运行可见和隐藏矩阵,然后才考虑让脚本接近真实的告警路径。结果不是一个完美的检查,而是一个你能说出具体盲点在何处的检查。