欢迎光临
我们一直在努力

别只让 GPT 写代码:用 Python 从 git diff 自动生成 PR Review Checklist

现在很多开发者已经不满足于让 GPT 写一个函数、解释一段报错了。

更常见的场景是:

  • 让 GPT 看一段旧代码;
  • 让 GPT 根据需求生成 patch;
  • 让 GPT 补测试;
  • 让 GPT 写 PR 描述;
  • 让 GPT 根据 diff 总结变更影响;
  • 让 GPT 帮忙提醒 Review 重点。

这确实比以前高效。

但也带来一个新问题:

AI 生成的代码,最后还是要进入 PR、Review、测试和合并流程。**

如果只停留在“GPT 帮我写了代码”,那还不算真正进入工程流。真正关键的是:

这段代码改了哪些文件?
有没有测试?
有没有改到配置?
有没有涉及鉴权、支付、订单、删除数据?
有没有对应文档?
Review 人应该重点看哪里?

这些问题不解决,AI 写得再快,也可能只是把风险更快地推到 PR 里。

这篇文章不讨论“怎么让 GPT 写更多代码”,而是写一个轻量级 Python 工具:

输入 git diff,输出 PR Review Checklist。**

目标是把 AI 生成代码后的检查动作标准化,避免开发者只看代码能不能跑,而忽略 Review 边界。


一、为什么需要从 diff 生成 Review Checklist?

很多团队使用 AI 编程工具时,会出现一个很典型的流程:

  • 把需求给 GPT;
  • GPT 生成代码;
  • 开发者复制到项目里;
  • 本地跑一下;
  • 提交 PR;
  • Review 时再靠人工经验检查。
  • 这个流程看起来没问题,但实际有几个风险。

    第一,AI 生成代码经常只关注“完成需求”,不一定关注影响范围。

    比如一个需求是“修复登录态丢失问题”,GPT 可能会修改 session、cookie、middleware、auth helper 等多个位置。代码能跑,不代表影响范围清楚。

    第二,AI 可能漏测试。

    它给你 patch,但没有提醒你应该补哪些单元测试、集成测试或回归测试。

    第三,AI 可能动到高风险区域。

    比如 payment、auth、admin、migration、delete、permission 这些关键词,一旦出现在 diff 里,Review 重点就完全不一样。

    第四,PR 描述容易写得很漂亮,但 Review Checklist 很空。

    PR 描述说“优化登录逻辑”,但 Reviewer 真正需要知道的是:

    • 是否影响已有登录态;
    • 是否影响 refresh token;
    • 是否需要回归移动端;
    • 是否涉及权限边界;
    • 是否有兼容老数据。

    所以,与其让 GPT 只写 PR 描述,不如先让脚本从 diff 里提取结构化信息,再生成 Review Checklist。


    二、我们要实现什么?

    这个脚本先做一个最小可用版本。

    输入:一段 git diff 文本。
    输出:一个 Review Checklist,包含:

    • changed_files:变更文件列表;
    • risk_keywords:命中的风险关键词;
    • has_tests:是否包含测试文件;
    • has_docs:是否包含文档变更;
    • suggested_review_points:建议 Review 关注点;
    • suggested_test_points:建议测试关注点;
    • risk_level:整体风险等级。

    这个工具不是替代 Reviewer,而是帮助 Reviewer 更快定位重点。

    尤其在 AI 生成代码越来越多的情况下,Review 不应该只靠“看一遍感觉还行”。


    三、输入示例:一段 git diff

    假设我们有这样一段 diff:

    diff –git a/src/auth/session.py b/src/auth/session.py
    index 9f1a2aa..e31bc98 100644
    — a/src/auth/session.py
    +++ b/src/auth/session.py
    @@ -12,7 +12,10 @@ def refresh_session(user_id, token):
    – session = get_session(user_id)
    – session.token = token
    + session = get_session(user_id)
    + if not session:
    + session = create_session(user_id)
    + session.token = token
    session.save()
    return session

    diff –git a/src/payment/order.py b/src/payment/order.py
    index 4d11aac..a91f0bc 100644
    — a/src/payment/order.py
    +++ b/src/payment/order.py
    @@ -45,6 +45,9 @@ def update_order_status(order_id, status):
    order = get_order(order_id)
    + if status == "PAID":
    + order.paid_at = now()
    order.status = status
    order.save()
    return order

    这段 diff 看起来不长,但已经涉及两个高风险点:

    • auth;
    • payment。

    而且没有看到测试文件变化。

    如果这个 PR 是 AI 生成的,就更应该让 Reviewer 重点关注:

    • 登录态创建是否安全;
    • session 是否会重复创建;
    • 支付状态是否幂等;
    • PAID 状态是否可能被重复写入;
    • 是否需要补测试。

    四、先定义数据结构

    用 dataclass 定义 ReviewResult。

    from dataclasses import dataclass, asdict
    from typing import List, Dict

    @dataclass
    class ReviewResult:
    changed_files: List[str]
    risk_keywords: List[str]
    has_tests: bool
    has_docs: bool
    risk_level: str
    suggested_review_points: List[str]
    suggested_test_points: List[str]

    def to_dict(self) > Dict:
    return asdict(self)

    字段设计尽量简单,方便后续导出 JSON,或者接入 CI、PR Bot、内部工具。


    五、实现 diff 解析器

    下面写一个基础版本。

    import re
    from dataclasses import dataclass, asdict
    from typing import List, Dict, Set

    @dataclass
    class ReviewResult:
    changed_files: List[str]
    risk_keywords: List[str]
    has_tests: bool
    has_docs: bool
    risk_level: str
    suggested_review_points: List[str]
    suggested_test_points: List[str]

    def to_dict(self) > Dict:
    return asdict(self)

    class DiffReviewChecklistGenerator:
    def __init__(self):
    self.risk_keyword_map = {
    "auth": ["auth", "login", "session", "token", "permission", "role"],
    "payment": ["payment", "order", "paid", "refund", "transaction"],
    "database": ["migration", "schema", "delete", "drop", "truncate"],
    "config": ["config", "env", "secret", "key"],
    "admin": ["admin", "root", "superuser"],
    }

    self.test_patterns = [
    "test_", "_test", "/tests/", "spec.", ".spec", ".test"
    ]

    self.doc_patterns = [
    ".md", "docs/", "README"
    ]

    def generate(self, diff_text: str) > ReviewResult:
    changed_files = self._extract_changed_files(diff_text)
    risk_keywords = self._detect_risk_keywords(diff_text, changed_files)
    has_tests = self._has_tests(changed_files)
    has_docs = self._has_docs(changed_files)

    risk_level = self._calculate_risk_level(
    risk_keywords=risk_keywords,
    has_tests=has_tests,
    changed_files=changed_files,
    )

    suggested_review_points = self._suggest_review_points(
    risk_keywords=risk_keywords,
    has_tests=has_tests,
    has_docs=has_docs,
    )

    suggested_test_points = self._suggest_test_points(
    risk_keywords=risk_keywords,
    has_tests=has_tests,
    )

    return ReviewResult(
    changed_files=changed_files,
    risk_keywords=sorted(risk_keywords),
    has_tests=has_tests,
    has_docs=has_docs,
    risk_level=risk_level,
    suggested_review_points=suggested_review_points,
    suggested_test_points=suggested_test_points,
    )

    def _extract_changed_files(self, diff_text: str) > List[str]:
    files = re.findall(r"diff –git a/(.*?) b/(.*?)\\n", diff_text)
    return sorted({target for _, target in files})

    def _detect_risk_keywords(self, diff_text: str, changed_files: List[str]) > Set[str]:
    text = " ".join(changed_files) + "\\n" + diff_text
    text = text.lower()

    detected = set()

    for category, keywords in self.risk_keyword_map.items():
    for keyword in keywords:
    if keyword.lower() in text:
    detected.add(category)
    break

    return detected

    def _has_tests(self, changed_files: List[str]) > bool:
    joined = "\\n".join(changed_files).lower()
    return any(pattern.lower() in joined for pattern in self.test_patterns)

    def _has_docs(self, changed_files: List[str]) > bool:
    joined = "\\n".join(changed_files)
    return any(pattern in joined for pattern in self.doc_patterns)

    def _calculate_risk_level(
    self,
    risk_keywords: Set[str],
    has_tests: bool,
    changed_files: List[str],
    ) > str:
    score = 0

    if "payment" in risk_keywords:
    score += 4

    if "auth" in risk_keywords:
    score += 3

    if "database" in risk_keywords:
    score += 4

    if "config" in risk_keywords:
    score += 2

    if "admin" in risk_keywords:
    score += 3

    if len(changed_files) >= 5:
    score += 2

    if risk_keywords and not has_tests:
    score += 2

    if score >= 7:
    return "high"

    if score >= 4:
    return "medium"

    return "low"

    def _suggest_review_points(
    self,
    risk_keywords: Set[str],
    has_tests: bool,
    has_docs: bool,
    ) > List[str]:
    points = []

    if "auth" in risk_keywords:
    points.append("重点检查登录态、权限边界、token 生命周期和异常分支。")

    if "payment" in risk_keywords:
    points.append("重点检查订单状态流转、支付幂等性、重复回调和异常回滚。")

    if "database" in risk_keywords:
    points.append("重点检查数据库迁移、删除操作、兼容性和回滚方案。")

    if "config" in risk_keywords:
    points.append("重点检查配置变更是否影响不同环境,避免泄露密钥。")

    if "admin" in risk_keywords:
    points.append("重点检查管理员权限、角色边界和越权风险。")

    if not has_tests:
    points.append("当前 diff 未检测到测试文件变更,建议 Reviewer 重点确认测试覆盖。")

    if not has_docs:
    points.append("当前 diff 未检测到文档变更,如接口或行为变化明显,建议补充说明。")

    if not points:
    points.append("未检测到明显高风险关键词,仍需按常规流程进行代码审查。")

    return points

    def _suggest_test_points(
    self,
    risk_keywords: Set[str],
    has_tests: bool,
    ) > List[str]:
    points = []

    if "auth" in risk_keywords:
    points.extend([
    "补充登录态不存在时的测试。",
    "补充 token 刷新失败时的测试。",
    "补充并发刷新 session 的测试。",
    ])

    if "payment" in risk_keywords:
    points.extend([
    "补充重复支付回调测试。",
    "补充订单状态从未支付到已支付的测试。",
    "补充支付状态重复写入的幂等性测试。",
    ])

    if "database" in risk_keywords:
    points.extend([
    "补充迁移前后数据兼容测试。",
    "补充回滚路径测试。",
    ])

    if not has_tests:
    points.append("至少补充一组与本次变更直接相关的单元测试或回归测试。")

    if not points:
    points.append("根据业务影响范围补充必要的回归测试。")

    return points

    这个脚本做的事情比较明确:

    • 从 diff 中提取变更文件;
    • 根据文件名和 diff 内容识别风险类别;
    • 判断是否有测试文件;
    • 判断是否有文档变更;
    • 根据风险类别生成 Review 建议;
    • 根据风险类别生成测试建议。

    它不追求完全智能,但足够做一层基础过滤。


    请添加图片描述

    六、运行示例

    把前面的 diff 输入进去:

    diff_text = """
    diff –git a/src/auth/session.py b/src/auth/session.py
    index 9f1a2aa..e31bc98 100644
    — a/src/auth/session.py
    +++ b/src/auth/session.py
    @@ -12,7 +12,10 @@ def refresh_session(user_id, token):
    – session = get_session(user_id)
    – session.token = token
    + session = get_session(user_id)
    + if not session:
    + session = create_session(user_id)
    + session.token = token
    session.save()
    return session

    diff –git a/src/payment/order.py b/src/payment/order.py
    index 4d11aac..a91f0bc 100644
    — a/src/payment/order.py
    +++ b/src/payment/order.py
    @@ -45,6 +45,9 @@ def update_order_status(order_id, status):
    order = get_order(order_id)
    + if status == "PAID":
    + order.paid_at = now()
    order.status = status
    order.save()
    return order
    """

    generator = DiffReviewChecklistGenerator()
    result = generator.generate(diff_text)

    print(result.to_dict())

    输出示例:

    {
    'changed_files': [
    'src/auth/session.py',
    'src/payment/order.py'
    ],
    'risk_keywords': [
    'auth',
    'payment'
    ],
    'has_tests': False,
    'has_docs': False,
    'risk_level': 'high',
    'suggested_review_points': [
    '重点检查登录态、权限边界、token 生命周期和异常分支。',
    '重点检查订单状态流转、支付幂等性、重复回调和异常回滚。',
    '当前 diff 未检测到测试文件变更,建议 Reviewer 重点确认测试覆盖。',
    '当前 diff 未检测到文档变更,如接口或行为变化明显,建议补充说明。'
    ],
    'suggested_test_points': [
    '补充登录态不存在时的测试。',
    '补充 token 刷新失败时的测试。',
    '补充并发刷新 session 的测试。',
    '补充重复支付回调测试。',
    '补充订单状态从未支付到已支付的测试。',
    '补充支付状态重复写入的幂等性测试。',
    '至少补充一组与本次变更直接相关的单元测试或回归测试。'
    ]
    }

    这个输出比“请帮我写一个 PR 描述”更有价值。

    因为它直接告诉 Reviewer:

    • 这次变更涉及 auth 和 payment;
    • 风险等级是 high;
    • 没有测试文件;
    • 没有文档变更;
    • Review 应重点看登录态和支付状态;
    • 测试至少要补哪些场景。

    这才是 AI 生成代码进入工程流程时应该补上的一环。


    七、生成 PR Review Prompt

    有了结构化结果之后,还可以把它转成 Prompt,让 GPT 帮忙写 PR Review Checklist。

    def build_review_prompt(result: ReviewResult) > str:
    review_points = "\\n".join(
    f"- {point}" for point in result.suggested_review_points
    )

    test_points = "\\n".join(
    f"- {point}" for point in result.suggested_test_points
    )

    files = "\\n".join(f"- {file}" for file in result.changed_files)

    return f"""
    你是一个代码审查助手。

    请根据下面的 diff 分析结果,生成 PR Review Checklist。

    要求:
    1. 不要直接判断代码可以合并。
    2. 必须区分 Review 重点和测试重点。
    3. 如果 risk_level 是 high,必须提醒需要人工重点复核。
    4. 不要使用“保证没问题”“可以直接上线”等表达。
    5. 输出格式使用 Markdown Checklist。

    变更文件:
    {files}

    风险类别:
    {", ".join(result.risk_keywords) or "无明显风险类别"}

    风险等级:
    {result.risk_level}

    Review 关注点:
    {review_points}

    测试关注点:
    {test_points}
    """

    调用:

    prompt = build_review_prompt(result)
    print(prompt)

    输出的 Prompt 可以直接交给 GPT,让它生成更规范的 PR Review 文本。

    注意这里的分工:

    • Python 负责结构化 diff;
    • GPT 负责把结构化信息变成可读 Checklist;
    • Reviewer 负责最终判断。

    这样比直接把 diff 丢给 GPT 更稳。


    八、Prompt 输出示例

    GPT 最终可以生成类似这样的 Checklist:

    ## PR Review Checklist

    ### 变更范围
    – [ ] 确认 `src/auth/session.py` 中 session 不存在时自动创建的逻辑符合预期。
    – [ ] 确认 `src/payment/order.py` 中 `paid_at` 写入不会破坏原有订单状态流转。

    ### 权限与登录态
    – [ ] 检查 session 重复创建或并发刷新时是否会产生脏数据。
    – [ ] 检查 token 生命周期是否受到影响。
    – [ ] 检查异常分支是否有明确处理。

    ### 支付与订单
    – [ ] 检查支付回调重复触发时是否具备幂等性。
    – [ ] 检查订单状态重复写入是否会影响后续流程。
    – [ ] 检查异常回滚路径是否明确。

    ### 测试要求
    – [ ] 补充登录态不存在时的测试。
    – [ ] 补充 token 刷新失败时的测试。
    – [ ] 补充重复支付回调测试。
    – [ ] 补充订单状态幂等性测试。

    ### 人工复核
    – [ ] 当前变更风险等级为 high,需要人工重点复核后再考虑合并。

    这就是比较理想的 AI 辅助方式。

    不是让 GPT 直接告诉你“能不能合并”,而是让它帮你把 Review 注意事项列清楚。


    九、传统 Review vs GPT 辅助 Review
    请添加图片描述
    这张表的核心不是“让 GPT 替代 Review”。

    而是:

    让 GPT 帮忙整理 Review 材料,让 Reviewer 更快看重点。


    十、进一步扩展:接入 Git 命令

    实际使用时,可以直接从 git diff 获取输入。

    import subprocess

    def get_git_diff(base_branch: str = "main") > str:
    result = subprocess.run(
    ["git", "diff", f"{base_branch}…HEAD"],
    capture_output=True,
    text=True,
    check=True,
    )
    return result.stdout

    if __name__ == "__main__":
    diff_text = get_git_diff("main")

    generator = DiffReviewChecklistGenerator()
    result = generator.generate(diff_text)

    prompt = build_review_prompt(result)

    with open("pr_review_prompt.md", "w", encoding="utf-8") as f:
    f.write(prompt)

    print("PR Review Prompt 已生成:pr_review_prompt.md")

    这样就可以把它作为一个本地小工具使用。

    后续还可以做成:

    • pre-push 检查;
    • CI Job;
    • GitHub Action;
    • PR Bot;
    • 内部代码审查插件。

    十一、技术边界提醒
    请添加图片描述

    ⚠️ 第一,关键词识别会误报。

    比如文件里出现 order,不一定真的涉及支付风险。
    所以脚本输出只是提醒,不是最终结论。

    ⚠️ 第二,关键词识别也会漏报。

    如果高风险逻辑没有明显命名,脚本可能检测不到。
    所以不能只靠脚本。

    ⚠️ 第三,GPT 不应该直接判断能不能合并。

    它可以生成 Review Checklist,但最终是否合并必须由 Reviewer 和团队流程决定。

    ⚠️ 第四,测试建议不等于测试覆盖。

    脚本可以提示补哪些测试,但测试是否真的覆盖风险,还要人工确认。

    ⚠️ 第五,不要把敏感 diff 随便丢给外部工具。

    涉及密钥、内部配置、客户数据、未公开业务逻辑时,要先脱敏,并遵守团队安全规范。


    十二、最后:新手程序员不要先追工具,先看自己有没有真实代码流程

    如果只是偶尔问 GPT 一个语法、解释一段报错、写一个小函数,普通问答已经能覆盖不少轻量场景。

    但如果你已经经常把 GPT 放进开发流程,比如读 diff、补测试、写 PR 描述、整理 Review Checklist、分析日志、理解旧项目,那就说明它正在进入真实工作流。

    这时再考虑更稳定的能力,才比较合理。

    如果你正在判断自己是否已经进入这种阶段,可以把 gpt43.com 当作一个新手买前判断参考入口,重点对照 ChatGPT Plus 和 编程场景常用 这两类场景,看自己是轻量问答,还是已经开始把 AI 放进真实代码流程。它更适合放在最后做边界核对,而不是一开始替你做决定。


    十三、总结

    AI 生成代码越来越快,但代码进入工程流程,不能只看“能不能生成”。

    更稳的做法是:

    先用 git diff 提取变更范围;
    再识别风险类别;
    再检查有没有测试和文档;
    再生成 Review Checklist;
    最后由 Reviewer 判断能不能合并。

    GPT 可以帮你写代码,也可以帮你整理 Review 材料。

    但它不应该替你做最终合并决定。

    📌 对开发者来说,真正可靠的 AI 编程工作流,不是“让 GPT 多写点代码”,而是“让 GPT 生成的东西能被检查、能被测试、能被 Review、能被追踪”。

    赞(0)
    未经允许不得转载:171主机测评 » 别只让 GPT 写代码:用 Python 从 git diff 自动生成 PR Review Checklist
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址