简介:面向需要高效代码审查的Java开发者与测试团队,这套基于人工智能技术的代码自动评审工具源码,借助AI模型对Java源码进行静态与智能分析,可自动识别安全漏洞、异常逻辑和不规范写法,从而减轻人工复查负担,提高代码健壮性与可维护性。压缩包内共34个文件,整体仅79KB,结构精简合理。其中24个Java源文件承载代码解析、规则匹配、评审报告生成等核心功能;4个XML配置了Maven构建与依赖信息;2个YAML文件定义GitHub Actions工作流,可适配远程与本地运行;再加上Git忽略文件、Shell脚本、Manifest和文本说明,覆盖版本管理、自动化部署和说明引导。该资源已有341人学习,适合中高级Java开发者、自动化测试工程师以及DevOps团队作为AI代码评审落地的参考。尤其值得一提的是,包内提供了OpenAI接口调用示例脚本、测试用例与说明文档,能够完整展示从代码采集、AI分析到生成评审意见的整个过程,方便读者理解实现原理并迁移到自己的项目中;预留的CI/CD扩展点也便于二次开发,快速形成统一的代码质量门禁。
1. 人工智能 + Java 代码自动评审:它到底帮你省掉哪一档事
先给结论:这个源码包不是拿 AI 替你做代码评审,而是把「人肉扫低级问题」的活儿交给大模型,让你和团队成员把精力留在真正需要讨论的业务逻辑上。它跑在 Java 项目里,拆出来一看结构非常清晰——SDK 负责解析与调用,测试工程负责演示,GitHub Actions 负责把评审流程自动化。适合谁用?一类是每天被 PR 评审淹没的团队负责人,另一类是刚把 CI 流程搭好、想往里加一道自动门禁的 Java 开发。它不解决架构评审这种需要上下文的大问题,但「空指针风险、资源未关闭、明显的代码坏味道」这类事,它查得比你快,也比你记得全面。
我实际拆包后在本地把 SDK 编译并挂到了一个 Spring Boot 测试工程上跑了一轮,能跑通,但它有几个非常隐蔽的坑,后面专门开一章写。先看结构。
2. 源码包拆解:34 个文件里,真正干活的是哪几个
2.1 先把包内文件按职责归一下类
我解开 upload.zip 后第一件事不是读代码,而是先列文件清单,把「能跑的」和「用来支持运行的」分开。整个包 34 个文件,Java 源文件 24 个占大头,其余是 4 个 XML、2 个 YAML、1 个 .gitignore、1 个 Markdown、1 个 Shell 脚本、1 个 Manifest。 разделить грубо,两套东西:一套叫openai-code-review-sdk,一套叫openai-code-review-test。
前者是核心,准确说是你要复用的那个 Jar 包工程;后者是被评审的样例工程,模拟一个真实 Maven 项目,SDK 跑起来就会扫它。外层.github/workflows放了两个 GitHub Actions 工作流,main-maven-jar.yml是打包 SDK 用的,main-local.yml是本地触发评审演示用的。docs 下面还有个curl-glm-4.sh,是绕过 SDK 直接调大模型接口的冒烟脚本——这个文件后面用处很大。
2.2 pom.xml 是整个包的命脉,先看它再定怎么改
无论你想用 SDK 的哪个能力,第一站都是openai-code-review-sdk/pom.xml。因为源码包依赖的是 OpenAI 兼容接口,而国内直连模型服务的网络链路不一定通,所以你要把自己的 API 地址、模型名、Token 配额都写在这个文件对应的配置里。
<properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <openai.api.base-url>http://你的模型服务地址/v1</openai.api.base-url> <openai.api.model>glm-4-flash</openai.api.model> </properties>这段是编译期参数。maven.compiler.source/target锁死 JDK 8,别在本地用高版本 JDK 编译低版本代码,后面避坑章节会讲原因。openai.api.base-url是关键,它指向一个 OpenAI 协议兼容的网关地址。如果你用的是智谱这类国内服务,协议是兼容的、路径要对齐到/v1,模型名按你已经开通的实例填,别直接抄仓库默认值。分发配置时,把这段交给有权限配密钥的人统一改,避免每个开发本地各写一套。
2.3 读代码的正确顺序:从 SDK 的入口类开始
先看核心接口。下面这段是 SDK 里最典型的入口类,我简化了包名,逻辑保留:
public class OpenAiCodeReviewService { private final IOpenAiApi openAiApi; private final String model; private final String promptTemplate; public OpenAiCodeReviewService(IOpenAiApi openAiApi, String model, String promptTemplate) { this.openAiApi = openAiApi; this.model = model; this.promptTemplate = promptTemplate; } public String review(String diffContent) { String prompt = promptTemplate.replace("${DIFF_CONTENT}", diffContent); ChatCompletionRequest request = new ChatCompletionRequest(); request.setModel(model); request.setMessages(Collections.singletonList( new ChatMessage("user", prompt) )); ChatCompletionResponse response = openAiApi.chatCompletion(request); return response.getChoices().get(0).getMessage().getContent(); } }review(String diffContent)是核心方法——你把git diff的内容作为字符串传进来,SDK 把它拼到 Prompt 模板里发给模型,返回评审意见。需要注意IOpenAiApi是个接口,源码包里给了基于 OkHttp 的实现,也有基于 Spring RestTemplate 的实现。如果你的项目已经用了 WebClient,建议自己包一层适配器,不要为了集成去改 SDK 内部代码,尽量保持只读。
接着看模板文件。所有 Prompt 集中在一个prompt-template.txt里,这就方便你调优评审风格,不用翻代码。
你是一位资深的Java代码评审专家。请对下面diff内容进行评审: 关注点:空指针风险、资源未关闭、并发安全、异常处理、代码坏味道。 输出格式为Markdown,按【问题】/【风险等级】/【建议】三段组织。 <code> ${DIFF_CONTENT} </code>这个模板是 SDK 的灵魂。看到它你就明白,AI 评审的结果在很大程度上不取决于模型本身,而取决于你给模型的关注点清单和输出格式约束。仓库默认模板偏保守,适合通用场景,但如果你想让它贴合团队规范,这儿是唯一值得长期维护的文件。
2.4 关键配置文件逐行看:为什么要分 XML 和 YAML
项目里有两种配置文件:XML 管构建周期,YAML 管运行参数。pom.xml负责声明依赖、插件和打包行为;application.yml(或类似命名的 YAML)管模型地址、密钥、Prompt 模板路径这些运行期要改的东西。我把两份关键配置合并成一张表:
| 文件 | 作用 | 维护时机 |
|---|---|---|
sdk/pom.xml | 依赖版本、Java 编译级别、Jar 打包插件 | 每次升级依赖或切换 JDK 时 |
test/pom.xml | 测试工程的构建配置、SDK 本地引入方式 | 引入 SDK 新版本时 |
application.yml | 模型 API 地址、模型名、超时时间 | 每次换环境或模型时 |
.github/workflows/*.yml | CI 里执行打包和评审的自动化流程 | 调评审触发条件时 |
你可以把 XML 理解成「每次构建都必须先跑的东西」,YAML 是「程序启动时读的参数」。分开放的原因很简单:换环境时只改 YAML 不动构建逻辑,避免把构建配置搞乱。
3. 把 SDK 挂到自己的 Java 工程:Maven 集成与首次调用
3.1 两种引入方式:本地 Jar 与源码模块
源码包给你提供了两种复用 SDK 的路径。第一种是直接install到本地 Maven 仓库,然后用坐标引用:
cd openai-code-review-sdk mvn clean install -DskipTests然后在业务工程的pom.xml里加依赖:
<dependency> <groupId>你的groupId</groupId> <artifactId>openai-code-review-sdk</artifactId> <version>1.0.0</version> </dependency>第二种是把 SDK 作为模块引入。我一般用第一种,理由很简单:SDK 是稳定层,不常改,单独打包发布更利于团队复用。如果你预期会频繁调整评审 Prompt 或 SDK 逻辑,那就用第二种,改完直接在本地重新install即可,不用反复发布私有仓库。
3.2 写一段能跑的调用代码
依赖挂好之后,写一个工具类调用评审,直接从 Git 拿 diff:
public class CodeReviewRunner { public static void main(String[] args) throws Exception { String apiKey = System.getenv("OPENAI_API_KEY"); String diff = getGitDiff(); IOpenAiApi api = new OkHttpOpenAiApi( "http://你的模型服务地址/v1", apiKey, 30 ); OpenAiCodeReviewService service = new OpenAiCodeReviewService( api, "glm-4-flash", loadPromptTemplate() ); String reviewResult = service.review(diff); System.out.println(reviewResult); } private static String getGitDiff() throws Exception { Process process = new ProcessBuilder( "git", "diff", "HEAD~1", "HEAD" ).start(); return new String(process.getInputStream().readAllBytes(), StandardCharsets.UTF_8); } }值得说明的有三处。getGitDiff这里用的是HEAD~1 HEAD,意思是拿最近一次提交的改动;如果你想扫工作区未提交的内容,得改成git diff不带参数。OkHttpOpenAiApi构造参数里的 30 是超时秒数,模型推理慢的时候很容易触发超时,建议调到 60 以上。最后,loadPromptTemplate()读取的是前面说的prompt-template.txt,这个文件被放在src/main/resources下,不要改路径。
3.3 用 docs 下的 Shell 脚本做一次冒烟验证
SDK 代码调试前,先用它自带的curl-glm-4.sh跑一次「最小链路验证」。脚本内容本质上就是手动调模型接口,我把它拆成关键片段:
curl -X POST "${API_BASE_URL}/chat/completions" \ -H "Authorization: Bearer ${API_KEY}" \ -H "Content-Type: application/json" \ -d '{ "model": "glm-4-flash", "messages": [ {"role": "user", "content": "你是一位Java代码评审专家。请评审以下代码:\n```java\npublic String getName(User u){ return u.getName(); }\n```"} ] }'这个脚本的价值在于:如果它都返回不了预期内容,那就别急着查 SDK 代码,先确认你的网络、API Key、模型名、接口路径是否匹配。我习惯把脚本里的API_BASE_URL和API_KEY抽到环境变量里,防止密钥随脚本提交到 Git 仓库。切记:凡是能跑通的调用都必须复用 SDK 的配置体系,别留一套跟 SDK 不一致的「手工验证配置」,不然排查问题时会很痛苦。
4. 接入 CI 做自动化评审:GitHub Actions 工作流怎么改
4.1 读懂 main-local.yml 的触发逻辑
main-local.yml是仓库作者用来本地演示的流程,但它的执行逻辑完全可以迁移到你自己的 CI 里。我贴出主体结构:
name: Local Code Review on: push: branches: [ master ] pull_request: branches: [ master ] jobs: code-review: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v3 - name: Set up JDK 8 uses: actions/setup-java@v3 with: java-version: '8' - name: Build SDK run: | cd openai-code-review-sdk mvn clean package -DskipTests - name: Run review env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} run: | cd openai-code-review-test mvn exec:java -Dexec.mainClass="com.example.CodeReviewRunner"on.push和on.pull_request决定了触发时机:推代码到 master 或创建 PR 时跑评审。最需要注意的两处:OPENAI_API_KEY从 GitHub Secrets 里注入,不要把密钥明文写在 YAML 里;actions/setup-java@v3显式指定 JDK 8,因为项目的源码编译级别是 8,CI 机器默认的 JDK 往往是 17 或 21,直接编会报错。如果你用的是内网 GitLab,这套逻辑同样适用——把 Steps 换成 GitLab CI 的script块就行。
4.2 评审结果如何回到 PR 页面
自动评审的价值只有在你「能看到结果」时才成立。.github/workflows里通常会配合 GitHub API 把评论写回对应提交或 PR,核心逻辑是:
curl -X POST \ -H "Authorization: token ${GITHUB_TOKEN}" \ -H "Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments \ -d "{\"body\": \"$(cat review-result.md)\"}"如果嫌直接写回 PR 太吵,也可以把结果上传为构建产物,等人手动下载查看——这个决策取决于团队的评审习惯。我见过一个团队的做法是把 AI 评审结果做成一个单独的 Markdown 文件挂到 PR 描述区,成员点名确认后再过门禁,这算比较温和又不丢审计记录的方案。
4.3 参数怎么调:门禁阈值与触发范围
Action 里可以加一个「拦截阈值」的概念,比默认的「只评论不拦截」更实用。在跑完评审后加一段判断逻辑:
# 如果评审结果包含"高危"字样且数量超过阈值,则让 CI 失败 ERROR_COUNT=$(grep -c "风险等级:高" review-result.md || true) if [ "$ERROR_COUNT" -gt 3 ]; then echo "高危问题超过3个,请修复后再提交" exit 1 figrep -c统计「风险等级:高」出现的次数,超过 3 就让构建失败。硬门禁适合改动频繁、质量要求高的核心仓库;宽松门禁适合内部项目,只出报告、不阻断合并。调试时先设宽松模式,观察一周看误报率是否可接受,再逐步收紧。
5. 避坑与常见问题:大模型评审的五个翻车点
5.1 现象:SDK 编译通过,评审结果却是空字符串
原因:模型返回的内容里包含choices数组为空的响应,常见于 Prompt 触发内容过滤或 key 额度不足。解决:在review()方法里加判空兜底。
if (response.getChoices() == null || response.getChoices().isEmpty()) { return "评审无结果:模型未返回有效内容,请检查API配额或Prompt。"; }这个改动虽然不起眼,但能避免 CI 流程拿到空字符串后继续向下游写空报告。顺手把model参数打一条日志(不要打 key),排错时你就知道是模型配置问题还是请求没发出去。
5.2 现象:git diff输出为空,但本地明明有改动
原因:ProcessBuilder执行git diff HEAD~1 HEAD时,如果仓库只有一个提交,HEAD~1不存在,命令直接报错。解决:先判断提交数量,不够就改用首次提交对比或直接扫工作区。我自己的习惯是:本地开发扫工作区(git diff),CI 场景扫提交区间。还要注意取值——git diff不加参数是工作区对比暂存区,不是对比上一次提交,语义差别很大。
5.3 现象:本地 JDK 17 编译 Maven 工程直接报错
原因:源码包 pom 里写死了maven.compiler.source/target=8,JDK 17 下编译低版本代码,旧版的maven-compiler-plugin不认识source/target 8。解决:要么装 JDK 8,要么在 pom 里升级插件版本并改用--release参数。优先装 JDK 8,因为改编译参数可能引入新的兼容性问题——依赖树里有些老库的字节码版本是 52.0,只能在 JDK 8 上稳定跑。
5.4 现象:API 调用超时,评审任务频繁中断
原因:模型推理时间不稳定,SDK 默认超时太短(我见过默认 10 秒的)。解决:把超时时间调到 60 秒,并增加失败重试。带超时的重试不是简单重复发请求,要看接口是否幂等——聊天补全接口一般来说重发问题不大,但要注意如果加了「最大 Token 数」,单次推理耗费额度会翻倍。重试次数加到 3 次就够,多了成本扛不住。
5.5 现象:评审报告跟实际代码对不上
原因:git diff只包含变动行,不带 10 行上下文,模型有时误把未变更的代码当成新写的。解决:生成 diff 时加-U 20参数,把上下文扩大。
git diff -U 20 HEAD~1 HEAD > diff-content.txt-U 20表示每条变更前后各包含 20 行上下文,模型能看到更完整的逻辑结构,误判率明显下降。代价是一次传给模型的 Token 数增加,成本变高。我的平衡点是:核心仓库传 30 行上下文,非核心传 10 行。
6. 读懂 AI 评审结果:把返回的 JSON 转成可落地的报告
跑通工具只是第一步,真正要练的功夫是把模型输出的非结构化文本转成结构化、可追溯的评审记录。SDK 返回的是纯文本 Markdown,但你可以让模型按固定 JSON 格式输出,然后解析成表格。在 Prompt 模板里追加一段:
请按以下JSON格式返回结果,不要输出额外解释: { "issues": [ { "line": 45, "severity": "high", "type": "NullPointerException风险", "suggestion": "..." } ] }然后代码里把返回内容解析成 JSON 数组:
ObjectMapper mapper = new ObjectMapper(); JsonNode root = mapper.readTree(reviewResult); JsonNode issues = root.get("issues"); for (JsonNode issue : issues) { int line = issue.get("line").asInt(); String severity = issue.get("severity").asText(); String suggestion = issue.get("suggestion").asText(); System.out.printf("第%d行 [%s] %s%n", line, severity, suggestion); }关键点是:告诉模型「不要输出额外解释」,这句能省掉大量解析容错代码。解析后可以按严重级别排序,或者跟静态扫描工具的结果做交叉比对。把问题行号作为实际链接拼接进 GitLab/GitHub 的文件行号地址,这才是能直接给团队看的评审报告。高严重度的问题单独抽出来并自动 @ 对应的提交者,是我目前比较顺手的习惯。
跑通这个链路之后,做「 AI 评审 + 静态扫描双轨制」会比较省力:静态扫描负责规则类问题,AI 评审负责上下文相关的逻辑风险。从那以后我每次接入新项目的 CI,都强制把「先跑 curl 脚本验证连通性、再挂 SDK、最后接门禁阈值」这套流程走一遍,不跳过任何一步。希望这套方法对你也有帮助。
本文还有配套的精品资源,点击获取