上下文管理(Context Management)

概述
LLM 的上下文窗口就像一个有限容量的背包 🎒。每次对话、每个工具调用的结果都会往背包里放东西。随着对话进行,背包越来越满...
上下文管理就是一套帮你"管理背包"的机制,确保 AI 能够持续、高效地工作。
上下文管理机制设计受 OpenClaw 启发,由 QwenPaw 的 LightContextManager 独立实现。
工作原理 — 总结
QwenPaw 上下文管理分为两条并行的 Offload 路径,共同解决上下文窗口有限的问题:
每轮推理前,MemoryCompactionHook 按顺序执行:
未超限超限每轮推理前1 工具结果 Offload2 Token 超限检查正常推理3 压缩旧消息\n生成 compact_summary4 归档原始消息\n写入 dialog/
不丢失信息:被压缩的原始对话保存在
dialog/,工具输出保存在tool_result/,Agent 随时可通过read_file工具回溯保持连贯:
compact_summary保留结构化摘要 + 对话路径引导,确保 Agent 不失去上下文自动触发:无需手动干预,也可用
/compact主动触发
上下文结构
内存中的数据结构
QwenPaw 的上下文由两部分组成:
文件系统缓存
超出上下文的数据会 Offload 到文件系统,保持可追溯性:
消息区域划分
始终保留
超限压缩
系统提示
System Prompt可压缩区
Compactable Messages保留区
Recent Messages
结构示例
┌─────────────────────────────────────────┐ │ System Prompt (固定) │ ← 始终保留 │ "你是一个 AI 助手..." │ ├─────────────────────────────────────────┤ │ compact_summary (可选) │ ← 压缩后生成 │ - [对话路径引导] dialog/2025-01-15.jsonl│ │ - Goal: 构建用户登录系统 │ │ - Progress: 登录接口已完成... │ ├─────────────────────────────────────────┤ │ 可压缩区 │ ← 超限时会被压缩 │ [消息1] 用户: 帮我写个登录功能 │ │ [消息2] 助手: 好的,我来实现... │ │ [消息3] 工具调用结果... │ │ ... │ ├─────────────────────────────────────────┤ │ 保留区 │ ← 始终保留 │ [消息N-2] 用户: 再加个注册功能 │ │ [消息N-1] 助手: 好的... │ │ [消息N] 用户: 完成! │ └─────────────────────────────────────────┘
管理机制
架构概览
每轮推理前
超限
Agent
MemoryCompactionHook
compact_tool_result
压缩工具输出check_context
Token 计数compact_memory
生成摘要
相关代码
AsMsgHandler — 上下文检查与消息格式化
compactor_prompts — 压缩提示词
执行流程
否
是
messages
ToolCallResultCompact
Offload 超长工具输出ContextChecker
Token 计数Token > 阈值?
正常推理
保留最近 X% tokens
Compactor
压缩旧消息生成摘要SaveDialog
Offload 被压缩消息到
dialog/YYYY-MM-DD.jsonl更新 compact_summary + 清空旧消息
执行顺序:
ToolCallResultCompact— 超长工具输出 Offload 到tool_result/(如果启用)ContextChecker— 基于 Token 计数判断是否超限Compactor— 将旧消息压缩为结构化摘要(compact_memory)SaveDialog— 将被压缩的原始消息持久化到dialog/YYYY-MM-DD.jsonl
压缩机制
当上下文接近限制时,QwenPaw 会自动触发压缩,将旧对话浓缩为结构化摘要。
1. compact_tool_result — 工具结果压缩
当 tool_result_pruning_config.enabled 开启时(默认 true),对每条工具调用结果按新旧程度使用不同的字节阈值截断:
是
否
Tool Call Result
在 pruning_recent_n 内?
低截断比例
pruning_recent_msg_max_bytes
保存完整内容到 tool_result/uuid.txt
消息中保留片段 + 文件引用高截断比例
pruning_old_msg_max_bytes
指向已有文件路径
更激进截断Context
特殊工具说明:
Browser Use 类工具:首次调用保存原始内容到
tool_result/uuid.txt,消息中保留片段 + 文件引用,并提示从第 N 行读取;超出pruning_recent_n后进行二次截断read_file 工具:
pruning_recent_n内不截断也不保存(内容已是外部文件);超出后截断并保存到tool_result/超过
offload_retention_days天的文件自动清理
2. check_context — 上下文检查
基于 Token 计数判断上下文是否超限,自动拆分为「待压缩」和「保留」两组消息。
否
是
messages
Token 计数
total > threshold?
返回全部消息
从尾部向前保留
reserve tokensmessages_to_compact
早期消息messages_to_keep
近期消息is_valid
工具调用对齐?
核心逻辑:从尾部向前保留
memory_compact_reservetokens,超出部分标记为待压缩完整性保证:不拆分 user-assistant 对话对,不拆分 tool_use/tool_result 配对
3. compact_memory — 对话压缩
使用 ReActAgent 将历史对话压缩为结构化上下文摘要:
增量更新
messages
format_msgs_to_str
ReActAgent
reme_compactorprevious_summary
结构化摘要
4. 手动压缩(/compact 命令)
主动触发压缩:
/compact
你也可以为这次手动压缩附加一条说明:
/compact 只保留需求和关键决策
执行后返回:
**Compact Complete!** - Messages compacted: 12 **Compressed Summary:** <压缩摘要内容>
返回内容说明:
📊 Messages compacted - 压缩了多少条消息
📝 Compressed Summary - 生成的摘要内容
压缩摘要结构
compact_summary 由两部分组成:对话路径引导 + 结构化历史摘要。
对话路径引导
指向 dialog/YYYY-MM-DD.jsonl 中被压缩的原始对话数据(按时间顺序写入,建议从后往前读)。Agent 可通过 read_file 工具回顾历史细节,而无需将原始消息保留在上下文中。
结构化历史摘要
结构化历史摘要GoalConstraintsProgressKey DecisionsNext StepsCritical Context
增量更新:传入
previous_summary时,自动将新对话与旧摘要合并信息保留:压缩会保留确切的文件路径、函数名称和错误消息,确保上下文无缝衔接
配置
配置文件位于 ~/.qwenpaw/workspaces/{agent_id}/agent.json 中的 running 部分:
running 直接字段:
running.light_context_config 字段:
running.light_context_config.context_compact_config 字段:
running.light_context_config.tool_result_pruning_config 字段:
计算关系:
memory_compact_threshold=max_input_length × compact_threshold_ratio(触发压缩的阈值)memory_compact_reserve=max_input_length × reserve_threshold_ratio(保留的最近消息 tokens)
示例配置:
{
"agents": {
"running": {
"max_input_length": 128000,
"context_manager_backend": "light",
"light_context_config": {
"dialog_path": "dialog",
"context_compact_config": {
"enabled": true,
"compact_threshold_ratio": 0.8,
"reserve_threshold_ratio": 0.1
},
"tool_result_pruning_config": {
"enabled": true,
"pruning_recent_n": 2,
"pruning_old_msg_max_bytes": 3000,
"pruning_recent_msg_max_bytes": 50000
}
}
}
}}