AI写代码:从需求到实现
学会让AI帮你写代码,是AI编程最核心的技能。本文将教你如何从需求描述开始,一步步引导AI生成高质量的代码。
为什么需要学习这个技能?
你可能会想:"不就是描述需求让AI写代码吗?"
实际上,怎么描述需求决定了代码的质量:
❌ 差的需求描述
"帮我写个登录功能"
✅ 好的需求描述
"用FastAPI写一个用户登录API:
- 接收用户名和密码
- 验证用户身份
- 返回JWT token
- 包含错误处理
- 添加类型提示"好的描述能生成直接可用的代码,差的描述只能得到模糊的框架。
从需求到代码的完整流程
┌─────────────────────────────────────────────────────┐
│ Step 1: 梳理需求 │
│ 把想法变成清晰的功能描述 │
├─────────────────────────────────────────────────────┤
│ Step 2: 明确技术约束 │
│ 语言、框架、库、代码规范 │
├─────────────────────────────────────────────────────┤
│ Step 3: 描述给AI │
│ 用结构化的方式让AI理解需求 │
├─────────────────────────────────────────────────────┤
│ Step 4: 审查AI输出 │
│ 验证功能正确性、检查代码质量 │
├─────────────────────────────────────────────────────┤
│ Step 5: 迭代优化 │
│ 根据测试结果让AI改进代码 │
└─────────────────────────────────────────────────────┘Step 1:梳理需求
把模糊的想法变成具体的功能点
模糊的想法:
"我想做一个文件上传功能"
具体的功能点:
1. 用户可以上传文件(图片、PDF、Word文档)
2. 文件大小限制10MB
3. 支持拖拽上传和点击选择
4. 上传后显示预览
5. 上传成功返回文件URL
6. 失败时显示错误提示明确输入输出
输入:
- 文件(multipart/form-data)
- 文件类型限制:jpg, png, pdf, doc, docx
- 文件大小限制:10MB
输出:
- 成功:{"url": "https://...", "filename": "xxx"}
- 失败:{"error": "错误原因"}Step 2:明确技术约束
技术栈
后端:
- 语言:Python 3.11
- 框架:FastAPI
- 文件存储:本地存储或阿里云OSS
前端:
- 框架:React + TypeScript
- UI组件:Ant Design
- 请求库:axios代码规范
- 使用类型提示
- 遵循PEP 8规范
- 函数必须有文档字符串
- 错误使用自定义异常Step 3:描述给AI
使用结构化的提示词
请帮我用FastAPI实现一个文件上传API。
【功能需求】
1. 接收用户上传的文件
2. 验证文件类型(只允许jpg、png、pdf、doc、docx)
3. 验证文件大小(不超过10MB)
4. 保存文件到本地storage目录
5. 返回文件访问URL
【技术要求】
- Python 3.11 + FastAPI
- 使用类型提示
- 包含错误处理
- 包含文档字符串
【接口规范】
POST /api/upload
Content-Type: multipart/form-data
请求:
- file: 文件
响应(成功):
{
"success": true,
"url": "/files/xxx.pdf",
"filename": "document.pdf",
"size": 1024000
}
响应(失败):
{
"success": false,
"error": "文件类型不支持"
}AI生成的代码
python
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import FileResponse
from pathlib import Path
import uuid
import os
from typing import Optional
app = FastAPI()
# 配置
UPLOAD_DIR = Path("storage")
UPLOAD_DIR.mkdir(exist_ok=True)
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
ALLOWED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".pdf", ".doc", ".docx"}
def validate_file(filename: str, file_size: int) -> Optional[str]:
"""验证文件是否符合要求
Args:
filename: 文件名
file_size: 文件大小(字节)
Returns:
错误信息,如果验证通过则返回None
"""
# 检查文件扩展名
ext = Path(filename).suffix.lower()
if ext not in ALLOWED_EXTENSIONS:
return f"不支持的文件类型:{ext}"
# 检查文件大小
if file_size > MAX_FILE_SIZE:
return f"文件大小超过限制(最大10MB)"
return None
@app.post("/api/upload")
async def upload_file(file: UploadFile = File(...)):
"""文件上传接口
Args:
file: 上传的文件
Returns:
上传结果,包含文件URL
"""
# 读取文件内容以获取大小
content = await file.read()
file_size = len(content)
# 验证文件
error = validate_file(file.filename, file_size)
if error:
raise HTTPException(status_code=400, detail=error)
# 生成唯一文件名
ext = Path(file.filename).suffix.lower()
unique_filename = f"{uuid.uuid4()}{ext}"
file_path = UPLOAD_DIR / unique_filename
# 保存文件
with open(file_path, "wb") as f:
f.write(content)
return {
"success": True,
"url": f"/files/{unique_filename}",
"filename": file.filename,
"size": file_size
}
@app.get("/files/{filename}")
async def get_file(filename: str):
"""获取上传的文件"""
file_path = UPLOAD_DIR / filename
if not file_path.exists():
raise HTTPException(status_code=404, detail="文件不存在")
return FileResponse(file_path)Step 4:审查AI输出
检查清单
[ ] 功能是否完整?
- 文件上传 ✓
- 类型验证 ✓
- 大小验证 ✓
- 文件保存 ✓
- 返回URL ✓
[ ] 代码质量如何?
- 类型提示 ✓
- 文档字符串 ✓
- 错误处理 ✓
- 代码规范 ✓
[ ] 有没有安全隐患?
- 文件名安全?需要改进(可能包含恶意路径)
- 文件内容验证?缺失
[ ] 有没有性能问题?
- 大文件处理?一次性读入内存,需要改进发现的问题
- 文件名安全:没有对原始文件名进行安全处理
- 内存问题:大文件一次性读入内存
- 文件内容验证:没有验证文件实际类型(可能伪装扩展名)
Step 5:迭代优化
让AI改进代码
上面的代码有以下问题需要改进:
1. 文件名安全:原始文件名可能包含恶意路径,需要清洗
2. 内存问题:大文件应该流式写入,不要一次性读入内存
3. 文件类型验证:通过文件头验证真实类型,不只是检查扩展名
请改进代码解决这些问题。AI改进后的代码
python
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import FileResponse
from pathlib import Path
import uuid
import os
import magic # python-magic库,用于检测文件真实类型
from typing import Optional
app = FastAPI()
# 配置
UPLOAD_DIR = Path("storage")
UPLOAD_DIR.mkdir(exist_ok=True)
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
ALLOWED_EXTENSIONS = {".jpg", ".jpeg", ".png", ".pdf", ".doc", ".docx"}
ALLOWED_MIME_TYPES = {
"image/jpeg", "image/png",
"application/pdf",
"application/msword",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
}
CHUNK_SIZE = 1024 * 1024 # 1MB chunks
def sanitize_filename(filename: str) -> str:
"""清洗文件名,移除危险字符
Args:
filename: 原始文件名
Returns:
安全的文件名
"""
# 只保留文件名的最后部分(移除路径)
filename = Path(filename).name
# 移除危险字符
safe_chars = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_")
filename = "".join(c if c in safe_chars else "_" for c in filename)
return filename
def validate_file_type(file_header: bytes) -> Optional[str]:
"""通过文件头验证真实文件类型
Args:
file_header: 文件头字节
Returns:
错误信息,如果验证通过则返回None
"""
mime = magic.Magic(mime=True)
detected_type = mime.from_buffer(file_header)
if detected_type not in ALLOWED_MIME_TYPES:
return f"不支持的文件类型:{detected_type}"
return None
@app.post("/api/upload")
async def upload_file(file: UploadFile = File(...)):
"""文件上传接口(流式处理,支持大文件)
Args:
file: 上传的文件
Returns:
上传结果,包含文件URL
"""
# 生成唯一文件名
ext = Path(file.filename).suffix.lower()
if ext not in ALLOWED_EXTENSIONS:
raise HTTPException(status_code=400, detail=f"不支持的文件扩展名:{ext}")
unique_filename = f"{uuid.uuid4()}{ext}"
file_path = UPLOAD_DIR / unique_filename
# 流式读取和写入
total_size = 0
first_chunk = True
file_header = b""
with open(file_path, "wb") as f:
while chunk := await file.read(CHUNK_SIZE):
# 保存第一个块用于类型检测
if first_chunk:
file_header = chunk[:1024] # 前1KB足够检测类型
error = validate_file_type(file_header)
if error:
os.remove(file_path) # 删除已写入的部分
raise HTTPException(status_code=400, detail=error)
first_chunk = False
# 检查文件大小
total_size += len(chunk)
if total_size > MAX_FILE_SIZE:
os.remove(file_path)
raise HTTPException(status_code=400, detail="文件大小超过限制(最大10MB)")
f.write(chunk)
return {
"success": True,
"url": f"/files/{unique_filename}",
"filename": sanitize_filename(file.filename),
"size": total_size
}
@app.get("/files/{filename}")
async def get_file(filename: str):
"""获取上传的文件"""
# 防止路径遍历攻击
filename = Path(filename).name
file_path = UPLOAD_DIR / filename
if not file_path.exists():
raise HTTPException(status_code=404, detail="文件不存在")
return FileResponse(file_path)代码生成的最佳实践
实践一:分层描述
不要一次性描述所有需求,分层描述更容易得到好的结果:
第1层:核心功能
"写一个文件上传API"
第2层:添加约束
"添加文件类型和大小限制"
第3层:添加安全措施
"防止恶意文件上传"
第4层:优化性能
"支持大文件流式处理"实践二:提供示例
给出期望的输入输出示例:
【示例】
请求:
POST /api/upload
file: document.pdf (2MB)
响应:
{
"success": true,
"url": "/files/abc123.pdf",
"filename": "document.pdf",
"size": 2097152
}实践三:说明边界情况
【边界情况处理】
1. 文件为空 → 返回错误"请选择文件"
2. 文件类型不支持 → 返回错误"不支持的文件类型"
3. 文件过大 → 返回错误"文件大小超过限制"
4. 上传失败 → 返回错误"上传失败,请重试"小结
从需求到代码的核心要点:
| 步骤 | 关键动作 |
|---|---|
| 梳理需求 | 把模糊想法变成具体功能点 |
| 明确约束 | 技术栈、代码规范、接口规范 |
| 描述给AI | 结构化、详细、有示例 |
| 审查输出 | 功能完整性、代码质量、安全、性能 |
| 迭代优化 | 针对问题逐个改进 |
黄金法则
不要期望AI一次就生成完美代码。把它当作一个可以快速迭代的助手,通过多轮对话逐步完善代码质量。
下一步
学会了让AI写代码后,让我们继续学习 AI代码审查与重构,让AI帮你提升代码质量。