
最近技术圈被一个数字刷屏GitHub 让 AI 重写了自己的代码库三周时间128个PR83万行代码。消息传出来时很多人第一反应是“又是 AI 吹牛”但仔细扒完技术细节后我发现这恰恰是 AI 编程落地最该有的样子不是让 AI 自由发挥而是把它塞进一套严格的重构流水线里让机器做体力活人做决策。这套打法非常适合那些被历史代码拖住、想做大规模迁移却没胆量动手的团队。80万行这个量级放在任何一家公司都不是小工程。如果让开发团队人肉去改光是评审和冲突处理就可能拖上两三个月。GitHub 这个案例的价值不在于“AI 写得快”而在于它展示了一套从任务拆分、代码索引、自动生成 PR 到质量闸门、人工审查的完整闭环。这篇文章我把这套闭环拆开来讲重点说不清不楚的部分其实是哪里、128个PR怎么设计、哪些步骤可以直接抄到自己的仓库里。1. 三周83万行本质上不是“AI写代码”而是“AI做存量替换”1.1 重写的对象不是新功能而是“历史包袱”很多人看到“AI重写”就会以为是大模型凭空生成了83万行新代码这其实是一个误区。真实的大规模重构项目目标几乎都是同一个把已经存在的、能跑但不好维护的代码迁移到约定的目标状态。写新代码是创造性工作AI现在最多帮你打个底但替换旧调用、统一工具函数、迁移 API、调整目录结构这些是重体力劳动规则明确、重复度高恰恰是大模型最擅长的事。举个例子就明白了。假设你的旧代码库里到处是old_send_email()这个函数新代码库统一改成了notifier.send()参数顺序还变了。人工操作时要做的是先全局搜出所有调用点逐个判断上下文再按新签名改写然后跑测试看有没有漏网之鱼。这个过程枯燥、易错、耗时间而且数量一旦上到几千处人脑就会开始疲劳漏改。LLM不会疲劳你给它明确的映射规则它就能照着清单批量执行给它的上下文越大、规则越清楚输出越稳定。所以这个项目的本质是把“语义等价但不符合当前规范”的老代码用 AI 批量替换成“语义等价且符合新规范”的新代码。行为不变表达变清晰。这个目标决定了后面所有流程设计每一处改动都必须能被验证必须能回滚绝不能出现“AI顺手改了业务逻辑”这种事。1.2 128个PR的拆分逻辑为什么不是1个巨型PR83万行如果堆在一个PR里哪怕是AI写的也没有任何人敢点Merge。代码评审、CI验证、回滚定位全部会瘫痪。GitHub 的做法是把它拆成128个可独立合入的PR这背后不是拍脑袋而是有一套拆分原则。拆分的核心不是按“行数”而是按“风险边界”。一类典型的拆法是按目录拆比如src/billing/作为一组、src/auth/作为一组另一类拆法是按“迁移模式”拆比如“所有旧邮箱服务的调用”归成一个PR“所有缓存客户端切换”归成另一个。这样做有四个直接好处第一错误能快速定位。某个PR合并后线上出问题回滚这个PR就行不会波及83万行。第二CI压力可控。128个PR同时跑测试和128万个文件同时跑测试完全是两码事小批量能让失败集中在局部排查成本低。第三评审可以并行。每个模块的负责人只要看自己负责的那几个PR不用理解整个代码库。第四适合渐进式验证。先合入低风险的PR观察监控指标确认安全后再合下一批三周不是一口气冲完而是每天推进若干个“小确信”。128个PR听上去很多但平均下来每天也就是8个左右周末可能不跑每个PR大概覆盖几千行改动是一组人类Reviewer能在一小时内看完的体量。这个节奏既压不死人又能保持每周可见的推进速度。1.3 人机分工AI负责执行人负责定规矩这次重构最打动我的一句话是“AI提议人决策机器验证”。AI在整个流程里干的活是扫描、初稿、替换、自我检查人干的活是定义目标规则、划定改哪些文件、审查高风险diff、处理语义冲突。两者之间没有模糊地带。人不能在一开始就“让AI随便看看仓库哪里可以优化”。那会产生大量无法归类的变更Reviewer根本无从下手。正确的姿势是人先把迁移规则写死比如“所有old_api调用都换成new_api参数顺序按照映射表转换不允许动其他代码”然后AI只在这个框架内执行。等于说人先画好施工图纸AI是拿着图纸的施工队。这也是很多团队用AI做重构失败的原因——他们把AI当成了“建筑师”结果AI交还给你一堆风格混乱、超出预期的东西。反过来AI也承担了大量人做不好的工作检索全部调用点、保持风格一致、同时处理成百上千个文件而不遗漏。“人定边界、AI执行、测试兜底”这个闭环一旦跑起来三周83万行就不再是天方夜谭。2. 支撑128个PR的工程底座代码索引、自动流水线与质量闸门2.1 代码索引与上下文管理AI必须“看得到”整个仓库大模型不是数据库你给它一个仓库链接它并不会自动知道里面有什么。要让AI在83万行代码里精准干活第一步是给AI建立“施工清单”。我在类似项目里的做法是先写脚本扫描整个代码库把所有需要改动的位置提取出来。具体来说可以用 tree-sitter 解析每份代码的AST也可以用简单的 grep/RG 正则搜索把所有old_api的调用点捞出来然后聚合成“文件路径 行号 周围上下文”的索引条目。这个清单就是AI的任务输入它不需要理解整个仓库只需要针对每个清单项读取对应的文件片段并做出替换。在这里要注意一个关键约束模型的上下文窗口是有限的。83万行不可能一次性喂进去哪怕是128个文件也要分批。所以我的策略是“一次只让AI看一个文件或一组强关联文件”把仓库的整体结构、目录说明、依赖关系用一段很短的prompt告诉它剩下的靠代码自己说话。为了让模型不乱看我还会把无关区域压缩成占位注释比如// ---- 本段无需修改 ----这样既减少token消耗也大幅降低AI“顺手美化无关代码”的概率。这个技巧在批量重构中非常有效。2.2 自动PR流水线从“AI输出代码”到“合法PR”的最后一公里AI生成完代码之后最难的不是生成本身而是怎么把生成结果安全地变成Pull Request。我在实践中总结下来这一步必须有完全自动化的流水线否则你将会成为手工搬运工每天开分支、提交、建PR、填描述累到怀疑人生。GitHub生态提供的底座是 GitHub Actions GitHub CLI也就是gh命令。我们可以用workflow_dispatch手动触发一个“重构批处理workflow”这个workflow接收一个参数比如批量大小然后执行归档脚本。脚本流程大致如下name: ai-refactor-pipeline on: workflow_dispatch: inputs: batch_id: description: refactor batch id required: true permissions: contents: write pull-requests: write jobs: generate-pr: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: fetch-depth: 0 - name: Setup Python uses: actions/setup-pythonv5 with: python-version: 3.12 - name: Run AI refactor batch run: | python scripts/run_ai_refactor.py \ --batch-id ${{ inputs.batch_id }} \ --config configs/email-migration.yaml - name: Commit changes run: | git config user.name ai-refactor-bot git config user.email botexample.com git checkout -b refactor/${{ inputs.batch_id }} git add . git commit -m refactor: migrate email sender APIs in batch ${{ inputs.batch_id }} - name: Create Pull Request env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh pr create \ --base main \ --head refactor/${{ inputs.batch_id }} \ --title AI refactor: batch ${{ inputs.batch_id }} \ --body 本PR由AI重构流水线自动生成。变更范围email sender迁移。请保持原有业务逻辑不变只做API替换。 \ --label ai-generated为什么用workflow_dispatch而不是push事件因为重构流水线是你主动控制的批量操作如果每推一个分支就触发一次很可能出现“AI还在改文件workflow又跑起来”的循环爆炸。手动触发给足控制权随时可以暂停、调整参数、批量撤回。同时要注意GitHub的API速率限制。并发跑几十个任务时gh操作很容易撞上限制。我在脚本里给每个任务之间加了随机延迟并且把并发控制在3到5路不但稳定CI也扛得住。2.3 质量闸门没有这些检查AI写的东西不能合进去自动生成PR只是第一步能不能合入还得看质量闸门。这套闸门我在工程上一般设四层第一层是格式化和lint检查第二层是单元测试第三层是类型检查或构建第四层是针对核心模块的语义对比。所有检查都必须由CI跑完任何一层红了PR都不能动。GitHub的CODEOWNERS文件在此时非常有用。你可以把它简单理解成“不同目录的变更由哪些人负责”例如# .github/CODEOWNERS src/billing/** billing-team-lead billing-reviewer src/auth/** auth-maintainer src/experimental/** core-infra-team这样AI创建出来的PR会自动分配到对应模块的负责人名下不需要人工去群里喊“谁来审一下”。每批PR生成后我还会用一个异步任务去扫描全部PR的状态凡是CI失败的就自动重新触发一次修复流程这样AI生成的代码首测通过率可以从60%左右拉到90%以上。对于高风险目录我会用规则阻止自动合并。比如billing或者账号安全这类模块PR必须加上awaiting-human-review标签只有人类Reviewer点了Approve并且CI通过才允许合入。低风险的纯机械性替换则可以开启gh pr merge --auto让测试一过就自动合并。这个分层设计非常重要不是所有AI PR都需要人类盯着但关键路径必须盯死。3. 实操过程从0到128个PR关键环节逐段拆解3.1 任务书模板给AI的一次性清晰指令很多人让我看他们用AI做批量重构失败的原因一般不是模型太笨而是任务书写得像个白日梦。一套可复用的任务书至少要包含角色、目标、输入、约束、输出格式、自检要求。下面这份模板我会直接抄进项目里你可以根据自己的迁移类型微调【角色】你是一名资深代码重构工程师负责将一个大型代码库中的旧API调用迁移到新API。 【任务】把输入文件中的所有旧API调用替换为映射表中对应的新API调用。 本批任务只处理文件中的函数调用和参数顺序不改变任何业务逻辑。 【输入文件】 {file_content} 【API映射表】 - old_api(a, b) - new_api(a, b) - old_api(a, b, c) - new_api(a, c, b) // 注意新API把c参数提前了 【约束】 1. 只允许修改与API调用直接相关的代码行。 2. 禁止调整缩进、注释、空行和无关代码。 3. 如果某个调用在映射表中找不到请保持原样并整理到“未匹配列表”。 4. 所有参数必须是字面量或变量原样传参不得做任何推导计算。 【输出格式】 1. 完整的新文件内容。 2. 一个JSON数组列出每一处替换的位置和规则编号。 【自检】 请输出前统计本文件中可疑的旧API调用多少次其中已经匹配的有多少次每一行都是有原因的。第3条约束尤其关键它防止模型遇到没见过的调用时“自由发挥”而是要求它把不确定项列出来交给人类处理。自检步骤则让模型在生成前先想一遍“我要改多少处”能有效提高它的结构化推理能力。同一套任务书不能用于所有场景。改日志库的任务书和改ORM迁移的任务书约束完全不同。我的经验是每种迁移模式固化一份模板比如email-sender-migration.txt、cache-client-migration.txt谁用谁取而不是每次重新写提示词。3.2 扫描、分批、执行调度细节与并发控制拿到任务书之后主流程分四步走扫描影响面、划分批次、批量生成、自动提交PR。扫描时我会先跑一个脚本把所有匹配旧模式的调用点按文件聚合成清单。假设仓库里有600个文件受影响我不会一次性生成600个PR而是按目录或按依赖关系分到128个批次里。为什么不能一个文件一个PR因为有可能6个文件都是被同一套重构逻辑影响你拆成6个PR每个PR都只改一点点Reviewer反而要重复理解同一套背景纯属浪费。划分批次后调度器从清单里依次取出文件拼上任务书发送给大模型。并发数我控制在3到5路太少了赶不上三周完成太多了AI输出质量和CI稳定性都会崩。每一路生成完脚本先做一次“静默校验”检查diff里是否存在任务书禁止的改动比如无关缩进变化、注释翻译、新增空行。校验不过的自动退回重试重试仍不过的丢进人工异常队列。我建议所有批量生成都先开dry-run模式跑一轮。也就是说在正式建PR前先只生成diff输出到一个独立目录里挑10个PR粗看一遍。这一步能尽早暴露任务书里写漏的匹配模式否则128个PR全部生成后发现规则错了代价非常可怕。3.3 冲突处理和二次重试并行BF对撞的真实较量并行PR最大的隐患是冲突。你不可能让80个AI agent同时改代码而不碰撞。我的策略是在源头上尽可能错开同一模块或同一文件的改动尽量放进同一个批次里不改同一文件的PR可以大胆并行。这样真正发生的冲突大多是“两个PR合入后在语义层面互相影响”而不是机械的行级冲突。机械冲突一般用GitHub的 “Update branch” 功能就能解决点了之后自动rebase到最新main如果是简单的增删行Git能自动merge。麻烦的是语义冲突PR A把函数foo()改名成了bar()PR B又新增了20处foo()调用两个PR各自跑测试都绿合到一起之后直接编译失败。这种问题没有完美的自动化解法我能做的只有两点一是保持每个PR改动范围足够单一二是每合入一批PR后在聚合分支里跑一次全量测试。重试逻辑也得设计好。AI生成的代码第一次不过CI最常见是小毛病少导入一个包、变量名拼错、忘了改调用点。我在脚本里配置了“最多重试两次”规则每次都把CI错误信息喂回给模型让它基于报错修正。跑完两次还失败的PR直接标记为needs-human-fix不再浪费计算资源。别指望AI一次成功率能到99%真实跑下来一次通过率在70%左右很正常加上自动重试能到90%以上剩下的10%才是人类的价值。4. 避坑手册84万行改下来最容易踩的五个坑4.1 AI“过度发挥”任务书约束不住怎么办比起漏改更让人头疼的是AI“爱干净”。它会在你只让它改API的地方顺手把整个文件夹的格式改成它觉得好看的样子。这直接污染diff让审查者找不到重点。解决的办法除了任务书里写死“禁止调整格式”之外最有效的是做“diff白名单校验”。我习惯在生成PR前用AST做一次对比旧代码的AST和新代码的AST除了规则中允许的节点变化外其余任何节点发生变化就判定这个PR不合格。比如只允许调用表达式层面的替换那FunctionCall节点的变化在允许列表里但ImportDeclaration的变化就必须被拦截。这个自动化检查比任何prompt都硬直接从机器层面锁死AI的自由发挥空间。另一个实用技巧是“隔离提示”。给模型喂文件时把不需要改动的函数体替换成/** 不需要修改仅供阅读上下文 */。这样模型在生成时注意力会被引导到真正需要改动的区域。实测下来这种方式能够明显减少“额外惊喜”。4.2 CI集群被128个PR打爆128个PR同时pushCI runner瞬间排队这是工程上一定会遇到的问题。我第一次跑类似项目时CI排队时间甚至比AI生成代码的时间还长直接拖垮进度。解决方案分三层。第一层是限制并发PR数量我前面说的3到5路就包含这个意思。第二层是在workflow里配置concurrency让同一时刻只有一个重构任务在跑CI避免互相挤占资源。第三层是分组处理失败如果出现“某测试文件大面积失败”先怀疑公共依赖是不是被改了而不是一个PR一个PR地修。真正节奏是“推挤前进”一批PR在跑CI另一批PR已经推送完成等在队列里人只处理需要决策的反馈。每天下班前清空一次失败队列第二天早上集中看合并结果三周时间就是这么一点一点挤出来的。4.3 代码审查者如何“看得过来”人类Reviewer一天看几十个PR每个PR几千行上线早就炸了。我的做法是强制每个AI PR自带“变更摘要”和“审查指引”。生成PR时脚本会自动统计这份diff里有多少处替换、涉及哪些文件、有无未匹配项然后把摘要填到PR描述里。Reviewer可以先读摘要再决定要不要深入看diff。GitHub的diff页面有一个非常实用但不被很多人注意的功能URL后面加?w1可以忽略空白字符差异。纯改API的PR白空差异一忽略很多“假改动”就消失了真正要看的逻辑变化一眼就能扫完。同时把“高风险目录”和“低风险目录”分开排队。低风险目录的PR用自动合并只要摘要没问题测试通过就合高风险目录集中留给人类精力最充沛的时间审。我在项目里还加了一个“回复超时提醒”24小时内没有approve或comment就自动在频道里提醒。评审不能成为瓶颈否则前面所有自动化建设都白费了。4.4 衡量重构成功不要只看83万行这个项目最容易被外行误解的地方就是拿“83万行”作为成果。行数只是过程产物真正应该看的是四个指标合并效率、回归缺陷数、变更纯净度、人工审查成本。合并效率比如平均每天合入多少个PR回归缺陷数指重构上线后一周内因为这次改动引入的bug数量变更纯净度即diff中跟任务书无关的改动行数占比正常应该控制在5%以内人工审查成本即每个PR平均需要多少review时间。用这四个指标复盘能清楚回答一个关键问题AI到底有没有帮团队省时间如果合并效率上去了回归缺陷没有增加人工审查时间还控制在可接受范围那这套流程就值得继续投入。反过来如果一次重构造成大量回归哪怕PR数量再漂亮也只是把问题从“开发期”挪到了“线上事故期”。我做过几次类似项目后有个很强烈的感受AI重构项目最怕的不是“AI不行”而是流程没有兜底。任务书不清晰、CI不严格、审查不分优先级任何一环掉链子AI生成得越快团队崩得越快。最后分享一个实操中的小经验。如果你也要在自家仓库里做这种大迁移别一上来就想复刻128个PR。先跑一个10个PR的试点用最小成本把任务书、白名单校验、审查节奏全部调顺再铺开到全库。你看到的是三周奇迹看不见的是背后每个PR都被当成一次小项目来对待的耐心。把这套耐心学到了AI才能真正干重活。