🎬 王家卫式开场
世界上有一种痛苦,叫做「上下文窗口溢出」。它就像你跟女朋友吵架,话说到一半,她突然说:「你刚才说什么?」——因为她的记忆只有最近三句话。
凌晨3点17分,我的Agent正在处理一份200页的PDF合同。第47页的时候,它突然忘了前面说了什么。那一刻,我意识到——上下文窗口不是无限的,就像人类的耐心一样。
世界上有一种痛苦,叫做「上下文窗口溢出」。它就像你跟女朋友吵架,话说到一半,她突然说:「你刚才说什么?」——因为她的记忆只有最近三句话。
凌晨3点17分,我的Agent正在处理一份200页的PDF合同。第47页的时候,它突然忘了前面说了什么。那一刻,我意识到——上下文窗口不是无限的,就像人类的耐心一样。
Context Window Overflow(上下文窗口溢出)是指当输入给大语言模型(LLM)的Token数量超过其上下文窗口(Context Window)限制时,最早的内容会被截断或遗忘,导致模型无法访问完整的历史信息。
简单来说:
Transformer架构的注意力机制是二次复杂度O(n²),窗口越大,计算成本越高。因此每个模型都有设计上限:
| 模型 | 上下文窗口 | 实际可用 |
|---|---|---|
| GPT-3.5-turbo | 4K / 16K | 约12K中文 |
| GPT-4-turbo | 128K | 约96K中文 |
| Claude 3 Opus | 200K | 约150K中文 |
| 腾讯 Coding Plan | 根据配置 | 动态调整 |
Agent系统比普通对话更容易触发溢出,因为:
OpenClaw 提供了多种机制来应对上下文窗口溢出:
# OpenClaw 配置示例:设置上下文预算
{
"model": "tencentcodingplan/tc-code-latest",
"contextBudget": {
"maxTokens": 100000,
"reserveForResponse": 4096,
"strategy": "sliding-window"
}
}
保留最近的N条消息,自动丢弃老的:
// OpenClaw Skills 中配置上下文管理
{
"agent": "code-reviewer",
"contextPolicy": {
"type": "sliding-window",
"keepRecent": 20, // 保留最近20条消息
"summarizeOld": true // 对老消息生成摘要
}
}
当工具返回大量数据时,自动压缩:
# 工具定义中启用结果压缩 ## Tool: search-docs - name: search-docs description: 搜索文档 compress: true # 自动压缩超过1000 tokens的结果 maxResultTokens: 500
长时间运行的Agent任务,可以分片处理:
// OpenClaw Sub-Agent 分片示例
sessions_spawn({
task: "分析第1-50页合同",
cleanup: "keep" // 保留结果,但释放上下文
});
sessions_spawn({
task: "分析第51-100页合同",
cleanup: "keep"
});
// 最后汇总
sessions_send({
message: "汇总所有分片结果"
});
// 简单的Token估算函数(中文约1.5-2字符=1Token)
function estimateTokens(text) {
const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
const otherChars = text.length - chineseChars;
return Math.ceil(chineseChars / 1.5 + otherChars / 4);
}
// 检查是否接近溢出
const context = getConversationHistory();
const currentTokens = estimateTokens(context);
const LIMIT = 100000;
if (currentTokens > LIMIT * 0.9) {
console.warn(`⚠️ 上下文已使用 ${Math.round(currentTokens/LIMIT*100)}%,即将溢出!`);
// 触发压缩或分片
compressContext();
}
# Python示例:使用LLM压缩历史消息
def compress_history(messages, max_tokens=50000):
"""将超出限制的历史消息压缩为摘要"""
total_tokens = sum(estimate_tokens(m) for m in messages)
if total_tokens <= max_tokens:
return messages
# 分离最近消息和老消息
recent = messages[-10:]
old = messages[:-10]
# 让LLM生成摘要
summary = llm.call(f"请总结以下对话的核心信息:\n{format_messages(old)}")
# 用摘要替换老消息
compressed = [{"role": "system", "content": f"[历史摘要]:{summary}"}] + recent
return compressed
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 滑动窗口 | 简单高效,自动管理 | 丢失早期细节 | 普通对话Agent |
| 摘要压缩 | 保留关键信息 | 压缩过程消耗Token | 长文档分析 |
| 会话分片 | 彻底解决溢出问题 | 需要汇总逻辑 | 长时间任务、Multi-Agent |
| 向量检索(RAG) | 只检索相关内容 | 可能漏掉关键信息 | 知识库问答 |
context window overflow,说白了就是「内存不够,脑子短路」。
就像你跟朋友吹牛逼,吹到一半突然忘了开头说了啥,只能尴尬地来一句:「总之就是很厉害!」
解决办法?要么少说点(压缩),要么拿个小本本记着(外部存储),要么换个脑子更大的朋友(换模型)。
——最后,我的Agent终于学会了「好记性不如烂笔头」这个道理。