@memory-river/core 0.2.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.
- package/LICENSE +201 -0
- package/README.md +222 -0
- package/README.zh-TW.md +186 -0
- package/dist/api.d.ts +100 -0
- package/dist/api.js +156 -0
- package/dist/cognition/causal-attribution.d.ts +36 -0
- package/dist/cognition/causal-attribution.js +239 -0
- package/dist/cognition/causal-engine.d.ts +105 -0
- package/dist/cognition/causal-engine.js +150 -0
- package/dist/cognition/conflict-detector.d.ts +39 -0
- package/dist/cognition/conflict-detector.js +193 -0
- package/dist/cognition/global-working-memory.d.ts +53 -0
- package/dist/cognition/global-working-memory.js +211 -0
- package/dist/cognition/hooks-engine.d.ts +99 -0
- package/dist/cognition/hooks-engine.js +672 -0
- package/dist/cognition/ralph-core.d.ts +28 -0
- package/dist/cognition/ralph-core.js +104 -0
- package/dist/distill/concentrator-adapter.d.ts +167 -0
- package/dist/distill/concentrator-adapter.js +1876 -0
- package/dist/engine.d.ts +402 -0
- package/dist/engine.js +2254 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/lifecycle/cleanup-engine.d.ts +80 -0
- package/dist/lifecycle/cleanup-engine.js +162 -0
- package/dist/lifecycle/cleanup-state.d.ts +34 -0
- package/dist/lifecycle/cleanup-state.js +50 -0
- package/dist/lifecycle/night-consolidation.d.ts +102 -0
- package/dist/lifecycle/night-consolidation.js +640 -0
- package/dist/lifecycle/night-recovery.d.ts +40 -0
- package/dist/lifecycle/night-recovery.js +107 -0
- package/dist/paths.d.ts +17 -0
- package/dist/paths.js +16 -0
- package/dist/pipeline/capsule-bridge.d.ts +35 -0
- package/dist/pipeline/capsule-bridge.js +86 -0
- package/dist/pipeline/compact-request.d.ts +30 -0
- package/dist/pipeline/compact-request.js +66 -0
- package/dist/pipeline/inbox-watcher.d.ts +112 -0
- package/dist/pipeline/inbox-watcher.js +1039 -0
- package/dist/ports.d.ts +29 -0
- package/dist/ports.js +1 -0
- package/dist/providers/embedder-v5.d.ts +46 -0
- package/dist/providers/embedder-v5.js +155 -0
- package/dist/providers/ollama-embedding.d.ts +25 -0
- package/dist/providers/ollama-embedding.js +166 -0
- package/dist/retrieval/abstractness-judge.d.ts +14 -0
- package/dist/retrieval/abstractness-judge.js +87 -0
- package/dist/retrieval/coverage-selection.d.ts +3 -0
- package/dist/retrieval/coverage-selection.js +53 -0
- package/dist/retrieval/cross-encoder-gate.d.ts +40 -0
- package/dist/retrieval/cross-encoder-gate.js +239 -0
- package/dist/retrieval/retriever-v4.d.ts +78 -0
- package/dist/retrieval/retriever-v4.js +1200 -0
- package/dist/skills/validate.d.ts +6 -0
- package/dist/skills/validate.js +69 -0
- package/dist/storage.d.ts +19 -0
- package/dist/storage.js +54 -0
- package/dist/store/aux-table-maintenance.d.ts +5 -0
- package/dist/store/aux-table-maintenance.js +64 -0
- package/dist/store/graph-enumerator.d.ts +21 -0
- package/dist/store/graph-enumerator.js +185 -0
- package/dist/store/graph-store.d.ts +107 -0
- package/dist/store/graph-store.js +478 -0
- package/dist/store/status-manager.d.ts +44 -0
- package/dist/store/status-manager.js +235 -0
- package/dist/store/store-v4.d.ts +339 -0
- package/dist/store/store-v4.js +2871 -0
- package/dist/transcript/keyword-search.d.ts +9 -0
- package/dist/transcript/keyword-search.js +67 -0
- package/dist/transcript/rehydrate-keyword.d.ts +6 -0
- package/dist/transcript/rehydrate-keyword.js +29 -0
- package/dist/transcript/rehydrate.d.ts +33 -0
- package/dist/transcript/rehydrate.js +285 -0
- package/dist/transcript/transcript-archive.d.ts +46 -0
- package/dist/transcript/transcript-archive.js +516 -0
- package/dist/types.d.ts +409 -0
- package/dist/types.js +104 -0
- package/dist/util/bounded-map.d.ts +1 -0
- package/dist/util/bounded-map.js +8 -0
- package/dist/util/rate-limiter.d.ts +12 -0
- package/dist/util/rate-limiter.js +54 -0
- package/dist/util/session-identity.d.ts +65 -0
- package/dist/util/session-identity.js +227 -0
- package/dist/util/util-hash.d.ts +1 -0
- package/dist/util/util-hash.js +4 -0
- package/package.json +59 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { performance } from "node:perf_hooks";
|
|
5
|
+
const MMARCO_MMINILM_RERANKER_MODEL = {
|
|
6
|
+
modelId: "cross-encoder/mmarco-mMiniLMv2-L12-H384-v1",
|
|
7
|
+
revision: "main",
|
|
8
|
+
onnxCandidates: [
|
|
9
|
+
"onnx/model_quint8_avx2.onnx",
|
|
10
|
+
"onnx/model_quantized.onnx",
|
|
11
|
+
"onnx/model.onnx",
|
|
12
|
+
],
|
|
13
|
+
};
|
|
14
|
+
const DEFAULT_TOP_K = 5;
|
|
15
|
+
const DEFAULT_ZH_LOGIT = -7.0;
|
|
16
|
+
const DEFAULT_EN_LOGIT = 3.47;
|
|
17
|
+
const DEFAULT_THREADS = 4;
|
|
18
|
+
const DEFAULT_BATCH_SIZE = 8;
|
|
19
|
+
const DEFAULT_MAX_LENGTH = 512;
|
|
20
|
+
const DEFAULT_PASSAGE_TOKEN_LIMIT = 320;
|
|
21
|
+
const CJK_RE = /[\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff\u3040-\u30ff\uac00-\ud7af]/u;
|
|
22
|
+
let scorerPromise = null;
|
|
23
|
+
let warningEmitted = false;
|
|
24
|
+
let scorerOverride = null;
|
|
25
|
+
export function isCragCrossEncoderGateEnabled(env = process.env) {
|
|
26
|
+
// Enabled by default (fix for the dead cosine gate); disable with MR_CRAG_CROSS_ENCODER=0.
|
|
27
|
+
return env.MR_CRAG_CROSS_ENCODER !== "0" && env.ENABLE_CRAG_GATE !== "0";
|
|
28
|
+
}
|
|
29
|
+
export function containsCjk(text) {
|
|
30
|
+
return CJK_RE.test(text);
|
|
31
|
+
}
|
|
32
|
+
export function cragGateThresholdForText(query, passage, env = process.env) {
|
|
33
|
+
const raw = containsCjk(`${query}\n${passage}`)
|
|
34
|
+
? env.MR_CRAG_GATE_ZH_LOGIT
|
|
35
|
+
: env.MR_CRAG_GATE_EN_LOGIT;
|
|
36
|
+
const parsed = raw === undefined ? Number.NaN : Number(raw);
|
|
37
|
+
if (Number.isFinite(parsed))
|
|
38
|
+
return parsed;
|
|
39
|
+
return containsCjk(`${query}\n${passage}`) ? DEFAULT_ZH_LOGIT : DEFAULT_EN_LOGIT;
|
|
40
|
+
}
|
|
41
|
+
export function cragGateTopK(env = process.env) {
|
|
42
|
+
const parsed = Number(env.MR_CRAG_GATE_TOPK);
|
|
43
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : DEFAULT_TOP_K;
|
|
44
|
+
}
|
|
45
|
+
function candidateScore(candidate) {
|
|
46
|
+
return Number(candidate.finalScore ?? candidate.fusedScore ?? 0);
|
|
47
|
+
}
|
|
48
|
+
export async function scoreCandidates(query, candidates, options) {
|
|
49
|
+
const gateOptions = options ?? {};
|
|
50
|
+
const env = gateOptions.env ?? process.env;
|
|
51
|
+
if (!isCragCrossEncoderGateEnabled(env) || candidates.length === 0)
|
|
52
|
+
return null;
|
|
53
|
+
const topK = gateOptions.topK ?? cragGateTopK(env);
|
|
54
|
+
const shortlist = candidates
|
|
55
|
+
.map((candidate, index) => ({ candidate, index, score: candidateScore(candidate) }))
|
|
56
|
+
.sort((a, b) => b.score - a.score || a.index - b.index)
|
|
57
|
+
.slice(0, Math.min(topK, candidates.length));
|
|
58
|
+
if (shortlist.length === 0)
|
|
59
|
+
return null;
|
|
60
|
+
const scorer = gateOptions.scorer ?? await getCrossEncoderScorer(gateOptions);
|
|
61
|
+
if (!scorer)
|
|
62
|
+
return null;
|
|
63
|
+
const pairs = shortlist.map(({ candidate }) => ({
|
|
64
|
+
query,
|
|
65
|
+
passage: candidate.entry.text,
|
|
66
|
+
}));
|
|
67
|
+
const started = performance.now();
|
|
68
|
+
const { logits } = await scorer.scorePairs(pairs);
|
|
69
|
+
const timingMs = performance.now() - started;
|
|
70
|
+
return {
|
|
71
|
+
scored: shortlist.map((shortlisted, index) => ({
|
|
72
|
+
candidate: shortlisted.candidate,
|
|
73
|
+
logit: Number(logits[index]),
|
|
74
|
+
})),
|
|
75
|
+
timingMs,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export async function applyCragCrossEncoderGate(query, candidates, options = {}) {
|
|
79
|
+
const env = options.env ?? process.env;
|
|
80
|
+
const result = await scoreCandidates(query, candidates, options);
|
|
81
|
+
if (result === null)
|
|
82
|
+
return candidates;
|
|
83
|
+
const rejected = new Set();
|
|
84
|
+
for (const { candidate, logit } of result.scored) {
|
|
85
|
+
const threshold = cragGateThresholdForText(query, candidate.entry.text, env);
|
|
86
|
+
if (Number.isFinite(logit) && logit < threshold) {
|
|
87
|
+
rejected.add(candidate);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return candidates.filter(candidate => !rejected.has(candidate));
|
|
91
|
+
}
|
|
92
|
+
async function getCrossEncoderScorer(options) {
|
|
93
|
+
if (scorerOverride)
|
|
94
|
+
return scorerOverride;
|
|
95
|
+
if (!scorerPromise) {
|
|
96
|
+
scorerPromise = loadCrossEncoderScorer(options).catch((err) => {
|
|
97
|
+
const logger = options.logger ?? console;
|
|
98
|
+
if (!warningEmitted) {
|
|
99
|
+
warningEmitted = true;
|
|
100
|
+
logger.warn("[CRAG] cross-encoder gate unavailable; passing candidates through:", err?.message ?? err);
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return scorerPromise;
|
|
106
|
+
}
|
|
107
|
+
function defaultCacheDir(env = process.env) {
|
|
108
|
+
return env.TRANSFORMERS_CACHE
|
|
109
|
+
?? env.HF_HOME
|
|
110
|
+
?? path.join(os.homedir(), ".cache", "huggingface");
|
|
111
|
+
}
|
|
112
|
+
function modelDirCandidates(spec, options) {
|
|
113
|
+
const cacheDir = options.cacheDir ?? defaultCacheDir(options.env);
|
|
114
|
+
return [...new Set([
|
|
115
|
+
...(options.modelDir ? [options.modelDir] : []),
|
|
116
|
+
path.join(cacheDir, spec.modelId),
|
|
117
|
+
path.join(cacheDir, ...spec.modelId.split("/")),
|
|
118
|
+
])];
|
|
119
|
+
}
|
|
120
|
+
function findOnnx(modelDir, candidates) {
|
|
121
|
+
for (const relative of candidates) {
|
|
122
|
+
const candidate = path.join(modelDir, relative);
|
|
123
|
+
if (fs.existsSync(candidate))
|
|
124
|
+
return candidate;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
function resolveModel(options) {
|
|
129
|
+
for (const modelDir of modelDirCandidates(MMARCO_MMINILM_RERANKER_MODEL, options)) {
|
|
130
|
+
const onnxPath = findOnnx(modelDir, MMARCO_MMINILM_RERANKER_MODEL.onnxCandidates);
|
|
131
|
+
if (onnxPath)
|
|
132
|
+
return { spec: MMARCO_MMINILM_RERANKER_MODEL, modelDir, onnxPath };
|
|
133
|
+
}
|
|
134
|
+
throw new Error(`${MMARCO_MMINILM_RERANKER_MODEL.modelId} ONNX not found under ` +
|
|
135
|
+
modelDirCandidates(MMARCO_MMINILM_RERANKER_MODEL, options).join(", "));
|
|
136
|
+
}
|
|
137
|
+
function innerTokenIds(tokenizer, text) {
|
|
138
|
+
const encoded = tokenizer.encode(text);
|
|
139
|
+
if (encoded.length >= 2 && encoded[0] === 0 && encoded[encoded.length - 1] === 2) {
|
|
140
|
+
return encoded.slice(1, -1);
|
|
141
|
+
}
|
|
142
|
+
return encoded;
|
|
143
|
+
}
|
|
144
|
+
function buildPairIds(input) {
|
|
145
|
+
const queryTokens = innerTokenIds(input.tokenizer, input.query);
|
|
146
|
+
const passageTokens = innerTokenIds(input.tokenizer, input.passage);
|
|
147
|
+
const maxPayload = Math.max(0, input.maxLength - 4);
|
|
148
|
+
const queryBudget = Math.min(queryTokens.length, maxPayload);
|
|
149
|
+
const query = queryTokens.slice(0, queryBudget);
|
|
150
|
+
const remaining = Math.max(0, maxPayload - query.length);
|
|
151
|
+
const passageBudget = Math.min(input.passageTokenLimit, remaining);
|
|
152
|
+
const passage = passageTokens.slice(0, passageBudget);
|
|
153
|
+
return [0, ...query, 2, 2, ...passage, 2];
|
|
154
|
+
}
|
|
155
|
+
function makeTensor(ort, values, dims) {
|
|
156
|
+
return new ort.Tensor("int64", BigInt64Array.from(values.map(value => BigInt(value))), [...dims]);
|
|
157
|
+
}
|
|
158
|
+
async function loadCrossEncoderScorer(options) {
|
|
159
|
+
const resolved = resolveModel(options);
|
|
160
|
+
const started = performance.now();
|
|
161
|
+
const [{ AutoTokenizer, env }, ortNamespace] = await Promise.all([
|
|
162
|
+
import("@xenova/transformers"),
|
|
163
|
+
import("onnxruntime-node"),
|
|
164
|
+
]);
|
|
165
|
+
const ort = ortNamespace.default ?? ortNamespace;
|
|
166
|
+
env.allowRemoteModels = false;
|
|
167
|
+
env.localModelPath = options.cacheDir ?? defaultCacheDir(options.env);
|
|
168
|
+
env.cacheDir = options.cacheDir ?? defaultCacheDir(options.env);
|
|
169
|
+
const tokenizer = await AutoTokenizer.from_pretrained(resolved.spec.modelId, {
|
|
170
|
+
revision: resolved.spec.revision,
|
|
171
|
+
});
|
|
172
|
+
const session = await ort.InferenceSession.create(resolved.onnxPath, {
|
|
173
|
+
executionProviders: ["cpu"],
|
|
174
|
+
intraOpNumThreads: DEFAULT_THREADS,
|
|
175
|
+
interOpNumThreads: 1,
|
|
176
|
+
graphOptimizationLevel: "all",
|
|
177
|
+
});
|
|
178
|
+
const scorer = new OnnxCrossEncoderScorer(ort, session, tokenizer);
|
|
179
|
+
await scorer.scorePairs([{ query: "warmup query", passage: "warmup passage" }]);
|
|
180
|
+
options.logger?.log?.(`[CRAG] cross-encoder gate loaded ${resolved.onnxPath} in ${Math.round(performance.now() - started)}ms`);
|
|
181
|
+
return scorer;
|
|
182
|
+
}
|
|
183
|
+
class OnnxCrossEncoderScorer {
|
|
184
|
+
ort;
|
|
185
|
+
session;
|
|
186
|
+
tokenizer;
|
|
187
|
+
constructor(ort, session, tokenizer) {
|
|
188
|
+
this.ort = ort;
|
|
189
|
+
this.session = session;
|
|
190
|
+
this.tokenizer = tokenizer;
|
|
191
|
+
}
|
|
192
|
+
async scorePairs(pairs) {
|
|
193
|
+
const logits = [];
|
|
194
|
+
for (let offset = 0; offset < pairs.length; offset += DEFAULT_BATCH_SIZE) {
|
|
195
|
+
const batch = pairs.slice(offset, offset + DEFAULT_BATCH_SIZE);
|
|
196
|
+
logits.push(...await this.runBatch(batch));
|
|
197
|
+
}
|
|
198
|
+
return { logits };
|
|
199
|
+
}
|
|
200
|
+
async runBatch(pairs) {
|
|
201
|
+
if (pairs.length === 0)
|
|
202
|
+
return [];
|
|
203
|
+
const encoded = pairs.map(pair => buildPairIds({
|
|
204
|
+
tokenizer: this.tokenizer,
|
|
205
|
+
query: pair.query,
|
|
206
|
+
passage: pair.passage,
|
|
207
|
+
maxLength: DEFAULT_MAX_LENGTH,
|
|
208
|
+
passageTokenLimit: DEFAULT_PASSAGE_TOKEN_LIMIT,
|
|
209
|
+
}));
|
|
210
|
+
const seqLength = Math.max(...encoded.map(item => item.length));
|
|
211
|
+
const inputIds = [];
|
|
212
|
+
const attentionMask = [];
|
|
213
|
+
for (const ids of encoded) {
|
|
214
|
+
inputIds.push(...ids);
|
|
215
|
+
attentionMask.push(...ids.map(id => id === 1 ? 0 : 1));
|
|
216
|
+
for (let index = ids.length; index < seqLength; index++) {
|
|
217
|
+
inputIds.push(1);
|
|
218
|
+
attentionMask.push(0);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const outputs = await this.session.run({
|
|
222
|
+
input_ids: makeTensor(this.ort, inputIds, [pairs.length, seqLength]),
|
|
223
|
+
attention_mask: makeTensor(this.ort, attentionMask, [pairs.length, seqLength]),
|
|
224
|
+
});
|
|
225
|
+
if (!outputs.logits)
|
|
226
|
+
throw new Error("ONNX session did not return logits");
|
|
227
|
+
return Array.from(outputs.logits.data).map(Number);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
export function __setCragCrossEncoderScorerForTests(scorer) {
|
|
231
|
+
scorerOverride = scorer;
|
|
232
|
+
scorerPromise = null;
|
|
233
|
+
warningEmitted = false;
|
|
234
|
+
}
|
|
235
|
+
export function __resetCragCrossEncoderForTests() {
|
|
236
|
+
scorerOverride = null;
|
|
237
|
+
scorerPromise = null;
|
|
238
|
+
warningEmitted = false;
|
|
239
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retriever - Hybrid Search (正確實現 RRF Fusion) + CRAG Evaluator
|
|
3
|
+
* memory-lance-v4
|
|
4
|
+
* * 核心功能:
|
|
5
|
+
* - 混合檢索 (向量 + BM25) 與 RRF Fusion
|
|
6
|
+
* - V4 記憶權重計分 (相似度 + 重要度 + 健康度)
|
|
7
|
+
* - 本地毫秒級 CRAG 評估器 (MiniLM 餘弦相似度)
|
|
8
|
+
* - 赫布理論 (Hebbian Learning) 自動強化記憶
|
|
9
|
+
*/
|
|
10
|
+
import { MemoryStore, type MemorySearchResult } from "../store/store-v4.js";
|
|
11
|
+
import { Embedder } from "../providers/embedder-v5.js";
|
|
12
|
+
import { HooksEngine, type HookTriggerResult } from "../cognition/hooks-engine.js";
|
|
13
|
+
import type { PluginConfig, MemoryEntry } from "../types.js";
|
|
14
|
+
export interface HybridSearchResponse {
|
|
15
|
+
results: MemorySearchResult[];
|
|
16
|
+
hookOriginIds: string[];
|
|
17
|
+
hookOriginKeywords: Record<string, string>;
|
|
18
|
+
queryHash: string;
|
|
19
|
+
}
|
|
20
|
+
type CausalChainNode = {
|
|
21
|
+
entry: MemoryEntry;
|
|
22
|
+
hopFromSeed: number;
|
|
23
|
+
origin: 'parent' | 'child' | 'seed';
|
|
24
|
+
};
|
|
25
|
+
export declare class Retriever {
|
|
26
|
+
private store;
|
|
27
|
+
private embedder;
|
|
28
|
+
private readonly rerankerCacheDir;
|
|
29
|
+
private vectorWeight;
|
|
30
|
+
private bm25Weight;
|
|
31
|
+
private candidatePoolMultiplier;
|
|
32
|
+
private hooksEngine;
|
|
33
|
+
private boostHealthQueue;
|
|
34
|
+
private boostHealthQueueRunning;
|
|
35
|
+
private recallMetadataQueue;
|
|
36
|
+
private recallMetadataQueueRunning;
|
|
37
|
+
constructor(store: MemoryStore, embedder: Embedder, config: PluginConfig["retrieval"], rerankerCacheDir: string, hooksEngine?: HooksEngine);
|
|
38
|
+
hybridSearch(query: string, limit?: number): Promise<HybridSearchResponse>;
|
|
39
|
+
hybridSearchWithoutBoost(query: string, limit?: number): Promise<HybridSearchResponse>;
|
|
40
|
+
private hybridSearchInternal;
|
|
41
|
+
private recordHookEffectiveness;
|
|
42
|
+
private enqueueRecallMetadata;
|
|
43
|
+
private drainRecallMetadataQueue;
|
|
44
|
+
private enqueueBoostHealth;
|
|
45
|
+
private drainBoostHealthQueue;
|
|
46
|
+
private recordQueueDrop;
|
|
47
|
+
private recordBackgroundWriteFailure;
|
|
48
|
+
private extractFragmentId;
|
|
49
|
+
vectorOnly(query: string, limit?: number): Promise<MemorySearchResult[]>;
|
|
50
|
+
ftsOnly(query: string, limit?: number): Promise<MemorySearchResult[]>;
|
|
51
|
+
/**
|
|
52
|
+
* 追蹤因果鏈:根據 parentId 向上找原因、向下找結果
|
|
53
|
+
* @param memoryId 起點記憶 ID
|
|
54
|
+
* @param depth 深度(預設 2:爺爺→爸爸→我→孩子→孫子)
|
|
55
|
+
* @returns 因果鏈上的所有 MemoryEntry
|
|
56
|
+
*/
|
|
57
|
+
getCausalChain(memoryId: string, depth?: number): Promise<CausalChainNode[]>;
|
|
58
|
+
setHooksEngine(engine: HooksEngine): void;
|
|
59
|
+
getStore(): MemoryStore;
|
|
60
|
+
updateMemoryRecord(id: string, updates: {
|
|
61
|
+
text?: string;
|
|
62
|
+
importance?: number;
|
|
63
|
+
}): Promise<void>;
|
|
64
|
+
triggerHooks(query: string): Promise<HookTriggerResult>;
|
|
65
|
+
cragEvaluate(query: string, results: MemorySearchResult[], enableHebbian?: boolean, hookInjectedIds?: Set<string>): Promise<MemorySearchResult[]>;
|
|
66
|
+
/**
|
|
67
|
+
* Entity Synergy Merge 實作
|
|
68
|
+
*
|
|
69
|
+
* 對所有 partial 記憶:
|
|
70
|
+
* 1. 兩兩抽取實體,計算 entity overlap
|
|
71
|
+
* 2. overlap 超過門檻的記憶對 → 合併文字
|
|
72
|
+
* 3. 合併後用 MiniLM 重新評估 relevancy
|
|
73
|
+
* 4. 通過的升級進結果池
|
|
74
|
+
*/
|
|
75
|
+
private trySynergyMerge;
|
|
76
|
+
private filterHooks;
|
|
77
|
+
}
|
|
78
|
+
export {};
|