
本文讨论的“Vibe Coding”并非玄学,而是特指以AI编程助手(Cursor IDE + Claude 3.5 Sonnet)为核心,辅以人类工程师代码审查(Code Review)与压力测试(Stress Testing)的迭代式开发工作流。
实验目标:在 4 小时内,从零构建一个支持分片上传(Multipart Upload)、断点续传的轻量级文件服务(技术栈:FastAPI + Redis + MinIO)。 硬件环境:MacBook M2 Pro(开发)+ 远程 Linux 服务器(4C/8G,部署 MinIO)。 评估维度:功能完整性、并发竞态条件(Race Condition)、数据库 N+1 查询、安全漏洞扫描(Snyk)。
为了避免AI生成“玩具代码”,我们采用上下文填充(Context Packing)策略。在 Cursor 的 Composer 中输入的系统级提示词(System Prompt)如下,而非简单的“帮我写个上传功能”:
【角色】高级后端架构师。
【约束】使用 FastAPI + aiobotocore + redis-py。必须包含以下非功能性需求:
1. 使用 Redis 分布式锁(Redlock 算法)防止并发分片覆盖。
2. 数据库模型(SQLAlchemy)需包含 `upload_id`, `part_number`, `etag`, `checksum_crc64`。
3. 所有 S3 操作必须配置 `connect_timeout=5s` 和 `read_timeout=30s`。
4. 异常处理需区分 `ClientError` 和 `ConnectionError`,并返回 RFC 7807 错误格式。第一版生成结果:AI 生成了约 400 行代码,通过了基础单元测试(Pytest)。但 Code Review 发现致命问题——AI 将所有分片信息缓存在内存字典中,未使用 Redis 持久化,这在服务重启时将导致 UploadId 丢失。
压测工具:使用 wrk 模拟 50 并发,每个客户端同时上传 8 个分片(Part)。
wrk -t 10 -c 50 -d 30s --script=upload.lua http://localhost:8000/upload崩溃现场:服务端报错 IncompleteBody: You did not provide the number of bytes specified by the Content-Length header。原因是 AI 生成的 complete_multipart_upload 函数在遍历分片列表时,未加锁,导致两个分片同时完成时,部分列表被覆盖。
AI 生成的缺陷代码片段(已脱敏):
# 缺陷:直接操作共享内存列表,未使用 Redis 原子操作
def complete_upload(upload_id: str):
parts = memory_cache.get(upload_id, []) # 非线程安全
parts.sort(key=lambda x: x['PartNumber'])
return s3.complete_multipart_upload(Bucket, Key, upload_id, parts)修复策略:通过提示词强制 AI 重构为Redis LUA 脚本原子操作。输入提示词:“将 complete 逻辑重构为 Redis Lua 脚本,确保 PartNumber 排序和 ETag 校验在单次原子执行中完成,防止并发覆盖。”
AI 修复后的代码(Redis Lua):
-- 原子获取并删除分片列表
local parts_json = redis.call('GET', KEYS[1])
if not parts_json then return nil end
redis.call('DEL', KEYS[1])
-- 在 Lua 内部完成排序与拼接,避免网络传输并发干扰
local parts = cjson.decode(parts_json)
table.sort(parts, function(a, b) return a.PartNumber < b.PartNumber end)
return cjson.encode(parts)修复结果:wrk 压测成功率从 92.3% 提升至 99.97%,P99 延迟从 1.8s 降至 620ms。
现象:服务运行 2 小时后,内存占用从 200MB 飙升至 3.2GB,触发 OOM Killer。
根源分析:Claude 3.5 生成的 aiobotocore 客户端未正确管理 Session 生命周期。AI 在每次请求中实例化新 Session,且未调用 close(),导致大量 TCP 连接处于 CLOSE_WAIT 状态。
AI 错误写法:
async def get_s3_client():
session = get_session() # 每次调用都新建
async with session.create_client('s3', endpoint_url=MINIO_URL) as client:
yield client # 依赖 FastAPI Depends,但并未在请求后显式清理工程修复指令:“将 S3 客户端改为应用级单例(Application Singleton),利用 Lifespan 上下文管理初始化并挂载到 app.state,避免请求级重复创建。”
修复后基准对比:
指标 | AI原生代码(V1) | 工程修复后(V2) |
|---|---|---|
内存占用(稳定态) | 3.1 GB | 412 MB |
活跃连接数 | 1,482 | 24 |
请求处理吞吐量 | 340 req/s | 520 req/s |
将 AI 生成的代码导入 SonarQube 和 Snyk 进行静态分析,发现高危路径遍历(Path Traversal)漏洞。
漏洞复现:构造请求 POST /upload?filename=../../etc/passwd,AI 仅使用了 os.path.basename 进行过滤,但在 Windows 环境下存在 \ 绕过风险。
AI 自主修复尝试:AI 给出的第一次修复是使用 werkzeug.secure_filename,但这在中文文件名场景下会过度转义(将所有中文替换为 _),导致业务不可用。
人工干预提示词:“放弃第三方转义,实现基于白名单的字符过滤,仅允许中英文、数字、点和下划线,且必须确保拼接后的绝对路径以 Base_Dir 为前缀。”
最终安全代码(AI 生成并通过 Snyk 扫描):
import os
def safe_join(base_dir: str, filename: str) -> str:
# 1. 过滤非法字符
clean_name = re.sub(r'[^\u4e00-\u9fa5A-Za-z0-9._-]', '', filename)
# 2. 绝对路径校验
target = os.path.join(base_dir, clean_name)
if os.path.commonprefix([os.path.realpath(target), os.path.realpath(base_dir)]) != os.path.realpath(base_dir):
raise PermissionError("Invalid path traversal attempt")
return target在查询分片上传历史记录时,AI 自动生成的 SQLAlchemy ORM 代码存在经典的 N+1 问题:
# 循环查询(N+1)
uploads = db.query(UploadModel).filter(UploadModel.user_id == uid).all()
for upload in uploads:
print(upload.parts) # 这里触发了新的懒加载查询修复措施:在 Cursor 中高亮该代码段,输入提示词:“将这部分查询改为使用 joinedload 预加载 parts 关系,并将查询语句通过 explain 分析确保走索引。”
性能对比(查询 100 条记录):
模式 | 执行 SQL 次数 | 平均耗时 |
|---|---|---|
AI 原生懒加载 | 101 次(1 + 100) | 2,400 ms |
预加载(joinedload) | 1 次(LEFT JOIN) | 120 ms |
经过 4 小时的迭代,该服务最终通过 QA 验收。基于本次实验,我们对 Vibe Coding 的工程能力得出以下量化边界:
最终建议:Vibe Coding 的有效性完全取决于工程师的审查严格度。请将 AI 视为“极度自信的初级程序员”,你的核心价值在于设计 Review Checklist 和编写高约束性的负面提示词(Negative Prompts),而非接受所有输出。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。