# IC Coder 会话存储技术文档 ## 1. 概述 IC Coder 的会话存储系统负责持久化保存用户与 AI 的对话历史,支持多项目、多任务的会话管理。系统采用文件系统存储方案,将会话数据按项目和任务组织,便于管理和检索。 ### 1.1 核心特性 - **多项目支持**:不同项目的会话数据独立存储 - **任务级管理**:每个会话作为独立任务进行管理 - **分页加载**:支持历史会话的分页查询,提升性能 - **实时更新**:会话数据实时保存,防止数据丢失 - **统计信息**:记录 Token 使用量、对话轮次等统计数据 ### 1.2 技术栈 - **存储方式**:文件系统(JSON/JSONL 格式) - **存储位置**:`~/.iccoder/projects/{项目路径编码}/{taskId}/` - **数据格式**: - `meta.json`:任务元数据 - `conversation.json`:完整对话历史 - `conversation_meta.jsonl`:对话轮次元数据(JSONL 格式) --- ## 2. 架构设计 ### 2.1 目录结构 ``` ~/.iccoder/ └── projects/ └── {项目路径编码}/ └── {taskId}/ ├── meta.json # 任务元数据 ├── conversation.json # 对话历史 └── conversation_meta.jsonl # 对话元数据 ``` **项目路径编码规则**: - 移除冒号 `:` - 将斜杠 `/` 和反斜杠 `\` 替换为 `--` - 示例:`C:\Users\admin\Documents\Project` → `C--Users--admin--Documents--Project` **任务 ID 格式**: - 格式:`task_{date}_{sequence}` - 示例:`task_20231226_a3f9k2` - `date`:8 位日期(YYYYMMDD) - `sequence`:6 位随机字符串 ### 2.2 核心类:ChatHistoryManager `ChatHistoryManager` 是会话存储的核心管理类,采用单例模式设计。 **主要职责**: 1. 管理会话存储目录 2. 创建和切换任务 3. 保存和加载对话历史 4. 记录统计信息 5. 提供会话历史查询接口 **关键属性**: ```typescript private static instance: ChatHistoryManager; private baseDir: string; // ~/.iccoder private currentTaskId: string | null; // 当前任务 ID private currentProjectPath: string | null; // 当前项目路径 ``` --- ## 3. 数据模型 ### 3.1 TaskMeta(任务元数据) 存储在 `meta.json` 文件中,记录任务的基本信息和统计数据。 ```typescript interface TaskMeta { taskId: string; // 任务 ID taskName: string; // 任务名称 projectPath: string; // 项目路径 createdAt: string; // 创建时间(ISO 8601) updatedAt: string; // 更新时间(ISO 8601) stats: { credits: number; // 消耗的积分 totalTokens: number; // 总 Token 数 inputTokens: number; // 输入 Token 数 outputTokens: number; // 输出 Token 数 }; } ``` **示例**: ```json { "taskId": "task_20231226_a3f9k2", "taskName": "实现计数器功能", "projectPath": "C:\\Users\\admin\\Documents\\Project", "createdAt": "2023-12-26T10:30:00.000Z", "updatedAt": "2023-12-26T11:45:00.000Z", "stats": { "credits": 0, "totalTokens": 15420, "inputTokens": 8200, "outputTokens": 7220 } } ``` ### 3.2 ChatMessage(对话消息) 存储在 `conversation.json` 文件中,记录完整的对话历史。 **消息类型枚举**: ```typescript enum MessageType { USER = "USER", // 用户消息 AI = "AI", // AI 消息 SYSTEM = "SYSTEM", // 系统消息 TOOL_EXECUTION_RESULT = "TOOL_EXECUTION_RESULT" // 工具执行结果 } ``` **用户消息**: ```typescript interface UserMessage { type: MessageType.USER; contents: Array<{ type: "TEXT"; text: string; }>; } ``` **AI 消息**: ```typescript interface AiMessage { type: MessageType.AI; text: string; toolExecutionRequests?: Array<{ id: string; toolName: string; parameters: any; }>; } ``` **系统消息**: ```typescript interface SystemMessage { type: MessageType.SYSTEM; text: string; } ``` **工具执行结果消息**: ```typescript interface ToolExecutionResultMessage { type: MessageType.TOOL_EXECUTION_RESULT; id: string; toolName: string; text: string; } ``` ### 3.3 ConversationMeta(对话轮次元数据) 存储在 `conversation_meta.jsonl` 文件中,每行一条记录(JSONL 格式)。 ```typescript interface ConversationMeta { turnId: number; // 对话轮次 ID timestamp: string; // 时间戳(ISO 8601) usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number; }; model?: string; // 使用的模型 duration?: number; // 耗时(毫秒) } ``` **示例**: ```jsonl {"turnId":1,"timestamp":"2023-12-26T10:30:15.000Z","usage":{"inputTokens":120,"outputTokens":350,"totalTokens":470},"model":"gpt-4","duration":2500} {"turnId":2,"timestamp":"2023-12-26T10:32:30.000Z","usage":{"inputTokens":200,"outputTokens":450,"totalTokens":650},"model":"gpt-4","duration":3200} ``` --- ## 4. 核心功能实现 ### 4.1 任务创建 **方法**:`createTask(projectPath: string, taskName: string): Promise` **流程**: 1. 生成唯一的任务 ID 2. 创建任务元数据对象 3. 创建任务目录 4. 保存 `meta.json` 5. 初始化空的 `conversation.json` 6. 设置为当前任务 **代码位置**:`chatHistoryManager.ts:114-146` ```typescript public async createTask(projectPath: string, taskName: string): Promise { const taskId = this.generateTaskId(); const now = new Date().toISOString(); const meta: TaskMeta = { taskId, taskName, projectPath, createdAt: now, updatedAt: now, stats: { credits: 0, totalTokens: 0, inputTokens: 0, outputTokens: 0 } }; this.currentTaskId = taskId; this.currentProjectPath = projectPath; // 创建任务目录 const taskDir = this.getTaskDir(projectPath, taskId); await this.ensureTaskDir(taskDir); // 保存 meta.json await this.saveTaskMeta(meta); // 初始化空的 conversation.json await this.saveConversation([]); return meta; } ``` ### 4.2 消息保存 系统提供了四种消息保存方法: #### 4.2.1 添加用户消息 **方法**:`addUserMessage(text: string): Promise` **代码位置**:`chatHistoryManager.ts:285-299` ```typescript public async addUserMessage(text: string): Promise { await this.ensureCurrentTask(); const messages = await this.loadConversation(); const userMessage: UserMessage = { type: MessageType.USER, contents: [{ type: "TEXT", text }] }; messages.push(userMessage); await this.saveConversation(messages); // 更新任务元数据 await this.updateTaskTimestamp(); } ``` #### 4.2.2 添加 AI 消息 **方法**:`addAiMessage(text: string, toolRequests?: any[]): Promise` **代码位置**:`chatHistoryManager.ts:304-319` #### 4.2.3 添加系统消息 **方法**:`addSystemMessage(text: string): Promise` **代码位置**:`chatHistoryManager.ts:324-335` #### 4.2.4 添加工具执行结果 **方法**:`addToolExecutionResult(id: string, toolName: string, result: string): Promise` **代码位置**:`chatHistoryManager.ts:340-353` ### 4.3 对话元数据记录 **方法**:`recordTurnMeta(turnId, usage?, model?, duration?): Promise` **功能**:记录每轮对话的元数据,包括 Token 使用量、模型信息、耗时等。 **代码位置**:`chatHistoryManager.ts:358-378` ```typescript public async recordTurnMeta( turnId: number, usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number }, model?: string, duration?: number ): Promise { const meta: ConversationMeta = { turnId, timestamp: new Date().toISOString(), usage, model, duration }; await this.appendConversationMeta(meta); // 更新任务统计 if (usage) { await this.updateTaskStats(usage); } } ``` ### 4.4 会话历史查询 **方法**:`getConversationHistoryList(projectPath, offset, limit): Promise<{items, total, hasMore}>` **功能**:分页查询项目的会话历史列表。 **参数**: - `projectPath`:项目路径 - `offset`:偏移量(从第几条开始,默认 0) - `limit`:每页数量(默认 10) **返回值**: ```typescript { items: Array<{ id: string; // 任务 ID title: string; // 会话标题(第一句用户消息) timestamp: string; // 创建时间 }>; total: number; // 总数 hasMore: boolean; // 是否还有更多 } ``` **代码位置**:`chatHistoryManager.ts:525-590` **实现逻辑**: 1. 获取项目的所有任务列表(按更新时间倒序) 2. 根据 offset 和 limit 进行分页 3. 读取每个任务的 `conversation.json` 4. 提取第一条用户消息作为标题(截取前 50 个字符) 5. 返回分页结果 --- ## 5. 前端集成 ### 5.1 会话历史栏组件 **文件**:`conversationHistoryBar.ts` **组件结构**: - 下拉按钮:显示 "Past Conversations" - 下拉菜单:显示会话历史列表 - 新建按钮:创建新会话 **关键功能**: #### 5.1.1 加载会话历史 ```javascript function loadMoreHistory() { if (isLoadingHistory || (currentOffset > 0 && !hasMoreHistory)) { return; } // 检查是否已达到最大数量(100 条) if (currentOffset >= MAX_HISTORY_ITEMS) { return; } isLoadingHistory = true; vscode.postMessage({ command: 'loadConversationHistory', offset: currentOffset, limit: HISTORY_PAGE_SIZE }); } ``` #### 5.1.2 渲染会话列表 ```javascript function renderConversationHistory(data) { isLoadingHistory = false; // 追加新数据 conversationHistory = conversationHistory.concat(data.items); totalHistory = data.total; hasMoreHistory = data.hasMore; currentOffset += data.items.length; // 渲染所有历史记录 historyList.innerHTML = conversationHistory.map(item => `
${item.title || '未命名会话'}
${formatTime(item.timestamp)}
`).join(''); // 如果还有更多数据,添加"加载更多"提示 if (hasMoreHistory && currentOffset < MAX_HISTORY_ITEMS) { historyList.innerHTML += `
滚动加载更多...
`; } } ``` #### 5.1.3 滚动加载 ```javascript historyDropdownMenu.addEventListener('scroll', () => { const menu = historyDropdownMenu; const scrollTop = menu.scrollTop; const scrollHeight = menu.scrollHeight; const clientHeight = menu.clientHeight; // 当滚动到距离底部 50px 时,加载更多 if (scrollHeight - scrollTop - clientHeight < 50) { loadMoreHistory(); } }); ``` #### 5.1.4 时间格式化 ```javascript function formatTime(timestamp) { const date = new Date(timestamp); const now = new Date(); const diff = now - date; if (diff < 60000) return '刚刚'; if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前'; if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前'; if (diff < 604800000) return Math.floor(diff / 86400000) + '天前'; // 超过7天显示具体日期 return date.toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }); } ``` ### 5.2 后端消息处理 **文件**:`ICHelperPanel.ts` **消息处理流程**: ```typescript case "loadConversationHistory": // 加载会话历史(支持分页) loadConversationHistory(panel, message.offset || 0, message.limit || 10); break; case "selectConversation": // 选择会话(暂未实现) break; case "createNewConversation": // 创建新会话 - 在当前编辑器组中打开新标签页 showICHelperPanel(context, panel.viewColumn); break; ``` **加载会话历史实现**: ```typescript async function loadConversationHistory( panel: vscode.WebviewPanel, offset: number = 0, limit: number = 10 ) { try { const historyManager = ChatHistoryManager.getInstance(); const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; if (!workspacePath) { // 没有打开的工作区,返回空历史 panel.webview.postMessage({ command: "conversationHistory", items: [], total: 0, hasMore: false, }); return; } // 获取会话历史列表(支持分页) const result = await historyManager.getConversationHistoryList( workspacePath, offset, limit ); // 发送会话历史到前端 panel.webview.postMessage({ command: "conversationHistory", items: result.items, total: result.total, hasMore: result.hasMore, }); } catch (error) { console.error("加载会话历史失败:", error); // 发生错误时返回空历史 panel.webview.postMessage({ command: "conversationHistory", items: [], total: 0, hasMore: false, }); } } ``` --- ## 6. 使用示例 ### 6.1 创建新任务并保存对话 ```typescript const historyManager = ChatHistoryManager.getInstance(); // 创建新任务 const task = await historyManager.createTask( 'C:\\Users\\admin\\Documents\\Project', '实现计数器功能' ); // 添加用户消息 await historyManager.addUserMessage('请帮我生成一个4位计数器'); // 添加 AI 消息 await historyManager.addAiMessage( '好的,我来帮你生成一个4位计数器...', [{ id: '1', toolName: 'generateCode', parameters: {} }] ); // 添加工具执行结果 await historyManager.addToolExecutionResult( '1', 'generateCode', '代码生成成功' ); // 记录对话元数据 await historyManager.recordTurnMeta( 1, { inputTokens: 120, outputTokens: 350, totalTokens: 470 }, 'gpt-4', 2500 ); ``` ### 6.2 查询会话历史 ```typescript const historyManager = ChatHistoryManager.getInstance(); // 获取第一页(前10条) const page1 = await historyManager.getConversationHistoryList( 'C:\\Users\\admin\\Documents\\Project', 0, 10 ); console.log('总数:', page1.total); console.log('是否还有更多:', page1.hasMore); console.log('会话列表:', page1.items); // 获取第二页(第11-20条) const page2 = await historyManager.getConversationHistoryList( 'C:\\Users\\admin\\Documents\\Project', 10, 10 ); ``` ### 6.3 切换任务 ```typescript const historyManager = ChatHistoryManager.getInstance(); // 切换到指定任务 const success = await historyManager.switchTask( 'C:\\Users\\admin\\Documents\\Project', 'task_20231226_a3f9k2' ); if (success) { // 获取当前任务会话 const session = await historyManager.getCurrentTaskSession(); console.log('任务元数据:', session.meta); console.log('对话历史:', session.messages); console.log('对话元数据:', session.conversationMeta); } ``` --- ## 7. 性能优化 ### 7.1 分页加载 - 前端默认每页加载 10 条记录 - 最多显示 100 条历史记录 - 滚动到底部时自动加载下一页 ### 7.2 懒加载 - 只在打开下拉菜单时才加载会话历史 - 避免不必要的文件读取操作 ### 7.3 缓存机制 - 前端缓存已加载的会话列表 - 避免重复请求相同数据 ### 7.4 文件格式优化 - 使用 JSONL 格式存储对话元数据,支持追加写入 - 避免频繁读写整个文件 --- ## 8. 错误处理 ### 8.1 目录不存在 系统会自动创建不存在的目录: ```typescript private async ensureTaskDir(taskDir: string): Promise { try { const uri = vscode.Uri.file(taskDir); try { await vscode.workspace.fs.stat(uri); } catch { // 目录不存在,创建它 await vscode.workspace.fs.createDirectory(uri); console.log(`创建任务目录: ${taskDir}`); } } catch (error) { console.error("创建任务目录失败:", error); throw error; } } ``` ### 8.2 文件读取失败 读取失败时返回默认值: ```typescript private async loadConversation(): Promise { try { const uri = vscode.Uri.file(conversationPath); const content = await vscode.workspace.fs.readFile(uri); const data = Buffer.from(content).toString('utf-8'); return JSON.parse(data); } catch (error) { // 文件不存在或读取失败,返回空数组 return []; } } ``` ### 8.3 无工作区处理 没有打开工作区时,自动创建默认任务: ```typescript private async ensureCurrentTask(): Promise { if (!this.currentTaskId || !this.currentProjectPath) { const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; if (workspacePath) { await this.createTask(workspacePath, "默认任务"); } else { throw new Error("没有打开的工作区,无法创建任务"); } } } ``` --- ## 9. 未来扩展 ### 9.1 会话切换功能 目前 `selectConversation` 功能暂未实现,未来可以支持: - 点击历史会话,加载该会话的完整对话历史 - 在新标签页中打开历史会话 - 继续历史会话的对话 ### 9.2 会话搜索 - 支持按关键词搜索会话 - 支持按时间范围筛选 - 支持按 Token 使用量排序 ### 9.3 会话导出 - 导出为 Markdown 格式 - 导出为 JSON 格式 - 导出为 PDF 格式 ### 9.4 会话统计 - 显示总对话轮次 - 显示总 Token 使用量 - 显示平均响应时间 ### 9.5 云端同步 - 支持将会话数据同步到云端 - 支持多设备访问 - 支持团队协作 --- ## 10. 总结 IC Coder 的会话存储系统采用文件系统存储方案,具有以下优势: 1. **简单可靠**:无需额外的数据库依赖 2. **易于备份**:直接复制文件即可备份 3. **跨平台**:支持 Windows、macOS、Linux 4. **可扩展**:易于添加新的数据字段 5. **高性能**:分页加载,避免一次性加载大量数据 系统已经实现了核心的会话管理功能,包括任务创建、消息保存、历史查询等,为用户提供了完整的会话历史管理体验。