【从0到1构建一个ClaudeAgent】内存管理-上下文压缩

📅 2026/6/30 4:36:44
【从0到1构建一个ClaudeAgent】内存管理-上下文压缩
Java实现代码javapublic class ContextCompactSystem { // --- 配置 --- private static final Path WORKDIR Paths.get(System.getProperty(user.dir)); private static final Path TRANSCRIPT_DIR WORKDIR.resolve(.transcripts); // 新增对话存档目录 private static final Gson gson new GsonBuilder().setPrettyPrinting().create(); // 压缩参数 private static final int THRESHOLD_TOKENS 50000; // 触发自动压缩的 token 阈值 private static final int KEEP_RECENT 3; // 保留的最近工具结果数量 // --- 工具枚举 --- public enum ToolType { BASH(bash, Run a shell command.), READ_FILE(read_file, Read file contents.), WRITE_FILE(write_file, Write content to file.), EDIT_FILE(edit_file, Replace exact text in file.), COMPACT(compact, Trigger manual conversation compression.); // 新增手动压缩工具 public final String name; public final String description; ToolType(String name, String description) { this.name name; this.description description; } } // ... 省略相同的 ToolExecutor 接口和基础工具实现 // --- 三层次压缩系统 --- /** * Layer 1: 微观压缩 - 静默替换旧的工具结果 */ private static ListMapString, Object microCompact(ListMapString, Object messages) { // 收集所有的 tool_result 条目 ListToolResultInfo toolResults new ArrayList(); for (int msgIdx 0; msgIdx messages.size(); msgIdx) { MapString, Object msg messages.get(msgIdx); if (user.equals(msg.get(role))) { Object content msg.get(content); if (content instanceof List) { SuppressWarnings(unchecked) ListMapString, Object contentList (ListMapString, Object) content; for (int partIdx 0; partIdx contentList.size(); partIdx) { MapString, Object part contentList.get(partIdx); if (tool_result.equals(part.get(type))) { toolResults.add(new ToolResultInfo(msgIdx, partIdx, part)); } } } } } if (toolResults.size() KEEP_RECENT) { return messages; } // 从先前的 assistant 消息中映射 tool_use_id 到 tool_name MapString, String toolNameMap new HashMap(); for (MapString, Object msg : messages) { if (assistant.equals(msg.get(role))) { Object content msg.get(content); if (content instanceof List) { SuppressWarnings(unchecked) ListMapString, Object contentList (ListMapString, Object) content; for (MapString, Object block : contentList) { if (tool_use.equals(block.get(type))) { String toolId (String) block.get(id); String toolName (String) block.get(name); toolNameMap.put(toolId, toolName); } } } } } // 清除旧的结果保留最近的 KEEP_RECENT 个 ListToolResultInfo toClear toolResults.subList(0, toolResults.size() - KEEP_RECENT); for (ToolResultInfo info : toClear) { MapString, Object result info.result; Object content result.get(content); if (content instanceof String ((String) content).length() 100) { String toolId (String) result.get(tool_use_id); String toolName toolNameMap.getOrDefault(toolId, unknown); result.put(content, [Previous: used toolName ]); // 静默替换 } } return messages; } /** * Layer 2: 自动压缩 - 保存完整对话并生成摘要 */ private static ListMapString, Object autoCompact(ListMapString, Object messages) throws IOException { // 保存完整对话到磁盘 Files.createDirectories(TRANSCRIPT_DIR); Path transcriptPath TRANSCRIPT_DIR.resolve(transcript_ System.currentTimeMillis() .jsonl); try (BufferedWriter writer Files.newBufferedWriter(transcriptPath)) { for (MapString, Object msg : messages) { writer.write(gson.toJson(msg)); writer.newLine(); } } System.out.println([transcript saved: transcriptPath ]); // 调用 LLM 生成摘要 String conversationText gson.toJson(messages); if (conversationText.length() 80000) { conversationText conversationText.substring(0, 80000); } String summary simulateLLMSummary(conversationText); // 用摘要替换整个对话历史 ListMapString, Object compressedMessages new ArrayList(); compressedMessages.add(Map.of( role, user, content, [Conversation compressed. Transcript: transcriptPath ]\n\n summary )); compressedMessages.add(Map.of( role, assistant, content, Understood. I have the context from the summary. Continuing. )); return compressedMessages; } /** * Layer 3: 手动压缩工具 * 当 Agent 主动调用 compact 工具时触发 */ private static String handleCompactTool(MapString, Object args) { String focus (String) args.get(focus); String focusMsg focus ! null ? Focus: focus : ; return Manual compression requested. focusMsg; } /** * 估算 token 数量 * 简单实现约 4 个字符对应 1 个 token */ private static int estimateTokens(ListMapString, Object messages) { String messagesStr gson.toJson(messages); return messagesStr.length() / 4; } // --- 工具处理器映射 --- private static final MapString, ToolExecutor TOOL_HANDLERS new HashMap(); static { // ... 省略基础工具注册 TOOL_HANDLERS.put(ToolType.COMPACT.name, ContextCompactSystem::handleCompactTool); } // --- Agent 主循环集成了三层压缩--- public static void agentLoop(ListMapString, Object messages) { while (true) { try { // Layer 1: 每次调用前进行微观压缩 messages microCompact(messages); // Layer 2: 如果 token 数超过阈值自动压缩 if (estimateTokens(messages) THRESHOLD_TOKENS) { System.out.println([auto_compact triggered]); messages autoCompact(messages); } // ... 省略相同的 LLM 调用逻辑 boolean manualCompact false; for (MapString, Object block : content) { if (tool_use.equals(block.get(type))) { String toolName (String) block.get(name); // 检查是否是 compact 工具 if (ToolType.COMPACT.name.equals(toolName)) { manualCompact true; // 标记手动压缩 } // ... 执行工具 } } // Layer 3: 如果调用了 compact 工具执行手动压缩 if (manualCompact) { System.out.println([manual compact]); messages autoCompact(messages); } } catch (Exception e) { System.err.println(Error in agent loop: e.getMessage()); e.printStackTrace(); return; } } } // --- 辅助类和方法 --- private static class ToolResultInfo { int msgIndex; int partIndex; MapString, Object result; ToolResultInfo(int msgIndex, int partIndex, MapString, Object result) { this.msgIndex msgIndex; this.partIndex partIndex; this.result result; } }