openclaw-amem 1.1.3 → 1.1.5
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.
- package/dist/index.js +430 -284
- package/openclaw.plugin.json +2 -2
- package/package.json +8 -7
package/dist/index.js
CHANGED
|
@@ -30,56 +30,24 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
30
|
));
|
|
31
31
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
32
|
|
|
33
|
-
// ../amem-core/
|
|
34
|
-
var dist_exports = {};
|
|
35
|
-
__export(dist_exports, {
|
|
36
|
-
addMemory: () => addMemory,
|
|
37
|
-
addNote: () => addNote,
|
|
38
|
-
checkQuality: () => checkQuality,
|
|
39
|
-
configure: () => configure,
|
|
40
|
-
consolidateMemories: () => consolidateMemories,
|
|
41
|
-
cosineSimilarity: () => cosineSimilarity,
|
|
42
|
-
countNotes: () => countNotes,
|
|
43
|
-
createStorageContext: () => createStorageContext,
|
|
44
|
-
deleteNote: () => deleteNote,
|
|
45
|
-
encode: () => encode,
|
|
46
|
-
ensureCollection: () => ensureCollection,
|
|
47
|
-
findByHash: () => findByHash,
|
|
48
|
-
generateReviewBatch: () => generateReviewBatch,
|
|
49
|
-
getDataDir: () => getDataDir,
|
|
50
|
-
getEvoCount: () => getEvoCount,
|
|
51
|
-
getNote: () => getNote,
|
|
52
|
-
getNotesByDatePrefix: () => getNotesByDatePrefix,
|
|
53
|
-
incrementEvoCount: () => incrementEvoCount,
|
|
54
|
-
invalidateNote: () => invalidateNote,
|
|
55
|
-
listMemories: () => listMemories,
|
|
56
|
-
listNotes: () => listNotes,
|
|
57
|
-
llmCall: () => llmCall,
|
|
58
|
-
llmConstructNote: () => llmConstructNote,
|
|
59
|
-
llmCrudDecision: () => llmCrudDecision,
|
|
60
|
-
llmEvolutionJudge: () => llmEvolutionJudge,
|
|
61
|
-
llmEvolveNote: () => llmEvolveNote,
|
|
62
|
-
llmShouldLink: () => llmShouldLink,
|
|
63
|
-
llmShouldMerge: () => llmShouldMerge,
|
|
64
|
-
mergeSimilarNotes: () => mergeSimilarNotes,
|
|
65
|
-
patchNotePayload: () => patchNotePayload,
|
|
66
|
-
queryByEmbedding: () => queryByEmbedding,
|
|
67
|
-
replaceLinkReferences: () => replaceLinkReferences,
|
|
68
|
-
resetCollectionReady: () => resetCollectionReady,
|
|
69
|
-
scanLowQuality: () => scanLowQuality,
|
|
70
|
-
searchMemory: () => searchMemory,
|
|
71
|
-
shouldRunEvolution: () => shouldRunEvolution,
|
|
72
|
-
t: () => t,
|
|
73
|
-
updateNote: () => updateNote,
|
|
74
|
-
updateNoteContent: () => updateNoteContent,
|
|
75
|
-
updateNoteLinks: () => updateNoteLinks
|
|
76
|
-
});
|
|
33
|
+
// ../amem-core/src/config.ts
|
|
77
34
|
function configure(opts) {
|
|
78
35
|
if (opts.dataDir) _dataDir = opts.dataDir;
|
|
79
36
|
}
|
|
80
37
|
function getDataDir() {
|
|
81
38
|
return _dataDir;
|
|
82
39
|
}
|
|
40
|
+
var os, path, _dataDir;
|
|
41
|
+
var init_config = __esm({
|
|
42
|
+
"../amem-core/src/config.ts"() {
|
|
43
|
+
"use strict";
|
|
44
|
+
os = __toESM(require("os"), 1);
|
|
45
|
+
path = __toESM(require("path"), 1);
|
|
46
|
+
_dataDir = process.env.AMEM_DATA_DIR || path.join(os.homedir(), ".amem");
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// ../amem-core/src/embedding.ts
|
|
83
51
|
async function getExtractor() {
|
|
84
52
|
if (extractor) return extractor;
|
|
85
53
|
if (!pipeline) {
|
|
@@ -133,11 +101,28 @@ async function encode(text) {
|
|
|
133
101
|
}
|
|
134
102
|
throw new Error("Unexpected embedding output shape");
|
|
135
103
|
}
|
|
104
|
+
async function loadModel() {
|
|
105
|
+
await getExtractor();
|
|
106
|
+
}
|
|
107
|
+
function isModelLoaded() {
|
|
108
|
+
return extractor !== null;
|
|
109
|
+
}
|
|
136
110
|
function cosineSimilarity(a, b) {
|
|
137
111
|
let dot = 0;
|
|
138
112
|
for (let i = 0; i < a.length; i++) dot += a[i] * b[i];
|
|
139
113
|
return dot;
|
|
140
114
|
}
|
|
115
|
+
var pipeline, extractor, MODEL_NAME;
|
|
116
|
+
var init_embedding = __esm({
|
|
117
|
+
"../amem-core/src/embedding.ts"() {
|
|
118
|
+
"use strict";
|
|
119
|
+
pipeline = null;
|
|
120
|
+
extractor = null;
|
|
121
|
+
MODEL_NAME = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ../amem-core/src/evo-counter.ts
|
|
141
126
|
function counterFile() {
|
|
142
127
|
return process.env.AMEM_EVO_COUNTER_PATH || path2.join(getDataDir(), "amem_evo_cnt.json");
|
|
143
128
|
}
|
|
@@ -158,6 +143,199 @@ function shouldRunEvolution() {
|
|
|
158
143
|
const count = incrementEvoCount();
|
|
159
144
|
return count % EVO_THRESHOLD === 0;
|
|
160
145
|
}
|
|
146
|
+
var fs, path2, EVO_THRESHOLD;
|
|
147
|
+
var init_evo_counter = __esm({
|
|
148
|
+
"../amem-core/src/evo-counter.ts"() {
|
|
149
|
+
"use strict";
|
|
150
|
+
fs = __toESM(require("fs"), 1);
|
|
151
|
+
path2 = __toESM(require("path"), 1);
|
|
152
|
+
init_config();
|
|
153
|
+
EVO_THRESHOLD = 20;
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// ../amem-core/src/prompts.ts
|
|
158
|
+
var LOCALE, en, zh, templates, t;
|
|
159
|
+
var init_prompts = __esm({
|
|
160
|
+
"../amem-core/src/prompts.ts"() {
|
|
161
|
+
"use strict";
|
|
162
|
+
LOCALE = process.env.AMEM_PROMPT_LOCALE === "zh" ? "zh" : "en";
|
|
163
|
+
en = {
|
|
164
|
+
crudDecision: (userText, assistantText, memoryList) => `You are a memory management agent. Analyze the conversation and decide what memory operations are needed.
|
|
165
|
+
|
|
166
|
+
## Conversation
|
|
167
|
+
|
|
168
|
+
User: ${userText}
|
|
169
|
+
Assistant: ${assistantText}
|
|
170
|
+
|
|
171
|
+
## Existing relevant memories (identified by integer idx)
|
|
172
|
+
|
|
173
|
+
${memoryList}
|
|
174
|
+
|
|
175
|
+
## Task
|
|
176
|
+
|
|
177
|
+
Extract only genuinely important long-term facts (decisions, preferences, account info, project status, key insights). Skip small talk, confirmations, and information already captured in existing memories.
|
|
178
|
+
|
|
179
|
+
## Operation types
|
|
180
|
+
- NEW: Extract a brand new fact not present in existing memories
|
|
181
|
+
- UPDATE: New information refines or supersedes an existing memory; specify existingIdx
|
|
182
|
+
- DELETE: An existing memory is outdated, contradicted, or wrong; specify existingIdx, fact = original content
|
|
183
|
+
- NONE: Nothing worth recording, or information already fully captured
|
|
184
|
+
|
|
185
|
+
## Output format
|
|
186
|
+
|
|
187
|
+
Return a JSON array. Each item:
|
|
188
|
+
{"action": "NEW"|"UPDATE"|"DELETE"|"NONE", "fact": "fact content", "existingIdx": integer or omit, "reason": "optional"}
|
|
189
|
+
|
|
190
|
+
Return at most 3 operations. If nothing is worth recording, return [].
|
|
191
|
+
Return only the JSON array, no other text.
|
|
192
|
+
|
|
193
|
+
Examples:
|
|
194
|
+
|
|
195
|
+
1. New preference:
|
|
196
|
+
[{"action": "NEW", "fact": "User prefers TypeScript over JavaScript", "reason": "Explicitly stated tech preference"}]
|
|
197
|
+
|
|
198
|
+
2. Updating an existing memory (idx 0 was "User is evaluating React and Vue"):
|
|
199
|
+
[{"action": "UPDATE", "fact": "User decided to use React (dropped Vue)", "existingIdx": 0, "reason": "Decision finalized, update evaluation status"}]
|
|
200
|
+
|
|
201
|
+
3. Conversation is just "Sure, thanks" / "Got it" with no new info:
|
|
202
|
+
[]`,
|
|
203
|
+
shouldMerge: (contentA, contentB) => `You are a memory deduplication assistant. Determine whether two memories express essentially the same information.
|
|
204
|
+
|
|
205
|
+
Memory A: ${contentA}
|
|
206
|
+
Memory B: ${contentB}
|
|
207
|
+
|
|
208
|
+
Rules:
|
|
209
|
+
- If both memories express the same core fact (possibly different wording or granularity), return:
|
|
210
|
+
{"shouldMerge": true, "merged": "Concise merged statement preserving key details from both, more complete than either alone"}
|
|
211
|
+
- If the memories are complementary, on different topics, or contain different specific facts, return:
|
|
212
|
+
{"shouldMerge": false}
|
|
213
|
+
|
|
214
|
+
Return only JSON, no other text.
|
|
215
|
+
|
|
216
|
+
Examples:
|
|
217
|
+
|
|
218
|
+
1. Should merge (different granularity):
|
|
219
|
+
A: "Project uses PostgreSQL"
|
|
220
|
+
B: "Project's primary database is PostgreSQL 16, deployed on AWS RDS"
|
|
221
|
+
-> {"shouldMerge": true, "merged": "Project uses PostgreSQL 16 as primary database, deployed on AWS RDS"}
|
|
222
|
+
|
|
223
|
+
2. Should NOT merge (complementary but distinct):
|
|
224
|
+
A: "User prefers VS Code"
|
|
225
|
+
B: "User's VS Code uses One Dark Pro theme"
|
|
226
|
+
-> {"shouldMerge": false}`,
|
|
227
|
+
evolutionJudge: (oldContent, newContent) => `You are a memory evolution judge. Analyze the relationship between an old and new memory and return JSON.
|
|
228
|
+
|
|
229
|
+
Old memory: ${oldContent}
|
|
230
|
+
New memory: ${newContent}
|
|
231
|
+
|
|
232
|
+
Classification rules:
|
|
233
|
+
|
|
234
|
+
- EVOLVE: New content deepens or updates the old memory (e.g. "Considering Next.js" -> "Decided on Next.js 14 App Router")
|
|
235
|
+
Return: {"type": "EVOLVE", "mergedContent": "Merged content preserving the evolution trajectory"}
|
|
236
|
+
|
|
237
|
+
- CONFLICT: Old and new information directly contradict each other on the same attribute (e.g. "Uses MySQL as primary DB" vs "Migrated to PostgreSQL")
|
|
238
|
+
Return: {"type": "CONFLICT"}
|
|
239
|
+
|
|
240
|
+
- EXPAND: New information supplements the old memory on the same topic (e.g. "Handles backend dev" + "Backend uses Go and gRPC")
|
|
241
|
+
Return: {"type": "EXPAND", "mergedContent": "Merged content integrating both pieces of information"}
|
|
242
|
+
|
|
243
|
+
- NEW: Completely unrelated information, no substantive connection to the old memory
|
|
244
|
+
Return: {"type": "NEW"}
|
|
245
|
+
|
|
246
|
+
Return only JSON, no other text.`
|
|
247
|
+
};
|
|
248
|
+
zh = {
|
|
249
|
+
crudDecision: (userText, assistantText, memoryList) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u7BA1\u7406 agent\uFF0C\u8D1F\u8D23\u5206\u6790\u5BF9\u8BDD\u5185\u5BB9\u5E76\u51B3\u5B9A\u5982\u4F55\u64CD\u4F5C\u8BB0\u5FC6\u5E93\u3002
|
|
250
|
+
|
|
251
|
+
## \u5BF9\u8BDD\u5185\u5BB9
|
|
252
|
+
|
|
253
|
+
\u7528\u6237\uFF1A${userText}
|
|
254
|
+
\u52A9\u624B\uFF1A${assistantText}
|
|
255
|
+
|
|
256
|
+
## \u5DF2\u6709\u76F8\u5173\u8BB0\u5FC6\uFF08\u7528\u6574\u6570 idx \u6807\u8BC6\uFF09
|
|
257
|
+
|
|
258
|
+
${memoryList}
|
|
259
|
+
|
|
260
|
+
## \u4EFB\u52A1
|
|
261
|
+
|
|
262
|
+
\u5206\u6790\u4E0A\u8FF0\u5BF9\u8BDD\uFF0C\u51B3\u5B9A\u9700\u8981\u54EA\u4E9B\u8BB0\u5FC6\u64CD\u4F5C\u3002\u53EA\u63D0\u53D6\u771F\u6B63\u91CD\u8981\u7684\u957F\u671F\u4E8B\u5B9E\uFF08\u51B3\u7B56\u3001\u504F\u597D\u3001\u8D26\u53F7\u4FE1\u606F\u3001\u9879\u76EE\u72B6\u6001\u3001\u5173\u952E\u6D1E\u5BDF\uFF09\u3002\u8DF3\u8FC7\u95F2\u804A\u3001\u786E\u8BA4\u8BED\u3001\u91CD\u590D\u4FE1\u606F\u3002
|
|
263
|
+
|
|
264
|
+
## \u64CD\u4F5C\u7C7B\u578B
|
|
265
|
+
- NEW\uFF1A\u63D0\u53D6\u5168\u65B0\u4E8B\u5B9E\uFF08\u5DF2\u6709\u8BB0\u5FC6\u4E2D\u6CA1\u6709\u7684\u4FE1\u606F\uFF09
|
|
266
|
+
- UPDATE\uFF1A\u65B0\u4FE1\u606F\u66F4\u65B0\u4E86\u67D0\u6761\u5DF2\u6709\u8BB0\u5FC6\uFF0C\u7528 existingIdx \u6307\u5B9A\u8981\u66F4\u65B0\u7684\u6761\u76EE
|
|
267
|
+
- DELETE\uFF1A\u67D0\u6761\u5DF2\u6709\u8BB0\u5FC6\u5DF2\u7ECF\u8FC7\u65F6\u3001\u53D1\u751F\u51B2\u7A81\u6216\u9519\u8BEF\uFF0C\u7528 existingIdx \u6307\u5B9A\uFF0Cfact \u586B\u539F\u5185\u5BB9
|
|
268
|
+
- NONE\uFF1A\u4E0D\u503C\u5F97\u8BB0\u5F55\u6216\u5DF2\u6709\u5B8C\u5168\u76F8\u540C\u7684\u4FE1\u606F
|
|
269
|
+
|
|
270
|
+
## \u8F93\u51FA\u683C\u5F0F
|
|
271
|
+
|
|
272
|
+
\u8FD4\u56DE JSON \u6570\u7EC4\uFF0C\u6BCF\u6761\u683C\u5F0F\uFF1A
|
|
273
|
+
{"action": "NEW"|"UPDATE"|"DELETE"|"NONE", "fact": "\u4E8B\u5B9E\u5185\u5BB9", "existingIdx": \u6574\u6570\u6216\u7701\u7565, "reason": "\u539F\u56E0\uFF08\u53EF\u9009\uFF09"}
|
|
274
|
+
|
|
275
|
+
\u6BCF\u6B21\u6700\u591A\u8FD4\u56DE 3 \u6761\u64CD\u4F5C\u3002\u5982\u679C\u6CA1\u6709\u503C\u5F97\u64CD\u4F5C\u7684\u5185\u5BB9\uFF0C\u8FD4\u56DE []\u3002
|
|
276
|
+
\u53EA\u8FD4\u56DE JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002
|
|
277
|
+
|
|
278
|
+
\u793A\u4F8B\uFF1A
|
|
279
|
+
|
|
280
|
+
1. \u63D0\u53D6\u65B0\u504F\u597D\uFF1A
|
|
281
|
+
[{"action": "NEW", "fact": "\u7528\u6237\u504F\u597D TypeScript \u800C\u975E JavaScript", "reason": "\u660E\u786E\u8868\u8FBE\u7684\u6280\u672F\u504F\u597D"}]
|
|
282
|
+
|
|
283
|
+
2. \u66F4\u65B0\u5DF2\u6709\u8BB0\u5FC6\uFF08idx 0 \u539F\u4E3A"\u7528\u6237\u6B63\u5728\u8BC4\u4F30 React \u548C Vue"\uFF09\uFF1A
|
|
284
|
+
[{"action": "UPDATE", "fact": "\u7528\u6237\u51B3\u5B9A\u4F7F\u7528 React\uFF08\u653E\u5F03\u4E86 Vue\uFF09", "existingIdx": 0, "reason": "\u51B3\u7B56\u5DF2\u660E\u786E\uFF0C\u66F4\u65B0\u8BC4\u4F30\u72B6\u6001"}]
|
|
285
|
+
|
|
286
|
+
3. \u5BF9\u8BDD\u4EC5\u4E3A"\u597D\u7684\uFF0C\u8C22\u8C22"/"\u6CA1\u95EE\u9898"\u7B49\u786E\u8BA4\u8BED\uFF0C\u65E0\u65B0\u4FE1\u606F\uFF1A
|
|
287
|
+
[]`,
|
|
288
|
+
shouldMerge: (contentA, contentB) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u53BB\u91CD\u52A9\u624B\uFF0C\u8D1F\u8D23\u5224\u65AD\u4E24\u6761\u8BB0\u5FC6\u662F\u5426\u8868\u8FBE\u4E86\u672C\u8D28\u76F8\u540C\u7684\u4FE1\u606F\u3002
|
|
289
|
+
|
|
290
|
+
\u8BB0\u5FC6A\uFF1A${contentA}
|
|
291
|
+
\u8BB0\u5FC6B\uFF1A${contentB}
|
|
292
|
+
|
|
293
|
+
\u5224\u65AD\u89C4\u5219\uFF1A
|
|
294
|
+
- \u5982\u679C\u4E24\u6761\u8BB0\u5FC6\u8868\u8FBE\u7684\u662F\u672C\u8D28\u76F8\u540C\u7684\u4FE1\u606F\uFF08\u53EF\u80FD\u63AA\u8F9E\u4E0D\u540C\u3001\u7C92\u5EA6\u4E0D\u540C\uFF0C\u4F46\u6838\u5FC3\u4E8B\u5B9E\u4E00\u81F4\uFF09\uFF0C\u8FD4\u56DE JSON\uFF1A
|
|
295
|
+
{"shouldMerge": true, "merged": "\u5408\u5E76\u540E\u7684\u7B80\u6D01\u8868\u8FF0\uFF0C\u4FDD\u7559\u4E24\u6761\u8BB0\u5FC6\u7684\u5173\u952E\u4FE1\u606F\uFF0C\u6BD4\u4EFB\u4F55\u4E00\u6761\u90FD\u66F4\u5B8C\u6574"}
|
|
296
|
+
- \u5982\u679C\u4E24\u6761\u8BB0\u5FC6\u662F\u4E92\u8865\u4FE1\u606F\u3001\u4E0D\u540C\u4E3B\u9898\u3001\u6216\u5305\u542B\u4E0D\u540C\u7684\u5177\u4F53\u4E8B\u5B9E\uFF0C\u8FD4\u56DE JSON\uFF1A
|
|
297
|
+
{"shouldMerge": false}
|
|
298
|
+
|
|
299
|
+
\u53EA\u8FD4\u56DE JSON\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002
|
|
300
|
+
|
|
301
|
+
\u793A\u4F8B\uFF1A
|
|
302
|
+
|
|
303
|
+
1. \u5E94\u5408\u5E76\uFF08\u7C92\u5EA6\u4E0D\u540C\uFF09\uFF1A
|
|
304
|
+
A: "\u9879\u76EE\u4F7F\u7528 PostgreSQL \u6570\u636E\u5E93"
|
|
305
|
+
B: "\u9879\u76EE\u7684\u4E3B\u6570\u636E\u5E93\u662F PostgreSQL 16\uFF0C\u90E8\u7F72\u5728 AWS RDS \u4E0A"
|
|
306
|
+
\u2192 {"shouldMerge": true, "merged": "\u9879\u76EE\u4F7F\u7528 PostgreSQL 16 \u4F5C\u4E3A\u4E3B\u6570\u636E\u5E93\uFF0C\u90E8\u7F72\u5728 AWS RDS \u4E0A"}
|
|
307
|
+
|
|
308
|
+
2. \u4E0D\u5E94\u5408\u5E76\uFF08\u4E92\u8865\u4F46\u4E0D\u540C\uFF09\uFF1A
|
|
309
|
+
A: "\u7528\u6237\u559C\u6B22\u7528 VS Code"
|
|
310
|
+
B: "\u7528\u6237\u7684 VS Code \u4F7F\u7528 One Dark Pro \u4E3B\u9898"
|
|
311
|
+
\u2192 {"shouldMerge": false}`,
|
|
312
|
+
evolutionJudge: (oldContent, newContent) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u6F14\u5316\u5224\u65AD\u52A9\u624B\u3002\u5206\u6790\u4EE5\u4E0B\u4E24\u6761\u8BB0\u5FC6\u7684\u5173\u7CFB\u5E76\u8FD4\u56DE JSON\u3002
|
|
313
|
+
|
|
314
|
+
\u65E7\u8BB0\u5FC6\uFF1A${oldContent}
|
|
315
|
+
\u65B0\u8BB0\u5FC6\uFF1A${newContent}
|
|
316
|
+
|
|
317
|
+
\u5224\u65AD\u89C4\u5219\uFF1A
|
|
318
|
+
|
|
319
|
+
- EVOLVE\uFF1A\u65B0\u5185\u5BB9\u662F\u5BF9\u65E7\u8BB0\u5FC6\u7684\u6DF1\u5316/\u66F4\u65B0\uFF08\u5982\u300C\u6B63\u5728\u8003\u8651\u7528 Next.js\u300D\u2192\u300C\u51B3\u5B9A\u7528 Next.js 14 App Router\u300D\uFF09
|
|
320
|
+
\u8FD4\u56DE\uFF1A{"type": "EVOLVE", "mergedContent": "\u878D\u5408\u540E\u7684\u5B8C\u6574\u5185\u5BB9\uFF0C\u4FDD\u7559\u6F14\u5316\u8F68\u8FF9"}
|
|
321
|
+
|
|
322
|
+
- CONFLICT\uFF1A\u65B0\u65E7\u4FE1\u606F\u5728\u540C\u4E00\u5C5E\u6027\u4E0A\u76F4\u63A5\u77DB\u76FE\uFF08\u5982\u300C\u4F7F\u7528 MySQL \u4F5C\u4E3A\u4E3B\u6570\u636E\u5E93\u300Dvs\u300C\u5DF2\u8FC1\u79FB\u5230 PostgreSQL\u300D\uFF09
|
|
323
|
+
\u8FD4\u56DE\uFF1A{"type": "CONFLICT"}
|
|
324
|
+
|
|
325
|
+
- EXPAND\uFF1A\u65B0\u4FE1\u606F\u662F\u5BF9\u65E7\u8BB0\u5FC6\u540C\u4E00\u4E3B\u9898\u7684\u8865\u5145\u6269\u5C55\uFF08\u5982\u300C\u8D1F\u8D23\u540E\u7AEF\u5F00\u53D1\u300D+\u300C\u540E\u7AEF\u4F7F\u7528 Go \u548C gRPC\u300D\uFF09
|
|
326
|
+
\u8FD4\u56DE\uFF1A{"type": "EXPAND", "mergedContent": "\u5408\u5E76\u540E\u7684\u5B8C\u6574\u5185\u5BB9\uFF0C\u6574\u5408\u53CC\u65B9\u4FE1\u606F"}
|
|
327
|
+
|
|
328
|
+
- NEW\uFF1A\u5168\u65B0\u4FE1\u606F\uFF0C\u4E0E\u65E7\u8BB0\u5FC6\u65E0\u5B9E\u8D28\u5173\u8054\uFF08\u5982\u300C\u559C\u6B22 dark mode\u300Dvs\u300C\u4E0B\u5468\u8981\u53BB\u51FA\u5DEE\u300D\uFF09
|
|
329
|
+
\u8FD4\u56DE\uFF1A{"type": "NEW"}
|
|
330
|
+
|
|
331
|
+
\u53EA\u8FD4\u56DE JSON\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002`
|
|
332
|
+
};
|
|
333
|
+
templates = { en, zh };
|
|
334
|
+
t = templates[LOCALE];
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// ../amem-core/src/llm.ts
|
|
161
339
|
async function llmCall(prompt, maxTokens = 500) {
|
|
162
340
|
try {
|
|
163
341
|
const isThinking = MODEL.includes("gemini") || MODEL.includes("pro-agent");
|
|
@@ -381,24 +559,54 @@ ${linkedStr}`;
|
|
|
381
559
|
return { tags: null, context: null, shouldStrengthen: false, suggestedConnections: [], tagsToUpdate: [] };
|
|
382
560
|
}
|
|
383
561
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
562
|
+
var import_sdk, client, MODEL, VALID_CONFIDENCE, VALID_CATEGORIES, VALID_EVOLUTION_TYPES;
|
|
563
|
+
var init_llm = __esm({
|
|
564
|
+
"../amem-core/src/llm.ts"() {
|
|
565
|
+
"use strict";
|
|
566
|
+
import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
|
|
567
|
+
init_prompts();
|
|
568
|
+
client = new import_sdk.default({
|
|
569
|
+
...process.env.AMEM_LLM_API_KEY && { apiKey: process.env.AMEM_LLM_API_KEY },
|
|
570
|
+
...process.env.AMEM_LLM_BASE_URL && { baseURL: process.env.AMEM_LLM_BASE_URL }
|
|
571
|
+
});
|
|
572
|
+
MODEL = process.env.AMEM_LLM_MODEL ?? "claude-sonnet-4-6";
|
|
573
|
+
VALID_CONFIDENCE = /* @__PURE__ */ new Set(["high", "medium", "low"]);
|
|
574
|
+
VALID_CATEGORIES = /* @__PURE__ */ new Set([
|
|
575
|
+
"Technical",
|
|
576
|
+
"Business",
|
|
577
|
+
"Personal",
|
|
578
|
+
"Project",
|
|
579
|
+
"Research",
|
|
580
|
+
"System",
|
|
581
|
+
"General"
|
|
582
|
+
]);
|
|
583
|
+
VALID_EVOLUTION_TYPES = /* @__PURE__ */ new Set(["EVOLVE", "CONFLICT", "EXPAND", "NEW"]);
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
// ../amem-core/src/storage.ts
|
|
588
|
+
async function qdrant(method, path6, body) {
|
|
589
|
+
const res = await fetch(`${QDRANT_URL}${path6}`, {
|
|
590
|
+
method,
|
|
591
|
+
headers: { "Content-Type": "application/json" },
|
|
592
|
+
body: body ? JSON.stringify(body) : void 0
|
|
593
|
+
});
|
|
594
|
+
const data = await res.json();
|
|
595
|
+
if (!res.ok || data.status && data.status !== "ok" && data.status !== "acknowledged") {
|
|
596
|
+
throw new Error(`Qdrant ${method} ${path6} failed: ${data.error || JSON.stringify(data)}`);
|
|
597
|
+
}
|
|
598
|
+
return data.result;
|
|
599
|
+
}
|
|
600
|
+
async function pingQdrant() {
|
|
601
|
+
const res = await fetch(`${QDRANT_URL}/readyz`);
|
|
602
|
+
if (!res.ok) throw new Error(`Qdrant GET /readyz failed: ${res.status}`);
|
|
603
|
+
}
|
|
604
|
+
function resetCollectionReady() {
|
|
605
|
+
_collectionReady = false;
|
|
606
|
+
_collectionReadyMap.clear();
|
|
607
|
+
}
|
|
608
|
+
async function ensureCollection(collectionName) {
|
|
609
|
+
const col = collectionName || getCollection();
|
|
402
610
|
if (collectionName) {
|
|
403
611
|
if (_collectionReadyMap.get(col)) return;
|
|
404
612
|
} else {
|
|
@@ -776,6 +984,19 @@ async function patchNotePayload(id, fields) {
|
|
|
776
984
|
async function replaceLinkReferences(oldId, newId, agentId) {
|
|
777
985
|
return makeCrud(getCollection()).replaceLinkReferences(oldId, newId, agentId);
|
|
778
986
|
}
|
|
987
|
+
var QDRANT_URL, getCollection, VECTOR_DIM, _collectionReady, _collectionReadyMap;
|
|
988
|
+
var init_storage = __esm({
|
|
989
|
+
"../amem-core/src/storage.ts"() {
|
|
990
|
+
"use strict";
|
|
991
|
+
QDRANT_URL = "http://localhost:6333";
|
|
992
|
+
getCollection = () => process.env.AMEM_COLLECTION || "amem_notes";
|
|
993
|
+
VECTOR_DIM = 384;
|
|
994
|
+
_collectionReady = false;
|
|
995
|
+
_collectionReadyMap = /* @__PURE__ */ new Map();
|
|
996
|
+
}
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
// ../amem-core/src/memory.ts
|
|
779
1000
|
function getJieba() {
|
|
780
1001
|
if (!_jieba) _jieba = new import_jieba.Jieba();
|
|
781
1002
|
return _jieba;
|
|
@@ -1041,6 +1262,44 @@ async function addMemory(content, agentId = "main", opts) {
|
|
|
1041
1262
|
console.log(`[done] Note added: ${note.id}`);
|
|
1042
1263
|
return note.id;
|
|
1043
1264
|
}
|
|
1265
|
+
async function addEpisodic(content, agentId = "main", opts) {
|
|
1266
|
+
const scope = opts?.scope ?? "private";
|
|
1267
|
+
const ctx = opts?.storageCtx ?? defaultCtx();
|
|
1268
|
+
const quality = checkQuality(content);
|
|
1269
|
+
if (!quality.ok) {
|
|
1270
|
+
throw new Error(`[quality] \u5199\u5165\u62D2\u7EDD: ${quality.reason}`);
|
|
1271
|
+
}
|
|
1272
|
+
const embedding = await encode(content);
|
|
1273
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1274
|
+
const note = {
|
|
1275
|
+
id: (0, import_uuid.v4)(),
|
|
1276
|
+
content,
|
|
1277
|
+
timestamp: now,
|
|
1278
|
+
keywords: [],
|
|
1279
|
+
tags: [],
|
|
1280
|
+
context: "",
|
|
1281
|
+
embedding,
|
|
1282
|
+
links: [],
|
|
1283
|
+
agent_id: scope === "shared" ? "shared" : agentId,
|
|
1284
|
+
hash: (0, import_crypto.createHash)("md5").update(content).digest("hex"),
|
|
1285
|
+
retrieval_count: 0,
|
|
1286
|
+
last_accessed: now,
|
|
1287
|
+
evolution_history: [],
|
|
1288
|
+
category: "General",
|
|
1289
|
+
is_active: true,
|
|
1290
|
+
note_type: "memory",
|
|
1291
|
+
topics: [],
|
|
1292
|
+
pending_merge: false,
|
|
1293
|
+
conflict: false,
|
|
1294
|
+
ephemeral: quality.ephemeral,
|
|
1295
|
+
low_quality: false,
|
|
1296
|
+
owner: agentId,
|
|
1297
|
+
readers: scope === "shared" ? ["*"] : [agentId],
|
|
1298
|
+
writers: [agentId]
|
|
1299
|
+
};
|
|
1300
|
+
await ctx.addNote(note);
|
|
1301
|
+
return note.id;
|
|
1302
|
+
}
|
|
1044
1303
|
async function searchMemory(query, topK = 5, agentId = "main", opts) {
|
|
1045
1304
|
const useBfs = opts?.useBfs !== false;
|
|
1046
1305
|
const bfsSimThreshold = opts?.bfsSimThreshold ?? 0.25;
|
|
@@ -1130,7 +1389,7 @@ async function listMemories(agentId = "main", storageCtx) {
|
|
|
1130
1389
|
return { count };
|
|
1131
1390
|
}
|
|
1132
1391
|
function sleep(ms) {
|
|
1133
|
-
return new Promise((
|
|
1392
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1134
1393
|
}
|
|
1135
1394
|
async function mergeSimilarNotes(agentId, storageCtx) {
|
|
1136
1395
|
const ctx = storageCtx ?? defaultCtx();
|
|
@@ -1361,6 +1620,26 @@ async function consolidateMemories(agentId, logger, storageCtx) {
|
|
|
1361
1620
|
log.info(`[Consolidation] Completed consolidation run. Merged ${mergedCount} pairs.`);
|
|
1362
1621
|
return mergedCount;
|
|
1363
1622
|
}
|
|
1623
|
+
var import_uuid, import_crypto, fs2, path3, import_jieba, _jieba, EPHEMERAL_SIGNALS;
|
|
1624
|
+
var init_memory = __esm({
|
|
1625
|
+
"../amem-core/src/memory.ts"() {
|
|
1626
|
+
"use strict";
|
|
1627
|
+
import_uuid = require("uuid");
|
|
1628
|
+
import_crypto = require("crypto");
|
|
1629
|
+
fs2 = __toESM(require("fs"), 1);
|
|
1630
|
+
path3 = __toESM(require("path"), 1);
|
|
1631
|
+
init_embedding();
|
|
1632
|
+
init_storage();
|
|
1633
|
+
init_llm();
|
|
1634
|
+
init_evo_counter();
|
|
1635
|
+
init_config();
|
|
1636
|
+
import_jieba = require("@node-rs/jieba");
|
|
1637
|
+
_jieba = null;
|
|
1638
|
+
EPHEMERAL_SIGNALS = ["\u5F85\u8DD1", "\u7B49\u786E\u8BA4", "\u6628\u65E5", "\u660E\u5929\u5B8C\u6210"];
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
|
|
1642
|
+
// ../amem-core/src/quality.ts
|
|
1364
1643
|
async function scanLowQuality(agentId) {
|
|
1365
1644
|
const notes = await listNotes(agentId);
|
|
1366
1645
|
const now = Date.now();
|
|
@@ -1430,10 +1709,21 @@ function severityBadge(reasons) {
|
|
|
1430
1709
|
return "\u{1F7E0} CONFLICT";
|
|
1431
1710
|
}
|
|
1432
1711
|
async function generateReviewBatch(agentId, outputPath) {
|
|
1712
|
+
const root = path4.resolve(DEFAULT_OUTPUT_DIR);
|
|
1713
|
+
let filePath;
|
|
1714
|
+
let batchN;
|
|
1715
|
+
if (outputPath) {
|
|
1716
|
+
const name = path4.basename(outputPath);
|
|
1717
|
+
if (name !== outputPath || name === "" || name === "." || name === "..") {
|
|
1718
|
+
throw new Error(`[quality] outputPath \u5FC5\u987B\u662F\u7EAF\u6587\u4EF6\u540D\uFF08\u4E0D\u542B\u76EE\u5F55\uFF09: ${outputPath}`);
|
|
1719
|
+
}
|
|
1720
|
+
filePath = path4.join(root, name);
|
|
1721
|
+
batchN = 0;
|
|
1722
|
+
} else {
|
|
1723
|
+
batchN = nextBatchNumber(root);
|
|
1724
|
+
filePath = path4.join(root, `amem-review-batch${batchN}.md`);
|
|
1725
|
+
}
|
|
1433
1726
|
const items = await scanLowQuality(agentId);
|
|
1434
|
-
const dir = outputPath ? path4.dirname(outputPath) : DEFAULT_OUTPUT_DIR;
|
|
1435
|
-
const batchN = outputPath ? 0 : nextBatchNumber(dir);
|
|
1436
|
-
const filePath = outputPath || path4.join(dir, `amem-review-batch${batchN}.md`);
|
|
1437
1727
|
const now = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1438
1728
|
const lines = [];
|
|
1439
1729
|
const title = LOCALE2 === "zh" ? "A-MEM \u8D28\u91CF\u5BA1\u6838" : "A-MEM Quality Review";
|
|
@@ -1483,228 +1773,84 @@ async function generateReviewBatch(agentId, outputPath) {
|
|
|
1483
1773
|
fs3.writeFileSync(filePath, lines.join("\n"), "utf8");
|
|
1484
1774
|
return filePath;
|
|
1485
1775
|
}
|
|
1486
|
-
var
|
|
1487
|
-
var
|
|
1488
|
-
"../amem-core/
|
|
1776
|
+
var fs3, path4, LOCALE2, DEFAULT_OUTPUT_DIR;
|
|
1777
|
+
var init_quality = __esm({
|
|
1778
|
+
"../amem-core/src/quality.ts"() {
|
|
1489
1779
|
"use strict";
|
|
1490
|
-
os = __toESM(require("os"), 1);
|
|
1491
|
-
path = __toESM(require("path"), 1);
|
|
1492
|
-
fs = __toESM(require("fs"), 1);
|
|
1493
|
-
path2 = __toESM(require("path"), 1);
|
|
1494
|
-
import_sdk = __toESM(require("@anthropic-ai/sdk"), 1);
|
|
1495
|
-
import_uuid = require("uuid");
|
|
1496
|
-
import_crypto = require("crypto");
|
|
1497
|
-
fs2 = __toESM(require("fs"), 1);
|
|
1498
|
-
path3 = __toESM(require("path"), 1);
|
|
1499
|
-
import_jieba = require("@node-rs/jieba");
|
|
1500
1780
|
fs3 = __toESM(require("fs"), 1);
|
|
1501
1781
|
path4 = __toESM(require("path"), 1);
|
|
1502
|
-
|
|
1503
|
-
pipeline = null;
|
|
1504
|
-
extractor = null;
|
|
1505
|
-
MODEL_NAME = "Xenova/paraphrase-multilingual-MiniLM-L12-v2";
|
|
1506
|
-
EVO_THRESHOLD = 20;
|
|
1507
|
-
LOCALE = process.env.AMEM_PROMPT_LOCALE === "zh" ? "zh" : "en";
|
|
1508
|
-
en = {
|
|
1509
|
-
crudDecision: (userText, assistantText, memoryList) => `You are a memory management agent. Analyze the conversation and decide what memory operations are needed.
|
|
1510
|
-
|
|
1511
|
-
## Conversation
|
|
1512
|
-
|
|
1513
|
-
User: ${userText}
|
|
1514
|
-
Assistant: ${assistantText}
|
|
1515
|
-
|
|
1516
|
-
## Existing relevant memories (identified by integer idx)
|
|
1517
|
-
|
|
1518
|
-
${memoryList}
|
|
1519
|
-
|
|
1520
|
-
## Task
|
|
1521
|
-
|
|
1522
|
-
Extract only genuinely important long-term facts (decisions, preferences, account info, project status, key insights). Skip small talk, confirmations, and information already captured in existing memories.
|
|
1523
|
-
|
|
1524
|
-
## Operation types
|
|
1525
|
-
- NEW: Extract a brand new fact not present in existing memories
|
|
1526
|
-
- UPDATE: New information refines or supersedes an existing memory; specify existingIdx
|
|
1527
|
-
- DELETE: An existing memory is outdated, contradicted, or wrong; specify existingIdx, fact = original content
|
|
1528
|
-
- NONE: Nothing worth recording, or information already fully captured
|
|
1529
|
-
|
|
1530
|
-
## Output format
|
|
1531
|
-
|
|
1532
|
-
Return a JSON array. Each item:
|
|
1533
|
-
{"action": "NEW"|"UPDATE"|"DELETE"|"NONE", "fact": "fact content", "existingIdx": integer or omit, "reason": "optional"}
|
|
1534
|
-
|
|
1535
|
-
Return at most 3 operations. If nothing is worth recording, return [].
|
|
1536
|
-
Return only the JSON array, no other text.
|
|
1537
|
-
|
|
1538
|
-
Examples:
|
|
1539
|
-
|
|
1540
|
-
1. New preference:
|
|
1541
|
-
[{"action": "NEW", "fact": "User prefers TypeScript over JavaScript", "reason": "Explicitly stated tech preference"}]
|
|
1542
|
-
|
|
1543
|
-
2. Updating an existing memory (idx 0 was "User is evaluating React and Vue"):
|
|
1544
|
-
[{"action": "UPDATE", "fact": "User decided to use React (dropped Vue)", "existingIdx": 0, "reason": "Decision finalized, update evaluation status"}]
|
|
1545
|
-
|
|
1546
|
-
3. Conversation is just "Sure, thanks" / "Got it" with no new info:
|
|
1547
|
-
[]`,
|
|
1548
|
-
shouldMerge: (contentA, contentB) => `You are a memory deduplication assistant. Determine whether two memories express essentially the same information.
|
|
1549
|
-
|
|
1550
|
-
Memory A: ${contentA}
|
|
1551
|
-
Memory B: ${contentB}
|
|
1552
|
-
|
|
1553
|
-
Rules:
|
|
1554
|
-
- If both memories express the same core fact (possibly different wording or granularity), return:
|
|
1555
|
-
{"shouldMerge": true, "merged": "Concise merged statement preserving key details from both, more complete than either alone"}
|
|
1556
|
-
- If the memories are complementary, on different topics, or contain different specific facts, return:
|
|
1557
|
-
{"shouldMerge": false}
|
|
1558
|
-
|
|
1559
|
-
Return only JSON, no other text.
|
|
1560
|
-
|
|
1561
|
-
Examples:
|
|
1562
|
-
|
|
1563
|
-
1. Should merge (different granularity):
|
|
1564
|
-
A: "Project uses PostgreSQL"
|
|
1565
|
-
B: "Project's primary database is PostgreSQL 16, deployed on AWS RDS"
|
|
1566
|
-
-> {"shouldMerge": true, "merged": "Project uses PostgreSQL 16 as primary database, deployed on AWS RDS"}
|
|
1567
|
-
|
|
1568
|
-
2. Should NOT merge (complementary but distinct):
|
|
1569
|
-
A: "User prefers VS Code"
|
|
1570
|
-
B: "User's VS Code uses One Dark Pro theme"
|
|
1571
|
-
-> {"shouldMerge": false}`,
|
|
1572
|
-
evolutionJudge: (oldContent, newContent) => `You are a memory evolution judge. Analyze the relationship between an old and new memory and return JSON.
|
|
1573
|
-
|
|
1574
|
-
Old memory: ${oldContent}
|
|
1575
|
-
New memory: ${newContent}
|
|
1576
|
-
|
|
1577
|
-
Classification rules:
|
|
1578
|
-
|
|
1579
|
-
- EVOLVE: New content deepens or updates the old memory (e.g. "Considering Next.js" -> "Decided on Next.js 14 App Router")
|
|
1580
|
-
Return: {"type": "EVOLVE", "mergedContent": "Merged content preserving the evolution trajectory"}
|
|
1581
|
-
|
|
1582
|
-
- CONFLICT: Old and new information directly contradict each other on the same attribute (e.g. "Uses MySQL as primary DB" vs "Migrated to PostgreSQL")
|
|
1583
|
-
Return: {"type": "CONFLICT"}
|
|
1584
|
-
|
|
1585
|
-
- EXPAND: New information supplements the old memory on the same topic (e.g. "Handles backend dev" + "Backend uses Go and gRPC")
|
|
1586
|
-
Return: {"type": "EXPAND", "mergedContent": "Merged content integrating both pieces of information"}
|
|
1587
|
-
|
|
1588
|
-
- NEW: Completely unrelated information, no substantive connection to the old memory
|
|
1589
|
-
Return: {"type": "NEW"}
|
|
1590
|
-
|
|
1591
|
-
Return only JSON, no other text.`
|
|
1592
|
-
};
|
|
1593
|
-
zh = {
|
|
1594
|
-
crudDecision: (userText, assistantText, memoryList) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u7BA1\u7406 agent\uFF0C\u8D1F\u8D23\u5206\u6790\u5BF9\u8BDD\u5185\u5BB9\u5E76\u51B3\u5B9A\u5982\u4F55\u64CD\u4F5C\u8BB0\u5FC6\u5E93\u3002
|
|
1595
|
-
|
|
1596
|
-
## \u5BF9\u8BDD\u5185\u5BB9
|
|
1597
|
-
|
|
1598
|
-
\u7528\u6237\uFF1A${userText}
|
|
1599
|
-
\u52A9\u624B\uFF1A${assistantText}
|
|
1600
|
-
|
|
1601
|
-
## \u5DF2\u6709\u76F8\u5173\u8BB0\u5FC6\uFF08\u7528\u6574\u6570 idx \u6807\u8BC6\uFF09
|
|
1602
|
-
|
|
1603
|
-
${memoryList}
|
|
1604
|
-
|
|
1605
|
-
## \u4EFB\u52A1
|
|
1606
|
-
|
|
1607
|
-
\u5206\u6790\u4E0A\u8FF0\u5BF9\u8BDD\uFF0C\u51B3\u5B9A\u9700\u8981\u54EA\u4E9B\u8BB0\u5FC6\u64CD\u4F5C\u3002\u53EA\u63D0\u53D6\u771F\u6B63\u91CD\u8981\u7684\u957F\u671F\u4E8B\u5B9E\uFF08\u51B3\u7B56\u3001\u504F\u597D\u3001\u8D26\u53F7\u4FE1\u606F\u3001\u9879\u76EE\u72B6\u6001\u3001\u5173\u952E\u6D1E\u5BDF\uFF09\u3002\u8DF3\u8FC7\u95F2\u804A\u3001\u786E\u8BA4\u8BED\u3001\u91CD\u590D\u4FE1\u606F\u3002
|
|
1608
|
-
|
|
1609
|
-
## \u64CD\u4F5C\u7C7B\u578B
|
|
1610
|
-
- NEW\uFF1A\u63D0\u53D6\u5168\u65B0\u4E8B\u5B9E\uFF08\u5DF2\u6709\u8BB0\u5FC6\u4E2D\u6CA1\u6709\u7684\u4FE1\u606F\uFF09
|
|
1611
|
-
- UPDATE\uFF1A\u65B0\u4FE1\u606F\u66F4\u65B0\u4E86\u67D0\u6761\u5DF2\u6709\u8BB0\u5FC6\uFF0C\u7528 existingIdx \u6307\u5B9A\u8981\u66F4\u65B0\u7684\u6761\u76EE
|
|
1612
|
-
- DELETE\uFF1A\u67D0\u6761\u5DF2\u6709\u8BB0\u5FC6\u5DF2\u7ECF\u8FC7\u65F6\u3001\u53D1\u751F\u51B2\u7A81\u6216\u9519\u8BEF\uFF0C\u7528 existingIdx \u6307\u5B9A\uFF0Cfact \u586B\u539F\u5185\u5BB9
|
|
1613
|
-
- NONE\uFF1A\u4E0D\u503C\u5F97\u8BB0\u5F55\u6216\u5DF2\u6709\u5B8C\u5168\u76F8\u540C\u7684\u4FE1\u606F
|
|
1614
|
-
|
|
1615
|
-
## \u8F93\u51FA\u683C\u5F0F
|
|
1616
|
-
|
|
1617
|
-
\u8FD4\u56DE JSON \u6570\u7EC4\uFF0C\u6BCF\u6761\u683C\u5F0F\uFF1A
|
|
1618
|
-
{"action": "NEW"|"UPDATE"|"DELETE"|"NONE", "fact": "\u4E8B\u5B9E\u5185\u5BB9", "existingIdx": \u6574\u6570\u6216\u7701\u7565, "reason": "\u539F\u56E0\uFF08\u53EF\u9009\uFF09"}
|
|
1619
|
-
|
|
1620
|
-
\u6BCF\u6B21\u6700\u591A\u8FD4\u56DE 3 \u6761\u64CD\u4F5C\u3002\u5982\u679C\u6CA1\u6709\u503C\u5F97\u64CD\u4F5C\u7684\u5185\u5BB9\uFF0C\u8FD4\u56DE []\u3002
|
|
1621
|
-
\u53EA\u8FD4\u56DE JSON \u6570\u7EC4\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002
|
|
1622
|
-
|
|
1623
|
-
\u793A\u4F8B\uFF1A
|
|
1624
|
-
|
|
1625
|
-
1. \u63D0\u53D6\u65B0\u504F\u597D\uFF1A
|
|
1626
|
-
[{"action": "NEW", "fact": "\u7528\u6237\u504F\u597D TypeScript \u800C\u975E JavaScript", "reason": "\u660E\u786E\u8868\u8FBE\u7684\u6280\u672F\u504F\u597D"}]
|
|
1627
|
-
|
|
1628
|
-
2. \u66F4\u65B0\u5DF2\u6709\u8BB0\u5FC6\uFF08idx 0 \u539F\u4E3A"\u7528\u6237\u6B63\u5728\u8BC4\u4F30 React \u548C Vue"\uFF09\uFF1A
|
|
1629
|
-
[{"action": "UPDATE", "fact": "\u7528\u6237\u51B3\u5B9A\u4F7F\u7528 React\uFF08\u653E\u5F03\u4E86 Vue\uFF09", "existingIdx": 0, "reason": "\u51B3\u7B56\u5DF2\u660E\u786E\uFF0C\u66F4\u65B0\u8BC4\u4F30\u72B6\u6001"}]
|
|
1630
|
-
|
|
1631
|
-
3. \u5BF9\u8BDD\u4EC5\u4E3A"\u597D\u7684\uFF0C\u8C22\u8C22"/"\u6CA1\u95EE\u9898"\u7B49\u786E\u8BA4\u8BED\uFF0C\u65E0\u65B0\u4FE1\u606F\uFF1A
|
|
1632
|
-
[]`,
|
|
1633
|
-
shouldMerge: (contentA, contentB) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u53BB\u91CD\u52A9\u624B\uFF0C\u8D1F\u8D23\u5224\u65AD\u4E24\u6761\u8BB0\u5FC6\u662F\u5426\u8868\u8FBE\u4E86\u672C\u8D28\u76F8\u540C\u7684\u4FE1\u606F\u3002
|
|
1634
|
-
|
|
1635
|
-
\u8BB0\u5FC6A\uFF1A${contentA}
|
|
1636
|
-
\u8BB0\u5FC6B\uFF1A${contentB}
|
|
1637
|
-
|
|
1638
|
-
\u5224\u65AD\u89C4\u5219\uFF1A
|
|
1639
|
-
- \u5982\u679C\u4E24\u6761\u8BB0\u5FC6\u8868\u8FBE\u7684\u662F\u672C\u8D28\u76F8\u540C\u7684\u4FE1\u606F\uFF08\u53EF\u80FD\u63AA\u8F9E\u4E0D\u540C\u3001\u7C92\u5EA6\u4E0D\u540C\uFF0C\u4F46\u6838\u5FC3\u4E8B\u5B9E\u4E00\u81F4\uFF09\uFF0C\u8FD4\u56DE JSON\uFF1A
|
|
1640
|
-
{"shouldMerge": true, "merged": "\u5408\u5E76\u540E\u7684\u7B80\u6D01\u8868\u8FF0\uFF0C\u4FDD\u7559\u4E24\u6761\u8BB0\u5FC6\u7684\u5173\u952E\u4FE1\u606F\uFF0C\u6BD4\u4EFB\u4F55\u4E00\u6761\u90FD\u66F4\u5B8C\u6574"}
|
|
1641
|
-
- \u5982\u679C\u4E24\u6761\u8BB0\u5FC6\u662F\u4E92\u8865\u4FE1\u606F\u3001\u4E0D\u540C\u4E3B\u9898\u3001\u6216\u5305\u542B\u4E0D\u540C\u7684\u5177\u4F53\u4E8B\u5B9E\uFF0C\u8FD4\u56DE JSON\uFF1A
|
|
1642
|
-
{"shouldMerge": false}
|
|
1643
|
-
|
|
1644
|
-
\u53EA\u8FD4\u56DE JSON\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002
|
|
1645
|
-
|
|
1646
|
-
\u793A\u4F8B\uFF1A
|
|
1647
|
-
|
|
1648
|
-
1. \u5E94\u5408\u5E76\uFF08\u7C92\u5EA6\u4E0D\u540C\uFF09\uFF1A
|
|
1649
|
-
A: "\u9879\u76EE\u4F7F\u7528 PostgreSQL \u6570\u636E\u5E93"
|
|
1650
|
-
B: "\u9879\u76EE\u7684\u4E3B\u6570\u636E\u5E93\u662F PostgreSQL 16\uFF0C\u90E8\u7F72\u5728 AWS RDS \u4E0A"
|
|
1651
|
-
\u2192 {"shouldMerge": true, "merged": "\u9879\u76EE\u4F7F\u7528 PostgreSQL 16 \u4F5C\u4E3A\u4E3B\u6570\u636E\u5E93\uFF0C\u90E8\u7F72\u5728 AWS RDS \u4E0A"}
|
|
1652
|
-
|
|
1653
|
-
2. \u4E0D\u5E94\u5408\u5E76\uFF08\u4E92\u8865\u4F46\u4E0D\u540C\uFF09\uFF1A
|
|
1654
|
-
A: "\u7528\u6237\u559C\u6B22\u7528 VS Code"
|
|
1655
|
-
B: "\u7528\u6237\u7684 VS Code \u4F7F\u7528 One Dark Pro \u4E3B\u9898"
|
|
1656
|
-
\u2192 {"shouldMerge": false}`,
|
|
1657
|
-
evolutionJudge: (oldContent, newContent) => `\u4F60\u662F\u4E00\u4E2A\u8BB0\u5FC6\u6F14\u5316\u5224\u65AD\u52A9\u624B\u3002\u5206\u6790\u4EE5\u4E0B\u4E24\u6761\u8BB0\u5FC6\u7684\u5173\u7CFB\u5E76\u8FD4\u56DE JSON\u3002
|
|
1658
|
-
|
|
1659
|
-
\u65E7\u8BB0\u5FC6\uFF1A${oldContent}
|
|
1660
|
-
\u65B0\u8BB0\u5FC6\uFF1A${newContent}
|
|
1661
|
-
|
|
1662
|
-
\u5224\u65AD\u89C4\u5219\uFF1A
|
|
1663
|
-
|
|
1664
|
-
- EVOLVE\uFF1A\u65B0\u5185\u5BB9\u662F\u5BF9\u65E7\u8BB0\u5FC6\u7684\u6DF1\u5316/\u66F4\u65B0\uFF08\u5982\u300C\u6B63\u5728\u8003\u8651\u7528 Next.js\u300D\u2192\u300C\u51B3\u5B9A\u7528 Next.js 14 App Router\u300D\uFF09
|
|
1665
|
-
\u8FD4\u56DE\uFF1A{"type": "EVOLVE", "mergedContent": "\u878D\u5408\u540E\u7684\u5B8C\u6574\u5185\u5BB9\uFF0C\u4FDD\u7559\u6F14\u5316\u8F68\u8FF9"}
|
|
1666
|
-
|
|
1667
|
-
- CONFLICT\uFF1A\u65B0\u65E7\u4FE1\u606F\u5728\u540C\u4E00\u5C5E\u6027\u4E0A\u76F4\u63A5\u77DB\u76FE\uFF08\u5982\u300C\u4F7F\u7528 MySQL \u4F5C\u4E3A\u4E3B\u6570\u636E\u5E93\u300Dvs\u300C\u5DF2\u8FC1\u79FB\u5230 PostgreSQL\u300D\uFF09
|
|
1668
|
-
\u8FD4\u56DE\uFF1A{"type": "CONFLICT"}
|
|
1669
|
-
|
|
1670
|
-
- EXPAND\uFF1A\u65B0\u4FE1\u606F\u662F\u5BF9\u65E7\u8BB0\u5FC6\u540C\u4E00\u4E3B\u9898\u7684\u8865\u5145\u6269\u5C55\uFF08\u5982\u300C\u8D1F\u8D23\u540E\u7AEF\u5F00\u53D1\u300D+\u300C\u540E\u7AEF\u4F7F\u7528 Go \u548C gRPC\u300D\uFF09
|
|
1671
|
-
\u8FD4\u56DE\uFF1A{"type": "EXPAND", "mergedContent": "\u5408\u5E76\u540E\u7684\u5B8C\u6574\u5185\u5BB9\uFF0C\u6574\u5408\u53CC\u65B9\u4FE1\u606F"}
|
|
1672
|
-
|
|
1673
|
-
- NEW\uFF1A\u5168\u65B0\u4FE1\u606F\uFF0C\u4E0E\u65E7\u8BB0\u5FC6\u65E0\u5B9E\u8D28\u5173\u8054\uFF08\u5982\u300C\u559C\u6B22 dark mode\u300Dvs\u300C\u4E0B\u5468\u8981\u53BB\u51FA\u5DEE\u300D\uFF09
|
|
1674
|
-
\u8FD4\u56DE\uFF1A{"type": "NEW"}
|
|
1675
|
-
|
|
1676
|
-
\u53EA\u8FD4\u56DE JSON\uFF0C\u4E0D\u8981\u4EFB\u4F55\u5176\u4ED6\u6587\u5B57\u3002`
|
|
1677
|
-
};
|
|
1678
|
-
templates = { en, zh };
|
|
1679
|
-
t = templates[LOCALE];
|
|
1680
|
-
client = new import_sdk.default({
|
|
1681
|
-
...process.env.AMEM_LLM_API_KEY && { apiKey: process.env.AMEM_LLM_API_KEY },
|
|
1682
|
-
...process.env.AMEM_LLM_BASE_URL && { baseURL: process.env.AMEM_LLM_BASE_URL }
|
|
1683
|
-
});
|
|
1684
|
-
MODEL = process.env.AMEM_LLM_MODEL ?? "claude-sonnet-4-6";
|
|
1685
|
-
VALID_CONFIDENCE = /* @__PURE__ */ new Set(["high", "medium", "low"]);
|
|
1686
|
-
VALID_CATEGORIES = /* @__PURE__ */ new Set([
|
|
1687
|
-
"Technical",
|
|
1688
|
-
"Business",
|
|
1689
|
-
"Personal",
|
|
1690
|
-
"Project",
|
|
1691
|
-
"Research",
|
|
1692
|
-
"System",
|
|
1693
|
-
"General"
|
|
1694
|
-
]);
|
|
1695
|
-
VALID_EVOLUTION_TYPES = /* @__PURE__ */ new Set(["EVOLVE", "CONFLICT", "EXPAND", "NEW"]);
|
|
1696
|
-
QDRANT_URL = "http://localhost:6333";
|
|
1697
|
-
getCollection = () => process.env.AMEM_COLLECTION || "amem_notes";
|
|
1698
|
-
VECTOR_DIM = 384;
|
|
1699
|
-
_collectionReady = false;
|
|
1700
|
-
_collectionReadyMap = /* @__PURE__ */ new Map();
|
|
1701
|
-
_jieba = null;
|
|
1702
|
-
EPHEMERAL_SIGNALS = ["\u5F85\u8DD1", "\u7B49\u786E\u8BA4", "\u6628\u65E5", "\u660E\u5929\u5B8C\u6210"];
|
|
1782
|
+
init_storage();
|
|
1703
1783
|
LOCALE2 = process.env.AMEM_PROMPT_LOCALE === "zh" ? "zh" : "en";
|
|
1704
1784
|
DEFAULT_OUTPUT_DIR = process.env.AMEM_REVIEW_DIR || process.cwd();
|
|
1705
1785
|
}
|
|
1706
1786
|
});
|
|
1707
1787
|
|
|
1788
|
+
// ../amem-core/src/index.ts
|
|
1789
|
+
var src_exports = {};
|
|
1790
|
+
__export(src_exports, {
|
|
1791
|
+
addEpisodic: () => addEpisodic,
|
|
1792
|
+
addMemory: () => addMemory,
|
|
1793
|
+
addNote: () => addNote,
|
|
1794
|
+
bm25Score: () => bm25Score,
|
|
1795
|
+
buildBM25: () => buildBM25,
|
|
1796
|
+
checkQuality: () => checkQuality,
|
|
1797
|
+
configure: () => configure,
|
|
1798
|
+
consolidateMemories: () => consolidateMemories,
|
|
1799
|
+
cosineSimilarity: () => cosineSimilarity,
|
|
1800
|
+
countNotes: () => countNotes,
|
|
1801
|
+
createStorageContext: () => createStorageContext,
|
|
1802
|
+
deleteNote: () => deleteNote,
|
|
1803
|
+
encode: () => encode,
|
|
1804
|
+
ensureCollection: () => ensureCollection,
|
|
1805
|
+
findByHash: () => findByHash,
|
|
1806
|
+
generateReviewBatch: () => generateReviewBatch,
|
|
1807
|
+
getDataDir: () => getDataDir,
|
|
1808
|
+
getEvoCount: () => getEvoCount,
|
|
1809
|
+
getNote: () => getNote,
|
|
1810
|
+
getNotesByDatePrefix: () => getNotesByDatePrefix,
|
|
1811
|
+
incrementEvoCount: () => incrementEvoCount,
|
|
1812
|
+
invalidateNote: () => invalidateNote,
|
|
1813
|
+
isModelLoaded: () => isModelLoaded,
|
|
1814
|
+
listMemories: () => listMemories,
|
|
1815
|
+
listNotes: () => listNotes,
|
|
1816
|
+
llmCall: () => llmCall,
|
|
1817
|
+
llmConstructNote: () => llmConstructNote,
|
|
1818
|
+
llmCrudDecision: () => llmCrudDecision,
|
|
1819
|
+
llmEvolutionJudge: () => llmEvolutionJudge,
|
|
1820
|
+
llmEvolveNote: () => llmEvolveNote,
|
|
1821
|
+
llmShouldLink: () => llmShouldLink,
|
|
1822
|
+
llmShouldMerge: () => llmShouldMerge,
|
|
1823
|
+
loadModel: () => loadModel,
|
|
1824
|
+
mergeSimilarNotes: () => mergeSimilarNotes,
|
|
1825
|
+
patchNotePayload: () => patchNotePayload,
|
|
1826
|
+
pingQdrant: () => pingQdrant,
|
|
1827
|
+
queryByEmbedding: () => queryByEmbedding,
|
|
1828
|
+
replaceLinkReferences: () => replaceLinkReferences,
|
|
1829
|
+
resetCollectionReady: () => resetCollectionReady,
|
|
1830
|
+
rrfMerge: () => rrfMerge,
|
|
1831
|
+
scanLowQuality: () => scanLowQuality,
|
|
1832
|
+
searchMemory: () => searchMemory,
|
|
1833
|
+
shouldRunEvolution: () => shouldRunEvolution,
|
|
1834
|
+
simpleTokenize: () => simpleTokenize,
|
|
1835
|
+
t: () => t,
|
|
1836
|
+
updateNote: () => updateNote,
|
|
1837
|
+
updateNoteContent: () => updateNoteContent,
|
|
1838
|
+
updateNoteLinks: () => updateNoteLinks
|
|
1839
|
+
});
|
|
1840
|
+
var init_src = __esm({
|
|
1841
|
+
"../amem-core/src/index.ts"() {
|
|
1842
|
+
"use strict";
|
|
1843
|
+
init_config();
|
|
1844
|
+
init_embedding();
|
|
1845
|
+
init_evo_counter();
|
|
1846
|
+
init_llm();
|
|
1847
|
+
init_memory();
|
|
1848
|
+
init_prompts();
|
|
1849
|
+
init_quality();
|
|
1850
|
+
init_storage();
|
|
1851
|
+
}
|
|
1852
|
+
});
|
|
1853
|
+
|
|
1708
1854
|
// src/index.ts
|
|
1709
1855
|
var index_exports = {};
|
|
1710
1856
|
__export(index_exports, {
|
|
@@ -1733,7 +1879,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
1733
1879
|
var os2 = __toESM(require("os"));
|
|
1734
1880
|
var path5 = __toESM(require("path"));
|
|
1735
1881
|
var import_plugin_entry = require("openclaw/plugin-sdk/plugin-entry");
|
|
1736
|
-
|
|
1882
|
+
init_src();
|
|
1737
1883
|
var import_crypto2 = require("crypto");
|
|
1738
1884
|
|
|
1739
1885
|
// src/hook-liveness.ts
|
|
@@ -1762,7 +1908,7 @@ function hookNeverFiredWarning(now = Date.now()) {
|
|
|
1762
1908
|
}
|
|
1763
1909
|
|
|
1764
1910
|
// src/index.ts
|
|
1765
|
-
|
|
1911
|
+
init_src();
|
|
1766
1912
|
var _config = {};
|
|
1767
1913
|
function register(api) {
|
|
1768
1914
|
const logger = api.logger;
|
|
@@ -2040,7 +2186,7 @@ ${text}${hookWarning}` }],
|
|
|
2040
2186
|
properties: {
|
|
2041
2187
|
outputPath: {
|
|
2042
2188
|
type: "string",
|
|
2043
|
-
description: "Custom
|
|
2189
|
+
description: "Custom filename for the review batch (optional, auto-generates if omitted). A bare filename only \u2014 it is written under AMEM_REVIEW_DIR; a path with directories is rejected."
|
|
2044
2190
|
}
|
|
2045
2191
|
},
|
|
2046
2192
|
required: []
|
|
@@ -2110,7 +2256,7 @@ ${text}${hookWarning}` }],
|
|
|
2110
2256
|
id: r.id,
|
|
2111
2257
|
content: r.content
|
|
2112
2258
|
}));
|
|
2113
|
-
const { llmCrudDecision: llmCrudDecision2 } = await Promise.resolve().then(() => (
|
|
2259
|
+
const { llmCrudDecision: llmCrudDecision2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
2114
2260
|
const operations = await llmCrudDecision2(
|
|
2115
2261
|
userText,
|
|
2116
2262
|
assistantText,
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-amem",
|
|
3
3
|
"name": "Memory (A-MEM v2)",
|
|
4
|
-
"description": "
|
|
5
|
-
"version": "1.1.
|
|
4
|
+
"description": "OpenClaw memory plugin implementing A-MEM — memories evolve, not just accumulate. Graph linking, hybrid retrieval, LLM-driven evolution. No Python.",
|
|
5
|
+
"version": "1.1.5",
|
|
6
6
|
"kind": "memory",
|
|
7
7
|
"openclaw": {
|
|
8
8
|
"compat": {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openclaw-amem",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "A-MEM
|
|
3
|
+
"version": "1.1.5",
|
|
4
|
+
"description": "OpenClaw memory plugin implementing A-MEM — memories evolve, not just accumulate. Graph linking, hybrid retrieval, LLM-driven evolution. No Python.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"openclaw": {
|
|
@@ -16,23 +16,23 @@
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@anthropic-ai/sdk": "^0.
|
|
20
|
-
"@huggingface/transformers": "^
|
|
19
|
+
"@anthropic-ai/sdk": "^0.110.0",
|
|
20
|
+
"@huggingface/transformers": "^4.2.0",
|
|
21
21
|
"@node-rs/jieba": "^2.0.1",
|
|
22
22
|
"@qdrant/js-client-rest": "^1.18.0",
|
|
23
|
-
"@types/uuid": "^
|
|
23
|
+
"@types/uuid": "^11.0.0",
|
|
24
24
|
"uuid": "^14.0.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@eslint/js": "^10.0.1",
|
|
28
|
+
"@types/node": "^26.1.1",
|
|
28
29
|
"eslint": "^10.4.1",
|
|
29
30
|
"prettier": "^3.8.3",
|
|
30
31
|
"tsup": "^8.4.0",
|
|
31
32
|
"tsx": "^4.21.0",
|
|
32
33
|
"typescript": "^5.9.3",
|
|
33
34
|
"typescript-eslint": "^8.60.1",
|
|
34
|
-
"vitest": "^4.1.
|
|
35
|
-
"amem-core": "0.1.0"
|
|
35
|
+
"vitest": "^4.1.10"
|
|
36
36
|
},
|
|
37
37
|
"optionalDependencies": {
|
|
38
38
|
"@node-rs/jieba-darwin-arm64": "^2.0.1",
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
"scripts": {
|
|
69
69
|
"build": "tsup src/index.ts --format cjs --out-dir dist",
|
|
70
70
|
"dev": "tsup src/index.ts --format cjs --watch",
|
|
71
|
+
"typecheck": "tsc --noEmit",
|
|
71
72
|
"lint": "eslint src",
|
|
72
73
|
"format": "prettier --check \"src/**/*.ts\"",
|
|
73
74
|
"format:fix": "prettier --write \"src/**/*.ts\"",
|