用 DeepSeek 给 Git 提交做自动 Code Review:从 0 落地一个本地 AI 审查流程

在这篇文章里,我们从一个空目录开始,搭建一套在 git commit 前自动调用 DeepSeek 做代码审查的工作流:

  • 使用 Node.js 脚本 读取本次提交的 git diff
  • 通过 DeepSeek Chat API 做代码审查,并按 严重程度分级
  • 在 pre-commit hook 阶段自动执行,有严重问题就阻止提交
  • 审查规则抽象为 rules.json 配置
  • 模型配置通过 .env.local 管理,方便本地开发
    你可以直接把这里的方案搬进自己的项目。

场景与目标

问题:团队日常开发中,Review 资源有限,小问题容易被漏掉,大问题又可能在合并后才暴露。目标:在每次 git commit 之前,让 AI 帮你快速过一遍变更,提前发现严重问题,并给出可执行的改进建议。我们设计的方案满足这些要求:

  • 只审查本次提交的已暂存改动(Staged Diff)
  • AI 按 严重 / 一般 / 建议 三个级别输出问题
  • 有严重问题时:阻止提交
  • 所有规则(关注点、文案、阻断策略)都写在 rules.json,后续可自定义
  • 模型 Key & 地址放在 .env.local,便于本地环境管理

项目初始化

在一个空目录(例如 AI-Codereview)下,我们先手写一个简单的 package.json:

js 复制代码
{
  "name": "ai-codereview",
  "version": "0.1.0",
  "description": "在 git 提交时调用 DeepSeek 模型进行代码自动审查的简单工具",
  "main": "scripts/deepseek-review.js",
  "bin": {
    "deepseek-review": "scripts/deepseek-review.js"
  },
  "scripts": {
    "review": "node scripts/deepseek-review.js"
  },
  "license": "MIT"
}

这里有两个点:

  • bin 字段:将来可以通过 npm install -g . 安装成全局 CLI,直接用 deepseek-review 命令。
  • scripts.review:方便本地调试,npm run review 即可执行审查脚本。

核心:DeepSeek 代码审查脚本

我们在 scripts/deepseek-review.js 里完成几件事:

  1. 加载 .env.local 和规则配置 rules.json
  2. 从 git diff --cached 读取当前已暂存的变更
  3. 构造一份带有「规则说明 + 输出格式」的 Prompt
  4. 调用 DeepSeek Chat API
  5. 解析 AI 输出中的 BLOCK: YES/NO,决定是否阻止提交

下面是部分代码实现结构:

js 复制代码
// 读取当前已暂存的 diff
function run(cmd) {
  return execSync(cmd, { encoding: "utf8" }).trim();
}

function getStagedDiff() {
  try {
    return run("git diff --cached --unified=0");
  } catch (e) {
    console.error("[deepseek-review] 读取 git diff 失败:", e.message);
    process.exit(1);
  }
}

// 构造 Prompt,把规则和输出格式喂给模型
function buildPrompt(diff, rules) {
  const focusList = Array.isArray(rules.focus) && rules.focus.length
    ? rules.focus.map((item) => `- ${item}`).join("\n")
    : "- 逻辑正确性与潜在 Bug\n- 边界条件与异常处理\n- 性能问题\n- 安全问题\n- 可维护性与可读性";

  const severeDesc = rules.severityLevels?.severe?.description
    || "可能导致功能不可用、数据损坏、安全漏洞或构建失败的问题。";
  const majorDesc = rules.severityLevels?.major?.description
    || "可以运行,但存在潜在 Bug、性能隐患或较大维护成本的问题。";
  const suggestionDesc = rules.severityLevels?.suggestion?.description
    || "风格、命名、结构优化、更优写法等。";

  const of = rules.outputFormat || {};

  return `
你是一个严格的资深代码审查专家,请审查下面这次 git 提交的变更 diff。

【审查重点】
${focusList}

【分级规则】
- 严重问题:${severeDesc}
- 一般问题:${majorDesc}
- 建议与优化:${suggestionDesc}

【输出格式(必须严格遵守,方便工具解析)】
${of.overview || "1. 总体评价(1-2 句)"}
${of.severe || "2. 严重问题(如果没有,请写"无严重问题")\n   - 【严重】描述问题 + 涉及的文件/大致位置 + 原因"}
${of.major || "3. 一般问题(如果没有,请写"无一般问题")\n   - 【一般】描述问题 + 涉及的文件/大致位置 + 原因"}
${of.suggestion || "4. 建议与优化(如果没有,可以写"暂无明显优化建议")"}

${of.blockRule || "5. 最终结论(只保留一行,且用英文 BLOCK 标记,必须大写):\n   - 若必须在提交前修复,请输出:BLOCK: YES\n   - 若允许提交,仅给出建议,请输出:BLOCK: NO"}

注意:
- 不要输出除上述格式以外的额外总结行。
- "BLOCK: YES/NO" 必须是独立一行,前后不要加其他字符。

下面是本次提交的 diff(统一 diff 格式):

${diff}
`;
}

// 调用 DeepSeek Chat API
function callDeepseek({ apiKey, baseUrl, model, prompt }) {
  return new Promise((resolve, reject) => {
    const postData = JSON.stringify({
      model,
      messages: [
        { role: "system", content: "你是一个资深代码审查工程师,擅长发现问题并提出可行建议。" },
        { role: "user", content: prompt }
      ],
      temperature: 0.2
    });

    const url = new URL("/v1/chat/completions", baseUrl);

    const options = {
      method: "POST",
      hostname: url.hostname,
      path: url.pathname,
      port: url.port || 443,
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${apiKey}`,
        "Content-Length": Buffer.byteLength(postData)
      }
    };

    const req = https.request(options, (res) => {
      let data = "";
      res.on("data", (chunk) => (data += chunk));
      res.on("end", () => {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          try {
            const json = JSON.parse(data);
            const content =
              json.choices?.[0]?.message?.content ||
              json.choices?.[0]?.text ||
              "";
            resolve(content.trim());
          } catch (e) {
            reject(new Error("解析 DeepSeek 响应失败:" + e.message));
          }
        } else {
          reject(
            new Error(`DeepSeek API 响应错误:${res.statusCode} ${res.statusMessage} ${data}`)
          );
        }
      });
    });

    req.on("error", (err) => reject(err));
    req.write(postData);
    req.end();
  });
}

你可以直接通过修改这个文件来:

  • 调整审查关注点(例如换成「支付正确性」「权限控制」等)
  • 改写分级说明,贴近你们团队的规范
  • 修改输出格式,比如增加某个模块的专门小节
  • 切换默认阻断策略(blockOnSevereDefault)

模型配置通过 .env.local 管理

为了避免把 Key 写死在代码或 shell 配置里,我们采用 Next.js 常见的 .env.local 风格:

js 复制代码
# .env.local(不提交到仓库)
DEEPSEEK_API_KEY=你的_API_Key
DEEPSEEK_API_BASE=https://api.deepseek.com
DEEPSEEK_MODEL=deepseek-chat

# 是否在严重问题时阻止提交(1 阻止 / 0 不阻止)
DEEPSEEK_BLOCK_ON_SEVERE=1

脚本启动时会:

  1. 查找当前工作目录的 .env.local
  2. 解析每一行 KEY=VALUE,写入 process.env
  3. 如果系统环境变量里已经有同名 KEY,则以系统环境变量为准

这样做有几个好处:

  • 适合前端/Node 项目现有习惯
  • 可以按仓库存储不同 Key/配置
  • 不需要全局污染 shell 环境

Git Hook:在提交前自动执行审查

最后一步,把脚本接到 Git 的 pre-commit 阶段。

由于是拿本项目来执行验证的这里使用 .githooks/pre-commit + core.hooksPath 的方式,方便版本管理。

在项目根目录创建 .githooks/pre-commit:

js 复制代码
#!/bin/sh

# 简单的 pre-commit hook 示例:
# 在执行 git commit 时,自动调用 DeepSeek 代码审查脚本。

echo "[pre-commit] 调用 DeepSeek 进行代码审查..."

node scripts/deepseek-review.js
STATUS=$?

if [ $STATUS -ne 0 ]; then
  echo "[pre-commit] DeepSeek 代码审查脚本执行失败或发现严重问题,终止提交。"
  exit $STATUS
fi

exit 0

然后在仓库里启用它:

shell 复制代码
git config core.hooksPath .githooks
chmod +x .githooks/pre-commit
chmod +x scripts/deepseek-review.js

从此以后,每次在这个仓库里执行:

js 复制代码
git add .
git commit -m "feat: xxx"

都会自动触发:

  1. pre-commit → node scripts/deepseek-review.js
  2. 脚本读取 git diff --cached
  3. 调用 DeepSeek → 按规则分级输出问题,并在末尾给出 BLOCK: YES/NO
  4. 如果是 BLOCK: YES 且当前规则/环境要求阻断 → 本次提交直接失败

审核示例

总结与一些延伸想法

到这里,我们落地了一套本地可配置的 AI Code Review 流程,特点是:

  • 与 Git 强绑定:在提交前提前拦截严重问题
  • 规则可配置:通过 rules.json 抽象了审查维度和分级
  • 模型可配置:模型信息放在 .env.local,符合前端/Node 生态的习惯
  • 行为可调:既可以强制阻断严重问题,也可以在开发早期只做「提示不阻断」

基于这套结构,你还可以继续扩展:

  • 针对不同分支(main / dev)选择不同的规则文件
  • 在审查结果中增加代码示例或重构建议模板
  • 将结果同步到 Git 平台(GitLab MR comment / GitHub PR comment)

由于工具当前只是 0.1.0 版本还有很多待改进的地方,后续将不断优化改进。

相关推荐
ByteCraze36 分钟前
系统性整理组件传参14种方式
前端·javascript·vue.js
之恒君36 分钟前
PromiseResolveThenableJobTask 微任务是怎么被执行的
前端
华仔啊36 分钟前
CSS常用函数:从calc到clamp,实现动态渐变、滤镜与变换
前端·css
大杯咖啡37 分钟前
基于 Vue3 (tsx语法)的动态表单深度实践-只看这一篇就够了
前端·javascript·vue.js
Aniugel37 分钟前
Vue2简单实现一个权限管理
前端·vue.js
乐无止境38 分钟前
系统性整理组件传参14种方式
前端
爱泡脚的鸡腿39 分钟前
uni-app D8 实战(小兔鲜)
前端·vue.js
睡神雾雨41 分钟前
Vite 环境变量配置经验总结
前端