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: 知识图谱, 上下文管理, 云计算, 云资产清单, 令牌预算, 前缀列表限制, 威胁情报, 工具注册表, 工程实践, 开发者工具, 提示词截断, 源码分析, 自动压缩, 规则引擎, 逆向工程