“为什么我的上下文越长,答案反而越离谱?”
如果你也在深夜对着 128k 窗口的 GPT-4 怀疑人生,这篇文章就是写给正在抠头的你。
故事从一次“奖励黑客”调研开始
上周,老板甩给我一个看似人畜无害的需求:
“整理一下 RLHF 里所有奖励黑客(reward hacking)的玩法,明天分享。”
我反手把 200 页论文塞进 Claude,结果——
-
前半段总结得头头是道; -
后半段开始胡编“比特币挖矿能缓解奖励黑客”; -
Token 账单 25k,信用卡先哭了。
这就是 Drew Breunig 说的 Context Rot:
当上下文像衣柜一样塞爆,模型找不到那件“正确答案”T 恤,只能随手抓一条“幻觉”内裤套头上。
Context 为什么会“腐烂”?4 种典型死法
死法 | 现场目击 |
---|---|
Context Poisoning | 早期检索到一篇错误博客,后面每轮回答都引用它,越错越自信。 |
Context Distraction | 历史对话里 80% 是寒暄,模型把“你好”当成核心线索。 |
Context Confusion | 同时塞入 GitHub 代码、维基百科、小红书段子,模型被迫“端水”。 |
Context Clash | 两篇文章对“RLHF”定义打架,模型直接精神分裂。 |
6 个招式,手把手把上下文瘦成一道闪电
下面所有代码都跑在 how_to_fix_your_context 这个官方仓库里,每一步都能复现。
我用的是 Python 3.11 + uv,整个环境 30 秒就能搭好:
# 1. 一把梭克隆
git clone https://github.com/langchain-ai/how_to_fix_your_context
cd how_to_fix_your_context
# 2. uv 瞬建虚拟环境
uv venv && source .venv/bin/activate # Windows 用 .venv\Scripts\activate
# 3. 依赖一次性装完
uv pip install -r requirements.txt
# 4. 把钥匙塞兜里
export OPENAI_API_KEY="sk-xxx"
export ANTHROPIC_API_KEY="sk-ant-xxx"
招式 ① RAG:别全文背诵,只带“小抄”进场
核心思想:先查再答,让模型只读“与问题强相关”的段落。
LangGraph 打法:
-
把 Lilian Weng 的 14 篇 RL 博文切成 512 token 的小块; -
用 OpenAI text-embedding-3-small
做向量; -
Claude-3.5 Sonnet 当“阅卷老师”,先让用户澄清范围,再决定检索几轮。
结果:同样“奖励黑客”问题,Token 从 25k 降到 15k,幻觉率目测减半。
图:RAG 节点图——澄清 → 检索 → 生成,三轮对话内解决。
招式 ② Tool Loadout:工具箱只带“今天用得上的扳手”
痛点:一次把 200 个数学函数全塞 system prompt,模型直接“选择困难”。
解法:语义剪枝。把每个函数说明也 embedding 了,用户问“阶乘”只给乘、阶乘、伽马 3-5 个最相关工具。
代码 3 行搞定动态绑定:
relevant_tools = vector_store.similarity_search(query, k=5)
graph.update_state({"tools": [tool_registry[t.id] for t in relevant_tools]})
好处:
-
工具描述不打架,调用准确率↑; -
每次 prompt 省 500-800 token,长线对话更明显。
招式 ③ Context Quarantine:让“数学宅”与“搜索狂”分居
场景:既要算积分,又要搜最新 ArXiv。
做法:Supervisor 节点当“项目经理”,把任务丢给只装计算器的 Math Agent 或只装浏览器的 Research Agent,两 agent 彼此不见面。
LangGraph 的 Supervisor
架构代码像写 DAG 一样爽:
supervisor = Supervisor(agents=[math_agent, research_agent])
supervisor.add_edge("math_agent", END) # 算完就收工
supervisor.add_edge("research_agent", END)
收益:
-
单 agent 上下文<4k,永远专注; -
再也不会把“∫x dx”算成“x 专利号 2025SB250”。
招式 ④ Context Pruning:让“小模型”当“编辑”,先砍废稿
流程:
-
用 GPT-4o-mini 当“实习编辑”,把召回的 20k 文档先过一遍; -
只保留和“奖励黑客”相关的句子,生成 3k “精华补丁”; -
再送主模型深加工。
实测:同样回答质量,25k → 11k,成本直接腰斩。
提示词核心就一句人话:
“你是严谨编辑,请删除与‘奖励黑客’无关的段落,保留技术细节,输出 Markdown。”
招式 ⑤ Context Summarization:如果全是干货,就“脱水”而非丢弃
与 Pruning 的区别:
-
Pruning 是“扔菜叶”; -
Summarization 是“把三碗浓汤熬成一碗”。
做法:把召回的 5 篇长文先用 GPT-4o-mini 压缩成 50-70% 长度,保留所有公式、引用,再去生成。
适合“每段都有用”的场景,比如法律、医疗。
Token 再省 30-40%,且关键数字零丢失。
招式 ⑥ Context Offloading:把“记忆”搬到硬盘,别全塞在脑子里
两种“外挂”:
-
Session Scratchpad:当前线程的“草稿纸”,随用随擦。 def write_note(note: str) -> str: state.scratchpad += f"- {note}\n" return "已记录"
-
Persistent Memory:跨线程的“笔记本”,存在 InMemoryStore
里,下次重启还能读。
做 7×24 研究 agent 时,把“今日计划”“结论”写进去,第二天接着写,而不是把昨天 20k 记录再读一遍。
效果:
-
上下文长度恒定,跟对话轮数无关; -
支持“多用户+多话题”隔离,命名空间一把钥匙开一把锁。
如何把这 6 招串成一套“组合拳”?
Step-by-Step 指南(How-To 风格,可直接抄):
-
先用 RAG 召回 → 拿到 20k 候选文档。 -
判断相关度: -
若 <30% 可用 → Pruning 直接扔掉杂音; -
若大部分可用但啰嗦 → Summarization 脱水。
-
-
看任务类型: -
需要算东西 → Quarantine 把计算代理单拎; -
需要查实时信息 → Tool Loadout 只给搜索工具。
-
-
全程用 Offloading 把中间结论写“草稿纸”,末尾再一次性读盘,生成最终报告。
一句话顺序:
RAG → (Pruning|Summarization) → Quarantine → Tool Loadout → Offloading → Final Answer。
常见问题解答(FAQ)
Q1. 我只有 OpenAI key,能跑完所有 notebook 吗?
A:除了 Claude 节点需 Anthropic,其余全部可 OpenAI 平替。把 ChatAnthropic
换成 ChatOpenAI(model="gpt-4o")
即可。
Q2. 向量库必须用 Pinecone 这种云服务吗?
A:仓库默认 Chroma
内存版,零配置,10 万段文本秒内建成。上生产再考虑 Pinecone / Weaviate。
Q3. Pruning 和 Summarization 会不会丢失关键信息?
A:作者用“奖励黑客”问答对做了 100 次人工评测,事实召回率 96% vs baseline 94%,可放心食用。建议上线前做自己的 golden set 回归。
Q4. 我想在 Node 里加自定义逻辑,但不会写 LangGraph?
A:记住三句话——“节点纯函数,状态是可变字典,边写条件函数”。官方可视化 print(graph.get_graph().draw_mermaid())
一键生成流程图,对着图 Debug 比 print 爽。
写在最后:上下文不是越大越好,而是“刚刚好”才最好
Karpathy 说,“Context Engineering 是新的 Prompt Engineering。”
当 128k、1M token 窗口成为营销口号,真正的工程师却在练减法:
-
减掉噪音,让信号更尖锐; -
减掉幻觉,让事实更锋利; -
减掉账单,让老板更开心。
把这 6 招装进工具箱,下次再遇到“越长越离谱”的上下文,你会像外科医生一样下刀,而不是像仓鼠一样囤纸。
现在,轮到你把仓库克隆下来,第一次运行就把 25k token 砍到 11k,然后回来告诉我:
“原来大模型不是失忆,是我没给它戴降噪耳机。”