Context window 就是模型的"工作集(working set)"——类似进程的虚拟内存:你以为它能装无限东西,实际上有硬上限,并且越靠近上限性能越糟。后端世界对应:缓冲池(buffer pool)、SQL 的 work_mem、CPU 的 L1/L2 cache——尺寸都是 trade-off,不是"越大越好"。
Transformer 的 attention 是 O(n²) 计算复杂度——每个 token 都要和窗口里所有 token 算相关性。窗口 1M token 意味着 1M × 1M = 一万亿次相似度计算,显存按 KV cache 线性增长(每个 token 的 Key/Value 矩阵都要存)。所以窗口尺寸是硬件成本问题,不是"想多大就多大"。
2026 年主流模型窗口:Claude 4.7 / GPT-5 ~200K-1M、Gemini 2.5 ~2M、开源模型多在 32K-128K。但核心反直觉是 Liu et al. 2023 的发现——"Lost in the Middle":把关键信息放在 100K 上下文的开头或结尾,准确率 ~80%;放在中间,掉到 ~50%。注意力分布是 U 形的:
即使是 2025 年宣传"100% needle-in-haystack 召回"的新模型,在多 needle + 推理任务上依然有显著的位置偏差。把 context window 当成"无限注意力"是常见的认知误区。
from anthropic import Anthropic client = Anthropic() # 需要 ANTHROPIC_API_KEY # 永远先用 SDK 量化 token 数,再决定要不要塞进去 long_doc = open("report.txt").read() resp = client.messages.create( model="claude-opus-4-7", max_tokens=1024, messages=[{ "role": "user", # 关键问题放最后——利用 U 型注意力的"近因端" "content": f"<document>{long_doc}</document>\n\n基于以上,回答:核心结论是什么?" }] ) print(resp.usage.input_tokens, "input tokens") # 监控用量
把 prompt 当成 HTTP 请求来看:不变的 system prompt + 工具 schema + few-shot 例子 就是"静态资源",每次请求都重新跑 attention 等价于不开 CDN。Context caching = 把这段 prefix 的 KV cache 持久化到 GPU 显存或快速 SSD,下次命中时直接复用——和 Redis 缓存、HTTP 304、Postgres prepared statement 是同一类东西。
典型 Agent 应用的 prompt 80% 是不变的(角色定义 + 工具描述 + 历史对话),只有最后用户问题在变。但 LLM 默认每次都从头跑 prefill——对一个 50K token 的固定 prefix 来说,这就是 50K 次 attention 白算。Caching 的核心机制:
from anthropic import Anthropic client = Anthropic() # cache_control 标记"这一段及之前的所有内容"作为可缓存前缀 resp = client.messages.create( model="claude-opus-4-7", max_tokens=512, system=[ {"type": "text", "text": "你是资深财报分析师..."}, {"type": "text", "text": long_company_filing, # 50K token 财报,反复查询同一份 "cache_control": {"type": "ephemeral"}} # ← 标记缓存断点 ], messages=[{"role": "user", "content": "营收增长来自哪个业务线?"}] ) print(resp.usage.cache_creation_input_tokens) # 第一次:写入 print(resp.usage.cache_read_input_tokens) # 后续:命中(便宜 90%)
长会话的上下文管理 = 数据库的日志滚动(log rotation) + compaction。MySQL binlog 不会无限增长——超过阈值就 rotate + 压缩 + 归档。LLM 上下文同理:对话越长 token 越多,压缩是工程必需,不是可选项。
当对话超过 50K-100K token,三个问题一起爆:窗口溢出(hard limit)、注意力稀释(lost in the middle)、每轮成本线性涨(所有历史每次都重算)。四类主流压缩策略,按"信息保真度 vs 成本"递增:
Anthropic 2025 推出的 Memory 工具和自动 compaction把这套机制内置——上下文接近上限时自动总结老消息,不需要应用层手写。但什么该被压缩、压缩到什么粒度仍然是产品决策,不是默认值能解决的。
def compress_history(messages, max_tokens=8000): # 简单分层:最近 6 轮保全文,更早的总结成一段 recent = messages[-6:] older = messages[:-6] if not older or count_tokens(messages) < max_tokens: return messages older_text = "\n".join(f"{m['role']}: {m['content']}" for m in older) summary = client.messages.create( model="claude-haiku-4-5-20251001", # 总结用便宜模型 max_tokens=400, messages=[{"role": "user", "content": f"用 5 个 bullet 总结这段对话的关键事实、决策、未解问题:\n{older_text}"}] ).content[0].text # 拼回:摘要 + 最近原文(这样最近上下文细节不丢) return [{"role": "system", "content": f"<earlier_conversation_summary>\n{summary}\n</earlier_conversation_summary>"}] + recent
LLM 本身是无状态的——每次 API 调用都是一次冷启动。要让它"记得你",必须像 OS 设计内存层级一样,给它配一套分层存储:CPU register(当前 context)→ L1/L2 cache(缓存的 prefix)→ RAM(最近会话)→ disk(向量库 + 笔记)。"Memory 管理"就是设计这套层级。
把"过去三个月每次对话都塞 context"既不可能(窗口爆)也无意义(lost in middle)。认知科学借来的四类记忆,对应不同存储策略:
主流实现:MemGPT / Letta 用类操作系统的"分页"机制让 LLM 自己决定什么换入换出;mem0 / LangMem 把 episodic + semantic 抽象成 SDK;OpenAI ChatGPT 的"记忆"、Claude 的 Memory 工具是产品化版本。共同模式:提取(extract)→ 存储(store)→ 检索(retrieve)→ 注入(inject)四步。
关键工程决策:什么值得记?——不是"对话里说过的都记",而是用一个抽取 LLM 判断这条信息是否会影响未来回答。Mem0 论文 (2024) 显示,主动抽取 + 去重的 memory 比"全文存档 + 检索"准确率高 26%、延迟低 91%。
from mem0 import Memory # pip install mem0ai m = Memory() # 1) 写入:mem0 自己用 LLM 抽取"值得记的事实",去重后存向量库 m.add("我对花生过敏,孩子喜欢蓝色", user_id="bigcat") m.add("上周和我讨论过 Mamba 架构,结论是 SSM 适合长序列", user_id="bigcat") # 2) 检索:根据当前 query 拉相关 memory(不是塞全部历史) hits = m.search("给孩子做生日蛋糕的建议", user_id="bigcat", limit=3) # 3) 注入 prompt——只附加相关的几条 ctx = "\n".join(h["memory"] for h in hits["results"]) resp = client.messages.create( model="claude-opus-4-7", max_tokens=512, system=f"<known_about_user>\n{ctx}\n</known_about_user>", messages=[{"role": "user", "content": "周六给孩子做生日蛋糕,推荐配方"}]) # → 模型会自动避开花生,知道孩子可能喜欢蓝色装饰