多智能体代码审查引擎架构设计与实战:从零构建基于AI Agent的代码审查系统 各位下午好。在日常的软件开发流程里代码审查一直是保障工程质量的核心环节。但说实话传统的人工 Review 方式既耗费资深工程师大量时间又容易出现“盯了半天没发现问题合并之后线上炸了”的尴尬状况。所以当看到 Uber 分享的 uReview 多智能体代码审查引擎相关思路时我的第一反应是终于有大厂把 AI Agent 往这个深水区推进了。这篇内容不是去拆解 Uber 内部某个具体版本的功能清单而是从工程落地角度完整拆解一个“多智能体Multi-Agent代码审查系统”该怎么设计、怎么实现、怎么避坑。不管你是后端开发、平台工程师还是对 AI Engineer 方向感兴趣的同学这篇文章都能给你一套可执行的架构蓝图和代码骨架。1. 背景与核心概念1.1 为什么传统代码审查需要被重构先讲一个实际场景。假设一个中大型项目每次 Pull Request 平均改动 300 到 500 行代码。人工审查时Reviewer 需要关注业务逻辑是否正确是否有潜在的并发安全问题异常处理是否覆盖了边界条件新引入的依赖是否有漏洞代码风格是否与团队规范一致数据库索引和查询是否存在性能隐患。这么多检查维度靠人肉逐一核对效率极低。更麻烦的是不同背景的 Reviewer 关注点不一样有人擅长业务、有人擅长性能很难有一个“全能型”审查者。而 uReview 这类系统的核心思路就是把代码审查拆分成多个独立任务分配给不同的智能体Agent并行负责。每个 Agent 只关注一个维度最后由一个聚合模块统一汇总、过滤、排序生成一份高质量且可执行的审查意见。1.2 什么是多智能体系统多智能体系统Multi-Agent SystemMAS是人工智能领域的一个重要方向。通俗理解就是让多个具备独立推理能力的 AI Agent 协同工作像一支分工明确的团队一样完成复杂任务。和单智能体模式相比多智能体模式有四个明显优势专业化分工每个 Agent 专注一个方向提示词Prompt可以设计得更加精准并行计算多个独立的审查任务可以并发执行缩短整体耗时降低幻觉概率每个 Agent 只处理它擅长的问题输出质量更高流程可编排可以像流水线一样编排 Agent 的执行顺序和依赖关系。放到代码审查场景里就可以设计成安全审查 Agent、性能审查 Agent、并发正确性审查 Agent、代码风格审查 Agent、依赖漏洞审查 Agent最后再来一个 Review 汇总 Agent。1.3 uReview 类系统在整个研发流程中的位置uReview 并不是要取代人工 Reviewer而是把机器擅长的“确定性检查”和“模式识别”做到极致把人类从重复劳动中解放出来让工程师把精力集中在架构设计、业务合理性、可维护性这些真正需要人类判断的问题上。它在研发流程中的位置大致如下代码提交 → CI 触发 → 拉取 Diff 数据 → 多智能体并行审查 → 汇总生成审查报告 → 回写到 PR/MR 评论区 → 工程师处理意见从 AI Engineer 的角度看这本质上是一个 RAG Agent 工作流编排的综合工程问题。2. 多智能体代码审查引擎的整体架构在设计一个多智能体代码审查引擎时我的习惯是先画清楚架构再写代码。为了避免依赖具体的内部实现这篇文章做的是通用化的架构设计你可以直接套用到自己的项目里。架构上分为五层分别是输入接入层、代码分析层、智能体编排层、上下文管理层、反馈输出层。2.1 输入接入层负责对接不同的代码托管平台比如 GitHub、GitLab、Gitee以及企业内部自建的代码仓库。核心任务是监听合并请求Merge Request / Pull Request事件拉取变更文件列表获取每个文件的 Diff 内容获取提交信息、评论信息等元数据。这一层更推荐使用 Webhook 加消息队列的方式实现。Webhook 负责事件通知消息队列负责削峰填谷避免代码提交高峰期把下游服务冲垮。# 以 GitLab Webhook 为例推送触发地址 POST http://your-review-system/api/webhook/gitlab2.2 代码分析层这一层不是直接用大模型去读全部代码而是先用传统静态分析工具把代码做一轮预处理。主要工作包括解析改动文件的语言类型提取变更的函数、类、方法识别新增的依赖项扫描明显的基础问题比如语法错误、未使用变量、明显的空指针风险。为什么要先做这一层原因很简单大模型有上下文窗口限制也不可能真的编译代码。如果能把代码结构提前解析成结构化数据再喂给 Agent效果会稳定很多。常用的静态分析工具有 ESLintJavaScript/TypeScript、PylintPython、SpotBugsJava、SonarQube 等。2.3 智能体编排层这是整个系统的核心。编排层会启动多个细分的审查 Agent例如Agent 名称审查方向关注点安全审查 Agent安全性SQL 注入、XSS、硬编码密钥、越权访问并发审查 Agent并发与线程安全共享变量、锁的用法、线程池配置性能审查 Agent性能循环内查询数据库、大对象复制、索引使用异常处理审查 Agent健壮性吞异常、空指针、边界条件缺失代码风格审查 Agent可读性命名、函数长度、重复代码依赖审查 Agent供应链安全新引入依赖的版本漏洞、许可证问题每个 Agent 可以基于不同的 Prompt 模板运行也可以针对不同语言配置不同的审查规则。2.4 上下文管理层大模型上下文窗口资源非常宝贵不可能把所有代码都塞进去。所以需要做上下文裁剪与检索增强只提取变更函数的前后 20 行代码作为上下文按语义相似度检索仓库内相近实现作为参考把团队自定义规范向量化存储按需注入对超过窗口的内容采用分块摘要后再聚合。2.5 反馈输出层各 Agent 执行完毕后返回的是结构化结果。需要一个汇总 Agent 做二次加工合并重复问题按严重程度排序自动生成修改建议过滤低价值评论以评论或报告形式回写到 PR 页面。3. 环境准备与项目结构说明3.1 技术选型本文示例采用以下技术组合来演示一个最小可运行的多智能体代码审查引擎组件选型说明开发语言Java 17使用 Spring Boot 3.x 搭建服务AI 模型接入OpenAI 兼容接口通过 HTTP 调用不绑定具体厂商编排框架Spring AI / 自研编排用代码手动编排多智能体协作流程消息队列先用内存队列演示阶段不做复杂依赖存储H2 内存数据库存储任务记录与审查结果构建工具Maven统一依赖管理实际生产项目中这套架构可以很方便地替换为更专业的组件比如 LangChain4j、Ray、Temporal或者公司内部自研的 Agent 编排平台。3.2 示例项目目录结构先创建一个 Maven 工程目录结构如下ureview-demo/ ├── pom.xml ├── src/main/java/com/example/ureview/ │ ├── UReviewApplication.java │ ├── controller/ │ │ └── ReviewController.java │ ├── model/ │ │ ├── ReviewRequest.java │ │ ├── DiffFile.java │ │ ├── AgentResult.java │ │ └── ReviewReport.java │ ├── agent/ │ │ ├── AgentExecutor.java │ │ ├── SecurityReviewAgent.java │ │ ├── PerformanceReviewAgent.java │ │ ├── ConcurrencyReviewAgent.java │ │ └── SummaryAgent.java │ ├── service/ │ │ ├── DiffParserService.java │ │ └── ReviewOrchestrator.java │ └── config/ │ └── LlmConfig.java └── src/main/resources/ └── application.yml4. 完整实战从零构建一个多智能体代码审查服务4.1 创建 Maven 工程并添加依赖在pom.xml中添加核心依赖?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.3.0/version relativePath/ /parent groupIdcom.example/groupId artifactIdureview-demo/artifactId version1.0.0-SNAPSHOT/version nameureview-demo/name descriptionMulti-Agent Code Review Engine Demo/description properties java.version17/java.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.squareup.okhttp3/groupId artifactIdokhttp/artifactId version4.12.0/version /dependency dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId /dependency dependency groupIdcom.h2database/groupId artifactIdh2/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build /project这里需要说明一点为了避免绑定某个具体大模型厂商我在示例中用 OkHttp 直接调用兼容 OpenAI 协议的 HTTP 接口。你可以自行配置 base-url 和 api-key指向本地部署的模型服务或者任何兼容的服务。4.2 编写核心配置类在application.yml中配置模型服务和线程池参数server: port: 8080 llm: base-url: https://your-llm-service.example.com/v1 api-key: ${LLM_API_KEY:sk-demo} model: deepseek-chat temperature: 0.2 max-tokens: 2000 review: thread-pool-size: 4 max-diff-size: 50000注意LLM_API_KEY使用了环境变量注入这是生产环境的标准做法密钥不允许硬编码在配置文件里。4.3 定义数据模型首先定义统一的审查请求对象// 文件路径src/main/java/com/example/ureview/model/ReviewRequest.java package com.example.ureview.model; import lombok.Data; import java.util.List; Data public class ReviewRequest { private String repoName; private String branchName; private String commitId; private ListDiffFile diffFiles; }定义变更文件对象// 文件路径src/main/java/com/example/ureview/model/DiffFile.java package com.example.ureview.model; import lombok.Data; Data public class DiffFile { private String filePath; private String changeType; private String patch; private String language; }定义 Agent 的审查结果// 文件路径src/main/java/com/example/ureview/model/AgentResult.java package com.example.ureview.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; Data NoArgsConstructor AllArgsConstructor public class AgentResult { private String agentName; private ListString issues; }定义最终审查报告// 文件路径src/main/java/com/example/ureview/model/ReviewReport.java package com.example.ureview.model; import lombok.Data; import java.util.List; Data public class ReviewReport { private String repoName; private String commitId; private String summary; private ListAgentResult details; }4.4 实现 LLM 调用封装这一层负责与大模型交互是所有 Agent 的基础能力。// 文件路径src/main/java/com/example/ureview/config/LlmConfig.java package com.example.ureview.config; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import okhttp3.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeUnit; Component public class LlmConfig { private final OkHttpClient httpClient; private final ObjectMapper objectMapper; Value(${llm.base-url}) private String baseUrl; Value(${llm.api-key}) private String apiKey; Value(${llm.model}) private String model; Value(${llm.temperature}) private double temperature; public LlmConfig() { this.httpClient new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(60, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS) .build(); this.objectMapper new ObjectMapper(); } /** * 调用兼容 OpenAI 协议的接口 */ public String chatCompletion(String systemPrompt, String userPrompt) throws IOException { ObjectNode requestBody objectMapper.createObjectNode(); requestBody.put(model, model); requestBody.put(temperature, temperature); ArrayNode messages requestBody.putArray(messages); ObjectNode systemMessage messages.addObject(); systemMessage.put(role, system); systemMessage.put(content, systemPrompt); ObjectNode userMessage messages.addObject(); userMessage.put(role, user); userMessage.put(content, userPrompt); Request request new Request.Builder() .url(baseUrl /chat/completions) .addHeader(Authorization, Bearer apiKey) .addHeader(Content-Type, application/json) .post(RequestBody.create( requestBody.toString(), MediaType.parse(application/json; charsetutf-8) )) .build(); try (Response response httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException(LLM API error: response.code() response.body().string()); } String responseBody response.body().string(); JsonNode root objectMapper.readTree(responseBody); return root.path(choices).path(0).path(message).path(content).asText(); } } }这段代码的核心逻辑是构造 OpenAI 兼容的 Chat Completion 请求把 System Prompt 和 User Prompt 发给模型然后返回模型生成的文本。所有 Agent 都会复用这个方法。4.5 实现 Diff 解析服务真实场景中代码托管平台返回的 Diff 格式是需要解析的。本文做一个简化版把 patch 内容按文件整理成可读文本。// 文件路径src/main/java/com/example/ureview/service/DiffParserService.java package com.example.ureview.service; import com.example.ureview.model.DiffFile; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; Service public class DiffParserService { /** * 将 Diff 文件列表转换为可供 Agent 阅读的格式化文本 */ public String toAgentInput(ListDiffFile diffFiles) { return diffFiles.stream() .map(this::formatSingleFile) .collect(Collectors.joining(\n\n)); } private String formatSingleFile(DiffFile diffFile) { StringBuilder sb new StringBuilder(); sb.append(### 文件: ).append(diffFile.getFilePath()).append(\n); sb.append(变更类型: ).append(diffFile.getChangeType()).append(\n); sb.append().append(diffFile.getLanguage() null ? : diffFile.getLanguage()).append(\n); sb.append(diffFile.getPatch()).append(\n); sb.append(\n); return sb.toString(); } }4.6 实现 Agent 执行器这是多智能体最核心的部分。先定义一个抽象执行器子类只需要提供各自的 System Prompt。// 文件路径src/main/java/com/example/ureview/agent/AgentExecutor.java package com.example.ureview.agent; import com.example.ureview.config.LlmConfig; import com.example.ureview.model.AgentResult; import com.example.ureview.model.DiffFile; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * 所有 Agent 的抽象基类。 * 子类只需要指定 agentType 和对应的 systemPrompt。 */ public abstract class AgentExecutor { protected final LlmConfig llmConfig; protected AgentExecutor(LlmConfig llmConfig) { this.llmConfig llmConfig; } public abstract String agentType(); protected abstract String systemPrompt(); protected abstract String userPromptTemplate(); public AgentResult execute(ListDiffFile diffFiles) throws Exception { String diffContent diffFiles.stream() .map(this::formatDiff) .collect(Collectors.joining(\n)); String userPrompt String.format(userPromptTemplate(), diffContent); String modelOutput llmConfig.chatCompletion(systemPrompt(), userPrompt); // 解析模型输出为问题列表。每行一个 issue以 - 开头。 ListString issues Arrays.stream(modelOutput.split(\n)) .map(String::trim) .filter(line - line.startsWith(- )) .map(line - line.substring(2).trim()) .collect(Collectors.toList()); return new AgentResult(agentType(), issues); } private String formatDiff(DiffFile diffFile) { return 文件: diffFile.getFilePath() \n diffFile.getPatch(); } }这里的关键设计是让模型以固定格式输出也就是每行一个以-开头的问题。这种结构化输出方式比让模型自由发挥可靠得多。4.7 实现核心审查 Agent安全审查 Agent// 文件路径src/main/java/com/example/ureview/agent/SecurityReviewAgent.java package com.example.ureview.agent; import com.example.ureview.config.LlmConfig; import org.springframework.stereotype.Component; Component public class SecurityReviewAgent extends AgentExecutor { public SecurityReviewAgent(LlmConfig llmConfig) { super(llmConfig); } Override public String agentType() { return SecurityReviewAgent; } Override protected String systemPrompt() { return 你是一名资深的安全工程师专注于代码安全审查。 你擅长发现 SQL 注入、跨站脚本攻击XSS、硬编码密钥、不安全的反序列化、越权访问等安全问题。 请基于给定的代码变更内容进行分析。; } Override protected String userPromptTemplate() { return 请审查以下代码变更找出所有可能的安全问题。\n 要求\n 1. 只输出你发现的问题不输出没有问题的内容。\n 2. 每个问题单独一行以 \\\- \\\ 开头。\n 3. 格式- [严重级别: 高/中/低] 问题描述\n\n 代码变更内容\n%s; } }性能审查 Agent// 文件路径src/main/java/com/example/ureview/agent/PerformanceReviewAgent.java package com.example.ureview.agent; import com.example.ureview.config.LlmConfig; import org.springframework.stereotype.Component; Component public class PerformanceReviewAgent extends AgentExecutor { public PerformanceReviewAgent(LlmConfig llmConfig) { super(llmConfig); } Override public String agentType() { return PerformanceReviewAgent; } Override protected String systemPrompt() { return 你是一名资深的性能优化专家。 你擅长发现循环内查询数据库、N1 查询问题、不必要的对象创建、大对象复制、缺少索引等性能隐患。 请基于给定的代码变更内容进行分析。; } Override protected String userPromptTemplate() { return 请审查以下代码变更找出所有可能的性能问题。\n 要求\n 1. 只输出你发现的问题不输出没有问题的内容。\n 2. 每个问题单独一行以 \\\- \\\ 开头。\n 3. 格式- [严重级别: 高/中/低] 问题描述\n\n 代码变更内容\n%s; } }并发安全审查 Agent// 文件路径src/main/java/com/example/ureview/agent/ConcurrencyReviewAgent.java package com.example.ureview.agent; import com.example.ureview.config.LlmConfig; import org.springframework.stereotype.Component; Component public class ConcurrencyReviewAgent extends AgentExecutor { public ConcurrencyReviewAgent(LlmConfig llmConfig) { super(llmConfig); } Override public String agentType() { return ConcurrencyReviewAgent; } Override protected String systemPrompt() { return 你是一名精通 Java 并发编程的专家。 你擅长识别线程安全问题例如共享可变状态、缺少同步、锁顺序不一致、线程池使用不当、死锁风险等。 请基于给定的代码变更内容进行分析。; } Override protected String userPromptTemplate() { return 请审查以下代码变更找出所有可能的并发或线程安全问题。\n 要求\n 1. 只输出你发现的问题不输出没有问题的内容。\n 2. 每个问题单独一行以 \\\- \\\ 开头。\n 3. 格式- [严重级别: 高/中/低] 问题描述\n\n 代码变更内容\n%s; } }4.8 实现汇总 Agent汇总 Agent 不直接审查代码而是对多个 Agent 的结果做合并和排序。// 文件路径src/main/java/com/example/ureview/agent/SummaryAgent.java package com.example.ureview.agent; import com.example.ureview.config.LlmConfig; import com.example.ureview.model.AgentResult; import org.springframework.stereotype.Component; import java.util.List; import java.util.stream.Collectors; Component public class SummaryAgent { private final LlmConfig llmConfig; public SummaryAgent(LlmConfig llmConfig) { this.llmConfig llmConfig; } /** * 汇总多个 Agent 的结果生成最终审查报告摘要 */ public String summarize(String repoName, ListAgentResult agentResults) throws Exception { String systemPrompt 你是一名高级代码审查协调者。你的职责是汇总多名专家 Agent 的审查结果 生成一份清晰、可执行的最终审查报告。; String agentOutput agentResults.stream() .map(result - ### result.getAgentName() \n result.getIssues().stream() .map(issue - - issue) .collect(Collectors.joining(\n))) .collect(Collectors.joining(\n\n)); String userPrompt 以下是多个审查 Agent 对仓库 repoName 代码变更的审查结果\n\n agentOutput \n\n请生成最终审查报告包括\n 1. 按严重程度从高到低排列问题。\n 2. 去掉重复问题。\n 3. 提出修改建议。\n 4. 以 Markdown 格式输出。; return llmConfig.chatCompletion(systemPrompt, userPrompt); } }4.9 实现核心编排服务这是整个系统的执行中枢。编排器的职责是获取所有 Agent按配置并行执行最后统一汇总。// 文件路径src/main/java/com/example/ureview/service/ReviewOrchestrator.java package com.example.ureview.service; import com.example.ureview.agent.AgentExecutor; import com.example.ureview.agent.SummaryAgent; import com.example.ureview.model.AgentResult; import com.example.ureview.model.DiffFile; import com.example.ureview.model.ReviewReport; import com.example.ureview.model.ReviewRequest; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.*; Service public class ReviewOrchestrator { private final DiffParserService diffParserService; private final ListAgentExecutor agentExecutors; private final SummaryAgent summaryAgent; private final ExecutorService executorService; Value(${review.max-diff-size}) private int maxDiffSize; public ReviewOrchestrator( DiffParserService diffParserService, ListAgentExecutor agentExecutors, SummaryAgent summaryAgent, Value(${review.thread-pool-size}) int threadPoolSize) { this.diffParserService diffParserService; this.agentExecutors agentExecutors; this.summaryAgent summaryAgent; this.executorService Executors.newFixedThreadPool(threadPoolSize); } /** * 编排多智能体审查流程 */ public ReviewReport runReview(ReviewRequest request) throws InterruptedException { ListDiffFile diffFiles filterLargeFiles(request.getDiffFiles()); // 第一步并行启动所有 Agent ListFutureAgentResult futures new ArrayList(); for (AgentExecutor agent : agentExecutors) { FutureAgentResult future executorService.submit( () - agent.execute(diffFiles) ); futures.add(future); } // 第二步收集所有 Agent 的结果 ListAgentResult agentResults new ArrayList(); for (FutureAgentResult future : futures) { try { agentResults.add(future.get(120, TimeUnit.SECONDS)); } catch (ExecutionException e) { agentResults.add(new AgentResult( ErrorAgent, Collections.singletonList(Agent 执行失败: e.getCause().getMessage()) )); } catch (TimeoutException e) { agentResults.add(new AgentResult( ErrorAgent, Collections.singletonList(Agent 执行超时已跳过。) )); } } // 第三步调用汇总 Agent String summary ; try { summary summaryAgent.summarize(request.getRepoName(), agentResults); } catch (Exception e) { summary 摘要生成失败请人工查看详细结果。原因: e.getMessage(); } ReviewReport report new ReviewReport(); report.setRepoName(request.getRepoName()); report.setCommitId(request.getCommitId()); report.setSummary(summary); report.setDetails(agentResults); return report; } private ListDiffFile filterLargeFiles(ListDiffFile diffFiles) { return diffFiles.stream() .filter(file - file.getPatch() null || file.getPatch().length() maxDiffSize) .toList(); } }这段代码体现了多智能体编排的核心要点通过ListAgentExecutor自动注入所有 Agent 实例后续新增 Agent 时不需要修改编排器代码采用线程池并发执行避免串行耗时对异常和超时做了兜底防止单个 Agent 故障拖垮整个审查流程对超大文件做了过滤防止上下文溢出。4.10 编写 Web 控制器最后提供一个 HTTP 入口方便测试调用。// 文件路径src/main/java/com/example/ureview/controller/ReviewController.java package com.example.ureview.controller; import com.example.ureview.model.ReviewReport; import com.example.ureview.model.ReviewRequest; import com.example.ureview.service.ReviewOrchestrator; import org.springframework.web.bind.annotation.*; RestController RequestMapping(/api/review) public class ReviewController { private final ReviewOrchestrator orchestrator; public ReviewController(ReviewOrchestrator orchestrator) { this.orchestrator orchestrator; } PostMapping(/run) public ReviewReport runReview(RequestBody ReviewRequest request) throws InterruptedException { return orchestrator.runReview(request); } GetMapping(/health) public String health() { return uReview demo is running.; } }4.11 运行与验证启动应用mvn spring-boot:run然后在另一个终端窗口模拟一次审查请求curl -X POST http://localhost:8080/api/review/run \ -H Content-Type: application/json \ -d { repoName: demo-project, branchName: feature/user-login, commitId: a1b2c3d4, diffFiles: [ { filePath: src/main/java/com/demo/UserService.java, changeType: modified, language: java, patch: -10,15 10,18 \n public class UserService {\n public User login(String username, String password) {\n String sql \SELECT * FROM users WHERE name \ username \ AND password \ password \\;\n return jdbcTemplate.query(sql, userRowMapper);\n }\n } } ] }如果配置正确模型会返回安全 Agent 发现的 SQL 注入问题以及其他 Agent 的分析结果。这里要强调一个很重要的现实不同模型对复杂推理任务的表现差异很大。如果你使用的是参数量较小或能力较弱的模型Agent 可能无法发现细节问题。在生产环境中建议针对目标代码语言和审查维度做 Prompt 调优并通过样例评测集来控制输出质量。5. 多智能体系统的核心架构与运行原理解析这一段回到更一般化的视角。在做 uReview 这类系统时我们实际上是在实现一个通用的多智能体系统。它的核心架构与运行原理可以拆成四个关键点。5.1 任务分解与角色设计多智能体系统能够工作的前提是被执行的“任务”能够被清晰地拆解为多个子任务。在代码审查场景中任务分解相对容易因为审查维度是相对清晰的。但如果是更开放的任务比如“重构这个模块”就必须由上层规划 Agent 先做任务分析得出子任务清单再分发给下层执行 Agent。任务分解完成后每个 Agent 需要有自己的角色定义Role包括职责范围输入输出格式可调用的工具集合行为约束。HR 领域有个概念叫“职位说明书”Agent 的角色定义本质上就是一份给大模型的职位说明书。定义得越清楚Agent 的行为就越可控。5.2 上下文管理策略多智能体系统的运行瓶颈通常不是模型推理而是上下文管理。每个 Agent 在运行时需要读取大量的上下文用户需求、代码仓库结构、相关历史代码、团队规范、项目文档。但上下文窗口是有限的所以需要做取舍。常见的策略有即时裁剪只保留当前 Agent 完成任务所需的最小片段层级摘要先对文档做摘要按需展开详细内容向量检索用 Embedding 检索最相关的代码片段外部记忆把历史审查记录存放在外部存储减少重复注入。5.3 智能体间的通信与协作多智能体系统的协作模式大致分为三种协作模式特点适用场景管道模式上一个 Agent 的输出作为下一个 Agent 的输入代码分析 → 审查 → 汇总主从模式一个主控 Agent 分发任务、收集结果任务动态拆分的复杂流程去中心化模式Agent 之间相互通信协商复杂博弈、群体决策场景uReview 当下最合适的模式是“主从 管道”的混合结构ReviewOrchestrator 作为主控分发任务给各审查 Agent审查 Agent 之间相互独立最后交给 SummaryAgent 做管道式汇总。5.4 流程控制与容错设计发过线上事故的同事应该都有体会任何一个引入 AI 的系统都必须预设“AI 会犯错”的可能性。多智能体系统里的容错体现在多个层面超时控制防止模型响应过慢拖垮流水线结果校验Agent 返回结果后做格式校验不符合规范的触发重新生成降级策略当主模型不可用时切换备用模型或直接跳过该 Agent人工兜底所有 AI 审查结论都需要标记为“参考意见”保留人工复核通道。6. 常见问题与排查思路在实际开发和部署多智能体代码审查引擎的过程中下面这些问题我基本都踩过提前整理出来供大家参考。6.1 Agent 返回内容为空或格式混乱问题现象常见原因解决思路模型没有按照要求输出Prompt 约束不够具体在 System Prompt 中增加严格格式说明并给出 few-shot 示例输出是 JSON 但不是预期的结构没有使用 JSON Mode 强制格式兼容接口支持时开启response_format: {type: json_object}英文模型偶尔输出中文标签模型指令跟随不稳定用规则解析做后处理把非结构化文本归一化6.2 审查结果总是“假阳性”很多团队试用时会遇到这个问题Agent 报告了一堆问题人工一看全是误报。问题现象常见原因解决思路把没有问题的代码报出来Diff 上下文不足模型无法判断真实逻辑把变更函数的前后若干行代码一并提供给 Agent结果离谱且无参考价值模型本身能力不足使用更强的模型跑审查或走小模型初步筛选 大模型复核的路线6.3 并发执行导致大模型 API 限流问题现象常见原因解决思路大量 Agent 同时发起请求线程池并发度过高使用信号量或令牌桶限流控制 QPS429 限流报错API 配额不足引入重试机制按指数退避重试6.4 代码上下文超过模型窗口问题现象常见原因解决思路请求被拒或截断补丁内容过大按文件大小过滤、按变更块拆分、对超大文件做摘要后再审查审查结果遗漏问题关键词高密度的部分被截断调整拆分策略考虑按函数级别分片审查7. 最佳实践与工程建议这一节整理几条从工程落地角度最值得注意的建议。7.1 从单 Agent 起步逐步演进到多 Agent不要一开始就追求复杂的 Agent 编排。我比较推荐的演进路径是先用一个 Agent 跑通全流程拉 Diff → 调模型 → 输出报告验证模型在真实代码上的审查准确率和误报率再按审查维度拆分 Agent比如先拆出安全审查和性能审查最后再加入汇总 Agent、日志埋点、人工反馈回路。每一步都要有可量化的指标。比如误报率漏报率单次平均审查耗时人工采纳率。没有指标衡量的多智能体系统最后只会演变成“看起来很高级但没人真用”的玩具。7.2 用 Static Analysis 配合大模型而不是完全依赖大模型大模型做代码审查的优势是理解语义和意图但劣势也很明显不能真正执行代码存在幻觉风险。而传统的静态分析工具比如 ESLint、SpotBugs、SonarQube、Semgrep可以非常确定地检查出一类模式化问题。最佳实践是静态分析工具负责第一层确定性扫描大模型负责第二层语义分析两层结果做合并去重。这样既保证准确率又降低对模型能力的依赖。7.3 审查结果必须关联代码位置如果只是给一句“这里存在 SQL 注入风险”工程师根本没有精力去定位。更好的做法是为每条审查意见关联文件路径行号原始代码片段模型推理说明修改建议。在代码实现层面可以要求 Agent 输出 JSON 结构{ file_path: src/main/java/com/demo/UserService.java, line_start: 13, line_end: 14, severity: high, type: sql_injection, description: 直接拼接用户输入构造 SQL 查询存在注入风险, suggestion: 使用 PreparedStatement 参数化查询 }7.4 建立人工反馈闭环任何一个 AI 驱动的系统都必须建一个“人类反馈”回路。具体做法是在审查报告页面增加两个按钮“采纳”和“忽略”。把人工采纳的数据收集起来定期分析找出系统误报率高和漏报率高的类别再针对性优化 Prompt 和 Agent 分工。甚至可以更进一步用人工采纳数据构造微调数据集对自建模型做增量训练。7.5 安全与权限控制代码审查系统能接触到最敏感的代码资产安全边界必须重视系统内部服务只在内网暴露不直接对外公网访问Webhook 回调要做签名校验防止伪造请求调用大模型时不要上传不必要的敏感配置字段所有审查记录要定期清理或加密存储对操作行为做审计日志。8. 总结这篇文章从 Uber uReview 的思路出发完整拆解了一个多智能体代码审查引擎的设计与实现。我们从传统代码审查的痛点谈起介绍了多智能体系统的核心概念和通用架构输入接入层、代码分析层、智能体编排层、上下文管理层、反馈输出层。然后用 Java Spring Boot 实现了一个最小可运行的 Demo包括 Diff 解析、三个独立审查 Agent、一个汇总 Agent以及核心编排器。在工程层面重点讨论了几件事多智能体系统如何做任务分解、上下文管理、协作模式和容错落地过程中常见的假阳性、上下文溢出、API 限流问题以及从单 Agent 起步、静态分析工具配合大模型、建立人工反馈闭环等最佳实践。说句实在话多智能体代码审查目前还远没有到“全自动取代人工”的阶段。它更像是一个不知疲倦的初级Reviewer能帮你提前扫掉一批低级的、模式化的、肉眼容易漏掉的问题让你把精力省下来去关注真正需要人类经验的部分。如果你正在做 AI Engineer 相关方向不妨从今天这个最小骨架开始往里面加你自己的 Agent、模型和规则。框架本身不复杂复杂的是让每个 Agent 都足够可靠。建议直接拿一个真实的 PR 试试看看你的 Agent 能发现哪些问题又会漏掉哪些问题——这种真实反馈比任何理论都更有价值。