拆.
上下文与记忆——AI 怎么'记得' · 第 01

Context Window

AI 的'工作记忆'——attention 偏好驱动 prompt 设计

4500读完约 23 分钟craft:B+发布于 2026-06-21

一、原理:context 在 LLM 内部怎么工作

我们已经多次提到 context window——0.3 章讲过 token 是稀缺资源、2.3 章讲过 token 经济学、2.4 章讲过 compaction。这一章我们走深一层——讲 context window 在 LLM 内部是什么、怎么工作、有什么"不为人知的"特性。

理解 context 的内部机制对设计 AI 产品非常重要。很多产品决策的"反直觉处"——为什么大 context 不一定好、为什么 prompt 顺序影响输出、为什么有些 prompt 工程技巧那么有效——都源于 context 的内部物理特性。

我们从一个简单事实开始——LLM 是无状态的

每次 LLM API 调用都是独立的。你发送一段 messages,LLM 返回一段响应。LLM 自己不记得上次你发了什么、不知道现在是几点几分、不知道用户是谁。

LLM 的"记忆"完全来自你这次请求里塞的 context。你每次请求都要把"它需要记得的所有东西"都塞进 context 里——system prompt、对话历史、工具列表、当前环境。LLM 看到这些信息后生成响应。

这就是 context window 的本质——这次 LLM 调用能"看到"的所有信息

但 context 不是简单的"信息列表"。它在 LLM 内部经过 attention 机制处理——LLM 把 context 里的所有 token 互相比较、计算"相关性权重"、用这些权重生成下一个 token。

attention 机制有几个反直觉特性

特性 1:位置偏好

LLM 对 context 不同位置的注意力不均等。具体说:

  • 开头位置:LLM 注意力最强。system prompt 放开头有原因——LLM "记得最牢"
  • 结尾位置:注意力次强。用户最新的输入放结尾——LLM 优先响应它
  • 中间位置:注意力最弱。长 context 里中间段经常被"忽略"

这就是著名的 lost in the middle 现象——一份 50K tokens 的文档,里面写在第 25K 位置的关键信息可能被 LLM 漏掉。

这意味着 prompt 设计的位置策略:重要的放开头或结尾、次要的放中间、不要把关键约束埋在中间段。

特性 2:注意力是 O(n²)

attention 计算的复杂度是 context 长度的平方。1K tokens 的 context 比 10K tokens 快 100 倍。

这导致 LLM 处理长 context 时变慢——首 token 延迟增加、生成速度下降。一个 100K tokens 的 prompt 可能要 10-30 秒才出首 token。

这影响产品体验——你不能让用户等 30 秒看到响应。所以"塞越多 context 越好"在实际产品里不成立——必须有节制。

特性 3:注意力是稀疏的

attention 在长 context 里不是"均匀地看所有 token"——它会"挑重点"。哪些是重点?LLM 自己判断——基于训练数据里的模式。

具体说:

  • 数字、变量名、文件路径这些"硬标识符" → 注意力强
  • 重复出现的概念 → 注意力强(多次出现说明重要)
  • 命令式句子(MUST、NEVER、ALWAYS) → 注意力强
  • 解释性文字(because、since、while) → 注意力弱
  • 客套话、铺垫 → 注意力极弱

这影响 prompt 设计的密度策略:把重要内容用 LLM 高注意力的形式表达(命令式、具体词、多次重复)。

特性 4:context 长度跟"理解准确率"非线性关系

直觉是"context 越长,LLM 越能利用所有信息做更好的回答"。实际上不是

实测中,对一些类型的任务(特别是"在长文档中找特定信息"),LLM 的准确率随 context 长度变化是 U 形——短 context 准确率 95%、中等 context 跌到 70%、超长 context 又回升到 85%。

为什么?因为 lost in the middle——中等长度的 context 已经触发了 attention 散开,但还没到 LLM 学过的"超长 context 优化"机制。

这意味着——你的产品 context 不一定要做到最大。某些场景"短 context"反而准确率更高。

把这 4 个特性合起来,context window 不是"线性资源"——它是有复杂物理特性的注意力空间。理解这个空间,你才能正确设计 prompt。

二、案例:opencode 怎么管理 context

opencode 在 context 管理上有几个具体设计反映对上面 4 个特性的理解。

设计 1:核心约束放在 system prompt 开头

5 张脸 prompt 的第一段都是身份和核心定位

  • anthropic.txt 第一行:"You are OpenCode, the best coding agent on the planet."
  • gpt.txt 第一行:"You are OpenCode, You and the user share the same workspace..."

最重要的"你是谁"放最前面——利用 attention 的开头偏好。

第二段通常是重要的禁令 —— "Don't generate URLs unless..."、"Be honest about uncertainty"。重要禁令也靠前——避免 lost in the middle。

细节性、操作性的 usage notes 放最后。

这是有意的"位置策略"——按重要性排序,从前到后。

设计 2:tool 描述放在每次请求里,不放 system prompt

opencode 的 tool description 不是塞进 system prompt 的字符串——而是通过 LLM API 的 tools 字段独立传入。

为什么?因为 tools 字段在 API 协议层是特殊处理的——LLM 内部对 tools 字段的 attention 跟普通 text 不一样。

具体说,LLM 在 SFT 阶段学过——tools 字段里的内容是"我可以调用的能力清单"——它会给这部分 attention 加权。

把 tool 描述放 tools 字段比塞进 system prompt 效率高得多——同样 tokens 的"被关注度"高得多。

设计 3:用户消息放最后

opencode 的请求结构是:

{
  system: [...]  // system prompt
  messages: [
    { role: "user", content: "用户第一句" },
    { role: "assistant", content: "AI 第一次响应" },
    { role: "user", content: "用户第二句" },
    { role: "assistant", content: "AI 第二次响应" },
    ...
    { role: "user", content: "用户最新输入" }  // 最关键
  ],
  tools: [...]
}

用户最新输入是最后一个 message——利用 attention 的结尾偏好。LLM 最关心"用户刚说了什么"——位置上也强调。

如果反过来——把最新输入放在 messages 数组开头、历史在后——LLM 会困惑,输出质量明显下降。

设计 4:避免长 prompt

opencode 的 5 张脸长度都控制在 1000-2000 字。不是"想长就长"——是有意识控制密度,避免 lost in the middle。

如果某张脸非常长——比如 5000 字——中间段的指令会被忽略。每张脸都精炼到只保留必要内容。

设计 5:环境信息每次重组

opencode 每次请求都重新生成环境信息(cwd、git status、time)。这一段每次都变,但总是放在 system prompt 末尾——固定位置。

LLM 学会"我可以在 system prompt 末尾找环境信息"——形成稳定的查找模式。如果环境信息位置乱跳,LLM 找起来累。

固定位置 + 重新生成——保证 LLM 时刻看到最新环境,同时有稳定的查找路径。

设计 6:tool 输出有截断

read 工具默认读 2000 行、bash 输出可能截断、grep 限制结果数。这些"截断"让 tool 输出不会单独吃光 context

如果 LLM 调用 read 读一个 50000 行的文件——50000 行的内容会塞进下一次请求的 messages 历史,吃光 context。

opencode 默认截断到合理范围——LLM 仍然能"看到大部分",但 context 不会爆。

三、设计启示:context 不是越大越好

这一章的核心论点:context window 是"有物理特性的资源"——不是越大越好,要按物理特性优化

如果你做 AI 产品,下面几条原则有用:

1. 优先位置策略

重要内容放开头或结尾。次要放中间。别把关键约束埋在中间——会被忽略。

system prompt 里:身份 → 核心约束 → 详细行为指引 → 环境信息(这个顺序)。

2. 控制 prompt 长度

不是越长越好。每段 prompt 最长 2000 字——超过会触发 attention 散开。

如果你觉得 system prompt 必须长——拆成几段、按主题组织、每段不超过 1500 字。

3. 利用 attention 偏好

重要内容用 LLM 高 attention 的形式:

  • 命令式(MUST、NEVER、IMPORTANT)
  • 具体硬标识符(确切文件名、变量名)
  • 重复(同一约束在 prompt 不同位置出现多次)

不要用 LLM 低 attention 的形式:

  • 解释性长句("因为...所以...")
  • 客套铺垫("please be helpful")
  • 抽象概念("behave professionally")

4. tool 描述放 tools 字段

不要把 tool 描述硬塞进 system prompt——用 LLM API 的 tools 字段。LLM 对 tools 字段有专门 attention 处理,效率高得多。

5. 用户输入放最后

messages 数组里,用户最新输入永远是最后一个 message。利用 attention 的结尾偏好。

不要"把任务指令插在历史中间"——LLM 会困惑哪个是"当前要做的"。

6. context 长度跟成功率的关系不线性

跑实验测——你的产品场景下,context 多长准确率最高?可能不是最大。可能 50K 比 100K 更好。

不要盲目追"塞越多越好"——按数据决策。

7. tool 输出要截断

让 tool 默认有合理截断——避免单次 tool 调用吃光 context。read 2000 行、bash 200K 字符、grep 100 行——这种数字按你的场景调。

8. 长 context 任务用"分块 + 总结"模式

如果用户给你 1MB 文档让 AI 处理——不要一次性塞进 context。分块处理、每块生成结论、最后总结。这种"map-reduce"模式比"塞全部"准确率高。

最后一个观察。Context window 这件事长期被 marketing 误导。LLM 厂商都在比拼"context window 谁更大"——Claude 200K vs Gemini 1M vs Llama 2M。

生产环境里大 context 用得少。绝大多数 AI 产品的实际 prompt 在 10K-50K tokens 之间。超过这个范围,准确率下降、延迟上升、成本飙升。

opencode 的 PRUNE_MINIMUM = 20K 和 PRUNE_PROTECT = 40K 是这种"实际工作区"的反映——大多数有效工作在这个范围内,超出就要 compaction。

理解了 context 的物理特性,你才能跨过"context window 大小竞赛"的迷雾——做出真正适合产品的 prompt 设计

下一章 4.2 我们看 opencode 怎么把这些"位置策略"和"密度策略"落到 system prompt 的 6 步拼装链。