Codex 在 SELECT 里写了 user.handle——但列名是 user.username。或者调 sendEmail(to, subject, body),但签名是 sendEmail({ to, subject, html })。或者硬编码 https://api.example.com——你项目里这个 URL 走 process.env.API_URL。代码看着对,但要么不编译、要么不跑、更糟跑到了错的目标。
这是「自信幻觉」:Codex 没读你的真实文件,从先验形状里推断。没有 grounding 即便能力再强也会猜。修法:把「先引述再写」做成硬规则,把每行生成代码锚到 Codex 必须先读的真实文件上。
常见原因
按命中率从高到低:
1. Codex 没开文件,从 import 推断
Prompt 里点了文件名,但 Codex 根本没打开过。它从这个函数在别处被 import 的样子、或者训练先验里猜了它的形状——参数细节就错了。
如何判断:在 chat 里搜读相关文件的 tool 调用,没有就是 Codex 凭想象。
2. AGENTS.md / CLAUDE.md 描述的是过去的现实
文档写 users 表有 handle 字段,6 个月前你改名成了 username,文档没更新——Codex 信了文档。
如何判断:在 AGENTS.md 里 grep Codex 用的错符号——存在就是文档过期。
3. Codex 套了「网红」泛模式
sendEmail(to, subject, body) 是 Node.js 博客里的经典写法,但你项目用 options-object 变体。Codex 默认走了网红那个。
如何判断:错签名匹配某个明显的开源库形状——Codex 套 meme 没套你的代码。
4. Schema 在 Codex 没读的生成文件里
Prisma 的 schema.prisma 是源,但类型在生成的 node_modules/.prisma/client/index.d.ts 里。Codex 两个都没读,退回猜。
如何判断:错类型/列出现在该用 Prisma 生成类型的地方。看 Codex 的 read 列表里有没有 schema.prisma 或生成的 .d.ts。
5. 环境变量名/形状是编的
你写 STRIPE_KEY=sk_test_...,Codex 用了 STRIPE_API_KEY(多了个词)。或者 process.env.PORT 直接当数字用——env 都是 string,需要 parseInt。
如何判断:Codex 的 env 名 + 形状对照 .env.example,不一致基本都是编的。
6. 过度相信过期的注释/JSDoc
函数有 JSDoc 写 @param userId: string,但实现其实接受 number | string。Codex 信了 doc,传 string 没事——直到别处 caller 传 number、类型系统又没拦。
如何判断:实现签名和 JSDoc 不一致——JSDoc 是谎言,实现是真相。
最短修复路径
按收益从高到低,Step 1 一步逮住 80% 的不安全假设。
Step 1:先引述再写
每个生成代码的 prompt 里加:
写任何要调用外部函数 / schema / 环境变量的代码前:
1. 读它定义所在的文件。
2. 在回答里原样引述相关行(签名 / 列 / export)。
3. 然后才写调用代码。
如果引述不出来(文件没找到、不编译),停下来问。
不要从 import 推断,也不要靠训练先验。
多一步,但根除了「我以为长这样」这种失败。
Step 2:schema 强制先读
你要对 `users` 写查询。
先:读 `prisma/schema.prisma`,把 `User` 模型原样引述。
然后:只用引述里出现过的列写查询。
缺列就建议加(不要假设有)。
没 ORM 就指向 migration 或 DDL 文件。
Step 3:函数签名强制先读
你要调 `sendEmail`。
先:读 `src/lib/email.ts`,把 export 签名引述出来。
然后:按引述的签名写调用。
引述不出来就是文件根本不存在——这种不一致最好在写代码前就显出来。
Step 4:环境变量锚到 .env.example
你要用一个环境变量。
1. 读 `.env.example`,引述相关行。
2. 用 `.env.example` 里出现过的精确名字——不要编变体。
3. 没有的话建议加(不要假设)。
Step 5:写完用 grep 验证
不要信汇报——核实引用的符号真存在:
# 新代码里每个独立标识符都 grep 一下定义
grep -rn "function sendEmail\|export const sendEmail\|export function sendEmail" src/
grep -rn "model User\|interface User" prisma/ src/
grep 不到 = Codex 用了不存在的符号。
Step 6:靠 AGENTS.md 之前先刷新
AGENTS.md 超过 3 个月没更新而代码变化很快——它在骗 Codex。看时间戳:
# 关键 doc 和关键文件的最后更新时间
git log -1 --format="%ai" AGENTS.md
git log -1 --format="%ai" prisma/schema.prisma
git log -1 --format="%ai" src/lib/email.ts
schema/email 比 AGENTS.md 新——刷新 AGENTS.md 或删掉过期段,让 Codex 直接读源。
预防建议
- “Quote before write” 写进 AGENTS.md 永久规则,不要每个 prompt 重复
- schema / env / 函数签名的源文件在 AGENTS.md 里点名
- 把生成类型(Prisma、GraphQL codegen)当真相——Codex 应该看它们
- 季度审 AGENTS.md,删与代码不一致的部分
- Codex 写完后 grep 引用的符号——缺一个就是幻觉
- AI 的自信和正确率没关系——验证,别信语气
相关阅读
标签: #Codex #Coding Agent #排查 #排查 #不安全假设