当前位置: 首页 > news >正文

【learn-claude-code】S06ContextCompact - 上下文压缩:上下文会满,你需要腾出空间

核心理念

“上下文会满,你需要腾出空间” – 三层压缩策略,实现无限会话。

  • 源码:https://github.com/xiayongchao/learn-claude-code-4j/blob/main/src/main/java/org/jc/agents/S06ContextCompact.java
  • 原版:https://github.com/shareAI-lab/learn-claude-code
  • 上篇:S05SkillLoading - 技能加载

上篇回顾

上篇文章我们实现了 SkillLoader,按需加载专业知识,避免上下文膨胀。

问题

Agent 工作越久,messages 数组越大。每次工具调用的结果都永久留在上下文里,直到超出模型的上下文窗口上限。

Claude Code 的解决方案:三层压缩策略,让无限会话成为可能。

解决方案:三层压缩

LLM 调用前 | v +---+ Layer 1: microCompact (每次调用前) | | 替换旧的 tool_result 为占位符 +---+ | token 超过阈值? v +---+ Layer 2: autoCompact (自动) | | 保存会话记录 + 总结 + 重建上下文 +---+ | 模型调用 compact 工具? v +---+ Layer 3: manual compact (手动) | | 用户或模型主动触发压缩 +---+

Java 实现详解

1. 压缩触发条件

privatestaticfinalStringSYSTEM="你是运行在 "+Commons.CWD+" 工作目录下的编程智能体,请使用工具完成各项任务";privatestaticfinalintTHRESHOLD=3000;// token 阈值

2. Token 估算:混合文本计算

publicstaticintestimateTokens(Stringtext){if(text==null||text.isBlank()){return0;}doubletokenCount=0;for(inti=0;i<text.length();i++){charc=text.charAt(i);// 中文:每个字符 1 tokenif(Character.UnicodeScript.of(c)==Character.UnicodeScript.HAN){tokenCount++;}else{// 英文/数字/符号:每 4 个字符 ≈ 1 tokentokenCount+=0.25;}}return(int)Math.ceil(tokenCount);}

3. Layer 1:microCompact - 微观压缩

每次 LLM 调用前执行,将旧的 tool_result 替换为占位符:

privatestaticfinalintKEEP_RECENT=3;// 保留最近 3 条publicstaticvoidmicroCompact(List<ChatCompletionMessageParam>messages){// 1. 收集所有 tool 消息List<ChatCompletionMessageParam>toolMessages=newArrayList<>();for(ChatCompletionMessageParammessage:messages){if(message.isTool()){toolMessages.add(message);}}if(toolMessages.size()<=KEEP_RECENT){return;// 不足 3 条,无需压缩}// 2. 建立 tool_call_id -> tool_name 映射Map<String,String>toolNameMap=newHashMap<>();for(ChatCompletionMessageParammessage:messages){if(!message.isAssistant())continue;// ... 遍历 tool_calls 建立映射}// 3. 替换旧的 tool_result 为占位符List<ChatCompletionMessageParam>toClearMessages=Commons.getFirstN(toolMessages,KEEP_RECENT);for(ChatCompletionMessageParammessage:toClearMessages){// 保留最近 3 条,其余替换为:// "[上一步: 已使用 readFile]"Stringplaceholder=String.format("[上一步: 已使用 %s]",toolName);// ... 构建新消息}}

效果:"file content of pom.xml... (5000 chars)""[上一步: 已使用 readFile]"

4. Layer 2:autoCompact - 自动压缩

当 token 超过阈值时执行:

publicstaticvoidautoCompact(List<ChatCompletionMessageParam>messages){try{// 1. 保存完整会话记录到磁盘PathtranscriptDirPath=Paths.get(Commons.TRANSCRIPT_DIR);Files.createDirectories(transcriptDirPath);Stringtimestamp=String.valueOf(System.currentTimeMillis()/1000);PathtranscriptPath=transcriptDirPath.resolve("transcript_"+timestamp+".jsonl");List<String>lines=newArrayList<>();for(ChatCompletionMessageParammsg:messages){lines.add(Messages.toStandardJson(msg));}Files.write(transcriptPath,lines);System.out.println("[会话记录已保存: "+transcriptPath+"]");// 2. 让 LLM 总结对话Stringprompt="对本次对话进行连贯总结,需包含:"+"1)已完成事项;2)当前所处状态;3)达成的关键决策。"+"请保持总结简洁,保留关键细节。\n"+conversationText;ChatCompletionCreateParamsparams=ChatCompletionCreateParams.builder().model("qwen3.5-plus").messages(List.of(ChatCompletionMessageParam.ofUser(ChatCompletionUserMessageParam.builder().content(prompt).build()))).maxCompletionTokens(2000).build();ChatCompletionchatCompletion=Commons.getClient().chat().completions().create(params);Stringsummary=Commons.getAssistantText(chatCompletion.choices().get(0).message().toParam());// 3. 重建上下文:只保留 2 条消息messages.clear();messages.add(ChatCompletionMessageParam.ofUser(ChatCompletionUserMessageParam.builder().content("[对话已压缩。会话记录:"+transcriptPath+"]\n\n"+summary).build()));messages.add(ChatCompletionMessageParam.ofAssistant(ChatCompletionAssistantMessageParam.builder().content("我已获取总结中的上下文,继续执行").build()));}catch(Exceptione){// 异常处理}}

5. agentLoop 集成三层压缩

publicstaticvoidagentLoop(List<ChatCompletionMessageParam>messages){while(true){// Layer 1: 每次调用前微观压缩Compacts.microCompact(messages);// Layer 2: token 超过阈值则自动压缩if(Tokens.countDialogTokens(messages)>THRESHOLD){System.out.println("[已触发自动压缩]");Compacts.autoCompact(messages);}// 正常 LLM 调用...ChatCompletionchatCompletion=Commons.getClient().chat().completions().create(params);messages.add(ChatCompletionMessageParam.ofAssistant(message.toParam()));// ... 工具执行 ...// Layer 3: 手动压缩(通过 compact 工具触发)if(manualCompact){System.out.println("[手动压缩]");Compacts.autoCompact(messages);}}}

6. compact 工具

TOOL_HANDLERS.put("compact",args->"已请求手动压缩");// 模型调用 compact 工具时,设置标记for(ChatCompletionMessageToolCalltoolCall:toolCalls){if(Tools.isCompactTool(toolCall)){manualCompact=true;continue;// 不执行,只标记}// ... 其他工具}

三层压缩对比

层级触发条件作用粒度
Layer 1: microCompact每次调用前替换旧结果为占位符消息级
Layer 2: autoCompacttoken > 3000保存记录 + 总结会话级
Layer 3: manual模型调用 compact 工具同 autoCompact手动触发

压缩效果示意

压缩前 (假设 500 条消息): User: 读取所有 Java 文件... Assistant: 我来读取... Tool: readFile pom.xml 返回 2000 字符... Tool: readFile App.java 返回 3000 字符... Tool: readFile Service.java 返回 2500 字符... ... (更多工具调用) 压缩后 (2 条消息): User: [对话已压缩。会话记录: transcript_123456.jsonl] 总结:已完成读取 pom.xml(App 配置)、App.java(主类)、Service.java(业务逻辑)... Assistant: 我已获取总结中的上下文,继续执行

相对 s05 的变更

组件s05s06
压缩三层压缩策略
Token 估算Tokens.estimateTokens()
会话持久化transcript_*.jsonl
compact 工具手动触发压缩

试试看

  1. 逐一读取 agents/ 目录下的每个 java 文件
  2. 持续读取文件,直到压缩功能自动触发
  3. 使用 compact 工具手动压缩对话

核心要义

“Context will fill up; you need a way to make room”
上下文有限,但会话可以无限

设计原则:

  • 微观压缩:保留关键信息,去除冗余细节
  • 自动压缩:阈值触发,无感知清理
  • 手动压缩:用户/模型主动控制
  • 会话持久化:压缩前保存,保留完整历史
http://www.jsqmd.com/news/585890/

相关文章:

  • 实测好用!translategemma-4b-it图文翻译模型快速上手体验
  • 手把手教你搞定RK3568上的Serdes摄像头:THCV244+IMX307驱动与DTS配置避坑指南
  • 02 主流AI编程工具选型:Copilot / 文心快码 / CodeLlama 实操对比
  • 3个核心问题:为什么你的旧Mac无法升级?OpenCore Legacy Patcher如何让老设备重获新生?
  • 5种解决方案:Windows 11安装硬件限制完全绕过指南
  • 零侵入、极简适配!飞桨CINN实现类CUDA硬件“即插即用”
  • 2026/4/3 【春假提高思维测试】H. 增减序列
  • 【实践指南】三维Copula建模:从数据导入到联合分布计算的完整流程解析
  • 告别限速困扰:BaiduPCS-Web 与 KinhDown 的终极百度网盘加速方案
  • FunASR语音识别镜像亲测:支持中英日韩粤语,一键生成字幕和文本
  • 数据方舟:开源工具如何守护你的数字资产安全
  • 你的数字记忆会消失吗?这款开源工具让微信聊天永久留存
  • OpenModScan:工业自动化工程师必备的高效Modbus测试技术方案
  • Qwen3-ForcedAligner-0.6B详细步骤:音频预处理建议(降噪/重采样/静音裁剪)
  • SecGPT-14B私有化部署:在OpenClaw中接入内网安全模型
  • 仅此一场,武汉首发!AICA10期数智创新公开课,邀你共探智造新路径
  • 5步掌握多平台资源捕获:res-downloader全场景应用指南
  • 别再让MOS管开关慢了!手把手教你选对加速电容(附仿真波形对比)
  • BsMax:让Blender工作效率翻倍的终极插件指南
  • AI赋能学术门户,用快马智能生成《构石》官网并集成审稿推荐与摘要生成
  • 你的STM32设备时间总飘?试试用NTP服务器做高精度时钟同步(含STM32CubeMX配置)
  • Qwen3-VL-8B-Instruct-GGUF效果展示:同一张餐厅菜单图,模型准确识别菜品+价格+辣度标签
  • ai辅助开发新体验:智能诊断centos7安装问题,实时生成解决方案代码
  • Perl:命令行参数的处理
  • OpenClaw镜像体验:30分钟玩转Qwen2.5-VL-7B多模态自动化
  • 开源模拟器性能优化指南:解决yuzu运行问题的系统方法
  • 魔兽争霸3兼容性终极解决方案:WarcraftHelper让你的经典游戏焕发新生
  • 【无人机路径规划】 K-means和遗传算法的无人机路径规划【含Matlab源码 15282期】
  • 3分钟找回遗忘QQ号:手机号查询工具让数字身份不再丢失
  • HsMod:突破炉石传说体验限制的终极模改解决方案