AI 重构不是看到重复就合并:用 Boffin 把架构约束路由到当前编辑
让 AI 编程助手修改一个成熟仓库,最危险的时刻往往不是它不会写代码,而是它写得“太整齐”。两个分支看似重复、两个状态处理看似可以合并、同步和异步流程看似只差一个 await:这些判断如果只依据局部文本,很容易把承载业务语义、并发恢复或生命周期边界的差异压成一段漂亮却错误的通用代码。
Boffin 是一个面向 AI 编程 Agent 的 MIT 许可控制层。它不把整份仓库规范一次性塞进上下文,也不是替代测试的安全沙箱;它做的是在 Agent 准备编辑某个文件时,按文件、任务与语言把相关架构约束路由出来,并要求与变更规模相称的外部验证。其 npm 包名为 boffinit,目前覆盖 Cursor、Claude Code、Codex 与 OpenCode 等交付入口。
这类工具的价值不在于再加一套“永远别出错”的提示词,而在于把一次修改拆成可审阅的问题:这次改动的范围是什么?哪些契约不能被清理动作破坏?哪些状态或所有权必须保留?最后应该跑哪一个最小但足以证明结果的检查?
静态规则文件为什么常常不够
AGENTS.md、CLAUDE.md 或 IDE 规则文件对于沉淀项目约定很有用,但它们通常有一个结构性困难:同一段静态说明会同时面对所有目录、所有语言和所有任务。
于是有两种相反的失败方式。规则写得短,Agent 在改并发、鉴权或资源回收逻辑时得不到足够上下文;规则写得极长,模型又要在每次小改动时阅读大量不相关内容,真正关键的例外条件反而被淹没。更重要的是,静态规则通常只能“建议”先测试,不能把“先识别不变量、后做抽象”变成一套明确的工作顺序。
Boffin 的底层 ParselFire Core 将指导内容组织成小型 pack。运行时先根据当前文件和任务选择主规则,再按阶段图加载实际触及的补充规则,目标是得到一组小而可解释的读取集合,而不是在仓库里搜索一条看起来方便的规则来为改动背书。
先走正确性阶段,再讨论代码是否 DRY
ParselFire Core 的公开文档将工作划为 S00 到 S06 七个阶段:
- S00 范围与必要性:保持在用户请求的改动边界内;
- S01 契约与不变量:识别精确的接口、例外和安全行为;
- S02 状态与结果:保留有意义的状态及不同结果;
- S03 所有权与生命周期:明确可变状态、发布、重建和销毁的归属;
- S04 共享抽象:确认语义后才提取共同不变量;
- S05 边界与传递:保证行为穿过真实子系统边界,而不是藏进旁路;
- S06 收敛与删除:只有证明替代路径成立后才移除旧层。
这套次序尤其值得用于“顺手重构”。S04 的抽象不能绕过 S01—S03:更少的代码行不是削弱契约、抹平状态差异或混淆所有权的理由。对于范围明确的小修复,Boffin 的文档建议只加载实际需要的指导,完成变更后运行最窄的外部测试或 lint;对于开放式审查或清理,则先做只读审计,列出每个阶段的发现与跳过理由,再逐项修改和验证。
一个可复现的接入起点
如果团队先在 Cursor 项目中试用,可从项目根目录执行官方 README 给出的安装命令:
npx boffinit cursor
安装后,不应立即把它当作“自动重构开关”。更可靠的试验是挑一处风险可控但确有历史包袱的改动,例如:一个 Python 服务中重复的鉴权解析,或一个 C++ 模块中同时涉及状态重置与资源生命周期的局部重构。
建议把任务描述写成可验证的约束,而不是“把这段代码写优雅一些”:
只修改指定模块;先确认重复逻辑是否共享同一契约。 不要合并同步与异步入口;保留所有显式错误分支。 完成后运行该模块已有的定向测试,并说明未改动的例外路径。
随后让 Agent 先给出其理解的范围、风险边界和验证计划。若它提出跨文件的大面积整理,先要求说明这些文件与原问题的因果关系;“附近代码看上去也能统一”不应自动成为扩大 diff 的理由。这个人工检查点并没有被工具取代:Boffin 的作用是让模型更早暴露假设,让审阅者能围绕不变量讨论,而不是只在最终 diff 上猜测它遗漏了什么。
两类案例说明了它如何约束抽象
项目公开的 FastAPI 案例处理 fastapi/security/http.py 中的授权解析。案例把 Bearer 与 Digest 的共同机制提取到 HTTPBase,但保留 HTTP Basic 的独立路径,因为后者仍包含 Base64 解码和凭据形状校验。记录显示该改动没有改变公开 API,并针对相关安全测试运行了 49 个测试。这个结论不是“重复代码一定该抽取”,而是先确认变体是否只属于受限策略,再决定共享哪些部分。
C++ 的 DuckDB 案例则更能体现为什么状态机不能按外观合并。该案例检查并行执行器时,区分了看似类似的 BLOCKED 与 INTERRUPTED 路径:它们会保存不同的线程继续执行标记。最终只抽取了真正重复的批次索引初始化,移除了一个未读取状态,并补上一次重置遗漏的 profiling 标记。项目记录的验证为 8 个测试文件共 2,104 项断言通过,改动统计是 17 行新增、17 行删除。它是公开案例而非受控 A/B 基准,不能据此推导速度、成本或通用正确率;但它清楚展示了审计式约束能把“删除冗余”改写成“识别哪些冗余是承重结构”。
选择模式,而不是把治理变成阻塞
Boffin 提供 lite、full 与 max 三种 profile,其中 full 是默认值。文档说明它们调节的是清理雄心,而不是正确性底线:早期的契约、数据丢失防护、安全与可访问性要求仍保留。因此它并不适合每一个一次性脚本或原型;对小型、低风险、生命周期极短的试验,额外的阶段审查可能超过收益。
它更适合三种情况:仓库已存在隐性规则但 AI 常在跨文件修改时遗漏;团队希望把“为什么没有合并这两个分支”留成可审计证据;以及重构需要明确连接到目标项目自己的测试、lint 或构建。它也不能替代命令沙箱、网络隔离、权限控制或人工代码审查。README 明确指出,Boffin 不隔离进程、不拦截 shell 命令,也不限制文件系统或网络访问。
把验证证据纳入合并标准
落地时,最容易被忽略的不是安装,而是如何保存证据。一次 Agent 改动至少应在 PR 中留下四类信息:目标文件与未触及范围、它识别到的不变量、实际运行的命令及结果、以及仍未覆盖的风险。这样做并不要求每个任务都写长篇审计报告;小修复可以是一段简短说明和一条定向测试命令,状态机、并发或安全边界上的重构则应升级为可逐项复查的清单。
还要避免把“验证通过”误解为“没有风险”。定向测试证明的是它覆盖到的行为,不代表替代了集成测试、生产监控或安全评审。相反,阶段化路由的意义是让团队能准确决定下一步该补什么:若风险来自协议兼容,就扩大契约测试;若来自资源释放和并发可见性,就检查生命周期和竞争路径;若只是局部实现重排,则保持验证范围与 diff 相称。把这层取舍写清楚,才能避免治理工具把正常开发拖成形式化流程。
把 AI 放进工程流程,不应只问它能生成多少代码,更应问它是否知道何时停止抽象、如何证明局部修改没有破坏原有行为。把约束随编辑位置路由,并把验证作为完成条件,正是让 Agent 从“输出代码”走向“参与可审计工程变更”的一条实际路径。