之前我在这里发过帖子,说我在用"氛围编码"的方式给老婆做一款手机游戏(Godot + Claude Code + ElevenLabs,零广告、零内购,就是一只狗,你可以给它穿上越来越离谱的设计师配饰)。那部分很顺利。游戏发布了,浏览器里免费就能玩,她很喜欢。

我当时没发帖说的是接下来发生的事,因为那时候写起来并不愉快。

当这个游戏从一个玩具项目逐渐长大,当初构建它的那套工作流也开始悄悄把它搞坏。不是那种检查工具(linter)能抓到的错误。代理程序(agent)会在一个文件里重命名某个东西,然后漏掉两文件之外的一个调用方。它会修改一个字符串,而另一个地方的路由器正好在匹配这个字符串,不匹配的问题直到我运行游戏、某个界面加载不出来才发现。重构时我移动了一个成员,但其他代码还不知道它被移动了,还在读取它。写代码时不会崩溃。编译时也不会崩溃,如果真有编译这一步的话。它就这样静悄悄地不再按我预期的方式工作,然后我得花二十分钟追踪一个检查工具(linter)愉快放过的bug。

一旦我明白了,模式就很明显:检查工具(linter)读一个文件。坏掉的东西藏在文件之间的接缝里、字符串契约里、场景连接里、我自己的 AGENTS.md 声称存在但没有任何东西在检查的边界上。每次修复都是在修补症状,而不是真正的缺口——真正的缺口是,代理程序(agent)非常擅长编辑当前打开的文件,却不知道代码库里还有什么东西在悄悄依赖这个文件保持原样。

所以,我没有再用"氛围编码"编另一个CRUD应用、封装器或者第二款游戏,而是用"氛围编码"编了一个工具,它能在让任何代理程序(agent)动代码之前回答一个问题:下一个代理程序编辑这个文件时,会发生什么?

这个工具叫 VibeCheck。它是一个针对用AI代理程序构建的代码库的审计器,除了 Python 3.9 和 git 之外没有任何依赖,所有操作都在你本地执行,除非你明确选择模型辅助审查,否则不会把任何东西发到你的机器之外。默认情况下它不调用 LLM。它会跨文件追踪字符串契约(比如传给路由器的屏幕名称、在处理程序表中查找的事件名称、从存档文件中读取的键值)、把你自己的文档声称存在的边界和实际的依赖图进行比对、标记重命名后不再存在的成员、捕获构建陷阱(比如你编译时预加载了一个文件,但导出过滤器把这个文件剥离了——这在桌面上看起来很正常,到了设备上就是一片空白)、还会捡起代理程序(agent)留在你文档里的过期提示残骸。

"氛围编码"者实际怎么用它:

三个命令基本搞定所有事情:

python3 -m vibecheck scan .

指向你的仓库。你会得到一个自包含的 HTML 报告:六个类别的氛围债务分数(Vibe Debt Score)、一个"别让AI碰这个"的风险最高文件排名、一个依赖图,以及一个你可以输入任何计划改变的冲击范围框。

python3 -m vibecheck blast . "替换库存系统"

这才是我每天实际用的那个。在我把任务交给代理程序之前,我用英文描述一下要改什么,然后运行这个命令。它会吐出一个简报:这个改动可能会涉及哪些文件、什么可能会出问题以及为什么、应该先读什么、要维护的不变量(以及固定这些不变量的实际代码行)。我会直接把这段内容粘贴到 Claude Code 或 Codex 里作为上下文,让它开始编辑。它把"代理程序自信地在三个文件之外搞坏了什么东西"变成了"代理程序事先就画好了接缝地图"。

python3 -m vibecheck check . --fail-on high

一个 CI 门控。如果发现严重程度等于或高于你设定的代理程序风险,则退出码为 1,并给出文件:行数,让你知道具体要看哪里。

我不想光说它有用,所以我用最早坏掉的那个游戏的真实提交历史对它做了回溯测试:41 个提交,21 个符合条件的提交,每个只扫描当时已有的历史,然后根据那个提交实际改动的内容打分。在预测一个提交改动最频繁的文件时,VibeCheck 的 recall@5 是 0.48,recall@10 是 0.65,高于仅静态图、仅共同改动和同目录猜测,也远高于随机。这是一个仓库的样本量,所以只能当作"比抛硬币好、比按文件夹猜好",不是金科玉律。

它原生支持 GDScript/Godot,因为这个工具就是从那里诞生的,同时完全支持 Python,并有一个语言无关的标记化器用于其他语言(JS/TS、C#、Go、Rust、Swift、着色器)。

链接:

如果你也遇到过同样的问题——代理程序输出每个文件看上去都没问题,但代码库在接缝处悄悄腐烂——我真的很想知道你是怎么抓住这些问题的,因为对我来说,"更仔细地看diff"很久以前就不够用了。