cortico-world-memory 0.1.3 → 0.1.4

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/world.ts +26 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cortico-world-memory",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "Cortico World 扩展:五层记忆(事实库/知识库/反思/人格/档案)+ 三级记忆底座 + Hybrid Recall 向量混合检索(移植自 E:/qq_bot 的记忆系统与 memory_server 向量 sidecar,qq_bot 源码零改动)。",
6
6
  "license": "MIT",
package/src/world.ts CHANGED
@@ -31,6 +31,29 @@ function isInboundUser(e: EventEnvelope): boolean {
31
31
  return true;
32
32
  }
33
33
 
34
+ /**
35
+ * 自动捕获的记忆域(scope):优先按「发送者 QQ 号」,做到私聊↔群聊按人统一——
36
+ * 同一个人无论是在私聊还是群聊发言,记忆都沉淀到同一个 `qq:<号>` 域,无缝衔接。
37
+ * 非 QQ 来源(没有 QQ 号)退化为会话地址(senderKey)→ 来源(source)→ main(世界无关,不报错)。
38
+ */
39
+ function scopeOfEvent(e: EventEnvelope): string {
40
+ const meta = (e.meta ?? {}) as Record<string, any>;
41
+ // 1. world 提供的发送者 QQ 号(meta.user)
42
+ const u = meta.user;
43
+ if (typeof u === 'number' && Number.isFinite(u)) return `qq:${u}`;
44
+ if (typeof u === 'string' && /^\d{4,}$/.test(u.trim())) return `qq:${u.trim()}`;
45
+ // 2. 从 meta.sender 形如「昵称(QQ:123456)」里抠 QQ 号
46
+ const s = meta.sender;
47
+ if (typeof s === 'string') {
48
+ const m = s.match(/QQ[::]\s*(\d{4,})/);
49
+ if (m) return `qq:${m[1]}`;
50
+ }
51
+ // 3. 退化:会话地址 → 来源 → main
52
+ if (typeof e.senderKey === 'string' && e.senderKey.trim()) return e.senderKey.trim();
53
+ if (typeof e.source === 'string' && e.source) return e.source;
54
+ return 'main';
55
+ }
56
+
34
57
  export class MemoryWorld implements World {
35
58
  readonly id = 'memory';
36
59
  private host: WorldHost | null = null;
@@ -160,10 +183,8 @@ export class MemoryWorld implements World {
160
183
  if (!isInboundUser(e)) continue;
161
184
  const text = cleanCaptureText(e.text ?? '');
162
185
  if (text.length < (this.cfg.captureMinLength ?? 2)) continue;
163
- // scope 取对话地址(senderKey,如群号/私聊号);没有则按来源(source)分域——QQ 没开也不报错。
164
- const scope = (typeof e.senderKey === 'string' && e.senderKey.trim())
165
- ? e.senderKey.trim()
166
- : (typeof e.source === 'string' && e.source ? e.source : 'main');
186
+ // scope 优先按发送者 QQ 号(私聊↔群聊按人统一);非 QQ 来源退化为会话地址/来源。
187
+ const scope = scopeOfEvent(e);
167
188
  this.facts!.add(scope, [text]);
168
189
  const vec = this.cfg.embeddingProvider === 'off' ? null : await this.embedder!.embed(text);
169
190
  this.vectors!.add(scope, text, vec, this.cfg.maxVectorsPerScope);
@@ -225,7 +246,7 @@ export class MemoryWorld implements World {
225
246
  this.vectors!.add(scope, text, this.cfg.embeddingProvider === 'off' ? null : await this.embedder!.embed(text), this.cfg.maxVectorsPerScope);
226
247
  return added ? `OK 已记住(${scope}): ${text.slice(0, 80)}` : `OK 已存在,跳过重复(${scope})`;
227
248
  }),
228
- mem_recall: mk('mem_recall', '按语义+关键词混合检索长期记忆(事实库全文 + BM25+向量+RRF 融合),找旧账/回忆约定时用。scope 留空或填 main = 跨全部记忆域(含自动捕获的对话);填具体 scope 只搜该域。', { query: str('查询'), top_k: num('条数'), scope: str('记忆域(留空=全部)') }, ['query'], ['read'],
249
+ mem_recall: mk('mem_recall', '按语义+关键词混合检索长期记忆(事实库全文 + BM25+向量+RRF 融合),找旧账/回忆约定时用。scope 留空或填 main = 跨全部记忆域(含自动捕获的对话);填具体 scope 只搜该域;可填某人的 qq:<QQ号> 来检索 TA 在私聊与群聊里统一沉淀的记忆(私聊↔群聊无缝衔接)。', { query: str('查询'), top_k: num('条数'), scope: str('记忆域(留空=全部)') }, ['query'], ['read'],
229
250
  async (a) => {
230
251
  const scope = scopeOf(a);
231
252
  const topK = Math.min(20, Number(a.top_k) || this.cfg.vectorTopK);