AI Agent实践案例:如何用AI Agent开发一个代码审查助手
一、开场:代码审查是个体力活
大家好,我是老金。
代码审查是个体力活:
- 重复的规范检查
- 类似的错误模式
- 永远review不完的PR
今天分享一个实战案例:用AI Agent做一个代码审查助手。
二、项目背景
2.1 需求分析
┌─────────────────────────────────────────────────────────┐
│ 代码审查助手需求 │
├─────────────────────────────────────────────────────────┤
│ │
│ 用户痛点: │
│ • 人工review太慢 │
│ • 重复问题反复出现 │
│ • 代码风格不统一 │
│ • 安全漏洞难发现 │
│ │
│ 目标: │
│ • 自动检查代码规范 │
│ • 发现常见bug │
│ • 检测安全漏洞 │
│ • 提供修改建议 │
│ │
└─────────────────────────────────────────────────────────┘
2.2 技术选型
# 技术栈选择
TECH_STACK = {
"框架": "LangChain + CrewAI",
"LLM": "GPT-4 (代码分析强)",
"代码解析": "Tree-sitter (支持多语言)",
"向量存储": "Chroma (轻量)",
"前端": "Streamlit (快速原型)"
}
三、系统设计
3.1 整体架构
┌─────────────────────────────────────────────────────────┐
│ 代码审查助手架构 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Web界面 │ │
│ │ (Streamlit) │ │
│ └────────────────────┬────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Multi-Agent协作 │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ 架构师 │ │ 审查员 │ │ 建议员 │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ │ │
│ └────────────────────┬────────────────────────────┘ │
│ ↓ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 工具层 │ │
│ │ • 代码解析器 │ │
│ │ • Linter集成 │ │
│ │ • 漏洞库查询 │ │
│ │ • 文档检索 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
3.2 Agent职责划分
# Agent定义
AGENTS = {
"architect": {
"role": "架构审查员",
"goal": "评估代码架构是否合理",
"backstory": """你是资深架构师,擅长:
- 系统设计评估
- 设计模式识别
- 扩展性分析
- 性能瓶颈发现""",
"tools": ["code_parser", "doc_search"]
},
"reviewer": {
"role": "代码审查员",
"goal": "发现代码质量问题",
"backstory": """你是代码质量专家,擅长:
- 代码规范检查
- Bug模式识别
- 坏味道检测
- 测试覆盖评估""",
"tools": ["linter", "vulnerability_db"]
},
"advisor": {
"role": "改进建议员",
"goal": "提供可操作的修改建议",
"backstory": """你是经验丰富的开发者,擅长:
- 给出具体代码示例
- 解释为什么要改
- 提供最佳实践
- 平衡质量和效率""",
"tools": ["code_search"]
}
}
四、核心实现
4.1 项目结构
# project_structure.py
PROJECT_STRUCTURE = """
code-review-agent/
├── src/
│ ├── agents/
│ │ ├── __init__.py
│ │ ├── architect.py # 架构审查Agent
│ │ ├── reviewer.py # 代码审查Agent
│ │ └── advisor.py # 建议Agent
│ ├── tools/
│ │ ├── __init__.py
│ │ ├── code_parser.py # 代码解析
│ │ ├── linter集成.py # Linter工具
│ │ └── vulnerability.py # 漏洞检测
│ ├── crew/
│ │ └── review_crew.py # Agent编排
│ └── utils/
│ ├── tree_sitter.py # 语法树
│ └── llm.py # LLM封装
├── tests/
├── requirements.txt
└── main.py
"""
4.2 代码解析工具
# tools/code_parser.py
from tree_sitter import Language, Parser
import tree_sitter_languages
class CodeParser:
"""代码解析器"""
def __init__(self):
self.parsers = {}
self._init_parsers()
def _init_parsers(self):
"""初始化多语言解析器"""
for lang in ['python', 'javascript', 'typescript', 'java', 'go', 'rust']:
try:
parser = Parser(tree_sitter_languages.get_language(lang))
self.parsers[lang] = parser
except:
pass
def parse(self, code: str, language: str) -> dict:
"""解析代码"""
if language not in self.parsers:
return {"error": f"Unsupported language: {language}"}
parser = self.parsers[language]
tree = parser.parse(bytes(code, "utf8"))
return {
"tree": tree,
"language": language,
"root": tree.root_node
}
def extract_functions(self, tree) -> List[dict]:
"""提取函数"""
functions = []
def walk(node, depth=0):
# 根据语言调整函数节点类型
func_types = ['function_definition', 'function_declaration', 'function']
if node.type in func_types:
functions.append({
"name": self._get_function_name(node),
"start": node.start_point,
"end": node.end_point,
"lines": node.end_point[0] - node.start_point[0] + 1
})
for child in node.children:
walk(child, depth + 1)
walk(tree.root_node)
return functions
def _get_function_name(self, node) -> str:
"""获取函数名"""
for child in node.children:
if child.type == 'identifier':
return child.text.decode('utf8')
return "unknown"
def get_complexity(self, tree) -> int:
"""计算圈复杂度(简化版)"""
complexity = 1
def walk(node):
nonlocal complexity
branch_types = [
'if_statement', 'elif_clause', 'else_clause',
'for_statement', 'while_statement',
'case', 'when'
]
if node.type in branch_types:
complexity += 1
for child in node.children:
walk(child)
walk(tree.root_node)
return complexity
4.3 架构审查Agent
# agents/architect.py
from crewai import Agent
from langchain.tools import Tool
class ArchitectAgent:
"""架构审查Agent"""
def __init__(self, llm, tools: List[Tool]):
self.agent = Agent(
role="架构审查员",
goal="评估代码架构是否合理,提供改进建议",
backstory=AGENTS["architect"]["backstory"],
llm=llm,
tools=tools,
verbose=True
)
async def review(self, code: str, language: str) -> dict:
"""架构审查"""
# 1. 解析代码结构
parser = CodeParser()
parsed = parser.parse(code, language)
functions = parser.extract_functions(parsed.get("tree", None))
complexity = parser.get_complexity(parsed.get("tree", None))
# 2. 构建审查Prompt
prompt = f"""
代码架构审查:
代码:
```{language}
{code}
代码结构分析:
- 函数数量:{len(functions)}
- 函数列表:{[f[‘name’] for f in functions]}
- 圈复杂度:{complexity}
请审查:
- 整体架构是否合理?
- 函数划分是否清晰?
- 是否违反SOLID原则?
- 有无明显的设计模式?
- 扩展性如何?
请给出具体的改进建议。
“””
# 3. 执行审查
result = await self.agent.execute_task(prompt)
return {
"functions": functions,
"complexity": complexity,
"review": result
}
### 4.4 代码审查Agent
```python
# agents/reviewer.py
import subprocess
class ReviewerAgent:
"""代码审查Agent"""
def __init__(self, llm, tools: List[Tool]):
self.agent = Agent(
role="代码审查员",
goal="发现代码质量问题、安全漏洞和潜在bug",
backstory=AGENTS["reviewer"]["backstory"],
llm=llm,
tools=tools,
verbose=True
)
async def review(self, code: str, language: str) -> dict:
"""代码审查"""
# 1. 运行Linter
linter_results = await self._run_linter(code, language)
# 2. 安全检查
security_issues = await self._check_security(code, language)
# 3. LLM深度审查
llm_review = await self._llm_review(code, linter_results, security_issues)
return {
"linter": linter_results,
"security": security_issues,
"llm_review": llm_review
}
async def _run_linter(self, code: str, language: str) -> List[dict]:
"""运行Linter"""
# 根据语言选择Linter
linters = {
"python": ["ruff", "pylint", "mypy"],
"javascript": ["eslint"],
"typescript": ["eslint"],
}
results = []
# 简化实现
if language == "python":
# 使用ruff(最快)
result = subprocess.run(
["ruff", "--output-format=json", "-"],
input=code,
capture_output=True,
text=True
)
if result.stdout:
import json
issues = json.loads(result.stdout)
results.extend(issues)
return results
async def _check_security(self, code: str, language: str) -> List[dict]:
"""安全检查"""
# 与漏洞库对比
security_patterns = {
"python": [
("eval(", "使用eval存在代码注入风险"),
("pickle.load", "pickle反序列化不安全"),
("subprocess.call", "注意命令注入"),
("os.system", "避免使用os.system"),
],
"javascript": [
("eval(", "使用eval存在XSS风险"),
("innerHTML", "使用textContent代替innerHTML"),
]
}
issues = []
patterns = security_patterns.get(language, [])
for pattern, reason in patterns:
if pattern in code:
issues.append({
"type": "security",
"pattern": pattern,
"reason": reason,
"severity": "high"
})
return issues
async def _llm_review(self, code, linter_results, security_issues) -> str:
"""LLM辅助审查"""
prompt = f"""
代码审查任务:
代码:
```{code}```
Linter发现的问题:
{linter_results}
安全问题:
{security_issues}
请分析:
1. 还有哪些Linter没发现的问题?
2. 性能优化的建议?
3. 可维护性问题?
4. 测试覆盖建议?
重点关注Linter没覆盖的问题。
"""
# 调用LLM
return "..."
4.5 建议Agent
# agents/advisor.py
class AdvisorAgent:
"""改进建议Agent"""
def __init__(self, llm):
self.agent = Agent(
role="改进建议员",
goal="将审查结果转化为可操作的修改建议",
backstory=AGENTS["advisor"]["backstory"],
llm=llm,
verbose=True
)
async def generate_suggestions(
self,
code: str,
architect_review: dict,
reviewer_review: dict
) -> List[dict]:
"""生成修改建议"""
prompt = f"""
根据以下审查结果,生成具体的修改建议:
代码:
```{code}```
架构审查结果:
{architect_review.get('review', '无问题')}
代码审查结果:
- Linter问题:{reviewer_review.get('linter', [])}
- 安全问题:{reviewer_review.get('security', [])}
- 其他问题:{reviewer_review.get('llm_review', '')}
请为每个问题生成修改建议,格式:
## 问题1: [问题描述]
- 严重程度:[高/中/低]
- 当前代码:
```python
[有问题的代码]
- 建议修改:
[改进后的代码] - 理由:[为什么要这样改]
优先级:高 → 中 → 低
“””
result = await self.agent.execute_task(prompt)
return self._parse_suggestions(result)
## 五、Agent编排
### 5.1 Crew编排
```python
# crew/review_crew.py
from crewai import Crew, Task, Process
from agents.architect import ArchitectAgent
from agents.reviewer import ReviewerAgent
from agents.advisor import AdvisorAgent
class CodeReviewCrew:
"""代码审查Crew"""
def __init__(self, llm):
# 初始化Agent
self.architect = ArchitectAgent(llm, tools)
self.reviewer = ReviewerAgent(llm, tools)
self.advisor = AdvisorAgent(llm)
# 创建Crew
self.crew = Crew(
agents=[
self.architect.agent,
self.reviewer.agent,
self.advisor.agent
],
process=Process.sequential, # 顺序执行
verbose=True
)
async def review(self, code: str, language: str) -> dict:
"""执行代码审查"""
# 1. 架构审查
architect_task = Task(
description=f"审查以下{language}代码的架构:nn{code[:2000]}...",
agent=self.architect.agent,
expected_output="架构审查报告"
)
# 2. 代码审查(依赖架构审查结果)
reviewer_task = Task(
description=f"审查以下{language}代码的质量和安全:nn{code[:2000]}...",
agent=self.reviewer.agent,
context=[architect_task], # 获取架构审查结果
expected_output="代码审查报告"
)
# 3. 生成建议(综合前两个结果)
advisor_task = Task(
description="生成可操作的修改建议",
agent=self.advisor.agent,
context=[architect_task, reviewer_task],
expected_output="修改建议列表"
)
# 执行Crew
result = self.crew.kickoff(
inputs={
"code": code,
"language": language
}
)
return {
"architect": architect_task.output,
"reviewer": reviewer_task.output,
"advisor": advisor_task.output,
"final_result": result
}
5.2 异步执行
# 异步接口
import asyncio
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class ReviewRequest(BaseModel):
code: str
language: str
@app.post("/review")
async def review_code(request: ReviewRequest):
"""代码审查接口"""
crew = CodeReviewCrew(llm)
# 异步执行
result = await crew.review(request.code, request.language)
return {
"status": "success",
"review": result
}
@app.post("/review/stream")
async def review_code_stream(request: ReviewRequest):
"""流式审查结果"""
crew = CodeReviewCrew(llm)
# 逐步返回结果
architect_result = await crew.architect.review(request.code, request.language)
yield f"架构审查完成: {architect_result['review'][:100]}...n"
reviewer_result = await crew.reviewer.review(request.code, request.language)
yield f"代码审查完成: 发现 {len(reviewer_result['security'])} 个安全问题n"
suggestions = await crew.advisor.generate_suggestions(
request.code, architect_result, reviewer_result
)
yield f"建议生成完成: {len(suggestions)} 条建议n"
六、前端界面
6.1 Streamlit界面
# ui/app.py
import streamlit as st
from src.crew.review_crew import CodeReviewCrew
from src.utils.llm import get_llm
st.set_page_config(page_title="AI代码审查助手")
st.title("🤖 AI代码审查助手")
# 侧边栏
with st.sidebar:
st.header("设置")
language = st.selectbox(
"代码语言",
["python", "javascript", "typescript", "java", "go", "rust"]
)
review_level = st.slider(
"审查深度",
min_value=1,
max_value=3,
value=2,
help="1=快速审查,3=深度审查"
)
# 主界面
code = st.text_area(
"粘贴你的代码",
height=300,
placeholder="在这里粘贴需要审查的代码..."
)
if st.button("开始审查", type="primary"):
if not code:
st.warning("请输入代码")
else:
with st.spinner("AI正在审查中..."):
# 审查
llm = get_llm(model="gpt-4" if review_level == 3 else "gpt-3.5-turbo")
crew = CodeReviewCrew(llm)
result = asyncio.run(crew.review(code, language))
# 显示结果
st.success("审查完成!")
# 架构审查
with st.expander("🏗️ 架构审查", expanded=True):
st.markdown(result["architect"]["review"])
# 代码审查
with st.expander("🔍 代码审查", expanded=True):
st.markdown(result["reviewer"]["llm_review"])
if result["reviewer"]["security"]:
st.error(f"发现 {len(result['reviewer']['security'])} 个安全问题!")
# 改进建议
with st.expander("💡 改进建议", expanded=True):
st.markdown(result["advisor"]["final_result"])
七、效果展示
7.1 审查结果示例
# AI代码审查报告
## 架构审查 ✅
- 函数划分清晰,单一职责
- 无明显架构问题
- 建议:可考虑使用装饰器模式
## 代码审查 ⚠️
### Linter问题 (3个)
- [ ] 未使用的变量 `unused_var`
- [ ] 缺少文档字符串
- [ ] 行长度超限
### 安全问题 (1个) 🔴
- [ ] 第15行使用 eval(),存在代码注入风险
### 其他建议
- [ ] 可使用 dataclass 简化代码
- [ ] 考虑添加类型注解
## 改进建议
### 高优先级
1. **安全问题**: 使用 ast.literal_eval 替代 eval()
```python
# Before
result = eval(user_input)
# After
result = ast.literal_eval(user_input)
中优先级
- 代码规范: 添加函数文档字符串
7.2 性能指标
审查性能对比:
人工审查:30分钟/PR
AI审查:2分钟/PR
提升:15倍
问题发现率:
人工:60%
AI:85%(结合Linter)
八、总结
项目亮点
- Multi-Agent协作:架构、审查、建议分工明确
- 工具集成:Linter、语法解析器、安全库
- 流式输出:实时反馈审查进度
- 渐进增强:快速审查→深度审查
扩展方向
下一步扩展:
1. 支持GitHub PR自动审查
2. 添加团队编码规范适配
3. 集成更多安全扫描工具
4. 支持自定义审查规则
5. 历史问题追踪
核心代码
# 核心调用
crew = CodeReviewCrew(llm)
result = await crew.review(code, language)
这个案例展示了如何用AI Agent解决实际问题。
代码审查助手只是开始,类似的思路可以延伸到:
- 文档生成
- 测试用例生成
- 代码翻译
- 技术支持
核心就是:让AI Agent做重复劳动,让人做更有价值的事。
相关阅读
- Agent框架横评(四):CrewAI让Multi-Agent协作像搭乐高一样简单
- AI Agent开发实战(五):Multi-Agent协作让团队工作
- AI Agent开发实战(一):环境搭建与工具链配置
正文完