GitHub 官方在庆祝 AI 为开源生态带来的"巨大贡献",但贡献质量的断崖式下跌却被刻意忽略。PR 数量暴涨、Issue 泛滥、README 被机器翻译填充——开源维护者正在承受一场没有硝烟的垃圾洪流。而大多数科技公司仍在歌颂 AI 辅助编程的生产力神话。
今年早些时候,一个获得 VC 支持的 AI 原生项目 Archestra 选择站出来说真话:AI 垃圾正在系统性地毁掉开源生态。他们在一个 GitHub Issue 上挂出 900 美元悬赏,不是为了吸引更多贡献,而是为了找到一种方式把垃圾挡在门外。
垃圾长什么样
AI 生成的低质量贡献有几种典型形态,维护者一眼就能辨认:
模板化 PR——标题格式统一、描述空洞、改动仅涉及文档措辞或注释重写,没有实质性逻辑变更。十个这样的 PR 可能来自同一个账号的批量脚本。
翻译填充——README 或 docs 被机器翻译成多语言版本,语法生硬、术语错乱,反而降低了文档的可读性。
Issue 洪水——大量格式完美但内容空洞的 Issue,描述问题却不提供复现步骤,提建议却不考虑实现路径。AI 写得漂亮,但毫无可操作性。
代码"优化"——把 for i in range(len(arr)) 改成 for i, v in enumerate(arr),把 if x == True 改成 if x——语法上正确,语义上零价值,却消耗维护者的审查时间。
这些贡献的共同特征:形式合规,实质空洞。它们通过了 CI,通过了 linter,甚至通过了某些自动化审查,但给项目带来的是净负值。
Archestra 的白名单思路
Archestra 的做法不是封禁 AI,而是建立一道筛选门槛:白名单机制。只有经过验证的贡献者——无论是人类还是被明确授权的 AI 辅助流程——才能提交实质性改动。其余贡献进入更严格的审查队列,甚至直接标记为需要人工复核。
这背后的逻辑很清晰:开源项目的核心资源不是代码量,而是维护者的注意力。每一个垃圾 PR 都在偷走这份注意力。白名单不是歧视,是保护。
在你的项目中部署一道防线
下面是一个可以直接用在你的 GitHub 项目中的防线方案,包含两层:一个 Python 脚本检测常见 AI 垃圾模式,以及一个 GitHub Actions 工作流在 PR 提交时自动运行检测。
AI 垃圾模式检测脚本
#!/usr/bin/env python3
"""ai_spam_detector.py — 检测 PR 中的常见 AI 垃圾模式"""
import re
import sys
import json
from pathlib import Path
# 白名单贡献者(GitHub 用户名)
WHITELIST = [
"your-trusted-contributor-1",
"your-trusted-contributor-2",
# 从项目 CODEOWNERS 或长期贡献者列表中提取
]
# AI 垃圾的典型信号
PATTERNS = {
"hollow_pr_title": r"^(Update|Improve|Refactor|Fix|Enhance|Optimize)\s+(README|docs|comments|typo)",
"trivial_style_change": r"^\s*(if\s+\w+\s*==\s*True|for\s+\w+\s+in\s+range\(len\(",
"ai_generated_disclaimer": r"(This (PR|change) was (generated|created|assisted) by (AI|Copilot|ChatGPT|GPT))",
"generic_issue_template": r"(I noticed|It seems|This appears to be)\s+(a|an)\s+(issue|bug|problem)",
}
def load_pr_data(pr_json_path: str) -> dict:
"""从 GitHub API 输出或事件 payload 加载 PR 数据"""
with open(pr_json_path) as f:
return json.load(f)
def detect_spam_signals(pr: dict, changed_files: list[str]) -> list[str]:
"""返回检测到的垃圾信号列表"""
signals = []
# 白名单贡献者直接放行
author = pr.get("user", {}).get("login", "")
if author in WHITELIST:
return signals # 空列表 = 无垃圾信号
# 检查 PR 标题
title = pr.get("title", "")
for name, pattern in PATTERNS.items():
if re.search(pattern, title, re.IGNORECASE):
signals.append(f"标题命中模式: {name}")
# 检查 PR 正文
body = pr.get("body", "") or ""
for name, pattern in PATTERNS.items():
if re.search(pattern, body, re.IGNORECASE):
signals.append(f"正文命中模式: {name}")
# 检查改动文件——全是文档/注释类改动是典型信号
code_files = [f for f in changed_files
if not f.endswith((".md", ".txt", ".rst", ".po"))]
if len(changed_files) > 3 and len(code_files) == 0:
signals.append("所有改动文件均为文档类型,无代码变更")
# 检查改动行数——大量微小改动是批量脚本特征
additions = pr.get("additions", 0)
deletions = pr.get("deletions", 0)
if additions > 50 and deletions > 50 and abs(additions - deletions) < 5:
signals.append("增删行数几乎对称,疑似批量替换式改动")
return signals
def main():
pr_path = sys.argv[1] if len(sys.argv) > 1 else "pr_data.json"
files_path = sys.argv[2] if len(sys.argv) > 2 else "changed_files.json"
pr = load_pr_data(pr_path)
with open(files_path) as f:
changed_files = json.load(f)
signals = detect_spam_signals(pr, changed_files)
if signals:
print("⚠️ 检测到 AI 垃圾信号:")
for s in signals:
print(f" - {s}")
print("\n建议: 标记 PR 为 needs-human-review,通知维护者人工复核。")
sys.exit(1) # 非0退出码,CI 可据此判断
else:
print("✅ 未检测到明显垃圾信号,进入正常审查流程。")
sys.exit(0)
if __name__ == "__main__":
main()
运行方式:
# 先用 GitHub CLI 获取 PR 数据
gh pr view 42 --json title,body,user,additions,deletions > pr_data.json
# 获取改动文件列表
gh pr diff 42 --name-only > changed_files.json
# 运行检测
python3 ai_spam_detector.py pr_data.json changed_files.json
GitHub Actions 自动化防线
# .github/workflows/ai-spam-guard.yml
name: AI Spam Guard
on:
pull_request_target:
types: [opened, edited, synchronize]
jobs:
detect:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Fetch PR metadata
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr view ${{ github.event.pull_request.number }} \
--repo ${{ github.repository }} \
--json title,body,user,additions,deletions \
> pr_data.json
gh pr diff ${{ github.event.pull_request.number }} \
--repo ${{ github.repository }} \
--name-only \
| python3 -c "import sys,json; print(json.dumps(sys.stdin.read().strip().split('\n')))" \
> changed_files.json
- name: Run spam detection
run: python3 ai_spam_detector.py pr_data.json changed_files.json
- name: Label suspicious PRs
if: failure()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh pr edit ${{ github.event.pull_request.number }} \
--repo ${{ github.repository }} \
--add-label "needs-human-review"
gh pr comment ${{ github.event.pull_request.number }} \
--repo ${{ github.repository }} \
--body "⚠️ 此 PR 被自动检测标记为疑似 AI 垃圾贡献,需维护者人工复核后再决定是否合并。"
使用前需要修改的地方:
WHITELIST列表——填入你项目的可信贡献者 GitHub 用户名,可以从CODEOWNERS文件或长期贡献者统计中提取。PATTERNS规则——根据你项目遇到的实际垃圾模式调整正则表达式。- GitHub Actions 中
pull_request_target事件——注意这是从 fork 仓库触发时也能读取仓库 secrets 的安全写法;如果你不需要 fork PR,可以换成pull_request。
白名单的代价与边界
白名单机制不是万能药,它有自己的代价:
冷启动问题——新贡献者被默认归入"可疑"队列,可能挫伤真正想参与的人。缓解办法:为新贡献者设置一条快速验证通道,比如要求他们在一个专门的"入门 Issue"上先完成一次小改动,通过人工确认后加入白名单。
AI 不等于垃圾——有些 AI 辅助的贡献质量很高。白名单不应排斥 AI 工具本身,而是排斥未经审查的批量低质量输出。可以在白名单中标注"AI 辅助但经人工审核"的贡献者。
维护成本——白名单需要持续更新,垃圾模式需要持续识别。这不是一次性工程,而是持续运营。
误判风险——正则匹配永远有漏报和误报。脚本检测只是第一道筛,最终决策权必须在维护者手中。
一份开源维护者的自检清单
如果你正在维护一个活跃的开源项目,以下清单可以帮助你评估是否需要部署类似防线:
- ☐ 过去 30 天内,你花在审查低质量 PR 上的时间是否超过总审查时间的 30%?
- ☐ 是否有账号在短时间内提交了大量格式相似但内容空洞的 PR?
- ☐ 你的 Issue 列表中是否有大量"描述完美但无法复现"的报告?
- ☐ 你的文档是否被不熟悉的语言翻译填充,且翻译质量明显低于原文?
- ☐ 你是否有明确的贡献者准入流程(CODEOWNERS、贡献指南、入门 Issue)?
- ☐ 你的 CI 中是否有任何针对贡献质量的检测步骤?
如果前三项中有任何一项为"是",你的项目正在被 AI 垃圾侵蚀。如果后三项中有任何一项为"否",你的项目缺少基础防线。
Archestra 的 900 美元悬赏不是终点,而是一个信号:开源社区需要正视这个问题,而不是继续假装一切正常。白名单只是手段之一,但核心原则只有一个——保护维护者的注意力,比增加代码量更重要。