开源项目正被一波低质量 PR 淹没——背后是 LLM 批量生成的代码提交。Rust 官方仓库没有选择沉默,而是发布了一份明确的 LLM 使用政策文件,把"能用"和"不能用"的边界画了出来。这份政策不是几个人拍脑袋决定的,它建立在 Zulip 上超过 3000 条消息的社区讨论之上。
垃圾 PR 的真实痛点
LLM 让写代码变快了,但也让"制造 PR"变得极低成本。Rust 仓库维护者面对的现实是:大量提交看起来格式正确、语法无误,但逻辑上完全不通——模型生成的代码缺少对项目上下文的理解,reviewer 的时间被白白消耗在甄别这些"看起来像人写的"提交上。
这不是个别项目的问题。任何有一定知名度的开源仓库,最近一两年都经历了类似冲击。核心矛盾在于:LLM 产出代码的速度远超人类 review 的速度,而低质量提交的筛选成本全部落在了维护者身上。
政策划定的两条线
Rust 的政策文件把 LLM 使用分成明确的两类:
允许的使用——贡献者可以用 LLM 来辅助理解代码、学习概念、探索思路,比如用模型解释一段你不熟悉的模块逻辑,或者让它帮你梳理某个 API 的设计意图。这些场景下,LLM 是工具,最终的理解和判断仍然由人完成。
禁止的使用——直接把 LLM 生成的代码、文档或评论作为提交内容发到仓库,而不经过人类的实质性审查和验证。换句话说,模型输出不能替代你的专业判断。如果你自己无法解释提交中的每一行代码为什么这样写,这个提交就不该出现在仓库里。
这条红线的关键不在"用了 LLM"这个动作本身,而在"人是否真正承担了责任"。政策的核心逻辑是:贡献者必须对自己提交的内容具备完全的理解和辩护能力。
对贡献者的实际影响
政策落地后,贡献者的工作方式需要调整:
- 用 LLM 学习和探索没问题,但提交前你必须逐行验证生成内容,确保自己能回答 reviewer 关于每一处细节的提问。
- 不能用 LLM 批量生成 PR 来"刷贡献量"。即使每个 PR 单独看质量尚可,如果提交者无法深入讨论其中的设计决策,仍然会被视为违规。
- 文档和评论同样适用——LLM 生成的文档段落如果贡献者自己读不懂、无法解释为什么这样表述,就不该提交。
团队特别强调:这份政策建立在大量社区讨论之上,后续讨论应聚焦政策文本本身,而不是重新争论基本原则。
实践:为你的项目配置 LLM 提交筛查
Rust 的做法可以迁移到你自己的项目。下面是一个 GitHub Actions 工作流示例,用于检测可能未经人工审查的 LLM 批量提交,配合仓库级别的贡献政策一起使用。
name: llm-pr-screening
on:
pull_request:
types: [opened, edited]
jobs:
check-pr-quality:
runs-on: ubuntu-latest
steps:
- name: 检查 PR 描述与变更比例
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const bodyLength = (pr.body || '').trim().length;
// 获取变更文件数
const { data: files } = await github.rest.pulls.listFiles({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number
});
const changedFiles = files.length;
const additions = files.reduce((sum, f) => sum + f.additions, 0);
// 信号1:大量文件变更但描述极短——常见于批量生成提交
if (changedFiles > 15 && bodyLength < 50) {
core.setFailed(
`PR 变更了 ${changedFiles} 个文件但描述仅 ${bodyLength} 字符。` +
`请补充详细说明,确保你对每处变更都能解释原因。`
);
}
// 信号2:单次新增行数异常多——可能是模型批量输出
if (additions > 500 && bodyLength < 100) {
core.setFailed(
`PR 新增 ${additions} 行但描述不足。` +
`大规模新增需要逐行审查说明。`
);
}
- name: 检查提交者近期 PR 频率
uses: actions/github-script@v7
with:
script: |
const pr = context.payload.pull_request;
const author = pr.user.login;
// 查询该作者最近7天的PR数量
const oneWeekAgo = new Date(Date.now() - 7 * 24 * 3600 * 1000).toISOString();
const { data: recentPRs } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'all',
sort: 'created',
direction: 'desc',
per_page: 50
});
const authorPRs = recentPRs.filter(
p => p.user.login === author && new Date(p.created_at) >= new Date(oneWeekAgo)
);
if (authorPRs.length >= 5) {
console.log(`警告:${author} 最近7天开了 ${authorPRs.length} 个PR,请关注质量`);
// 不直接 fail,但留下日志供维护者审查
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: `⚠️ @${author} 最近一周在本仓库提交了 ${authorPRs.length} 个PR。` +
`\n请维护者确认这些提交均经过人工深度审查,符合项目LLM使用政策。`
});
}
使用前需要修改的地方:
changedFiles > 15和additions > 500这两个阈值,根据你的项目规模调整。小项目可以更严格,大项目可能需要放宽。- 频率检查的
>= 5同样需要因地制宜。 - 这套检测只捕捉"异常信号",不判断内容本身的质量——人工 review 仍然不可替代。
给维护者的落地建议
如果你也在维护一个被 LLM 提交冲击的开源项目,Rust 的做法提供了几条可参考的思路:
- 先写政策再执法——没有明确规则的情况下直接拒绝提交,容易引发社区对立。Rust 花了大量时间在 Zulip 上讨论,最终形成的政策才有公信力。
- 聚焦"人的责任"而非"工具本身"——禁止的不是 LLM,而是"人不对提交内容负责"的行为。这个表述比"禁止使用 AI"更精确,也更难被绕过。
- 用自动化辅助筛查,但不替代判断——上面的 GitHub Actions 只标记可疑模式,最终决定权在维护者手里。
- 把政策写进 CONTRIBUTING.md——让每个新贡献者在提交前就能看到规则,减少事后争议。
Rust 这份政策的核心立场很清晰:LLM 是工具,贡献者是人,人必须为自己的提交担责。这个原则放到任何项目都成立,区别只在于你打算用多大的力度去执行它。