ai-memory-claw 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,149 @@
1
+ /**
2
+ * 分类分析模块
3
+ *
4
+ * 分析对话内容,判断主分类和子分类
5
+ */
6
+
7
+ export class CategoryAnalyzer {
8
+ // 分类关键词映射
9
+ private categoryKeywords: Record<string, string[]> = {
10
+ '代码开发': [
11
+ '代码', '编程', '脚本', 'Python', 'JavaScript', '开发', '函数', 'bug',
12
+ 'code', 'program', 'script', 'python', 'javascript', 'coding', 'function', 'bug'
13
+ ],
14
+ '系统运维': [
15
+ '部署', '服务器', '运维', 'Docker', '配置', '安装', '备份',
16
+ 'deploy', 'server', 'docker', 'config', 'install', 'backup'
17
+ ],
18
+ '文档撰写': [
19
+ '文档', '报告', '文章', '写作', '撰写', '总结',
20
+ 'document', 'report', 'article', 'write', 'summary'
21
+ ],
22
+ '数据分析': [
23
+ '分析', '数据', '统计', '图表', '可视化', 'CSV', 'Excel',
24
+ 'analysis', 'data', 'statistics', 'chart', 'visualization', 'csv', 'excel'
25
+ ],
26
+ '创作设定': [
27
+ '角色', '故事', '世界观', '创作', '小说', '设定',
28
+ 'character', 'story', 'world', 'creative', 'novel', 'setting'
29
+ ],
30
+ '用户偏好': [
31
+ '喜欢', '偏好', '习惯', '讨厌', '想要', '口味',
32
+ 'prefer', 'like', 'hate', 'want', 'habit', 'taste'
33
+ ],
34
+ '日常事务': [
35
+ '日程', '邮件', '安排', '提醒', '天气', '时间',
36
+ 'schedule', 'email', 'remind', 'weather', 'time'
37
+ ]
38
+ };
39
+
40
+ /**
41
+ * 分析内容,返回分类
42
+ */
43
+ analyze(content: string): { category: string; subCategory: string } {
44
+ const lower = content.toLowerCase();
45
+ let bestCategory = '其他';
46
+ let maxScore = 0;
47
+
48
+ // 1. 查找最佳匹配分类
49
+ for (const [category, keywords] of Object.entries(this.categoryKeywords)) {
50
+ let score = 0;
51
+ for (const keyword of keywords) {
52
+ if (lower.includes(keyword.toLowerCase())) {
53
+ score += 1;
54
+ }
55
+ }
56
+ if (score > maxScore) {
57
+ maxScore = score;
58
+ bestCategory = category;
59
+ }
60
+ }
61
+
62
+ // 2. 确定子分类
63
+ const subCategory = this.getSubCategory(content, bestCategory);
64
+
65
+ return {
66
+ category: bestCategory,
67
+ subCategory
68
+ };
69
+ }
70
+
71
+ /**
72
+ * 获取子分类
73
+ */
74
+ private getSubCategory(content: string, category: string): string {
75
+ const lower = content.toLowerCase();
76
+
77
+ if (category === '代码开发') {
78
+ if (/python|脚本/.test(lower)) return '脚本';
79
+ if (/web|前端|html|css|react|vue/.test(lower)) return '前端';
80
+ if (/api|后端|server|node/.test(lower)) return '后端';
81
+ if (/bug|debug|错误/.test(lower)) return '调试';
82
+ if (/算法|数据结构/.test(lower)) return '算法';
83
+ return '通用';
84
+ }
85
+
86
+ if (category === '系统运维') {
87
+ if (/docker|容器/.test(lower)) return '容器';
88
+ if (/部署|发布/.test(lower)) return '部署';
89
+ if (/备份|恢复/.test(lower)) return '备份';
90
+ if (/服务器|server/.test(lower)) return '服务器';
91
+ if (/监控|日志/.test(lower)) return '监控';
92
+ return '通用';
93
+ }
94
+
95
+ if (category === '文档撰写') {
96
+ if (/需求|规格/.test(lower)) return '需求文档';
97
+ if (/技术|开发/.test(lower)) return '技术文档';
98
+ if (/用户|手册/.test(lower)) return '用户手册';
99
+ return '通用';
100
+ }
101
+
102
+ if (category === '数据分析') {
103
+ if (/可视化|图表/.test(lower)) return '可视化';
104
+ if (/统计|报表/.test(lower)) return '统计分析';
105
+ if (/机器学习|ai|人工智能/.test(lower)) return '机器学习';
106
+ return '通用';
107
+ }
108
+
109
+ if (category === '创作设定') {
110
+ if (/角色|人物/.test(lower)) return '角色设定';
111
+ if (/世界观|背景/.test(lower)) return '世界观';
112
+ if (/剧情|情节/.test(lower)) return '剧情';
113
+ return '通用';
114
+ }
115
+
116
+ if (category === '用户偏好') {
117
+ if (/饮食|口味/.test(lower)) return '饮食偏好';
118
+ if (/工作|学习/.test(lower)) return '工作习惯';
119
+ if (/工具|软件/.test(lower)) return '工具偏好';
120
+ return '通用';
121
+ }
122
+
123
+ return '通用';
124
+ }
125
+
126
+ /**
127
+ * 获取所有分类
128
+ */
129
+ getAllCategories(): string[] {
130
+ return Object.keys(this.categoryKeywords);
131
+ }
132
+
133
+ /**
134
+ * 获取分类的中文名称
135
+ */
136
+ getCategoryDisplayName(category: string): string {
137
+ const displayNames: Record<string, string> = {
138
+ '代码开发': '代码开发',
139
+ '系统运维': '系统运维',
140
+ '文档撰写': '文档撰写',
141
+ '数据分析': '数据分析',
142
+ '创作设定': '创作设定',
143
+ '用户偏好': '用户偏好',
144
+ '日常事务': '日常事务',
145
+ '其他': '其他'
146
+ };
147
+ return displayNames[category] || category;
148
+ }
149
+ }
package/src/config.ts ADDED
@@ -0,0 +1,74 @@
1
+ /**
2
+ * 配置管理模块
3
+ *
4
+ * 定义插件配置类型和默认配置
5
+ */
6
+
7
+ // 默认触发关键词
8
+ export const DEFAULT_TRIGGER_KEYWORDS = [
9
+ "记得", "之前", "上次", "以前", "查一下", "看看之前的记忆",
10
+ "用一下之前的", "参考之前的", "以前是怎么做的", "你还记得吗",
11
+ "记忆里", "历史上", "之前那次"
12
+ ];
13
+
14
+ export type MemoryConfig = {
15
+ dataDir: string;
16
+ autoRecall: boolean;
17
+ autoCapture: boolean;
18
+ captureStrategy: "always" | "selective";
19
+ autoRecallInNewSession: boolean;
20
+ newSessionMemoryLimit: number;
21
+ manualTriggerEnabled: boolean;
22
+ manualTriggerKeywords: string[];
23
+ manualRecallLimit: number;
24
+ recallThreshold: number;
25
+ recallLimit: number;
26
+ captureMaxChars: number;
27
+ enableSummary: boolean;
28
+ enableForget: boolean;
29
+ enableIntegration: boolean;
30
+ forgetIntervalDays: number;
31
+ integrationThreshold: number;
32
+ };
33
+
34
+ export const defaultMemoryConfig: MemoryConfig = {
35
+ dataDir: "~/.ai-memory-claw/data",
36
+ autoRecall: true,
37
+ autoCapture: true,
38
+ captureStrategy: "always",
39
+ autoRecallInNewSession: true,
40
+ newSessionMemoryLimit: 1,
41
+ manualTriggerEnabled: true,
42
+ manualTriggerKeywords: DEFAULT_TRIGGER_KEYWORDS,
43
+ manualRecallLimit: 3,
44
+ recallThreshold: 0.6,
45
+ recallLimit: 2,
46
+ captureMaxChars: 500,
47
+ enableSummary: true,
48
+ enableForget: true,
49
+ enableIntegration: true,
50
+ forgetIntervalDays: 7,
51
+ integrationThreshold: 0.8
52
+ };
53
+
54
+ export function mergeWithDefaults(pluginConfig: Partial<MemoryConfig>): MemoryConfig {
55
+ return {
56
+ dataDir: pluginConfig.dataDir || defaultMemoryConfig.dataDir,
57
+ autoRecall: pluginConfig.autoRecall ?? defaultMemoryConfig.autoRecall,
58
+ autoCapture: pluginConfig.autoCapture ?? defaultMemoryConfig.autoCapture,
59
+ captureStrategy: pluginConfig.captureStrategy || defaultMemoryConfig.captureStrategy,
60
+ autoRecallInNewSession: pluginConfig.autoRecallInNewSession ?? defaultMemoryConfig.autoRecallInNewSession,
61
+ newSessionMemoryLimit: pluginConfig.newSessionMemoryLimit ?? defaultMemoryConfig.newSessionMemoryLimit,
62
+ manualTriggerEnabled: pluginConfig.manualTriggerEnabled ?? defaultMemoryConfig.manualTriggerEnabled,
63
+ manualTriggerKeywords: pluginConfig.manualTriggerKeywords || defaultMemoryConfig.manualTriggerKeywords,
64
+ manualRecallLimit: pluginConfig.manualRecallLimit ?? defaultMemoryConfig.manualRecallLimit,
65
+ recallThreshold: pluginConfig.recallThreshold ?? defaultMemoryConfig.recallThreshold,
66
+ recallLimit: pluginConfig.recallLimit ?? defaultMemoryConfig.recallLimit,
67
+ captureMaxChars: pluginConfig.captureMaxChars ?? defaultMemoryConfig.captureMaxChars,
68
+ enableSummary: pluginConfig.enableSummary ?? defaultMemoryConfig.enableSummary,
69
+ enableForget: pluginConfig.enableForget ?? defaultMemoryConfig.enableForget,
70
+ enableIntegration: pluginConfig.enableIntegration ?? defaultMemoryConfig.enableIntegration,
71
+ forgetIntervalDays: pluginConfig.forgetIntervalDays ?? defaultMemoryConfig.forgetIntervalDays,
72
+ integrationThreshold: pluginConfig.integrationThreshold ?? defaultMemoryConfig.integrationThreshold
73
+ };
74
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * 向量嵌入模块
3
+ *
4
+ * 将文本转换为向量表示,用于相似度搜索
5
+ * 支持多种嵌入模型
6
+ */
7
+
8
+ export class EmbeddingGenerator {
9
+ private cache: Map<string, number[]> = new Map();
10
+ private vectorDim: number = 384;
11
+ private initialized: boolean = false;
12
+
13
+ constructor() {
14
+ // 使用简单的384维向量
15
+ }
16
+
17
+ async initialize(): Promise<void> {
18
+ this.initialized = true;
19
+ }
20
+
21
+ isInitialized(): boolean {
22
+ return this.initialized;
23
+ }
24
+
25
+ /**
26
+ * 生成文本向量
27
+ */
28
+ async embed(text: string): Promise<number[]> {
29
+ // 1. 检查缓存
30
+ const cacheKey = text.slice(0, 100);
31
+ if (this.cache.has(cacheKey)) {
32
+ return this.cache.get(cacheKey)!;
33
+ }
34
+
35
+ // 2. 生成向量 (关键词向量化)
36
+ const vector = this.keywordEmbedding(text);
37
+
38
+ // 3. 缓存结果
39
+ this.cache.set(cacheKey, vector);
40
+
41
+ return vector;
42
+ }
43
+
44
+ /**
45
+ * 关键词向量化 - 简单的基于关键词的向量化
46
+ */
47
+ private keywordEmbedding(text: string): number[] {
48
+ const vector = new Array(this.vectorDim).fill(0);
49
+
50
+ // 分词
51
+ const words = this.tokenize(text);
52
+ const wordFreq = new Map<string, number>();
53
+
54
+ for (const word of words) {
55
+ wordFreq.set(word, (wordFreq.get(word) || 0) + 1);
56
+ }
57
+
58
+ // 填充向量
59
+ let idx = 0;
60
+ for (const [word, freq] of wordFreq.entries()) {
61
+ for (let i = 0; i < word.length && idx < this.vectorDim; i++) {
62
+ // 使用字符编码 + 词频
63
+ vector[idx] += (word.charCodeAt(i) * freq) / words.length;
64
+ idx = (idx + 1) % this.vectorDim;
65
+ }
66
+ }
67
+
68
+ // 归一化
69
+ return this.normalize(vector);
70
+ }
71
+
72
+ /**
73
+ * 分词
74
+ */
75
+ private tokenize(text: string): string[] {
76
+ // 中英文分词
77
+ const tokens: string[] = [];
78
+ let current = '';
79
+
80
+ for (const char of text.toLowerCase()) {
81
+ if (/[\u4e00-\u9fa5]/.test(char)) {
82
+ // 中文字符
83
+ if (current.length > 0) {
84
+ tokens.push(...current.split(/\s+/).filter(t => t));
85
+ current = '';
86
+ }
87
+ tokens.push(char);
88
+ } else if (/[a-zA-Z0-9]/.test(char)) {
89
+ // 英文字母或数字
90
+ current += char;
91
+ } else {
92
+ // 标点符号
93
+ if (current.length > 0) {
94
+ tokens.push(...current.split(/\s+/).filter(t => t));
95
+ current = '';
96
+ }
97
+ }
98
+ }
99
+
100
+ if (current.length > 0) {
101
+ tokens.push(...current.split(/\s+/).filter(t => t));
102
+ }
103
+
104
+ return tokens;
105
+ }
106
+
107
+ /**
108
+ * 向量归一化
109
+ */
110
+ private normalize(vector: number[]): number[] {
111
+ const norm = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
112
+ if (norm === 0) return vector;
113
+ return vector.map(val => val / norm);
114
+ }
115
+
116
+ /**
117
+ * 余弦相似度计算
118
+ */
119
+ cosineSimilarity(a: number[], b: number[]): number {
120
+ if (a.length !== b.length) return 0;
121
+
122
+ let dotProduct = 0;
123
+ let normA = 0;
124
+ let normB = 0;
125
+
126
+ for (let i = 0; i < a.length; i++) {
127
+ dotProduct += a[i] * b[i];
128
+ normA += a[i] * a[i];
129
+ normB += b[i] * b[i];
130
+ }
131
+
132
+ const denominator = Math.sqrt(normA) * Math.sqrt(normB);
133
+ return denominator === 0 ? 0 : dotProduct / denominator;
134
+ }
135
+
136
+ /**
137
+ * 清除缓存
138
+ */
139
+ clearCache(): void {
140
+ this.cache.clear();
141
+ }
142
+
143
+ /**
144
+ * 获取缓存大小
145
+ */
146
+ getCacheSize(): number {
147
+ return this.cache.size;
148
+ }
149
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * 遗忘机制模块
3
+ *
4
+ * 自动删除低价值记忆,保持存储效率
5
+ */
6
+
7
+ import { MemorySystem } from './memory-system';
8
+ import type { Memory } from './types';
9
+
10
+ export interface ForgetResult {
11
+ id: string;
12
+ action: 'deleted' | 'compressed';
13
+ reason: string;
14
+ }
15
+
16
+ export class Forgrtter {
17
+ private memorySystem: MemorySystem;
18
+
19
+ constructor(memorySystem: MemorySystem) {
20
+ this.memorySystem = memorySystem;
21
+ }
22
+
23
+ /**
24
+ * 检查并执行遗忘
25
+ */
26
+ async checkAndForget(): Promise<ForgetResult[]> {
27
+ const results: ForgetResult[] = [];
28
+ const memories = this.memorySystem.getAllMemories();
29
+ const now = Date.now();
30
+
31
+ const DAY_MS = 24 * 60 * 60 * 1000;
32
+
33
+ for (const memory of memories) {
34
+ const lastAccess = new Date(memory.lastAccessedAt).getTime();
35
+ const daysSinceAccess = (now - lastAccess) / DAY_MS;
36
+ const importanceScore = this.getImportanceScore(memory.importance);
37
+
38
+ let shouldForget = false;
39
+ let reason = '';
40
+
41
+ // 遗忘条件
42
+ if (daysSinceAccess > 30 && importanceScore < 0.3) {
43
+ // 30天未访问 + 低重要性 = 直接删除
44
+ shouldForget = true;
45
+ reason = '30天未访问 + 低重要性';
46
+ } else if (daysSinceAccess > 60 && importanceScore < 0.5) {
47
+ // 60天未访问 + 中低重要性 = 直接删除
48
+ shouldForget = true;
49
+ reason = '60天未访问 + 中低重要性';
50
+ } else if (daysSinceAccess > 90) {
51
+ // 90天未访问 = 直接删除
52
+ shouldForget = true;
53
+ reason = '90天未访问';
54
+ }
55
+
56
+ if (shouldForget) {
57
+ await this.memorySystem.delete(memory.id);
58
+ results.push({
59
+ id: memory.id,
60
+ action: 'deleted',
61
+ reason
62
+ });
63
+ }
64
+ }
65
+
66
+ if (results.length > 0) {
67
+ console.log(`[Forgrtter] 已遗忘 ${results.length} 条记忆`);
68
+ }
69
+
70
+ return results;
71
+ }
72
+
73
+ /**
74
+ * 获取重要性分数
75
+ */
76
+ private getImportanceScore(importance: string): number {
77
+ switch (importance) {
78
+ case 'critical': return 1.0;
79
+ case 'high': return 0.7;
80
+ case 'medium': return 0.5;
81
+ case 'low': return 0.2;
82
+ default: return 0.5;
83
+ }
84
+ }
85
+
86
+ /**
87
+ * 获取遗忘候选列表
88
+ */
89
+ getForgetCandidates(): { memory: Memory; score: number; reason: string }[] {
90
+ const candidates: { memory: Memory; score: number; reason: string }[] = [];
91
+ const memories = this.memorySystem.getAllMemories();
92
+ const now = Date.now();
93
+
94
+ const DAY_MS = 24 * 60 * 60 * 1000;
95
+
96
+ for (const memory of memories) {
97
+ const lastAccess = new Date(memory.lastAccessedAt).getTime();
98
+ const daysSinceAccess = (now - lastAccess) / DAY_MS;
99
+ const importanceScore = this.getImportanceScore(memory.importance);
100
+
101
+ // 计算遗忘分数 (越高越应该遗忘)
102
+ let forgetScore = 0;
103
+ let reason = '';
104
+
105
+ if (daysSinceAccess > 30 && importanceScore < 0.3) {
106
+ forgetScore = 0.9;
107
+ reason = '30天未访问 + 低重要性';
108
+ } else if (daysSinceAccess > 60 && importanceScore < 0.5) {
109
+ forgetScore = 0.7;
110
+ reason = '60天未访问 + 中低重要性';
111
+ } else if (daysSinceAccess > 90) {
112
+ forgetScore = 0.5;
113
+ reason = '90天未访问';
114
+ } else if (daysSinceAccess > 14 && importanceScore < 0.3) {
115
+ forgetScore = 0.3;
116
+ reason = '14天未访问 + 低重要性';
117
+ }
118
+
119
+ if (forgetScore > 0) {
120
+ candidates.push({ memory, score: forgetScore, reason });
121
+ }
122
+ }
123
+
124
+ // 按遗忘分数排序
125
+ candidates.sort((a, b) => b.score - a.score);
126
+
127
+ return candidates;
128
+ }
129
+ }
@@ -0,0 +1,197 @@
1
+ /**
2
+ * 记忆整合模块
3
+ *
4
+ * 自动将相似记忆整合为记忆簇
5
+ */
6
+
7
+ import { MemorySystem } from './memory-system';
8
+ import { type MemoryConfig } from './config';
9
+ import type { Memory } from './types';
10
+
11
+ export interface IntegrationResult {
12
+ clusterId: string;
13
+ action: 'merged' | 'updated';
14
+ memoryIds: string[];
15
+ }
16
+
17
+ export class Integrator {
18
+ private memorySystem: MemorySystem;
19
+ private config: MemoryConfig;
20
+
21
+ constructor(memorySystem: MemorySystem, config: MemoryConfig) {
22
+ this.memorySystem = memorySystem;
23
+ this.config = config;
24
+ }
25
+
26
+ /**
27
+ * 执行记忆整合
28
+ */
29
+ async integrate(): Promise<IntegrationResult[]> {
30
+ const results: IntegrationResult[] = [];
31
+ const memories = this.memorySystem.getAllMemories();
32
+
33
+ if (memories.length < 2) {
34
+ return results;
35
+ }
36
+
37
+ // 按分类分组
38
+ const byCategory = new Map<string, Memory[]>();
39
+
40
+ for (const memory of memories) {
41
+ const key = `${memory.category}/${memory.subCategory}`;
42
+ if (!byCategory.has(key)) {
43
+ byCategory.set(key, []);
44
+ }
45
+ byCategory.get(key)!.push(memory);
46
+ }
47
+
48
+ // 对每个分类进行检查
49
+ for (const [categoryKey, categoryMemories] of byCategory.entries()) {
50
+ if (categoryMemories.length < 2) continue;
51
+
52
+ // 查找相似记忆
53
+ const similarPairs = this.findSimilarPairs(categoryMemories);
54
+
55
+ for (const pair of similarPairs) {
56
+ // 可以选择合并或保留
57
+ const result = await this.processSimilarPair(pair);
58
+ if (result) {
59
+ results.push(result);
60
+ }
61
+ }
62
+ }
63
+
64
+ if (results.length > 0) {
65
+ console.log(`[Integrator] 已整合 ${results.length} 组记忆`);
66
+ }
67
+
68
+ return results;
69
+ }
70
+
71
+ /**
72
+ * 查找相似记忆对
73
+ */
74
+ private findSimilarPairs(memories: Memory[]): { m1: Memory; m2: Memory; score: number }[] {
75
+ const pairs: { m1: Memory; m2: Memory; score: number }[] = [];
76
+ const threshold = this.config.integrationThreshold;
77
+
78
+ for (let i = 0; i < memories.length; i++) {
79
+ for (let j = i + 1; j < memories.length; j++) {
80
+ const m1 = memories[i];
81
+ const m2 = memories[j];
82
+
83
+ // 检查是否已处理过
84
+ if (m1.mergedFrom?.includes(m2.id) || m2.mergedFrom?.includes(m1.id)) {
85
+ continue;
86
+ }
87
+
88
+ // 简单的文本相似度计算
89
+ const score = this.calculateSimilarity(
90
+ m1.content.task + ' ' + m1.content.result,
91
+ m2.content.task + ' ' + m2.content.result
92
+ );
93
+
94
+ if (score >= threshold) {
95
+ pairs.push({ m1, m2, score });
96
+ }
97
+ }
98
+ }
99
+
100
+ return pairs;
101
+ }
102
+
103
+ /**
104
+ * 计算文本相似度
105
+ */
106
+ private calculateSimilarity(text1: string, text2: string): number {
107
+ const words1 = new Set(this.tokenize(text1));
108
+ const words2 = new Set(this.tokenize(text2));
109
+
110
+ if (words1.size === 0 || words2.size === 0) return 0;
111
+
112
+ let intersection = 0;
113
+ for (const word of words1) {
114
+ if (words2.has(word)) intersection++;
115
+ }
116
+
117
+ const union = words1.size + words2.size - intersection;
118
+ return union > 0 ? intersection / union : 0;
119
+ }
120
+
121
+ /**
122
+ * 分词
123
+ */
124
+ private tokenize(text: string): string[] {
125
+ return text
126
+ .toLowerCase()
127
+ .replace(/[^\w\u4e00-\u9fa5]/g, ' ')
128
+ .split(/\s+/)
129
+ .filter(w => w.length > 1);
130
+ }
131
+
132
+ /**
133
+ * 处理相似记忆对
134
+ */
135
+ private async processSimilarPair(pair: { m1: Memory; m2: Memory; score: number }): Promise<IntegrationResult | null> {
136
+ const { m1, m2, score } = pair;
137
+
138
+ // 简单策略:保留访问次数多的,删除访问次数少的
139
+ const keep = m1.usageCount >= m2.usageCount ? m1 : m2;
140
+ const remove = m1.usageCount >= m2.usageCount ? m2 : m1;
141
+
142
+ // 更新被保留的记忆
143
+ keep.mergedFrom = keep.mergedFrom || [];
144
+ keep.mergedFrom.push(remove.id);
145
+ keep.version++;
146
+ keep.updatedAt = new Date();
147
+
148
+ // 添加合并记录到摘要
149
+ if (keep.content.insights) {
150
+ keep.content.insights.push(`[整合] 合并了记忆 ${remove.id.slice(0, 8)},相似度 ${(score * 100).toFixed(0)}%`);
151
+ }
152
+
153
+ // 删除被合并的记忆
154
+ await this.memorySystem.delete(remove.id);
155
+
156
+ return {
157
+ clusterId: keep.id,
158
+ action: 'merged',
159
+ memoryIds: [m1.id, m2.id]
160
+ };
161
+ }
162
+
163
+ /**
164
+ * 获取整合候选列表
165
+ */
166
+ getIntegrationCandidates(): { m1: Memory; m2: Memory; score: number }[] {
167
+ const candidates: { m1: Memory; m2: Memory; score: number }[] = [];
168
+ const memories = this.memorySystem.getAllMemories();
169
+ const threshold = this.config.integrationThreshold;
170
+
171
+ for (let i = 0; i < memories.length; i++) {
172
+ for (let j = i + 1; j < memories.length; j++) {
173
+ const m1 = memories[i];
174
+ const m2 = memories[j];
175
+
176
+ // 跳过已合并的
177
+ if (m1.mergedFrom?.includes(m2.id) || m2.mergedFrom?.includes(m1.id)) {
178
+ continue;
179
+ }
180
+
181
+ const score = this.calculateSimilarity(
182
+ m1.content.task + ' ' + m1.content.result,
183
+ m2.content.task + ' ' + m2.content.result
184
+ );
185
+
186
+ if (score >= threshold) {
187
+ candidates.push({ m1, m2, score });
188
+ }
189
+ }
190
+ }
191
+
192
+ // 按相似度排序
193
+ candidates.sort((a, b) => b.score - a.score);
194
+
195
+ return candidates;
196
+ }
197
+ }