不知道你有没有这种经历:明明把一段报错原封不动粘给了 Codex,它给出的方案却治标不治本;明明只说了一句“帮我优化下这段代码”,它改完反而让功能崩了。问题常常不在于 Codex 本身多笨,而在于提问方式没有给它铺好路。很多人把 Codex 当成一个“能听懂人话的搜索框”,但它的真实工作方式是先理解你给的上下文,再预测下一步最合理的输出。你给的信息越结构化,它越能精准命中你的意图。
这篇急救卡,就是给程序员在真实开发中应对 Codex 时准备的。我把日常高频场景拆成了 7 个通用模板,再讲怎么把模板组合起来形成一套连续追问的工作流。无论你是刚开始用 AI 辅助写代码的新手,还是已经被 Codex 带偏过几次的老手,都可以直接照着抄,改几个占位符就能用。
1. 为什么你问 Codex 总答非所问:问题出在提问姿势
1.1 它不是在“猜心思”,而是在“读上下文”
很多程序员对 Codex 有一个错误预期:觉得我人话已经说清楚了,它应该能理解。但你忽略了一个事实——Codex 没有在你电脑前坐过,不知道你的项目结构,不知道你的命名习惯,也不知道你心里默认的那些“显然不能改”的约束。
打个比方:这就像你让一个新入职的同事改一个模块,你只说了一句“这个函数有点慢,优化一下”。同事大概率会反问:哪个函数?慢到什么程度?是响应慢还是吞吐慢?优化用什么指标衡量?能不能动其他模块?Codex 其实也一样,只不过它不会主动问你那么多问题,它会直接按它的默认假设开始干。默认假设一旦和你的真实意图偏差,结果就会变成“答非所问”。
所以提问的第一原则是:把 Codex 当成一个“能力很强但完全不了解项目背景的新同事”,你需要把背景、任务、边界、预期输出都说清楚。它对你的项目了解多少,完全取决于你给了多少上下文。
1.2 一句话提问与结构化提问的差异对比
我见过太多“一句话提问”,比如:
- “这段代码有问题吗?”
- “怎么优化这个接口?”
- “帮我写个登录功能。”
这三个问题单独看都没毛病,但 Codex 拿到之后会面临信息不足。以“这段代码有问题吗”为例,它不知道你说的“问题”是指逻辑 bug、性能瓶颈、风格问题还是安全隐患,也不知道这段代码是独立片段还是属于某个系统的一部分,更不知道你希望它用口头回答还是直接改代码。
把同样的问题结构化之后,效果会明显不同。我整理了一个对比:
| 提问方式 | 用户输入 | Codex 的典型反应 | 结果质量 |
|---|---|---|---|
| 模糊提问 | 这段代码有问题吗 | 泛泛列出可能的风险点 | 需要反复追问才能定位 |
| 结构化提问 | 这段代码用于核对订单金额,请按“正确性→安全性→可读性”三个层次检查 | 按指定维度输出结论 | 一次拿到可用答案 |
结构化不是说要把句子写得像公文,而是把“角色、任务、约束、输入、输出”这五个关键字段交代清楚。Codex 的上下文理解能力很强,但它不会替你脑补你没有说的信息。宁可多说一句边界条件,也不要让它自由发挥。
2. 急救卡第一层:7 个通用模板逐个拆解
这一层是整个急救卡的主体。我按日常开发中最高频的场景选了 7 个模板,每个模板都给出了核心结构、示例和常见变体。你不需要按顺序背下来,先用前三个解决 80% 的日常问题,后面几个按需翻看。
2.1 模板一:任务定位模板(角色 + 目标 + 约束)
这是所有模板的“地基”,适合“从零开始写一段代码”或“完成一个独立功能”的场景。它把一次任务拆成四个部分:角色、目标、约束、输入输出。
基本写法:
角色:资深 Python 后端工程师 目标:为一个 Flask 项目编写一个商品列表分页接口 约束: - 使用 Flask 自带的路由方式,不引入新框架 - 分页参数为 page 和 page_size,page 从 1 开始 - 返回格式为 {"items": [...], "total": n},items 中每项包含 id、name、price - 异常情况返回 400 和错误信息 输入:已有商品表 model Product 输出:给出完整路由代码,并用注释说明每个关键步骤这个模板的关键在于“角色”和“约束”不是装饰。角色能让 Codex 自动调整回答的深度和代码风格,比如“资深后端工程师”比“新手”更倾向于考虑异常处理、边界条件和可维护性;约束则能防止它跑偏。很多人在第一步就漏掉“返回格式”和“不允许引入新框架”这类约束,导致生成的代码根本不贴合项目实际。
实战中我还会在这个模板后追加一句“如果某个需求在现有项目中难以实现,请直接说明风险,不要强行生成代码”。这句能有效减少那种看起来很完整、但一跑就崩的“幻觉代码”。
2.2 模板二:代码修改模板(现状 + 期望 + 边界)
修改现有代码是这个场景里最容易出错的一种提问。因为 Codex 没有直接看你整个文件,它只能根据你贴过来的片段做局部判断。如果你不说清楚“哪里不能动”,它很可能顺手把其他逻辑也重构一遍。
修改模板的核心结构:
现状: 现有函数 calculate_discount(order): if order["total"] > 100: return order["total"] * 0.9 return order["total"] 期望行为: - 当订单 total 大于 500 时打 8 折 - 大于 100 时打 9.2 折 - 小于等于 100 不打折 - 如果订单包含 coupon 字段,则先减 coupon 再计算折扣 边界: - 保留原函数名和参数名,不动其他函数 - 不改变返回类型 输出: - 给出修改后的完整函数 - 用 Markdown 表格列出输入和期望输出这个模板能有效避免 Codex 自作主张。尤其是“边界”这一栏,相当于给修改画了一条红线。你还可以更激进一点,直接在“边界”里写“除函数体重写外,禁止修改任何其他代码”,这样它输出的 diff 会非常干净。
我实际用过很多次这个模板,最大的体验是:只要你把“期望行为”写成可验证的输入输出对,Codex 的修改准确率会大幅提升。它不需要猜“9 折”到底是指“乘以 0.9”还是“减去 10%”,因为你的例子已经把语义锁死了。
2.3 模板三:报错诊断模板(报错全文 + 已尝试 + 上下文)
报错排错大概是 Codex 使用率最高的场景,但绝大多数人只会贴一行错误摘要。错误摘要通常只告诉你结果,不告诉你触发路径,Codex 只能猜。更糟糕的是,如果代码里还有别的隐藏问题,猜中的概率就更低了。
推荐结构:
报错信息: Traceback (most recent call last): File "app.py", line 32, in get_user_profile return user_profile["phone"] KeyError: 'phone' 触发步骤: 1. 登录用户进入个人中心 2. 点击“编辑资料” 3. 页面加载时调用 get_user_profile 已尝试: - 打印过 user_profile 内容,确实没有 phone 字段 - 数据库表结构包含 phone 列,但部分老数据可能缺失 相关代码: def get_user_profile(user_id): user_profile = get_db_user(user_id) return user_profile["phone"] 请: 1. 定位最可能的原因 2. 给出能兼容老数据缺失的修复方案 3. 用同一套逻辑处理类似字段缺失问题这里有个关键点:不要只给“相关代码”,也要给“触发步骤”。Codex 在分析报错时,如果能知道“什么条件下触发”,就能判断是数据问题、逻辑问题还是时序问题。比如上面这个例子,如果不知道“老数据可能缺失”,它可能会建议你检查数据库表结构,把排查方向带偏。
另外,“已尝试”这一栏一定要写。它能告诉 Codex“哪些路已经走过了”,避免它浪费时间建议你重复操作。如果你什么都不写,它可能会让你先打印、再检查、再验证,来回浪费好几轮。
2.4 模板四:代码解释模板(代码片段 + 关注点 + 输出要求)
这个模板适合你接手一段别人的代码,或者在 Codex 生成代码后想弄明白它的思路。很多人上来就问“这代码什么意思”,Codex 会把每一行都解释一遍,信息量太大反而抓不住重点。
代码解释模板的核心在于“限定关注点”和“限定输出形式”。你可以这样写:
代码片段: def merge_intervals(intervals): intervals.sort(key=lambda x: x[0]) merged = [] for interval in intervals: if not merged or merged[-1][1] < interval[0]: merged.append(interval) else: merged[-1][1] = max(merged[-1][1], interval[1]) return merged 关注点: - 为什么先按第一个元素排序 - merged[-1][1] < interval[0] 这个判断条件的边界情况 - 时间复杂度是多少 输出要求: - 不要逐行解释 - 先用两句话概括整体思路,再回答我列出的三个关注点限定输出形式后,Codex 就不会给你甩一篇冗长的注释版代码,而是真正回答你关心的点。我自己常用的输出要求还有“用比喻解释”“用图示文字描述流程”“只回答是与不是,并给出一行理由”,不同场景调不同输出格式,省事不少。
如果你是在做 Code Review,还可以在这个模板后面加一句“请指出代码中可能导致 bug 的边界条件,并用 1-5 分评估严重程度”。这会让它从“解释器”切换成“审查者”,非常实用。
2.5 模板五:方案选型模板(需求 + 候选 + 对比维度)
很多技术选型问题,比如“消息队列用哪个”“数据库连接池选哪个”,直接问“推荐什么”往往得到一堆正确答案,但答案不一定适合你的场景。因为“适合”这件事高度依赖你的团队规模、流量水位、运维能力、业务增长预期,这些 Codex 都不知道。
方案选型模板这样写:
需求背景: 我们团队 6 人,主要使用 Java Spring Boot,目前项目日均请求量 1 万,订单模块需要异步处理短信通知。 候选方案: A. RabbitMQ B. Kafka C. 普通线程池 + 数据库状态表 对比维度: - 团队学习成本 - 部署和运维复杂度 - 消息丢失带来的业务影响 - 未来半年请求量翻倍的扩展空间 请: 1. 按上面 4 个维度逐一打分(1-5 分) 2. 给出一个推荐结论 3. 说明在什么条件下候选方案会反转这个模板比“哪个好”高明在:它让 Codex 先按你关心的维度分析,再给结论,而不是泛泛而谈。“说明在什么条件下会反转”尤其有用,因为我见过太多选型讨论最后变成站队。加了这个要求后,你能看到方案背后的适用边界,决策信息会完整很多。
使用这个模板时,候选人不要超过 3 个。候选人一多,对比表格会过于复杂,Codex 容易陷入“全世界都很好”的折中话术。
2.6 模板六:重构/优化模板(现状 + 痛点 + 约束 + 验收)
重构类提问最怕的是“无标准优化”。你觉得代码不够优雅,但 Codex 不知道“优雅”具体指什么。它可能把变量名改得漂漂亮亮,却让你的性能原地踏步,甚至破坏行为。
重构优化模板:
现状: @RequestMapping("/order") public Order getOrder(@RequestParam Long id) { Order order = orderService.findById(id); if (order == null) { throw new RuntimeException("order not found"); } return order; } 痛点: - RuntimeException 不能返回 404,前端无法友好处理 - 方法里缺少对 id <= 0 的校验 - orderService.findById 可能返回 null,但异常类型不准确 约束: - 不改变对外接口路径和参数 - 只重构这个方法本身,不修改 orderService - 保持 Spring Boot 风格 验收: - 参数非法时返回 400 - 订单不存在时返回 404 - 正常请求仍返回 Order JSON“验收”是这个模板的灵魂。你给出的验收条件越多,Codex 的改动就越有方向。它甚至能根据验收条件自己先写一个简单的测试思路,然后回头检验自己的改动。如果你没写验收,它就会基于自己的审美来改,结果经常不符合你的预期。
我在实际重构中还会加一句“如果某个改动涉及其他模块,请指出而不是直接实现”。这个边界可以避免 Codex 在重构时悄悄修改依赖函数的签名,造成连锁崩坏。
2.7 模板七:学习/演练模板(目标 + 例子 + 难度 + 要求)
最后一个模板不是直接用于写业务代码,而是用来让 Codex 带你理解一个知识点或做技术练习。很多程序员把 Codex 当“答案生成器”,但它其实是一个不错的“陪练”。只要你会提学习型问题。
学习模板:
目标:理解 Python 装饰器的执行顺序 前置知识:我会写简单的函数,知道 *args 和 **kwargs 难度:从入门到中等 请: 1. 先给一个不超过 20 行的最小例子,演示装饰器叠加时从下到上执行的机制 2. 用生活类比解释“外层装饰器先接收函数”的过程 3. 对比“不加装饰器”和“加两个装饰器”两种情况的输出 4. 最后给我 3 道练习题,答案放在回复的折叠区这个模板的价值在于它把“学习路径”拆成了例题、类比、对比、练习四步,而不是让 Codex 甩给你一段百科体解释。对学习型提问,我也会加“答案暂时不要直接给出,先让我自己思考”这类要求,让 Codex 扮演一个引导者而不是答题机器。
3. 进阶组合写法:把单一模板拼成完整工作流
单独用模板能解决单次问题,但真实开发是连续过程。一个需求通常包含理解、编码、调试、优化、代码审查好几个阶段,每个阶段用不同的提问模板就已经比很多人强了,但真正提升效率的是把它们组合起来。
3.1 组合式提问的基本规则:一段一问、先域后题
组合不是把七个模板拼成一篇 500 字的小作文。你写一个超长提示,Codex 反而会迷失在信息里。组合的核心规则是两个:一段一问、先给领域背景再给具体任务。
所谓“一段一问”,是说每轮对话里只推进一个任务。比如你第一轮用“任务定位模板”让 Codex 写接口,第二轮发现报错,就应该单独开出“报错诊断模板”,把新信息交给它,而不是在第一轮的问题后面追加“另外刚才那个代码跑起来报错了”。追加会让 Codex 混淆“修改原方案”和“诊断新问题”两个意图。
“先给领域背景”的意思是,如果接下来的几个问题都围绕“订单模块”,那第一轮先把项目背景说清楚,后续每轮只需要引用“上面提到的订单模块”,而不是每轮都重复背景。Codex 的上下文窗口有限,重复背景会挤压真正有用的信息空间。
3.2 常见组合套路:新增功能 + 报错诊断、重构 + 代码解释、方案选型 + 落地实施
我实际用下来最顺手的组合套路有三个。
第一个是“新增功能 + 报错诊断”。先用任务定位模板让 Codex 实现功能,拿到答案后直接运行。如果报错,不要在同一轮里说“你写的代码有 bug”,而是你会话滚动到新的一轮,用报错诊断模板把报错全文、触发步骤、已尝试方案和刚才的代码一起贴进去。因为 Codex 对“上一轮自己生成的代码”有很强的上下文记忆,只要你没有开新会话,它会主动结合刚才的实现来分析,修复效果通常比重新问一遍要好。
第二个是“重构 + 代码解释”。我一般在让 Codex 重构之前,会先让它解释现有代码,尤其是我刚接手的模块。解释完,我心中有一个“逻辑基线”,再把这个基线作为重构后的验收条件提交给它。比如代码解释模板里它告诉我某个字段为空时会导致数组越界,我在重构模板的验收条件里就会明确写“当字段为空时必须返回空列表”。有了这层关系,重构风险会小很多。
第三个是“方案选型 + 落地实施”。先用方案选型模板确定技术方向,然后立刻用任务定位模板让 Codex 按选定的方案写具体实现。这个组合的关键在于,方案选型模板最后一定要有“落地约束总结”,比如“最终结论:使用 RabbitMQ,交换机类型为 topic,消费者需手动 ack”。这样第二轮的写代码任务才能聚焦,而不是重新做一轮选型。
3.3 一个完整案例:从模糊需求到可执行任务书
用订单模块加“导出 CSV”功能为例,看一个完整的多轮组合。
第一轮,用一种比较差的方式问:“给订单模块加个导出功能。” Codex 会给你一个导出接口,但它不知道字段有哪些、不知道用什么框架、不知道编码要不要处理,返回的代码大概率是“看起来能用但直接接上就出错”的半成品。
换成组合写法,第一轮:
角色:Python 后端工程师 任务:给现有订单模块增加一个 CSV 导出接口 技术栈:FastAPI + SQLAlchemy 约束: - 数据来自已有函数 list_orders(filter_dict) - 导出字段为 order_id、user_name、amount、status - 中文内容使用 UTF-8-BOM 编码,避免 Excel 打开乱码 - 不要修改 list_orders 的内部实现 输出: - 给出一个 POST /orders/export 接口的完整代码 - 标注限流和内存风险 - 用文字说明调用方式第二轮,如果接口报错,比如UnicodeEncodeError,就单独用报错诊断模板贴出完整堆栈、触发步骤,并说明“第一轮生成了 CSV 导出接口,字段中有中文”。Codex 会根据上下文直接定位到编码问题,大概率给出加utf-8-sig的方案。
第三轮,你可以接着用重构模板说“导出接口目前一次性查询全部数据,请改成按批次查询”,再结合验收条件“内存占用不超过 200MB”“接口在导出 10 万条数据时仍能正常返回”。
你会发现这三个轮次之间并没有重复描述项目背景,因为第一轮已经把技术栈和模块背景说清楚了。这就是组合写法的意义:一次把地基打好,后续每轮只变更增量信息。
4. 实测下来最容易被忽略的坑:上下文污染、伪精确与超长提问
4.1 上下文污染:旧需求残留比提问格式错误更致命
使用模板一段时间后,我发现真正翻车的往往不是第一次提问,而是连续多轮对话中的“上下文污染”。Codex 会默认把同一会话里的所有历史信息都当作可用上下文,即使你已经明确说“刚才那个不用了”。
举个例子,你前面几轮一直在问 Java 的 MyBatis 问题,后来切到 Python 的 SQLAlchemy 问题,如果不新开会话,Codex 可能会在回答里不自觉地带上 MyBatis 的写法或者 Java 风格异常处理。这不是它笨,而是上下文窗口里旧信息权重太高。
我自己定的规则是:当需求发生领域切换、技术栈切换、项目切换时,直接新开会话,并把关键背景写进新会话的第一轮问题。不要试图通过“别管刚才的”这句话来纠正上下文,实测效果很差,因为旧信息仍然留在前面的对话记录里。
另外,在粘贴代码时也要注意上下文污染。很多人习惯把整个文件贴进去,哪怕只有一段函数报错。Codex 会把文件里其他无关逻辑当作影响因素,反而干扰定位。更合理的做法是只贴最小可复现片段,文件名和函数名一句话说明即可。
4.2 伪精确:参数越多不等于答案越好
结构化提问的另一个极端是“伪精确”。有人听说要给背景和约束,就把所有想到的条件都塞进去,结果指令之间互相矛盾。比如既要代码“高性能”,又要“兼容低版本浏览器”,还要“使用最新的语言特性”,这三个目标本身就需要权衡,矛盾时 Codex 只能挑其中一个执行。
我建议每次提问只保留与当前决策直接相关的约束。如果确实存在多个优化目标,要明确告诉 Codex“优先级从高到低”,比如“先保证正确性,再考虑性能,最后才压缩代码行数”。没有优先级的多目标约束,本质上等于没有约束。
还有一种伪精确是给了看似精确的参数,但参数本身可验证性不足。例如“尽量快一点”不如“接口响应时间从 800ms 降到 300ms 以内”;“代码优雅一点”不如“把超过 20 行的函数拆分为多个小函数”。模板里的约束条,最好都能在代码或测试结果中看到,而不是感觉。
4.3 对话轮次管理:什么时候应该开新会话
组合写法不是无限在一轮对话里追加提问。我建议给每轮对话设置一个“最大轮次”,通常是 5 到 8 轮。超过这个轮次后,早期背景的记忆权重会下降,而且后续输出的质量会明显变差。
可以这样判断是否开新会话:
| 场景 | 建议 |
|---|---|
| 同一个功能连续调试同一个报错 | 继续当前会话 |
| 调试完一个 bug,准备做下一个需求 | 新开会话,第一轮重新说明背景 |
| 从写代码切换到审查代码 | 新开会话,避免“生成者”视角干扰 |
| 对话超过 8 轮仍没解决问题 | 新开会话,重新贴最小问题描述 |
开新会话不是浪费时间,反而是给 Codex 一次“清除噪声”的机会。你在新会话第一轮里写清楚背景,效果比在旧会话里说十句“注意不要忘记某某前提”都好。
5. 把这些模板改造成自己的提问系统
5.1 按工作类型建立个人模板库
上面的模板是通用版本,但要让它真正成为你的“急救卡”,我建议按自己的工作类型做裁剪。后端工程师可以把模板二、三、六作为高频卡;前端工程师可能更常用模板一、四、五;如果你是数据开发,报错诊断模板里的“已尝试”部分要多关注任务调度链路。
我自己的做法是在编辑器里维护一个纯文本模板文件,按场景存放。写代码时复制模板一,排错时复制模板三,选型时复制模板五,每次只改占位符。这样不用记忆模板内容,也能保证提问质量稳定。这个文件就是我的“急救卡”本体,和这篇博客里的示例相比,里面所有示例都被替换成我自己项目里的真实函数名和字段名。
5.2 给模板加“验收条件”
我会额外建议你在每个模板里加一条“验收条件”。这条不是可有可无的,它是你和 Codex 之间约定的“交付检查清单”。
比如写接口时,验收条件可以写“生成的代码必须包含参数校验和异常处理”;重构时,验收条件写“必须保持原有对外接口签名不变”;学习时,验收条件写“不直接给出答案,先引导我思考”。Codex 在生成回答时会尝试满足你的所有显式要求,如果验收条件清晰,它的自检意识会明显增强。
我最常用的一条万能验收条件是:“生成完成后,请以旁观者身份检查一遍自己的代码,列出可能出错的三个边界情况。”这一句话可以让 Codex 从“生成模式”切换到“审查模式”,实测能避免大量低级 bug。
5.3 持续校准:记录哪些提问方式管用
模板不是一成不变的标准答案。每个项目的代码风格、技术栈、领域差异都很大,Codex 在不同场景下的表现也不一样。我建议你在使用过程中做一个小记录,比如“这个项目的模板里必须写明数据库表名,否则 Codex 会自己生成错误表名”“这个团队的 Codex 在代码注释上过于啰嗦,需要输出要求里加一句注释精简”。
这类校准信息一开始看起来很碎,但积累多了,你的个人提问系统会越来越准。我自己会保持一个简单的“有效提问记录”,把那些当时觉得“这个提示太好用了”的 Prompt 原文存起来,后面复用到类似场景。这比到处收藏别人的模板更贴合自己的项目。
最后再分享一个小技巧:如果你发现 Codex 在连续两轮里仍然没有理解你的意图,不要继续换着法儿重写同一个问题,先停下来想想是不是模板里某个约束与目标冲突了。大多数“AI 听不懂话”背后,其实是提问者自己都没想清楚优先级。模板只是帮你把优先级显性化,真正的判断力还是在你自己手里。