别只会用 Codex:Hook 才是进阶玩法

整理 Codex Hook 的核心用法:它能在 Codex 的关键生命周期事件中运行自定义脚本,适合做隐私检查、命令审查、会话记录、结果校验和团队规范约束。

很多人用 Codex 时,重点都放在“让它帮我写代码、改文件、跑命令”。这当然是 Codex 最直观的能力,但如果只停留在这里,很容易忽略一个更适合进阶使用的功能:Hook。

Hook 的价值不在于让 Codex 更会写代码,而在于让 Codex 更按规矩工作。它可以在 Codex 的关键生命周期事件里运行自定义脚本,例如用户提交提示词时、工具调用前、工具调用后、请求权限时、会话停止时等。这样一来,你可以把团队规则、安全检查、隐私保护和自动化记录放进 Codex 的工作流里,而不是每次都靠人工提醒。

一个典型例子是隐私检查:当用户把 prompt 发给 Codex 前,先用 Hook 扫一遍是否包含 API key、token、私钥、手机号、内部地址或其他敏感信息。如果命中规则,就阻止这次提交,或者给出明确提示,让用户先清理内容。

Hook 是什么

Codex Hook 可以理解为 Codex 工作流里的生命周期处理器。它会在指定事件发生时触发,并把当前上下文以 JSON 形式传给你的脚本。脚本可以返回提示信息、附加上下文,也可以在部分事件里阻止后续动作。

官方文档里提到,Hook 可以用于这些场景:

  • 把对话发送到自定义日志或分析系统;
  • 扫描团队 prompt,阻止误贴 API key;
  • 自动总结对话,生成持久记忆;
  • 在一轮对话结束时运行自定义校验;
  • 根据当前目录补充特定提示或规则。

所以 Hook 更像一层“工作流控制”和“安全护栏”,而不是普通的提示词技巧。提示词告诉 Codex 应该怎么做,Hook 则能在 Codex 做事前后插入检查、记录和约束。

Hook 放在哪里

Codex 可以从 hooks.json 加载 Hook,也可以从 config.toml 里的 [hooks] 表加载内联配置。常用位置有四个:

  • ~/.codex/hooks.json
  • ~/.codex/config.toml
  • <repo>/.codex/hooks.json
  • <repo>/.codex/config.toml

用户级 Hook 适合放个人通用规则,例如“不要把疑似密钥发送出去”“每次结束时做摘要”。项目级 Hook 适合放仓库规范,例如“禁止直接改生成目录”“提交前检查 front matter”“运行命令前拦截危险操作”。

需要注意的是,项目级 .codex/ 配置只有在该项目层被信任后才会加载;用户级 Hook 则不依赖项目是否被信任。这个设计很重要,因为项目里的 Hook 本质上是会执行本地脚本的配置,不能对陌生仓库无条件信任。

常见触发时机

Hook 不是只有一个触发点。不同事件适合不同用途。

UserPromptSubmit 发生在用户 prompt 即将提交时。它适合做隐私检查、敏感词检查、提示词规范检查。比如发现 prompt 里疑似包含 sk- 开头的 key,就直接阻止提交。

PreToolUse 发生在 Codex 调用工具前。它适合拦截命令、文件写入或 MCP 工具调用。例如检测到 rm -rf、向系统目录写文件、读取敏感路径,就返回拒绝理由。

PermissionRequest 发生在 Codex 准备请求权限时。它适合自动批准或拒绝某些明确的权限请求。比如团队可以允许固定的测试命令自动通过,但拒绝访问某些目录。

PostToolUse 发生在工具执行后。它适合检查命令输出、扫描生成文件、提醒后续步骤。它不能撤销已经发生的副作用,但可以让 Codex 在继续前看到额外反馈。

SessionStart 发生在会话开始或恢复时。它适合加载项目约定、团队规范、常用上下文或本地提示。

Stop 发生在一轮响应准备停止时。它适合做收尾检查,例如确认是否运行测试、是否还存在 TODO、是否需要继续补一轮验证。

一个隐私检查 Hook 应该怎么设计

隐私检查最适合放在 UserPromptSubmit,因为它发生在用户内容真正进入 Codex 工作流之前。设计时不要追求一步到位,先覆盖最容易误贴的内容就够了。

可以优先检查这些模式:

  • OpenAI、GitHub、云厂商等 API key;
  • -----BEGIN PRIVATE KEY----- 这类私钥块;
  • .env 文件片段;
  • 访问令牌、Bearer token、JWT;
  • 内网域名、数据库连接串;
  • 身份证、手机号、邮箱等个人信息。

Hook 脚本接收 JSON 输入后,读取其中的 prompt 字段,使用正则或规则表扫描。如果发现问题,可以返回阻止结果:

1
2
3
4
{
  "decision": "block",
  "reason": "检测到疑似 API key,请先移除敏感信息后再提交。"
}

也可以不阻止,只返回额外上下文,让 Codex 在后续回答中注意某些约束。不过对于密钥、私钥和令牌这类高风险内容,更稳妥的做法是直接阻止。

hooks.json 配置示例

如果使用仓库级配置,可以创建 .codex/hooks.json:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python .codex/hooks/privacy_check.py",
            "timeout": 10,
            "statusMessage": "Checking prompt privacy"
          }
        ]
      }
    ]
  }
}

对应的脚本可以从标准输入读取 JSON:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import json
import re
import sys

payload = json.load(sys.stdin)
prompt = payload.get("prompt", "")

patterns = [
    r"sk-[A-Za-z0-9_-]{20,}",
    r"ghp_[A-Za-z0-9_]{20,}",
    r"-----BEGIN [A-Z ]*PRIVATE KEY-----",
    r"Bearer\s+[A-Za-z0-9._-]{20,}",
]

for pattern in patterns:
    if re.search(pattern, prompt):
        print(json.dumps({
            "decision": "block",
            "reason": "检测到疑似密钥、令牌或私钥,请先移除敏感信息。"
        }, ensure_ascii=False))
        raise SystemExit(0)

这个示例只演示思路,实际使用时应该把规则拆成可维护的列表,并控制误报。比如普通文档里出现 Bearer 示例不一定就是泄露,项目里也可能有专门用于测试的假 key。规则越严格,越容易误拦;规则越宽松,越容易漏掉真正的风险。

config.toml 内联写法

如果不想单独维护 hooks.json,也可以写在 config.toml 里。官方文档示例中,内联 TOML Hook 和 hooks.json 使用同样的事件结构:

1
2
3
4
5
6
7
8
[[hooks.PreToolUse]]
matcher = "^Bash$"

[[hooks.PreToolUse.hooks]]
type = "command"
command = 'python ".codex/hooks/pre_tool_use_policy.py"'
timeout = 30
statusMessage = "Checking Bash command"

如果同一层配置里同时存在 hooks.json 和内联 [hooks],Codex 会同时加载并给出警告。实际项目里最好二选一,避免规则分散后难以排查。

使用 Hook 时要注意什么

第一,Hook 会执行本地命令,所以一定要审查来源。非托管 Hook 需要被 review 和 trust 后才会运行;Hook 内容变化后,也需要重新信任。陌生仓库里的 .codex/hooks.json 不应该随便放行。

第二,多个匹配的 Hook 可能都会运行。不要假设只有一个 Hook 能拦住所有事情,也不要把规则写得互相冲突。团队环境里要明确哪些是用户级规则,哪些是项目级规则,哪些是管理员托管规则。

第三,Hook 是护栏,不是完整安全边界。比如 PreToolUse 可以拦截很多工具调用,但官方也明确提示,它还不能覆盖所有 shell 调用路径,也不能拦截所有非 shell、非 MCP 工具。真正高风险的场景仍然需要权限配置、沙箱、代码审查和最小权限配合。

第四,Hook 脚本要快。隐私扫描、命令审查这类 Hook 发生在交互链路中,如果脚本每次跑几十秒,会直接拖慢体验。能用规则完成的事情,不要每次都调用大型外部服务。

第五,输出格式要严格。不同事件支持的返回字段不完全一样。比如 UserPromptSubmit 可以用 decision: "block" 阻止 prompt,PreToolUse 则有自己的 hookSpecificOutput 结构。写 Hook 前先看对应事件的输入输出约定,不要混用字段。

哪些场景最值得先做 Hook

如果刚开始用 Hook,不建议一下子做成复杂的企业级策略系统。可以先从三个小场景开始。

第一个是隐私检查。它最容易带来真实收益,也最适合个人和团队通用。误贴密钥、token、.env 片段,是 AI 编程工作流里很现实的问题。

第二个是危险命令拦截。可以针对 Bash 的 PreToolUse 做基础规则,例如阻止递归删除、阻止改写系统目录、阻止向未知远端上传文件。

第三个是收尾检查。用 Stop 或 PostToolUse 提醒 Codex 在结束前确认测试、构建、格式化、文档和未提交改动。这类 Hook 不一定要强制阻断,但能显著减少“差一步就收工”的情况。

结论

Codex Hook 的进阶之处,不是让 Codex 多一个花哨功能,而是把“规则”接进了 Codex 的执行循环。

普通用法里,你靠 prompt 告诉 Codex 应该怎么做;进阶用法里,你用 Hook 在关键节点检查它是否按规矩做。隐私检查、命令审查、权限策略、会话摘要、结束前校验,这些都可以从口头提醒变成可执行的工作流。

如果你已经开始把 Codex 用在真实项目里,Hook 值得尽早配置。它不替代人工判断,也不替代沙箱和权限控制,但它能把很多容易遗忘的安全和流程要求,变成每次都会自动触发的默认动作。

参考来源:OpenAI Codex Advanced Configuration - Hooks、OpenAI Codex Hooks