Codex 提示词工程实战:编写高效 AI 编程指令的系统方法论

引言

用过 AI 编程工具的同学都有这样的体会:同样的工具,有人能用它半小时搞定一个功能模块,有人跟它纠缠一下午却产出一堆废代码。差距在哪里?提示词质量

Codex 作为 OpenAI 开源的终端 AI 编程助手,提供了丰富的提示词定制能力——从项目级的 AGENTS.md 到毫秒级触发的钩子系统,再到 exec 模式的自动化管道。本文将从 Codex 的上下文架构出发,系统讲解如何编写让 AI 精准执行的提示词,并给出可直接套用的实战模板。

1. 理解 Codex 的上下文架构

在写提示词之前,你需要知道这些文字最终会以什么形式送到模型面前。

Codex 的上下文由多个 碎片(Fragment) 组成,每个碎片都实现 ContextualUserFragment trait,按角色和优先级注入到请求中:

| 碎片类型 | 角色 | 说明 |
|---------|------|------|
| UserInstructions | user | AGENTS.md 内容的最终形态 |
| PersonalitySpecInstructions | developer | 通过 personality 字段定义的行为约束,requires_separate_message = true |
| EnvironmentContext | user | 文件系统权限、网络策略、工作区根路径 |
| PermissionsInstructions | user | 审批策略的上下文提示 |
| SkillInstructions | user | .codex/skills/ 中定义的技能指令 |
| TokenBudgetContext | user | Token 预算警告信息 |
| CurrentTimeReminder | user | 当前时间感知 |

核心设计原则有两个:增量构建(不能删除或重写历史消息,只能追加)和 缓存友好(频繁变更可缓存的前缀会导致缓存未命中,增加延迟)。这意味着你的提示词应当保持稳定——把变动频繁的指令放在钩子中,把稳定的约定写在 AGENTS.md 里。

2. AGENTS.md:项目级的全局提示词

AGENTS.md 是 Codex 提示词体系中最核心的一环。它定义了 AI 在你项目中的"世界观"。

2.1 级联发现机制

Codex 从当前工作目录向上遍历,直到找到项目根标记(默认 .git),收集路径上所有的 AGENTS.mdAGENTS.override.md,并按优先级拼接:

AGENTS.override.md > AGENTS.md > project_doc_fallback_filenames

当你的项目有多个子模块时,可以在每个子目录放置针对性的 AGENTS.md:

my-project/
├── AGENTS.md              # 全局约定:语言、框架、代码风格
├── backend/
│   └── AGENTS.md          # 后端特定指令:ORM 规范、API 设计约定
├── frontend/
│   └── AGENTS.md          # 前端特定指令:组件规范、状态管理策略
└── AGENTS.override.md     # 个人偏好覆盖,不提交到 Git

注入到模型的最终格式为:

# AGENTS.md instructions for /path/to/project

<INSTRUCTIONS>
[根目录 AGENTS.md]
--- project-doc ---
[子目录 AGENTS.md]
</INSTRUCTIONS>

2.2 编写高质量 AGENTS.md 的四个原则

原则一:精确优于详尽。 Codex 的 AGENTS.md 有 project_doc_max_bytes 限制,且超过 10K token 的单项会被拒绝。不要写成项目文档,要写成给 AI 看的执行规范

低效写法:

我们这个项目使用 Go 语言开发,采用 gin 框架,数据库是 MySQL。
我们团队遵循 Clean Architecture 的分层设计,controller 层负责处理 HTTP 请求,
service 层负责业务逻辑,repository 层负责数据访问...

高效写法:

## 技术栈
- Go 1.22+, gin v1.9+, GORM v1.25+, MySQL 8.0+

## 分层约束
- handler/:仅做参数绑定和响应返回,禁止包含业务逻辑
- service/:业务逻辑,通过接口依赖 repository
- repository/:数据访问,使用 GORM,返回值为 domain 结构体而非 ORM 模型

## 代码规范
- 导出函数必须有 doc comment
- 错误处理:service 层返回 `fmt.Errorf("操作描述: %w", err)` 包装
- HTTP 响应统一使用 `utils.Response{Code, Msg, Data}` 结构体

原则二:利用 AGENTS.override.md 做个人偏好覆盖。 团队共享的 AGENTS.md 保持稳定,个人偏好(如日志级别、调试开关)写在 override 文件中,不干扰他人。

原则三:输出约束要具体。 不要写"写出好的代码",要写"生成的代码必须通过 golangci-lint run 且无 warning"。

原则四:搭配 model_instructions_file 使用。codex.toml 中指定:

model_instructions_file = ".codex/system_prompt.md"

将角色定义、通用行为准则等系统级提示词模板外置到独立文件,保持 AGENTS.md 聚焦项目约定。

3. 交互模式 vs Exec 模式的提示词策略

Codex 的两种工作模式虽然共享同一套上下文构建管道,但对提示词的侧重点完全不同。

| 维度 | 交互模式(codex TUI) | Exec 模式(codex exec) |
|------|------------------------|--------------------------|
| 会话持续性 | 多轮对话,上下文逐步累积 | 单次执行,独立进程 |
| 审批流程 | 逐条交互式审批 | 由 approval_policy 配置决定 |
| 提示词风格 | 可迭代修正,允许简短模糊指令 | 必须一次性完整描述,包含边界条件 |

3.1 Exec 模式:一次性完整描述

在 exec 模式下,提示词是你和 AI 之间唯一的沟通媒介。任务描述越模糊,AI 的发挥空间越大——但跑偏的概率也越高。

低效的 exec 调用:

codex exec "修复 login 模块的 bug"

高效的 exec 调用:

codex exec "修复 login 模块中 OAuth2 回调无法获取用户邮箱的 bug。\
错误日志:Error: email is required in /oauth/callback handler。\
预期行为:GitHub OAuth 回调后应正确解析用户邮箱字段。\
约束:只修改 handler/oauth.go 和 service/auth.go,不要改动 proto 定义。\
验证方式:修改后运行 go test ./handler/... -run TestOAuthCallback"

关键差异在于后者的提示词包含了五个要素:

问题描述:具体哪个环节出错

上下文证据:错误日志或现象

预期结果:修复后应当达成的行为

边界约束:允许修改的文件范围

验证指令:修复后如何确认

3.2 Exec 模式的 CLI 覆盖技巧

不需要每次修改配置文件,可以通过 -c 参数临时覆盖:

# 使用高强度推理模式处理复杂重构
codex -c model=gpt-5.1-codex-max -c model_reasoning_effort=high exec "将 user 模块从 GORM v1 迁移到 v2,注意 v2 的软删除和批量插入 API 变化"

# 全自动执行,跳过审批
codex -c approval_policy=untrusted exec "运行 gofmt -w . 并确保所有文件格式化通过"

# 指定自定义 AGENTS.md
codex -c project_doc_fallback_filenames='["CONVENTIONS.md"]' exec "实现新的支付接口"

3.3 交互模式:迭代式精确化

在 TUI 中,你可以从模糊需求开始,通过多轮对话逐步收敛。但高效的起点能节省大量迭代轮次:

# 第一轮:建立上下文
> 我正在重构 payment 模块,目标是将 Stripe 替换为支付宝。相关文件在 service/payment.go 和 handler/order.go。先帮我列出所有需要修改的地方。

# 第二轮:指定方案
> 基于上面的分析,把 Stripe 相关的调用封装到一个 PaymentProvider 接口后面,如果后续再换支付渠道只需新增实现。

# 第三轮:执行并验证
> 按照这个方案实现,完成后运行 go build ./... 确认编译通过。

4. 钩子系统:动态提示词注入

钩子是 Codex 最强大的提示词扩展机制。它在关键生命周期节点触发自定义逻辑,让你可以在模型请求发出前动态注入指令。

4.1 常用钩子场景

codex.toml 中定义:

# 会话启动时注入工作流指令
[[hooks]]
event = "SessionStart"
handlers = [
  { type = "prompt", prompt = "你正在操作一个生产级 Go 项目。所有数据库操作必须先输出 SQL 语句让用户确认。任何涉及删除、TRUNCATE 的操作必须额外询问。", matcher = "" }
]

# 用户提交提示词前,自动追加测试要求
[[hooks]]
event = "UserPromptSubmit"
handlers = [
  { type = "prompt", prompt = "完成代码修改后,请运行与改动相关的单元测试,如有失败请自行修复。", matcher = "" }
]

# 工具调用前检查——阻止危险命令
[[hooks]]
event = "PreToolUse"
handlers = [
  { type = "command", command = "echo 'WARNING: shell command about to execute' >> /tmp/codex_audit.log", matcher = "tool == shell" }
]

4.2 钩子类型选择

| 钩子类型 | 用途 | 适用场景 |
|---------|------|---------|
| prompt | 注入纯文本提示 | 风格约束、质量要求、步骤提醒 |
| command | 执行 Shell 脚本 | 审计日志、环境检查、预装依赖 |
| agent | 派生子 Agent | 并行处理子任务(代码审查、测试生成) |

command 类型钩子的完整配置:

[[hooks]]
event = "PreToolUse"
handlers = [
  {
    type = "command",
    command = ".codex/hooks/git-branch-check.sh",
    timeout = 5000,               # 超时 5 秒
    async = false,                # 同步执行,阻塞工具调用直到脚本完成
    statusMessage = "检查当前分支...",
    additionalContextLimit = 2500 # 脚本输出超过 2500 token 时写入磁盘
  }
]

4.3 实战:用钩子实现自动代码审查

# 每次工具调用完成后,自动检查代码质量
[[hooks]]
event = "PostToolUse"
handlers = [
  { type = "command", command = "golangci-lint run --new-from-rev=HEAD~1 ./... 2>&1 | tail -20", timeout = 30000, matcher = "tool == write_file" }
]

这样一来,Codex 每次写入文件后,你都能立刻看到 lint 结果,不用手动跑检查。

5. 提示词优化实战技巧

5.1 正向约束优于负向约束

AI 对"不要做什么"的理解远不如"要做什么"准确。

# 低效:负向约束
不要使用全局变量,不要写超过 50 行的函数,不要忽略错误。

# 高效:正向约束
- 使用依赖注入管理状态,通过构造函数传入所需接口
- 单个函数控制在 50 行以内,超出则拆分为私有辅助函数
- 所有可能返回 error 的调用必须检查返回值并用 fmt.Errorf 包装

5.2 利用 Personality 字段预置行为模式

# codex.toml
personality = """
你是一位熟悉 Go 微服务架构的高级工程师。
代码风格偏好:简洁优于花哨,显式优于隐式。
对于不确定的技术选型,优先选择标准库方案,避免引入不必要的第三方依赖。
在给出代码之前,先简述设计思路。
"""

Personality 以 developer 角色注入,与 user 消息分离,模型会更稳定地遵循这些行为指令。

5.3 分步指令有效降低模型跑偏概率

如果你发现一次性的复杂指令经常产出意料之外的结果,拆成多个简单指令依次执行:

# 先探索
codex exec "分析 service/order.go 中 CreateOrder 函数的依赖关系,列出所有外部调用"

# 再规划
codex exec "基于上述分析,设计一个将 CreateOrder 拆分为 3 个独立函数的重构方案。先描述方案,不要写代码。"

# 最后执行
codex exec "按上述方案实施重构,完成后运行 go test ./service/..."

分步执行虽然看起来"慢",但每一步都有明确的检查点,避免了一条路走到黑的窘境——尤其是对复杂任务来说,这反而更快。

5.4 善用命令实现批量自动化

# 为项目中所有 Go 文件添加版权头
codex exec "为所有 .go 文件头部添加版权声明:// Copyright 2026 YourCompany. All rights reserved.\n// 许可协议:MIT"

# 批量替换废弃 API
codex exec "查找项目中所有使用 ioutil.ReadFile 的地方并替换为 os.ReadFile。约束:只修改 .go 文件,不修改 vendor/ 目录"

# 生成测试骨架
codex exec "为 service/user.go 中所有导出函数生成 table-driven 测试,覆盖正常路径和边界条件。使用 gomock 模拟外部依赖。"

6. 实战模板

模板一:Bug 修复

## 问题
[描述 Bug 现象,附上错误日志或复现步骤]

## 影响范围
[列出受影响的功能模块或用户群体]

## 根因分析
[如果已有初步判断,写在这里;如没有,让 AI 分析]

## 修复要求
- 修改文件:[file_list]
- 不修改:[文件或模块黑名单]
- 约束:[架构约束、性能要求、兼容性要求]

## 验证
修复后执行:[测试命令]

模板二:新功能开发

## 功能描述
[一句话描述功能目标]

## API 设计
- Method: POST
- Path: /api/v1/xxx
- Request: { ... }
- Response: { code: 0, data: { ... } }

## 实现要点
- [分层要求:handler / service / repository]
- [数据库变更:是否需要 migration]
- [依赖注入:需要新增哪些接口]

## 约束
- 遵循现有 AGENTS.md 中的代码规范
- 新增代码必须有单元测试
- 不修改 [禁止修改的文件/模块]

## 验证
完成后运行:[编译/测试/格式化命令]

模板三:代码审查

请审查以下变更,重点关注:
1. 是否存在 SQL 注入、XSS 等安全漏洞
2. 错误处理是否完整(所有 error 返回值都被检查)
3. 是否存在 goroutine 泄漏或资源未释放
4. 并发场景下是否有 data race 风险
5. 函数复杂度是否过高(圈复杂度 > 10 需标注)

对每个问题给出具体文件和行号的建议。

总结

高效的 Codex 提示词不是靠"写得更长",而是靠"写得更准"。核心方法论可以归纳为五条:

理解上下文注入路径——知道你的提示词在哪个环节、以什么格式进入模型,才能精准控制输出。

AGENTS.md 是全局底座——把稳定的项目约定写进去,让每次交互都自带规范。

Exec 模式追求一次完整——包含问题描述、错误证据、预期结果、边界约束、验证指令五个要素。

钩子是动态注入点——利用 UserPromptSubmitPostToolUse 等钩子实现自动化质量保障。

正向约束、分步执行、模板复用——这三招足以覆盖 90% 的日常场景。

当你把 AGENTS.md 写好、钩子配好、模板备好,Codex 就不再是一个需要小心翼翼哄着的"实习生",而是一个真正能分担开发压力的 AI 搭档。