本文是《AI-Infra 开源贡献指南》系列的第 5 篇(共五篇)。上一篇:两个真实 PR 的完整走读——PyTorch 与 vLLM。
四篇正文回答了一个问题:面对一个百万行的开源项目,如何找到切入点、做出一个能被合入的改动。第一篇讲怎么读——从符号、报错、issue 出发有目标地检索;第二篇讲怎么选——maintainer 已经写出了”我们想要什么”,选题是去读它们再查一次重;第三篇讲怎么交——每条规则都还原成”reviewer 的十分钟”的一个侧面;第四篇把前三篇放到两个真实 PR 的时间线上,量出时间花在哪里。样本始终是 PyTorch v2.14.0 与 vLLM v0.28.0,GitHub 上的动态信息截至 2026-09 用 gh 查询。
本文不讲新内容,做三件事:把四篇压成一张表与四段回顾,把贯穿四篇的几条线拎出来,然后给一套三段式的通关自测——判断与计算、跨篇综合、面试题。各篇末尾的自测检验的是”这一篇读懂了没有”,这里检验的是”四篇能不能连起来用”。第四篇末尾「系列总结」讲的三样东西(贡献日志、两套流程的对照、看 PR 的眼光)与三种能力,本文第三章完整覆盖。
先把整个系列放在一张图上——箭头是推导或前置上的依赖(箭头尾端的结论被箭头头端当作前提),不是阅读顺序:
%%{init: {"flowchart": {"wrappingWidth": 420}}}%%
%% 图:开源贡献系列全景:读懂 → 找到 → 做出 → 两个实例
flowchart TB
C1["01 读懂一个百万行代码库:地图、入口、生成代码、构建、测试、历史"] --> C2["02 找到切入点:标签、RFC、CI 失败、性能回归;先讨论、查重"]
C2 --> C3["03 做出能被合入的改动:最小 diff、测试、lint、描述、CI、review"]
C3 --> C4["04 两个真实 PR 的走读:PyTorch #185344 · vLLM #47272——前三篇的规则在时间线上各出现在哪"]
一、总览:系列回答的问题与主线
系列的一句话主张是:maintainer 的 review 时间是项目最稀缺的资源,所有规则都是为了保护它;细节会变,这条逻辑不变,理解了它就能在规则变化后自己推导出新的做法。四篇按一次贡献的自然顺序推进——先读懂、再找到、再做出、最后看两个完整的实例——每篇同时用 PyTorch 与 vLLM 做例子:一个是十年历史、治理成熟、流程厚重、CI 全跑、bot 合入的框架;一个是两周一版、按需跑 CI、maintainer 手动合入、规则还在快速演化的引擎。
| 篇 | 回答的问题 | 一句话结论 | 必记的数字 / 判据 |
|---|---|---|---|
| 第一篇:读懂一个百万行的代码库 | 给你一个从未见过的百万行仓库和一个报错,两小时之内能定位到一个文件的一个函数吗?靠什么? | 能。靠有目标的检索:提取符号 → 画地图 → rg → 找登记表 → 沿链追 → 识别生成代码 → 读测试 → 读历史;构建是为了工具链,可选、放最后 |
PyTorch c10/ → aten/ → torch/csrc/ → torch/,登记表 native_functions.yaml;vLLM csrc/ → vllm/,登记表 pyproject.toml / torch_bindings.cpp;发布约 2 个月 vs 约 2 周;引言案例四十分钟得出”已在 v2.11.0 修复” |
| 第二篇:找到切入点 | 每天几十个 issue、几十个 PR,maintainer 最希望有人来做的是哪一类?怎么判断自己选的题不会一周后被关? | 他们已决定要做、写清了要什么、自己没时间做的事;预测器是标签状态、maintainer 最后一条评论、open PR 数、规模与 RFC 门槛、硬件、项目政策 | PyTorch 682 个标签、actionable 396 占不到 3%、状态链四态;vLLM 63 个标签、closed-as-slop 97;RFC 门槛 >500 LOC(不含 kernel / data / config / test);stale 90 + 30 天;#191394 下 5 个 open PR |
| 第三篇:做出一个能被合入的改动 | reviewer 打开你的 PR 只有十分钟,他要确认什么?diff、描述、测试、CI 状态分别替他回答了哪个问题? | 四件事:改了什么且只改了这一件、为什么改怎么验证、怎么证明对怎么防回归、有没有弄坏别的;diff、描述、测试、CI 各答一个 | PyTorch 2000 行硬上限、61 个 linter、148 个 workflow、49 个 ciflow/*、33 条 merge rule、4 个工作日可催;vLLM 6 个 open PR 上限、35 个 test_area、pre-commit 需 verified / ready 或 ≥4 个合入 PR、2–3 天 / 7 天、DCO 每个 commit |
| 第四篇:两个真实 PR 的完整走读 | 两个都是”小”PR,却各花了作者一到几周。时间花在哪里?哪些可省,哪些是正常成本? | 小 PR 的时间不在写代码:PyTorch 那个在数据(正常成本),vLLM 那个在等待(大半可省) | #185344:+104 −0、27 天采 3792 个点、PR 5 天、3 小时 42 分收到 review、merge -i、进 v2.13.0;#47272:+109 −16、47 天无 review 未 ping、6 个自己引入的 CI 失败、合入 08-20 不在 v0.28.0(分支 08-17 切出) |
1. 本文的章节安排
| 章 | 内容 |
|---|---|
| 二 | 逐篇回顾:核心问题、结论、必记、常见误解 |
| 三 | 贯穿四篇的四条线:reviewer 的时间、每个结论一条命令、两种项目风格、贡献日志的四页 |
| 四 | 常见误区表 |
| 五 | 通关自测:A 判断与计算 10 题、B 跨篇综合 5 题、C 面试题 7 题、D 掌握判据 |
| 六 | 下一步 |
二、逐篇回顾
1. 第一篇:读懂一个百万行的代码库
核心问题:给你一个从未见过的百万行仓库和一个报错信息,两小时之内你能把它定位到一个文件的一个函数吗?靠什么?
结论:能,靠有目标的检索而不是阅读——四种典型失败(从头读、随便读、被生成代码卡住、只读代码不读测试与历史)都是把阅读当成了线性活动。六步方法与项目无关:画地图、找入口、识别生成代码、构建一次、读测试、读历史。关键是找到把”名字”映射到”实现”的登记表:PyTorch 是 native_functions.yaml,dispatch: 指向 native/*.cpp,再经 DEFINE_DISPATCH 桩到 cpu/、cuda/ 下的 kernel;vLLM 是 pyproject.toml 的 [project.scripts] 与 csrc/libtorch_stable/torch_bindings.cpp。”找不到定义”的主因是 torchgen 生成的代码,处理办法是回 yaml、改 .pyi.in、C++ 侧构建后用 clangd;构建是为了工具链,第一次追踪不需要,第一个 PR 之前必需。测试是最准确的规格,历史是注释——PyTorch 的 commit 正文含 PR 描述,vLLM 的”为什么”要 gh pr view。
必记:
- 规模与分层:PyTorch 21663 个文件约 475 万行,
c10/→aten/→torch/csrc/→torch/;vLLM 6596 个文件约 202 万行,csrc/→vllm/,tests/40 个子目录对应vllm/子系统。 - 构建:PyTorch
pip install -e . -v --no-build-isolation(纯 Python 用tools/nightly.py);vLLMVLLM_USE_PRECOMPILED=1 uv pip install -e .跳过 CUDA 编译,改 kernel 用cmake --preset release增量。 - 测试框架三件套:
TestCase/run_tests、instantiate_device_type_tests(CI 里的TestBinaryUfuncsDeviceCUDA.test_logaddexp_cuda_complex128要去掉后缀找源码)、OpInfo。 - 发布节奏:PyTorch 约 2 个月一个 minor、cut 到发布 3–4 周、
@pytorchbot cherry-pick -c regression等五种理由;vLLM 约 2 周一版、cut 到发布 1–2 天。
常见误解:”仓库不完整、头文件丢了”——ATen/ops/logaddexp_native.h 在源码树里确实不存在,它由 torchgen 构建时生成,回 yaml 读比读生成物快。另一个:”构建好了才能开始读”——两条追踪四五条 rg 就走完,引言的案例四十分钟得出”已在 v2.11.0 修复”的结论。
2. 第二篇:找到切入点——从 issue、RFC 到性能回归
核心问题:一个项目每天新增几十个 issue、几十个 PR。maintainer 最希望有人来做的是哪一类工作?你怎么判断自己选的题不会在一周后被关闭?
结论:大多数失败的贡献不是做错了而是选错了——撞车、越级、选了项目不要的、选了做不完的。选题是去读 maintainer 已写出的”想要什么”,再在动手前查一次重。PyTorch 真正决定选题的是状态标签链 needs reproduction → needs research → needs design → actionable,只有 actionable 该动手,因为打标的 maintainer 已承诺 review;vLLM 没有状态机,help wanted 加分步骤正文是”欢迎”的信号。RFC 的载体不同(pytorch/rfcs 仓库 vs 750-RFC.yml issue 模板),判断标准相同——有没有需要 maintainer 拍板的设计决策,而不是行数。issue 之外的信号源:CI 失败、性能回归、成体系的文档与类型批次、补测试、deprecation 流水线、复现、测量——产出是信息而不是 diff,几乎不会撞车。查重是三条 gh 命令,第三条按关键词搜最能抓到没链 issue 的重复 PR。
必记:
- PyTorch:682 个标签(
module:257、ciflow/68、release notes:65);actionable396 open、占 13,985 个 open issue 的不到 3%;needs reproduction535、skipped200。 - vLLM:63 个标签;
good first issue21、help wanted32、closed-as-slop97 个已关闭 PR;600-new-model.yml写的 “new model” 与实际标签new-model不一致,按标题前缀搜。 - RFC 门槛:vLLM >500 LOC 架构改动,不含 kernel / data / config / test,无 RFC 标
rfc-required(该标签截至 2026-09 不在列表中);Feedback Period 通常至少一周。 - 三条查重命令:
gh issue view <n> --comments、gh pr list --state open --search "<n> in:body"、--search "<keywords>";Fail-closed。
常见误解:”issue 还 open 就是没人在做”——#191394 挂着 5 个 open PR,第六个没有价值;要看 open PR 数与最后一条评论日期。另一个:”typo PR 是最安全的第一个 PR”——vLLM 明文 “No low-value busywork PRs”,PyTorch 的 AI_POLICY.md 划了”meaningful human involvement”的底线;替代方案是 systematic 的批次,#183036 里 maintainer 要的正是”不让其他 optimizer 出同样问题”的修法。
3. 第三篇:做出一个能被合入的改动
核心问题:reviewer 打开你的 PR,只有十分钟。这十分钟里他要确认什么?你的 diff、描述、测试、CI 状态分别替他回答了哪个问题?
结论:从改动到合入的十几道关卡都能还原成同一句话——review 时间最稀缺,规则让这十分钟效率最大化并把不值得的 PR 提前挡掉。六条通用做法:一个 PR 只做一件事、改动必须带测试、性能改动必须带数字、先过本地 lint 再推、按模板写描述、把 CI 变绿是作者的责任。PyTorch 把体积写进了 CI(超 2000 行直接失败),大改动用 ghstack 叠 PR;vLLM 有 500 行 RFC 线与 6 个 open PR 上限,拆分靠顺序开 PR。描述上 PyTorch 元数据化(三个模板、Fixes #),vLLM 正文化(Purpose / Test Plan / Test Result 加标题前缀)。CI 是两种哲学:PyTorch 推送即跑、分层触发;vLLM 默认不跑、按需 /ci run。CI 红了先问 main 是否也红。合入 PyTorch 由 @pytorchbot merge 按 merge_rules.yaml 校验,vLLM 由 maintainer 打 ready 后手动合入。被拒分方向 / 时机 / 做法三类,只有第三类值得”改了再提”。AI 政策两家都不禁止用 AI,要求提交的人读懂每一行、能为每一行辩护、在描述里声明。
必记:
- PyTorch:2000 行硬上限;61 个 linter;148 个 workflow;49 个
ciflow/*(需写权限);merge_rules.yaml33 条,每条patterns/approved_by/mandatory_checks_name(几乎都是EasyCLA、Lint、pull),Core Maintainers兜底;合入还需release notes:或topic: not user facing标签。 - vLLM:500 行 RFC 线、6 个 open PR 上限;35 个 test_area;pre-commit 需
verified/ready或作者 ≥4 个合入 PR;/ci run授权链:写权限 → 受信名单 → 非作者拒绝 → draft 拒绝 →ready标签 → 受信 reviewer 的 approval;run_all_patterns(csrc/、CMakeLists.txt、setup.py等)改了就全跑。 - benchmark 六要素:基线、对比、硬件、shape、方法与命令、不利 case;vLLM kernel 脚本放
benchmarks/kernels/,端到端用vllm bench(benchmarks/benchmark_*.py在 v0.28.0 只是弃用桩)。 - 签名与 review:CLA 签一次 vs DCO 每个 commit
-s(漏签要 rebase 重写);PyTorch 4 个工作日无回应留言、再去每周五 Office Hours;vLLM 7 天可 ping、pr-review-request@vllm.ai加急。
常见误解:”我的 PR 被 approve 了为什么合不进”——approve 的人不在改动文件对应规则的 approved_by 里,或缺 release notes: 标签,或 pull 里有失败项;拒绝信息会告诉你差哪条。另一个:”CI 是绿的”在两边含义不同——vLLM 新贡献者的 PR 默认连 pre-commit 都不跑,一片灰色不等于绿。
4. 第四篇:两个真实 PR 的完整走读——PyTorch 与 vLLM
核心问题:两个都是”小”PR,却各花了作者一到几周。这些时间花在哪里了?哪些是可以省的,哪些是这个项目的正常成本?
结论:走读分七个阶段——起点、阅读、diff、测试与数据、CI、review、合入之后——每阶段一条 gh / git 命令作为出处,时间戳排成时间线,每段空白归到”谁在等谁”。PyTorch #185344(torch.linalg.lu 的后端切换启发式):issue 由 linalg maintainer 开、长期贡献者接,没有 actionable 也合入了——那条规则针对新贡献者;diff 一个文件 +104 −0;没有新增测试,用 4 种 GPU、3792 个采样点的命中率表(97.5%)代替;3 小时 42 分后收到三条行内意见,全部关于注释——数字与描述对齐、”Blackwell” 指哪块卡、删掉代码注释里的 “Authored with Claude”;@pytorchbot merge 被两个无关的 docs 任务挡住,merge -i 合入;进 v2.13.0。vLLM #47272(启动检查预留 KV null block):issue 被部分修复过又被 stale bot 关闭,PR 开出后 47 天无 review、作者未 ping;社区成员开配套 PR #52530 并主动划界;maintainer 一条带代码的意见把范围扩到 auto-fit;ready → /ci run → 6 个失败全是自己引入的边界测试 → 修测试 → 88 项全绿 → 合入;08-20 合入 main,但 v0.28.0 的分支 08-17 已切出。
必记:
- #185344:+104 −0;issue 到 PR 27 天(推断为采数据);PR 5 天 6 小时,等 review 约 1 天 6 小时、往返约 1.5 小时、CI 约 20 分钟、作者工作约 4 天;219 项检查 2 失败;v2.13.0 release 07-08,issue 到用户可装 69 天。
- #47272:+109 −16;PR 49 天 18 小时,等 review 约 49 天(一段 47 天 13 小时)、往返约 8 小时、CI 约 4 小时、作者工作不到 1 天;Buildkite #84702 6 失败 → #84725 88 项全绿;可省估计 30–40 天加一轮 CI。
- 查已合入 PR:PyTorch 的合入是 pytorchbot 关闭 PR 并推 commit,要搜
label:Merged而不是is:merged;vLLM 是 GitHub 原生 merged。 - 两条经验:注释与描述的数字对齐、署名放描述不放代码;第 8 天 ping、提交前
rg边界测试、查重要重复做(#48724 的”不重复”声明在 08-18 之后过期)。
常见误解:”review 意见是在检查算法对不对”——三条意见没有一条关于算法,reviewer 守的是可维护性、数据来源可追溯、项目政策。另一个:”合入了就在最新版里”——#47272 合入六天后 v0.28.0 发布,却不在里面;不查 tag 就回答”在最新版里”是错的。
三、贯穿全系列的几条线
1. reviewer 的时间是最稀缺的资源
这是总纲提出、四篇各自展开的那条逻辑。第一篇里它还是隐含的:”四十分钟得出已在 v2.11.0 修复”避免的是白做一周再让 reviewer 关掉一个重复 PR。第二篇把它变成选题判据:已有 5 个 PR 的 issue 第 6 个几乎不增加价值,没有 RFC 的千行 PR 让 reviewer 在 PR 里补设计讨论,typo PR 走完 CI、review、merge 的全部开销换一个字母——actionable、help wanted、closed-as-slop、”No low-value busywork PRs” 是同一件事的正反面。
第三篇把它写成六条通用做法与一张十分钟的表:标题标签 → 目的 → CI → diff → 测试 → 数据 → 风险,任何一格空着,reviewer 就在那一分钟停下来写 “how did you test this?”。第四篇量出了价格:PyTorch 那个 PR 三条意见合计 42 分钟往返,全是提交前自查就能避免的;vLLM 那个 PR 47 天沉默里作者一次没 ping。承诺是平均值,个案要靠自己推进。
2. 每个结论一条命令:从 rg 到 gh api
工具线是递进的:第一篇 rg / clangd / git log -S / git blame -w -C,第二篇 gh label list / gh issue view --comments / gh pr list --search,第三篇 spin lint / pre-commit run / gh pr checks --json / ci-fetch-log.sh,第四篇 gh pr view / gh pr diff / gh api …/reviews /comments /commits /events /actions/runs / git tag --contains。工具在变,原则不变:每个结论都要有一条能重复执行的命令作为出处,没有一条引用来自记忆。
这条线在第四篇收束为”走读七阶段”,也就是总纲说的”一种看 PR 的眼光”:打开任何一个已合入的 PR,用同样的七条命令走一遍,一小时内就能学到这个项目的 reviewer 在守什么、CI 在拦什么、时间通常花在哪。第一篇的”两小时定位流程”与第四篇的”七阶段走读”是同一种方法的两个方向:一个从报错追到代码,一个从 PR 追到时间线。
3. 两种项目风格:厚重而自动 vs 轻快而按需
PyTorch 与 vLLM 在每篇并排出现,四张对照表叠起来是两种一致的风格。第一篇:PyTorch 分四层、torchgen 生成代码、commit 正文含 PR 描述、约两个月一版;vLLM 两层、无源码生成、commit 只有 trailer、约两周一版。第二篇:PyTorch 682 个标签五层前缀加四态状态机、RFC 在独立仓库、flaky test 由 bot 自动开 issue;vLLM 63 个标签平铺、RFC 是 issue 模板、CI 失败由人开 issue。第三篇:PyTorch 推送即跑几百项检查、@pytorchbot merge 按 merge_rules.yaml 校验、CLA 一次;vLLM 默认只跑 pre-commit、/ci run 按需、maintainer 打 ready 手动合入、DCO 每个 commit。第四篇落到编号:#185344 的 219 项检查、3 小时 42 分收到 review;#47272 的 88 项检查、47 天沉默。
两种风格各有代价:PyTorch 把信息放进结构化元数据,机器先替 reviewer 回答很多问题,但外部贡献者打不了 ciflow/*;vLLM 把信息放进描述正文,描述写得好坏影响更大,且新贡献者连 lint 都要等人打 verified。总纲说学会这两套再读 NCCL、Megatron-LM、FlashAttention、Triton、SGLang 的 CONTRIBUTING.md,半小时就能定位差别——先问”它更像哪一个、差在哪几格”。
4. 贡献日志:四页证据链
系列的练手项目不是代码,是一份 Markdown 日志,每篇新增一页:第一篇”项目地图”(目录职责、构建命令、测试入口、rg 模式、追过的符号路径);第二篇”切入点清单”(六个候选的来源、open PR 编号与最后活动日期、规模与硬件、是否需先讨论、结论,允许写”放弃”);第三篇”PR 草稿与往返记录”(diff 摘要、测试、benchmark、描述初稿、每次 push 的 CI 归因、每轮 review 的改 / 争 / 问——”争”占多数通常说明选题阶段就有问题);第四篇”复盘”(按七阶段重述自己的 PR、时间去了哪、下次省什么)。
四页有同一条要求:每一项都有出处——一条命令、一个链接、一段日志。第四篇解释了为什么:走读只能算出 GitHub 上留下痕迹的时间,vLLM 作者 PR 开出前那 63 天从外面看不到;只有作者自己记,才知道时间真正去了哪里。这四页填完,就是总纲说的三种能力——阅读、判断、交付——的全部证据。
| 概念 | 出现的篇 | 关系 |
|---|---|---|
| reviewer 的时间最稀缺 | 二、三、四 | 二用它定选题判据;三推出六条做法与十分钟表;四量出价格(42 分钟 vs 47 天) |
actionable 与 RFC 门槛 |
二、三、四 | 二定义状态链与 >500 LOC;三讲被要求先开 issue / RFC 后怎么办;四解释 #181999 无 actionable 却合入(规则针对新贡献者) |
| 查重 | 二、三、四 | 二给三条 gh 命令与 Fail-closed;三把 “Not a duplicate” 列入 AI 辅助 PR 的四项必填;四展示 #52530 主动划界、#48724 的声明过期——查重不是一次性的 |
| 测试是规格 / 改动带测试 | 一、三、四 | 一教怎么找与读(OpInfo、tests/<subsystem>/);三讲怎么写(instantiate_device_type_tests、AGENTS.md 四问、opcheck);四看既有测试加数据表 vs 三个新用例与三个被修正的边界测试 |
| CI:谁触发、红了是谁的 | 二、三、四 | 二把 CI 失败当切入点;三讲两种哲学与判断表;四对照退出码 127(无关,merge -i)与 6 个自己引入的失败(修测试) |
| AI 辅助政策 | 二、三、四 | 二从”不要 typo PR”引出 AI_POLICY.md / AGENTS.md;三逐条对照;四看 “Authored with Claude” 该放哪、Co-authored-by trailer |
四、常见误区
| 误区 | 为什么错 | 正确的说法 | 出处 |
|---|---|---|---|
rg 找不到 def、头文件不存在,说明仓库不完整 |
torch.<op> 是 C++ 绑定,ATen/ops/*.h 与 .pyi 由 torchgen 生成;vLLM 的 torch.ops._C.<op> 在 .so 里 |
回 native_functions.yaml、改 .pyi.in、回 torch_bindings.cpp |
第一篇 |
| issue 还 open 就是没人在做 | #191394 挂着 5 个 open PR;good first issue 是最挤的池子 |
三条查重命令看 open PR 数与最后一条评论 | 第二篇 |
| 代码写得多就需要 RFC,写得少就不需要 | 标准是有没有需要 maintainer 拍板的设计决策;500 行不含 kernel / data / config / test | 800 行纯 kernel 优化可不走 RFC,200 行引入新公开接口的改动应该走 | 第二篇 |
| typo PR 是最安全的第一个 PR | vLLM 明文不接受 busywork;PyTorch 受 AI_POLICY.md 约束;closed-as-slop 已打在 97 个 PR 上 |
做成有范围、有依据、能说出”怎么找全”的 systematic 批次 | 第二篇 |
| vLLM 的 PR 开出来 CI 是灰的,等它自己跑 | 默认只跑 pre-commit 且有门槛;测试任务要 /ci run;新 commit 不自动重跑 |
本地 pre-commit run --all-files;等 reviewer 敲 /ci run 或打 ready;每次 push 后重新敲 |
第三篇 |
| review 意见是在检查算法对不对 | #185344 的三条意见全是注释与来源 | 提交前用描述里的数字校对代码注释;署名放描述不放代码 | 第四篇 |
| 合入了就在最新版里 | #47272 合入六天后 v0.28.0 发布却不含它——分支已于 08-17 切出 | git tag --contains <sha>;merge-base --is-ancestor 核对 |
第四篇 |
五、通关自测
A. 判断与计算(10 题)
-
一个 PyTorch PR 的
git diff --stat共 2300 行,其中 500 行来自.gitattributes标为linguist-generated=true的文件。pr-sanity-checks会不会拦下它?答案
不会。
pr-sanity-check.sh统计的是非生成文件的增删行数之和,2300 − 500 = 1800 ≤ 2000。但 1800 行 reviewer 十分钟读不完,仍应拆成 ghstack 的一叠。 -
一个 vLLM PR 改了 900 行 CUDA kernel、300 行测试、120 行
vllm/v1/core/sched/的调度器代码。按docs/contributing/README.md的规则,需要先开 RFC 吗?答案
不需要。500 LOC 的门槛不计 kernel / data / config / test,计入的只有 120 行调度器代码。但真正的标准是”有没有需要 maintainer 拍板的决策”,引入新设计的话仍应先在 issue 里说一句。
-
一位 vLLM 贡献者已有 3 个合入的 PR,新开的 PR 没有任何标签。pre-commit 会在 CI 上跑吗?要让它跑,最轻的标签是哪个?
答案
不会。
pre-run-check要求verified/ready/ready-run-all-tests之一,或作者至少 4 个合入 PR;3 个不够。最轻的是verified——”Run pre-commit for new contributors without triggering other tests”。 -
vLLM PR 的作者在自己的 PR 下评论
/ci run。PR 已被打上ready标签,但仍是 draft。CI 会跑吗?答案
不会。
authorize按顺序判断,第 4 步 “PR authors cannot run CI while the PR is a draft” 先于第 5 步的ready检查。转正式 PR 后再敲。 -
一个 PyTorch PR 只改了
torch/distributed/checkpoint/下的文件,labeler.yml自动打上了release notes: distributed (checkpoint),一位 linalg 模块的 maintainer approve 了它,pull、Lint、EasyCLA全绿。@pytorchbot merge会成功吗?答案
大概率失败。
find_matching_merge_rule要找一条”改动的每个文件都匹配patterns、且至少一个 approver 在approved_by里”的规则;linalg maintainer 不在 distributed checkpoint 规则的名单里,除非他同时在兜底规则Core Maintainers里。标签与 checks 已满足,拒绝信息会指出差的是哪条规则。 -
一个新功能 PR 于 2026-08-15 合入 PyTorch main。按 v2.14.0
RELEASE.md的日程表,用户最早在哪个正式版本拿到它?如果它是一个 regression fix 呢?答案
2.14 的 branch cut 是 2026-08-10,cut 之后 “new features are not added to the release branch”,所以进 2.15(cut 09-28、发布 10-28)。regression fix 可以在 release tracker issue 里提名
@pytorchbot cherry-pick -c regression进 2.14 的 patch 版本。 -
vLLM issue #35541 于 2026-02-27 开出,最后一次活动是 2026-03-31。按
.github/workflows/stale.yml的规则,它会在哪天被标stale、哪天被关闭?答案
90 天无活动标
stale:03-31 + 90 天 = 06-29;再 30 天关闭:07-30。与第四篇查到的事实一致(06-29 被标记、07-30 自动关闭)——那时 #47272 已经开了一个月。 -
CI 日志里失败的测试名是
TestLinalgCUDA.test_lu_family_cuda_float64。去源码里找时应该搜哪个类名、哪个方法名?为什么?答案
类
TestLinalg、方法test_lu_family。instantiate_device_type_tests把一个测试类按设备实例化成<Class>CPU、<Class>CUDA,并给方法名加设备与 dtype 后缀;源码里没有带后缀的名字。 -
一个 PyTorch issue 被打上
module: inductor,一个 PR 被打上release notes: distributed (fsdp)。按.github/label_to_label.yml,它们各自会自动多出什么标签?对贡献者意味着什么?答案
issue 进
oncall: pt2队列;PR 触发ciflow/h100-distributed。前者告诉你 issue 会被哪个 oncall 看到——没人理先查它有没有落进任何oncall:;后者意味着改 FSDP 的 PR 会自动在 H100 上跑分布式任务。 -
一个 vLLM PR 有 5 个 commit,第 3 个漏了
-s。DCO check 会怎样?最少要重写几个 commit?PyTorch 的 CLA 遇到同样情况呢?答案
DCO 红,mergify 的
comment-dco-failure留言。要用git rebase --exec 'git commit --amend --no-edit -s' main重写第 3 个及其后的 commit(至少 3 个,rebase 会改写后续 sha);装了signoff-commit钩子以后不会再漏。PyTorch 的 CLA 签一次即可,历史 commit 不用改。
B. 跨篇综合(5 题)
-
你在 PyTorch 看到一个 issue:”
torch.X在 complex64 上 CPU 与 CUDA 结果不一致”,标签是needs reproduction、module: linear algebra。从看到它到决定下一步,你会做哪几件事?各用哪一篇的方法?答案
第一篇:
rg '^- func: X' native_functions.yaml→dispatch:→cuda/下的 kernel,rg -l X test/*.py找测试与 OpInfo,git log -- <kernel 文件>看是否已有人修(引言案例正是这样发现 #163509 已修、在 v2.11.0)。第二篇:needs reproduction不是actionable,发 PR 会被关;能做的是复现并把复现写成TestCase风格的最小测试草稿贴在评论里,推动它走向actionable。第三篇:真要修时测试用instantiate_device_type_tests写成设备无关;一个 kernel 修复通常是 kernel 文件 + 测试 + OpInfo 的 dtype 更新。 -
你打算优化 vLLM
csrc/下的一个 kernel。从读代码到 PR 合入,哪些步骤分别由第一、二、三篇决定?哪一步会让 CI 周期比改 Python 长得多?答案
第一篇:登记表在
csrc/libtorch_stable/torch_bindings.cpp,实现在同目录的.cu;用cmake --preset release增量编译并打开CMAKE_EXPORT_COMPILE_COMMANDS。第二篇:先查重(三条命令)、确认手上的卡能复现。第三篇:kernel 不计入 500 行 RFC 线;正确性用torch.library.opcheck加现有 pytest,性能脚本放benchmarks/kernels/而不是tests/;Test Result 放六要素的表。周期长在 CI:ci_config.yaml的run_all_patterns含csrc/,改一行 kernel 就触发全量 CI,且要等 reviewer/ci run。 -
用户问你:”我的修复合进 vLLM main 了,什么时候能
pip install到?”第一篇与第四篇各给了你什么工具回答这个问题?如果是 PyTorch 呢?答案
第一篇:
RELEASE.md——vLLM 约两周一版、branch cut 在发布前 1–2 天、cut 后只收符合准则的 cherry-pick;PyTorch 约两个月一版、cut 到发布 3–4 周。第四篇:不要凭节奏猜,用git tag --contains <sha>与git merge-base --is-ancestor核对——#47272 08-20 合入、v0.28.0 08-26 发布却不含它,因为分支 08-17 已切出;#185344 06-01 合入,进了 07-08 发布的 v2.13.0。 -
PyTorch #185344 的 issue 没有
actionable标签却顺利合入,而第二篇说新贡献者的 PR 必须对应actionableissue。两者矛盾吗?第三篇的哪条机制解释了这个 PR 合入时真正被检查的是什么?答案
不矛盾。
CONTRIBUTING.md原文是 “as a new contributor”——规则针对新贡献者;#181999 由 linalg maintainer @IvanYashchuk 开,作者 @nikitaved 是长期贡献者,第四篇的判断是这类题通常被熟人接走,反而不是新人的好切入点。合入时trymerge.py检查的是第三篇讲的:merge_rules.yaml“Linear Algebra” 规则的approved_by(IvanYashchuk 在名单里)与mandatory_checks_name(EasyCLA、Lint、pull)、release notes: linalg_frontend标签——pull里两个 docs 失败让第一次 merge 被拒,-i忽略后合入。 -
两个 PR 都遇到了 CI 失败:#185344 的
pull里两个 docs 任务退出码 127,#47272 的 Buildkite 六个测试任务失败。用第三篇的判断表分别归类,再说说第二篇里 CI 失败作为”切入点”与这里的关系。答案
第三篇判断表:docs 任务与一个只改
.cpp的 PR 无关、Dr. CI 归为脚本错误 → “与改动无关”,处理是@pytorchbot merge -i忽略;六个 Buildkite 失败成对出现(mirror: amd)、失败测试都把 KV 池开在新边界上 → “main 绿我红且在改动附近 → 我的问题”,处理是修三个测试并再/ci run。第二篇:如果 docs 任务在 main 上也持续红,它本身就是一个 CI 失败切入点(HUD 与DISABLEDissue;Project 20 看板与450-ci-failure.yml)——分辨”我的”与”main 的”是同一套动作的两个用途。
C. 面试题(7 题)
-
给你一个从未接触过的百万行仓库和一个报错,两小时内怎么定位到要改的函数?以 PyTorch 为例说一遍。
答案
答案要点:(1) 从报错提取 2–3 个最具体的符号,字符串字面量优先,
rg -F;(2) 画顶层地图判断在哪一层——c10/→aten/→torch/csrc/→torch/,CONTRIBUTING.md“Codebase structure” 是自带地图;(3) 找登记表native_functions.yaml,沿dispatch:到native/*.cpp,再rg -l到cpu/、cuda/下的 kernel;(4) 头文件找不到、.pyi跳转断掉,知道是torchgen生成的,回 yaml;(5) 读测试与 OpInfo 当规格,git log -S看有没有人已经修了;(6) 构建放最后、可选。 追问方向:vLLM 怎么追(pyproject.toml→cli/main.py→api_server.py→v1/engine/);git blame为什么要-w -C;什么时候必须构建。 好答案与一般答案的区别:一般答案说”用 grep 和 IDE 跳转”;好答案先找登记表、知道哪些文件是生成的、把测试与历史也当阅读对象,并给出时间预算。 -
你要在 PyTorch 或 vLLM 做第一次贡献,怎么挑 issue?怎么判断它一周后不会被关?
答案
答案要点:(1) 读”想要什么”——PyTorch 只盯
actionable加 maintainer 的 “I’d review a PR that …”,vLLM 看help wanted加分步骤正文与 Job Board;(2) 读”不要什么”——单个 typo、纯格式、没 RFC 的大改、needs research的 issue;(3) 三条gh查重命令,看 open PR 数与最后一条评论日期,good first issue是最挤的池子(#191394 有 5 个 PR);(4) 估规模与 RFC 门槛(>500 LOC)、硬件;(5) 真正空着的位置在needs reproduction(535 个)复现成测试、skipped的 flaky test、测量类任务;(6) 留一条带信息的认领留言然后等。 追问方向:PyTorch 四态各能做什么;vLLM 标签谁打;什么改动要 RFC。 好答案与一般答案的区别:一般答案说”找 good first issue”;好答案说出 maintainer 承诺 review 的信号、查重的具体命令,并知道最挤的池子在哪。 -
reviewer 打开你的 PR 只有十分钟,他会看什么?你的 PR 每一部分替他回答了什么?
答案
答案要点:(1) 标题与标签——这是什么、该我看吗;(2) 描述——为什么改、讨论过吗(
Fixes #/ Purpose);(3) CI——会不会弄坏别的;(4) diff——改动对不对,一个 PR 一件事、≤2000 行、无顺手重构;(5) 测试——怎么证明对、怎么防回归(instantiate_device_type_tests;AGENTS.md四问;opcheck);(6) benchmark——值不值(六要素);(7) 风险与签名——BC-breaking?、EasyCLA / DCO、AI 声明。任何一格空着,PR 就回到队列末尾。 追问方向:PyTorch 把信息放元数据、vLLM 放正文,各自的后果;review 意见怎么回(改 / 争 / 问)。 好答案与一般答案的区别:一般答案列”写清描述、加测试”;好答案按分钟对应到 reviewer 的问题,并说出两个项目各用什么机制保证每一格有东西。 -
PyTorch 与 vLLM 的 CI 有什么本质差别?”CI 是绿的”在两边分别意味着什么?
答案
答案要点:(1) PyTorch 推送即跑、分层触发:
pull+Lint必跑,其余靠ciflow/*标签(49 个,需写权限);(2) vLLM 默认不跑、按需触发:PR 上只有 pre-commit 且需verified/ready或 ≥4 合入 PR,测试任务要/ci run,新 commit 不自动重跑;(3) vLLM 按source_file_dependencies选任务,run_all_patterns(csrc/等)例外全跑;(4) 所以 PyTorch 的绿可能还没跑 trunk,vLLM 的灰不是绿;(5) 红了先看 main。 追问方向:ciflow/trunk的实现(bot 打 git tag);”Which commit is used in CI?”;mirror: amd为什么失败成对。 好答案与一般答案的区别:一般答案说”一个用 GitHub Actions 一个用 Buildkite”;好答案说出两种哲学(机器换时间 vs 人的判断换机器)与对新贡献者的具体后果。 -
你用 AI 助手写了一个 PR,两个项目分别要求你怎么做?政策的边界在哪里?
答案
答案要点:(1) 两家都不禁止用 AI,都要求人读懂每一行、能为每一行辩护;(2) PyTorch
AI_POLICY.md五条:AI 内容包起来并加人的评注、不贴未审阅的 AI 文本、读懂再提、未完成用 draft、不接受全自主 agent;CONTRIBUTING.md加新贡献者需actionableissue、新功能 issue 里不放 AI 方案;(3) vLLMAGENTS.md:查重三命令、无 busywork、”Pure code-agent PRs are not allowed”、描述四项必填、Co-authored-bytrailer、违规封禁;(4) 边界的例子:#185344 reviewer 要求删掉代码注释里的 “Authored with Claude”,描述里的同一句保留进了 commit——声明放描述,代码里只放对维护者有用的东西;#47272 是合规样本。 追问方向:为什么 PyTorch 更在意讨论质量、vLLM 更在意 PR 数量;closed-as-slop97 个说明什么。 好答案与一般答案的区别:一般答案说”声明一下用了 AI”;好答案分清两家各自的痛点与条目,并能用真实 PR 说出边界在哪一行。 -
你的 PR 三周没人理,或者被拒了,怎么办?
答案
答案要点:(1) 对照承诺:PyTorch 4 个工作日无回应留言 @ reviewer、再去 Office Hours;vLLM 7 天可 ping、
pr-review-request@vllm.ai加急;#47272 的 47 天沉默是可省的大半;(2) 自查:标题前缀、只请求一两位路径匹配的 reviewer、needs-rebase、lint / DCO;(3) 很多”没人理”根源在选题——回第二篇看 issue 是否actionable、是否已有人在做;(4) 被拒分三类:方向不对(拒绝里没有关于代码的具体意见)→ 放弃回 issue / RFC;时机不对 → 保留分支等;做法不对 → 改,这是唯一该”改了再提”的;(5) 放弃时把 maintainer 在意什么记进日志。 追问方向:怎么回应 nit 与设计异议;force-push 与 ghstack;同一件事别人先合了怎么办。 好答案与一般答案的区别:一般答案说”礼貌地催一下”;好答案给出两个项目的时点与升级通道、先自查 PR 与选题、并能分类拒绝。 -
你的团队维护一份 vLLM 内部 fork,每次升级都重新打补丁。怎么规划把改动推回上游?
答案
答案要点:(1) 先建项目地图(第一篇):
csrc/→vllm/、tests/与子系统的对应、VLLM_USE_PRECOMPILED=1免编译;(2) 把补丁按”一件事”切开,逐个查重,>500 行架构改动先开[RFC]:issue;每人 6 个 open PR 上限决定并行度(第二、三篇);(3) 每个 PR 按 Purpose / Test Plan / Test Result 写、带前缀、每个 commit-s、本地pre-commit run --all-files;改csrc/触发全量 CI;(4) 时间线用第四篇校准:CI 等ready+/ci run,7 天没 review 就 ping,合入后看git tag --contains;(5) 每个 PR 按复盘模板记时间去了哪;(6) 与 maintainer 预期一致的改动(roadmap 未勾选项、help wanted)优先,纯内部需求考虑 plugin 而不是硬推。 追问方向:怎么判断某个补丁上游不会要;deprecation 三阶段怎么走。 好答案与一般答案的区别:一般答案说”一个个提 PR”;好答案把四篇的方法排成有顺序、有数字预期的计划,并知道哪些补丁不该推。
D. 掌握判据
| 水平 | 表现 |
|---|---|
| 读过 | 能说出四篇各讲什么;知道 native_functions.yaml、actionable、ghstack、/ci run、ready、DCO 这些名词 |
| 掌握 | A 组能不翻书答出 8 题以上;B 组能说出每题用了哪几篇的什么;拿到一个陌生项目的 CONTRIBUTING.md 能在半小时内说出它更像 PyTorch 还是 vLLM、差在哪几格;能用七阶段走读一个自己没参与的 PR |
| 能教人 | C 组每题能给出全部要点并预判追问;能解释四篇里每个反直觉结论为什么成立(构建放最后、good first issue 最挤、vLLM 的灰不是绿、合入了不一定在最新版、review 意见全是注释) |
通关标准:A 组至少 8 题、B 组至少 4 题、C 组每题能说出一半以上要点。没过的部分回到第二章对应篇的”必记”,再回该篇正文;如果卡在 B 组,说明各篇读懂了但没连起来,重读第三章的四条线。
六、下一步
四篇讨论的只是如何参与一个 AI-Infra 开源项目:阅读方法、选题方法、提交规范、协作规则。总纲划出的边界之外,几个方向紧邻但不在范围内:
- 任何一层的技术原理——算子怎么分发、kernel 怎么写、调度器怎么工作、通信怎么走。这些内容在 AI-Infra 各层的技术系列里,入口是《AI-Infra 工程师学习地图》——本系列在那张地图上是横切的第 12 个系列,”任何阶段,贡献者路径”。
- PyTorch 与 vLLM 之外项目的具体流程——NCCL、Megatron-LM、FlashAttention、Triton、SGLang 在正文中只做定性对照;它们的规则以各自仓库的
CONTRIBUTING.md为准,本系列教的是如何在半小时内读懂它们。
还没决定走哪条线的读者,先看《AI 全栈学习地图》,它把本系列放在”跑模型”一段的末尾,作为贡献者路径的收尾。
回到总纲:《AI-Infra 开源贡献指南(总纲)》。
七、延伸阅读
本系列只讨论如何参与一个 AI-Infra 开源项目:阅读方法、选题方法、提交规范、协作规则。以下内容与它紧邻,但不在范围内:
- 任何一层的技术原理:算子怎么分发、kernel 怎么写、调度器怎么工作、通信怎么走。第四篇走读 PR 时会解释那两个改动本身在做什么,但只到读懂这个 diff 所需的程度,不展开背后的机制。
- git 与 GitHub 的基本操作:fork、branch、rebase、解决冲突、开 PR。假设读者作为工程师已经熟练;本系列只讲这些操作在两个项目里的特殊约定(如
ghstack、Signed-off-by)。 - 开源许可、CLA/DCO 的法律含义:只说明两个项目各自要求什么,不讨论为什么。
- 成为 maintainer 之后的工作:triage、release management、governance。PyTorch 的
docs/source/community/governance.md描述了 module maintainer 与 core maintainer 的机制,本系列只把它作为”谁有权批准我的 PR”的背景来读。 - PyTorch 与 vLLM 之外项目的具体流程:NCCL、Megatron-LM、FlashAttention、Triton、SGLang 在正文中只做定性提及;它们的贡献规则请以各自仓库的文档为准。
-
四个:给一个报错能不能两小时定位到函数、靠什么(登记表、生成代码、测试即规格、历史即注释);maintainer 最想要哪类工作、怎么判断题一周后不被关(
actionable/help wanted、maintainer 最后一条评论、open PR 数、RFC 门槛、硬件、政策);reviewer 的十分钟要确认什么、diff / 描述 / 测试 / CI 各答哪个问题;小 PR 的时间花在哪、哪些可省哪些是正常成本(数据是正常成本,等待大半可省)。详见第二章。 ↩ -
PyTorch 分层
c10/→aten/→torch/csrc/→torch/与登记表native_functions.yaml;vLLMcsrc/→vllm/与pyproject.toml/torch_bindings.cpp;发布约 2 个月 vs 约 2 周;PyTorch 682 个标签、actionable396 占不到 3%、状态链四态;vLLM 63 个标签、RFC 门槛 >500 LOC 不含 kernel / data / config / test、stale 90 + 30 天;三条查重命令;PyTorch 2000 行硬上限、61 个 linter、148 个 workflow、49 个ciflow/*、33 条 merge rule、4 个工作日可催;vLLM 6 个 open PR 上限、35 个 test_area、pre-commit 需verified/ready或 ≥4 合入 PR、/ci run七步授权、2–3 天 / 7 天、DCO 每个 commit;#185344 +104 −0、27 天 3792 个点、3 小时 42 分收到 review、merge -i、进 v2.13.0;#47272 47 天无 review、6 个自己引入的失败、不在 v0.28.0。详见第一章、第三章。 ↩ -
用第五章的三段自测:A 组 10 题判断与计算(至少 8 题)、B 组 5 题跨篇综合(至少 4 题)、C 组 7 道面试题(每题说出一半以上要点);D 组的表给出”读过 / 掌握 / 能教人”三级的表现。详见第五章。 ↩
本文由 arganzheng 创作,采用 CC BY 4.0 许可协议。在保留原文作者、署名以及完整原文链接(https://arganzheng.life/contributing-to-ai-infra-open-source-series-recap-and-self-test.html)的前提下,欢迎各种形式的转载、翻译或商业引用。
COMMENTS
评论存放在 GitHub Discussions, 用 GitHub 账号登录即可发表,支持 Markdown。 想针对正文某句话说?选中那段文字,点浮出的「评论」即可划线评论;觉得哪里写错了,发表时勾上「同时提交 Issue」。 有人回复你时 GitHub 会按你的通知设置发邮件,不用守在这里。