别只会用 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 编程工作流里很现实的问题。

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

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

结论

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

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

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

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

记录并分享
使用 Hugo 构建
主题 StackJimmy 设计