####################################
Tutorial 8: AI 辅助代码审查
####################################
.. include:: ../links.ref
.. include:: ../tags.ref
.. include:: ../abbrs.ref
============ ==========================
**Abstract** 使用 AI 进行代码审查
**Authors** Walter Fan
**Status** WIP
**Updated** |date|
============ ==========================
.. contents::
:local:
为什么用 AI 做代码审查
======================
代码审查是软件开发的重要环节,但传统方式有一些问题:
- **耗时**: 审查者需要花大量时间理解代码
- **主观**: 不同审查者标准不一
- **疲劳**: 审查质量随时间下降
- **延迟**: 等待审查者有空
AI 辅助代码审查可以:
- **即时反馈**: 提交后立即获得反馈
- **一致标准**: 始终按照相同标准审查
- **全面覆盖**: 检查安全、性能、风格等多个维度
- **学习工具**: 从 AI 的建议中学习
AI 代码审查的维度
=================
.. code-block:: text
AI 代码审查维度
┌─────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 代码质量 │ │ 安全性 │ │ 性能 │ │
│ ├─────────────┤ ├─────────────┤ ├─────────────┤ │
│ │ • 可读性 │ │ • SQL 注入 │ │ • 时间复杂度│ │
│ │ • 命名规范 │ │ • XSS │ │ • 空间复杂度│ │
│ │ • 代码重复 │ │ • 认证授权 │ │ • 数据库查询│ │
│ │ • 复杂度 │ │ • 敏感数据 │ │ • 内存泄漏 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 最佳实践 │ │ 测试覆盖 │ │ 文档 │ │
│ ├─────────────┤ ├─────────────┤ ├─────────────┤ │
│ │ • 设计模式 │ │ • 单元测试 │ │ • 注释 │ │
│ │ • 错误处理 │ │ • 边界测试 │ │ • Docstring │ │
│ │ • 日志记录 │ │ • 异常测试 │ │ • README │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
在 Cursor 中进行代码审查
========================
基本审查
--------
选中代码或打开文件,使用 Chat 进行审查:
::
@current_file
请审查这段代码,关注以下方面:
1. 代码质量和可读性
2. 潜在的 bug
3. 性能问题
4. 安全漏洞
5. 最佳实践
专项审查
--------
**安全审查**::
@current_file
作为安全专家,请审查这段代码的安全问题:
1. SQL 注入
2. XSS 攻击
3. 认证/授权问题
4. 敏感数据处理
5. 输入验证
**性能审查**::
@current_file
作为性能优化专家,请审查这段代码:
1. 时间复杂度
2. 空间复杂度
3. 数据库查询效率
4. 缓存使用
5. 并发处理
**架构审查**::
@folder/
作为架构师,请审查这个模块的设计:
1. 职责是否单一
2. 依赖是否合理
3. 接口设计是否清晰
4. 是否易于测试
5. 是否易于扩展
代码审查 Checklist
==================
通用 Checklist
--------------
.. code-block:: markdown
## 代码审查 Checklist
### 功能正确性
- [ ] 代码实现了预期功能
- [ ] 边界条件处理正确
- [ ] 错误处理完善
### 代码质量
- [ ] 命名清晰有意义
- [ ] 函数/方法长度适中(< 50 行)
- [ ] 圈复杂度合理(< 10)
- [ ] 没有重复代码
- [ ] 注释适当
### 安全性
- [ ] 输入已验证
- [ ] 无 SQL 注入风险
- [ ] 无 XSS 风险
- [ ] 敏感数据已加密
- [ ] 权限检查正确
### 性能
- [ ] 无 N+1 查询
- [ ] 适当使用缓存
- [ ] 无内存泄漏风险
- [ ] 大数据集有分页
### 测试
- [ ] 有单元测试
- [ ] 测试覆盖关键路径
- [ ] 测试边界条件
### 文档
- [ ] 公共 API 有文档
- [ ] 复杂逻辑有注释
- [ ] README 已更新
Python 专项 Checklist
---------------------
::
请根据以下 checklist 审查 Python 代码:
### Python 特定
- [ ] 使用类型注解
- [ ] 遵循 PEP 8
- [ ] 使用 f-string 而非 % 或 format
- [ ] 使用 pathlib 而非 os.path
- [ ] 使用 context manager 管理资源
- [ ] 异常处理具体(不用 bare except)
- [ ] 使用 dataclass 或 Pydantic
- [ ] import 顺序正确
JavaScript/TypeScript 专项 Checklist
------------------------------------
::
请根据以下 checklist 审查 TypeScript 代码:
### TypeScript 特定
- [ ] 类型定义完整(无 any)
- [ ] 使用 const/let,不用 var
- [ ] 使用 async/await,不用回调
- [ ] 使用可选链 ?.
- [ ] 使用空值合并 ??
- [ ] 组件 props 有类型定义
- [ ] 正确处理 Promise 错误
- [ ] 避免不必要的 re-render
实战:审查示例代码
==================
示例 1:发现安全问题
--------------------
**待审查代码**:
.. code-block:: python
def get_user(user_id):
query = f"SELECT * FROM users WHERE id = {user_id}"
result = db.execute(query)
return result.fetchone()
def login(request):
username = request.form['username']
password = request.form['password']
user = get_user_by_name(username)
if user and user.password == password:
session['user_id'] = user.id
return redirect('/dashboard')
return "Login failed"
**审查提示词**::
请审查这段代码的安全问题
**AI 审查结果**:
.. code-block:: text
发现以下安全问题:
1. SQL 注入(严重)
位置:get_user 函数
问题:直接拼接 SQL 字符串
修复:使用参数化查询
2. 明文密码比较(严重)
位置:login 函数
问题:密码没有哈希
修复:使用 bcrypt 比较
3. 会话固定攻击风险
位置:login 函数
问题:登录成功后没有重新生成 session
修复:登录后调用 session.regenerate()
4. 信息泄露
位置:login 函数
问题:"Login failed" 没有区分用户不存在和密码错误
建议:统一返回 "Invalid credentials"
**修复后的代码**:
.. code-block:: python
from werkzeug.security import check_password_hash
def get_user(user_id: int) -> User | None:
query = "SELECT * FROM users WHERE id = :id"
result = db.execute(query, {"id": user_id})
return result.fetchone()
def login(request):
username = request.form.get('username', '')
password = request.form.get('password', '')
if not username or not password:
return "Invalid credentials", 401
user = get_user_by_name(username)
if user and check_password_hash(user.password_hash, password):
session.regenerate()
session['user_id'] = user.id
return redirect('/dashboard')
return "Invalid credentials", 401
示例 2:发现性能问题
--------------------
**待审查代码**:
.. code-block:: python
def get_order_details(order_ids: list[int]) -> list[dict]:
results = []
for order_id in order_ids:
order = Order.query.get(order_id)
user = User.query.get(order.user_id)
items = OrderItem.query.filter_by(order_id=order_id).all()
products = []
for item in items:
product = Product.query.get(item.product_id)
products.append({
'name': product.name,
'quantity': item.quantity
})
results.append({
'order_id': order.id,
'user_name': user.name,
'products': products
})
return results
**审查提示词**::
请审查这段代码的性能问题,假设 order_ids 可能有 1000 个
**AI 审查结果**:
.. code-block:: text
发现严重的 N+1 查询问题:
对于 1000 个订单,这段代码会执行:
- 1000 次 Order 查询
- 1000 次 User 查询
- 1000 次 OrderItem 查询
- N 次 Product 查询(N = 订单项总数)
总计可能超过 5000 次数据库查询!
优化建议:
1. 使用 eager loading 一次性加载关联数据
2. 使用 IN 查询批量获取
3. 考虑使用 joinedload 或 selectinload
**修复后的代码**:
.. code-block:: python
from sqlalchemy.orm import joinedload, selectinload
def get_order_details(order_ids: list[int]) -> list[dict]:
# 一次查询获取所有数据
orders = (
Order.query
.filter(Order.id.in_(order_ids))
.options(
joinedload(Order.user),
selectinload(Order.items).joinedload(OrderItem.product)
)
.all()
)
return [
{
'order_id': order.id,
'user_name': order.user.name,
'products': [
{
'name': item.product.name,
'quantity': item.quantity
}
for item in order.items
]
}
for order in orders
]
# 现在只需要 3 次查询:
# 1. Orders
# 2. Users (joinedload)
# 3. OrderItems + Products (selectinload + joinedload)
自动化代码审查
==============
Git Hook 集成
-------------
创建 pre-commit hook,在提交前进行 AI 审查:
.. code-block:: bash
#!/bin/bash
# .git/hooks/pre-commit
# 获取暂存的文件
FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.(py|js|ts)$')
if [ -z "$FILES" ]; then
exit 0
fi
echo "Running AI code review..."
# 使用 aider 或其他工具进行审查
for FILE in $FILES; do
echo "Reviewing $FILE..."
# 这里可以调用 AI API 进行审查
done
CI/CD 集成
----------
在 GitHub Actions 中集成 AI 审查:
.. code-block:: yaml
# .github/workflows/ai-review.yml
name: AI Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Get changed files
id: changed-files
uses: tj-actions/changed-files@v35
- name: AI Review
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
for file in ${{ steps.changed-files.outputs.all_changed_files }}; do
echo "Reviewing $file"
# 调用 AI API 进行审查
done
- name: Post Review Comments
uses: actions/github-script@v6
with:
script: |
// 将审查结果作为 PR 评论发布
审查反馈处理
============
理解 AI 建议
------------
AI 的建议分为几个级别:
.. list-table::
:header-rows: 1
:widths: 20 30 50
* - 级别
- 描述
- 处理方式
* - 🔴 Critical
- 安全漏洞、严重 bug
- 必须修复
* - 🟠 Major
- 性能问题、设计缺陷
- 应该修复
* - 🟡 Minor
- 代码风格、命名问题
- 建议修复
* - 🟢 Suggestion
- 改进建议
- 可选
判断建议质量
------------
AI 的建议不一定都是正确的,需要判断:
1. **理解上下文**: AI 可能不了解完整的业务背景
2. **权衡利弊**: 有些建议可能引入新的复杂性
3. **验证建议**: 对于重要修改,要验证正确性
4. **保持批判**: 不要盲目接受所有建议
常见误判
--------
- **过度优化**: 建议优化不是瓶颈的代码
- **过度抽象**: 建议创建不必要的抽象
- **忽略约束**: 建议使用项目中不允许的库
- **风格偏好**: 将个人偏好作为问题
代码审查对话
============
多轮对话进行深入审查:
**第一轮**::
@file.py
请审查这段代码
**第二轮**::
你提到了 SQL 注入问题,能详细解释一下攻击场景吗?
**第三轮**::
修复后的代码如下,请再次审查:
[粘贴修复后的代码]
**第四轮**::
除了安全问题,这段代码的测试应该怎么写?
小结
====
本教程介绍了 AI 辅助代码审查:
- **审查维度**: 质量、安全、性能、最佳实践
- **审查方法**: 基本审查、专项审查、Checklist
- **实战案例**: 安全问题、性能问题
- **自动化**: Git Hook、CI/CD 集成
关键要点:
1. AI 审查是补充,不是替代人工审查
2. 要判断 AI 建议的质量
3. 建立团队的审查 Checklist
下一步
------
在下一个教程中,我们将学习 AI 辅助测试和调试。
练习
====
1. 用 AI 审查你最近写的代码
2. 创建你的代码审查 Checklist
3. 设置 pre-commit hook 进行自动审查
4. 对比 AI 审查和人工审查的结果
参考资源
========
- `Google Code Review Guidelines `_
- `OWASP Code Review Guide `_
- `Clean Code by Robert C. Martin `_