主要改进: 1. 实现selectConversation功能,支持点击任务历史列表加载会话 2. 优化会话存储格式,保存完整的segments信息(包括工具调用) 3. 添加旧格式到新格式的自动转换,兼容历史数据 4. 改进错误处理,自动清理无效的空任务目录 5. 优化路径编码逻辑,确保跨平台一致性 6. 前端支持clearChat、addUserMessage、addAiMessage命令 技术细节: - 扩展AiMessage数据结构,添加segments字段 - 修改messageHandler保存逻辑,将完整segments保存到一条消息 - 实现loadTaskSession方法,加载指定任务的完整会话 - 添加自动清理机制,删除无效的空任务目录
18 KiB
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 是会话存储的核心管理类,采用单例模式设计。
主要职责:
- 管理会话存储目录
- 创建和切换任务
- 保存和加载对话历史
- 记录统计信息
- 提供会话历史查询接口
关键属性:
private static instance: ChatHistoryManager;
private baseDir: string; // ~/.iccoder
private currentTaskId: string | null; // 当前任务 ID
private currentProjectPath: string | null; // 当前项目路径
3. 数据模型
3.1 TaskMeta(任务元数据)
存储在 meta.json 文件中,记录任务的基本信息和统计数据。
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 数
};
}
示例:
{
"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 文件中,记录完整的对话历史。
消息类型枚举:
enum MessageType {
USER = "USER", // 用户消息
AI = "AI", // AI 消息
SYSTEM = "SYSTEM", // 系统消息
TOOL_EXECUTION_RESULT = "TOOL_EXECUTION_RESULT" // 工具执行结果
}
用户消息:
interface UserMessage {
type: MessageType.USER;
contents: Array<{
type: "TEXT";
text: string;
}>;
}
AI 消息:
interface AiMessage {
type: MessageType.AI;
text: string;
toolExecutionRequests?: Array<{
id: string;
toolName: string;
parameters: any;
}>;
}
系统消息:
interface SystemMessage {
type: MessageType.SYSTEM;
text: string;
}
工具执行结果消息:
interface ToolExecutionResultMessage {
type: MessageType.TOOL_EXECUTION_RESULT;
id: string;
toolName: string;
text: string;
}
3.3 ConversationMeta(对话轮次元数据)
存储在 conversation_meta.jsonl 文件中,每行一条记录(JSONL 格式)。
interface ConversationMeta {
turnId: number; // 对话轮次 ID
timestamp: string; // 时间戳(ISO 8601)
usage?: {
inputTokens?: number;
outputTokens?: number;
totalTokens?: number;
};
model?: string; // 使用的模型
duration?: number; // 耗时(毫秒)
}
示例:
{"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<TaskMeta>
流程:
- 生成唯一的任务 ID
- 创建任务元数据对象
- 创建任务目录
- 保存
meta.json - 初始化空的
conversation.json - 设置为当前任务
代码位置:chatHistoryManager.ts:114-146
public async createTask(projectPath: string, taskName: string): Promise<TaskMeta> {
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<void>
代码位置:chatHistoryManager.ts:285-299
public async addUserMessage(text: string): Promise<void> {
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<void>
代码位置:chatHistoryManager.ts:304-319
4.2.3 添加系统消息
方法:addSystemMessage(text: string): Promise<void>
代码位置:chatHistoryManager.ts:324-335
4.2.4 添加工具执行结果
方法:addToolExecutionResult(id: string, toolName: string, result: string): Promise<void>
代码位置:chatHistoryManager.ts:340-353
4.3 对话元数据记录
方法:recordTurnMeta(turnId, usage?, model?, duration?): Promise<void>
功能:记录每轮对话的元数据,包括 Token 使用量、模型信息、耗时等。
代码位置:chatHistoryManager.ts:358-378
public async recordTurnMeta(
turnId: number,
usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number },
model?: string,
duration?: number
): Promise<void> {
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)
返回值:
{
items: Array<{
id: string; // 任务 ID
title: string; // 会话标题(第一句用户消息)
timestamp: string; // 创建时间
}>;
total: number; // 总数
hasMore: boolean; // 是否还有更多
}
代码位置:chatHistoryManager.ts:525-590
实现逻辑:
- 获取项目的所有任务列表(按更新时间倒序)
- 根据 offset 和 limit 进行分页
- 读取每个任务的
conversation.json - 提取第一条用户消息作为标题(截取前 50 个字符)
- 返回分页结果
5. 前端集成
5.1 会话历史栏组件
文件:conversationHistoryBar.ts
组件结构:
- 下拉按钮:显示 "Past Conversations"
- 下拉菜单:显示会话历史列表
- 新建按钮:创建新会话
关键功能:
5.1.1 加载会话历史
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 渲染会话列表
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 => `
<div class="history-item" onclick="selectConversation('${item.id}')">
<div class="history-item-title">${item.title || '未命名会话'}</div>
<div class="history-item-time">${formatTime(item.timestamp)}</div>
</div>
`).join('');
// 如果还有更多数据,添加"加载更多"提示
if (hasMoreHistory && currentOffset < MAX_HISTORY_ITEMS) {
historyList.innerHTML += `
<div class="history-load-more">
<span>滚动加载更多...</span>
</div>
`;
}
}
5.1.3 滚动加载
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 时间格式化
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
消息处理流程:
case "loadConversationHistory":
// 加载会话历史(支持分页)
loadConversationHistory(panel, message.offset || 0, message.limit || 10);
break;
case "selectConversation":
// 选择会话(暂未实现)
break;
case "createNewConversation":
// 创建新会话 - 在当前编辑器组中打开新标签页
showICHelperPanel(context, panel.viewColumn);
break;
加载会话历史实现:
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 创建新任务并保存对话
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 查询会话历史
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 切换任务
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 目录不存在
系统会自动创建不存在的目录:
private async ensureTaskDir(taskDir: string): Promise<void> {
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 文件读取失败
读取失败时返回默认值:
private async loadConversation(): Promise<ChatMessage[]> {
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 无工作区处理
没有打开工作区时,自动创建默认任务:
private async ensureCurrentTask(): Promise<void> {
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 的会话存储系统采用文件系统存储方案,具有以下优势:
- 简单可靠:无需额外的数据库依赖
- 易于备份:直接复制文件即可备份
- 跨平台:支持 Windows、macOS、Linux
- 可扩展:易于添加新的数据字段
- 高性能:分页加载,避免一次性加载大量数据
系统已经实现了核心的会话管理功能,包括任务创建、消息保存、历史查询等,为用户提供了完整的会话历史管理体验。