AlexKenbo/codex-harness-internals
GitHub: AlexKenbo/codex-harness-internals
反向工程解析 OpenAI Codex CLI 引导器的规范,构建可导航的知识图谱以解决指令裁剪与上下文管理问题。
Stars: 2 | Forks: 0
# codex-harness-internals
**语言:** 英语 · [Русский](./README.ru.md)
反向工程解析 OpenAI Codex CLI 引导程序(harness)的规范——代理循环、工具注册表、压缩(compaction)和上下文管理的机制。从源代码仓库提取并组织为可导航的知识图谱,供构建 AI 代理或为其编写指令的工程师使用。
## 5 个指令被静默截断的位置
大多数指令作者没有意识到,引导程序在其自身的提示组装逻辑中应用了硬性数值裁剪。裁剪静默发生——没有警告、没有错误。以下是实际发生裁剪的五个位置。
**1. 5 000 字节 —— 渲染的已批准命令前缀文本的限制。** 当你通过允许的前缀列表(`git commit`、`npm install` 等)定义 shell 命令审批策略时,引导程序会将它们渲染为开发者指令中的一个文本块。一旦该文本超过 5 000 字节,它就会在最后一个有效的 UTF-8 字符处被截断,并追加标记 `...[Some commands were truncated]`。模型会看到一个不完整的列表——而你对此一无所知。**该做什么:** 将前缀分组(使用 `git *` 而不是五个独立的 `git commit`、`git push`……),或将关键规则移至 `base_instructions` 作为纯文本。
**2. 100 个条目 —— 前缀列表中的项目数量限制。** 即使字节数低于 5 000,引导程序也最多只取前 100 个前缀(按 `(length, combined length, alphabetical)` 排序)。其余的前缀会被无声丢弃。**该做什么:** 如果你拥有超过 100 条规则,那就不再是前缀列表了——而是一种策略,应该在 `base_instructions` 中以声明式表达,而不是枚举。
**3. 20 000 个标记 —— 压缩后用户消息的预算。** 当引导程序运行自动压缩时,它只保留最近的用户消息——正好填满 20 000 个标记的总量。更早的都会溶解为一段摘要。如果你“逐步展开”规则跨越十几个用户消息,序列的开头会在压缩后消失。**该做什么:** 将规则骨架保留在 `base_instructions` 中(它们每轮都会重建),并理解逐步细化只在下一次压缩前有效。
**4. 90% 的上下文窗口 —— 自动压缩阈值。** 当总标记使用量超过模型上下文窗口的 9/10 时,引导程序会触发压缩。压缩后,所有开发者消息都会被完全丢弃——`should_keep_compacted_history_item` 过滤器将它们归类为“陈旧/重复指令”。这意味着:通过开发者消息注入到引导程序的规则(审批策略、沙箱指令、屏幕环境等)在第一次压缩后将不再存在于历史记录中。**该做什么:** 不要依赖开发者指令被“记住”;如果规则很重要,它必须属于 `base_instructions`。
**5. 1 MiB —— 用户输入硬性上限。** 任何超过 1 048 576 个字符的用户输入都会在到达模型之前被引导程序拒绝。这对文本查询来说已经很多,但如果你程序化地拼接大块内容(日志文件、差异、转储)很容易达到上限。**该做什么:** 在你这边预先截断大型插入内容——不要指望引导程序“自行处理”。
## 定义引导程序行为的 5 个参数
对引导程序工程师而言,重要的不是“81 种事件类型”,而是定义与客户端契约的那些值。
**1. 提交通道 —— 容量 512。** 引导程序通过固定大小为 512 的通道从客户端接收操作。当通道填满时,客户端会阻塞(背压)。如果你正在构建自己的编排器,必须慎重选择等效参数:太小会导致体验卡顿,太大会掩盖处理缓慢。Codex 选择 512 是在响应性与防止重负载“洪水”之间的权衡。
**2. 代理作业并发 —— 默认 16,最大 64。** 当引导程序运行 CSV-spawn(来自表格的并行子代理)时,默认是 16 个并行工作线程,最大为 64。这个上限并非技术约束,而是关于成本:每个活跃子代理都会占用自己的回合和标记消耗。如果你的引导程序没有类似上限,客户端可能无意中启动数百个并行代理并触发计费冲击。
**3. 锁重试 —— 10 次尝试,间隔 100 毫秒。** 会话历史记录以锁写入回滚文件;发生冲突时,引导程序会重试锁最多 10 次,每次休眠 100 毫秒。这是为支持同一会话中的多个并发进程(例如 CLI + VS Code 扩展)而做的设计选择。如果你正在编写自己的持久化层,请注意缺乏重试的机制在首次并发访问时就会崩溃。
**4. 恢复时的最大扫描文件数 —— 10 000。** 当引导程序从回滚存档恢复会话时,最多扫描 10 000 个文件。这限制了可通过“回滚”追溯的历史深度。对引导程序工程师而言,这个限制关乎启动资源;若不加限制扫描会导致客户端启动延迟数分钟。
**5. 等待超时 —— 10 秒至 1 小时。** 对于阻塞型工具调用(等待操作),引导程序将超时归一化到 10–3600 秒范围内。低于 10 秒会被拒绝(防止忙循环),超过一小时也会被拒绝(防止卡住会话)。如果你的工具注册表允许模型提供任意超时,你可能会面临 DoS(模型请求永远等待)或“抖动”(模型每秒调用一次等待)。
## 目标读者
### 代理指令与提示作者(主要受众)
你编写系统提示、AGENTS.md、规则集和能力包——并希望了解**引导程序如何实际组合这些内容并与历史记录交互,再传递给模型**。本规范说明:
- 哪些指令能存活压缩,哪些不能;
- 你的规则在最终提示中实际出现在哪里;
- 哪些阈值会在模型看到之前静默裁剪你的文本;
- 技能提及如何激活注入而不膨胀上下文;
- 为什么你的规则在 N 轮后“被遗忘”。
### 引导程序系统工程师
你在构建自己的代理循环——回合循环、流式传输、工具调度、会话持久化——并希望参考一个实现。该规范提供:
- 完整的回合机制:Item 生命周期、TurnItem → 旧版 EventMsg 映射、TTFT/TTFM 指标;
- 两条路径的压缩(内联摘要与 `/responses/compact` 端点);
- 带前缀缓存的 `ContextWindowExceeded` 重试策略;
- 线程的 fork / resume / rollback 语义。
## 规范包含的内容
18 个 SRC 模块 × 3 个视角:
| 视角 | 内容 | 总数 |
|---|---|---|
| `rules.json` | 不变量、阈值、公式、GWT 示例(Given / When / Then) | **1 028 条规则** |
| `structural.json` | 实体、字段、关系、数据流 | **588 个结构** |
| `use-case.json` | 场景:步骤、Cockburn 扩展、边界情况、生命周期 | **325 个用例** |
| **总计** | | **≈ 1 941 种组合** |
**覆盖范围:** D1 协议(类型、线格式)→ D2 状态(会话、线程、回滚)→ D3 代理(核心:生成、回合循环、压缩)→ D4 上下文(回合上下文、初始上下文、环境)→ D5 工具(注册表、分发、子代理)。
**不在范围内:** 沙箱、安全、MCP 集成、钩子/插件内部机制、认证、分析、网络策略、模型提供者、CLI / TUI / IDE 端口。
## Codex 与众不同的设计决策
这些并非“代码中的术语”,而是 Codex 团队做出的、与典型代理行业实践不同的决策。对每一项:说明做了什么、通常的做法是什么,以及它为指令作者或引导程序工程师带来了什么。选择标准是:(1) 存在可见的替代方案;(2) 替代方案在行业中常见(LangChain / AutoGen / Assistants API 实现方式不同);(3) 该差异会改变用户行为,而不仅是内部细节。
### 1. 指令分为五个层级,各有不同的生命周期
**Codex 的做法:** 将指令拆分为五个语义层级,每层拥有自己的生命周期:`base_instructions`(每轮重建)、`initial_context`(压缩后重新注入)、`skill injections`(通过 @ 提及激活)、开发者消息(运行时策略)、用户消息(任务特定内容)。
**通常的做法:** 在 LangChain / AutoGen / Assistants API 中,只有一个扁平的 `system prompt` + `messages[]`,系统提示在整个生命周期内保持不变。指令要么一次性硬编码,要么在整个线程中“涂抹”进用户消息,而没有结构化区分。
**带给受众的价值:** 指令作者能清楚回答“该把规则放在哪里才能始终生效”。位于 `base_instructions` 的规则能存活压缩且不依赖历史;位于开发者消息的规则是易失的,会在第一次压缩时消失;位于技能文件中的规则仅在需要时激活。这使得可以刻意设计规则集,而不是寄希望于“模型会记住”。
### 2. 技能通过 @ 提及激活——它们是惰性指令
**Codex 的做法:** 模块化指令包(技能)不会默认进入提示。仅当用户显式通过 `@skill-name` 提及技能(`collect_explicit_skill_mentions`),或引导程序在之前回合中已见过该技能(`implicit_invocation_seen_skills`)时,才将其拉入。
**通常的做法:** 在典型代理中,所有工具描述和所有规则都会提前加载到提示中。这在 5–10 个工具时可行,但扩展到 50 个时就会失效——模型会失去焦点,且提示膨胀成数万标记的恒定开销。
**带给受众的价值:** 你可以维护一个任意大的规则目录,按主题划分(`@testing-rules`、`@security-review`、`@refactor-style`),而每轮只看到实际需要的那些。基础提示保持小巧——只包含“何时调用哪个技能”的元规则。这是与之前完全不同的指令扩展模型。
### 3. 上下文通过差异更新,而非全量重新注入
**Codex 的做法:** 当环境在回合之间未发生变化时,引导程序不会重新发送它。为此它存储 `reference_context_item`——上次发送上下文的快照。在新回合中,`build_environment_update_item` 会对比当前状态与参考,并仅发出发生变化的字段,包装在 `` 标记内。
**通常的做法:** 经典做法是将完整的系统上下文粘合到每个请求中。模型会反复看到相同的 CWD、Git 分支和文件系统。
**带给受众的价值:** 静态规则的标记节省——更重要的是,模型不会因为重复内容而“迷失”。当相同的环境消息每轮都发送时,模型会赋予其额外权重;而差异更新让上下文变成“仅在有变化时才出现”。通过标准 `` 片段放置环境信息的指令作者会自动获得这种节省。
### 4. 压缩存在,但 `/undo` 仍然可用
**Codex 的做法:** 在压缩之前,引导程序会收集“幽灵快照”(history snapshots),并在新的压缩历史末尾附加它们。`should_keep_compacted_history_item` 过滤器特别保留这些快照,即使其他内容都被丢弃。
**通常的做法:** 大多数系统中的压缩是不可逆的:旧历史被摘要替换,用户无法“回退”。一旦压缩发生,用户就可能丢失继续推进的能力。
**带给受众的价值:** 对引导程序工程师而言,这是一种压缩设计模式:在单一提示大小约束与“能够回滚”之间保持不变的约束。它需要额外的记账(快照标记和过滤器),但结果是长会话中的安全性——用户不必担心“做得太多”而丢失进度。
### 5. 用户指令与脚手架是**不同的东西**
**Codex 的做法:** 引导程序区分六种“上下文片段”类型,每种都有自己的 XML 标记:``、``、``、``、``、``。这些文本在形式上**不算**用户输入——它们不作为 `user_turn_boundary` 参与回滚,在修剪过程中单独处理,部分(如 `AGENTS_MD`、`SKILL`)甚至不参与记忆生成。
**通常的做法:** 所有用户角色文本被同等对待:真实用户命令、脚手架指令和环境转储看起来没有区别。结果是,“回滚到上一个用户消息”可能落到错误的位置——因为“最后一个用户消息”其实是一个环境片段。
**带给受众的价值:** 对指令作者而言——你不能随意用自定义 XML 标签包裹规则并期望引导程序识别它们。如果希望规则存活,要么放入 `base_instructions`,要么使用标准的 `SKILL` 片段通过技能注入。对引导程序工程师而言,“用户输入”与“服务插入”之间的区别应作为一等公民概念,而不是注释约定。
### 6. 审查代理拥有自己的策略,并不继承调用者
**Codex 的做法:** 当 Guardian——专门用于 shell 命令审批的子代理——被触发时,它会忽略从父级继承的 exec-policy,并使用内置的安全检查(`ExecPolicyManager::default`)。代码中明确存在 `guardian-reviewer-bypass-exec-policy` 规则。
**通常的做法:** 子代理通常会继承调用者的策略。这很直观——如果主代理配置为信任 `git *`,子代理也会信任它。但对审查者而言这很危险:调用者可能通过审查者“自我批准”自己的命令。
**带给受众的价值:** 对引导程序工程师而言,这是一种架构模式:“安全关键型子代理必须在架构上与调用者隔离”。这是一种罕见但有原则的区分:审查者不能被调用者的上下文“收买”。Codex 将其作为不变量直接编码。
## 使用方法——查询示例
以下两个示例演示如何在实际查询中使用本规范。读者带着任意问题进入,获得的答案会从 SRC 模块中组装而成,无需事先熟悉规范文件。
### 示例 1. 解释一个复杂的机制
**简短回答:** Codex 最复杂的机制是**对话历史的自动压缩**。它之所以重要,是因为在上下文窗口填满时保持回合循环;之所以复杂,是因为它同时涉及五个独立任务:检测接近限制、为不同提供者选择实现方式、安全重试、保持历史不变性,以及支持 `/undo`。
#### 触发时机
引导程序将阈值计算为**模型上下文窗口的 90%**,可通过配置收紧(取较小值):
```
auto_compact_token_limit = min(config_limit, context_window × 9/10)
```
若两者均未设置,则自动压缩被禁用。它在三种模式下触发:
- **pre-turn**——在处理新用户消息之前,若上一轮总使用量已超过阈值;
- **mid-turn**——在活跃回合中,当模型即将达到窗口限制时;
- **manual**——用户命令 `/compact`。
区别很重要:mid-turn 会保留当前回合的“锚点”,使下一轮可通过差异继续;手动和 pre-turn 则不会设置锚点,下一轮会执行完整重新注入。
#### 两种实现:内联与远程
引导程序首先询问提供者:“是否支持远程压缩?”若支持——整个历史会被发送到服务器端点 `/responses/compact`,该端点返回已压缩的 `Vec`。若不支持——引导程序执行内联摘要:注入自己的 `SUMMARIZATION_PROMPT`,运行一次常规流式回合,并获得摘要文本。
该决策由 `provider.supports_remote_compaction()` 标志决定,并在启动时固定,之后不会中途切换。
#### 内联压缩中的重试循环(窗口溢出)
这是最复杂的部分。内联压缩本身会发起流式请求,也可能收到 `ContextWindowExceeded`——因为包含历史与摘要指令的提示仍然无法适应。引导程序的应对方式如下:
- **若历史包含多于一项** → 移除第一项历史(`history.remove_first_item()`),`truncated_count++`,重置 `retries = 0`,继续循环。我们牺牲最旧项以保留提供者的前缀缓存和最新的用户指令;
- **若只剩一项或更少** → 设置 `total_tokens_full`,触发错误事件并退出。即使最小上下文也无法容纳——已无救。
其他错误(网络、限流)会按 `provider.stream_max_retries()` 进行指数退避重试。`Interrupted` / `TurnAborted` 会立即退出且不重试。预修剪时,UI 会弹出:
#### 压缩后历史中实际保留的内容
引导程序构建 `build_compacted_history_with_limit`:
1. 取出所有用户消息,**排除**摘要消息(通过识别 `SUMMARIZATION_PROMPT` 前缀);
2. 逆序遍历并收集,直到总标记数超过 `COMPACT_USER_MESSAGE_MAX_TOKENS = 20 000`;
3. 组装最终列表:`initial_context` + 收集到的最近用户消息 + `summary_user_message` 作为最后一项。
若 `summary_text` 为空,则替换为 `"(no summary available)"`。
#### 初始上下文在物理上最终落在何处
Codex 模型被训练为在历史记录的**最后位置**看到摘要。引导程序维护这一不变量:
```
insertion_index =
last_real_user_index // before the last real user message
?? last_user_or_summary_index // if no real user found — before the summary
?? last_compaction_index // if neither — before the last Compaction
// otherwise — append at the end
```
GWT 示例:
- **Given:** `compacted_history = [compaction, summary_user_message]`, `initial_context = [env_msg, dev_msg]`
- **When:** 插入
- **Then:** `last_real_user_index = None`(摘要不是真实用户),`last_user_or_summary_index = 1`,插入位置 = 1 → `[compaction_item, env_msg, dev_msg, summary_user_message]`
摘要始终保留在尾部——这是与训练模型的契约。
#### 远程预修剪:可以裁剪什么,不能裁剪什么
在发送到远程压缩之前,引导程序会估算 `estimate_token_count_with_base_instructions`。若预测超出窗口——它会裁剪历史尾部,但**仅限**于最后一项为 `codex-generated` 的情况:
- 可裁剪:`FunctionCallOutput` / `ToolSearchOutput` / `CustomToolCallOutput`、开发者消息;
- 不可裁剪:用户 / 助理消息——**不得跨越用户边界**。
不变量:移除工具输出时,必须同时移除对应的工具调用(通过 `normalize::remove_corresponding_for`),否则模型会收到一个“孤儿”调用而无响应。
#### 幽灵快照:压缩后保留 `/undo`
在压缩之前,引导程序会收集 `GhostSnapshot` 项——用于回滚的快照。新的 `compacted_history` 组装完成后,这些快照会被追加到末尾,并通过 `should_keep_compacted_history_item` 过滤器标记为保留。否则,压缩后的 `/undo` 将不可用。
#### 远程端点下 `should_keep_compacted_history_item` 过滤器的行为
远程端点可能返回任意类型的项;引导程序会过滤:
**丢弃:** 开发者消息(陈旧/重复指令)、非 UserMessage/HookPrompt 的用户角色消息、Reasoning、FunctionCall/Output、ToolSearchCall/Output、CustomToolCall/Output、LocalShellCall、WebSearchCall、ImageGenerationCall、GhostSnapshot、Other。
**保留:** 助理消息、压缩项、用户角色警告、摘要消息。
实际含义:指令作者通过“一次性”开发者注入的内容在压缩后会被丢弃。
#### 遥测与功能开关
所有压缩事件都会以硬编码的 `strategy = CompactionStrategy::Memento` 发送到分析端点——无论实际实现如何。发射由 `Feature::GeneralAnalytics` 开关控制,且在 `CompactionAnalyticsAttempt::begin` 中快照该标志;之后 `track()` 不会再检查。禁用分析不会中断已在进行的回合。
最终器将 `Result` 转换为 `CompactionStatus`:
- `Ok` → `Completed`
- `Err(Interrupted)` / `Err(TurnAborted)` → `Interrupted`
- 其他 `Err` → `Failed`
#### 对指令作者的一个副作用
内联压缩完成后,引导程序会弹出警告提示:
这是一个降质信号:每轮压缩都会丢弃额外的一层上下文,因为开发者指令无法通过过滤器,摘要已是“重述的重述”。因此规则是:**系统指令必须放在 `base_instructions` 中(每轮重建),而不是运行时开发者注入中。**
#### 为何这是“最复杂的”机制
单次执行路径跨越六个独立不变量:
1. 标记算术(`estimate_tokens_with_base_instructions`);
2. 提供者检测(远程 vs 内联);
3. 带前缀缓存的网络重试;
4. 历史不变性(调用/输出对、用户/助理边界、摘要最后);
5. 回滚(幽灵快照);
6. 功能开关与分析状态机。
任一环节出错都可能导致 OOM 或模型质量无声下降。
---
### 示例 2. 解决规则繁多时的注意力衰减
**简短回答:** 引导程序已针对“规则多 vs 窗口有限”问题提供了解决方案,但不会原谅指令作者将规则放在“易失性”区域。如果规则放错了位置,模型会静默收到被裁剪的指令。以下六条实用规则,每条都附带机制与具体数值。
#### 规则 1. 保持低于 90% 的上下文窗口
引导程序在 `used_tokens > context_window × 9/10` 时自动触发压缩。压缩后:
- 仅保留最近总计 **20 000 标记** 的用户消息(`COMPACT_USER_MESSAGE_MAX_TOKENS`);
- 更早的内容溶解为摘要;
- 开发者消息(包括携带规则的)**会被完全丢弃**,视为“陈旧/重复指令”。
**实践:** 若窗口为 200k,安全上限为 180k。若你“逐步展开”规则跨越多个用户消息,序列开头会在首次压缩后消失。
#### 规则 2. 骨架规则只能放在 `base_instructions`
引导程序解析 `base_instructions` 的优先级如下:
```
base_instructions =
config.base_instructions // explicit override
?? conversation_history.get_base_instructions() // from rollout session_meta
?? model_info.get_model_instructions(personality) // model default
```
关键属性:**`base_instructions` 每轮重建**。它们不驻留历史,而是作为系统指令在独立请求中发送。因此能存活压缩,且与历史切片无关。
**实践:**
- 行为不变量、响应风格、硬规则 → `base_instructions`;
- 运行时策略(沙箱、审批)→ 开发者消息(引导程序会放置);
- 任务特定细节 → 用户消息(压缩后最近 20k 标记内存活)。
#### 规则 3. 已批准命令前缀列表有硬限制
若通过前缀列表配置 shell 命令审批,引导程序会将其渲染为开发者指令,并经历两次裁剪:
- **字节限制:** `MAX_ALLOW_PREFIX_TEXT_BYTES = 5 000`。溢出时在最后一个有效 UTF-8 字符处截断,并追加 `...[Some commands were truncated]`;
- **项目限制:** `MAX_RENDERED_PREFIXES = 100`。排序按 `(len, combined_str_len, alphabetical)`,仅取前 100 条,其余直接丢弃。
若你列出 200 条允许命令,模型只能看到 100 条——具体哪 100 条取决于字典序。引导程序不会警告。
**变通方法:**
- 将前缀分组(`git *` 替代 `git commit`、`git push`、`git pull` 分别列出);
- 将关键禁止规则放入 `base_instructions` 作为纯文本,而非执行策略。
#### 规则 4. 使用技能提及而非臃肿的系统提示
引导程序支持技能的惰性注入。回合内流程为:
1. `collect_explicit_skill_mentions(user_input)`——在用户输入中查找 `@` 提及的技能;
2. `resolve_skill_dependencies_for_turn`——检查这些技能所需的环境变量;
3. `build_skill_injections`——仅对提及的技能组装指令;
4. `record_conversation_items`——将其加入回合历史。
你可以拥有数十个技能及其详细规则,但只有用户明确调用的技能才会进入提示。这是保持基础提示小巧、避免上下文膨胀的根本方法。
**实践:** 将规则按主题拆分为模块(`@testing-rules`、`@security-review`、`@refactoring-style`)。基础提示仅包含“何时调用哪个技能”的元规则。详情按需拉取。
#### 规则 5. 尊重上下文片段标记
引导程序严格区分六种上下文片段,每种都有特定 XML 标记:
- `... `(`AGENTS_MD_FRAGMENT`)
- `... `
- `... `(`SKILL_FRAGMENT`)
- `... `
- `... `
- `... `
在历史归一化和预压缩修剪过程中,这些标记会被特殊对待:环境与子代理通知片段会被保留在内存输入中。
**后果:** 若你用自定义标签(如 `...`)包裹规则,引导程序会将其视为普通用户文本并优先裁剪——与内容无关。存活方案只能是 `base_instructions` 或标准 `SKILL_FRAGMENT`(通过技能注入)。
#### 规则 6. 初始上下文注入的位置
中回合压缩后,`initial_context` 的插入位置遵循优先级链:
1. 插入最后一个真实用户消息之前(而非摘要之前);
2. 若无,则插入最后一个“用户类”项(包括摘要)之前;
3. 若无,则插入最后一个压缩项之前;
4. 否则追加到末尾。
最终模型看到的顺序为 `[..., initial_context, last_real_user, summary]` 或 `[..., initial_context, summary]`。摘要始终位于最后——这是与训练模型的契约。
**实践 不要试图在历史末尾“追加”内容;引导程序会重排。应在初始上下文块中放置环境、agents.md 等,而非在历史末尾用用户消息提醒。
#### 正确放置规则的模板
```
┌─────────────────────────────────────────────────────────────┐
│ base_instructions ← invariants: style, safety, │
│ behavior boundaries │
│ (rebuilt every turn) │
├─────────────────────────────────────────────────────────────┤
│ initial_context ← env, AGENTS.md, persistent skill │
│ metadata │
│ (re-injected after compaction) │
├─────────────────────────────────────────────────────────────┤
│ skill injections ← modular detailed rulesets, │
│ activated by @-mention │
│ (not kept in permanent context) │
├─────────────────────────────────────────────────────────────┤
│ developer messages ← runtime policy (approval/sandbox) │
│ (volatile, 5000-byte / │
│ 100-prefix cap, silently cut, │
│ discarded on compaction) │
├─────────────────────────────────────────────────────────────┤
│ user messages ← task-specific content │
│ (last 20 000 tokens │
│ survive compaction) │
└─────────────────────────────────────────────────────────────┘
```
#### 如何验证规则是否“到达”模型
- **存活压缩** → 它存在于 `base_instructions`、`initial_context` 或最近 20k 用户标记内;
- **未被裁剪** → 审批前缀列表 ≤ 5000 字节且 ≤ 100 项;
- **出现在正确上下文** → 使用标准 `SKILL_FRAGMENT` 并通过提及激活。
若任一条件不满足,模型读取的将是你指令的裁剪版本——而大多数情况下无人会察觉。
## 结构与导航
```
knowledge/
├── SRC-0001/ ... SRC-0018/ # 18 modules, each with:
│ ├── rules.json # invariants and formulas
│ ├── structural.json # entities and relations
│ └── use-case.json # scenarios
├── scaffold_index.json # flat index of composition IDs per SRC
├── graph.json # edges: triggers / depends_on / contains
└── README.md
```
组合通过引用相互连接,例如 `→triggers SRC-0011/uc.sub/auto-compact-inline`。你可以通过 `scaffold_index.json` 和 `graph.json` 进行导航。
关于规范查询方法(grep + jq 配方):参见 `HOW-TO-QUERY.md`。
## 关键词
`openai codex harness` · `codex cli internals` · `codex agent loop` · `harness engineering` · `agent conversation turn lifecycle` · `tool registry` · `thread turn item` · `context compaction` · `auto-compact` · `responses compact endpoint` · `codex app server` · `prompt engineering reference` · `AI agent architecture spec` · `codex reverse engineering` · `base_instructions priority` · `initial context injection` · `SUMMARIZATION_PROMPT` · `CompactionStrategy::Memento` · `skill injection` · `prompt cache key` · `context window management`
参考规范:OpenAI Codex CLI 的反向工程规范(18 个 SRC 模块),涵盖协议、会话状态、代理核心、回合上下文与工具注册表。配套《Unrolling the Codex Agent Loop》系列。用途:编写代理指令、构建自有代理 harness、反向工程压缩策略、理解回合生命周期。
## 感谢使用
若本规范对你有帮助——**请给仓库加星**。这能帮助其他代理构建者与指令作者通过 GitHub 搜索找到它。
欢迎在 Issues 中提出反馈与扩展建议(新增 SRC 模块、公式优化、缺失边界情况等)。
标签:agent loop, AI 指令编写, CLI harness, OpenAI Codex, SEO: Codex CLI, SEO: 提示工程, SEO: 源码逆向, SEO: 知识图谱, 上下文管理, 云计算, 云资产清单, 令牌预算, 前缀列表限制, 威胁情报, 工具注册表, 工程实践, 开发者工具, 提示词截断, 源码分析, 自动压缩, 规则引擎, 逆向工程