ARTICLE · 1035336
AI Agent 工程化实战(十):实战——代码审查 Agent 完整项目
AI Agent 工程化实战(十):实战——代码审查 Agent 完整项目
大家好,我是老 J。
上期我们完成了企业客服 Agent,这期来做第二个实战项目:代码审查 Agent。
这个项目解决的问题很实际:每次提交代码,人工审查耗时耗力,能不能让 AI 先审一遍?
答案是能。而且我们上期讲的多 Agent 协作、工具系统、可观测性,在这个项目里全部用上。
一、需求分析
1.1 业务场景
一个研发团队,每天有几十个 PR 需要审查。人工审查的问题:
1.2 核心需求
• 多维度审查:安全、质量、性能三个维度 • 自动化触发:PR 提交时自动审查 • 结构化输出:结果按严重程度排序 • 可集成:接入 CI/CD 流程
二、架构设计
┌─────────────────────────────────────────────────────────────────┐
│ 代码审查 Agent 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Git Push ──► CI/CD 触发 ──► 代码审查服务 │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ 安全审查 │ │ 质量审查 │ │ 性能审查 │ │
│ │ Agent │ │ Agent │ │ Agent │ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
│ └───────────────┼───────────────┘ │
│ ▼ │
│ ┌─────────────┐ │
│ │ 汇总报告 │ │
│ │ (Markdown) │ │
│ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘三、项目结构
code-review-agent/
├── pom.xml
├── src/main/java/com/example/review/
│ ├── CodeReviewApplication.java
│ ├── config/
│ │ └── ReviewAgentConfig.java
│ ├── controller/
│ │ └── ReviewController.java
│ ├── service/
│ │ ├── CodeReviewService.java
│ │ ├── ReviewAggregator.java
│ │ └── GitService.java
│ ├── agent/
│ │ ├── SecurityReviewAgent.java
│ │ ├── QualityReviewAgent.java
│ │ └── PerformanceReviewAgent.java
│ └── model/
│ ├── ReviewRequest.java
│ ├── ReviewResult.java
│ └── ReviewReport.java
└── src/main/resources/
└── application.yml四、核心代码实现
4.1 Maven 依赖
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>6.7.0.202309050840-r</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>4.2 三个审查 Agent
@Component
publicclassSecurityReviewAgent {
privatefinal ChatClient chatClient;
publicSecurityReviewAgent(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("""
你是安全审查专家,负责检查代码中的安全漏洞。
重点关注:
1. SQL 注入(拼接 SQL、未使用参数化查询)
2. XSS 攻击(未转义的输出)
3. 敏感信息硬编码(密码、密钥、token)
4. 权限校验缺失
5. 不安全的反序列化
6. 路径遍历
7. SSRF 攻击
输出格式(JSON):
{
"risks": [
{"level": "高/中/低", "line": 行号, "issue": "问题描述", "suggestion": "修复建议"}
],
"summary": "总体评价"
}
""")
.build();
}
public String review(String code) {
return chatClient.prompt("审查以下代码:\n```\n" + code + "\n```")
.call().content();
}
}@Component
publicclassQualityReviewAgent {
privatefinal ChatClient chatClient;
publicQualityReviewAgent(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("""
你是代码质量审查专家。
重点关注:
1. 命名规范(是否清晰、有意义)
2. 方法长度(超过 50 行需拆分)
3. 嵌套深度(超过 3 层需重构)
4. 重复代码
5. 注释完整性
6. 异常处理
7. 魔法数字
输出格式(JSON):
{
"issues": [
{"level": "高/中/低", "line": 行号, "issue": "问题描述", "suggestion": "改进建议"}
],
"summary": "总体评价"
}
""")
.build();
}
public String review(String code) {
return chatClient.prompt("审查以下代码:\n```\n" + code + "\n```")
.call().content();
}
}@Component
publicclassPerformanceReviewAgent {
privatefinal ChatClient chatClient;
publicPerformanceReviewAgent(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("""
你是性能审查专家。
重点关注:
1. N+1 查询(循环中查数据库)
2. 不必要的对象创建
3. 资源未释放(连接、流)
4. 算法复杂度
5. 缓存使用不当
6. 大循环中的耗时操作
输出格式(JSON):
{
"issues": [
{"level": "高/中/低", "line": 行号, "issue": "问题描述", "suggestion": "优化建议"}
],
"summary": "总体评价"
}
""")
.build();
}
public String review(String code) {
return chatClient.prompt("审查以下代码:\n```\n" + code + "\n```")
.call().content();
}
}4.3 审查服务(并行执行)
@Service
publicclassCodeReviewService {
privatefinal SecurityReviewAgent securityAgent;
privatefinal QualityReviewAgent qualityAgent;
privatefinal PerformanceReviewAgent performanceAgent;
publicCodeReviewService(SecurityReviewAgent securityAgent,
QualityReviewAgent qualityAgent,
PerformanceReviewAgent performanceAgent) {
this.securityAgent = securityAgent;
this.qualityAgent = qualityAgent;
this.performanceAgent = performanceAgent;
}
public ReviewReport review(String code) {
// 并行执行三个维度的审查
CompletableFuture<String> securityFuture =
CompletableFuture.supplyAsync(() -> securityAgent.review(code));
CompletableFuture<String> qualityFuture =
CompletableFuture.supplyAsync(() -> qualityAgent.review(code));
CompletableFuture<String> performanceFuture =
CompletableFuture.supplyAsync(() -> performanceAgent.review(code));
// 等待全部完成
CompletableFuture.allOf(securityFuture, qualityFuture, performanceFuture).join();
// 汇总
ReviewReportreport=newReviewReport();
try {
report.setSecurityReview(securityFuture.get());
report.setQualityReview(qualityFuture.get());
report.setPerformanceReview(performanceFuture.get());
} catch (Exception e) {
thrownewRuntimeException("审查失败", e);
}
return report;
}
}4.4 报告聚合器
@Service
publicclassReviewAggregator {
public String aggregate(ReviewReport report) {
StringBuildersb=newStringBuilder();
sb.append("# 代码审查报告\n\n");
sb.append("生成时间:").append(LocalDateTime.now()).append("\n\n");
sb.append("## 🔒 安全审查\n\n");
sb.append(report.getSecurityReview()).append("\n\n");
sb.append("## 📐 质量审查\n\n");
sb.append(report.getQualityReview()).append("\n\n");
sb.append("## ⚡ 性能审查\n\n");
sb.append(report.getPerformanceReview()).append("\n\n");
sb.append("---\n");
sb.append("*本报告由 AI 代码审查 Agent 生成,仅供参考。*\n");
return sb.toString();
}
public ReviewSummary summarize(ReviewReport report) {
// 统计各维度问题数量
intsecurityIssues= countIssues(report.getSecurityReview());
intqualityIssues= countIssues(report.getQualityReview());
intperformanceIssues= countIssues(report.getPerformanceReview());
return ReviewSummary.builder()
.totalIssues(securityIssues + qualityIssues + performanceIssues)
.securityIssues(securityIssues)
.qualityIssues(qualityIssues)
.performanceIssues(performanceIssues)
.build();
}
privateintcountIssues(String reviewJson) {
// 解析 JSON,统计 issues 数组长度
try {
ObjectMappermapper=newObjectMapper();
JsonNodenode= mapper.readTree(reviewJson);
JsonNodeissues= node.get("issues");
return issues != null ? issues.size() : 0;
} catch (Exception e) {
return0;
}
}
}4.5 控制器
@RestController
@RequestMapping("/api/review")
publicclassReviewController {
privatefinal CodeReviewService reviewService;
privatefinal ReviewAggregator aggregator;
publicReviewController(CodeReviewService reviewService,
ReviewAggregator aggregator) {
this.reviewService = reviewService;
this.aggregator = aggregator;
}
@PostMapping("/code")
public Map<String, Object> reviewCode(@RequestBody ReviewRequest request) {
longstart= System.currentTimeMillis();
ReviewReportreport= reviewService.review(request.getCode());
Stringmarkdown= aggregator.aggregate(report);
ReviewSummarysummary= aggregator.summarize(report);
longduration= System.currentTimeMillis() - start;
return Map.of(
"summary", summary,
"report", markdown,
"duration_ms", duration
);
}
@PostMapping("/file")
public Map<String, Object> reviewFile(@RequestBody ReviewRequest request) {
try {
Stringcode= Files.readString(Paths.get(request.getFilePath()));
request.setCode(code);
return reviewCode(request);
} catch (Exception e) {
return Map.of("error", "读取文件失败: " + e.getMessage());
}
}
}4.6 请求/响应模型
@Data
publicclassReviewRequest {
private String code;
private String filePath;
privateStringlanguage="java";
}
@Data
@Builder
publicclassReviewSummary {
privateint totalIssues;
privateint securityIssues;
privateint qualityIssues;
privateint performanceIssues;
}
@Data
publicclassReviewReport {
private String securityReview;
private String qualityReview;
private String performanceReview;
}五、集成 CI/CD
5.1 GitHub Actions 配置
name:AICodeReview
on:
pull_request:
branches: [main]
jobs:
review:
runs-on:ubuntu-latest
steps:
-uses:actions/checkout@v4
with:
fetch-depth:0
-name:Getchangedfiles
id:changed
run:|
git diff --name-only origin/main...HEAD > changed_files.txt
echo "files=$(cat changed_files.txt | tr '\n' ' ')" >> $GITHUB_OUTPUT
-name:RunAIReview
run:|
curl -X POST ${{ secrets.REVIEW_SERVICE_URL }}/api/review/code \
-H "Content-Type: application/json" \
-d "{\"code\": \"$(cat changed_files.txt)\"}"5.2 输出到 PR 评论
// 在控制器中添加 GitHub PR 评论
@PostMapping("/pr")
public Map<String, Object> reviewPR(@RequestBody Map<String, String> request) {
StringprUrl= request.get("prUrl");
Stringcode= request.get("code");
ReviewReportreport= reviewService.review(code);
Stringmarkdown= aggregator.aggregate(report);
// 通过 GitHub API 提交评论
// ... 具体实现参考 GitHub API 文档
return Map.of("status", "reviewed", "report", markdown);
}六、测试验证
6.1 测试脚本
#!/bin/bash
BASE_URL="http://localhost:8080"
echo"1️⃣ 审查代码"
curl -s -X POST $BASE_URL/api/review/code \
-H "Content-Type: application/json" \
-d '{
"code": "public void transfer(String from, String to, int amount) {\n String sql = \"UPDATE account SET balance = balance - \" + amount + \" WHERE id = \" + from;\n jdbcTemplate.execute(sql);\n}"
}' | jq '.summary'6.2 预期输出
{
"summary":{
"totalIssues":5,
"securityIssues":3,
"qualityIssues":1,
"performanceIssues":1
},
"report":"# 代码审查报告\n\n## 🔒 安全审查\n\n风险:SQL 注入...",
"duration_ms":3500
}七、系列总结
十期 AI Agent 工程化实战到此全部完结:
我是老 J,Agent 工程化系列十期完结,下个系列见。