openclaw-memory-alibaba-local 1.0.13 → 1.0.14

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 (3) hide show
  1. package/index.ts +22 -29
  2. package/package.json +1 -1
  3. package/prompts.ts +5 -3
package/index.ts CHANGED
@@ -181,13 +181,6 @@ function formatRelevantMemoriesContext(
181
181
  ].join("\n");
182
182
  }
183
183
 
184
- function getThresholdForCategory(cfg: MemoryConfig, category: MemoryCategory): number {
185
- if (isUserMemoryCategory(category) || isFullContextSourceCategory(category) || category === FULL_CONTEXT_MEMORY) {
186
- return cfg.similarityThresholdUserMemory;
187
- }
188
- return cfg.similarityThresholdSelfImproving;
189
- }
190
-
191
184
  /** 精简日志:仅记录 tag + prompt 字符数,不贴原文。 */
192
185
  function logLlmCall(tag: string, promptChars: number): void {
193
186
  console.debug(`[openclaw-memory-alibaba-local] llm ${tag} prompt (${promptChars} chars)`);
@@ -1182,6 +1175,16 @@ async function runAgentEndCapture(
1182
1175
 
1183
1176
  console.debug(`[openclaw-memory-alibaba-local] agentEndCapture fullRows=${fullRows.length} userTexts=${userRawTexts.length} uaLines=${uaLines.length}`);
1184
1177
 
1178
+ // Save cursor BEFORE extraction so that a failure in extraction/storage
1179
+ // does not leave the cursor un-advanced, which would cause duplicate
1180
+ // full_context rows on retry.
1181
+ map[key] = {
1182
+ version: 2,
1183
+ roleCounts: { ...running },
1184
+ lastMessagesLength: messages.length,
1185
+ };
1186
+ saveAgentEndCursorMap(lancedbDir, map);
1187
+
1185
1188
  if (fullRows.length > 0) {
1186
1189
  await db.storeMany(
1187
1190
  agentId,
@@ -1205,13 +1208,6 @@ async function runAgentEndCapture(
1205
1208
  captureUserMemoryFromInboundTexts(cfg, db, backend, agentId, sid, userId, userRawTexts),
1206
1209
  captureSelfImprovingFromLines(cfg, db, backend, agentId, sid, userId, uaLines),
1207
1210
  ]);
1208
-
1209
- map[key] = {
1210
- version: 2,
1211
- roleCounts: { ...running },
1212
- lastMessagesLength: messages.length,
1213
- };
1214
- saveAgentEndCursorMap(lancedbDir, map);
1215
1211
  }
1216
1212
 
1217
1213
  /** User memory from raw user message texts (agent_end user delta). */
@@ -1274,8 +1270,10 @@ async function captureUserMemoryFromInboundTexts(
1274
1270
  // ---- Always split: User-related items vs event items ----
1275
1271
  const userItems: LLMExtractionItem[] = [];
1276
1272
  const eventItems: LLMExtractionItem[] = [];
1273
+ // Match both English "User" and Chinese "用户" to correctly route user preferences
1274
+ const USER_SUBJECT_RE = /\bUser\b|用户/;
1277
1275
  for (const item of extractions) {
1278
- if (/\bUser\b/.test(item.text)) {
1276
+ if (USER_SUBJECT_RE.test(item.text)) {
1279
1277
  userItems.push(item);
1280
1278
  } else {
1281
1279
  eventItems.push(item);
@@ -1646,21 +1644,16 @@ async function storeOneCaptureItem(
1646
1644
  if (vectors.length === 0) {
1647
1645
  throw new Error("openclaw-memory-alibaba-local: encodeForStorage returned no vectors");
1648
1646
  }
1649
- const threshold = getThresholdForCategory(cfg, item.category);
1650
1647
  const dedupCategories = getDedupCategories(item.category);
1651
1648
  const rows = buildChunkRows(item, vectors, options);
1652
1649
 
1653
- if (!cfg.memory_duplication_conflict_process) {
1654
- const similar = await db.searchMerged(agentId, vectors, 1, threshold, [...dedupCategories]);
1655
- if (similar.length > 0) {
1656
- await deleteSimilarLogicalMemory(db, agentId, options?.sessionId, similar[0]!);
1657
- }
1658
- const stored = await db.storeMany(agentId, rows);
1659
- return { action: similar.length > 0 ? "updated" : "created", entry: stored[0]! };
1660
- }
1661
-
1662
- // With conflict_process: simple similarity-based dedup (batch merge handles LLM dedup for user memory)
1663
- const recallMinScore = Math.max(0.5, threshold - 0.35);
1650
+ // Guard against near-exact duplicates only (score >= 0.92).
1651
+ // Lower thresholds (e.g. 0.65) would incorrectly treat similar-but-different
1652
+ // items as duplicates (e.g. "likes apples" vs "likes fish").
1653
+ // Semantic dedup for lower-similarity candidates is handled by the LLM CRUD
1654
+ // pipeline (userPipeline / eventPipeline) when memory_duplication_conflict_process is on.
1655
+ const NEAR_EXACT_THRESHOLD = 0.92;
1656
+ const recallMinScore = Math.max(0.5, NEAR_EXACT_THRESHOLD - 0.15);
1664
1657
  const candidates = await db.searchMerged(
1665
1658
  agentId,
1666
1659
  vectors,
@@ -1668,11 +1661,11 @@ async function storeOneCaptureItem(
1668
1661
  recallMinScore,
1669
1662
  [...dedupCategories],
1670
1663
  );
1671
- if (candidates.length > 0 && candidates[0]!.score >= threshold) {
1664
+ if (candidates.length > 0 && candidates[0]!.score >= NEAR_EXACT_THRESHOLD) {
1672
1665
  await deleteSimilarLogicalMemory(db, agentId, options?.sessionId, candidates[0]!);
1673
1666
  }
1674
1667
  const stored = await db.storeMany(agentId, rows);
1675
- return { action: candidates.length > 0 && candidates[0]!.score >= threshold ? "updated" : "created", entry: stored[0]! };
1668
+ return { action: candidates.length > 0 && candidates[0]!.score >= NEAR_EXACT_THRESHOLD ? "updated" : "created", entry: stored[0]! };
1676
1669
  }
1677
1670
 
1678
1671
  // ---------------------------------------------------------------------------
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-memory-alibaba-local",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "OpenClaw memory plugin: local LanceDB + DashScope-compatible embeddings",
5
5
  "type": "module",
6
6
  "engines": {
package/prompts.ts CHANGED
@@ -205,8 +205,9 @@ Only INSERT or UPDATE information that reveals something lasting about the User:
205
205
 
206
206
  # Refinement Principles
207
207
  1. **Prefer the richer version**: When a batch item and a Store item describe the same topic, keep whichever has the most information. If the batch adds new details, UPDATE to include them.
208
- 2. **High cohesion**: Only merge entries about the exact same topic. Entries about different topics stay separate.
209
- 3. **Strip date prefixes**: Input text may contain [date] or [as of ...] prefixesremove them from the output text. User profile memories are evergreen and should not carry temporal tags.
208
+ 2. **High cohesion**: Only merge entries about the exact same specific topic. Entries about different topics stay separate.
209
+ 3. **Multiple preferences coexist**: Different concrete items under the same category are NOT duplicates. For example, "User likes apples" and "User likes fish" are two separate preferences INSERT both, do NOT UPDATE or DELETE one for the other. Only UPDATE/DELETE when the new item truly contradicts or refines the old one (e.g. "User no longer likes apples" replaces "User likes apples").
210
+ 4. **Strip date prefixes**: Input text may contain [date] or [as of ...] prefixes — remove them from the output text. User profile memories are evergreen and should not carry temporal tags.
210
211
 
211
212
  # Actions (one per batch index)
212
213
  - **INSERT**: New lasting personal info not in Store.
@@ -270,7 +271,8 @@ Only INSERT or UPDATE information that captures a concrete, verifiable fact or e
270
271
  1. **Prefer the richer version**: When a batch item and a Store item describe the same topic, keep whichever has the most information. If the batch is richer, DELETE the old Store item and INSERT the batch item. If they are roughly equal, UPDATE to merge details.
271
272
  2. **Preserve temporal markers**: Keep [as of ...] or [date] prefixes — world facts are time-sensitive.
272
273
  3. **High cohesion**: Only merge entries about the exact same event or fact. Different events stay separate even if related.
273
- 4. **Contradiction = replace**: If a batch item directly contradicts a Store item (e.g. different outcome), DELETE the old item and INSERT the new one.
274
+ 4. **Multiple items coexist**: Different concrete items under the same category are NOT duplicates. For example, "likes apples" and "likes fish" are two separate facts — INSERT both. Only DELETE when the new item truly contradicts the old one (e.g. a corrected outcome).
275
+ 5. **Contradiction = replace**: If a batch item directly contradicts a Store item (e.g. different outcome), DELETE the old item and INSERT the new one.
274
276
 
275
277
  # Actions (one per batch index)
276
278
  - **INSERT**: New world fact not in Store.