学 AI 产品 · 专业 AI 产品经理播客第 3 章 · T3 解剖 OpenAI Codex:把安全观写进类型系统 · EP 05
第 3 章 · EP 05

把上下文治理写进 code review

时长 13:42音色 云健 · 男声

同步字幕

章节导航(点击跳转)

0:00开场 · 类型放行的东西谁来看1:30
1:30六条禁令 · 十行原文念一遍2:21
3:51四份改动 · 逐条过评审1:40
5:31第二条 · 缓存前缀是怎么被打掉的1:48
7:20第一条 · 压缩换窗口并留下记录1:52
9:12六条里零条有 lint · 靠什么守住1:48
11:01横向对比与可带走的原则2:41
解读全文

ep16 · 把上下文治理写进 code review

本集对应课程章节:CHAPTER(OpenAI Codex · 代码模式 —— 把上下文治理写进 code review)
内容来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》),本页为二次演绎的解读与音频稿件版本。

一、本集解决什么工程问题

上一章把「往上下文里塞东西」收成了类型:编译器从此只接受实现了 ContextualUserFragment 的结构体。但类型过了,这段文字仍然可以合法地又长、又勤、又无界。

本集回答两个问题:

  1. 为什么实现了 ContextualUserFragment 的改动,编译器仍会放行一份会打掉 cache、撑满窗口、或让旧会话恢复失败的 PR?
  2. 六条禁令里,哪几条落在代码里,哪几条只能靠人和 skill 守?

核心判断:类型管形状,评审管成本。 挡住成本后果的,是仓库根十行禁令,外加一份会把同一节原文再读一遍的评审 skill。

能力地图

维度读完本集你能做到对应证据
六条禁令逐条复述 AGENTS.md「Model visible context」六条及其源码落点AGENTS.md L91–100
分组判断把六条归为「入口 / 上界 / 时机与注意力」三组课程逻辑轨迹
评审实操对一份注入类改动,判断它撞第几条四份 PR 教学案例
缓存前缀解释为何「每轮重写 environment」会让 cache 从第一层作废client.rs L262–274、prompt_caching.rs
历史不可改说明 replace_compacted_history 如何开新窗口而非改旧行session/mod.rs L3373–3418
远端压缩说明服务端 transcript 不可信时的本地重渲染滤网compact_remote.rs L354–372
自动化缺口说清六条里哪几条有测试影子、哪几条纯靠人AGENTS.md L91–100 + 源码
横向视野对比 DeepSeek Harness 的「Model-visible ⟺ logged」与 Codex 差异DSH AGENTS.md L107

二、六条禁令原文与分组

出处:仓库根 AGENTS.md 第 91–100 行,标题 Model visible context。同一段原文被抄进 .codex/skills/code-review-context/SKILL.md 第 7–13 行——评审机器人读到的就是这六条,没有第二套解释。

条原文要点盯的成本源码落点
1No history rewrite —— 上下文必须增量构建改旧行 → 恢复失败session/mod.rs L3383、AGENTS.md L95 / L110
2避免频繁改动上下文造成 cache miss每轮改前缀 → 缓存作废client.rs L272、AGENTS.md L96
3不允许无界条目,必须有上界与硬 cap无界注入protocol.rs L3112、AGENTS.md L97
4单条不得超过 10K tokens单条过大model_info.rs L167、AGENTS.md L98
5新的单条若可能超过 1K tokens,标 P0 并额外人工评审注意力分配additional_context.rs L5、AGENTS.md L99
6注入片段必须在 core/context 定义为 struct 并实现 ContextualUserFragment入口未登记AGENTS.md L100

分组:第 6 条管入口;第 3、4 条管上界;第 1、2、5 条管时机与注意力。

提示1 · 评审顺序:先问入口,再问上界

任何注入类改动,按固定顺序过:登记了吗(第 6 条)→ 有硬 cap 吗(第 3 条)→ 会超 10K 吗(第 4 条)→ 每轮变吗(第 2 条)→ 是追加还是改旧行(第 1 条)→ 新种类可能超 1K 吗(第 5 条)。顺序错了会浪费一整轮评审。


三、四份改动 · 逐条过评审

改动内容撞哪条换写法能否救
甲给 environment 加 git_status 字段,每轮写入完整工作区状态第 2 条(每轮改已发出的前缀)改成增量 + 硬 cap,红灯可灭;可能过 1K 仍标 P0
乙在 turn.rs 里 format! 一句 hint 提醒跑测试第 6 条(未走 trait,handler 直接拼 Message)换写法也过不了——必须先登记为类型
丙新建 fragment 把整个源文件塞进可见文本,不写截断第 3、4 条(无界 + 可能过 10K)增量 + 硬 cap 可灭灯
丁AGENTS.md 变更时就地改写历史里那一条说明第 1 条 + breaking changes 第 5 项(从已有 rollout 恢复会话)仍是在给旧消息打补丁,换写法无效

关键:四份都能写出干净的 struct 和干净的测试,编译器只看有没有 struct、有没有 marker,不看这条文字每轮变不变、有多长、会不会改旧会话。

教学说明:四份 PR 与写法切换为课程化设定,用于展示六条禁令各自盯的那一类成本;逻辑轨迹右侧行号对应真实源码。

四、第 2 条 · 缓存前缀是怎么被打掉的

原理:消息历史从前往后拼,缓存按前缀匹配。前面任何一处变了,后面全部作废。每轮重写 environment XML、每轮换一次工具清单 → cache 从第一层作废,代价按轮翻倍。

易忽略的细节:即使内容这一轮与上一轮完全相同,只要它是被重写而非被追加,位置或格式的任何抖动都会打断前缀。所以第 2 条说的是「避免频繁改动」,不是「避免大改动」。

源码落点

  • 会话级客户端把跨 turn 稳定与turn 内粘滞拆开,sticky token 不准跨 turn 重放 —— core/src/client.rs L262–274
  • Guardian 审查会话故意复用同一条 trunk,以保住 prompt_cache_key —— core/src/guardian/review.rs L932–934
  • 集成测试 prompt_caching.rs 要求连续两轮的 instructions 和 tools 必须一致

五、第 1 条 · 压缩换窗口,留下记录

典型踩坑:AGENTS.md 改了 → 最省事是找到历史里那条 UserInstructions 把正文换掉。当前轮少占一条消息,token 看起来还降了;但从旧 rollout 恢复时读到的是被改过的文本,会话对不上。

压缩看似也在改历史(旧窗口从 live history 消失)。若把压缩做成「打开历史文件改一行」,第 1 条与 breaking changes 第 5 项会一起被踩中。

Codex 的答法:给压缩换一个定义

  • replace_compacted_history 把新表整表装进 live history,旧内容以带 replacement_history 的 CompactedItem 追加到 rollout,不回改旧行
  • 注释写明「Compaction starts a new history window」
  • 生产路径里就地换表的 replace_history 标了 #[cfg(test)] —— 这条路在生产中被关掉
  • 出处:core/src/session/mod.rs L3373–3418、AGENTS.md L95 / L110

远端压缩的额外滤网:服务端送回的 transcript 不可信,developer 消息直接丢弃,再由本地按当前 world state 把带 marker 的 fragment 重新渲染进去。历史继续增量构建,压缩继续换窗口 —— core/src/compact_remote.rs L354–372。

为什么长期成立:追加写 + 用快照换窗口,是日志系统的通用形状。事件溯源换的是投影,LSM 树换的是 SSTable,都不回改已经写下的旧行。禁止就地更新,恢复才有得对。

提示2 · 判断压缩实现是否合规

问一句就够了:旧内容是被追加成一条可回放的记录,还是被就地改写? 前者是换窗口,后者是改历史。生产代码里若出现就地换表的函数,检查它是否仅存在于 #[cfg(test)] 之下。


六、六条里零条有专用 lint

条自动化程度兜底机制
1有集成测试影子prompt_caching.rs 之外基本靠人
2有集成测试prompt_caching.rs:连续两轮 instructions / tools 必须一致
3局部 cap工具输出侧按字节 10_000 截断,超出从中间砍,前置警告行告知原 token 数与总行数(protocol.rs L3112、output-truncation/src/lib.rs L12–24)
4局部 capmodel_info.rs L167
5纯靠人无 P0 枚举,也无 lint 估算新 struct 的 body() 是否超 1K(additional_context.rs L5,通用附加上下文卡 1_000 token)
6部分拦得住「未实现 trait 就走 render_full」,拦不住在 handler 里直接拼一段 Message

结论:skill 的存在本身就说明执行主体是评审,不是编译器。把禁令原文抄进 SKILL.md,机器人与人读同一段文字——规则只有一份,就不会漂移。

提示3 · 两条「换写法也救不了」的红线

  • 未登记类型(第 6 条):handler 里 format! 一段 Message,编译能过,评审打回,改写法无效。
  • 改旧行(第 1 条):就地 patch 旧消息,换写法仍是 patch,且会踩 breaking changes 第 5 项。

这两条必须在写第一行代码之前就决定,不能寄望于事后修补。


七、总量谁来管

六条禁令没有写总量数字,代码用两层补上:

层机制出处
模型窗口full_context_window_limit 硬顶core/src/session/context_window.rs L53–54
会话树RolloutBudget 按加权 token 记账,用尽对整棵 thread 停写core/src/rollout_budget.rs L45–65、L74–79

含义:40 条都合法、每条 9K,总量仍会被满窗或会话预算拦住。单条合规不等于整体合规。


八、横向对比

项目主张覆盖范围
DeepSeek Harness「Model-visible ⟺ logged」:送到模型请求里的东西必须能从会话日志重建;新的模型可见输入必须对应一条 session 事件(AGENTS.md L107)管可见与落盘对齐;不管是否无界、是否每轮改、单条是否超 10K
Codex六条禁令 + ContextualUserFragment 类型入口管入口、上界、时机与注意力
Claude Code——用 Model visible context / ContextualUserFragment / unbounded context 检索,未找到公开的上下文注入评审规范。REVIEW.md 是给审查模型看的产品化 PR 规则,与运行时工程红线不是同一层。本格留空待补

额外值得学的一点:DSH 会把被否决过的路立档——一篇讨论「是否把压缩的定义包与唯一实现折在一起」的笔记,Status 写明 rejected,并单独留下 Alternatives considered(将来可能有远端 / recall 后端,但不构成现在拆包的理由)。Codex 一侧没有这样的 rejected/ 目录,后来者只能从 prompt_caching.rs 与 Guardian 注释反推「某次每轮注入 git status 为何撤回」。


九、审查清单

注入模型上下文的改动,合并前逐项自查:

  • 是否已在 core/context 登记为 struct 并实现 ContextualUserFragment(第 6 条)
  • 是否有硬 cap,而非「理论上不会太大」(第 3 条)
  • 单条是否可能超过 10K token(第 4 条)
  • 新种类若可能超过 1K token,PR 是否已标 P0 并安排额外人工评审(第 5 条)
  • 是追加新行还是改写历史中的旧行(第 1 条)
  • 是否会导致每轮前缀变化,进而作废 cache(第 2 条)
  • 从已有 rollout 恢复会话时,读到的是否仍是未被改动的文本(breaking changes 第 5 项)
  • 总量是否会在 40 条 × 9K 的极端情形下被 full_context_window_limit 或 RolloutBudget 拦住
  • 若使用压缩,旧内容是否以 CompactedItem 追加而非就地换表
  • 评审依据的禁令原文是否与 SKILL.md 中一致(规则只有一份)

提示4 · 课堂练习:这句 format 能留吗

有人要在 session/turn.rs 里 format! 一段 <workspace_map>,把当前目录树塞进去,声称只有调试时才开。

  • 按六条逐条过:哪几条亮红?改成什么样才能留?
  • 进阶一问:若目录树最坏超过 1K token,PR 标题要不要标 P0?源码里有没有对应的属性宏替你标?(答案:没有,第五条纯靠人。)

十、约束说明

  1. 源码口径:依据本地仓库 openai/codex,核对文件 AGENTS.md,commit 4f39251a01,核对日期 2026-08-22;代码块保留源码原文。
  2. 教学化内容:四份 PR 与写法切换为课程化设定,用于展示六条禁令各自盯的成本;行号对应真实源码。
  3. 横向对比口径:DeepSeek Harness 依据其 AGENTS.md 与 Agent Notes(2026-08-22);Claude Code 一格为已检索未找到,不是「不存在」,留待新的还原源码复查。
  4. 方法边界:六条禁令针对运行时注入模型上下文的工程红线,不等同于产品化的 PR 审查规则,两者不在同一层。

类型管形状,评审管成本。注入必须登记为类型,必须有硬 cap,只追加不改旧行。压缩换窗口并留下 CompactedItem。just 查不到的那几条,靠人和会把同一节原文再读一遍的 skill。


来源:xueai.miyang.cn(小山学堂 · 洛小山《学 AI 产品,从入门到精通》)
本页为二次演绎的解读与音频稿件版本,音频与本页配套发布。