一个前端组件库,200个待审PR里藏着69个重复劳动。不是抄袭,是聪明人各自为战——AI让写代码变快了,却让协作变贵了。
数据冲击:69个"隐形重复"
开发者Pete最近在推特上吐槽:OpenClaw这类高流量仓库,AI代理(AI Agent,指能自主执行任务的AI程序)正在淹没维护者。它们批量生成PR,逻辑雷同,却与项目长期愿景脱节。
这条吐槽成了导火索。我决定造个工具,自动审计PR并打标签。测试场地选在shadcn-ui/ui——一个高流量的React组件库,200个近期PR,完美实验田。
预期是发现简单的代码克隆。实际结果更棘手:三份看起来完全不同的PR,改的是同一个功能故障。
案例很典型。三份PR分别修改Config、Utils、Registry三个不相干的文件,系统却识别出它们都在修复同一个问题:/blocks页面链接失效。
PR #10088直接重命名文件,根治问题。而#10156和#10096还在各自修文档,还没合并就已经沦为冗余。这叫"目标重复"(Goal Duplication),对维护者来说是手动过滤成本最高的噪音。
最终数字:200个PR里,脚本标记出69个有效冗余。不是语法相似,是意图撞车。
技术拆解:三层漏斗怎么筛
这套检测引擎跑在三层过滤上,每层解决不同层面的误判。
第一层是向量检索。用Gemini嵌入模型(Embedding,将文本/代码转为数值向量的技术)把PR转成向量,存进Upstash Vector。新PR进来,直接搜最相似的8个候选。
但向量相似≠功能重复。早期测试暴露"结构偏见":两份完全不同的JSON新增,只因为形状长得像,就被误判为重复。
第二层修正策略:强制引擎优先比对字面量——ID、URL这些具体值,而非语法结构。筛子从"找相似代码"收窄为"找相同目标"。
第三层交给大语言模型(LLM,Large Language Model)。把向量检索的Top候选喂进深度推理,理解作者真实意图。这一步最烧资源,也最准。
成本管控是生死线。预算为零,必须在免费额度里活下来。策略是激进压缩:过滤掉大规模代码变更的噪音,只保留核心逻辑片段。
用Octokit分页抓取PR,盯着main/master等关键分支。速率限制靠路由自动切换解决——一个API触限,立刻跳备用通道。
为什么这事儿值得认真看
表面是技术方案,底层是协作模式的变化。
AI代理让"提PR"的门槛趋近于零。一个开发者想到修复方案,不再先搜"有没有人做过",而是直接让AI生成、提交、等待。结果?三个聪明人,三天内,用三种写法,解同一道题。
对维护者,这是三重伤害:审阅时间×3,上下文切换成本×3,合并冲突风险×3。69个冗余背后,是大量本可避免的注意力税。
更隐蔽的是"愿景漂移"。Pete提到的"与长期愿景脱节",在shadcn案例里体现为:文档修修补补,不如文件重命名一步到位。但AI代理不懂愿景,它懂的是"这个报错怎么消"。
我最初想建两套东西:历史数据回扫脚本,+实时机器人做在线分流。最终全力扑在第一套——历史冗余集群才是检测引擎的终极压力测试。
实时拦截很性感,但历史数据里藏着模式。哪些功能点最容易撞车?什么类型的PR重复率最高?这些洞察才能让工具进化。
给同类项目的野路子建议
如果你也在维护高流量开源项目,这套思路可以直接搬。
向量库别自建,Upstash这类托管服务省掉80%的运维。嵌入模型选Gemini或OpenAI都行,关键是对比"字面量权重"的调参——syntax相似度权重太高,误杀率爆炸。
LLM环节别省。向量检索是召回,LLM是精排。两层缺一不可,否则要么漏报,要么淹没在假阳性里。
最反直觉的一点:检测"目标重复"比"代码重复"更有价值。代码重复有现成工具(如SonarQube),目标重复需要理解意图——这正是LLM的甜点。
预算为零也能跑。激进压缩代码片段,分页抓取+路由切换扛住速率限制。免费 tier 的额度,够审几千个PR。
最后,别指望实时拦截解决一切。历史回扫帮你找到系统性盲区——哪些模块是撞车高发区?哪些维护者最容易收到重复PR?数据比直觉准。
AI代理不会退潮。问题是,我们的协作基础设施准备好了吗?当"写代码"变成"指挥AI写代码",代码审查的复杂度也在指数级上升。这套检测引擎是个开始,但远不是终点——如果明天有10个AI代理同时给同一个bug提PR,你的仓库扛得住吗?
热门跟贴