Codex Harness 源码课 / 中文
LESSON 06 / 12 35 分钟

上下文选择:从历史入库到请求装配

逐段追踪 Prompt.input 的来源,分开接纳规则、输出截断、状态差量和协议规范化。

公开源码快照 · 2026-10-13 · f82e365922

学完你能做到

本课目录 · 6 节

1. “select 上下文”其实有几次选择

先限定问题:这里追踪的是普通请求从本地状态到 Prompt.input 的装配过程,以及后续所使用的保留逻辑。它并不是“从仓库里自动取最相关的代码”这一单一算法。仓库文件只有通过读取工具或显式输入进入历史,才会进入这条路径。

阶段选择依据对信息的影响
历史入库协议项类型和 不是所有事件都进入历史;部分配置更新需要框架来源标记。
工具输出入库单项容量策略对活跃历史副本做;原始记录可另行持久化。
指令与环境更新当前 和已知基线初次完整注入,后续按分区状态产生更新。
请求前规范化调用关系、输入模态补缺失结果、去孤立结果,移除不支持的媒体。
后的历史重建分支、类型白名单、预算和时间顺序哪些原文保留由客户端规则决定;摘要或结果是另一份信息。

在这些函数中没有看到面向整段活跃历史的 embedding 检索或相关性排序。这个结论只针对所列调用链:插件、外部检索工具或扩展可以先取得资料,再把它作为输入加入。不能由此断言整个产品没有 。

普通请求的上下文装配:入库过滤、单项截断、状态更新、历史规范化与 Prompt 构造。
普通请求的上下文装配:入库过滤、单项截断、状态更新、历史规范化与 Prompt 构造。 · 教学示意,省略次要分支;不是完整调用图。

2. 入库:先判断这是不是模型历史

ContextManager 的核心集合是带元数据的 ResponseItemEnvelope,而不是界面上显示的全部消息。record_item_with_metadata 先调用 is_api_message;普通 Message 排除 system 角色,ConfigurationUpdate 只有在 harness_authored_configuration 为真时进入;CompactionTrigger 与 Other 不进入。工具请求、输出、推理项和项等则有明确的接纳分支。

这里排除原始 system 消息,不意味着请求没有基础指令。基础指令有单独的持有与装配路径。反过来,把输入文本的 role 写成 developer,也不能制造 client_authored 等来源元数据。类型和来源共同影响后续保留行为。

真实源码 · Apache-2.0 · 未改写
fn is_api_message(message: &ResponseItem, metadata: Option<&CodexHarnessMetadata>) -> bool {
    match message {
        ResponseItem::Message { role, .. } => role.as_str() != "system",
        ResponseItem::ConfigurationUpdate { .. } => {
            metadata.is_some_and(|metadata| metadata.harness_authored_configuration)
        }
        ResponseItem::AdditionalTools { .. }

入库规则不是单凭角色优先级排序。配置更新还检查来源。

查看原文及相邻代码 ↗

完整接纳白名单 ↗codex-rs/core/src/context_manager/history.rs:1031–1054

历史快照使用 Arc 共享数据。clone_history 并不等于每次先深拷贝所有内容;规范化需要修改时,经 make_mut/unwrap_or_clone 得到请求侧副本。这个 设计让重试和请求组装可以使用稳定快照,同时避免每个只读阶段都复制大历史。

快照转请求材料 ↗codex-rs/core/src/context_manager/history.rs:603–617

3. 工具输出:先限制单项,再讨论整个窗口

record_annotated_items 的注释明确区分活跃历史与原始 rollout。实际入库时,函数输出和自定义输出在副本上被:若 带 history_truncation_token_limit,采用该覆盖值;否则在策略上增加序列化余量。工具输出的原始 envelope 没有在此处被同样替换,持久化仍有独立策略。

活跃历史截断不改写原始输出 ↗codex-rs/core/src/context_manager/history.rs:517–528

单项工具输出预算选择 ↗codex-rs/core/src/context_manager/history.rs:556–577

默认余量是 policy × 1.2。文本保留首尾,省略中段; 估算采用 UTF-8 字节数除以 4 向上取整。它不是 tokenizer,因此中文、代码和媒体的真实使用量可能不同,省略标记也不能直接当作精确额度证明。

预算余量与截断入口 ↗codex-rs/utils/output-truncation/src/lib.rs:17–40

字节近似常量 ↗codex-rs/utils/string/src/truncate.rs:1–5

首尾截断及估算实现 ↗codex-rs/utils/string/src/truncate.rs:61–115

4. 指令和环境:完整注入与差量更新

当前环境、权限、指令、项目约定和工具等被组织为 分区。没有 reference_context_item 时,record_context_updates_and_set_reference_context_item 构造完整初始并记录快照;已有基线时,更新世界状态并只发出相应分区的差量。

完整注入或状态更新的分支 ↗codex-rs/core/src/session/mod.rs:4744–4785

每个分区知道怎样比较自身状态,也知道怎样识别历史中仍保留的片段。render_history_diff 即使找到了旧快照,如果对应片段已不在活跃历史里,也会把它视作 Absent,再次渲染。这个检查避免“本地基线认为已经发过,但后的输入其实看不到”的失配。

真实源码 · Apache-2.0 · 未改写
            if let Some(previous) = previous.and_then(|previous| previous.sections.get(id)) {
                if section.has_retained_fragment_matcher()
                    && !has_retained_fragment(items.clone(), section)
                {
                    PreviousSectionState::Absent
                } else {
                    PreviousSectionState::Known(previous)
                }
            } else if has_legacy_fragment(items.clone(), section) {
                PreviousSectionState::Unknown

精确快照和活跃历史是否含有对应片段,需要一起核对。

查看原文及相邻代码 ↗

以 分区为例:状态相同不输出;替换时加入 replacement notice;删除时发出 removal notice。新文本不是简单追加给去猜哪些旧指令失效。分区更新还区分前缀、独立项和可合并片段,装配时维持规定的顺序。

约定的相同、替换与删除分支 ↗codex-rs/core/src/context/world_state/agents_md.rs:45–82

上下文放置策略 ↗codex-rs/core/src/context/world_state/mod.rs:68–80

5. for_prompt:结构修复,而不是挑出“重要消息”

正常克隆当前历史,调用 for_prompt(model.input_modalities),然后把结果交给 run_sampling_request。for_prompt 的关键动作是 normalize_history。它不在这里重新对所有消息打相关性分,也不默认只保留最近 N 条。

正常采样请求读取活跃历史 ↗codex-rs/core/src/session/turn.rs:523–540

顺序操作必须保留的语义
1给缺失输出的调用补合成结果普通函数/自定义调用使用 aborted 结果;不是补一个“成功”。工具搜索缺失时可补空目录输出。
2去除没有对应请求的孤立输出具名、无 的外部工具事件有特殊规则,不能一概删除。
3移除不支持的图像按当前 input_modalities 转换消息和工具输出。
4移除不支持的音频模态能力变化影响输入形状,和历史相关性是两回事。

四步规范化顺序 ↗codex-rs/core/src/context_manager/history.rs:962–980

这是协议 的修复:调用与结果必须对齐,输入必须符合当前能力。合成结果的 ID 从源项稳定派生,源码注释说明这是为重复规范化、重试和恢复时的缓存复用保留稳定性。

合成结果的稳定身份 ↗codex-rs/core/src/context_manager/normalize.rs:145–170

6. 组装 Prompt:工具和基础指令有两种放法

常规模式中,Prompt 把历史 input、model_visible_specs 和 base_instructions 分别装入字段。若当前窗口采用增量工具模式,build_prompt 会把 tools 设为空、base_instructions 设为空,因为这些信息通过历史前缀与更新项提供。

build_prompt 的常规与增量分支 ↗codex-rs/core/src/session/turn.rs:1563–1592

增量模式把工具和基本指令放入状态 ↗codex-rs/core/src/session/world_state.rs:144–160

所以分析请求大小时,应区分“逻辑输入由什么组成”和“这次传输只发了哪些增量”。连接复用或请求缓存是传输层问题,不能用网络包很小推断只看见最后一条消息。哪些历史原文在窗口重建后保留,继续读下一课的三种分支。

沿着源码读

以下定位均锁定本课程版本。先读注释与分支,再深入类型定义。

READ & VERIFY / 小练习

实现推演

构造四个输入项:无来源标记的 ConfigurationUpdate、超限 FunctionCallOutput、只有调用没有输出的 FunctionCall、含图像的用户消息。给当前设置仅文本模态,逐阶段写出每个项是否进入历史、怎样变化、是否影响持久化原文。

阅读练习无需模型账号。示例命令请先确认目录与环境。

自测与边界检查

换成仅文本能力后,是哪个阶段决定图像不再进入普通请求?

展开自测答案

for_prompt 调用 normalize_history,按 input_modalities 移除不支持的媒体。它不是按相关性选择图像。此前入库与后续对媒体还有各自的容量规则。

为什么记录了一个 快照还不够?

展开自测答案

需要看到快照所代表的实际片段。render_history_diff 会同时检查历史是否仍有该片段;没有就按 Absent 重新渲染。否则或回滚后,框架与可见状态可能失配。

学习记录保存在本机

搜索课程

输入关键词,搜索全部课程、词典与来源。

就地词典

在完整词典中查看 →

完整架构总图

放大后横向、纵向滚动阅读;聚焦图示区域后可用方向键滚动。

完整架构职责图,内容与正文总图相同