AB
AiBoss
チュートリアル

Claude Code

チュートリアル

Claude Code 大规模语言迁移实战:六阶段流程、脚本约束与完整示例

用 Claude Code 推进整库语言迁移的参考教程:拆解六阶段人工签核流程、八个提示词与六个脚本的分工、磁盘产物清单,以及脚本真正会拦截的失败条件,并给出一个可跑通的最小示例。

把一整个代码库从一种语言换到另一种语言,难点通常不在翻译本身,而在于「翻到哪了」「翻得对不对」「有没有偷偷改掉架构」。Claude Code 配合一套迁移工具包,可以把这件事拆成有人工签核的批次:每个阶段结束由人确认,再由人启动下一个提示词,脚本负责把文件依赖、路径替换、输出存在性、命令拦截和构建日志这些机械环节固定下来。本文按流程、产物、脚本约束、完整示例的顺序整理,适合准备做整库迁移、又希望保留架构与数据结构的工程团队参考。价格、配额、版本等信息请以官网当前信息为准。

准备工作

这套流程默认的立场是:保留架构、数据结构和文件边界,只替换语言。它面向的是全面迁移——所有文件都要搬过去,旧语言最终消失。增量的、中途共存的迁移(例如 JavaScript 逐步转 TypeScript)不在设计目标内。

开始之前需要确认几件事:

  • 迁移范围是全面的。如果只想改一部分文件、旧语言还要长期保留,这套流程的出口条件不成立。
  • 有可用的测试面。公开接口层面的测试、或者能临时搭起来的验证脚手架,是判断「行为是否一致」的依据。如果既没有公开面测试,也没有第三方语言的测试套件,出口条件会缺失。
  • 准备好规则手册的落点。工具包把规则写在 migration/RULEBOOK.md,它是后续所有阶段的判据来源。
  • 准备好 Claude Code 的权限配置。工具包提供 templates/settings.json,里面是 permissions.deny 的拦截模式。
  • 确认模型分层策略。写规则和做评审用较大的模型,大批量翻译用较小的模型,这个分层写在 CLAUDE.md 里,属于流程的一部分,不是可选项。

工具包本身以提示词、模板和脚本的形式提供,随附的脚本共六个,模板包括规则手册、缺口清单的列定义,以及上面提到的权限配置。仓库自身的说明里明确写着:这些提示词是面向生产迁移的泛化重构,而不是某次真实迁移的逐字副本。

操作步骤

第一步:理解六阶段结构与人工签核点

整个流程分六段:

  1. 地图与规则
  2. 规则的压测
  3. 翻译
  4. 编译
  5. 启动
  6. 行为一致性验证

每一段结束都需要人工签核,下一个提示词也由人来启动。这是这套流程和「让代理一路跑到底」最大的区别:脚本不负责判断阶段是否合格,人负责。

有一个分支需要提前判断:如果要做的是重新设计,规则手册的角色会变成设计文档,第二阶段的「对比测试」不适用。如果公开面测试还在,那么可行性报告之后直接进入第一阶段;如果测试依赖内部实现细节,则先用 prompts/00b 造出判定器,再往下走。

第二步:按角色挑选提示词

可粘贴使用的提示词共八条,各自承担一个角色:

文件承担的角色
prompts/00可行性评估,包含三个 call 与 Model plan
prompts/00b判定器的创建
prompts/01依赖地图,附带两名怀疑视角的评审
prompts/02缺口清单
prompts/03试点翻译
prompts/04评审,两名评审人
prompts/05修复者,要求 TODO(port) 的计数与 grep 结果一致
prompts/06标记的消化处理

注意 prompts/00 里的 Model plan:模型分层如果没写清楚,属于流程违规。分层检查并不能替代模型选择本身。

第三步:用脚本把机械约束固定下来

随附脚本六个,分工如下:

脚本作用
depmap_python.pyPython 依赖地图,用 ast 取边
depmap_ts.mjsJavaScript / TypeScript 依赖地图,看相对 specifier
depmap_c.pyC 依赖地图,看 "..." 形式的 include 与同名头文件
make_manifest.py生成清单
queue_runner.mjs把还没有输出的文件送进队列
build_daemon.sh只执行一次构建

翻译完成之后,由 queue_runner 挑出还没有产出的文件;调查性构建之后,由 build_daemon 只跑一次构建。队列本身不保留对话记忆——它靠磁盘上文件是否存在来判断进度,这也是它可以随时中断、随时续跑的原因。

第四步:确认磁盘上应该留下什么

完成的单位不是「对话里说完成了」,而是规则手册里命名的输出文件确实存在于磁盘上。主要产物包括:

  • migration/RULEBOOK.md——规则手册
  • migration/depmap/——目录下含 edges.tsv、order.txt、cycles.txt
  • migration/inventory.tsv——清单
  • migration/manifest.tsv——映射表
  • 翻译后的目标文件
  • migration/build-output-rN.txt——构建输出
  • migration/cost-log.tsv——成本记录

第五步:分清「脚本会拦」和「提示词要求」

约束分两类,混淆这两类是常见的踩坑来源。

脚本或配置真正会拦下的:

  • 文件之间的依赖,以及文件粒度的循环——三个 depmap 脚本输出边、批次顺序和大小不小于 2 的强连通分量。它们不看内容是否正确。
  • 多文件却一条边都没有——三个脚本都会往 stderr 写警告,但退出码仍然是 0,并且照样写出空的 edges.tsv。
  • 转换目标路径和原路径相同——make_manifest.py 在 target == source 时以 exit 1 退出。它比较的是路径字符串,不是结构是否一致。
  • 翻译是否完成——queue_runner.mjs 只看输出文件是否存在;它的 verify 只对 0 字节文件报错。
  • 循环中的代表性构建,以及命中 templates/settings.json 中 deny 模式的破坏性 git 与 Bash 命令。
  • 构建执行是否收敛到一处——build_daemon.sh 在树的哈希变化时执行一次 --cmd,把标准输出和标准错误保存到 migration/build-output-rN.txt。它不做诊断表拆解,也不重跑测试。

提示词要求、但脚本不会拦的:

  • 保持结构还是重新设计——写在 prompts/00 的三个 call 和规则手册 §0 的立场段落里,没有脚本检测替换。
  • 相同架构、相同数据结构、相同文件边界——规则手册 §0 有要求,但没有脚本比较类型、字段、文件划分是否一致。
  • 包或 crate 边界的循环——prompts/01 要求「在脚本里压缩」,但随附 depmap 只输出文件路径的强连通分量。
  • 依赖地图的过与不足——靠 prompts/01 的两名怀疑评审,file:line 的逐条比对由模型完成。
  • 缺口清单的覆盖度与行正确性——prompts/02 里 wc -l 只是行数的收据,没有检查器验证每个站点都有对应行。
  • 循环中不编辑规则手册——写在 CLAUDE.md 和各提示词里;权限配置的说明文档明确写着这个区别无法用 permission 表达,只能靠评审发现。
  • 翻译是否遵守规则——靠 prompts/03 的试点和 prompts/04 的两名评审,契约是「指摘必须引用规则或源码行」,但没有脚本验证引用真实存在。
  • PORT STATUS 里的 todos=——prompts/05 要求修复者与 grep -c 'TODO(port)' 对齐,执行者是模型,queue_runner 不看这个。而且等式只覆盖 TODO(port),BUG(port) 和 PERF(port) 不计入。
  • 行为是否一致——靠第六阶段和 prompts/00b 的判定器,用使用侧测试或临时搭的脚手架;成败记录是 RUN-NOTES 里的自我申报。

第六步:按需补上自己的失败条件

想保留的层次,光写进规则手册是不会被检查的。要让它真正生效,得加进自己的脚本失败条件里。常见的四个补充方向:

  1. 除了文件图,再按目标语言的包边界检测循环。
  2. 把「0 条边」改成 exit 1。
  3. 在 queue_runner 的 verify 里加入尾注条数与 TODO(port) / BUG(port) / PERF(port) 的一致性检查,而不是只留在提示词里。
  4. 在 fan-out 之前,由脚本检查 .claude/settings.json 的 deny 是否存在,而不是写一句「让代理自己看」。

另外,行为判定器应该在翻译之前先跑一次「用坏输入让它失败」,把这次失败记录下来。prompts/00b 写了这个步骤,但运行记录显示它尚未被执行。

一个完整示例

下面是一个从头到尾能跑通的最小流程,假设目标是把一个小型 Python 库整体迁移到另一种语言,仓库根目录为工作目录。

1. 建立迁移目录并放入规则手册

mkdir -p migration/depmap
cp templates/RULEBOOK.md migration/RULEBOOK.md
cp templates/settings.json .claude/settings.json

规则手册 §0 要写清立场:是保持架构与数据结构,还是重新设计。这一步的结论决定后面是否使用第二阶段的对比测试。

2. 生成依赖地图

python scripts/depmap_python.py --root . --out migration/depmap

产出三个文件:edges.tsv 是边,order.txt 是批次顺序,cycles.txt 是大小不小于 2 的强连通分量。如果仓库里明明有多个文件却一条边都没有,stderr 会出现警告,但退出码仍是 0,edges.tsv 会是空的——这种情况需要人工介入,不能当成「没有循环」。

3. 生成清单与映射表

python scripts/make_manifest.py --root . --out migration/manifest.tsv

如果某个条目的目标路径和源路径完全相同,脚本会以 exit 1 退出。这是路径字符串层面的检查,不代表结构真的对应上了。

4. 用队列推进翻译

node scripts/queue_runner.mjs --manifest migration/manifest.tsv --dir migration

队列只把「输出文件还不存在」的条目交出来,不保留对话记忆。中断之后重新执行同一条命令即可续跑。verify 只对 0 字节的输出报错,所以「文件存在但内容不对」不会被这一步拦住,需要靠 prompts/04 的两名评审。

5. 单次构建并留存日志

bash scripts/build_daemon.sh --cmd "make build"

树的哈希发生变化时,它执行一次 --cmd,把标准输出与标准错误写进 migration/build-output-rN.txt。它不拆解诊断表,也不重跑测试。把编译器放进循环之外是有意为之:配置说明里提到,代理会为了迎合编译器而收敛翻译的野心。

6. 人工签核并进入下一阶段

检查规则手册里命名的输出文件是否都在磁盘上,然后由人启动下一个提示词。整个流程的推进权始终在人手里。

注意事项

规模数字要分开读。公开材料里出现的行数、token 数、费用、测试件数,来源并不相同:一部分是作者博客对自己那次运行的自我申报,另一部分是工具包自己跑过的小规模运行记录。工具包本身没有价格表。把博客里的数字当成这套工具包的实测结果,是不成立的。

工具包的运行记录规模很小。已完成的自我验证覆盖的是几个小目标:一个 5 文件 1425 行的库、一个约 730 行的表达式求值器,以及一次 7 文件 2513 行的重译。这不是生产级规模。

运行记录里的已知问题。第一次运行因为比较器缺陷,12 项里有 12 项都是假差异;prompts/00b 标注为尚未自我验证;第二次运行到最后都没有把 settings 装进去;第二、三次运行用的是默认模型。这些都属于自我申报,不是外部复核结论。

deny 不是安全边界。权限配置的说明文档明确写着:确定性的包装器可以绕过这些模式,它「不是安全边界」。没有 deny 的运行,脚本不会阻止。prompts/03 和 prompts/04 允许通过 deviation log 里的显式豁免来放行缺失项。deny 在包装器以外的路径上能守到什么程度,公开资料范围内没有定论。

行数会因数法差 1。规则手册模板里写的行数与 wc -l 的结果可能相差 1,原因是文件末行没有换行符。GitHub 显示的行数包含最后一行,wc -l 数的是换行符个数。核对时留意这一点。

文件边界的处理方式可能和工具包默认不同。有的迁移文档会要求:文件末尾的 @import 不要一对一照搬、alias 要删掉、生成文件写成三行 stub。这类做法与工具包「保持相同文件边界」的默认立场并不一致,需要提前决定采用哪一套。

仓库的维护状态。默认分支只有一次提交,说明文档里写着不看 Issue 和 PR。Issue 功能本身是开启的,但当时没有未关闭的缺陷报告;有一个 PR 正文为空、未合并,内容是给 Java 和 .NET 增加 depmap 的提案,没有进入主分支。许可证文件正文是 Apache License 2.0,而 GitHub 的 license API 当时返回的 spdx_id 是 NOASSERTION,两者显示不一致。

以下条件会让这套模板的机械部分失去出口。做重新设计时,对比测试不适用;做不删除旧语言的增量迁移时,出口条件不成立;既没有公开面测试也没有第三方语言测试套件时,出口条件缺失;类型检查足够便宜、于是去掉 deny 并把第四阶段并入第三阶段时,调查性构建不会被使用;忘记装 settings 时,会在没有护栏的情况下直接跑完。

最后,模型分层要保留:写规则和做评审用较大的模型,大批量翻译用较小的模型。分层检查不会让模型选择这件事消失。