作者用多个AI agent分工(设计、审查、实现)协作开发开源RAG框架Vestibule,分享了AI review发现竞态条件、测试覆盖率等真实工程教训。
我花了两个月构建 Vestibule,一个面向 RAG 摄入层"无聊部分"的开源 Python 框架——稳定的文档 ID、状态账本、错误分类、按垂类治理。这些是每个团队在 demo 跑通但进入生产后都会遇到的问题。
大部分代码不是我敲的。四个 AI agent 完成了工作——一个写设计、一个 review、一个实现、一个 review 代码——全部通过真实的 GitHub pull request,我只在各关卡签字放行。最终成果:十二个组件、三次发布、878 个测试。
有两个时刻定义了整个体验。
最棘手的组件在首次使用时按需创建向量索引,即使 worker 之间相互竞争也能保证安全。它的设计在任何代码出现之前就被否决并修订了五次。第一轮,reviewer agent 发现了一个真实的竞态条件:一个仍在慢速索引创建调用中(约 390 秒,含重试)的 worker 会看起来已经过时(阈值默认 300 秒),从而失去对等待 worker 的声明权,导致两个 worker 创建同一个索引。这是一个生产级竞态,出现在默认配置中,由一个 AI 阅读另一个 AI 的设计时发现——彼时连一行代码都还没写。
v0.2 发布后,我写了一个快速入门脚本,然后用陌生人的方式运行整个管道——第一次。
pip install 根本无法工作。打包冲突导致整个框架无法安装,而 483 个测试却一路绿灯。真正使用了一个小时后又发现了两个问题:一个从未真正对着真实 SDK 工作过的默认模型名称,以及一个在可选依赖缺失时会是整个包崩溃的 import。
问题不在测试本身——而在于测试衡量的是什么。它们只证明了代码与自身一致:同一个工作树、同一套 mocked 接口。没有任何东西真正检查过用户所处的世界:干净的机器、真实的安装、真实的 SDK。测试通过与产品可用,原来是两码事。
最终的修复不是三个补丁,而是一条新的 CI 任务:现在每次 PR 都会构建一个干净的 virtualenv、执行真实安装、运行快速入门脚本。三个 bug 都在 release notes 中公开命名。
每个 RAG 教程都讲解析、分块、嵌入。没有任何教程讲六个月后会坏什么:重试导致分块重复、文档在管道中途静默消失、一个团队的配置变更污染了另一个团队的索引、没有人能回答"那个文档进去了吗?"
Vestibule 就是那层缺失的东西——四个契约,其他一切插拔其中:
统一的到达信封。 每份文档都通过同一个经验证的形态进入,ACL upfront 要求——不是事后补救。
确定性身份。 doc_id 和 chunk_id 是输入的纯函数。重试覆写而非重复。重新摄入一份缩减后的文档不会留下孤儿分块。
状态账本。 每个文档一行,作为合法的状态机,所以"文档 X 在哪里、为什么失败了?"是一次查询,而非一次调查。
失败分类学。 每个错误都被分类为永久性或暂时性。一份损坏的 PDF 失败一次就停止;限流错误则退避重试。失败的文档排队等待人类处理,错误附在其上——重新排队或归档,一通电话搞定。
在此之上:按垂类配置(HR 和 Legal 获得不同的分块大小、不同的索引、不同的 ACL 策略——运行时变更,无需重新部署),以及在新垂类第一份文档到达时自动创建索引。
解析器、分块器、嵌入器、向量存储都是可插拔的适配器——PyMuPDF、Azure Document Intelligence、Azure OpenAI,以及完全本地化的选项今天都已发版。
动手之前先了解真实需求。 Agentic 项目大多死于延迟、成本和职责不清——而非模型太弱。用已有的东西;只构建没人发版的那一层。
关卡胜过速度。 胜利不在于快速的代码生成,而在于每个阶段都检查前一阶段。五轮设计花了我几个小时。那个生产级竞态要是没被发现,将付出一次事故的代价。
用自己的东西,冷启动。 在一台干净的机器上安装它,就像陌生人一样使用它。我最严重的 bug 恰恰生活在"测试通过"和"有人运行过"之间的那片空间。
试试看——60 秒,无需云账号
bash git clone https://github.com/vk032503/vestibule
cd vestibule && pip install -e ".[local]"
python examples/quickstart.py
现在是 v0.3,release notes 里平实地写明了还缺什么。如果你在生产环境跑 RAG,告诉我什么坑过你——这是我想要的反馈。