
一、为什么选博客这个小项目做"互喷辩论"的完整验证?
上篇(代码审查谁说了算?我让 3 个 LLM 互喷到一方服)发出去后,有读者问我:
"你那套'互喷辩论'方法论,有没有真的走完整流程?"
这个问题问到痛点了。上篇我确实用了一个中型项目做实战演示——让 2 个 LLM 出报告,Claude Code 复盘辩论,最后生成整改方案。但那次项目比较大,e2e 测试跑一遍要不少时间,所以文章重点放在"方法论设计"上,实施验证部分写得简略。
这次我特意选了博客这个小项目,原因很实在:
- 项目小,改起来快
- api-test 跑一遍很快(394 个 case 不到2秒就跑完)
- 所有接口都可以本地验证,测试结果可以直接验证
这意味着我可以完整走完整个流程:
3 个 LLM 出报告 → Claude Code 辩论 → 二次复核 → 实施修改 → 跑测试对比
而不只是"出 fix-plan 就完事"。
所以这篇算是上篇的"完整版"——同样的"互喷辩论"方法论,但这次真的走完了从"审查"到"修复验证"的全流程。
本文就是这次完整实战的记录。
二、为什么是 e2e 测试框架?
选 cases.yaml 作为审查对象,有三个原因:
1. 它是"测试我的测试"——元审查
cases.yaml 是我的博客后端项目的 e2e 测试用例清单,394 个 case、17 个 phase,覆盖所有 API 端点、所有认证矩阵、所有边界值。
问题在哪?
- case 是我自己写的,可能有盲区
- note 字段标注的"已知 bug / 现状说明"可能过期(代码修了,但 note 没更新)
- 框架不支持的断言可能被静默吞掉("摆设测试")
让 LLM 审 cases.yaml,相当于"测试我的测试"——如果 case 本身有 bug,那 394/394 绿灯就是"虚假的安全感"。
2. 它是"多 LLM 协作"的绝佳试验场
cases.yaml 是 YAML 格式,394 个 case,每个 case 有 id、method、path、auth、body、expect、note 等字段。
LLM 审查这种文件,正好考验:
- 能不能读懂 YAML 结构
- 能不能对照后端源码(handle/.go、domain//dao.go、service/*.go)验证断言对不对
- 能不能找出"摆设测试"、"过期 note"、"覆盖盲点"
这三点是上篇方法论里"互喷辩论"的核心——不是看 LLM 能不能出报告,是看 LLM 能不能拿出证据说服对方。
3. 它真的值得审
394/394 绿灯,但我心里没底。因为:
- cases.yaml 是我几个月前写的,中间后端代码改过好几轮
- note 字段里标注的"已知 bug"可能已经修了(但我没更新 note)
- 有些 case 的 expect 可能是"抄"的——我看源码觉得应该返 200,就写了 200,但没真的跑过边界值
这种"没底"的感觉,正是 LLM 审查的价值所在。
三、工具选型:3 个新工具进场
上篇用了 workbuddy(auto)和 qoder(qwen3.7-max),这次多加一个:codex(minimax-m3)。
最终进场 3 个工具:
| 工具 | 模型 | 结果 | 备注 |
|---|---|---|---|
| codex | minimax-m3(好友订阅享9折+Builder权益) | ✅ 跑通 | 新工具,报告最详细(32 个 finding) |
| qoder | qwen3.7-max(每天免费200积分) | ✅ 跑通 | 上篇用过,这次报告更聚焦(11 个 finding) |
| workbuddy | hy3.0 preview(超便宜,省积分) | ✅ 跑通 | 上篇用过,这次报告最少(7 个 finding),但抓到了几个独特的点 |
为什么选这 3 个?
- codex:新工具,调用的minimax-m3
- qoder:上篇表现稳定,qwen3.7-max 对 YAML 格式理解不错
- workbuddy:hy3.0 preview 表现还可以
zcode 再次缺席——这次项目小,glm-5.2 的赠送额度肯定能跑完,但突然发现 zcode 免费额度又不送了,连进场机会都没了。
四、核心方法论:完整流程(这次真的跑完了)
上篇演示了"互喷辩论 + 二次复核"流程,但因为测的是中型项目,e2e 跑一遍太慢,文章重点放在"方法论设计"上,实施验证部分写得简略。
这次我用小项目(博客),终于能把完整流程跑通了:

这次跟上次最大的不同:
- 真的跑了 4 个 verifier agent 并行核对——不是"想",是真去 grep + Read 源码
- 真的回了二次复核——不是"辩论完就定稿",是让原工具带着辩论记忆再看一次
- 真的达成了统一——11/11 个核心 finding 全部三方共识,可以直接进入实施
五、实战记录:三轮迭代的完整产出
第 1 轮:3 个工具出报告
用时:每家几分钟
产出:
| 工具 | finding 数量 | 状态 |
|---|---|---|
| codex | 32 个 | ✅ 跑通,报告最详细 |
| qoder | 11 个 | ✅ 跑通,报告更聚焦 |
| workbuddy | 7 个 | ✅ 跑通,报告最少但独特 |
合计 66 个 finding(去重前),按 P0→P4 分级后:
- 5 个 P0 真 bug / 摆设测试(含 1 个真生产安全漏洞)
- 8 个 P1-P2 handler bug / 覆盖盲点(含 1 个真生产安全漏洞 JWT 撤销)
- 6 个 P3 文档过期 / 维护债务
- 2 个 P4 风格 / 轻量新增
第一轮的"互喷"还没发生——3 个工具是独立的,各自出报告,谁也不服谁。
第 2 轮:Claude Code 验证 + 辩论 + 生成 fix-plan
这一步是整套流程的核心。Claude Code 配合 external-audit-fix skill,做了这几件事:
- 把 66 个 finding 拆给 4 个 verifier agent 并行处理
- 每个 agent 亲自去 backend/ 真实代码里 grep + Read
- 跟 codex 一对一逐条辩论:每条 finding 都要拿到证据(代码位置 + 反例),拿不出证据就反驳掉
- 跟 qoder 一对一逐条辩论:同样标准
- 跟 workbuddy 一对一逐条辩论:同样标准
- 只有三家 + Claude Code 三方共同认同的 finding,才进 fix-plan
结果:
| 判定 | 数量 | 含义 |
|---|---|---|
| ✅ 三方共同认同 | 21 | 真 bug,需要修 |
| 🟡 部分认同 | 3 | 部分对,部分需要纠正 |
| ❌ Claude Code 反驳 | 11 | 报告误读源码,不修 |
最精彩的一幕:
qoder 报告里有个 P0 finding:公开文章列表泄露草稿(SEC-1)。
- qoder 原始报告就抓到了,但 codex 和 workbuddy 都没报
- Claude Code 翻代码验证:
handle/article.go:101-105GetArticles 无 status 参数时不过滤,P11-03case 的oneof: [0, 1]允许草稿返回 - 这是真生产安全漏洞——未登录用户能看到草稿
如果没有"多 LLM 协作",这个漏洞可能就这么漏过去了(workbuddy 漏报,codex 一开始也没单独提)。
另一个精彩瞬间:
workbuddy 报告里说 A-50 note 过期(service 层手动校验 guest_name max=50,但 note 说"未生效")。
- codex 说"expect vs note 自相矛盾"
- qoder 说"note 过期(已修)"
- Claude Code 翻代码验证:
service/comment.go:89确实有if len(req.GuestName) > 50校验
三家各有偏差,最准确的是 workbuddy。这就是"互喷辩论"的价值——不是简单相信某家报告,是真去翻代码 + 用证据说服对方。
这一轮用时约 10 分钟(Claude Code 配合 external-audit-fix skill,自动拆 agent 并行验证)。
⚠️ Token 成本警告:我这次在 Claude Code 验证 3 个文档时使用了 workflow 模式,这个模式果然费 token——就这一个指令下去,用掉了快 30M token。如果你也想试,记得先确认额度够不够。
第 3 轮:二次复核(事后诸葛亮)
这一步是整个流程的"金矿"——把 fix-plan 回传给原 3 个工具,让它们带着辩论时的记忆,再看一次最终结论。
codex 二次复核:
- 32 项里,补充了 1 个之前漏报的 finding(SEC-1 公开列表泄露草稿,一轮时没单独提,二轮补回了)
- 降级了 1 个 finding(FULLTEXT-SQLITE 行为,从 P0 降级到 P2)
qoder 二次复核:
- 11/11 全部一致,0 实质补充
- 但澄清了 1 个误判:一轮把 AU-05/AU-11/AU-21 note 过期标成 P0,二轮澄清"finding 对但优先级应 P3"
workbuddy 二次复核:
- 7 项完全同意
- 补充了 2 项有价值的点:
P16-06self 路径 case 设计有问题(PUT /users/profile 不读 URL,u2 调就是改 u2,不存在"意图改 u1")A-29size=100 不是 bug(base.go:62 硬上限是 100,不是 50)
最终共识:
| 共识强度 | finding | 来源 |
|---|---|---|
| ⭐⭐⭐ 全员 | #1 SEC-1 公开列表泄漏草稿(P0 真安全漏洞) | qoder 原始 + codex 二轮补回 + verifier |
| ⭐⭐⭐ 全员 | #2 FW-1 P14-08/P15-05 摆设测试 | 三方一致 |
| ⭐⭐⭐ 全员 | #3 SEC-2 JWT 撤销 | codex + codex-re |
| ⭐⭐⭐ 全员 | #4 HANDLER-1 A-58/A-59 50000→2001 | codex + codex-re |
| ⭐⭐⭐ 全员 | #5 HANDLER-PAGE-1 P-17/P-20 50000→40400 | qoder + codex-re |
| ⭐⭐⭐ 全员 | #6~#11 其余 6 个 finding | 三方 |
11/11 个核心 finding 全部达成强共识,可直接进入实施阶段。
六、核心发现:2 个真生产安全漏洞 + 1 个摆设测试
🔴 P0:公开文章列表泄露草稿(qoder 抓到,codex/workbuddy 漏报)
漏洞描述:
handle/article.go:101-105 GetArticles 无 status 参数时不过滤,P11-03 case 的 oneof: [0, 1] 允许草稿返回。
后果:
未登录用户(或普通用户)调用 GET /api/v1/articles 能看到草稿(status=0 的文章)。
修复方案:
- handler 强制公开端点 status=1
- cases.yaml 期望改
oneof: [1](只允许已发布文章)
这个漏洞怎么被发现的?
qoder 原始报告就标了 P0-2,但 codex 和 workbuddy 都没报。如果没有"多 LLM 协作",这个漏洞就这么漏过去了。
这就是"互喷辩论"的价值——不是看哪家最强,是看多家加起来能不能覆盖盲区。
🟠 P1:JWT 撤销(codex 抓到)
漏洞描述:
middleware/auth.go:15-62 AuthMiddleware 不查 user.Status。banned 用户旧 token 永远有效直到过期。
后果:
用户被管理员 banned 后,旧 token 仍然能用(直到过期)。如果用户 token 过期时间是 7 天,那 banned 后还能访问 7 天。
修复方案:
- middleware 加 status 校验
- 新增 U-19/U-20/U-21 三个 case(测试 banned 用户 token 被拒绝)
🟠 P1:P14-08/P15-05 摆设测试(3 家全报)
问题描述:
P14-08 和 P15-05 两个 case 用了 body_not_contains 断言,但框架不支持该断言。两个 case 跑了但没断言任何事——等于没测。
后果:
cases.yaml 里标了这两个 case 是 ✅ 绿灯,但实际上没做任何断言。394/394 绿灯里,有 2 个是"虚假的绿灯"。
修复方案:
run_tests.py加body_not_contains支持- P14-08/P15-05 case 加真实断言
七、复盘:这次实验最值得记住的事
1. "多 LLM 协作"不是噱头,是真能抓到单 LLM 漏的漏洞
这次最有力的证据:
- SEC-1 公开列表泄露草稿:qoder 抓到了,codex 和 workbuddy 都没报
- SEC-2 JWT 撤销:codex 抓到了,qoder 和 workbuddy 都没报
- HANDLER-PAGE-1 P-17/P-20 50000→40400:qoder 抓到了,codex 和 workbuddy 都没报
没有哪家是"最强"的——每家都有盲区,但三家加起来,盲区就小多了。
2. "互喷辩论"比"投票"更有效
最容易踩的坑是让 Claude Code 当"裁判"——把 3 份报告合并一下,挑哪个对哪个错。
真实做法是"分别辩论":Claude Code 跟每家一对一逐条辩论,每条 finding 都要拿到证据(代码位置 + 反例),拿不出证据就反驳掉。只有三家 + Claude Code 三方共同认同的 finding,才进 fix-plan。
这套流程下来,21 个 confirmed finding 全部有代码证据,不是"某家说有 bug"就信。
3. 二次复核是"金矿"
workbuddy 二次复核时补充了 2 项:
P16-06self 路径 case 设计有问题A-29size=100 不是 bug(base.go:62 硬上限是 100)
如果不让 workbuddy 复核,Claude Code 自己在辩论时也看不出这两点。
LLM 之间的"辩论 + 复核"是这次实验真正的金矿。
4. 经验必须沉淀成工具
上篇做完后,Claude Code 顺带总结了踩坑教训,生成了 external-audit-fix skill。
这次直接用这个 skill 跑,效果显著:
- 自动拆 4 个 verifier agent 并行核对
- 自动生成 3 份 fix-plan + 1 份 rectification-report
- 自动判断"是否达成统一"
"代码能复用,经验也得能复用"——LLM 协作流程尤其需要 skill 化。
八、实施结果:5 个 PR 全部落地
达成统一并生成 fix-plan 后,我真的动手实施了。最终结果:
✅ 实施完成
- 最终状态:399/399 测试通过(0 fail / 0 skip)— 比修复前的 394 还多 6 个新 case
- 5 个 PR 全部落地(在同一个分支
fix/p1-handler-leakage,可按需 cherry-pick 拆分支)
5 个 PR 落地情况
| PR | commit | 内容 | 用例变化 |
|---|---|---|---|
| PR-1 | 1bccbee | 真安全漏洞修复:SEC-1 公开列表强制 status=1 + page/tags 错误码修正 | 改 9 case |
| PR-2 | f4d198f | framework 加 body_not_contains 支持,闭环 P14-08/P15-05 摆设测试 | 改 2 case |
| PR-3 | 39327dc | 真安全漏洞修复:JWT 撤销 — AuthMiddleware 校验 user.Status | 改 1 case |
| PR-4 | 65f649c | 清理 16 条过期 note + A-50 措辞修正 | 删 16 note |
| PR-5 | f3977db | 覆盖盲点:self 路径 / admin 删评论 / emoji 边界 | +6 case |
总改动量:30 个文件 / +6259 行 / -45 行 / 5 个 commit + 1 个 docs commit
实施中的关键发现
-
SEC-1 修复需强化:初版"未传 ?status 才强制"被 P13-28 绕过(匿名 ?status=0 仍返草稿)。最终方案是无条件覆盖非 admin 的 status 过滤
-
router 缺 OptionalAuthMiddleware:
/articles是纯 public group,admin token 不被 parse。修复 = 路由层加OptionalAuthMiddleware(),让 handler 能识别 admin role -
JWT 撤销需 DB 查询:AuthMiddleware 增加
global.DB.Model(&User{}).Where("id = ?", claims.UserID)检查 status。增加 settings 开关auth.skip_status_check用于测试 -
workbuddy 修订版更准确:P16-06/P16-07 我原本设计是"u2 改 u1 测越权",但 handler 走 JWT 不读 URL,根本不存在越权。workbuddy 改为"缺鉴权 / 缺必填参数"回归保护,更符合实际 handler 行为
跳过的覆盖盲点(范围说明)
- 限流 cases:需 runner 改造清计数器(避免 394 case 互相触发限流)
- CASCADE cases:删除用户/分类/标签会破坏 phase 依赖链,需新建隔离 phase
- pages 列表分页:handler 不支持(仅 published bool),需先扩 backend
- multipart 上传:runner 不支持
files=字段(SKILL.md 已写"推未来")
最终验证
| 指标 | 修复前(2026-06-22 19:55:49) | 修复后(2026-06-22 21:53:27) |
|---|---|---|
| 总用例数 | 394 | 399(+6 新 case) |
| pass | 394 | 399 |
| fail | 0 | 0 |
| SEC-1 真安全漏洞 | ❌ 漏洞 + 测试帮凶 | ✅ 已修 |
| P14-08 / P15-05 摆设 | ❌ 跑了但没断言 | ✅ 真断言负向 |
| JWT 撤销 | ❌ banned 旧 token 永远有效 | ✅ AuthMiddleware 校验 status |
| 过期 note 数量 | 16 条误导维护者 | ✅ 删 + 改 A-50 措辞 |
| Self 路径回归保护 | ❌ 未来 handler 改 URL 解析会漏 | ✅ P16-06/07 锁住 |
详细实施报告:external-audits/20260622/implementation-report.md(包含每个 PR 的实施细节、中间踩坑、跳过的范围说明、提交历史、cherry-pick 拆分 PR 的命令)
九、最终测试报告:399/399 全绿 ✅
5 个 PR 全部落地后,跑完整 e2e 测试,结果:
399 cases, 399 pass / 0 fail / 0 skip 用时 3.4s
从 394 到 399,多出来的 6 个 case 是:
| case | 来源 | 覆盖什么 |
|---|---|---|
| CM-38-prep | codex #16 | 准备 admin 待删评论 |
| CM-38 | codex #16 | admin 删任意评论 |
| P16-06 | workbuddy 修订版 | self 路径缺鉴权 → 40100 |
| P16-07 | workbuddy 修订版 | self 路径缺 old_password → 40001 |
| C-25 | workbuddy #5 | 分类名含 emoji |
| T-27 | workbuddy #5 | 标签名含 emoji |
之前 394/394 绿灯是虚假的安全感——P14-08/P15-05 跑了但没断言任何事,SEC-1 漏洞一直开着。 现在 399/399,每一盏绿灯都是真的绿。
写在最后
整个实验做完,最大的感慨是:
"多 LLM 协作"不是让多个 AI 投票,而是让它们互喷,用证据说服对方。
单 LLM 永远有盲区。但让多个 LLM 互喷 + 用证据说服对方,反而能逼出"哪条 bug 是真的"、"哪条方案更好"。
这次实战验证了上篇提出的"互喷辩论"方法论——真的能揪出生产安全漏洞,真的能达成统一,真的能生成可实施的 fix-plan。
如果你也在用 LLM 做代码审查,建议试试这套完整流程:
- 3 家出报告(不要只信一家)
- Claude Code 复盘辩论(必须翻真实代码,不能"想")
- 二次复核(事后诸葛亮环节,ROI 最高的 5 分钟)
- 生成 fix-plan + PR 拆分(直接可实施)
最大的成本不是 token,是第一次跑会踩坑——但写进 skill 后,第二次就会更流畅和高效。
本文完整输出物:
external-audits/20260622/ ├── codex-minimax-m3.md # codex 原报告 ├── codex-minimax-m3.fix-plan.md # codex fix-plan(Claude Code 生成) ├── codex-minimax-m3.fix-plan-re.md # codex 二次复核 ├── qoder-qwen3.7-max.md ├── qoder-qwen3.7-max.fix-plan.md ├── qoder-qwen3.7-max.fix-plan-re.md # qoder 二次复核 ├── workbuddy-hy3.0-preview.md ├── workbuddy-hy3.0-preview.fix-plan.md ├── workbuddy-hy3.0-preview.fix-plan-re.md # workbuddy 二次复核 ├── rectification-report.md # 一轮综合 ├── final-rectification-report.md # 二轮综合(达成统一) └── implementation-report.md # 实施报告(5 个 PR 落地细节)
实施已完成:5 个 PR 全部落地,399/399 测试通过,P0/P1 漏洞全部修复。
