1. 为什么明明把任务丢给了 Codex,它却总是答非所问
先说一个我观察到的普遍现象:很多人对 Codex 这类编程助手的第一印象是“它好像很聪明,又好像不太聪明”。让它写段排序算法、生成个接口文档,表现还不错;可一旦涉及具体的业务代码,比如“帮我看看这个模块为什么内存涨得这么快”“给这个函数补测试”,它就容易东扯西扯,给出一些看起来能用、实际上跟你的项目半毛钱关系都没有的答案。
问题出在哪?大部分时候不是模型不行,而是提问的方式太随意了。
你想想,我们平时在代码里写注释都嫌麻烦,更别说给 Codex 完整交代一段代码的背景、约束、输入输出和期望了。但模型并不知道你真实的工程上下文,它只能靠你这句话里的信息量来做推断。你说“帮我把这段代码优化一下”,它会默认你是想让它把变量名改短一点、把 if 换成 switch;你说“这个函数有问题”,它会默认你只是想知道语法错误,而不是想让它帮忙查并发竞态。
我觉得程序员向 Codex 要产出,跟向一个刚入职、有一定基础但完全不了解你项目的实习生布置任务是类似的。你不告诉他边界条件、不告诉他验收标准、不告诉他项目里哪些约定不能破坏,他交上来的东西大概率得返工。
所以我才把常用的提问方式整理成几张“急救卡”,核心思路就一条:先诊断,后开方,再复诊。也就是先让 Codex 理解现状,再让它给出方案,最后根据实际运行结果继续追问。这套思路并不复杂,但把它固定成模板以后,每天能省下的沟通成本非常可观,尤其是那种一天要问十几轮、二十几轮的高强度开发日。
这篇文章想做的事很简单:给你七张可以直接抄的提问卡片,每张都带一个真实场景下的完整提问示例;再讲一讲怎么把这些卡片组合起来,应对那种“既要改 A、又要测 B、还要不破坏 C”的复杂需求。
如果你是刚开始用 Codex,或者用了一段时间但总觉得产出不稳定,这篇内容应该能帮你找到问题所在。需要说明的是,下文涉及的代码场景都做了脱敏处理,但提问结构和思维方式是可以直接迁移到你自己项目里的。
2. 七张“急救卡”逐张拆解:每张都有可直接套用的完整案例
这七张卡片分别对应七类高频开发任务:重构优化、故障定位、文档注释、测试补齐、代码评审、跨语言迁移、陌生代码库速览。每一张我都会给出典型场景、完整提问示例,以及提问时最容易踩的坑。
2.1 重构卡:让 Codex 只动结构、不动行为
重构是所有卡片里最容易翻车的一类,原因很简单:模型往往分不清“重构”和“顺手改逻辑”的边界。
我见过最典型的一个事故是这样的:某开发者让 Codex 把一个 Python 函数改成更容易理解的写法,模型不仅压缩了代码量,还顺手改变了两个边界条件下的返回行为。单测跑不过去,这位同事花了大半天时间才意识到问题出在模型“好心帮忙”改了一个分支判断。
后来我把重构类提问固定在这样一个结构里,用起来稳定很多:
角色和目标先交代清楚,比如“你是一名熟悉该业务的老同事”;然后是位置,给出具体函数名和文件路径;接着是动机,说清楚是为了可读性、性能还是消除重复;最关键的最后一句一定是“只允许调整结构和命名,不允许改变任何输入输出行为和异常抛出逻辑”。
比如这种写法:
有一个函数 calculate_discount,定义在 src/pricing.py 里。 当前问题是它的嵌套 if 太深,读起来很费劲,整个函数有七八层缩进。 我需要你帮我重构它,目标是降低圈复杂度。 要求: 1. 保持函数签名不变; 2. 保持所有输入输出映射关系不变; 3. 每一步重构都要写一句理由,说明为什么这样拆。 请先输出重构后的完整代码,再输出每一处改动的动机说明。这里有个关键点:一定要点名“输入输出映射关系”,它相当于给 Codex 划了一条数学上可检验的底线。如果它输出的代码里出现了一个原函数根本不会出现的异常类型,或者某个边界条件从“返回 None”变成了“抛异常”,你一眼就能发现。
另外,我自己会额外加一句“不要引入第三方库”。模型有时候图省事,直接给你推荐装一个什么库来简化逻辑,这在生产项目里往往是不想接受的,尤其是在依赖管理严格的团队里。
2.2 定位卡:不急着让 Codex 修,先让它列出最可能的原因
遇到线上问题或者测试挂掉的时候,很多人第一反应是直接把报错信息丢给 Codex,问它“怎么修”。我的建议是反过来——先问它“可能是哪几个原因”,让它按概率排出前三名。
这不是绕弯子。Codex 给出的“修法”通常是基于报错文本的浅层推断,它没有执行过你的服务,看不到真实的调用链和栈帧。但如果让它基于报错文本和经验知识列出假设,本质上就是在帮我们缩小排查范围,这是它最擅长也最不容易出错的部分。
我常用的排错提问长这样:
我的程序在调用 send_message 时抛了 TypeError: unsupported operand type(s) for +: 'NoneType' and 'str',发生在 message_queue.py 的第 44 行。 我已经确认过第 44 行确实是字符串拼接操作。 请列出可能导致这个 None 出现在这里的三个最可能原因,按可能性从高到低排列。 每个原因需要说明:可能发生的业务场景、如何用日志或调试器快速验证、以及相应的修复思路。 先不要直接给我改代码。注意最后那半句,“先不要直接给我改代码”,非常关键。一旦你允许它边分析边改,它写出来的修复代码往往是针对最表面的那个假设,万一假设错了,反而浪费更多时间。
等它列完假设,你再根据自己的业务知识挑一个最可疑的方向深入,这时候再让它输出小范围的修复补丁,准确率会高很多。这其实就是医生看病的逻辑——先鉴别诊断,再开处方。
还有一个经验:在排错类提问中,文件路径和行号一定要写准,行号可以不确定,但路径和函数名最好不要含糊。Codex 对位置信息的理解方式跟人脑不太一样,你不给位置,它就按“最常见的使用方式”猜,而你项目里的使用方式往往不是最常见的。
2.3 注释卡:让注释服务新人,而不是服务 Codex
写注释这件事,争议一直很大。有人认为代码即文档,注释越少越好;有人觉得没有注释的新人接手就是灾难。我的态度是折中:代码应该自解释,但涉及业务意图、边界约定和非显然处理的地方,必须有注释。
Codex 有个很大的优势——耐性。让它把一个一百行的函数逐段解释清楚,它不会烦。但它的默认注释风格有两个问题:第一,废话太多,喜欢解释那些代码本身已经很清楚的东西,比如“这里将 x 加一”;第二,中文注释里偶尔混入英文术语,或者在注释里编造业务背景。
所以我的注释卡长这样:
给下面的函数 generate_report 写中文注释。 函数路径在 src/reporter.py。 要求: 1. 给函数整体写一段说明,包括它服务于什么业务流程、前置条件和副作用; 2. 给核心代码块写行内注释,只解释“为什么这样做”和“这里的边界约定”,不解释“做了什么”; 3. 不允许出现“这里将值赋值给变量”之类的废话; 4. 若某处代码本身含义模糊,请明确指出“此处理解为...,如果实际意图不同请纠正我”。你可能会问,最后一条会不会有点多余?其实很有用。模型对代码的理解偶尔会跑偏,比如把一个重试逻辑理解成业务判断。如果它敢于把理解说出来,你就能在合并注释之前发现这种偏差,而不是看着一个看起来很合理的注释被提交进去。
至于“前置条件和副作用”,这两个词是我从工程文档里借鉴来的,效果奇佳。让 Codex 明确写出“这个函数假设 config 里一定包含 api_key”“这个函数会修改 session 状态”,往往能暴露出很多平时没人注意的隐性依赖。
2.4 测试卡:把正常路径、边界值、异常路径写进同一个请求
让 Codex 补测试是性价比极高的一件事,但默认生成的测试往往只覆盖正常路径,个别时候连正常路径都覆盖得很敷衍。我的做法是在提问时主动规定测试结构,要求它按三层来写。
三层指的是:正常路径、边界值、异常场景。这个框架不是给模型定的,是我们写测试本来就应该遵守的基本逻辑,只不过用提问的方式把它表达出来。
示例提问:
为以下函数编写 Jest 测试用例,文件路径是 src/validator.ts。 函数签名: validateUsername(username: string): { ok: boolean; reason?: string } 已知规则:长度 3 到 16 个字符;只能包含字母、数字、下划线;不能以下划线开头。 请按以下结构输出: 1. 正常路径用例:两个,覆盖长度中间值和含数字/下划线的组合; 2. 边界值用例:三个,覆盖长度等于 3、长度等于 16、以下划线开头; 3. 异常场景用例:三个,覆盖长度越界、包含非法字符、传入空字符串。 每个用例请写明断言的内容和为什么这样断言。这里有个容易被忽略的点:在提问里给出具体的输入值范围。如果你只写“请给 validateUsername 写测试”,模型可能拿外部输入、类型异常、undefined 等情况来充数,反而把你真正关心的业务边界给漏了。你喂给它的规则越具体,它产出的测试就越贴合你的验收标准。
我还习惯在得到测试代码后追加一轮:“把这些用例当作黑盒测试,反推一下如果被测函数是这样实现的,哪些用例会失败?”这一步本质上是在做变异测试的思路。Codex 会帮你挑出那些即使代码写错也能通过的“弱断言”,然后你再针对性修正。
2.5 评审卡:以某位资深同事的口吻挑剔代码
代码评审这事,在 GitHub/GitLab 上做是一套流程,但你也可以先在本地用 Codex 过一遍,减少给同事添麻烦的次数。我的经验是:让 Codex 以特定角色来评审,比不带角色的评审效果翻倍。
不带角色的评审,模型输出的大多是比较泛的赞美和“建议增加注释”“建议拆分函数”这种万金油。带上角色之后,它会真的去挑刺,而且挑刺的方向更接近一个资深工程师的视角。
你看这个例子:
假设你是一位注重可维护性和并发安全的高级工程师,请评审下面这段代码的 diff。 代码逻辑:一个购物车接口,收到请求后更新 Redis 中的库存数据。 评审要求: 1. 重点关注并发问题、资源泄漏、异常处理遗漏、可扩展性风险; 2. 每条意见标注严重级别:严重/一般/建议; 3. 每条意见给出针对性的修改建议,并说明为什么; 4. 如果某处写法你认为是合理的,直接忽略,不用客气。我特别强调“如果某处写法合理,直接忽略”,因为模型的默认输出太长,为了“显得认真”,它会强行凑几条不痛不痒的意见。这一句能帮你把噪音砍掉一大半。
从实际效果看,Codex 在这类任务里最突出的能力是发现并发问题。比如“这个 Redis 操作不是原子的,存在超卖风险”,或者“这个连接没放进 finally 里,异常时会泄漏”。这些点恰恰是人肉评审时最容易疲劳忽略的。
2.6 迁移卡:从 Python 到 JavaScript,重点是要“本地化风格”
跨语言迁移,也是我很常用 Codex 的场景之一。但很多人的提问方式就是简单一句“把这段 Python 转成 JavaScript”。结果呢?生成的东西看起来像“Python 语法换皮”,处处带着非惯用的痕迹。
原因在于,模型默认会保守地保留原语言的语法结构,比如把 Python 的列表推导式翻译成 JavaScript 的 for 循环,把字典推导式翻译成连续的对象赋值。它需要明确指令,才会使用目标语言的惯用表达。
我常用的迁移卡片长这样:
请把下面这段 Python 代码迁移成 TypeScript。 代码功能:从配置文件读取一组规则,按优先级排序后返回。 迁移要求: 1. 不逐行直译,请按照 TypeScript 的惯用法重写,比如充分利用类型、接口、可选链; 2. 不要为了对齐源结构而引入多余的类或装饰器; 3. 对原代码中的动态类型行为,请显式定义联合类型并给出注释; 4. 迁移后请指出两处你认为目标语言做得更好的地方。“指出两处你认为目标语言做得更好的地方”这句话是我后来加上的。它有两个作用:一是强迫模型认真思考两门语言的差异,而不是机械替换;二是让你在审查迁移结果时,能同步学习到一些目标语言的最佳实践。
再补一点关于迁移的通用原则:数据格式和业务语义优先于语法。你在提问里最好能明确说一句“这两段代码必须保持相同的输入输出结构,尤其是日期格式、数值精度、异常类型”。迁移脚本最容易踩的坑就是精度和时区问题,提前打了预防针,后面省一堆事。
2.7 速览卡:把陌生代码库变成一次有向导的参观
接手别人留下的项目,最怕的就是不知道从哪里看起。这时候我用于“速览”的提问模板是这样的:先让 Codex 给出代码库的整体结构地图,再让它挑出最核心的一条主链路来精讲。
我之前完全没有接触过这个项目,请帮我快速了解它。 项目根目录是 /workspace/order-service,主要语言是 Java。 请先完成以下三件事: 1. 用 200 字以内概括这个服务的职责和入口点; 2. 列出一个新订单从接口进入、经过哪些核心类、最终落库的完整调用链; 3. 指出三个你认为要改这个项目最先需要理解的文件,并分别说明原因。这个模板最值钱的地方在第三问——“如果只让你读三个文件,你会选哪三个”。模型给出的答案,往往是一个合格的程序员真正理解项目时才会关注的枢纽文件,比如启动类、配置类、核心领域模型。而不是泛泛地列几个 Controller 让你挨个读。
拿到速览结果后,我的习惯流程是:打开它提到的几个文件,人工快速过一遍,然后回来继续追问细节。这比自己从 readme 开始读、再到目录里摸索,至少快一个下午。
3. 模板的组合写法:两个甚至三个急救卡变成一条工作流
单张卡片本身解决的是孤立问题。但在真实项目中,很少有“只重构”“只补测试”“只看代码库”这种单一动作的任务。大多数开发工作是一个链条:先理解,再动手,再验证。所以模板之间的组合能力,才是这套急救卡的真正价值所在。
3.1 最值得练的三个组合场景
我日常使用频率最高的是下面这三种组合,分别对应接手旧代码、线上紧急处理和开发新功能。
场景一是“接手历史遗留模块”:
- 第一步,用速览卡(2.7)让 Codex 概括这个模块的职责和数据流;
- 第二步,用测试卡(2.4)给关键函数先补上基线测试;
- 第三步,用重构卡(2.1)在测试的保护下开始改结构。
这三步的逻辑很像修老房子:先画图纸,再打地基,最后才动土木。没有测试基线就重构,和摸着黑拆承重墙没什么两样。
场景二是“线上紧急故障”:
- 第一步,用定位卡(2.2)让 Codex 列出可能原因;
- 第二步,从日志系统把真实堆栈贴回去,追问“如果是这个原因,修复方案是什么”;
- 第三步,用评审卡(2.5)让 Codex 检查修复补丁本身会不会带来新问题。
线上问题最重要的约束是不扩大影响范围。定位卡帮你把猜测范围框住,评审卡帮你在发布前拦住一刀切式的修复。
场景三是“开发新功能”:
- 先用速览卡(2.7)确认新功能应该挂在现有架构的哪个位置;
- 然后用一个不是模板中的自由提问,让 Codex 给出实现方案框架;
- 最后把方案框架当作约束,再进去用重构卡(2.1)优化细节。
这本质上是“先问架构再问实现”的两种粒度切换。很多人一上来就让 Codex 直接生成一个完整的 Controller+Service+Repository,结果模型把项目里并不存在的中间层也幻想出来了。分两步走,就是为了避免这种过度生成。
3.2 组合时的三条上下文控制铁律
模板组合最怕遇到的问题,不是模型不懂,而是它“记混了”——把上一轮你展示的代码文件,当成这一轮需要修改的对象。为了避免这种情况,我给自己定了几条必守的原则。
第一,每轮对话都明确点出当前目标是哪三个文件。如果你在上一轮贴了 a.ts、b.ts、c.ts 三个文件,这一轮实际只想改 a.ts,那就开口先写“本轮只考虑 a.ts,b.ts 和 c.ts 只作为调用方背景”。模型没有人类的短时记忆边界,你不强调,它就可能把三个文件都改了。
第二,对模型的输出,别直接相信“验证通过”这句话。Codex 有时候会在代码注释里写“// 以上代码已通过测试”,实际上它根本没跑过。这个时候我会用一条追问模板:“你刚才的修改涉及 b.ts 的 import 变更,请确认这个变更不会破坏 xxx 场景下的类型推导。”说白了,把模型当作一个诚实但偶尔迷糊的同事,需要反复确认它说的验证到底靠不靠谱。
第三,每完成一轮任务,主动发起一次上下文压缩。比如“前面讨论的定位过程已经结束了,现在只保留最终结论”。这能防止 Codex 在后续对话里被前面很长一串排错细节干扰,重新回到正确轨道上来。很多长会话跑到后面质量下降,就是因为没有做这种显式的上下文切换。
组合写法还有一个小技巧:把上一轮 Codex 的代码输出,连同你的修改意见,一起作为新问题的一部分贴回去。比如“我改了你输出的第 12 行,去掉了可选链,因为业务上这个字段一定存在,请基于新的代码重新审查”。看起来只是单纯复述,实际上这是用“增量反馈”的方式,让模型逐步逼近你真正想要的结果。
4. 从急救卡到日常习惯:排除不稳定输出和沉淀个人提问库
模板能解决“不知道该怎么说”的问题,但不能解决所有问题。在我用 Codex 大半年之后,剩下的工作主要是两件事:一是处理它偶尔的不稳定表现,二是把每次有效的提问沉淀成自己的“提问弹药库”。
4.1 面对 Codex 的不稳定输出,我的处理节奏
不稳定主要出现在三类情况:输出过长导致后半部分质量下降;多次生成结果不一致;以及对某些小众语言的支持明显比对 Python、JavaScript 弱。
输出过长的问题,典型的场景是让它生成一个完整模块,结果写到后面它忘了前面的约定,比如变量名不一致、函数签名漂移。我的应对很简单,在一开始就给输出加长度约束:“请先输出第一个核心文件的完整代码,其他文件用接口占位,分两轮完成。”把大任务拆短,正好避开了模型长文本容易走神的问题。
多次生成结果不一致的,常见于“设计类”提问,因为没有唯一正确答案,模型每次给出的方案侧重不同。我不会跟它在同一轮里死磕,而是多开几轮让它各给一版方案,然后人工横向对比,挑出最符合当前项目约束的那一版,再把挑中的版本作为唯一基准继续往下聊。
至于小众语言,说实话目前没有特别好的办法。我的经验是退一步,先用 Codex 写出基于逻辑的伪代码,然后挂一个“请标注哪些语法需要到官方文档确认”的指令,把自己从容易出错的部分中解放出来。
4.2 怎么构建属于自己的十六张、二十张提问卡
最开始的我,也是从别人分享的模板开始抄的,但抄完总觉得不完全贴合自己的项目。后来我改了一个策略:不再背模板本身,而是背“模板里的变量名”。
什么意思?就是每个模板里,真正有用的其实不只是句式,而是那几个需要你手动填的槽位。比如重构卡里必须填的四个槽位是文件路径、函数名、重构动机、不可破坏的约束;定位卡里必须填的是报错信息、代码位置、已排查方向、处理顺序;测试卡里必须填的是被测对象、规则列表、用例结构。
把这些变量抽象出来之后,我就给自己建了一个纯文本的 prompt 片段库,按用途分类存放。合在一起是一个大的“急救手册”,但实际上每个条目都可以单独取用,也可以跟别的条目拼装。平时写代码时,我不需要复制整段模板,只需要按场景召唤对应的几行。
这里有一点想提醒你:别追求完美提示词,先追求稳定产出。我见过有的开发者,为了一个提问反复措辞十几分钟,这种求完美的心态反而背离了模板的意义。模板的价值是让你三十秒内发出请求,而不是三十秒内写出完美请求。就算第一次给的产出方向不对,后面加一轮修正也能拉回来,比你一开始陷入分析瘫痪要强得多。
还有一个更进阶的用法,是把这些提示词片段沉淀到团队的文档里。比如团队内部的“快捷命令手册”“Codex 使用约定”一类的地方。新人接手项目时,直接看到“重构前先贴测试卡”“未知文件先走势图卡”,比一遍遍口头讲解靠谱多了。团队里每个人都在用同一套语言跟 Codex 沟通,代码审查、交接成本也会低不少。
说到底,急救卡不是让你变成提问大师,而是让 Codex 在你手里从“偶尔好用”变成“大部分时候都算得上顺手”。如果你下次打开编辑器,第一反应不是敲一行普通的话,而是顺手抽出对应场景那张卡,那你已经读完这篇文章最大的收获了。