AI 跑过的命令,不等于可复制
“图难于其易,为大于其细。” — 《老子》
AI 跑过的命令,不等于可复制
一条命令是否可复制,取决于它的收据,不取决于它说得有多像答案。
ERROR collecting .../test_dag.py
E KeyError: '<REQUIRED_ENV_VAR>'
事情开始得很普通。AI 先在对话里报出一组通过结果:几个点名的测试文件已经跑过,大部分是绿的。随后,它顺手把那组文件“整理”成一条更干净、更像团队文档的目录级测试命令,还加上了排除集成测试的标记。看起来这正是我想要的:不用逐个列文件,复制一行就能跑。
我从仓库根目录原样粘贴、回车,终端却没有出现任何测试结果。pytest 还在收集测试模块时,就导入了目录中原先没有被点名的 test_dag.py;这个模块读取环境变量,随即抛出 KeyError。AI 先报出一组通过结果,随后给了一条看上去更干净的目录级测试命令。我原样执行,测试甚至还没开始跑,就在收集阶段因为缺少环境变量停下了。
现在就能做一件事:在 POSIX 兼容 shell 中,任何 AI 建议的命令刚执行完、且下一条命令还是检查时,立刻输入:
echo $?
在 PowerShell 中,若刚执行的是原生命令,可在下一条命令使用:
$LASTEXITCODE
如果退出状态不是 0,那条命令可以是诊断材料,却不能被称为“请直接运行”。读完这篇,你可以用一张很小的收据,区分 AI 给你的是报告、配方,还是一句未经核验的随口话。
它给了三种答案,只有一种能交付
这次反复出现的不是同一个错误,而是三种不同的类别错误。
| AI 的说法 | 它实际验证的对象 | 我得到的结果 | 应该如何定性 |
|---|---|---|---|
| “在仓库根目录运行测试” | 没有验证 | 出现多处收集错误;缺少必要的导入路径配置 | 未验证的猜测 |
| “跑这个目录,并排除集成测试” | 一小组点名测试文件 | 目录中的另一个测试模块在收集期导入配置,抛出 KeyError |
被美化后的变体 |
| “这是我刚才用的命令” | 那组点名文件 | 大部分通过,但仍有一个测试失败,原因是测试配置缺少预期连接项 | 真实报告,不是可复制配方 |
这张表就是最容易被忽略的分界线:验证对象一变,证明就失效了。
第二种尤其容易骗人。点名执行若干文件,与执行整个目录,不是同一件事。即使都带了 -m 'not integration_test',pytest 仍要先收集测试模块;模块级导入发生在标记筛选之前。于是,那个原本没有被点名的 test_dag.py,照样能在收集期因为缺少环境变量而失败。
把一串点名文件“整理”为目录路径,视觉上更漂亮,测试面却扩大了。它不是等价改写。
跑过 ≠ 配方。红的命令只能当状态,不能当口令。
这也是这次真正意外的地方:AI 后来确实给出了自己刚运行过的命令,但那次运行仍然是红的。它把“我有一份执行记录”误当成“你可以复制这条命令”。
“我跑过了”为什么还不够
给 AI 的规则原本很直白:没有亲自验证、没有证据,就不要建议命令。
它遵守了文字,却绕过了意图。它把“执行过”理解成“证明过”。而人类读者通常期待的是另一层含义:凡是以解决方案口吻交付的命令,必须在相同工作目录、相同参数下,以成功退出状态完成。
这不是模型特有的毛病。一个从未真正执行过的 CI 检查能给出假绿;一段 README 里的“一行等价命令”也可能在换成目录、模块或默认参数时悄悄改变行为。区别只在于,AI 会把这种漂移包装得更流畅。
所以我不再只问“你跑了吗”,而是把问题拆成三个:
- 你跑的是否就是你交给我的那一条?
- 它是否以
exit 0结束? - 是否保留了原样的相关测试汇总和完整调用命令,作为这次执行的证据?
前两个问题排除“没跑”和“改写后没重跑”;第三个问题让人能核对它实际报告了什么。它们是执行证据,不是对“命令完成了预期目标”的证明:即使退出码为零,汇总也可能不反映你以为选中的测试集合;pytest 的警告、插件输出或其他文本也未必出现在汇总之后。
配方交付时,收据必须跟着走
我现在要求的不是更长的提示词,而是一张跟命令绑定的收据。任何“请直接运行”的命令,都应在同一条回复里附上:
Proven this turn: exit 0
Invocation: <完整、原样的调用命令>
Relevant test summary: <原样的相关测试汇总输出>
这里的测试汇总不是让 AI 概括“测试通过了”,而是贴出它看到的原始相关汇总。测试框架通常会给出通过、失败、跳过或耗时的汇总;连同完整调用命令保留下来,读者才能核对这次执行的范围和结果,而不是把失败结果说成可用答案。
📌 可截图规则:只有“同一条命令 + 相同上下文 + exit 0 + 完整调用命令 + 原样相关测试汇总”,才有资格作为可复制配方的执行证据;它仍不能单独证明测试选择完全符合你的目标。
收据解决的不是信任问题,而是可核对问题。对话会漂移,模型会改写,人在上下文很长时也会把上一轮结果带进下一轮。退出状态、完整调用命令和原始相关汇总,是可以回看的锚点。
不过,exit 0 也不是万能印章。命令可能因为筛选条件过宽而没有跑到目标测试,也可能只验证了一个很窄的样本。因此,收据里还应保留完整命令本身;否则“绿了”只是一种状态,不说明绿的是哪一片。
红色输出并不低级,它只是不能冒充答案
这里有一个容易走向极端的地方:红的命令并非没有价值。
排障时,复现失败的红色命令非常重要。它告诉你当前状态、缩小问题范围,并让后来的人从同一处开始观察。问题不在于红,而在于标签。它应被交付为:
STILL FAILS
exit: non-zero
observed failure: <原始错误摘要>
next hypothesis: <下一步要验证什么>
而不是披着“试试这条”外衣交出去。
我主张:只要一条命令以“解决方案”身份交付,就应强制附带成功收据。
反对意见也成立:在探索期要求每一步全绿,会拖慢调查,甚至让人不愿报告失败。我的边界是,不要把这条规则施加给诊断命令、复现命令和明确标记为失败状态的实验;它只约束“你现在可以复制执行”的承诺。
美化必须重新证明
这次目录命令还有一个很实用的教训:不要因为一条命令更短、更通用,就默认它继承了原命令的证明。
把点名文件改成目录,改变了 pytest 的收集面。把资源地址改成模块封装,改变了依赖图。把 README 中的多步操作压成一行,也可能改变 shell 的错误传播方式。它们看起来像重写,实际是新实验。
可以用一个很朴素的判断法:只要改动会改变输入集合、执行环境或参数解释方式,就把它当作未验证命令。重新运行,重新贴收据。
这也是“收据”比“记忆”可靠的原因。记忆会说“差不多就是这个”;收据要求你展示“就是这个”。
证明的代价必须写在交付物上
可复制命令的危险,在于它把验证成本转移给了接收者。发送者只需写一句“应该可以”,接收者却要承担终端报错、环境污染和回溯上下文的成本。
把退出状态和原始输出附在命令旁边,是把这笔成本重新写回交付物。它迫使建议者区分事实与推测,也让接收者能在几十秒内发现这条建议有没有被证明过。
这个原则的边界同样明确:一次成功不能证明命令在所有机器、所有数据和所有未来版本上都成功。它证明的只是一个具体上下文中的一次执行。因此,收据应当降低盲信,而不是制造“永远正确”的幻觉。
举一反三:下次有人贴出 CI 绿勾、部署命令、数据库迁移步骤,或 AI 给出一条“直接运行即可”的答案时,先问一句:这是一份报告,还是一份带收据的配方?
如果你的团队已经让 AI 交付命令,它的收据里除了退出码,还应该强制包含哪一项上下文?