你跑了 codex audit(或直接发了”帮我 audit 一下项目”),收到一份 50+ 条的报告:「考虑用 const 替代 let」、「API key 应该走环境变量」、「这个函数 200 行了」、「建议补 edge case 测试」……读完只剩一个感受——下午到底先修哪 3 个?这份报告就成了一份你永远不会动手的”愧疚清单”。
这不是模型问题,是 audit prompt 问题。一份能用的 audit 必须只有一个维度、一个范围、固定输出大小、每条带文件锚点。下面拆解 audit 跑广的 6 个常见原因,以及能稳定产出 10 条可执行清单的 prompt 模板。
常见原因
按命中率从高到低:
1. Prompt 只有”审一下项目”
开放式 prompt 必然得到开放式报告。“audit”不指明维度,Codex 只能猜你在乎什么——而它的对冲策略就是”全部列一遍”。
你:audit 一下我的项目
Codex:[返回 50 条,覆盖安全 / 风格 / 性能 / 类型 / 测试 / 文档 / 可访问性]
如何判断:看你原始 prompt——只要没出现「安全」「性能」「类型」之类的单一维度词,报告肯定混维度。
2. 没限 scope,Codex 审了整个 monorepo
不指定路径时,Codex 会爬整棵树。monorepo 一审就把 apps/marketing/、packages/ui/、scripts/、docs/ 的问题全堆在一起——但你这次只关心 API 层。
如何判断:按文件路径分组——跨 4 个以上顶层目录就是 scope 没限。
3. 没要严重度,每条看起来一样重
不要求”为每条标 1–5 严重度”时,Codex 是按发现顺序返回的,不是按影响排序。一行 ; 漏写就紧挨着一个 SQL 注入风险。
如何判断:读前 5 条——明显严重的事项和小修小补的事项混在一起、没标签,就是没要 severity。
4. 重审时没传”已修”列表
第二轮 audit 又把上一轮你修过的问题列一遍——因为 Codex 不知道这期间改了什么。你又得手动标 40% 是「已修」。
如何判断:把这次和上次 audit 做 diff——文字 30%+ 重复就是没传 skip 列表。
5. 没文件锚点(file:line)
「建议改进 auth 流程的错误处理」这种条目没法立即执行——要花 20 分钟翻文件。报告显得长,其实是被大段含糊描述撑起来的。
如何判断:数有多少条带 file.ts:42 这种锚点——低于 50% 就没法直接排期。
6. 维度混杂:style + perf + security 一起跑
让 Codex 找”所有问题”时,它返回的是并集。不同维度的判断标准不一致,报告读起来就乱(漏注释和漏 CSRF 检查被给了一样的权重)。
如何判断:给每条打 security / perf / style / types / tests / docs 标签,直方图平铺到 4+ 个标签就是维度混了。
最短修复路径
按收益从高到低,前 3 步就能把”广 audit”变成 10 条可执行清单。
Step 1:只挑一个维度
每次只挑下面一个:
| 维度 | 什么时候跑 |
|---|---|
| security | 发版前;改了 auth / 支付 / 用户输入后 |
| perf | p95 延迟或 bundle size 退化时 |
| types | TypeScript 升级或大重构后 |
| tests | 每季度,或刚修完一个没补测试的 hotfix |
| style | 每季度跑一次,优先级最低 |
| docs | 新人 onboarding 前 |
安全审计单独跑,不要和 style 打包在一起。
Step 2:只选一个窄 scope
能落地的 scope 是「一个目录」或「一个功能」,不是「整个项目」。例子:
src/api/auth/— 一个功能模块src/components/billing/— 一个用户流migrations/*.sql— 一类文件
代码量大就切片跑,把发现合并进 issue tracker,不要合成一份巨型报告。
Step 3:用受约束的 audit prompt
把下面这段贴过去,填上方括号里的内容:
仅审计 [SCOPE] 下的 [DIMENSION] 问题。
约束:
- 最多 10 条,按严重度排序(P0 → P3)。
- 每条必须包含:
- severity(P0 = 阻塞发版,P1 = 发版前修,P2 = 下个迭代,P3 = 可选)
- file:line(或文件区间)
- 一句话问题描述
- 一句话修法
- 跳过纯外观问题(格式、命名),除非掩盖了 bug。
- 跳过这些已修事项:[贴上一轮 audit,或写"无"]。
- 不要提架构级重构。
用 markdown 表格输出,列:severity | file:line | problem | fix。
预期产出长这样:
| Sev | File:Line | Problem | Fix |
|---|---|---|---|
| P0 | src/api/auth/login.ts:42 | 密码用 `==` 比较,非常数时间 | 改 `crypto.timingSafeEqual` |
| P0 | src/api/auth/session.ts:118 | JWT 用 HS256,密钥在 env 不轮换 | 加 `kid` header,季度轮换 |
| P1 | src/api/auth/reset.ts:23 | 重置 token TTL 24h,RFC 建议 1h | `TOKEN_TTL` 降到 3600 |
| P2 | src/api/auth/middleware.ts:67 | 限流按 IP 不按账号 | key 加 `accountId` |
Step 4:进 issue tracker,不要留在 audit 文件里
每条 P0/P1 单独开 issue,标题里带 file:line。P2/P3 合并成一张「audit backlog」票。markdown 报告读完即弃。
# 用 gh CLI 批量从 Codex audit 建 issue
gh issue create -t "P0: login.ts:42 时序攻击" -b "Codex audit 2026-05-22"
Step 5:重审时传”skip”清单
第二轮 prompt:
审计 src/api/auth/ 的安全问题。
SKIP 这些已修事项:
- 常数时间密码比较(login.ts:42)
- JWT 轮换(session.ts:118)
- 重置 token TTL(reset.ts:23)
约束同第一轮。
得到的就是一份新清单,而不是同样 50 条再读一遍。
预防建议
- 每个维度维护一份 prompt 模板(
prompts/audit-security.md、prompts/audit-perf.md…),别现写 - 每次 audit 输出限 10 条;问题多就分片跑,下次再开
- 强制要求 severity + file:line + 一行修法,三者缺一即拒收
- 发现进 issue tracker,不要烂在 markdown——markdown 不更新,ticket 会被关
- 安全每发版审,性能每退化审,风格每季度审,按维度定 cadence
- 重审前把上一轮发现作为 skip 列表传进去,确保第 N 轮还在变短
相关阅读
标签: #Codex #Coding Agent #排查 #排查 #审计过广