返回 AIPA 笔记
AIPA Day 40

context engineering — JIT 检索与 context rot

context engineering — JIT 检索与 context rot

2026-07-24
context-engineeringjit-retrievalcontext-rot

日期: 2026-07-24 阶段: Phase 2 - AI-native 参考架构 标签: #context-engineering #jit-retrieval #context-rot

核心问题

P2 到现在,orchestrator(orchestratorAgent.ts)的子 agent 已经能并行检索、能记账(Budget)。但有一个被普遍误信的直觉没被挑战过:「上下文窗口越大,把越多资料塞进去越好」。Gemini 2.5 给百万 token、Claude 给 200K——很容易得出「先把 312 篇笔记 + 全部 trace + 全部 AML 案例预载进去,模型自己挑」的结论。

今天证明这是错的,而且是系统性地错。两个问题:

  1. 为什么塞满上下文反而降召回? 这不是工程 bug,是 transformer 架构的固有代价。要量化它(context rot 曲线),并讲清机制(n² 注意力 + 长序列训练数据稀疏)。
  2. JIT(just-in-time)检索怎么缓解? 即「不预载、给 agent 轻量引用(路径/查询/链接),运行时按需取」——它换来了什么、又付出了什么(运行时探索更慢)。

对 AML Copilot 这是合规质量问题:一份 SAR 的事实核查若被淹没在 100K token 的无关 trace 里,judge 拿到的就是「召回退化」的上下文,校准好的 κ 也救不回来——garbage context in, garbage verdict out

关键内容

A. context rot:召回随输入长度退化的机制与数据

Anthropic 的定义直白(Effective context engineering for AI agents, 2025-09):

"As the number of tokens in the context window increases, the model's ability to accurately recall information from that context decreases."

机制有三层,不是单一原因:

  1. n² 注意力预算被摊薄。transformer 对 n 个 token 产生 n² 对两两关系,每个 token 必须 attend 到其余所有 token。Anthropic 把上下文称为「有限资源,有边际递减回报」——「LLMs, like humans, have a limited attention budget」。token 越多,注意力被摊得越稀。
  2. 长序列训练数据稀疏。模型在长序列上见的训练数据更少,处理「跨整个上下文的依赖」的专用参数也更少——这是数据分布问题,不是推理时能修的。
  3. 语义近似的干扰项主动误导。Chroma 的 Context Rot 研究(2025)测了 18 个前沿模型(GPT-4.1 / Claude 4 / Gemini 2.5 / Qwen3),发现「语义相似但无关的内容会主动误导模型」,退化幅度超过「单纯长度」能解释的部分。

量化 context rot(Chroma 2025 关键发现,原文口径):

现象数据含义
普适退化18/18 模型随输入变长性能下降无一幸免,且「非均匀、常出人意料」
单个干扰项哪怕只加 1 个 distractor,相对「needle only」基线就掉干扰项「在幻觉回答里出现最频繁」
needle-问题相似度相似度低的 needle,随长度退化更快(PG essays 0.445–0.775 区间)检索目标与问题措辞越不像,长上下文越埋没它
位置效应(Liu 2023 + Chroma)上下文 <50% 满时 U 形(首尾好中间差);>50% 满时偏向近端「lost in the middle」随填充率变形
模型差异Claude 系幻觉率最低,GPT 系在有干扰项时最高退化幅度模型相关,但方向一致

把它画成召回曲线(示意,纵轴=正确召回率,横轴=上下文填充 token 数,定性形状取自 Chroma「非均匀单调下降」结论):

召回率
 1.0 ┤●●●○                      短上下文≈满分(needle-in-a-haystack 经典任务)
     │     ○○○                  几千 token 起肉眼可见下滑
 0.7 ┤         ○○○○             加入 1 个语义近似干扰项 → 整条曲线下移
     │              ○○○○○       >50% 满后偏向近端,中段证据被埋
 0.4 ┤                   ○○○○○○ 长尾:advertised 200K,effective 远小于此
     └───┬────┬────┬────┬────┬──► 上下文 token
        1K   10K  50K  100K 200K

反直觉洞察①(把所有上下文塞进窗口反而降召回):直觉是「窗口够大就全塞,省得漏」。但 context rot 说明有效上下文(effective context)远小于广告上下文(advertised window)——200K 的窗口不代表第 199K 个 token 还能被准确召回。更狠的是:塞进去的「语义近似但无关」的资料(比如另一个相似 SAR 的 trace)会主动拉低对目标证据的召回,比留白还糟。上下文不是越多越好,是越精越好;多余的 token 不是中性的,是负资产。

B. JIT 检索 vs 预载:用轻量引用换运行时按需加载

Anthropic 给的解法是 just-in-time(JIT)检索——把「检索时机」从「推理前一次性预载」推迟到「推理中按需触发」:

「maintain lightweight identifiers (file paths, stored queries, web links, etc.) and use these references to dynamically load data into context at runtime using tools.」

Claude Code 是范例:它对大数据库做分析时,写定向查询 + 用 head/tail 这类 Bash 命令切片,「without ever loading the full data objects into context」——agent 自己导航、自己取,全量数据从不进上下文。

JIT vs 预载(pre-inference / pre-loading)对比:

维度预载(pre-loading)JIT(按需检索)
何时取上下文推理前一次性全取推理中由 agent 用工具按需取
进窗口的内容全量原始数据仅轻量引用(路径/查询/链接)+ 命中片段
context rot 暴露高——全量挤占注意力预算低——只载相关片段
首字延迟低(数据已在手)高——运行时探索是多轮工具调用
失败模式召回被淹没、成本爆炸agent「乱用工具/死胡同探索」浪费上下文
Anthropic 原话「runtime exploration is slower than retrieving pre-computed data」

没有免费午餐——Anthropic 明确两条代价:(1)「runtime exploration is slower」;(2)「opinionated and thoughtful engineering is required」防止 agent 用错工具、陷入死胡同空耗上下文。所以官方推荐混合策略

「retrieve some data up front for speed, and pursu[e] further autonomous exploration at its discretion.」

Claude Code 自身就是混合体:CLAUDE.md 启动即载(高频、稳定、小),glob/grep 让它运行时 JIT 取文件(海量、按需、大)。

JIT 决策伪代码(把「该不该预载」变成一条规则):

def should_preload(item):
    # 预载只给「高频 × 稳定 × 小」的内容;其余走 JIT
    if item.access_frequency == 'every_turn' and item.is_stable and item.tokens < SMALL:
        return True          # 例:系统约束、pinned facts、AML rubric
    return False             # 例:312 篇笔记全文、全量 trace、案例库 → 留引用,JIT 取

context = [x for x in candidates if should_preload(x)]   # 瘦上下文
# 其余仅注入「索引/工具签名」,让 agent 运行时自取
tools = [hybridSearch, getCaseTrace, getNoteByPath]

这正是 RAG 的本质:RAG 就是 JIT 检索的一种——不预载全语料,给 agent 一个检索工具,命中片段才进上下文。hybridSearch.ts 的「over-retrieve per lane(pool=k×4)再 RRF 融合到 k」就是在控制「进窗口的量」。

C. 四技术合体:JIT 检索如何接进 orchestrator

Anthropic 列的四个上下文管理技术不是并列选项,是协同的一套,对应到本仓库已有/将建的部件:

Anthropic 技术做什么本项目落点
JIT retrieval留引用、运行时按需取hybridSearch.ts(命中才进窗口)+ 子 agent 工具
compaction近上限时摘要历史,保留架构决策、丢冗余输出contextBuilder.ts 的 summary 分支(>阈值触发 summarizeMessages
structured note-takingagent 维护外部持久记忆(NOTES.md),重置后恢复pinnedFactsStore.ts(pinned facts 跨 turn 持久)
sub-agent专职子 agent 处理聚焦任务,回传 1000–2000 token 压缩摘要orchestratorAgent.ts 子 agent 仅回 { text },不回原始检索结果

关键串联orchestratorAgent.ts 的子 agent execute 现在只 return { text: r.text }——这本身就是「sub-agent 压缩摘要」:检索的原始命中(可能上千 token)留在子 agent 的局部上下文里不回传给 lead,lead 只拿到浓缩结论。这把 context rot 挡在了 lead 的上下文之外。contextBuilder.tstotalTokens <= summaryThresholdTokens 分支则是 compaction 的触发器。

反直觉洞察②(JIT 的瓶颈不是「取不到」,是「取太多/取错」):上线 JIT 后,团队第一反应是担心「agent 漏检关键证据」。但 Anthropic 的实践警告恰恰相反——主要失败模式是 agent 乱用工具、陷入死胡同探索,把上下文喂满了「探索过程的噪声」。所以 JIT 不是「给个检索工具就完事」,必须配预算闸(Budget.assertCanToolCall)+ 工具描述精确化,否则 JIT 会用「探索噪声」复刻它本想消灭的 context rot。

设计要点/决策表

要点决策理由
默认载入策略JIT 优先,仅「高频×稳定×小」预载effective context ≪ advertised window,预载全量=自找退化
预载白名单系统约束、pinned facts、AML rubric每轮都用、稳定、token 小
JIT 通道hybridSearch + 案例/笔记按路径取命中片段才进窗口,over-retrieve 后融合裁剪到 k
子 agent 回传仅压缩摘要(≤2000 token),不回原始命中把 context rot 挡在 lead 上下文外
防 JIT 退化预算闸 + 精确工具描述主要失败是「乱探索」而非「漏检」
混合策略CLAUDE.md/约束预载 + RAG 工具 JIT对齐 Anthropic「up front for speed + autonomous exploration」

对本项目的落地

  • orchestratorPrompt.ts 注入 JIT 原则:在 ORCHESTRATOR_SYSTEM 增一段「Context discipline」——明确「不要求子 agent 回传原始检索结果,只回结论性摘要;需要细节时再派一次定向子查询」。这把「sub-agent 压缩摘要」从隐式行为升级为显式契约。
  • contextBuilder.ts 的 compaction 已就位但需补一条:当前 summary 分支保留「最后 3 条 message」+ summary。补一条规则——架构决策类消息(如已确认的 SAR 判定)应进 pinned facts 而非被 summarize 抹平(呼应 Anthropic「compaction 保留架构决策、丢冗余输出」)。具体:在 pinnedFactsStore.ts 增一类 kind: 'decision',compaction 时优先保活。
  • hybridSearch.ts 的 pool 是 JIT 的量控旋钮pool = max(k*4, 20) over-retrieve 后 RRF 裁到 k——这正是「按需取、只让相关片段进窗口」。Day 41 将在此基础上做自适应迭代检索(多跳/查询改写),让 JIT 从「单次命中」升级为「迭代逼近」。
  • AML 落点:SAR 事实核查走 JIT——judge 不预载全案 trace,而是按 caseId 用工具取该案 trace 切片(类比 Claude Code 的 head/tail)。src/aml/observability.ts 的 trace 导出应支持「按 caseId 切片取」而非「全量导出再喂」,避免一份核查被无关案例的 trace 淹没。
  • 诚实标注:上述 kind:'decision' pinned 类、orchestratorPrompt 的 context discipline 段为 W6 设计动作;AML trace 切片接口为 P3 上线前的运营增强,本周仅落原则与接口签名,不谎称已实现完整 JIT 调度器。

参考资料

  1. Anthropic — Effective context engineering for AI agents:context rot 定义、attention budget/n² 机制、JIT 检索(轻量引用+运行时取)、Claude Code 的 head/tail 范例、混合策略、四技术(JIT/compaction/note-taking/sub-agent)(2025-09)
  2. Chroma — Context Rot: How Increasing Input Tokens Impacts LLM Performance:18 模型普适退化、单干扰项即掉、needle-问题相似度低退化更快、>50% 满偏近端、Claude 幻觉率最低 (2025)
  3. the-decoder — Anthropic claims context engineering beats prompt engineering:context engineering 取代 prompt engineering 的转向解读 (2025-09)
  4. Morph / Redis — Context Rot 工程解读:effective context ≪ advertised window、实践缓解清单 (2025-2026)
  5. 本仓库 src/agent/orchestrator/orchestratorAgent.ts(子 agent 仅回 text=压缩摘要)、src/agent/memory/contextBuilder.ts(summary=compaction)、src/agent/rag/hybridSearch.ts(pool over-retrieve)、src/agent/memory/pinnedFactsStore.ts(2026-06)

SOTA 检查 (2026-06-11)

  • JIT 检索 + context rot 是 2026-06 的活口径,非过时:Anthropic 2025-09 文是当前 context engineering 的事实基准,Cognition 同期「context engineering 是 agent 工程师的 #1 工作」被反复引用;本日 WebSearch 未见推翻「effective context ≪ advertised window」的结论。
  • 百万 token 窗口没有让 context rot 过时:Gemini 2.5 / Llama 4 等长窗口模型仍受 Chroma 验证的退化曲线约束——窗口大≠有效上下文大,2026 仍是 live 的踩坑点(atlan《LLM Context Window Limitations in 2026》口径一致)。
  • 新进展(待跟踪):2026 年初出现 LOCA-bench(可控极端上下文增长基准)、AgentSwing(自适应并行上下文管理路由)、《Everything is Context: Agentic File System Abstraction》(arXiv 2512)——把「文件系统当上下文」推得更远,本质仍是 JIT。若 P3 要做长程 agent,应评估这些基准量化本项目的 effective context 边界。
  • 过时认知警示:「窗口越大塞越多越好」「RAG 已被长上下文取代」均为 2024 旧论,2025-2026 实证(Chroma)已证伪——RAG/JIT 因 context rot 反而更不可替代。
  • 下一步:Day 41 把单次 JIT 升级为自适应迭代检索(查询改写/多跳/防重复),接进 hybridSearch.ts