@scotthuang/engram 0.1.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.
Files changed (45) hide show
  1. package/README.md +73 -0
  2. package/dist/__tests__/bm25.test.d.ts +1 -0
  3. package/dist/__tests__/bm25.test.js +86 -0
  4. package/dist/__tests__/bm25.test.js.map +1 -0
  5. package/dist/__tests__/config.test.d.ts +1 -0
  6. package/dist/__tests__/config.test.js +31 -0
  7. package/dist/__tests__/config.test.js.map +1 -0
  8. package/dist/__tests__/profile.test.d.ts +1 -0
  9. package/dist/__tests__/profile.test.js +130 -0
  10. package/dist/__tests__/profile.test.js.map +1 -0
  11. package/dist/__tests__/recall.test.d.ts +1 -0
  12. package/dist/__tests__/recall.test.js +162 -0
  13. package/dist/__tests__/recall.test.js.map +1 -0
  14. package/dist/bm25.d.ts +43 -0
  15. package/dist/bm25.js +172 -0
  16. package/dist/bm25.js.map +1 -0
  17. package/dist/config.d.ts +15 -0
  18. package/dist/config.js +28 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/index.d.ts +7 -0
  21. package/dist/index.js +200 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/profile.d.ts +37 -0
  24. package/dist/profile.js +95 -0
  25. package/dist/profile.js.map +1 -0
  26. package/dist/recall.d.ts +37 -0
  27. package/dist/recall.js +173 -0
  28. package/dist/recall.js.map +1 -0
  29. package/dist/settle.d.ts +43 -0
  30. package/dist/settle.js +227 -0
  31. package/dist/settle.js.map +1 -0
  32. package/eslint.config.js +17 -0
  33. package/openclaw.plugin.json +63 -0
  34. package/package.json +34 -0
  35. package/src/__tests__/bm25.test.ts +102 -0
  36. package/src/__tests__/config.test.ts +34 -0
  37. package/src/__tests__/profile.test.ts +147 -0
  38. package/src/__tests__/recall.test.ts +186 -0
  39. package/src/bm25.ts +202 -0
  40. package/src/config.ts +39 -0
  41. package/src/index.ts +246 -0
  42. package/src/profile.ts +114 -0
  43. package/src/recall.ts +213 -0
  44. package/src/settle.ts +277 -0
  45. package/tsconfig.json +16 -0
package/dist/bm25.js ADDED
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Memory System Plugin - BM25 Short-term Memory Index
3
+ *
4
+ * 短期记忆的 BM25 索引构建与搜索
5
+ * 纯 TypeScript 实现,不依赖外部 BM25 库
6
+ */
7
+ import { promises as fs } from "node:fs";
8
+ import { join } from "node:path";
9
+ /**
10
+ * 简单中文分词:按字符 bigram + 单字
11
+ * 生产环境可替换为 jieba
12
+ */
13
+ function tokenize(text) {
14
+ // 去除标点符号,转为小写
15
+ const cleaned = text.toLowerCase().replace(/[^\w\u4e00-\u9fff]/g, " ");
16
+ const tokens = [];
17
+ // 中文 bigram
18
+ const chinese = cleaned.match(/[\u4e00-\u9fff]+/g) || [];
19
+ for (const segment of chinese) {
20
+ for (let i = 0; i < segment.length - 1; i++) {
21
+ tokens.push(segment.substring(i, i + 2));
22
+ }
23
+ // 也保留单字
24
+ for (const char of segment) {
25
+ tokens.push(char);
26
+ }
27
+ }
28
+ // 英文按空格分
29
+ const english = cleaned.replace(/[\u4e00-\u9fff]/g, " ").split(/\s+/).filter(Boolean);
30
+ tokens.push(...english);
31
+ return tokens;
32
+ }
33
+ export class BM25Index {
34
+ docs = [];
35
+ idf = new Map();
36
+ avgDl = 0;
37
+ /**
38
+ * 从短期记忆文件构建索引
39
+ */
40
+ async buildFromDirectory(dir, maxAgeDays = 7) {
41
+ this.docs = [];
42
+ const cutoff = new Date();
43
+ cutoff.setDate(cutoff.getDate() - maxAgeDays);
44
+ try {
45
+ const files = await fs.readdir(dir);
46
+ for (const file of files.sort().reverse()) { // 最新的优先
47
+ if (!file.endsWith(".md"))
48
+ continue;
49
+ const filePath = join(dir, file);
50
+ const stat = await fs.stat(filePath);
51
+ if (stat.mtime < cutoff)
52
+ continue;
53
+ const content = await fs.readFile(filePath, "utf-8");
54
+ const entries = this.parseEntries(content, filePath, file);
55
+ this.docs.push(...entries);
56
+ }
57
+ }
58
+ catch {
59
+ // 目录不存在时忽略
60
+ }
61
+ this.buildIDF();
62
+ }
63
+ /**
64
+ * 解析结构化的短期记忆文件
65
+ * 格式:### HH:MM [分类标签]\n摘要内容
66
+ */
67
+ parseEntries(content, filePath, fileName) {
68
+ const entries = [];
69
+ const lines = content.split("\n");
70
+ const dateMatch = fileName.match(/(\d{4}-\d{2}-\d{2})/);
71
+ const date = dateMatch ? dateMatch[1] : new Date().toISOString().split("T")[0];
72
+ let currentCategory = "随聊";
73
+ let currentText = "";
74
+ for (const line of lines) {
75
+ const headerMatch = line.match(/^###\s+\d{2}:\d{2}\s+\[(\w+)\]/);
76
+ if (headerMatch) {
77
+ // 保存上一条
78
+ if (currentText.trim()) {
79
+ const entry = {
80
+ text: currentText.trim(),
81
+ date,
82
+ category: currentCategory,
83
+ filePath,
84
+ };
85
+ entries.push({ tokens: tokenize(entry.text), entry });
86
+ }
87
+ currentCategory = headerMatch[1];
88
+ currentText = "";
89
+ }
90
+ else if (line.startsWith("#") || line.startsWith("---") || line.startsWith("```")) {
91
+ continue;
92
+ }
93
+ else {
94
+ currentText += line + " ";
95
+ }
96
+ }
97
+ // 最后一条
98
+ if (currentText.trim()) {
99
+ const entry = {
100
+ text: currentText.trim(),
101
+ date,
102
+ category: currentCategory,
103
+ filePath,
104
+ };
105
+ entries.push({ tokens: tokenize(entry.text), entry });
106
+ }
107
+ return entries;
108
+ }
109
+ /**
110
+ * 构建 IDF(逆文档频率)
111
+ */
112
+ buildIDF() {
113
+ const df = new Map(); // 文档频率
114
+ let totalLen = 0;
115
+ for (const doc of this.docs) {
116
+ const unique = new Set(doc.tokens);
117
+ for (const t of unique) {
118
+ df.set(t, (df.get(t) || 0) + 1);
119
+ }
120
+ totalLen += doc.tokens.length;
121
+ }
122
+ this.avgDl = this.docs.length > 0 ? totalLen / this.docs.length : 1;
123
+ const N = this.docs.length;
124
+ for (const [term, freq] of df) {
125
+ // IDF = log((N - df + 0.5) / (df + 0.5) + 1)
126
+ this.idf.set(term, Math.log((N - freq + 0.5) / (freq + 0.5) + 1));
127
+ }
128
+ }
129
+ /**
130
+ * BM25 搜索
131
+ * @returns 带分数的结果列表
132
+ */
133
+ search(query, topK = 3) {
134
+ const queryTokens = tokenize(query);
135
+ const k1 = 1.5; // 词频饱和参数
136
+ const b = 0.75; // 文档长度归一化参数
137
+ const scores = [];
138
+ for (const doc of this.docs) {
139
+ let score = 0;
140
+ for (const token of queryTokens) {
141
+ const idf = this.idf.get(token) || 0;
142
+ if (idf === 0)
143
+ continue;
144
+ // 词频
145
+ const tf = doc.tokens.filter(t => t === token).length;
146
+ const dl = doc.tokens.length;
147
+ // BM25 分数
148
+ const tfComponent = (tf * (k1 + 1)) / (tf + k1 * (1 - b + b * (dl / this.avgDl)));
149
+ score += idf * tfComponent;
150
+ }
151
+ if (score > 0) {
152
+ scores.push({ entry: doc.entry, score });
153
+ }
154
+ }
155
+ scores.sort((a, b) => b.score - a.score);
156
+ return scores.slice(0, topK);
157
+ }
158
+ /**
159
+ * 添加单条记录(用于实时写入后的即时索引)
160
+ */
161
+ addEntry(entry) {
162
+ this.docs.push({
163
+ tokens: tokenize(entry.text),
164
+ entry,
165
+ });
166
+ this.buildIDF(); // 简单处理:重建 IDF
167
+ }
168
+ get size() {
169
+ return this.docs.length;
170
+ }
171
+ }
172
+ //# sourceMappingURL=bm25.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bm25.js","sourceRoot":"","sources":["../src/bm25.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Memory System Plugin - Configuration
3
+ */
4
+ export type MemorySystemConfig = {
5
+ shortTermDays: number;
6
+ halfLifeDays: number;
7
+ recallTopK: number;
8
+ minScore: number;
9
+ vectorWeight: number;
10
+ textWeight: number;
11
+ settleModel: string;
12
+ embeddingModel: string;
13
+ };
14
+ export declare const DEFAULTS: MemorySystemConfig;
15
+ export declare function parseConfig(raw?: Record<string, unknown>): MemorySystemConfig;
package/dist/config.js ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Memory System Plugin - Configuration
3
+ */
4
+ export const DEFAULTS = {
5
+ shortTermDays: 7,
6
+ halfLifeDays: 30,
7
+ recallTopK: 3,
8
+ minScore: 0.4,
9
+ vectorWeight: 0.7,
10
+ textWeight: 0.3,
11
+ settleModel: "volcengine-plan/ark-code-latest",
12
+ embeddingModel: "text-embedding-v3",
13
+ };
14
+ export function parseConfig(raw) {
15
+ if (!raw)
16
+ return { ...DEFAULTS };
17
+ return {
18
+ shortTermDays: typeof raw.shortTermDays === "number" ? raw.shortTermDays : DEFAULTS.shortTermDays,
19
+ halfLifeDays: typeof raw.halfLifeDays === "number" ? raw.halfLifeDays : DEFAULTS.halfLifeDays,
20
+ recallTopK: typeof raw.recallTopK === "number" ? raw.recallTopK : DEFAULTS.recallTopK,
21
+ minScore: typeof raw.minScore === "number" ? raw.minScore : DEFAULTS.minScore,
22
+ vectorWeight: typeof raw.vectorWeight === "number" ? raw.vectorWeight : DEFAULTS.vectorWeight,
23
+ textWeight: typeof raw.textWeight === "number" ? raw.textWeight : DEFAULTS.textWeight,
24
+ settleModel: typeof raw.settleModel === "string" ? raw.settleModel : DEFAULTS.settleModel,
25
+ embeddingModel: typeof raw.embeddingModel === "string" ? raw.embeddingModel : DEFAULTS.embeddingModel,
26
+ };
27
+ }
28
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAaH,MAAM,CAAC,MAAM,QAAQ,GAAuB;IAC1C,aAAa,EAAE,CAAC;IAChB,YAAY,EAAE,EAAE;IAChB,UAAU,EAAE,CAAC;IACb,QAAQ,EAAE,GAAG;IACb,YAAY,EAAE,GAAG;IACjB,UAAU,EAAE,GAAG;IACf,WAAW,EAAE,iCAAiC;IAC9C,cAAc,EAAE,mBAAmB;CACpC,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,GAA6B;IACvD,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,GAAG,QAAQ,EAAE,CAAC;IACjC,OAAO;QACL,aAAa,EAAE,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa;QACjG,YAAY,EAAE,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY;QAC7F,UAAU,EAAE,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU;QACrF,QAAQ,EAAE,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ;QAC7E,YAAY,EAAE,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY;QAC7F,UAAU,EAAE,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU;QACrF,WAAW,EAAE,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW;QACzF,cAAc,EAAE,OAAO,GAAG,CAAC,cAAc,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,QAAQ,CAAC,cAAc;KACtG,CAAC;AACJ,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Engram Plugin - Main Entry
3
+ *
4
+ * 分层语义记忆系统 OpenClaw Plugin
5
+ * kind: "memory" (exclusive slot)
6
+ */
7
+ export default function register(api: any): void;
package/dist/index.js ADDED
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Engram Plugin - Main Entry
3
+ *
4
+ * 分层语义记忆系统 OpenClaw Plugin
5
+ * kind: "memory" (exclusive slot)
6
+ */
7
+ /* eslint-disable @typescript-eslint/no-explicit-any */
8
+ // OpenClaw Plugin API 没有提供类型定义,需要使用 any
9
+ import { parseConfig } from "./config.js";
10
+ import { RecallEngine } from "./recall.js";
11
+ import { ProfileManager } from "./profile.js";
12
+ import { runSettlement } from "./settle.js";
13
+ import { promises as fs } from "node:fs";
14
+ import { join } from "node:path";
15
+ export default function register(api) {
16
+ const rawConfig = api.pluginConfig || {};
17
+ const config = parseConfig(rawConfig);
18
+ const workspaceDir = api.workspaceDir || process.env.HOME + "/.openclaw/workspace";
19
+ // ---- 初始化引擎 ----
20
+ const recallEngine = new RecallEngine(workspaceDir, config);
21
+ const profileManager = new ProfileManager(workspaceDir);
22
+ // 向量搜索回调:复用现有 memory-lancedb 的能力
23
+ recallEngine.setVectorSearch(async (query, limit) => {
24
+ try {
25
+ // 尝试调用 OpenClaw 内置的 embedding 搜索
26
+ // 如果 memory-lancedb 不可用,返回空结果
27
+ const results = await api.memorySearch?.(query, limit);
28
+ if (results && Array.isArray(results)) {
29
+ return results.map((r) => ({
30
+ text: r.text || "",
31
+ source: "vector",
32
+ score: r.score || 0,
33
+ date: r.date,
34
+ category: r.category,
35
+ }));
36
+ }
37
+ return [];
38
+ }
39
+ catch {
40
+ return [];
41
+ }
42
+ });
43
+ // ---- Agent Tools ----
44
+ api.registerTool({
45
+ name: "memory_system_search",
46
+ label: "Memory Search",
47
+ description: "搜索记忆(短期+长期双路召回+画像增强)。用于查找用户偏好、历史事件、之前讨论的话题。",
48
+ parameters: {
49
+ type: "object",
50
+ properties: {
51
+ query: { type: "string", description: "搜索查询" },
52
+ limit: { type: "number", description: "最大返回数(默认使用配置值)" },
53
+ },
54
+ required: ["query"],
55
+ },
56
+ async execute(_toolCallId, params) {
57
+ const { query, limit } = params;
58
+ const { results, memoryContext } = await recallEngine.recall(query);
59
+ return {
60
+ content: [{ type: "text", text: results || "No relevant memories found." }],
61
+ details: { count: results.split("\n").length },
62
+ };
63
+ },
64
+ }, { name: "memory_system_search" });
65
+ api.registerTool({
66
+ name: "memory_system_profile",
67
+ label: "User Profile",
68
+ description: "查看或更新用户语义画像。",
69
+ parameters: {
70
+ type: "object",
71
+ properties: {
72
+ action: { type: "string", enum: ["get", "update"], description: "操作类型" },
73
+ dimension: { type: "string", description: "标签维度(update 时使用)" },
74
+ value: { type: "string", description: "标签值(update 时使用)" },
75
+ },
76
+ required: ["action"],
77
+ },
78
+ async execute(_toolCallId, params) {
79
+ const { action, dimension, value } = params;
80
+ const profile = await profileManager.load();
81
+ if (action === "get") {
82
+ const context = profileManager.getRecallContext(profile);
83
+ const fullTags = Object.entries(profile.tags)
84
+ .map(([dim, tags]) => `- **${dim}**: ${tags.map(t => `${t.value}(${(t.confidence * 100).toFixed(0)}%)`).join(", ")}`)
85
+ .join("\n");
86
+ return {
87
+ content: [{
88
+ type: "text",
89
+ text: `${context}\n\n## 完整画像\n${fullTags || "(暂无标签)"}`,
90
+ }],
91
+ };
92
+ }
93
+ if (action === "update" && dimension && value) {
94
+ profileManager.addTag(profile, dimension, value);
95
+ await profileManager.save(profile);
96
+ return {
97
+ content: [{ type: "text", text: `Added tag: [${dimension}] ${value}` }],
98
+ };
99
+ }
100
+ return { content: [{ type: "text", text: "Invalid action or missing params." }] };
101
+ },
102
+ }, { name: "memory_system_profile" });
103
+ // ---- Auto-Recall: before_prompt_build ----
104
+ api.on("before_prompt_build", async (event) => {
105
+ const prompt = event.prompt || "";
106
+ if (prompt.length < 5)
107
+ return;
108
+ try {
109
+ const { memoryContext } = await recallEngine.recall(prompt);
110
+ if (!memoryContext)
111
+ return;
112
+ return { prependContext: memoryContext };
113
+ }
114
+ catch (err) {
115
+ console.error(`[engram] Recall failed: ${err}`);
116
+ }
117
+ }, { priority: 5 });
118
+ // ---- Hooks: command:new 即时沉淀 ----
119
+ api.registerHook("command:new", async (event) => {
120
+ try {
121
+ // 快速保存:将 session 摘要写入当天短期记忆
122
+ const date = new Date().toISOString().split("T")[0];
123
+ const shortTermDir = join(workspaceDir, "memory", "short-term");
124
+ await fs.mkdir(shortTermDir, { recursive: true });
125
+ const filePath = join(shortTermDir, `${date}.md`);
126
+ const timestamp = new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
127
+ const entry = `\n### ${timestamp} [随聊]\nSession reset via /new at ${new Date().toISOString()}\n`;
128
+ await fs.appendFile(filePath, entry, "utf-8");
129
+ console.log(`[engram] Quick-save on /new: ${date}`);
130
+ }
131
+ catch (err) {
132
+ console.error(`[engram] Hook command:new failed: ${err}`);
133
+ }
134
+ }, {
135
+ name: "engram.command-new",
136
+ description: "会话重置时快速保存上下文",
137
+ });
138
+ // ---- Hooks: session:compact:before 保底沉淀 ----
139
+ api.registerHook("session:compact:before", async (event) => {
140
+ try {
141
+ const date = new Date().toISOString().split("T")[0];
142
+ const shortTermDir = join(workspaceDir, "memory", "short-term");
143
+ await fs.mkdir(shortTermDir, { recursive: true });
144
+ const filePath = join(shortTermDir, `${date}.md`);
145
+ const timestamp = new Date().toLocaleTimeString("zh-CN", { hour: "2-digit", minute: "2-digit" });
146
+ const entry = `\n### ${timestamp} [系统]\nCompaction triggered, saving context at ${new Date().toISOString()}\n`;
147
+ await fs.appendFile(filePath, entry, "utf-8");
148
+ console.log(`[engram] Pre-compaction save: ${date}`);
149
+ }
150
+ catch (err) {
151
+ console.error(`[engram] Hook session:compact:before failed: ${err}`);
152
+ }
153
+ }, {
154
+ name: "engram.compact-before",
155
+ description: "Compaction 前保底保存重要信息",
156
+ });
157
+ // ---- Service: 启动初始化 ----
158
+ api.registerService({
159
+ id: "engram",
160
+ async start() {
161
+ await recallEngine.startup();
162
+ console.log(`[engram] Service started (workspace: ${workspaceDir})`);
163
+ },
164
+ async stop() {
165
+ console.log("[engram] Service stopped");
166
+ },
167
+ });
168
+ // ---- CLI Commands ----
169
+ api.registerCli(({ program }) => {
170
+ const mem = program.command("memory-sys").description("Memory system commands");
171
+ mem.command("settle").description("Run settlement manually").action(async () => {
172
+ const results = await runSettlement({
173
+ workspaceDir,
174
+ config,
175
+ // LLM 和 vector 回调在 CLI 模式下使用默认实现
176
+ llmCall: async (prompt, sys) => {
177
+ // 简单回显,实际应接入 LLM
178
+ console.log("LLM call needed - configure in plugin");
179
+ return "[]";
180
+ },
181
+ });
182
+ results.forEach(r => console.log(` ${r}`));
183
+ });
184
+ mem.command("profile").description("Show user profile").action(async () => {
185
+ const profile = await profileManager.load();
186
+ const context = profileManager.getRecallContext(profile);
187
+ console.log(context || "(empty profile)");
188
+ console.log("\nFull tags:");
189
+ console.log(JSON.stringify(profile.tags, null, 2));
190
+ });
191
+ mem.command("stats").description("Show memory stats").action(async () => {
192
+ console.log(`Workspace: ${workspaceDir}`);
193
+ console.log(`Short-term days: ${config.shortTermDays}`);
194
+ console.log(`Half-life: ${config.halfLifeDays} days`);
195
+ // 可扩展更多统计
196
+ });
197
+ }, { commands: ["memory-sys"] });
198
+ console.log("[engram] Plugin registered");
199
+ }
200
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Memory System Plugin - Profile (语义画像)
3
+ *
4
+ * 画像 JSON 结构 + 读写 + 压缩摘要
5
+ */
6
+ export type ProfileTag = {
7
+ value: string;
8
+ confidence: number;
9
+ lastSeen: string;
10
+ };
11
+ export type SemanticProfile = {
12
+ summary: string;
13
+ coreTags: string[];
14
+ tags: Record<string, ProfileTag[]>;
15
+ updatedAt: string;
16
+ };
17
+ export declare const EMPTY_PROFILE: SemanticProfile;
18
+ export declare class ProfileManager {
19
+ private profile;
20
+ private profilePath;
21
+ constructor(workspaceDir: string);
22
+ ensureDir(): Promise<void>;
23
+ load(): Promise<SemanticProfile>;
24
+ save(profile: SemanticProfile): Promise<void>;
25
+ /**
26
+ * 获取召回用的摘要信息(控制 token 消耗)
27
+ */
28
+ getRecallContext(profile: SemanticProfile): string;
29
+ /**
30
+ * 添加标签(增量更新)
31
+ */
32
+ addTag(profile: SemanticProfile, dimension: string, value: string): SemanticProfile;
33
+ /**
34
+ * 降低标签置信度(遗忘)
35
+ */
36
+ decayTags(profile: SemanticProfile, factor?: number): SemanticProfile;
37
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Memory System Plugin - Profile (语义画像)
3
+ *
4
+ * 画像 JSON 结构 + 读写 + 压缩摘要
5
+ */
6
+ import { promises as fs } from "node:fs";
7
+ import { join } from "node:path";
8
+ export const EMPTY_PROFILE = {
9
+ summary: "",
10
+ coreTags: [],
11
+ tags: {},
12
+ updatedAt: new Date().toISOString(),
13
+ };
14
+ export class ProfileManager {
15
+ profile = null;
16
+ profilePath;
17
+ constructor(workspaceDir) {
18
+ this.profilePath = join(workspaceDir, "memory", "profile", "semantic_profile.json");
19
+ }
20
+ async ensureDir() {
21
+ await fs.mkdir(join(this.profilePath, ".."), { recursive: true });
22
+ }
23
+ async load() {
24
+ if (this.profile)
25
+ return this.profile;
26
+ try {
27
+ const raw = await fs.readFile(this.profilePath, "utf-8");
28
+ this.profile = JSON.parse(raw);
29
+ }
30
+ catch {
31
+ this.profile = { ...EMPTY_PROFILE };
32
+ }
33
+ return this.profile;
34
+ }
35
+ async save(profile) {
36
+ this.profile = profile;
37
+ profile.updatedAt = new Date().toISOString();
38
+ await this.ensureDir();
39
+ await fs.writeFile(this.profilePath, JSON.stringify(profile, null, 2), "utf-8");
40
+ }
41
+ /**
42
+ * 获取召回用的摘要信息(控制 token 消耗)
43
+ */
44
+ getRecallContext(profile) {
45
+ if (!profile.summary && profile.coreTags.length === 0) {
46
+ return "";
47
+ }
48
+ const parts = [];
49
+ if (profile.summary) {
50
+ parts.push(`【用户画像】${profile.summary}`);
51
+ }
52
+ if (profile.coreTags.length > 0) {
53
+ parts.push(`【核心标签】${profile.coreTags.join(", ")}`);
54
+ }
55
+ return parts.join("\n");
56
+ }
57
+ /**
58
+ * 添加标签(增量更新)
59
+ */
60
+ addTag(profile, dimension, value) {
61
+ if (!profile.tags[dimension]) {
62
+ profile.tags[dimension] = [];
63
+ }
64
+ // 避免重复
65
+ const existing = profile.tags[dimension].find(t => t.value === value);
66
+ if (existing) {
67
+ existing.confidence = Math.min(1.0, existing.confidence + 0.1);
68
+ existing.lastSeen = new Date().toISOString();
69
+ }
70
+ else {
71
+ profile.tags[dimension].push({
72
+ value,
73
+ confidence: 0.7,
74
+ lastSeen: new Date().toISOString(),
75
+ });
76
+ }
77
+ return profile;
78
+ }
79
+ /**
80
+ * 降低标签置信度(遗忘)
81
+ */
82
+ decayTags(profile, factor = 0.95) {
83
+ for (const dimension of Object.keys(profile.tags)) {
84
+ profile.tags[dimension] = profile.tags[dimension]
85
+ .map(t => ({ ...t, confidence: t.confidence * factor }))
86
+ .filter(t => t.confidence > 0.2); // 淘汰低置信度标签
87
+ // 清理空维度
88
+ if (profile.tags[dimension].length === 0) {
89
+ delete profile.tags[dimension];
90
+ }
91
+ }
92
+ return profile;
93
+ }
94
+ }
95
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Memory System Plugin - Recall (自动召回)
3
+ *
4
+ * 双路召回:BM25(短期记忆) + Vector(长期记忆)
5
+ * 分数融合 → 排重 → 时间衰减 → MMR 去重 → 画像注入
6
+ */
7
+ import { type MemorySystemConfig } from "./config.js";
8
+ export type RecallResult = {
9
+ text: string;
10
+ source: "short-term" | "vector";
11
+ score: number;
12
+ date?: string;
13
+ category?: string;
14
+ };
15
+ export declare class RecallEngine {
16
+ private bm25;
17
+ private profileManager;
18
+ private config;
19
+ private workspaceDir;
20
+ private vectorSearch?;
21
+ constructor(workspaceDir: string, config: MemorySystemConfig);
22
+ /**
23
+ * 设置向量搜索回调(由 Plugin 主入口注入)
24
+ */
25
+ setVectorSearch(fn: (query: string, limit: number) => Promise<RecallResult[]>): void;
26
+ /**
27
+ * 启动时初始化:加载画像 + 构建 BM25 索引
28
+ */
29
+ startup(): Promise<void>;
30
+ /**
31
+ * 核心:双路召回
32
+ */
33
+ recall(query: string): Promise<{
34
+ results: string;
35
+ memoryContext: string;
36
+ }>;
37
+ }