@futdevpro/fdp-agent-memory 1.1.166 → 1.1.194
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/build/package.json +8 -2
- package/build/src/_cli/_commands/prune-duplicates.command.js +93 -0
- package/build/src/_cli/_commands/retire-temporary.command.js +77 -0
- package/build/src/_cli/register-commands.js +4 -0
- package/build/src/_collections/config-catalog.const.js +75 -0
- package/build/src/_models/data-models/fam-entry.data-model.js +6 -0
- package/build/src/_models/data-models/fam-memory.data-model.js +3 -0
- package/build/src/_modules/embedding/_collections/fam-exact-duplicate-prune.util.js +85 -0
- package/build/src/_modules/embedding/_services/fam-embedding-bootstrap.control-service.js +32 -0
- package/build/src/_modules/embedding/_services/fam-entry.data-service.js +21 -0
- package/build/src/_modules/embedding/_services/fam-exact-duplicate-prune.control-service.js +152 -0
- package/build/src/_modules/embedding/index.js +7 -1
- package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +21 -1
- package/build/src/_modules/ingest/_services/fam-scan-job.control-service.js +55 -1
- package/build/src/_modules/ingest/_services/fam-scan-scheduler.control-service.js +26 -0
- package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +11 -0
- package/build/src/_modules/mcp/_services/fam-read-tool.service.js +2 -0
- package/build/src/_modules/mcp/_services/fam-write-tool.service.js +29 -7
- package/build/src/_modules/retrieval/_collections/fam-cross-table-note.util.js +136 -0
- package/build/src/_modules/retrieval/_collections/fam-lexical-match.util.js +75 -2
- package/build/src/_modules/retrieval/_collections/fam-literal-phrase.util.js +74 -0
- package/build/src/_modules/retrieval/_collections/fam-memory-retention.util.js +70 -0
- package/build/src/_modules/retrieval/_services/fam-memory-reactivation.control-service.js +8 -1
- package/build/src/_modules/retrieval/_services/fam-memory-retention.control-service.js +118 -0
- package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +33 -0
- package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +224 -5
- package/build/src/_modules/retrieval/index.js +6 -1
- package/build/src/_routes/server/api/api.controller.js +77 -15
- package/package.json +8 -2
- package/build/src/_integration-tests/_helpers/fam-integration-test-setup.util.js +0 -105
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MEMÓRIA-RETENCIÓ — az alvó beszélgetés-törmelék nyugdíjazásának döntési logikája (2026-09-02).
|
|
4
|
+
*
|
|
5
|
+
* **A probléma (mérve).** Az aktív `memory` tár 20,2%-a (8 717 bejegyzés) nyers user-prompt
|
|
6
|
+
* (`kind: user_prompt` / `tags: [temporary_context]`) — egy egyszeri, 2026-06-os claude-mem import
|
|
7
|
+
* hagyatéka (azóta 4 új keletkezett, tehát NINCS élő csap). **98,2%-uk soha nem lett felidézve**, mégis
|
|
8
|
+
* teljes súllyal versenyeznek a gondozott jegyzetekkel a nyers koszinusz-rangsorban — és mivel rövidek
|
|
9
|
+
* és tematikusak, gyakran nyernek is.
|
|
10
|
+
*
|
|
11
|
+
* **A szabály (v2, owner: „javítsd, amit kell" — 2026-09-02):** a törmelék-osztály 30+ nap után nyugdíjba
|
|
12
|
+
* vonul, a felidézés-számtól FÜGGETLENÜL.
|
|
13
|
+
*
|
|
14
|
+
* **Miért nem véd a `recallCount` ezen az osztályon — mérve.** Az első változat a „amit többet használtunk,
|
|
15
|
+
* az tovább él" elv szerint kivételezett a valaha felidézettekkel (164 túlélő). Az audit ezt megbuktatta:
|
|
16
|
+
* a legtöbbet „felidézett" túlélők **gépi zaj** voltak — `<task-notification>` és `<ide_opened_file>`
|
|
17
|
+
* XML-blokkok, recall=281-gyel a csúcson (67/164 tisztán ilyen; a maradék átirat-üledék: „Látod", „Azt",
|
|
18
|
+
* build-logok). A felidézés AUTOMATIKUS (minden visszaadott találat recallCount++-t kap), így ezen az
|
|
19
|
+
* osztályon a szám a RANGSOR-KITETTSÉGET méri, nem a használatot — és öngerjesztő: ami magasra rangsorol,
|
|
20
|
+
* azt a visszaadás „felidézi", amitől védetté válik. Az owner elve a MEMÓRIÁKRA vonatkozik; a nyers
|
|
21
|
+
* átirat-törmelék nem az. A nyugdíj ettől még NEM törlés: minden hidegen kereshető marad, és
|
|
22
|
+
* visszafordítható — a genuinly hasznos darab a cold-search-ben él, vagy gondozott bejegyzésként újraírható.
|
|
23
|
+
*
|
|
24
|
+
* Ugyanezen mérés miatt a REAKTIVÁCIÓ is kihagyja ezt az osztályt (`selectMemoryIds` szűrője) — a hurok
|
|
25
|
+
* motorja leállt: a törmelék többé nem tud „használtnak" látszani attól, hogy előkerül.
|
|
26
|
+
*
|
|
27
|
+
* **A nyugdíj NEM törlés:** `superseded: true` + `retiredAt` — a tartalom és a vektor a poolban marad,
|
|
28
|
+
* a `memory.supersededPenalty` (0.25) süllyeszti, a cold-search megtalálja. Visszafordítható.
|
|
29
|
+
*
|
|
30
|
+
* **Hatókör-korlátok (mind mért indokkal):**
|
|
31
|
+
* - CSAK a törmelék-osztály (`user_prompt` / `temporary_context`) — a `session-observation` NEM az:
|
|
32
|
+
* 24%-a felidézett és ma is aktívan nő; a `memory_entry` a gondozott törzs.
|
|
33
|
+
* - CSAK a 30+ naposak (`memory.retireTemporaryAfterDays`) — a friss kontextus a session alatt még
|
|
34
|
+
* ténylegesen kontextus; utána üledék.
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.FAM_MemoryRetention_Util = void 0;
|
|
38
|
+
/** A törmelék-osztály definíciója — UGYANAZ, amit a kereszt-tár szonda kizár (egy definíció, két felhasználás). */
|
|
39
|
+
const DEBRIS_KINDS = ['user_prompt'];
|
|
40
|
+
const DEBRIS_TAGS = ['temporary_context'];
|
|
41
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
42
|
+
class FAM_MemoryRetention_Util {
|
|
43
|
+
/** Törmelék-osztályú-e a bejegyzés (nyers prompt / átmeneti kontextus). */
|
|
44
|
+
static isDebris(candidate) {
|
|
45
|
+
if (candidate.kind && DEBRIS_KINDS.includes(candidate.kind)) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return (candidate.tags ?? []).some((tag) => DEBRIS_TAGS.includes(tag));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A döntés egyetlen bejegyzésre. A sorrend a VÉDELMI sorrend: előbb a kizáró okok (nem törmelék /
|
|
52
|
+
* már nyugdíjas / túl friss), és csak ha egyik sem áll, akkor `retire`. A `recallCount` ITT SZÁNDÉKOSAN
|
|
53
|
+
* nem véd — az indoklás (mért öngerjesztő hurok) a fájl fejlécében.
|
|
54
|
+
*/
|
|
55
|
+
static decide(candidate, set) {
|
|
56
|
+
if (!FAM_MemoryRetention_Util.isDebris(candidate)) {
|
|
57
|
+
return 'keep-not-debris';
|
|
58
|
+
}
|
|
59
|
+
if (candidate.superseded === true) {
|
|
60
|
+
return 'keep-already-superseded';
|
|
61
|
+
}
|
|
62
|
+
const createdAt = typeof candidate.createdAt === 'number' ? candidate.createdAt : Number.NaN;
|
|
63
|
+
// Datum nelkuli bejegyzest NEM nyugdijazunk — a hianyzo teny nem bizonyitek az oregsegre.
|
|
64
|
+
if (!Number.isFinite(createdAt) || set.now - createdAt < set.retireAfterDays * DAY_MS) {
|
|
65
|
+
return 'keep-too-young';
|
|
66
|
+
}
|
|
67
|
+
return 'retire';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.FAM_MemoryRetention_Util = FAM_MemoryRetention_Util;
|
|
@@ -7,6 +7,7 @@ const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
|
7
7
|
const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
|
|
8
8
|
const embedding_1 = require("../../embedding");
|
|
9
9
|
const fam_memory_data_model_1 = require("../../../_models/data-models/fam-memory.data-model");
|
|
10
|
+
const fam_memory_retention_util_1 = require("../_collections/fam-memory-retention.util");
|
|
10
11
|
/**
|
|
11
12
|
* `FAM_MemoryReactivation_ControlService` (dsgn-013 §4, MAM F2) — a memória FELIDÉZÉS-megerősítése. Amikor a `read`
|
|
12
13
|
* ténylegesen VISSZAAD egy `memory`-hitet (a felhasználó/agent felidézte), az „új energiát kap": `lastRecalledAt = now`,
|
|
@@ -55,7 +56,13 @@ class FAM_MemoryReactivation_ControlService {
|
|
|
55
56
|
*/
|
|
56
57
|
static selectMemoryIds(hits) {
|
|
57
58
|
const ids = hits
|
|
58
|
-
.filter((hit) => hit.table === fam_table_type_enum_1.FAM_Table.memory && typeof hit.id === 'string' && hit.id.length > 0
|
|
59
|
+
.filter((hit) => hit.table === fam_table_type_enum_1.FAM_Table.memory && typeof hit.id === 'string' && hit.id.length > 0
|
|
60
|
+
// A TORMELEK-osztaly (user_prompt / temporary_context) NEM kap felidezes-megerositest
|
|
61
|
+
// (2026-09-02, mert ongerjeszto hurok): a rovid atirat-toredek magasra rangsorol -> a
|
|
62
|
+
// visszaadas automatikus recallCount++-t adott -> "hasznaltnak" latszott es a retencio
|
|
63
|
+
// vedte. A csucson recall=281 egy <task-notification> XML-blokkon. A szamlalo ezen az
|
|
64
|
+
// osztalyon a rangsor-kitettseget merte, nem a hasznalatot — a motor itt all le.
|
|
65
|
+
&& !fam_memory_retention_util_1.FAM_MemoryRetention_Util.isDebris({ kind: hit.kind, tags: hit.tags }))
|
|
59
66
|
.map((hit) => hit.id);
|
|
60
67
|
return Array.from(new Set(ids));
|
|
61
68
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_MemoryRetention_ControlService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const mongoose_1 = tslib_1.__importDefault(require("mongoose"));
|
|
6
|
+
const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
7
|
+
const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
|
|
8
|
+
const embedding_1 = require("../../embedding");
|
|
9
|
+
const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
|
|
10
|
+
const fam_memory_retention_util_1 = require("../_collections/fam-memory-retention.util");
|
|
11
|
+
/**
|
|
12
|
+
* MEMÓRIA-RETENCIÓ (2026-09-02) — az alvó beszélgetés-törmelék heti nyugdíjazása. A döntési logika és a
|
|
13
|
+
* teljes indoklás a pure utilban (`fam-memory-retention.util.ts`); ez a réteg a Mongo-munka.
|
|
14
|
+
*
|
|
15
|
+
* A nyugdíj = `superseded: true` + `retiredAt` — NEM törlés: a tartalom és a vektor a poolban marad
|
|
16
|
+
* (a `memory.supersededPenalty` süllyeszti, a cold-search megtalálja). A pool-hoz ezért NEM nyúlunk
|
|
17
|
+
* (nincs `removeVector`) — pontosan úgy, ahogy a tartalom-leváltásos `superseded` ág sem teszi.
|
|
18
|
+
*
|
|
19
|
+
* **Heti automatika:** a scan-scheduler a heti re-scan UTÁN hívja (`memory.retireTemporaryEnabled`,
|
|
20
|
+
* default true) — a retenció lényege az automatizmus, különben a törmelék sosem vonul nyugdíjba.
|
|
21
|
+
*/
|
|
22
|
+
class FAM_MemoryRetention_ControlService {
|
|
23
|
+
static _instance;
|
|
24
|
+
static getInstance() {
|
|
25
|
+
if (!FAM_MemoryRetention_ControlService._instance) {
|
|
26
|
+
FAM_MemoryRetention_ControlService._instance = new FAM_MemoryRetention_ControlService();
|
|
27
|
+
}
|
|
28
|
+
return FAM_MemoryRetention_ControlService._instance;
|
|
29
|
+
}
|
|
30
|
+
/** A retenció futtatása — `dryRun` esetén CSAK mér, semmit nem ír. */
|
|
31
|
+
async retire(input = {}) {
|
|
32
|
+
const dryRun = input.dryRun !== false;
|
|
33
|
+
const retireAfterDays = typeof input.retireAfterDays === 'number' && input.retireAfterDays > 0
|
|
34
|
+
? Math.floor(input.retireAfterDays)
|
|
35
|
+
: await this.resolveRetireAfterDays();
|
|
36
|
+
const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(fam_table_type_enum_1.FAM_Table.memory);
|
|
37
|
+
if (!registryEntry) {
|
|
38
|
+
throw new Error('A memory tár nincs a store-registry-ben — a retenció nem futtatható.');
|
|
39
|
+
}
|
|
40
|
+
// A példányosítás regisztrálja a mongoose-modellt (a raw collection eléréséhez) — a bevett minta.
|
|
41
|
+
new embedding_1.FAM_Entry_DataService({ dataParams: registryEntry.dataParams, issuer: 'memory-retention' });
|
|
42
|
+
const collection = mongoose_1.default.models[registryEntry.dataParams.dataName]?.collection;
|
|
43
|
+
if (!collection) {
|
|
44
|
+
throw new Error('A memory collection nem elérhető — a retenció nem futtatható.');
|
|
45
|
+
}
|
|
46
|
+
// CSAK a törmelék-osztály kerül a jelölt-halmazba (a Mongo szűr, nem mi töltünk fölöslegesen) —
|
|
47
|
+
// a szűkítés a pure util definícióját tükrözi; a végső döntést bejegyzésenként a util hozza.
|
|
48
|
+
const raw = await collection.find({
|
|
49
|
+
_deleted: null,
|
|
50
|
+
$or: [{ kind: 'user_prompt' }, { tags: 'temporary_context' }],
|
|
51
|
+
}, { projection: { kind: 1, tags: 1, recallCount: 1, superseded: 1, __created: 1 } }).toArray();
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const toRetire = [];
|
|
54
|
+
let keptTooYoung = 0;
|
|
55
|
+
let alreadySuperseded = 0;
|
|
56
|
+
for (const row of raw) {
|
|
57
|
+
const candidate = {
|
|
58
|
+
id: String(row._id),
|
|
59
|
+
kind: row.kind,
|
|
60
|
+
tags: row.tags,
|
|
61
|
+
recallCount: row.recallCount,
|
|
62
|
+
superseded: row.superseded,
|
|
63
|
+
createdAt: row.__created instanceof Date ? row.__created.getTime() : undefined,
|
|
64
|
+
};
|
|
65
|
+
const verdict = fam_memory_retention_util_1.FAM_MemoryRetention_Util.decide(candidate, { now: now, retireAfterDays: retireAfterDays });
|
|
66
|
+
if (verdict === 'retire') {
|
|
67
|
+
toRetire.push(row._id);
|
|
68
|
+
}
|
|
69
|
+
else if (verdict === 'keep-too-young') {
|
|
70
|
+
keptTooYoung++;
|
|
71
|
+
}
|
|
72
|
+
else if (verdict === 'keep-already-superseded') {
|
|
73
|
+
alreadySuperseded++;
|
|
74
|
+
}
|
|
75
|
+
// `keep-not-debris` a Mongo-szűrő miatt nem fordulhat elő — ha mégis, az csendben kimarad
|
|
76
|
+
// a számlálókból, és a `scanned - összeg` eltérés jelzi a szűrő-drift-et.
|
|
77
|
+
}
|
|
78
|
+
if (!dryRun && toRetire.length) {
|
|
79
|
+
await collection.updateMany({ _id: { $in: toRetire } }, { $set: { superseded: true, retiredAt: now } });
|
|
80
|
+
fsm_dynamo_1.DyFM_Log.log(`[memory-retention] ${toRetire.length} alvo tormelek-bejegyzes nyugdijazva `
|
|
81
|
+
+ `(superseded+retiredAt; ${keptTooYoung} meg fiatal — a recallCount ezen az osztalyon nem ved, `
|
|
82
|
+
+ 'mert ongerjeszto rangsor-kitettseget mert).');
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
operation: 'retire_temporary_memories',
|
|
86
|
+
dryRun: dryRun,
|
|
87
|
+
retireAfterDays: retireAfterDays,
|
|
88
|
+
scanned: raw.length,
|
|
89
|
+
retired: toRetire.length,
|
|
90
|
+
keptTooYoung: keptTooYoung,
|
|
91
|
+
alreadySuperseded: alreadySuperseded,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** A `memory.retireTemporaryAfterDays` feloldása (hiba/hiány → 30 — a retenció sosem bukik configon). */
|
|
95
|
+
async resolveRetireAfterDays() {
|
|
96
|
+
try {
|
|
97
|
+
const value = (await config_control_service_1.FAM_Config_ControlService.getInstance()
|
|
98
|
+
.resolve('memory.retireTemporaryAfterDays', { table: fam_table_type_enum_1.FAM_Table.memory })).value;
|
|
99
|
+
const num = typeof value === 'number' ? value : Number(value);
|
|
100
|
+
return Number.isFinite(num) && num > 0 ? Math.floor(num) : 30;
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return 30;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** Engedélyezett-e a heti automatika (`memory.retireTemporaryEnabled`, default true). */
|
|
107
|
+
async isAutoEnabled() {
|
|
108
|
+
try {
|
|
109
|
+
const value = (await config_control_service_1.FAM_Config_ControlService.getInstance()
|
|
110
|
+
.resolve('memory.retireTemporaryEnabled', { table: fam_table_type_enum_1.FAM_Table.memory })).value;
|
|
111
|
+
return value !== false;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.FAM_MemoryRetention_ControlService = FAM_MemoryRetention_ControlService;
|
|
@@ -56,6 +56,39 @@ class FAM_RetrievalCandidate_DataService {
|
|
|
56
56
|
filter.referenceCodes = { $in: codes };
|
|
57
57
|
return this.findActiveCandidates(registryEntry, filter);
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* SZÓ SZERINTI IDÉZET-MENTÉS (2026-09-02): a `phrase`-t szó szerint tartalmazó aktív entry-k.
|
|
61
|
+
*
|
|
62
|
+
* **Miért kell:** mérve, a bemásolt hibaüzenetet hordozó chunk BE SEM KERÜL a vektor-jelöltek közé
|
|
63
|
+
* (60 találatból 0 tartalmazta a keresett stringet) — ez recall-hiba, amit rangsorolással nem lehet
|
|
64
|
+
* javítani. A minta a FEAT-003 kód-expanzió: determinisztikus, pontos egyezés a vektor-úton KÍVÜL.
|
|
65
|
+
*
|
|
66
|
+
* **MÉRET-KAPU (kötelező):** a `content` substring-keresés NEM indexelhető → collection-scan. Mérve:
|
|
67
|
+
* `rules` 296 ms · `coding_patterns` 504 ms · `knowledge` 2,1 s · `memory` 4,9 s · `documents` 13,1 s ·
|
|
68
|
+
* `codebase` 45,2 s. A `maxDocs` fölötti tár **kihagyva** (üres lista) — a mentés SOHA nem tehet egy
|
|
69
|
+
* olvasást másodperces műveletté. A darabszám `estimatedDocumentCount` (collection-metaadat, ~0 ms).
|
|
70
|
+
*/
|
|
71
|
+
async loadByLiteralPhrase(input, regexSource, set) {
|
|
72
|
+
const registryEntry = embedding_1.FAM_StoreRegistry_Util.getEntry(input.table);
|
|
73
|
+
if (!registryEntry || !regexSource) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
const model = mongoose_1.default.models[registryEntry.dataParams.dataName];
|
|
77
|
+
if (!model) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
80
|
+
const size = await model.collection.estimatedDocumentCount();
|
|
81
|
+
if (size > set.maxDocs) {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
const filter = this.buildFilter(input, registryEntry);
|
|
85
|
+
filter.content = { $regex: regexSource, $options: 'i' };
|
|
86
|
+
const rows = await model.collection
|
|
87
|
+
.find({ ...filter, _deleted: null }, { projection: { contentVector: 0 } })
|
|
88
|
+
.limit(set.limit)
|
|
89
|
+
.toArray();
|
|
90
|
+
return rows;
|
|
91
|
+
}
|
|
59
92
|
/**
|
|
60
93
|
* A topK-hit metaadat-betöltése `_id`-listából (az UNSCOPED read-fast-path-hez, SP-5.x perf). A főpool-
|
|
61
94
|
* keresés UTÁN CSAK a tényleges hit-eket tölti (a hit-row + weight + recency összefűzéséhez), NEM mind a
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.FAM_Retrieval_ControlService = exports.MAX_PAGE_SIZE = void 0;
|
|
4
|
+
const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
5
|
+
const fam_cross_table_note_util_1 = require("../_collections/fam-cross-table-note.util");
|
|
4
6
|
const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
|
|
5
7
|
const fam_reference_code_util_1 = require("../../../_collections/fam-reference-code.util");
|
|
6
8
|
const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
|
|
@@ -10,6 +12,7 @@ const fam_retrieval_candidate_data_service_1 = require("./fam-retrieval-candidat
|
|
|
10
12
|
const fam_retrieval_suggestions_util_1 = require("./fam-retrieval-suggestions.util");
|
|
11
13
|
const fam_memory_activation_util_1 = require("../_collections/fam-memory-activation.util");
|
|
12
14
|
const fam_lexical_match_util_1 = require("../_collections/fam-lexical-match.util");
|
|
15
|
+
const fam_literal_phrase_util_1 = require("../_collections/fam-literal-phrase.util");
|
|
13
16
|
const fam_result_diversity_util_1 = require("../_collections/fam-result-diversity.util");
|
|
14
17
|
const fam_memory_reactivation_control_service_1 = require("./fam-memory-reactivation.control-service");
|
|
15
18
|
const fam_memory_cold_search_control_service_1 = require("./fam-memory-cold-search.control-service");
|
|
@@ -98,7 +101,12 @@ class FAM_Retrieval_ControlService {
|
|
|
98
101
|
// FEAT-006 context-injection deduper: a hívó által MÁR injektált `_id`-k kizárása (a minScore/topK ELŐTT,
|
|
99
102
|
// hogy ne foglaljanak helyet) — egy session ne kapja vissza ugyanazt a kontextust kétszer. A kizárás a
|
|
100
103
|
// kód-exact-re IS vonatkozik (ha a session már birtokolja, nem kell újra, F3-1 ellenére sem).
|
|
101
|
-
|
|
104
|
+
// ④c SZÓ SZERINTI IDÉZET-MENTÉS (2026-09-02): ha a query idézet-szerű, a pontosan egyező entry-k a
|
|
105
|
+
// vektor-úton KÍVÜL is bekerülnek. Mérve: a bemásolt hibaüzenet chunkja BE SEM KERÜLT a jelöltek
|
|
106
|
+
// közé (60/0), tehát ez recall-hiba — rangsorolással nem javítható. Ugyanaz a minta, mint a
|
|
107
|
+
// FEAT-003 kód-expanzió; a már bent lévő hitek NEM változnak, csak a hiányzók egészülnek ki.
|
|
108
|
+
const rescuedHits = await this.applyLiteralRescue({ query, config, scopeExpand, hits: mergedHits, includeContent });
|
|
109
|
+
const visibleHits = this.excludeSeen(rescuedHits, query.excludeIds);
|
|
102
110
|
// ⑤ weight-szorzó MÁR a hit-eken (finalScore). ⑥ minScore-küszöb → relevantSet — DE F3-2: a kód-exact
|
|
103
111
|
// citáció (`matchedCodes` nem-üres) BYPASS-olja a minScore-t (a determinisztikusan citált kód eleme
|
|
104
112
|
// releváns a score-tól függetlenül; a `weight≤0` továbbra is kizárt, mert az már a merge ELŐTT kiesik).
|
|
@@ -112,11 +120,108 @@ class FAM_Retrieval_ControlService {
|
|
|
112
120
|
const dedupedSet = FAM_Retrieval_ControlService.dedupeByContentHash(relevantSet);
|
|
113
121
|
// ⑧ topK-vágás + dense-detektálás + suggestions.
|
|
114
122
|
const result = this.buildResult({ query, relevantSet: dedupedSet, config, scopeExpand, detectedCodes });
|
|
123
|
+
// ⑨ KERESZT-TÁR „mellesleg"-javaslat (2026-09-01): a `hits` MÁR KÉSZ, és NEM változik tőle. Csak akkor
|
|
124
|
+
// fut, ha a hívó NEM kérte a `memory`/`knowledge` tárat (különben már benne lenne a listában).
|
|
125
|
+
const probeOutcome = await this.probeCrossTable({ query, queryVector, scopeExpand, config, result });
|
|
126
|
+
if (probeOutcome?.note) {
|
|
127
|
+
result.crossTableNote = probeOutcome.note;
|
|
128
|
+
}
|
|
129
|
+
if (query.includeCrossTableProbe && probeOutcome?.debug) {
|
|
130
|
+
result.crossTableProbe = probeOutcome.debug;
|
|
131
|
+
}
|
|
115
132
|
// dsgn-013 §4 (MAM F2) — FELIDÉZÉS-megerősítés: a ténylegesen VISSZAADOTT `memory`-hitek „új energiát kapnak"
|
|
116
133
|
// (lastRecalledAt=now, recallCount++, dormant=false). ASYNC fire-and-forget — NEM blokkolja a read választ.
|
|
117
|
-
|
|
134
|
+
if (!query.skipReactivation) {
|
|
135
|
+
this.reactivateReturnedMemoryHits(result.hits, config);
|
|
136
|
+
}
|
|
118
137
|
return result;
|
|
119
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* A kereszt-tár szonda: EGY olcsó keresés a `memory` + `knowledge` táron, hogy kiderüljön, van-e ott
|
|
141
|
+
* ÉRDEMBEN erősebb találat. A döntés a tiszta `FAM_CrossTableNote_Util`-ban van (ott áll a kalibráció
|
|
142
|
+
* és a korlátok indoklása is); ez a metódus csak a jelölteket szerzi be.
|
|
143
|
+
*
|
|
144
|
+
* **Nem fut**, ha ki van kapcsolva, ha a küszöb 0, vagy ha a hívó ELEVE kérte ezeket a tárakat — akkor
|
|
145
|
+
* a találat már a listában van, és egy „mellesleg ott is van" mondat csak zaj lenne.
|
|
146
|
+
*
|
|
147
|
+
* **Hibatűrő:** a szonda MELLÉK-szolgáltatás. Ha bármi elromlik benne, a read válasza attól még teljes
|
|
148
|
+
* — némán elhagyjuk a jegyzetet, nem buktatjuk a hívást.
|
|
149
|
+
*/
|
|
150
|
+
async probeCrossTable(set) {
|
|
151
|
+
// A DIAGNOSZTIKA kérése önmagában is ok a futásra: enélkül a kikapcsolt szonda mérhetetlen lenne.
|
|
152
|
+
if ((!set.config.crossTableNoteEnabled || !(set.config.crossTableNoteMinDelta > 0))
|
|
153
|
+
&& !set.query.includeCrossTableProbe) {
|
|
154
|
+
return undefined;
|
|
155
|
+
}
|
|
156
|
+
const asked = new Set(set.query.tables);
|
|
157
|
+
const probeTables = set.config.crossTableNoteProbeTables
|
|
158
|
+
.filter((table) => !asked.has(table));
|
|
159
|
+
if (!probeTables.length) {
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
try {
|
|
163
|
+
// A tárak szondázása PÁRHUZAMOS. Mérve: sorosan +713 ms (+68,6%) minden read-en, ami egy
|
|
164
|
+
// 6,8%-os megszólalási arányhoz mérve elfogadhatatlan ár; a két keresés független egymástól.
|
|
165
|
+
const perTable = await Promise.all(probeTables.map((table) => this.probeTable(table, set)));
|
|
166
|
+
const candidates = perTable.flat();
|
|
167
|
+
// Az összevetés alapja a NYERS koszinusz — a `finalScore` tár-függő szorzókat hordoz (weight, decay,
|
|
168
|
+
// projekt-súly), így két tár között nem összemérhető.
|
|
169
|
+
const primaryTopScore = set.result.hits.reduce((top, hit) => (hit.score > top ? hit.score : top), 0);
|
|
170
|
+
// A JEGYZET csak akkor keletkezik, ha a funkció TÉNYLEGESEN be van kapcsolva. A diagnosztika
|
|
171
|
+
// (`includeCrossTableProbe`) megengedi a szonda LEFUTÁSÁT kikapcsolt állapotban is — de egy
|
|
172
|
+
// mérő-kapcsoló SOHA nem hozhat vissza egy kikapcsolt funkciót a válaszba.
|
|
173
|
+
const enabled = set.config.crossTableNoteEnabled && set.config.crossTableNoteMinDelta > 0;
|
|
174
|
+
const note = enabled ? fam_cross_table_note_util_1.FAM_CrossTableNote_Util.decide({
|
|
175
|
+
primaryTopScore: primaryTopScore,
|
|
176
|
+
candidates: candidates,
|
|
177
|
+
minDelta: set.config.crossTableNoteMinDelta,
|
|
178
|
+
minScore: set.config.crossTableNoteMinScore,
|
|
179
|
+
}) : undefined;
|
|
180
|
+
return { note: note, debug: fam_cross_table_note_util_1.FAM_CrossTableNote_Util.describeProbe(primaryTopScore, candidates, Boolean(note)) };
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
fsm_dynamo_1.DyFM_Log.warn(`[read] a kereszt-tar szonda hibara futott, a jegyzet elmarad (a valasz teljes): ${String(error)}`);
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* EGY szonda-tár lekérdezése — a jelöltekké alakított találatokkal.
|
|
189
|
+
*
|
|
190
|
+
* A configot a SZONDÁZOTT tárra oldjuk fel, NEM az elsődlegesre. Ez nem finomhangolás: a `memory`
|
|
191
|
+
* rangsorát a saját decay-/superseded-/cold-search-beállításai alakítják, és ezek per-tár defaultokkal
|
|
192
|
+
* bírnak. Az elsődleges tár configjával szondázva MÁS jelölt-halmazt kapunk, mint amit ugyanaz a kérdés
|
|
193
|
+
* egy közvetlen `memory`-kereséssel adna — a mérés és a működés más eredményre jut, és a kalibráció
|
|
194
|
+
* olyan számra épülne, ami sehol nem érvényes. (Mérve: 5/44 vs 2/44 megszólalás ugyanazon a 44 eseten.)
|
|
195
|
+
*/
|
|
196
|
+
async probeTable(table, set) {
|
|
197
|
+
const tableConfig = await this.resolveReadConfig(table, set.query);
|
|
198
|
+
// A szonda SAJÁT, kicsi topK-val fut — nem második találati listát épít, csak eldönti, van-e erősebb.
|
|
199
|
+
// A cold-search a KÖZVETLEN memória-kérdések dormant-mentőöve; egy mellék-jegyzet szondájának a
|
|
200
|
+
// forró pool elég — a superseded/dormant jelöltet úgyis kizárnánk. A szonda 3-as topK-jánál a
|
|
201
|
+
// `strongHotHits < topK` kapu szinte mindig nyitna, és a dormant-Mongo-munka a +514 ms része volt.
|
|
202
|
+
const probeConfig = {
|
|
203
|
+
...tableConfig, topK: set.config.crossTableNoteTopK, minScore: 0,
|
|
204
|
+
memoryColdSearchEnabled: false,
|
|
205
|
+
};
|
|
206
|
+
const hits = await this.searchTable({
|
|
207
|
+
table: table, query: set.query, queryVector: set.queryVector,
|
|
208
|
+
scopeExpand: set.scopeExpand, config: probeConfig, includeContent: false,
|
|
209
|
+
// 4× fejtér a superseded/törmelék/`weight<=0` szűrésnek — 500 helyett 12 betöltött entry.
|
|
210
|
+
vectorTopK: Math.max(12, set.config.crossTableNoteTopK * 4),
|
|
211
|
+
});
|
|
212
|
+
return hits.slice(0, set.config.crossTableNoteTopK).map((hit) => {
|
|
213
|
+
// A `kind`/`tags`/`scope` NEM dekoráció: az MCP-n ÍRT memória-bejegyzésnek nincs fájl-útja
|
|
214
|
+
// (mérve: 21 jelöltből 16-nak), és ott a nyers ObjectId semmit nem mond a hívónak.
|
|
215
|
+
const leafScope = hit.scopePath?.[hit.scopePath.length - 1];
|
|
216
|
+
return {
|
|
217
|
+
id: hit.id, table: hit.table, score: hit.score,
|
|
218
|
+
sourceFilePath: hit.sourceFilePath, headingPath: hit.headingPath,
|
|
219
|
+
kind: hit.kind, tags: hit.tags,
|
|
220
|
+
scope: leafScope?.canonicalName,
|
|
221
|
+
superseded: hit.superseded,
|
|
222
|
+
};
|
|
223
|
+
});
|
|
224
|
+
}
|
|
120
225
|
/**
|
|
121
226
|
* A visszaadott top-K `memory`-hitek async felidézés-megerősítése (dsgn-013 §4). Fire-and-forget (void): a read
|
|
122
227
|
* már elkészült, a felidézés-frissítés best-effort. Csak a `memory`-táras, `_id`-vel bíró hitre; üres → no-op.
|
|
@@ -189,6 +294,57 @@ class FAM_Retrieval_ControlService {
|
|
|
189
294
|
}
|
|
190
295
|
return Array.from(byId.values());
|
|
191
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* SZÓ SZERINTI IDÉZET-MENTÉS. A `FAM_LiteralPhrase_Util` dönti el, hogy a query idézet-szerű-e (a
|
|
299
|
+
* kritériumok és a mért indoklás ott); ha igen, a pontosan egyező entry-ket a data-service hozza be a
|
|
300
|
+
* MÉRET-KAPU betartásával. A már meglévő hitek ÉRINTETLENEK — a mentés csak kiegészít.
|
|
301
|
+
*
|
|
302
|
+
* A mentett entry `score`-ja NEM 1,0 (az a kód-expanzió exact-citation szintje): egy szó szerinti
|
|
303
|
+
* mondat-egyezés erős, de nem azonos egy explicit azonosító-citációval. A `LITERAL_RESCUE_SCORE` a
|
|
304
|
+
* `relevanceFloor` fölé teszi őket, a rangsorban pedig a szokásos szorzók (weight, lexikai boost —
|
|
305
|
+
* beleértve az idézet-bónuszt) döntenek. Így a mentés **felszínre hoz**, de nem **tol félre**.
|
|
306
|
+
*
|
|
307
|
+
* Hibatűrő: a mentés MELLÉK-szolgáltatás — ha elszáll, a válasz a vektor-találatokkal teljes.
|
|
308
|
+
*/
|
|
309
|
+
async applyLiteralRescue(set) {
|
|
310
|
+
if (!set.config.literalRescueEnabled) {
|
|
311
|
+
return set.hits;
|
|
312
|
+
}
|
|
313
|
+
const literal = fam_literal_phrase_util_1.FAM_LiteralPhrase_Util.extract(set.query.query);
|
|
314
|
+
if (!literal) {
|
|
315
|
+
return set.hits;
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
const byId = new Map();
|
|
319
|
+
for (const hit of set.hits) {
|
|
320
|
+
byId.set(hit.id, hit);
|
|
321
|
+
}
|
|
322
|
+
const regexSource = fam_literal_phrase_util_1.FAM_LiteralPhrase_Util.toRegexSource(literal.phrase);
|
|
323
|
+
for (const table of Array.from(new Set(set.query.tables))) {
|
|
324
|
+
const entries = await new fam_retrieval_candidate_data_service_1.FAM_RetrievalCandidate_DataService().loadByLiteralPhrase({
|
|
325
|
+
table: table,
|
|
326
|
+
scopeIdSet: set.scopeExpand.scopeIdSet,
|
|
327
|
+
tagFilter: set.query.tagFilter,
|
|
328
|
+
kindFilter: set.query.kindFilter,
|
|
329
|
+
}, regexSource, { maxDocs: set.config.literalRescueMaxDocs, limit: set.config.literalRescueLimit });
|
|
330
|
+
for (const entry of entries) {
|
|
331
|
+
const id = entry._id ? String(entry._id) : undefined;
|
|
332
|
+
const weight = entry.weight ?? 1;
|
|
333
|
+
// A `weight<=0` kizárás KONZISZTENS a vektor-ággal — a mentés nem kerülheti meg a tiltást.
|
|
334
|
+
if (!id || weight <= 0 || byId.has(id)) {
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
byId.set(id, this.toLiteralRescueHit({ entry, id, table, weight, includeContent: set.includeContent }));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return Array.from(byId.values());
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
fsm_dynamo_1.DyFM_Log.warn(`[read] a szo szerinti idezet-mentes hibara futott, a valasz a vektor-talalatokkal `
|
|
344
|
+
+ `teljes: ${String(error)}`);
|
|
345
|
+
return set.hits;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
192
348
|
/**
|
|
193
349
|
* Egy kód-citáló entry beolvasztása a hit-halmazba (FEAT-003). `score=1.0` (exact citation = maximális
|
|
194
350
|
* relevancia), `finalScore=1.0×weight` (a meglévő weight-modellbe illesztve). `weight≤0` → kizárt
|
|
@@ -221,6 +377,37 @@ class FAM_Retrieval_ControlService {
|
|
|
221
377
|
set.byId.set(id, this.toCodeHit({ entry: set.entry, id, table: set.table, weight, matched, includeContent: set.includeContent }));
|
|
222
378
|
}
|
|
223
379
|
/** Egy kód-citáló entry → `FAM_RetrievalHit` (exact citation: `score=1.0`, `matchedCodes` kitöltve). */
|
|
380
|
+
/**
|
|
381
|
+
* A mentett entry `score`-ja. SZÁNDÉKOSAN a `relevanceFloor` (0,60) FÖLÖTT, de a kód-expanzió
|
|
382
|
+
* exact-citation 1,0-ja ALATT: a szó szerinti mondat-egyezés erős jel, de nem azonos egy explicit
|
|
383
|
+
* azonosító-citációval. Így a mentett elem biztosan látszik, de nem söpri le a valódi top-találatokat.
|
|
384
|
+
*/
|
|
385
|
+
static LITERAL_RESCUE_SCORE = 0.75;
|
|
386
|
+
/** Egy szó szerint egyező entry → `FAM_RetrievalHit` (idézet-mentés; a `matchedCodes` üres marad). */
|
|
387
|
+
toLiteralRescueHit(set) {
|
|
388
|
+
const lastModified = set.entry.__lastModified;
|
|
389
|
+
const score = FAM_Retrieval_ControlService.LITERAL_RESCUE_SCORE;
|
|
390
|
+
return {
|
|
391
|
+
id: set.id,
|
|
392
|
+
table: set.table,
|
|
393
|
+
score: score,
|
|
394
|
+
weight: set.weight,
|
|
395
|
+
finalScore: score * set.weight,
|
|
396
|
+
content: set.includeContent ? set.entry.content : undefined,
|
|
397
|
+
contentHash: set.entry.contentHash,
|
|
398
|
+
kind: set.entry.kind,
|
|
399
|
+
tags: set.entry.tags ?? [],
|
|
400
|
+
...this.ruleMeta(set.entry, set.table),
|
|
401
|
+
scopePath: (set.entry.scopePath ?? []),
|
|
402
|
+
source: set.entry.source,
|
|
403
|
+
sourceFilePath: set.entry.sourceFilePath ?? null,
|
|
404
|
+
absolutePath: set.entry.source?.absolutePath ?? null,
|
|
405
|
+
chunkType: set.entry.chunkType,
|
|
406
|
+
headingPath: set.entry.headingPath ?? [],
|
|
407
|
+
position: set.entry.position,
|
|
408
|
+
lastModifiedMs: lastModified ? new Date(lastModified).getTime() : 0,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
224
411
|
toCodeHit(set) {
|
|
225
412
|
const lastModified = set.entry.__lastModified;
|
|
226
413
|
return {
|
|
@@ -276,7 +463,7 @@ class FAM_Retrieval_ControlService {
|
|
|
276
463
|
vectorHits = await embedding_1.FAM_VectorSearch_ControlService.getInstance().search({
|
|
277
464
|
table: set.table,
|
|
278
465
|
queryVector: set.queryVector,
|
|
279
|
-
topK: exports.MAX_PAGE_SIZE,
|
|
466
|
+
topK: set.vectorTopK ?? exports.MAX_PAGE_SIZE,
|
|
280
467
|
});
|
|
281
468
|
if (!vectorHits.length) {
|
|
282
469
|
return [];
|
|
@@ -312,12 +499,15 @@ class FAM_Retrieval_ControlService {
|
|
|
312
499
|
table: set.table,
|
|
313
500
|
queryVector: set.queryVector,
|
|
314
501
|
candidateIds: candidateIds,
|
|
315
|
-
topK: Math.min(candidateIds.length, exports.MAX_PAGE_SIZE),
|
|
502
|
+
topK: Math.min(candidateIds.length, set.vectorTopK ?? exports.MAX_PAGE_SIZE),
|
|
316
503
|
});
|
|
317
504
|
}
|
|
318
505
|
// ⑤ weight-szorzó: finalScore = score × (weight ?? 1); weight ≤ 0 → kizárt (nem kerül a hits közé).
|
|
319
506
|
// Improvement 2: a query lexikai term-jeit EGYSZER tokenizáljuk (a hibrid keyword-boost-hoz a toHit-ban).
|
|
320
507
|
const queryTerms = fam_lexical_match_util_1.FAM_LexicalMatch_Util.tokenize(set.query.query);
|
|
508
|
+
// A szó szerinti idézet-egyezéshez a NYERS, sorrendhelyes token-sorozat kell (stopwordökkel együtt) —
|
|
509
|
+
// EGYSZER számoljuk ki a query-re, nem hit-enként.
|
|
510
|
+
const queryPhraseTokens = fam_lexical_match_util_1.FAM_LexicalMatch_Util.tokenizeSequence(set.query.query);
|
|
321
511
|
const hits = [];
|
|
322
512
|
for (const vectorHit of vectorHits) {
|
|
323
513
|
const entry = byId.get(vectorHit.id);
|
|
@@ -330,6 +520,7 @@ class FAM_Retrieval_ControlService {
|
|
|
330
520
|
}
|
|
331
521
|
hits.push(this.toHit({
|
|
332
522
|
entry, vectorHit, table: set.table, weight, queryTerms: queryTerms,
|
|
523
|
+
queryPhraseTokens: queryPhraseTokens,
|
|
333
524
|
importChunkWeight: set.config.importChunkWeight, importSourceWeight: set.config.importSourceWeight,
|
|
334
525
|
includeContent: set.includeContent, config: set.config, nowMs: Date.now(),
|
|
335
526
|
}));
|
|
@@ -363,6 +554,7 @@ class FAM_Retrieval_ControlService {
|
|
|
363
554
|
});
|
|
364
555
|
const seen = new Set(hits.map((hit) => hit.id));
|
|
365
556
|
const queryTerms = fam_lexical_match_util_1.FAM_LexicalMatch_Util.tokenize(query.query);
|
|
557
|
+
const queryPhraseTokens = fam_lexical_match_util_1.FAM_LexicalMatch_Util.tokenizeSequence(query.query);
|
|
366
558
|
for (const cold of coldHits) {
|
|
367
559
|
const id = cold.entry._id;
|
|
368
560
|
const weight = cold.entry.weight ?? 1;
|
|
@@ -371,6 +563,7 @@ class FAM_Retrieval_ControlService {
|
|
|
371
563
|
}
|
|
372
564
|
hits.push(this.toHit({
|
|
373
565
|
entry: cold.entry, vectorHit: { id: id, score: cold.score }, table: set.table, weight: weight, queryTerms: queryTerms,
|
|
566
|
+
queryPhraseTokens: queryPhraseTokens,
|
|
374
567
|
importChunkWeight: set.config.importChunkWeight, importSourceWeight: set.config.importSourceWeight,
|
|
375
568
|
includeContent: set.includeContent, config: set.config, nowMs: Date.now(),
|
|
376
569
|
}));
|
|
@@ -466,7 +659,7 @@ class FAM_Retrieval_ControlService {
|
|
|
466
659
|
const lexicalScore = set.config.keywordWeight > 0
|
|
467
660
|
? fam_lexical_match_util_1.FAM_LexicalMatch_Util.score(set.queryTerms, {
|
|
468
661
|
content: set.entry.content, headingPath: set.entry.headingPath, sourceFilePath: set.entry.source?.path,
|
|
469
|
-
})
|
|
662
|
+
}, set.queryPhraseTokens)
|
|
470
663
|
: 0;
|
|
471
664
|
const keywordBoost = 1 + set.config.keywordWeight * lexicalScore;
|
|
472
665
|
// PROJEKT-SZINTU szorzo a `.famconfig.json`-bol — SZANDEKOSAN a temperalason KIVUL. A `weight` mezo az
|
|
@@ -727,6 +920,24 @@ class FAM_Retrieval_ControlService {
|
|
|
727
920
|
const memoryFloorWeight = await this.resolveNumber(config, 'memory.floorWeight', ctx, 0.05);
|
|
728
921
|
const memoryMaxFrequencyBoost = await this.resolveNumber(config, 'memory.maxFrequencyBoost', ctx, 2.5);
|
|
729
922
|
const memoryActivationWeight = await this.resolveNumber(config, 'memory.activationWeight', ctx, 0.35);
|
|
923
|
+
// KERESZT-TÁR „mellesleg"-javaslat (2026-09-01) — a `hits`-hez SOHA nem nyúl, csak külön mezőt ad.
|
|
924
|
+
// SZO SZERINTI IDEZET-MENTES (2026-09-02) — recall-javitas, meret-kapuval.
|
|
925
|
+
const literalRescueValue = (await config.resolve('read.literalRescueEnabled', ctx)).value;
|
|
926
|
+
const literalRescueEnabled = literalRescueValue !== false;
|
|
927
|
+
const literalRescueMaxDocs = await this.resolveNumber(config, 'read.literalRescueMaxDocs', ctx, 10000);
|
|
928
|
+
const literalRescueLimit = await this.resolveNumber(config, 'read.literalRescueLimit', ctx, 10);
|
|
929
|
+
const crossTableNoteValue = (await config.resolve('read.crossTableNoteEnabled', ctx)).value;
|
|
930
|
+
const crossTableNoteEnabled = crossTableNoteValue !== false;
|
|
931
|
+
const crossTableNoteMinDelta = await this.resolveNumber(config, 'read.crossTableNoteMinDelta', ctx, 0.025);
|
|
932
|
+
const crossTableNoteMinScore = await this.resolveNumber(config, 'read.crossTableNoteMinScore', ctx, 0.45);
|
|
933
|
+
const crossTableNoteTopK = await this.resolveNumber(config, 'read.crossTableNoteTopK', ctx, 3);
|
|
934
|
+
const probeTablesRaw = (await config.resolve('read.crossTableNoteProbeTables', ctx)).value;
|
|
935
|
+
// Csak az ERVENYES szonda-tarak mennek at (memory/knowledge) — elgepelt config nem tud mas tarat szondazni.
|
|
936
|
+
const crossTableNoteProbeTables = String(typeof probeTablesRaw === 'string' ? probeTablesRaw : 'memory')
|
|
937
|
+
.split(',')
|
|
938
|
+
.map((item) => item.trim())
|
|
939
|
+
.filter((item) => item === fam_table_type_enum_1.FAM_Table.memory || item === fam_table_type_enum_1.FAM_Table.knowledge)
|
|
940
|
+
.map((item) => item);
|
|
730
941
|
const memoryReactivateValue = (await config.resolve('memory.reactivateOnRead', ctx)).value;
|
|
731
942
|
const memoryReactivateOnRead = memoryReactivateValue !== false;
|
|
732
943
|
const memoryColdSearchValue = (await config.resolve('memory.coldSearchEnabled', ctx)).value;
|
|
@@ -736,6 +947,14 @@ class FAM_Retrieval_ControlService {
|
|
|
736
947
|
return {
|
|
737
948
|
topK: Math.min(topKDefault, exports.MAX_PAGE_SIZE),
|
|
738
949
|
minScore: minScore,
|
|
950
|
+
literalRescueEnabled: literalRescueEnabled,
|
|
951
|
+
literalRescueMaxDocs: literalRescueMaxDocs,
|
|
952
|
+
literalRescueLimit: literalRescueLimit,
|
|
953
|
+
crossTableNoteEnabled: crossTableNoteEnabled,
|
|
954
|
+
crossTableNoteMinDelta: crossTableNoteMinDelta,
|
|
955
|
+
crossTableNoteMinScore: crossTableNoteMinScore,
|
|
956
|
+
crossTableNoteTopK: crossTableNoteTopK,
|
|
957
|
+
crossTableNoteProbeTables: crossTableNoteProbeTables.length ? crossTableNoteProbeTables : [fam_table_type_enum_1.FAM_Table.memory],
|
|
739
958
|
relevanceFloor: relevanceFloor,
|
|
740
959
|
importChunkWeight: importChunkWeight,
|
|
741
960
|
importSourceWeight: importSourceWeight,
|
|
@@ -10,11 +10,16 @@
|
|
|
10
10
|
* opcionális relevance-filter = BACKLOG (NEM épül).
|
|
11
11
|
*/
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.FAM_RetrievalSuggestions_Util = exports.FAM_RetrievalCandidate_DataService = exports.FAM_RuleScopeContext_Util = exports.FAM_RulePropagation_Util = exports.FAM_RuleInjection_Util = exports.FAM_RuleInjection_ControlService = exports.FAM_GlobalRule_ControlService = exports.MAX_PAGE_SIZE = exports.FAM_Retrieval_ControlService = void 0;
|
|
13
|
+
exports.FAM_RetrievalSuggestions_Util = exports.FAM_RetrievalCandidate_DataService = exports.FAM_RuleScopeContext_Util = exports.FAM_RulePropagation_Util = exports.FAM_RuleInjection_Util = exports.FAM_RuleInjection_ControlService = exports.FAM_GlobalRule_ControlService = exports.FAM_MemoryRetention_Util = exports.FAM_MemoryRetention_ControlService = exports.MAX_PAGE_SIZE = exports.FAM_Retrieval_ControlService = void 0;
|
|
14
14
|
// SP-5.1/5.2/5.3 — a read-pipeline orchestrátor
|
|
15
15
|
var fam_retrieval_control_service_1 = require("./_services/fam-retrieval.control-service");
|
|
16
16
|
Object.defineProperty(exports, "FAM_Retrieval_ControlService", { enumerable: true, get: function () { return fam_retrieval_control_service_1.FAM_Retrieval_ControlService; } });
|
|
17
17
|
Object.defineProperty(exports, "MAX_PAGE_SIZE", { enumerable: true, get: function () { return fam_retrieval_control_service_1.MAX_PAGE_SIZE; } });
|
|
18
|
+
// Memoria-retencio (2026-09-02): az alvo tormelek heti nyugdijazasa — a dontesi logika a pure utilban.
|
|
19
|
+
var fam_memory_retention_control_service_1 = require("./_services/fam-memory-retention.control-service");
|
|
20
|
+
Object.defineProperty(exports, "FAM_MemoryRetention_ControlService", { enumerable: true, get: function () { return fam_memory_retention_control_service_1.FAM_MemoryRetention_ControlService; } });
|
|
21
|
+
var fam_memory_retention_util_1 = require("./_collections/fam-memory-retention.util");
|
|
22
|
+
Object.defineProperty(exports, "FAM_MemoryRetention_Util", { enumerable: true, get: function () { return fam_memory_retention_util_1.FAM_MemoryRetention_Util; } });
|
|
18
23
|
var fam_global_rule_control_service_1 = require("./_services/fam-global-rule.control-service");
|
|
19
24
|
Object.defineProperty(exports, "FAM_GlobalRule_ControlService", { enumerable: true, get: function () { return fam_global_rule_control_service_1.FAM_GlobalRule_ControlService; } });
|
|
20
25
|
// Rules-Management MP-2 — always-apply rule-injektálás (SessionStart inject + read-reflex) szerver-oldali assembly
|