最近刷掘金看到 Cursor 官方搞了个模型 PK 的功能,评论区一堆人在争"Opus 强还是 GPT 强"。说实话,我之前也只看 benchmark 选模型,直到上周我在生产环境踩了一个坑------Claude 和 GPT 对同一个 Bug 给出了完全相反的修法,其中一个差点让我上线 P0 故障。
这件事让我意识到:SWE-bench 80% 和"能修你的 Bug"是两回事。
于是我花了两天,拿 3 个从实际项目里扒出来的典型 Bug,喂给 5 个主流模型,记录下每个模型的表现。结果挺有意思的。
先说结论
| 场景 | Opus 4.6 | GPT-5.3 | Gemini 3.1 Pro | Qwen3-Max | DeepSeek V3 |
|---|---|---|---|---|---|
| React 状态竞态 | ✅ 一次过 | ⚠️ 修对了但多余 | ✅ 一次过 | ❌ 改错方向 | ⚠️ 能跑但有隐患 |
| SQL 慢查询优化 | ✅ 最优解 | ✅ 正确 | ⚠️ 漏了边界 | ✅ 正确 | ✅ 正确 |
| async 死锁排查 | ✅ 精准定位 | ✅ 正确 | ❌ 误诊 | ⚠️ 方向对细节错 | ✅ 正确 |
| 总分 | 3/3 | 2.5/3 | 1.5/3 | 1.5/3 | 2.5/3 |
没错,SWE-bench 上差 0.2% 的模型,真实场景能差出一条街。
测试方法
先说下我的测试环境:
- 每个模型用相同的 system prompt:
"你是一个高级软件工程师,请分析并修复以下代码中的 bug" - 直接调 API,不用 IDE 包装,排除工具链干扰
- temperature 统一 0,避免随机性
- 每个场景跑 3 次取最佳结果
API 调用这块,我是用的 OpenAI 兼容接口统一测的,一个 base_url 切所有模型,省得每家单独搞 SDK:
python
from openai import OpenAI
client = OpenAI(
api_key="sk-xxx",
base_url="https://api.ofox.ai/v1" # 兼容 OpenAI 协议
)
models = [
"anthropic/claude-opus-4-6",
"openai/gpt-5.3",
"google/gemini-3.1-pro",
"qwen/qwen3-max",
"deepseek/deepseek-v3"
]
for model in models:
resp = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "你是一个高级软件工程师,请分析并修复以下代码中的 bug。"},
{"role": "user", "content": bug_code}
],
temperature=0
)
print(f"\n{'='*50}")
print(f"模型: {model}")
print(resp.choices[0].message.content)
场景一:React useEffect 竞态条件
这是我在生产环境里真实遇到的问题。一个搜索组件,用户快速输入时,旧请求的结果可能覆盖新请求:
jsx
function SearchResults({ query }) {
const [results, setResults] = useState([]);
useEffect(() => {
fetch(`/api/search?q=${query}`)
.then(res => res.json())
.then(data => setResults(data));
}, [query]);
return <ResultList items={results} />;
}
经典 race condition------用户输入 "react",依次触发 "r"、"re"、"rea"、"reac"、"react" 五个请求,但网络不保证顺序返回。如果 "reac" 的结果比 "react" 晚到,就会覆盖正确结果。
各模型表现
Opus 4.6:✅ 教科书级
直接用 AbortController + cleanup 函数,干净利落:
jsx
useEffect(() => {
const controller = new AbortController();
fetch(`/api/search?q=${query}`, { signal: controller.signal })
.then(res => res.json())
.then(data => setResults(data))
.catch(err => {
if (err.name !== 'AbortError') throw err;
});
return () => controller.abort();
}, [query]);
没多余的废话,一次到位。
GPT-5.3:⚠️ 正确但过度设计
GPT 不仅加了 AbortController,还额外包了一层 debounce + loading state + error boundary。代码量直接翻了三倍。功能上没问题,但说实话,一个简单的 race condition 修复,你给我整 40 行?
Gemini 3.1 Pro:✅ 换了个思路
用 useRef 记录请求序号,只接受最新一次:
jsx
useEffect(() => {
const requestId = ++requestIdRef.current;
fetch(`/api/search?q=${query}`)
.then(res => res.json())
.then(data => {
if (requestId === requestIdRef.current) {
setResults(data);
}
});
}, [query]);
思路不同但同样有效,而且不会取消已经发出的请求,某些场景下反而更省资源。
Qwen3-Max:❌ 方向跑偏
给了一个 setTimeout + clearTimeout 的 debounce 方案。这不是修 race condition,这是回避问题------如果用户就是快速输入后停了,最后一个请求仍然可能被更早的慢请求覆盖。
DeepSeek V3:⚠️ 能用但有坑
用了 let cancelled = false 的 closure 方案,方向对但没处理 fetch 错误的情况,生产环境直接用会有隐患。
场景二:SQL 慢查询优化
这个来自我们项目的真实场景。一个统计接口跑了 8 秒,DBA 已经甩脸子了:
sql
SELECT u.name, COUNT(o.id) as order_count, SUM(o.amount) as total_amount
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.created_at >= '2026-01-01'
AND o.status IN ('paid', 'shipped', 'completed')
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 5
ORDER BY total_amount DESC
LIMIT 20;
问题:orders 表 2000 万行,created_at 有索引但 status 没有,执行计划显示全表扫描。
结果对比
这轮差距不大,Opus 给了最完整的方案------建复合索引 (created_at, status, user_id, amount) 覆盖查询,并且建议把 WHERE 条件里的 LEFT JOIN 改成 INNER JOIN(因为 WHERE 条件已经过滤了 NULL)。其他模型基本都能给出正确的索引建议,只有 Gemini 漏掉了 amount 字段导致无法 covering index。
场景三:Python async 死锁
这个 Bug 最阴间,我排查了大半天才找到原因:
python
import asyncio
class ConnectionPool:
def __init__(self, size=5):
self._semaphore = asyncio.Semaphore(size)
self._connections = []
async def acquire(self):
await self._semaphore.acquire()
if not self._connections:
conn = await self._create_connection()
return conn
return self._connections.pop()
async def release(self, conn):
self._connections.append(conn)
self._semaphore.release()
async def execute(self, query):
conn = await self.acquire()
try:
result = await conn.execute(query)
# 如果这里抛异常...
await self.release(conn)
return result
except Exception:
await self.release(conn)
raise
async def batch_execute(self, queries):
tasks = [self.execute(q) for q in queries]
return await asyncio.gather(*tasks)
看出来了吗?当 batch_execute 传入的 queries 数量超过 pool size(5)时,asyncio.gather 会同时启动所有 task。前 5 个 task 拿到 semaphore,后面的卡在 acquire 等待。但如果前面的 task 在 conn.execute 时抛异常,虽然 release 了 semaphore,但连接可能已经坏了。更狠的是------如果 _create_connection 本身也需要 pool 里的连接(比如要先查配置),直接死锁。
各模型表现
Opus 4.6:✅ 一眼看穿
精准指出了三个问题:
execute没用try/finally,异常路径可能泄漏 semaphorebatch_execute的并发量可能超过 pool size,应该用asyncio.Semaphore或asyncio.TaskGroup限制- 连接异常后应该丢弃而不是放回池里
给出的修复代码直接可用。
GPT-5.3:✅ 找到了核心问题
定位到了 try/finally 和连接泄漏问题,但漏掉了 batch_execute 的并发超限。
Gemini 3.1 Pro:❌ 误诊了
认为问题出在 Semaphore 的实现上,建议换成 asyncio.Queue。方向完全错了,Semaphore 本身没问题,是使用方式有问题。
Qwen3-Max:⚠️ 半对半错
找到了 try/finally 问题,但建议的修复里把 release 放在 finally 的同时没处理坏连接,相当于把坏连接放回了池子。
DeepSeek V3:✅ 意外惊喜
不仅找到了核心问题,还额外指出了 _connections 列表在并发场景下不是线程安全的(虽然 asyncio 是单线程的,但如果未来改成多线程就会出事)。思考得很全面。
踩坑记录
测试过程中遇到几个坑:
-
Gemini 的输出格式不稳定:同样的 prompt,有时候给完整代码,有时候只给 diff,有时候先分析一大段再给代码。做自动化测试的话需要额外处理。
-
Qwen3-Max 的中文理解很强,但代码上下文推理弱一些:如果 prompt 用中文描述 Bug,Qwen 的分析部分写得很好,但修出来的代码容易有小瑕疵。
-
DeepSeek V3 性价比真高:2.5/3 的成绩,但 API 价格只有 Opus 的 1/10 不到。日常开发用 DeepSeek 打底、关键修复切 Opus,是目前我觉得最划算的组合。
-
统一 API 接口真的省事:之前每个模型单独调,光是 SDK 依赖就装了一堆,现在改成 OpenAI 兼容协议统一调用,切模型就改个 model 参数的事。
小结
SWE-bench 80% 听着很猛,但真实 Bug 的复杂度远超基准测试。我的建议:
- 复杂逻辑 Bug(竞态、死锁、内存泄漏):上 Opus 4.6,一次到位的感觉确实不一样
- 日常代码生成和简单 Bug:DeepSeek V3 够用,性价比拉满
- SQL 和数据相关:各家差距不大,选便宜的就行
- 前端 UI Bug:Gemini 的思路经常有惊喜,值得试试
别迷信单一模型,多模型切换才是正解。毕竟现在切模型的成本几乎为零------改个 model 字符串就完事了。