从 0 到 1:用 AI Agent 自动审查团队代码质量
🦞 一只用 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,但报告里应该写"任务管理系统·后端"。所以需要映射:
typeProjectConfigstruct{NamestringBackend RepoInfo// 仓库名 + 负责人Frontend RepoInfo}配置在.env里:
CNB_REPOS=org/repo1,org/repo2 PROJECTS_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(✅)
funcFetchDiff(cfg*entities.Config,repo,shastring)(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)ifcfg.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 的 diffoutput,_:=exec.Command("git","-C",repoDir,"show","--no-color",sha).CombinedOutput()returnstring(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,}funcFilterSourceCode(diffstring)string{// 只保留源码文件的 diff 行// 2000 行 diff 可能只剩 500 行,Token 消耗降 75%}Step 3:AI 代码审查——Prompt 是灵魂
逐个审查:每个 commit 过一遍 AI
funcReviewDiff(cfg*entities.Config,diffBlockstring,commitInfostring)(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 合并同类项,报告才不会又臭又长。
汇总审查:把多份审查合并成一份周报
funcGenerateSummary(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 格式:
funcSendMarkdown(webhook,title,markdownstring)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))returnnil}💡 想用飞书、企业微信、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。
定时执行
funcmain(){config.LoadDotEnv()cfg:=config.Load()// 启动时立即执行一次runCodeReview(cfg)// 注册定时任务:每周三 23:11ifcfg.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 已有历史记录
📝 改进建议
- 清理注释残留代码 @后端A
- 提取硬编码数值为常量 @前端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. 钉钉群机器人 Webhook 4. 服务器上安装 Git ### 部署步骤 ```bash # 克隆项目 git clone https://github.com/lobster-bujiaban/code-review-agent.git cd 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-xxxxx CNB_TOKEN=your-token CNB_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=auto DATE_TO=auto # 每周三 23:00 CRON_SPEC=0 23 * * 3systemd 部署
[Unit] Description=Code Review Agent After=network.target [Service] Type=simple WorkingDirectory=/opt/code-review-agent ExecStart=/opt/code-review-agent/code-review-agent Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target# 部署sudocpcode-review-agent /opt/code-review-agent/sudocp.env /opt/code-review-agent/# 启动sudosystemctl daemon-reloadsudosystemctlenablecode-review-agentsudosystemctl start code-review-agent# 查看日志sudojournalctl-ucode-review-agent-f交叉编译(推荐,无需在服务器装 Go)
# Mac/LinuxGOOS=linuxGOARCH=amd64 go build-ocode-review-agent-linux# Windows PowerShell$env:GOOS="linux";$env:GOARCH="amd64";go build-ocode-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 搭副业产线的程序员
全平台同名:虾哥不加班
需要定制 AI 工具?来聊聊 → lob_ai
