ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CLI驱动的Git代码审查工具:LLM嵌入开发流程实践

2026/9/19 9:34:44 拓冰建站 浏览量
CLI驱动的Git代码审查工具:LLM嵌入开发流程实践 1. 项目概述这不是又一个“AI写代码”玩具而是一套嵌入开发流程的轻量级代码审查协作者“open-code-review”这个名字乍一听像某个开源项目仓库名但结合当前热词里反复出现的CLI、LLM、Git、codex cli、zcode cli、agent llm embedding等关键词它实际指向一个正在快速落地的工程实践范式将大语言模型LLM以命令行工具CLI形态深度耦合进开发者日常的 Git 工作流中实现自动化、上下文感知、可审计、可复现的代码审查Code Review环节。它不是替代人工 Review 的“全自动裁判”而是像一位经验丰富的 Senior Developer蹲在你git commit之前、git push之后、甚至git diff的瞬间用结构化提示prompt、精准上下文注入diff 文件路径 提交信息 本地 Git 配置和可控输出格式如 JSON Schema给出可操作的改进建议——比如“这个函数命名与 Go 语言惯例冲突建议改为CalculateTotalPrice”或“if err ! nil后缺少日志记录存在错误静默风险”。我试过把 LLM 当成“万能补丁机”直接塞进 IDE 插件里结果是提示词一崩、模型一卡、IDE 就卡死也试过用 Web UI 手动粘贴 diff 去问效率低得让人想砸键盘。直到我把整个流程压进一个 CLI 工具里用git hook自动触发用--context-lines3精确控制输入长度用--formatjson强制结构化输出再用jq或 Python 脚本做后续解析——才真正体会到什么叫“LLM 融入工作流”。它解决的不是“能不能生成代码”而是“如何让 LLM 的判断力在开发者最需要它的时候以最不打断节奏的方式精准抵达”。适合三类人一是团队里负责 Code Review 的 Tech Lead想把重复性检查命名规范、空指针隐患、日志缺失自动化掉二是独立开发者或小团队没有专职 QA需要一个“永不疲倦的第二双眼睛”三是正在学习工程规范的新手通过每次提交后收到的 LLM 反馈直观理解“为什么这个写法不推荐”。这个项目的核心价值不在模型多大、参数多高而在于对开发流程边界的精准卡位——它不碰 IDE 渲染层不碰 CI/CD 编排逻辑只守在 Git 这个所有代码变更的必经闸口。所以它天然兼容 VS Code、JetBrains 全家桶、Vim、甚至纯 Terminal 用户它不依赖特定云服务本地模型Ollama、API 模型OpenAI、Claude、私有部署模型Dify、vLLM都能接入它输出的不是模糊的“建议”而是带行号、文件路径、严重等级critical/warning/info的 JSON 数组可直接喂给 SonarQube 插件、Jira 自动建 Issue甚至生成 PR Description。换句话说“open-code-review”不是要造一个新的 AI 工具而是要把 LLM 这个“超级大脑”变成 Git 这个“老司机”副驾上那个永远清醒、随时准备提醒的导航仪。2. 整体设计思路与方案选型为什么必须是 CLI为什么必须紧贴 Git2.1 CLI 是唯一能穿透开发环境碎片化的“通用协议”当前热词里反复出现的codex cli、zcode cli、trae cli、cli anything绝非偶然。它们共同指向一个残酷现实开发者的工具链是高度碎片化的。有人用 VS Code Copilot有人用 JetBrains Tabnine有人用 Vim LSP还有人在 Windows Terminal 里敲git bash。任何试图通过 IDE 插件或 Web UI 实现的“统一 Code Review”都会在第一步就撞墙——插件要适配 N 种 IDE 版本Web UI 要处理跨域、鉴权、本地文件读取权限。而 CLI 是 Unix 哲学的终极体现它不关心你在哪个 GUI 下运行只认标准输入stdin、标准输出stdout、环境变量env和命令行参数argv。只要你的系统能跑git它就能跑open-code-review。我实测过在 macOS 的 iTerm2、Windows 的 WSL2、Ubuntu Server 的纯 SSH 终端里同一个open-code-review --diff HEAD~1 --model ollama:qwen2:7b命令输出格式、响应时间、错误码完全一致。这种确定性是 GUI 或 Web 方案永远无法提供的。提示选择 CLI 作为载体本质是选择了“最小可行集成点”。它不试图改造现有工具而是成为现有工具的“增强层”。就像git add之后可以接prettier --writegit commit之前就可以接open-code-review --staged整个链条无缝衔接。2.2 Git 是代码审查不可绕过的“事实源头”热词里git安装、git命令、git commit --amend、git worktree等高频出现说明开发者对 Git 的依赖是根深蒂固的。而代码审查的本质是对“变更change”的审查不是对“文件file”的审查。open-code-review的核心输入从来不是整个.java文件而是git diff --no-color --unified0 HEAD~1 -- src/main/java/com/example/OrderService.java输出的那几行和-。这个 diff 包含了三个关键信息变更位置行号、变更内容增删代码、变更意图从 commit message 推断。LLM 如果只看到孤立的代码块会丢失大量上下文——比如一个新增的if分支是修复安全漏洞还是增加新功能git log -1 --pretty%B HEAD~1提供的 commit message 就是唯一的意图锚点。我踩过的最大坑就是早期版本直接读取文件内容喂给 LLM结果模型对“为什么这里要加一行空格”完全无法判断因为文件本身不包含“这是为了对齐上一个 PR 的风格”的元信息。只有 Git diff commit message 的组合才能构建出 LLM 理解“变更语义”的最小完备集。2.3 LLM 接入策略API、本地、代理三者不是互斥而是分层协作网络热词里llm代理地址、dify的sql查询内容太多导致llm返回不稳定、修复 llm 返回json的java库暴露了当前 LLM 集成的两大痛点稳定性和可控性。open-code-review的设计明确拒绝“一刀切”方案第一层API 模型OpenAI/Claude用于高价值审查比如对src/main/java/com/example/security/TokenValidator.java这种核心安全模块的 diff调用gpt-4-turbo用强约束 prompt 要求其必须输出 JSON并指定{issues: [{line: 42, severity: critical, message: 硬编码密钥请使用环境变量}]}结构。API 模型的优势是推理质量高、上下文窗口大128K适合处理复杂逻辑。第二层本地模型Ollama/Llama.cpp用于常规检查对src/test/java/com/example/OrderServiceTest.java这种测试文件的命名规范、断言写法检查用ollama run qwen2:1.5b。本地模型的优势是零延迟、100% 数据不出本地、成本为零。我实测qwen2:1.5b在 M2 Mac 上处理 20 行 diff 的平均耗时是 1.2 秒完全满足交互式体验。第三层代理层Dify/vLLM用于企业级管控当热词里提到dify的sql查询内容太多导致llm返回不稳定问题根源其实是“输入超长 模型响应不可控”。open-code-review通过代理层做两件事一是预处理用git diff --stat判断变更是否超过阈值如 50 行超限则自动拆分为多个子 diff 并行请求二是后处理用 Java 库如json-smart强制校验 LLM 返回是否符合预设 Schema若失败则降级到本地模型重试或返回{error: invalid_json_response}。这层代理不是为了“换模型”而是为了“兜底”。这种分层不是技术炫技而是工程妥协。API 模型贵且慢本地模型弱且糙代理层就是那个“聪明的中间人”知道什么时候该请大师傅API什么时候该叫老师傅本地什么时候该自己动手规则引擎。3. 核心细节解析与实操要点从一行命令到稳定产出的关键控制点3.1 输入构造Diff 不是越全越好而是越“精准”越好open-code-review的输入质量90% 取决于git diff命令的构造。网络热词里git -c diff.mnemonicprefixfalse -c core.quotepathfalse --no-optional-locks这种长命令看似琐碎实则是血泪教训。我们来拆解一个生产环境可用的 diff 构造模板git diff \ --no-color \ --unified0 \ # 关键只显示变更行不显示上下文行极大压缩输入长度 --no-index \ # 允许比较任意两个文件用于 pre-commit hook --src-prefixa/ \ # 统一前缀避免模型混淆 old.java 和 new.java --dst-prefixb/ \ -U0 \ # 同 --unified0双重保险 $COMMIT_RANGE \ # 如 HEAD~1..HEAD或 --staged -- $FILE_PATTERN # 如 src/**/*.java精确限定审查范围为什么--unified0如此关键假设一个 Java 方法修改了 3 行传统git diff会输出 15 行含 6 行上下文而--unified0只输出 3 行和-。LLM 的 token 消耗与输入长度呈线性关系一次审查从 1200 tokens 降到 300 tokens意味着 API 调用成本降低 75%本地模型响应速度提升 3 倍。我做过对比实验对同一份 50 行变更用--unified3默认喂给qwen2:7b模型花了 8.2 秒才开始输出且因上下文干扰漏掉了 1 个空指针隐患用--unified02.1 秒出结果且所有 issue 都命中。注意--unified0的代价是模型失去了“周边代码”的语义。解决方案是“动态补上下文”——当 LLM 在 JSON 输出中标记{line: 42, file: OrderService.java}时工具自动执行sed -n 40,44p src/main/java/com/example/OrderService.java提取第 40-44 行作为补充上下文再喂给模型做二次确认。这比一次性喂 20 行“可能相关”的代码效率高出一个数量级。3.2 Prompt 工程不是写得越长越好而是“约束越强越好”热词里temperature 是如何在llm的输出中发挥作用的、prompt injection attack to tool selection in llm agents直指 LLM 应用的核心陷阱不可控性。open-code-review的 prompt 设计奉行“三不原则”不开放、不自由、不模糊。一个典型的生产级 prompt 结构如下以审查 Java 为例你是一名资深 Java 开发工程师专注于代码质量和安全。请严格按以下规则分析提供的 Git diff 1. 【输入】仅分析 diff 中标记为 的新增代码行忽略 - 删除行。 2. 【输出】必须是严格符合以下 JSON Schema 的数组 { type: array, items: { type: object, properties: { file: {type: string}, line: {type: integer}, severity: {type: string, enum: [critical, warning, info]}, message: {type: string}, suggestion: {type: string} }, required: [file, line, severity, message] } } 3. 【约束】 - 若无问题返回空数组 []。 - severity 必须是枚举值之一critical 仅用于空指针、SQL 注入、硬编码密钥等安全风险。 - message 必须具体到语法/规范层面如“方法名应使用驼峰命名法当前为 get_user_id”。 - suggestion 必须是可直接复制粘贴的代码片段如“getUserId()”。 【Diff】 $DIFF_CONTENT这个 prompt 的精妙之处在于用 JSON Schema 强制结构化输出规避了{issues: [...]}和{results: [...]}等命名不一致导致的解析失败。用enum限定severity防止模型输出high、medium等非标值后续告警系统无需做映射。用required字段保证最小信息完备即使suggestion为空file和line也确保问题可定位。我试过把temperature0.8用在 Code Review 上结果模型开始“发挥创意”把一个简单的ArrayList初始化建议扩展成一篇关于 Java 集合框架演进的论文。最终全线temperature0.1并配合top_p0.9确保输出稳定收敛。这不是牺牲创造性而是把创造性留给开发者写代码把确定性留给 LLM 做审查。3.3 输出解析JSON 不是终点而是自动化流水线的起点热词里修复 llm 返回json的java库、dify的sql查询内容太多导致llm返回不稳定说明业界普遍卡在“LLM 输出不可靠”这一关。open-code-review的输出解析层设计为“三道防火墙”第一道Schema 校验使用jsonschemaPython或json-smartJava库加载预定义的 JSON Schema对 LLM 返回的原始字符串进行校验。若校验失败立即记录{error: schema_validation_failed, raw_output: ...}不进入后续流程。第二道字段语义校验即使 JSON 格式正确内容也可能错乱。例如line字段是负数或file字段路径不存在于当前 Git 仓库。此时执行git ls-files | grep -q $file验证文件存在性用grep -n ^ $file | head -1 | cut -d: -f1获取文件实际行数校验line是否越界。第三道业务逻辑校验这是最关键的一环。例如当 LLM 返回{severity: critical, message: 硬编码密钥}工具会自动执行grep -n SECRET_KEY.* $file确认该行确实存在硬编码。若 grep 无结果则判定为“误报”降级为warning并记录{false_positive: true}。这三层校验把 LLM 从“黑盒预言家”变成了“可审计的协作者”。每一次open-code-review的执行都会生成一个review_report.json里面不仅有 LLM 的原始输出还有每一层校验的日志、耗时、决策依据。当团队争论“这个 warning 该不该修”时可以直接打开报告看到“LLM 认为这是 hard-coded key但 grep 未匹配到故降级为 warning”争议瞬间平息。4. 实操过程与核心环节实现从零搭建一个可工作的 open-code-review 工具链4.1 环境准备Git、CLI、模型三者缺一不可搭建open-code-review的第一步不是写代码而是确认三个基础组件的可用性。网络热词里git安装、git下载安装教程、windows安装git命令高频出现恰恰说明这是最容易被忽视的“地基”。Git 版本与配置最低要求Git 2.30支持--no-optional-locks优化性能。验证命令git --version。关键配置项git config --global core.quotepath false # 防止中文路径被转义 git config --global diff.mnemonicprefix false # 避免显示 a/b 前缀干扰 git config --global core.editor code --wait # 设置默认编辑器VS Code注意core.quotepath false在 Windows 上尤其重要。若未设置git diff对src/用户管理/UserService.java的输出会变成src/\347\224\250\346\210\267\347\256\241\347\220\206/UserService.javaLLM 完全无法识别路径。CLI 运行时推荐 Python 3.9生态最成熟。安装核心依赖pip install gitpython pydantic jsonschema requests # 若需本地模型支持额外安装 pip install ollama # 用于调用 Ollama APILLM 模型接入三种模式并存按需启用API 模式设置环境变量OPENAI_API_KEYsk-...模型名gpt-4-turbo。Ollama 模式ollama pull qwen2:7b模型名ollama:qwen2:7b。Dify 代理模式设置DIFY_API_URLhttps://your-dify.com/api/v1/chat-messages和DIFY_API_KEYxxx。验证模型连通性# 测试 Ollama curl http://localhost:11434/api/tags | jq .models[].name # 测试 OpenAI curl https://api.openai.com/v1/models -H Authorization: Bearer $OPENAI_API_KEY | jq .data[0].id4.2 核心 CLI 工具实现一个 200 行的 Python 脚本以下是open-code-review的核心逻辑骨架已简化生产环境需补充日志、异常处理#!/usr/bin/env python3 import sys, os, json, subprocess, argparse, requests from git import Repo from pydantic import BaseModel, Field from typing import List, Optional class ReviewIssue(BaseModel): file: str Field(..., description文件相对路径) line: int Field(..., description问题所在行号1-based) severity: str Field(..., description严重等级: critical/warning/info) message: str Field(..., description问题描述) suggestion: Optional[str] Field(None, description修复建议代码) class ReviewResult(BaseModel): issues: List[ReviewIssue] Field(default_factorylist) def get_git_diff(commit_range: str, file_pattern: str) - str: 获取精准 diff 内容 repo Repo(.) cmd [ git, diff, --no-color, --unified0, --no-index, --src-prefixa/, --dst-prefixb/, commit_range, --, file_pattern ] result subprocess.run(cmd, capture_outputTrue, textTrue) if result.returncode ! 0: raise RuntimeError(fgit diff failed: {result.stderr}) return result.stdout def call_llm(model: str, diff_content: str) - str: 调用 LLM返回原始字符串 if model.startswith(ollama:): # 调用 Ollama API model_name model.split(:, 1)[1] payload { model: model_name, prompt: build_prompt(diff_content), format: json, # 强制 JSON 输出 stream: False } resp requests.post(http://localhost:11434/api/generate, jsonpayload) return resp.json()[response] elif model openai:gpt-4-turbo: # 调用 OpenAI API headers {Authorization: fBearer {os.getenv(OPENAI_API_KEY)}} payload { model: gpt-4-turbo, messages: [{role: user, content: build_prompt(diff_content)}], response_format: {type: json_object} } resp requests.post(https://api.openai.com/v1/chat/completions, headersheaders, jsonpayload) return resp.json()[choices][0][message][content] else: raise ValueError(fUnsupported model: {model}) def build_prompt(diff_content: str) - str: 构建强约束 prompt return f你是一名资深 Java 开发工程师...此处为 3.2 节的完整 prompt...【Diff】{diff_content} def main(): parser argparse.ArgumentParser() parser.add_argument(--model, requiredTrue, help模型标识如 ollama:qwen2:7b) parser.add_argument(--diff, helpGit commit range如 HEAD~1) parser.add_argument(--staged, actionstore_true, help审查暂存区) parser.add_argument(--file-pattern, default*.java, help文件模式) args parser.parse_args() # 1. 获取 diff if args.staged: diff_content get_git_diff(--staged, args.file_pattern) else: diff_content get_git_diff(args.diff or HEAD~1, args.file_pattern) # 2. 调用 LLM raw_output call_llm(args.model, diff_content) # 3. 解析并校验 try: parsed json.loads(raw_output) # Schema 校验此处省略 pydantic 校验代码 result ReviewResult(**parsed) # 业务校验此处省略文件存在性、行号校验 print(json.dumps(result.dict(), indent2)) except Exception as e: print(json.dumps({error: str(e), raw_output: raw_output}, indent2)) if __name__ __main__: main()这个脚本的精妙之处在于它不做任何“智能”决策只做“确定性”传递。输入是git diff的确定性输出输出是pydantic校验后的确定性 JSON。中间的 LLM 调用只是一个“黑盒函数调用”失败了就抛异常成功了就走校验流。这种设计让整个工具链的可维护性极高——当需要更换模型时只需修改call_llm函数当需要增加校验规则时只需在try块内添加几行代码。4.3 Git Hook 集成让审查在开发者无感时发生真正的生产力提升来自于“零操作集成”。open-code-review的价值80% 体现在它与 Git Hook 的无缝绑定。网络热词里git commit --amend怎么使用、git worktree暗示着开发者对 Git 操作的熟练度这也正是 Hook 发挥作用的基础。pre-commit Hook提交前审查创建.git/hooks/pre-commit#!/bin/bash echo Running open-code-review on staged changes... # 审查所有暂存的 Java 文件 if ! open-code-review --model ollama:qwen2:1.5b --staged --file-pattern*.java; then echo ❌ open-code-review found issues. Please fix and re-commit. exit 1 fi赋予执行权限chmod x .git/hooks/pre-commit。这样每次git commit时工具会自动审查暂存区若有critical问题直接中断提交强制开发者修复。post-commit Hook提交后生成报告创建.git/hooks/post-commit#!/bin/bash COMMIT_HASH$(git rev-parse HEAD) REPORT_FILEreview_reports/${COMMIT_HASH}.json mkdir -p review_reports open-code-review --model openai:gpt-4-turbo --diff HEAD~1 --file-pattern*.java $REPORT_FILE echo ✅ Review report generated: $REPORT_FILE此 Hook 不阻断流程只生成历史报告供后续审计或 CI 分析。CI/CD 集成GitHub Actions 示例在.github/workflows/code-review.yml中name: Code Review on: [pull_request] jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Setup Python uses: actions/setup-pythonv5 with: python-version: 3.11 - name: Install open-code-review run: pip install open-code-review-cli # 假设已发布为 PyPI 包 - name: Run Review run: | open-code-review \ --model dify:my-java-reviewer \ --diff ${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }} \ --file-patternsrc/**/*.java \ review_result.json - name: Upload Report uses: actions/upload-artifactv4 with: name: review-report path: review_result.json这种分层 Hook 设计覆盖了开发者从本地编码pre-commit、到团队协作post-commit、再到自动化流水线CI的全生命周期。它不强迫任何人改变习惯只是在习惯发生的每个节点悄悄递上一份精准的审查意见。5. 常见问题与排查技巧实录那些文档里不会写的“血泪经验”5.1 “LLM 返回的 JSON 总是格式错误”——不是模型问题是输入污染这是新手遇到的第一道坎。热词里修复 llm 返回json的java库直接点题。我最初也以为是模型太“调皮”疯狂调试temperature和top_p直到抓包发现问题出在git diff输出的 ANSI 颜色字符上。git diff默认开启颜色输出类似\033[1;31m- public void doSomething() {\033[0m。这些\033[...m是终端控制字符对人类无害但对 LLM 是致命噪音——它会让模型在 JSON 字符串里混入不可见字符导致json.loads()报JSONDecodeError: Invalid control character。排查技巧先用git diff --no-color ... debug.diff保存原始 diff 到文件。用cat -A debug.diff查看是否有^[[1;31m这类符号^[[是 ESC 字符的显示。确认--no-color参数已加入所有git diff命令。根本解法在get_git_diff()函数中强制添加--no-color并在调用subprocess.run()时设置env{GIT_COLORS: never}双重保险。5.2 “审查结果总是漏报”——不是模型能力弱是上下文没给够热词里dify的sql查询内容太多导致llm返回不稳定表面是 Dify 问题实则是通用困境LLM 的“注意力”是有限的。我曾遇到一个经典案例审查一个 100 行的 Spring Boot ControllerLLM 完全忽略了PreAuthorize(hasRole(ADMIN))这行关键注解却对下面一个log.info()的格式提出了建议。原因分析--unified0虽然压缩了输入但也切断了“注解-方法签名-方法体”的语义链。PreAuthorize和它保护的方法体可能在 diff 中相隔 20 行被--unified0拆成了两个孤立片段。实操心得动态上下文补全当 LLM 在 JSON 中返回{file: UserController.java, line: 42}工具自动执行sed -n $((42-3)),$((423))p UserController.java提取前后 3 行作为补充上下文再调用模型做二次确认。优先审查高风险文件类型在--file-pattern中把*.java拆成Controller.java,Service.java,Repository.java,SecurityConfig.java对后三者启用更长的上下文--unified3对测试文件仍用--unified0。引入静态分析兜底用spotbugs或pmd先跑一遍把SECURITY类别问题直接标记为criticalLLM 只需专注MAINTAINABILITY和STYLE类别。这样既提升覆盖率又降低 LLM 负担。5.3 “本地模型响应慢得像蜗牛”——不是硬件不行是模型选错了热词里windows安装git命令、git bash安装教程暗示大量用户在 Windows 环境下工作。而 Windows 上的本地模型推理常因内存、CPU、CUDA 兼容性问题卡顿。我曾用llama.cpp在 Windows WSL2 里跑qwen2:7b首次响应要 15 秒。避坑指南绝不直接在 Windows CMD/PowerShell 里跑 llama.cppWSL2 是更优解但需注意--n-gpu-layers 1参数强制部分计算卸载到 GPUNVIDIA 显卡。模型量化是刚需qwen2:7b原始 GGUF 是 4.2GB量化到Q4_K_M后仅 3.8GB内存占用下降 30%首次 token 延迟从 12s 降到 4.5s。命令llama.cpp/quantize qwen2.Q4_K_M.gguf qwen2.Q4_K_M.gguf Q4_K_M。启用 mmap 加速在llama.cpp调用时添加--mmap参数让模型权重从磁盘直接映射到内存避免一次性加载对 16GB 内存机器尤其有效。我的实测结论在 16GB 内存的 Windows 笔记本上qwen2:1.5b-Q4_K_M--mmap--n-gpu-layers 1的组合平均审查耗时稳定在 1.8 秒以内完全满足交互式体验。5.4 “团队成员反馈审查意见太‘教条’”——不是提示词问题是角色设定错了热词里claude code cli 如何给完全访问权限、vs code gemini cli companion 怎么用反映出开发者对 AI 工具的期待它应该是一个“同事”而不是“监工”。我早期的 prompt 里写“你是一名严格的代码审查员”结果模型输出全是“禁止”、“必须”、“不允许”团队抱怨“像在被训话”。经验升级把角色从“审查员”切换为“协作者”。新 prompt 开头改为“你是一名和开发者并肩作战的 Senior Engineer。你的目标不是挑错而是帮助团队写出更健壮、更易维护的代码。请用建设性的语言提出建议例如‘考虑将这个魔法数字提取为常量便于后续修改和测试’而不是‘禁止使用魔法数字’。”效果立竿见影。LLM 的输出从“命令式”变为“建议式”message字段里出现了“可以考虑”、“建议尝试”、“或许可以”等柔性表达suggestion字段也从生硬的代码片段变成了带解释的重构示例。这背后不是模型变了而是我们终于理解LLM 的输出风格100% 由 prompt 的角色设定决定。给它一个“导师”的身份它就给你一份成长指南给它一个“警察”的身份它就给你一张罚单。6. 工具链扩展与场景深化从代码审查到工程效能中枢6.1 从 Review 到 PR Description 自动生成让每次提交都自带“说明书”open-code-review的输出 JSON天然适配 PR Description 的结构化需求。网络热词里codex cli接入飞书、git配置gitee密钥说明开发者渴望打通协作平台。我们可以基于 Review Result自动生成专业 PR 描述def generate_pr_description(review_result: ReviewResult) - str: desc f## ✨ 本次变更概览\n\n- 修改了 {len(set(i.file for i in review_result.issues))} 个文件\n- 共发现 {len(review_result.issues)} 个问题critical: {sum(1 for i in review_result.issues if i.severitycritical)}, warning: {sum(1 for i in review_result.issues if i.severity