乐于分享
好东西不私藏

从 0 到 1:用 AI Agent 自动审查团队代码质量

从 0 到 1:用 AI Agent 自动审查团队代码质量


痛点

我们团队 3 个项目、6 个仓库、前后端各一人。按理说每周应该做代码审查,但现实是:

  • 没人做
    。活都干不完,谁有空看别人的代码?
  • 做了也流于形式
    。随便扫两眼,”看起来没问题”,完事。
  • 问题照出
    。低级命名、重复代码、潜在 Bug,事后才发现。

说白了,代码审查重要性都知道,但执行力几乎为零。

那能不能让 AI 来做?每天自动采集代码提交,AI 审查质量,报告推送到钉钉群。团队不用花时间,但问题有人盯着。

这就是 code-review-agent


架构:四步走

CNB API → Git Show → DeepSeek AI → 钉钉推送 采集提交   获取Diff   AI审查代码   周报直达群
  • Step 1
    :从代码仓库拉取本周所有 commit 记录
  • Step 2
    :用 Git 获取每个 commit 的代码差异
  • Step 3
    :把代码差异丢给 AI,让它审查代码质量
  • Step 4
    :汇总成周报,推送到钉钉群

关键差异:周报只需要 commit message,代码审查需要看真正的代码变更。


Step 1:数据采集——项目映射

CNB 的仓库路径是 team/project/task-api,但报告里应该写”任务管理系统·后端”。所以需要映射:

type ProjectConfig struct {    Name     string    Backend  RepoInfo  // 仓库名 + 负责人    Frontend RepoInfo}

配置在 .env 里:

CNB_REPOS=org/repo1,org/repo2PROJECTS_JSON={"name":"任务管理系统","backend":{"repo":"task-api","dev":"后端A"},"frontend":{"repo":"task-web","dev":"前端A"}}PROJECTS_JSON={"name":"文档协作平台","backend":{"repo":"doc-api","dev":"后端B"},"frontend":{"repo":"doc-web","dev":"前端A"}}

采集时,把仓库名映射到项目和负责人,后面的报告才能按项目分组、按负责人 @提醒。


Step 2:获取代码差异——踩坑最多的环节

最初方案:CNB Diff API( 404)

url := fmt.Sprintf("https://api.cnb.cool/%s/commit/%s/diff", repo, sha)// 全部 404,这个 API 对第三方未开放

最终方案:Git Clone + Git Show(

func FetchDiff(cfg *entities.Config, repo, sha string) (string, error) {    repoDir := filepath.Join(os.TempDir(), "code-review-"+repo)    // 首次:克隆仓库    if _, err := os.Stat(repoDir); os.IsNotExist(err) {        repoURL := fmt.Sprintf("https://cnb.cool/%s.git", repo)        if cfg.GitUsername != "" {            repoURL = fmt.Sprintf("https://%s:%s@cnb.cool/%s.git",                cfg.GitUsername, cfg.GitPassword, repo)        }        exec.Command("git", "clone", "--depth=200", repoURL, repoDir).Run()    } else {        // 后续:只 fetch 更新        exec.Command("git", "-C", repoDir, "fetch", "--all").Run()    }    // 获取指定 commit 的 diff    output, _ := exec.Command("git", "-C", repoDir, "show", "--no-color", sha).CombinedOutput()    return string(output), nil}

关键点:

  • --depth=200
    :浅克隆,只拉最近 200 个 commit
  • --no-color
    :去掉 ANSI 颜色码,纯文本更利于 AI 解析
  • 缓存机制:仓库只克隆一次,后续只 fetch 更新

源码过滤:只审查有意义的代码

一个 commit 的 diff 里可能有 package-lock.json.env、二进制文件。这些丢给 AI 是浪费 Token:

sourceExts := map[string]bool{    ".go": true, ".py": true, ".js": true, ".ts": true,    ".vue": true, ".java": true, ".css": true, ".scss": true,}func FilterSourceCode(diff string) string {    // 只保留源码文件的 diff 行    // 2000 行 diff 可能只剩 500 行,Token 消耗降 75%}

Step 3:AI 代码审查——Prompt 是灵魂

逐个审查:每个 commit 过一遍 AI

func ReviewDiff(cfg *entities.Config, diffBlock string, commitInfo string) (string, error) {    systemPrompt := `你是代码审查助手。只输出有问题的代码,格式必须适合手机阅读。【输出格式 - 严格遵循】# 📋 代码审查报告## 📊 概览审查 X 个提交 | 质量评分 X/5---## ⚠️ 发现的问题### 问题1:问题标题**责任人:后端A****❌ 修改前:**` + "```" + `语言// 有问题的代码` + "```" + `**✅ 修改后:**` + "```" + `语言// 改进后的代码` + "```" + `**原因:** 简要说明问题---【强制规则】- ❌ 禁止使用 Markdown 表格- ✅ 每个问题必须有代码对比- ✅ 每个问题必须标注责任人- ✅ 只输出有问题的地方- ✅ 移动端友好`    reqBody := ChatRequest{        Model: cfg.DeepSeekModel,        Messages: []ChatMessage{            {Role: "system", Content: systemPrompt},            {Role: "user", Content: "Commit: " + commitInfo + "\n\nDiff:\n```diff\n" + diffBlock + "\n```"},        },    }    // 调用 DeepSeek API ...}

移动端友好的输出格式

这是迭代后的版本,解决了原版的问题:

  •  去掉 Markdown 表格(移动端显示乱)
  •  每个问题包含「修改前 vs 修改后」代码对比
  •  每个问题标注责任人
  •  只输出有问题的部分,正确的不写
  •  用 --- 分隔问题,适合聊天记录滚动查看

Prompt 设计的 5 条铁律

1. 格式必须写死

不加格式约束,AI 输出千奇百怪。在 Prompt 里把完整模板写出来,输出立刻可控。

2. 每个问题必须”修改前 vs 修改后”

如果 AI 只说”变量命名不规范”,读者还得猜怎么改。要求给出代码对比,可操作性翻倍。

3. @负责人

审查报告的价值在于可执行。”有个变量命名不好”不如”@后端A,第 42 行变量名建议改为 userList”。

4. 只关注代码质量,不关注业务逻辑

AI 看不懂你的业务。让它审查命名、结构、可读性、重复代码、潜在 Bug。这些是 AI 真正擅长的。

5. 合并共性问题

34 个 commit 逐个审查,可能有 10 个都犯了”变量名缩写过度”。汇总时让 AI 合并同类项,报告才不会又臭又长。

汇总审查:把多份审查合并成一份周报

func GenerateSummary(cfg *entities.Config, reviews []string, commits []entities.Commit) (string, error) {    // 构建项目统计信息    projectInfo := buildProjectStats(commits)    // 把所有审查结果拼接    combinedReview := strings.Join(reviews, "\n\n---\n\n")    // 丢给 AI 生成最终周报(格式同上)    // ...}

最终输出一份按项目分组的审查周报,钉钉群里直接渲染 Markdown。


Step 4:钉钉推送

上次用企业微信,这次团队用钉钉。逻辑完全一样,换了个 Webhook 格式:

func SendMarkdown(webhook, title, markdown string) error {    msg := DingTalkMarkdownMsg{MsgType: "markdown"}    msg.Markdown.Title = title    msg.Markdown.Text = markdown    jsonBody, _ := json.Marshal(msg)    resp, _ := http.Post(webhook, "application/json", bytes.NewReader(jsonBody))    return nil}

💡 想用飞书、企业微信、Slack?只需要改这一个函数,其他代码不用动。


项目结构

code-review-agent/├── main.go              # 入口 + 调度├── entities/            # 数据结构├── config/              # 配置加载├── cnb/                 # CNB API 采集 + 源码过滤├── git/                 # Git clone/show 操作├── review/              # DeepSeek AI 审查 + 汇总├── dingtalk/            # 钉钉推送├── go.mod├── .env.example└── README.md

拆分的理由很实际:main.go 超过 600 行后,改一个地方要上下翻好久。拆成 6 个包后,改 Git 逻辑只看 git/git.go,改审查 Prompt 只看 review/review.go


定时执行

func main() {    config.LoadDotEnv()    cfg := config.Load()    // 启动时立即执行一次    runCodeReview(cfg)    // 注册定时任务:每周三 23:11    if cfg.CronSpec != "" {        c := cron.New()        c.AddFunc(cfg.CronSpec, func() {            cfg.DateFrom = time.Now().AddDate(0, 0, -7).Format("2006-01-02")            cfg.DateTo = time.Now().Format("2006-01-02")            runCodeReview(cfg)        })        c.Start()        select {}    }}

⚠️ Cron 表达式的坑

robfig/cron/v3 的格式是:分 时 日 月 周

我想设”每周三 23:00″,一开始写了 0 3 23 * * 3,以为是从左到右递增。结果被解析成”每月 23 号周三凌晨 3 点”。

正确写法:0 23 * * 3


实际效果

34 个 commit 逐个审查,大约 15 分钟。其中 AI 调用是主要耗时,每个 commit 约 8-15 秒。

钉钉群里收到的报告格式:

# 📋 代码质量周报## 📊 概览审查 8 个提交 | 质量评分 3/5---## ⚠️ 问题清单### 问题1:死代码未删除**责任人:后端A****❌ 修改前:**```html<!-- <button class="submit">报送</button> -->

 修改后:(直接删除整行)

原因: 应删除而非注释,Git 已有历史记录


📝 改进建议

  1. 清理注释残留代码 @后端A
  2. 提取硬编码数值为常量 @前端A
可以看到:- ✅ 适合手机滚动查看- ✅ 每个问题有代码对比- ✅ @了具体负责人- ✅ 共性问题已合并---## 和周报 Agent 的对比两个 Agent 架构相似,但重点不同:- **周报 Agent**:输入 commit message,AI 做文案整理,输出给人看"做了什么"- **代码审查 Agent**:输入 commit message + 代码 Diff,AI 做代码审查,指出代码问题代码审查 Agent 新增了 3 个关键能力:1. **项目映射**:仓库名 → 项目名 + 负责人,报告才有归属2. **源码过滤**:只审查 `.go` `.vue` `.ts` 等源码文件,跳过配置和锁文件3. **汇总报告**:多份逐个审查 → 1 份按项目分组的周报---## 部署指南### 前置准备1. DeepSeek API Key(注册就送额度,审查一次约 0.1 元)2. CNB Token(或其他代码平台 Token)3. 钉钉群机器人 Webhook4. 服务器上安装 Git### 部署步骤```bash# 克隆项目git clone https://github.com/lobster-bujiaban/code-review-agent.gitcd code-review-agent# 配置cp .env.example .env# 编辑 .env,填入 API Key、仓库列表、项目映射、Webhook# 编译go build -o code-review-agent .# 测试运行./code-review-agent

.env 配置示例

DEEPSEEK_API_KEY=sk-xxxxxCNB_TOKEN=your-tokenCNB_REPOS=org/project-a/backend,org/project-a/frontend# 项目配置(每个项目一行)PROJECTS_JSON={"name":"项目A","backend":{"repo":"backend","dev":"后端A"},"frontend":{"repo":"frontend","dev":"前端A"}}DINGTALK_WEBHOOK=https://oapi.dingtalk.com/robot/send?access_token=xxx# 留空=自动最近7天,或指定日期DATE_FROM=autoDATE_TO=auto# 每周三 23:00CRON_SPEC=0 23 * * 3

systemd 部署

[Unit]Description=Code Review AgentAfter=network.target[Service]Type=simpleWorkingDirectory=/opt/code-review-agentExecStart=/opt/code-review-agent/code-review-agentRestart=on-failureRestartSec=10[Install]WantedBy=multi-user.target
# 部署sudo cp code-review-agent /opt/code-review-agent/sudo cp .env /opt/code-review-agent/# 启动sudo systemctl daemon-reloadsudo systemctl enable code-review-agentsudo systemctl start code-review-agent# 查看日志sudo journalctl -u code-review-agent -f

交叉编译(推荐,无需在服务器装 Go)

# Mac/LinuxGOOS=linux GOARCH=amd64 go build -o code-review-agent-linux# Windows PowerShell$env:GOOS="linux"; $env:GOARCH="amd64"; go build -o code-review-agent-linux

改造成你自己的

  • 换代码平台
     → 改 cnb/cnb.go
  • 换 AI 模型
     → 改 review/review.go 的 API 地址
  • 换推送渠道
     → 改 dingtalk/dingtalk.go
  • 加审查维度
     → 改 review/review.go 的 Prompt
  • 调审查频率
     → 改 .env 的 CRON_SPEC

踩过的坑

坑 1:CNB Diff API 404

API 文档里有 diff 端点,但第三方调用全部 404。浪费了 2 小时尝试各种参数。

解法:降级到 git clone + git show。多一步 Git 操作,但 100% 可靠。

教训:别信文档,先 curl 验证 API 可用性,再写代码。

坑 2:Cron 表达式字段顺序搞反

0 3 23 * * 3 → 我以为是”秒 分 时 日 月 周”,实际是”分 时 日 月 周”。

解法:改成 0 23 * * 3

坑 3:AI 输出格式不可控

第一版 Prompt 只有”你是代码审查员”,AI 输出五花八门。

解法:在 Prompt 里写死完整的 Markdown 模板。

教训:Prompt 不是聊天,是”编程”。你不说清楚格式,AI 就自由发挥。

坑 4:源码不过滤,Token 爆炸

一个 commit 可能改了 package-lock.json.env,这些文件 diff 动辄几千行。

解法:加了 FilterSourceCode() 函数,只保留源码文件的 diff。


总结

这个项目从想法到跑通,花了大约 两个晚上。比周报 Agent 多花了一倍时间,主要是因为 Diff 获取方案从 API 降级到 Git,以及 Prompt 迭代。

但核心架构是同一条线:

数据采集 → AI 处理 → 自动推送

下周我会写 W3 踩坑实录,把搭这两个 Agent 过程中所有踩过的坑详细拆解。

📦 源码地址

GitHub – lobster-bujiaban/code-review-agent

欢迎 Star、提 Issue、Fork 改造。


🦞一只用 AI Agent 搭副业产线的程序员

公众号:虾哥不加班 | B站:龙虾不加班 | 掘金:龙虾不加班