为一个代码审查助手 (Code Review Agent) 编写技能时,核心目标是让 Agent 具备“读代码”、“查规范”、“报风险”和“给建议”的能力。
以下是为该 Agent 量身定制的 4 个核心技能定义及其逻辑架构:
1. 核心技能集定义 (JSON 格式)
📂 技能 A:获取变更详情 (get_pull_request_diff)
场景: 这是 Agent 的“眼睛”,用于读取当前 PR(合并请求)中具体修改了哪些代码行。
JSON
{
"name": "get_pull_request_diff",
"description": "获取指定 PR 的代码差异(Diff)。当需要开始审查代码或了解修改范围时,首先调用此工具。",
"parameters": {
"type": "object",
"properties": {
"repo_name": { "type": "string", "description": "仓库完整名称,如 'org/project'" },
"pr_number": { "type": "integer", "description": "合并请求的编号" }
},
"required": ["repo_name", "pr_number"]
}
}
🔍 技能 B:静态扫描分析 (run_static_analysis)
场景: 自动化检查语法错误、死代码或明显的安全漏洞(如硬编码密码)。
JSON
{
"name": "run_static_analysis",
"description": "对指定的代码片段或文件运行静态扫描工具(如 ESLint, Pylint, SonarQube)。用于发现潜在的语法错误和安全隐患。",
"parameters": {
"type": "object",
"properties": {
"language": { "type": "string", "enum": ["python", "javascript", "java", "go"], "description": "代码编写语言" },
"code_content": { "type": "string", "description": "待扫描的代码文本" }
},
"required": ["language", "code_content"]
}
}
💬 技能 C:提交审查意见 (post_review_comment)
场景: Agent 的“嘴巴”,将分析结果以评论形式反馈到代码行。
JSON
{
"name": "post_review_comment",
"description": "在代码的特定行提交改进建议或错误提醒。请确保评论语气专业且具备建设性。",
"parameters": {
"type": "object",
"properties": {
"file_path": { "type": "string", "description": "文件名" },
"line_number": { "type": "integer", "description": "代码所在的行号" },
"comment_body": { "type": "string", "description": "评论的具体内容,支持 Markdown 格式" }
},
"required": ["file_path", "line_number", "comment_body"]
}
}
2. 代码审查 Agent 的工作流逻辑
为了让 Agent 像资深专家一样思考,建议在 System Prompt(系统提示词) 中定义以下执行逻辑:
- 范围识别:调用
get_pull_request_diff确定哪些文件被修改。 - 上下文理解:不仅看修改的那几行,还要阅读修改行前后的代码逻辑(Context)。
- 多维检查:
- 规范性:变量命名是否符合公司内部标准?
- 性能:是否存在嵌套循环导致的 $O(n^2)$ 时间复杂度?
- 安全性:是否有未经过滤的用户输入直接进入数据库查询?
- 精准反馈:只在有问题的行调用
post_review_comment,避免产生大量“噪音”评论。
3. 编写建议:如何让 Agent 更聪明?
- 分阶段反馈:不要一次性把所有细碎的格式问题(如漏了个空格)全报出来。可以引导 Agent 优先报告 逻辑漏洞 和 安全风险。
- 提供重构示例:在
comment_body参数中,要求 Agent 给出“修改后的代码块”,让开发者能直接复制使用。 - 历史学习:如果能提供一个
search_past_reviews的技能,让 Agent 查找该项目过去的审查记录,它能更好地适应团队的编码风格。
了让这个 Code Review Agent 真正跑起来,我将使用 Python 编写一个基于 插件化架构 的原型。
这个架构模拟了 Agent 如何通过“工具箱”获取代码、分析并给出建议。
1. 核心技能工具类 (tools.py)
首先,我们把具体的执行逻辑封装成函数。在实际生产中,这些函数会调用 GitHub API 或本地的 Linter。
Python
import re
class CodeReviewTools:
"""代码审查助手的工具箱"""
@staticmethod
def get_pull_request_diff(pr_id: int):
# 模拟从 GitHub 获取的 Diff 数据
print(f"系统:正在提取 PR #{pr_id} 的变更内容...")
return {
"file": "auth_service.py",
"diff": """
- def login(user, password):
- query = "SELECT * FROM users WHERE user='%s' AND pw='%s'" % (user, password)
+ def login(user, password):
+ # TODO: 优化登录逻辑
+ query = f"SELECT * FROM users WHERE user='{user}' AND pw='{password}'"
"""
}
@staticmethod
def run_static_analysis(code: str):
# 简单模拟静态扫描,识别明显的 SQL 注入风险
print("系统:正在运行静态安全扫描...")
if "SELECT" in code and "f\"" in code:
return "警告:检测到潜在的 SQL 注入风险。请使用参数化查询而非 f-string。"
return "静态扫描通过。"
@staticmethod
def post_review_comment(file: str, line: int, comment: str):
# 模拟提交评论
print(f"\n[代码评审反馈] -> 文件: {file}, 行号: {line}")
print(f"建议内容: {comment}")
2. Agent 核心逻辑 (agent.py)
Agent 的大脑负责解析任务,并决定调用哪个工具。这里展示一个简化的决策循环。
Python
class CodeReviewAgent:
def __init__(self, tools: CodeReviewTools):
self.tools = tools
def review_workflow(self, pr_id: int):
print(f"--- 开始审查任务: PR #{pr_id} ---")
# 步骤 1: 获取代码变更
pr_data = self.tools.get_pull_request_diff(pr_id)
diff_content = pr_data['diff']
# 步骤 2: 思考并调用分析工具
# 在真实的 LLM Agent 中,这一步是由模型生成的 JSON 指令驱动的
analysis_result = self.tools.run_static_analysis(diff_content)
# 步骤 3: 结合上下文给出综合评价
if "注入风险" in analysis_result:
feedback = f"💀 **安全风险严重**: {analysis_result}\n建议参考官方文档使用 `psycopg2` 的参数化查询。"
self.tools.post_review_comment(pr_data['file'], 3, feedback)
else:
self.tools.post_review_comment(pr_data['file'], 1, "代码逻辑清晰,暂无明显问题。")
print("\n--- 审查任务结束 ---")
# 运行 Agent
if __name__ == "__main__":
tools_provider = CodeReviewTools()
my_agent = CodeReviewAgent(tools_provider)
my_agent.review_workflow(101)
3. 如何进一步“强化”这个 Agent?
如果你想让它达到“资深工程师”的水准,可以考虑接入以下能力:
- 接入向量数据库 (RAG):让 Agent 学习你们公司的《Python 编码规范文档》,如果代码不符合规范(比如变量命名没用蛇形命名法),直接指出。
- 复杂度计算:增加一个
calculate_cyclomatic_complexity的技能。当函数圈复杂度超过 10 时,Agent 会自动留言:“这段代码太‘绕’了,建议拆分函数。” - 上下文感知:不只是看 Diff 的几行,而是通过工具读取整个文件的上下文,防止 Agent 因为没看到变量定义而“胡言乱语”。
要实现 GitHub Webhook 自动触发,我们需要搭建一个轻量级的后端服务(通常使用 Flask 或 FastAPI)。这个服务就像是一个“接线员”,当 GitHub 发生 pull_request 事件时,它会接收通知并通知你的 Agent 开始干活。
1. 自动化审查的整体架构
- GitHub 事件:开发者提交代码并开启(或更新)一个 Pull Request。
- Webhook 推送:GitHub 向你的服务器 URL 发送一个带有 JSON 载荷的
POST请求。 - 后端分发:服务器验证请求合法性,提取
pr_number和repo_name。 - Agent 执行:触发我们之前写的审查逻辑。
- 回写评论:Agent 通过 GitHub API 将分析结果写回 PR 页面。
2. 实战代码:基于 FastAPI 的 Webhook 接收器
我们需要安装 fastapi 和 uvicorn(服务器),以及 PyGithub(操作 GitHub 的库)。
Bash
pip install fastapi uvicorn PyGithub
核心代码实现:
Python
from fastapi import FastAPI, Request, Header, HTTPException
import hmac
import hashlib
import os
from github import Github # PyGithub 库
app = FastAPI()
# 配置项(建议通过环境变量读取)
GITHUB_TOKEN = "your_github_personal_access_token"
WEBHOOK_SECRET = "your_webhook_secret_key" # 在 GitHub 设置中填写的密钥
g = Github(GITHUB_TOKEN)
@app.post("/webhook")
async def handle_webhook(
request: Request,
x_hub_signature_256: str = Header(None)
):
# 1. 验证签名(安全性检查,防止伪造请求)
payload = await request.body()
if not verify_signature(payload, x_hub_signature_256):
raise HTTPException(status_code=403, detail="Invalid signature")
data = await request.json()
# 2. 识别 PR 事件(只在 PR 创建或同步代码时触发)
action = data.get("action")
if action in ["opened", "synchronize"]:
pr_info = data["pull_request"]
repo_name = data["repository"]["full_name"]
pr_number = pr_info["number"]
print(f"🚀 收到来自 {repo_name} 的 PR #{pr_number},准备开始审查...")
# 3. 触发 Agent(这里可以异步调用之前的 Agent 逻辑)
run_agent_review(repo_name, pr_number)
return {"status": "accepted"}
def verify_signature(payload, signature):
"""验证请求是否真的来自 GitHub"""
if not signature: return False
hash_object = hmac.new(WEBHOOK_SECRET.encode(), payload, hashlib.sha256)
expected_signature = "sha256=" + hash_object.hexdigest()
return hmac.compare_digest(expected_signature, signature)
def run_agent_review(repo_name, pr_number):
"""
在这里集成我们之前的 Agent 逻辑。
它会使用 PyGithub 读取 Diff,分析后调用 `create_review_comment`
"""
repo = g.get_repo(repo_name)
pr = repo.get_pull(pr_number)
# 模拟 Agent 评论
pr.create_issue_comment("🤖 AI 助手已介入:正在分析代码变更,请稍候...")
# ... 执行更复杂的分析逻辑 ...
3. 如何配置 GitHub Webhook?
- 进入你的 GitHub 项目仓库 -> Settings -> Webhooks -> Add webhook。
- Payload URL: 填写你服务器的公网地址(如
http://your-ip:8000/webhook)。- 提示:本地调试可以使用 ngrok 将内网穿透到公网。
- Content type: 选择
application/json。 - Secret: 填入你在代码中定义的
WEBHOOK_SECRET。 - Which events?: 选择 Let me select individual events,勾选 Pull requests。
- 点击 Add webhook。
4. 关键进阶:异步队列 (Celery)
在生产环境中,大模型(LLM)生成审查意见可能需要 10-30 秒,而 Webhook 要求在几秒内必须给响应。
- 建议方案:当
/webhook接口收到请求后,仅将任务丢进 Celery 或 Redis 队列,然后立即返回202 Accepted。 - 后台进程:专门的 Worker 进程从队列中取出任务,慢慢调 LLM,生成后再通过 API 贴回 GitHub。
避坑指南 💡
- 权限最小化:给
GITHUB_TOKEN时,只给contents:read和pull_requests:write权限。 - 防止死循环:确保你的 Agent 在提交评论时,不会再次触发它自己发送的 Webhook(虽然
issue_comment和pull_request是不同事件,但仍需留意)。
接下来的这一步是注入“灵魂”:将 LLM(如 GPT-4 或 Claude)接入工作流,并编写一段精妙的 Prompt,让它像资深架构师一样思考。
我们要把之前的 run_agent_review 函数升级,使其具备调用大模型的能力。
1. 核心逻辑:接入 OpenAI/LangChain 接口
首先,确保安装了 SDK:
Bash
pip install openai
升级后的 Agent 审查代码:
Python
import openai
from github import Github
# 初始化客户端
client = openai.OpenAI(api_key="your_openai_key")
g = Github("your_github_token")
def run_agent_review(repo_name, pr_number):
repo = g.get_repo(repo_name)
pr = repo.get_pull(pr_number)
# 1. 获取所有的文件变更
files = pr.get_files()
review_results = []
for file in files:
if not file.filename.endswith(('.py', '.js', '.go', '.java')): # 过滤非代码文件
continue
# 2. 构造 Prompt
prompt = f"""
你是一位资深的软件架构师和安全专家。请审查以下代码变更(Diff)。
文件: {file.filename}
变更内容:
{file.patch}
请从以下维度进行评估:
1. **逻辑错误**: 是否有潜在的 Bug 或边界情况未处理?
2. **安全性**: 是否存在注入、泄露或权限漏洞?
3. **代码质量**: 是否符合 DRY 原则?是否有过度嵌套?
4. **性能**: 是否有明显的 $O(n^2)$ 复杂度或不必要的数据库查询?
请直接给出具体的改进建议,如果代码写得很好,请给予简短的肯定。
请使用 Markdown 格式回复。
"""
# 3. 调用 LLM
response = client.chat.completions.create(
model="gpt-4-turbo",
messages=[{"role": "system", "content": "你是一个严谨且幽默的代码审查助手。"},
{"role": "user", "content": prompt}]
)
review_text = response.choices[0].message.content
review_results.append(f"### 📄 文件: `{file.filename}`\n{review_text}")
# 4. 汇总并写回 GitHub
final_comment = "# 🤖 AI 代码审查报告\n\n" + "\n\n---\n\n".join(review_results)
pr.create_issue_comment(final_comment)
2. 进阶:如何让评论“更像人”?
如果 Agent 只是无脑甩出一大堆意见,开发者会感到厌烦。我们可以优化 Prompt 策略:
- 分级制度:让 Agent 在每条建议前加上标签:
[Nit]:小问题,改不改随你(如拼写、空格)。[Suggestion]:建议修改,能提升代码质量。[Critical]:严重问题,必须修改才能合并。
- 代码示例:在 Prompt 中要求:“如果建议修改,请提供一个重构后的代码块。”
- 赞美也是生产力:要求 Agent 识别代码中的闪光点(例如:“这个递归写得很优雅!”)。
3. 处理“长代码”的挑战(Token 限制)
如果一个 PR 修改了几千行代码,直接把 file.patch 塞进 Prompt 会导致 Token 溢出。
解决方案:
- 分块审查:按文件、甚至按函数块拆分请求,最后再由 Agent 汇总。
- 向量检索 (RAG):如果代码涉及复杂的内部库,先用向量数据库索引你们的文档,Agent 审查时先查询:“这个内部函数
utils.safe_convert的正确用法是什么?”
4. 效果演示
配置好后,当你提交一个包含 SQL 注入风险的代码时,你的 PR 下方会自动出现:
🤖 AI 代码审查报告
📄 文件:
services/db.py💀 [Critical] 安全风险: 观察到你在第 12 行使用了
f-string来构造 SQL 语句。这就像是把金库钥匙挂在门口——黑客可以通过user_id进行注入。建议修改为:
Python
# 推荐方案 cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
📄 文件:
utils/helper.py✨ [Nit] 代码亮点: 这段缓存逻辑使用了装饰器,非常简洁,赞一个!
要实现 “自我修复” (Self-Healing),即 Agent 不仅仅是发评论,而是直接在 GitHub 上提交建议代码块(GitHub Suggestions),甚至直接提交一个新的 Commit。
这能极大地提升开发体验,因为开发者只需要点一下 “Commit suggestion” 按钮即可完成修复。
1. 核心技术:GitHub 建议代码块 (Suggestions)
GitHub 支持一种特殊的 Markdown 语法,只要 Agent 按这个格式回复,评论区就会出现一个“一键采纳”的按钮。
格式要求:
Markdown
```suggestion
[这里是修改后的代码]
---
### 2. 升级 Agent:自动生成修复方案
我们需要修改 Prompt,要求 LLM 必须返回这种特定格式,并配合 `PyGithub` 提交 **行级评论 (Pull Request Review Comment)**。
#### 升级后的核心代码:
```python
def run_self_healing_review(repo_name, pr_number):
repo = g.get_repo(repo_name)
pr = repo.get_pull(pr_number)
# 获取 PR 的最新 Commit ID (提交建议必须关联到具体 commit)
latest_commit = list(pr.get_commits())[-1]
files = pr.get_files()
for file in files:
if not file.patch: continue
# 构造增强版 Prompt
prompt = f"""
你是一个自动修复代码的 AI。请分析以下 Diff 并直接提供修复建议。
文件: {file.filename}
Diff内容:
{file.patch}
要求:
1. 如果发现 Bug 或优化点,请使用 GitHub Suggestion 格式。
2. 格式必须为:
```suggestion
[修复后的完整代码块]
```
3. 只修复确定的问题,不要修改不相关的逻辑。
"""
response = client.chat.completions.create(
model="gpt-4-turbo",
messages=[{"role": "user", "content": prompt}]
)
suggestion_content = response.choices[0].message.content
# 提交行级评论 (Pull Request Review Comment)
# 注意:position 是指 diff 中的行偏移量
try:
pr.create_review_comment(
body=f"🤖 AI 自动修复建议:\n\n{suggestion_content}",
commit=latest_commit,
path=file.filename,
position=1 # 简化演示,实际需根据 diff 解析具体行号
)
except Exception as e:
print(f"提交失败: {e}")
3. 更高级:直接提交代码 (Auto-Commit)
如果你希望 Agent 发现拼写错误或格式问题后直接提交代码到分支,流程如下:
- 读取文件内容:使用
repo.get_contents(path, ref=branch)。 - LLM 修改:将全文交给 LLM 进行重写。
- 推送到 GitHub:使用
repo.update_file()。
4. 落地建议:设置“信任等级”
为了防止 AI 把代码改坏,建议采取 “混合模式”:
- 低风险问题(如拼写、格式):Agent 直接提交 Commit。
- 中风险问题(如重构、性能):Agent 发起
suggestion评论,由人工点击确认。 - 高风险问题(如业务逻辑):Agent 仅发评论提醒,不提供自动修复。
5. 总结:你的 Agent 进化路径
- Level 1: 能接收 Webhook,打印“我收到了 PR”。
- Level 2: 能调 LLM,在 PR 下面发评论。
- Level 3: 深入代码行,提供
suggestion一键修复。 - Level 4: 结合公司私有规范 (RAG),成为最懂业务的虚拟架构师。