大模型的上下文窗口是稀缺资源,而 Skill、工具和能力描述却几乎可以无限扩展。于是,Harness Agent 这类产品真正竞争的,已经不只是模型本身的“智力”,而是对有限上下文资源的控制能力。谁能更好地做上下文管理、预算分配和能力装载,谁就更能把模型的能力稳定地交付到具体任务里。
128K tokens 的上下文窗口听起来很大,但它需要承载系统提示词、工具定义、对话历史、、Skill 的信息、模型输出预留空间。现在我们就一起感受下Codex对于Skill上下文预算控制的精妙设计。
预算控制设计
在深入算法之前,回忆下之前有说过Codex的Skill的架构设计,它的核心思想在于渐进式披露过程,我们初步的理解为以下3个层级步骤:
Skill 上下文预算控制,就是围绕整套 Skill 运作机制,对上下文资源做精细分配。它要解决的不是单纯的长度限制,而是在有限窗口内,优先保留真正决定模型行为的那部分技能信息,让关键能力能够稳定进入上下文、参与推理,并尽量避免无关内容稀释指令密度。
预算控制算法
预算计算算法
渐进式披露的第一步是确定 Skill 目录能占多少空间,即预先设定Skill目录空间限制边界。Codex对于边界限定计算有两条路径:
(1)上下文占比约束
当 Codex 能获取到模型的上下文窗口大小(如 128K tokens)是,约定预算 = 上下文窗口 ×2%,单位是 Tokens。例如OpenAI模型上下文窗口为128k时,所允许的Skill目录占用的空间约:128K → 128000 × 2% = 2560 tokens
(2)固定字符空间
当获取不到上下文窗口大小(模型信息未知,或值为负数),兜底预算 = 8000 字符,单位是 Characters。这是因为Token 估算方法时依赖于模型OpenApi tokenizer计算,对于未知的模型,每次预算计算都要跑一遍 tokenizer 不现实,所以固定字符空间约束, 8000 字符是一个近似的空间大小,token数 ≈ 字节数 ÷ 4。
APPROX_BYTES_PER_TOKEN = 4,基于英文文本的平均 token 长度经验值。对中文等语言会有偏差(1 个中文字 ≈ 3 字节 UTF-8 ≈ 0.75 token, 而实际 tokenizer 可能算 1-2 token),但作为预算控制足够用——预算控制不需要精确到每个 token,只需要大致不超就行。所以这里需要注意的是,同一个Skill描述,在codex中的不同模型下,可能计算出来开的cost是不同的,因为可能使用了不同计算方式
目录裁剪算法
目录裁剪算法是生成Skill目录过程中核心的预算控制算法,render_skill_lines_from_lines() 函数。它对所有 Skill 按三层策略递降处理,每层对应不同的裁剪力度。这里的目录成本核心指:名称、描述、路径
🟥 第一层:全部展示(无裁剪)
codex通过遍历所有 SkillLine,累加每个 Skill 的 full_cost(包含完整名称、描述的渲染成本),如果总和不超过预算,直接返回完整渲染结果。即所有skill目录成本总和<=预算,此时不做任何裁剪,全部展示给LLM。
在大多数正常使用情况下—5 个 System Skill + 几个用户 Skill,总共 8-10 个 Skill——完整展示的总成本通常在 500-1500 tokens 之间,远低于 2560 tokens的预算。所以通常来说,个人使用时基于不会进入到裁剪策略中。
🟥 第二层:裁剪描述(逐字符公平分配算法)
不出意外的出现意外了,当Skill过多时,所有目录成本超过了预期的预算时,可能就会触发当前裁剪策略。但是需要注意的是,当前策略触犯条件为:
所有目录成本超过预算,且去掉描述后的最小成本未超过预算
此时codex采用的策略是裁剪描述(逐字符公平分配算法),即所有 Skill 保留在列表中(不删除任何一个),但描述被裁剪到更短。
❌ 固定等分裁剪算法(固定分配)
Codex在做描述裁剪时,并不是固定等分的去裁剪每个skill,即假设有4个skill,预算大小为20,则平均每个skill的预算空间为5token,然后进行裁剪。这种方式存在一个较大的问题就是不公平,例如某些skill可能描述很短,但是却分配到了过多的预算,导致预算浪费,反而描述更长的skill却因为预算不足被裁剪。
✔️逐字符公平分配算法(按需分配)
Codex采用的是逐字符公平分配算法,这是一种最精巧的算法,目的在于按需分配,描述越长分越多,短描述Skill几乎没展示空间。
算法而核心为:每个 Skill 每轮获得 1 个描述字符的机会完全平等,但短描述 Skill 用完原始描述后自动退出,剩余预算自然流转给还在排队的长描述Skill。
假设预算留给描述的空间只有 6 个字符,有 3 个 Skill:
- Skill A 描述:"x"(1 字符)
- Skill B 描述:"abcdefghi"(9 字符)
- Skill C 描述:"uvwxyz"(6 字符)
最终的裁剪结果为:Skill-A完整、Skill-B(abc)、Skill-C(uv)。
但是需要注意的是,codex真正的成本计算不是简单的"1 字符 = 1 成本"。因为预算可能是 Token 单位,需要把字符增量转换为 Token 增量。
🟥 第三层:省略 Skill
如果Skill目录最小成本(整体成本 - 所有描述成本 = name: (file: path)格式)就已经超过了预算了,此时就不再裁剪描述,直接删除某些 Skill——名称和路径都不出现在目录中。这里本质上也很简单,就是按照之前说的Skill分层优先级来进行省略。
System → rank 0(最高优先级)
Admin → rank 1
Repo → rank 2
User → rank 3(最低优先级)
路径别名算法(空间优化)
路径别名算法不是实际裁剪算法,应该说是整个预算的一种空间优化算法。Skill 路径可能很长,尤其是 Plugin 提供的 Skill。比如一个 Plugin Skill 的路径可能是:
/Users/xl/.codex/plugins/cache/openai-curated/github/hash1234567890/skills/gh-fix-ci/SKILL.md,这个路径本身就有 90+ 字符,在预算紧张时光是路径就吃掉大量空间。
Codex 为长路径创建短别名——用 r0、r1、r2 等替代原始 Skill Root 路径,形成映射关系,例如:
### Skill roots - `r0` = `/Users/xl/.codex/plugins/cache/openai-curated/` ### Available skills - gh-fix-ci: Fix CI pipelines (file: r0/github/hash1234567890/skills/gh-fix-ci/SKILL.md)内容截断算法(硬截断)
Skill 目录只是第一层——始终可见的索引。当用户触发某个 Skill 时,它的完整 SKILL.md 内容会被注入,这是第二层。
需要注意的是codex在对Skill内容采用的是硬截断的方式:
它约束Skill最大内容上限约: 40000 字符(约 10000 tokens)
一旦超过 40000 字符,则在第 40000 个字符处截断,返回截断后内容 + truncated 标记。
同时截断时发送 Warning:"Skill xxx exceeded the main prompt context limit and was truncated."
采用硬截断的方式,核心是因为:
极端场景触发:首先40000 字符足够覆盖绝大多数 Skill(大多数 SKILL.md只有几百到几千字符),安全阀几乎不会被触发。它存在是为了防止极端情况——某个 Skill 的 SKILL.md 有 50000+字符(可能是作者不小心或包含了大段代码模板),不会全部涌入模型请求撑爆上下文。
所以裁剪是一种危险的行为,因此我们在定义Skill内容需要充分考虑到这些因素。
工程化总结
这里分析的内容对于Codex上下文管理,只是冰山一角,例如去重机制、安全阀控制都没有说到。但是也是收益匪浅。
Skill 系统的核心矛盾是无限增长的能力描述 vs 稀缺的上下文窗口。Codex 的应对方式不是"超了就砍",而是构建了一套分级的资源调度体系——预算计算定边界、目录裁剪在边 界内分配、内容截断防溢出止损。每一级都比上一级更激进,但都有明确的触发条件。
这套体系的设计哲学可以概括为一个词:按需。
目录始终注入,因为模型需要知道"有什么可用"——这是基础需求,不可省略
完整内容按需注入,因为只有被触发的 Skill 才需要详细指导——这是精准需求,不可浪费
路径别名按需启用,因为预算充裕时绝对路径更直观——简单方案优先
裁剪按需升级,因为轻度压力裁剪描述就够了,没必要直接省略 Skill
同时在实际落地Skill时,需要核心关注和思考一下:
描述的精炼度直接决定你能否被完整展示
层级选择决定你的生存优先级
SKILL.md 的长度控制在 40000 字符以内
渐进式披露的三层结构是你的设计模板
scripts 和 references 没有专属预算保护,自己节省
https://github.com/openai/codex