@kira905/dsh-memory-toolkit 0.1.3 → 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/CHANGELOG.md +92 -0
- package/README.en.md +225 -2
- package/README.md +180 -2
- package/lib/deploy-guard.mjs +463 -275
- package/lib/guard-http.mjs +59 -62
- package/lib/pitfall-form-map.mjs +94 -84
- package/lib/pitfall-score.mjs +262 -185
- package/lib/session-file-name.mjs +116 -100
- package/lib/zstd-frames.mjs +88 -67
- package/memory-db-tools/README.md +29 -1
- package/memory-db-tools/audit-analyze.mjs +1 -1
- package/memory-db-tools/audit-export.mjs +60 -16
- package/memory-db-tools/audit-struct-probe.mjs +29 -8
- package/memory-db-tools/lib/known-devices.mjs +137 -0
- package/memory-engine/README.md +58 -4
- package/memory-engine/bench/bench.mjs +1 -1
- package/memory-engine/cli/band-sentinel.mjs +164 -0
- package/memory-engine/cli/distill-rollback.mjs +112 -44
- package/memory-engine/cli/distill-sessions.mjs +237 -0
- package/memory-engine/cli/distill.mjs +196 -95
- package/memory-engine/cli/doc-index-rebuild.mjs +142 -108
- package/memory-engine/cli/doc-index.mjs +47 -38
- package/memory-engine/cli/domain-tags.mjs +246 -162
- package/memory-engine/cli/layer-migrate.mjs +176 -0
- package/memory-engine/cli/layer-move.mjs +189 -0
- package/memory-engine/cli/pitfall-hook.mjs +136 -70
- package/memory-engine/cli/recall.mjs +243 -110
- package/memory-engine/cli/selfcheck.mjs +93 -44
- package/memory-engine/cli/serve-start.mjs +221 -140
- package/memory-engine/cli/sweep.mjs +83 -44
- package/memory-engine/cli/sync.mjs +93 -57
- package/memory-engine/config/audit-devices.json +6 -0
- package/memory-engine/config/bands.json +7 -0
- package/memory-engine/config/layers.json +7 -0
- package/memory-engine/dict/LICENSE-/350/257/215/345/205/270.md +53 -0
- package/memory-engine/dict/lexicon-config.json +11 -0
- package/memory-engine/dict//350/257/215/345/205/270.txt +109750 -0
- package/memory-engine/dict//350/257/215/351/242/221.txt +20000 -0
- package/memory-engine/lib/band-sentinel.mjs +417 -0
- package/memory-engine/lib/cli-strict.mjs +128 -0
- package/memory-engine/lib/cli-util.mjs +147 -125
- package/memory-engine/lib/config.mjs +83 -96
- package/memory-engine/lib/dirty-ledger.mjs +127 -94
- package/memory-engine/lib/distill-rollback.mjs +173 -133
- package/memory-engine/lib/distill.mjs +878 -585
- package/memory-engine/lib/doc-index-store.mjs +208 -235
- package/memory-engine/lib/doc-index.mjs +351 -350
- package/memory-engine/lib/doc-scan.mjs +178 -156
- package/memory-engine/lib/doc-summarize.mjs +220 -205
- package/memory-engine/lib/domain-tags.mjs +358 -277
- package/memory-engine/lib/embedder.mjs +175 -194
- package/memory-engine/lib/hashes.mjs +22 -7
- package/memory-engine/lib/layer-migrate.mjs +330 -0
- package/memory-engine/lib/layer-move.mjs +307 -0
- package/memory-engine/lib/layers.mjs +191 -0
- package/memory-engine/lib/lexicon-recall.mjs +473 -0
- package/memory-engine/lib/memory-source.mjs +78 -80
- package/memory-engine/lib/pitfall-source.mjs +115 -114
- package/memory-engine/lib/policy-source.mjs +279 -261
- package/memory-engine/lib/recall-log.mjs +285 -0
- package/memory-engine/lib/recall.mjs +565 -240
- package/memory-engine/lib/selfcheck.mjs +142 -106
- package/memory-engine/lib/session-distill.mjs +603 -0
- package/memory-engine/lib/session-source.mjs +421 -0
- package/memory-engine/lib/sweep.mjs +107 -130
- package/memory-engine/lib/sync.mjs +110 -140
- package/memory-engine/lib/tick-hook.mjs +123 -148
- package/memory-engine/lib/vectors-store.mjs +213 -188
- package/memory-engine/test/12-layering.test.mjs +539 -0
- package/memory-engine/test/13-session-distill.test.mjs +616 -0
- package/memory-engine/tools/bridge-smoke.mjs +94 -55
- package/memory-engine/tools/fetch-model.mjs +167 -87
- package/memory-engine/tools/smoke-recall.mjs +94 -38
- package/memory-engine/tools/verify-recall-chain.mjs +413 -0
- package/package.json +1 -1
|
@@ -1,68 +1,104 @@
|
|
|
1
|
-
//
|
|
1
|
+
// 增量同步入口:把数据源里新增/改过的条目编码进独立向量库。
|
|
2
|
+
// 源库只读、向量库只写;--dry-run 时一条都不落盘,只报计划与差异。
|
|
3
|
+
// 换模型 / 换维度 / --full ⇒ 自动转全量重建(清掉该来源的旧向量再编)。
|
|
2
4
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// node cli/sync.mjs --full # 全量重建(换模型/维度也自动触发)
|
|
6
|
-
// node cli/sync.mjs --dry-run # 只算不写(计划与差异)
|
|
7
|
-
// node cli/sync.mjs --json # 机器可读(日志走 stderr)
|
|
8
|
-
// node cli/sync.mjs --stub-embedder # 骨架模式(无模型依赖)
|
|
9
|
-
// node cli/sync.mjs --source pitfall # 同步**坑库**来源(默认 memory;两来源共库、meta 分命名空间)
|
|
10
|
-
// 路径可注入:--memory-db / --vectors-db / --pitfalls-file / --model-dir / --model / --dim / --batch
|
|
5
|
+
// 路径与模型一律可注入:--memory-db / --vectors-db / --pitfalls-file / --model-dir / --model / --dim / --batch
|
|
6
|
+
// 退出码:0 = 跑完(0 条也算跑完)· 1 = 中途失败(进度已保存,可直接续跑)· 2 = 启动即炸。
|
|
11
7
|
import { isMainEntry, parseArgs, makeLogger, openRuntime, num } from '../lib/cli-util.mjs';
|
|
12
8
|
import { runIncrementalSync } from '../lib/sync.mjs';
|
|
13
9
|
|
|
14
|
-
|
|
10
|
+
const OK = 0;
|
|
11
|
+
const FAIL_RUN = 1;
|
|
12
|
+
const CRASH = 2;
|
|
13
|
+
const COLUMNS = 12;
|
|
14
|
+
|
|
15
|
+
/** 统一的启动三件套:原始 flag、是否机器可读、以及分好流的 logger */
|
|
16
|
+
function boot() {
|
|
15
17
|
const { flags } = parseArgs();
|
|
16
18
|
const json = Boolean(flags.json);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
return { flags, json, log: makeLogger({ json, quiet: Boolean(flags.quiet) }) };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** 开一个运行时,交给回调用,出了什么事都把库关掉 */
|
|
23
|
+
async function withRuntime(flags, work) {
|
|
24
|
+
const rt = await openRuntime(flags);
|
|
19
25
|
try {
|
|
20
|
-
|
|
21
|
-
const stats = await runIncrementalSync({
|
|
22
|
-
source: rt.source,
|
|
23
|
-
store: rt.store,
|
|
24
|
-
embedder: rt.embedder,
|
|
25
|
-
modelId: rt.config.modelId,
|
|
26
|
-
dim: rt.config.dim,
|
|
27
|
-
sourceName: rt.sourceName,
|
|
28
|
-
batchSize: num(flags.batch, rt.config.batchSize),
|
|
29
|
-
full: Boolean(flags.full),
|
|
30
|
-
dryRun: Boolean(flags['dry-run']),
|
|
31
|
-
log,
|
|
32
|
-
});
|
|
33
|
-
const out = {
|
|
34
|
-
ok: !stats.error,
|
|
35
|
-
source: rt.sourceName,
|
|
36
|
-
// 兼容字段:默认来源(memory)时语义与旧版一致
|
|
37
|
-
memoryDb: rt.config.memoryDb,
|
|
38
|
-
dataSource: rt.source.dbPath,
|
|
39
|
-
vectorsDb: rt.config.vectorsDb,
|
|
40
|
-
modelId: rt.config.modelId,
|
|
41
|
-
...stats,
|
|
42
|
-
};
|
|
43
|
-
if (json) process.stdout.write(JSON.stringify(out, null, 2) + '\n');
|
|
44
|
-
else {
|
|
45
|
-
log('');
|
|
46
|
-
log('=== 增量同步结果 ===');
|
|
47
|
-
log(`模式 : ${stats.mode}${stats.reasons.length ? `(${stats.reasons.join(' / ')})` : ''}`);
|
|
48
|
-
log(`来源 : ${rt.sourceName}(${rt.source.dbPath})`);
|
|
49
|
-
log(`向量库 : ${rt.config.vectorsDb}`);
|
|
50
|
-
log(`模型 : ${rt.config.modelId}(${rt.embedder.dim} 维)`);
|
|
51
|
-
log(`扫描/编码 : ${stats.scanned} / ${stats.encoded}(同哈希跳过 ${stats.skippedSameHash})`);
|
|
52
|
-
log(`向量条数 : ${stats.vectorsBefore} → ${stats.vectorsAfter}`);
|
|
53
|
-
log(`水位 : ${stats.watermarkStart} → ${stats.watermarkEnd}`);
|
|
54
|
-
log(`耗时 : ${(stats.elapsedMs / 1000).toFixed(2)}s`);
|
|
55
|
-
if (stats.error) log(`❌ 失败 : ${stats.error}(进度已保留,可直接续跑)`);
|
|
56
|
-
else log('✅ 完成');
|
|
57
|
-
}
|
|
58
|
-
process.exitCode = stats.error ? 1 : 0;
|
|
59
|
-
} catch (e) {
|
|
60
|
-
const msg = String(e && e.stack ? e.stack : e);
|
|
61
|
-
if (json) process.stdout.write(JSON.stringify({ ok: false, error: msg }, null, 2) + '\n');
|
|
62
|
-
else log(`❌ ${msg}`);
|
|
63
|
-
process.exitCode = 2;
|
|
26
|
+
return await work(rt);
|
|
64
27
|
} finally {
|
|
65
|
-
|
|
28
|
+
rt.close();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 起来就炸:json 走 stdout,文本走 logger,退出码固定 2 */
|
|
33
|
+
function oops(json, log, err) {
|
|
34
|
+
const detail = String(err?.stack ?? err);
|
|
35
|
+
if (json) process.stdout.write(`${JSON.stringify({ ok: false, error: detail }, null, 2)}\n`);
|
|
36
|
+
else log(`❌ ${detail}`);
|
|
37
|
+
process.exitCode = CRASH;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 中文占两格:按显示宽度补空格,冒号右对齐 */
|
|
41
|
+
function field(text) {
|
|
42
|
+
let used = 0;
|
|
43
|
+
for (const ch of String(text)) used += ch.codePointAt(0) > 0xff ? 2 : 1;
|
|
44
|
+
return String(text) + ' '.repeat(Math.max(0, COLUMNS - used));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function report(log, view, stats) {
|
|
48
|
+
const row = (name, value) => log(`${field(name)}: ${value}`);
|
|
49
|
+
log('');
|
|
50
|
+
log('=== 增量同步结果 ===');
|
|
51
|
+
row('模式', stats.mode + (stats.reasons.length ? `(${stats.reasons.join(' / ')})` : ''));
|
|
52
|
+
row('来源', `${view.source}(${view.dataSource})`);
|
|
53
|
+
row('向量库', view.vectorsDb);
|
|
54
|
+
row('模型', `${view.modelId}(${view.dim} 维)`);
|
|
55
|
+
row('扫描/编码', `${stats.scanned} / ${stats.encoded}(同哈希跳过 ${stats.skippedSameHash})`);
|
|
56
|
+
row('向量条数', `${stats.vectorsBefore} → ${stats.vectorsAfter}`);
|
|
57
|
+
row('水位', `${stats.watermarkStart} → ${stats.watermarkEnd}`);
|
|
58
|
+
row('耗时', `${(stats.elapsedMs / 1000).toFixed(2)}s`);
|
|
59
|
+
if (stats.error) log(`❌ 失败 : ${stats.error}(进度已保留,可直接续跑)`);
|
|
60
|
+
else log('✅ 完成');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function work(rt, flags, log, json) {
|
|
64
|
+
const { config, embedder, source, sourceName, store } = rt;
|
|
65
|
+
const stats = await runIncrementalSync({
|
|
66
|
+
source,
|
|
67
|
+
store,
|
|
68
|
+
embedder,
|
|
69
|
+
modelId: config.modelId,
|
|
70
|
+
dim: config.dim,
|
|
71
|
+
sourceName,
|
|
72
|
+
batchSize: num(flags.batch, config.batchSize),
|
|
73
|
+
full: Boolean(flags.full),
|
|
74
|
+
dryRun: Boolean(flags['dry-run']),
|
|
75
|
+
log,
|
|
76
|
+
});
|
|
77
|
+
const out = {
|
|
78
|
+
ok: !stats.error,
|
|
79
|
+
source: sourceName,
|
|
80
|
+
// 兼容老读法:缺省来源(memory)时与旧字段同义
|
|
81
|
+
memoryDb: config.memoryDb,
|
|
82
|
+
dataSource: source.dbPath,
|
|
83
|
+
vectorsDb: config.vectorsDb,
|
|
84
|
+
modelId: config.modelId,
|
|
85
|
+
...stats,
|
|
86
|
+
};
|
|
87
|
+
if (json) process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
|
|
88
|
+
else {
|
|
89
|
+
report(log, {
|
|
90
|
+
source: sourceName, dataSource: source.dbPath, vectorsDb: config.vectorsDb, modelId: config.modelId, dim: embedder.dim,
|
|
91
|
+
}, stats);
|
|
92
|
+
}
|
|
93
|
+
process.exitCode = stats.error ? FAIL_RUN : OK;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function main() {
|
|
97
|
+
const { flags, json, log } = boot();
|
|
98
|
+
try {
|
|
99
|
+
await withRuntime(flags, (rt) => work(rt, flags, log, json));
|
|
100
|
+
} catch (err) {
|
|
101
|
+
oops(json, log, err);
|
|
66
102
|
}
|
|
67
103
|
}
|
|
68
104
|
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "体检工具的「已知设备名」名单。代码里不写死任何机器名 —— 换机器只改这里,或设环境变量 MEMORY_DB_AUDIT_DEVICES(优先生效)。",
|
|
3
|
+
"_comment_fields": "devices = 设备标【设备:X】里 X 那一段的名单(大小写敏感的子串匹配;数组顺序 = 统计输出里各设备键的顺序)。内置缺省 alpha / beta 只是中性示例名,不代表任何真实机器。",
|
|
4
|
+
"_comment_why": "名字不认识的一律归 OTHER(结构探针会把它列出来让人复核);名单为空或全不合法时退回缺省,并打一行警告。",
|
|
5
|
+
"devices": ["alpha", "beta"]
|
|
6
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "号段表。单机安装只留一条 local 就够了;多机共用一个记忆库时按机器各加一条。",
|
|
3
|
+
"_comment_fields": "host = 这一段的名字(自定);base = 该段起始 id;width = 段宽(所有段共用)。非本机条目要加 remote 才会去读:{ ssh: 别名, node: 远端 node 路径, db: 远端库路径(可省) }。",
|
|
4
|
+
"self": "local",
|
|
5
|
+
"width": 1000000,
|
|
6
|
+
"bands": [{ "host": "local", "base": 2000000 }]
|
|
7
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
本目录下的「词典.txt」不是本项目原创,它来自 jieba 中文分词项目:
|
|
2
|
+
|
|
3
|
+
https://github.com/fxsjy/jieba
|
|
4
|
+
|
|
5
|
+
原文件是 jieba 的 extra_dict/dict.txt.small(README 里称「占用内存较小的词典」)。
|
|
6
|
+
本插件只保留其中的**词条本身**,去掉了原来的词频和词性两列(本插件不用那两列)。
|
|
7
|
+
|
|
8
|
+
--------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
本目录下的「词频.txt」跟「词典.txt」**来自同一个文件**,也是 jieba 的
|
|
11
|
+
extra_dict/dict.txt.small。区别是它**用回了原来被丢掉的那一列「词频」**。
|
|
12
|
+
|
|
13
|
+
它记的是「这个词在通用中文里有多常见」——跟谁在用、说过什么话都无关。
|
|
14
|
+
插件拿它做一件事:**最常用的那些中文词,不当检索线索**。
|
|
15
|
+
|
|
16
|
+
每行格式是「词 两位数字」:
|
|
17
|
+
|
|
18
|
+
词 通用中文里的常见程度,0 = 最常用,99 = 很具体(越少见数字越大)
|
|
19
|
+
|
|
20
|
+
算法(生成脚本里就这几行,可复现):
|
|
21
|
+
|
|
22
|
+
稀有度 = 1 - log10(词频 + 1) / log10(最大词频 + 1) → 0 ~ 1
|
|
23
|
+
数字 = round(稀有度 × 99)
|
|
24
|
+
|
|
25
|
+
只收通用中文里**最常见的前 20000 个词**;更靠后的词一律当「很具体」,
|
|
26
|
+
所以查不到就等于 99,不必全收进来(省体积)。
|
|
27
|
+
|
|
28
|
+
--------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
jieba 采用 MIT 授权协议。按协议要求,原样附上版权声明:
|
|
31
|
+
|
|
32
|
+
--------------------------------------------------------------------------
|
|
33
|
+
The MIT License (MIT)
|
|
34
|
+
|
|
35
|
+
Copyright (c) 2013 Sun Junyi
|
|
36
|
+
|
|
37
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
38
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
39
|
+
the Software without restriction, including without limitation the rights to
|
|
40
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
41
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
42
|
+
subject to the following conditions:
|
|
43
|
+
|
|
44
|
+
The above copyright notice and this permission notice shall be included in all
|
|
45
|
+
copies or substantial portions of the Software.
|
|
46
|
+
|
|
47
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
48
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
49
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
50
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
51
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
52
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
53
|
+
--------------------------------------------------------------------------
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_note": "词表第二路(稀疏召回)的开关文件。enabled=false(出厂值)时这一路完全不存在,召回结果与没有它逐字节一致;改成 true 即启用,不必改代码(改完下一次调用生效)。",
|
|
3
|
+
"_keys": "enabled 总开关 · topK 这一路给几条候选 · floor 通用词门限 0~1(越大越只用生僻词,0.3 只滤掉最常见的那批) · ttlMs 索引缓存寿命(ms) · k1/b BM25 的 tf 饱和与长度归一键",
|
|
4
|
+
"_switch": "临时开关:环境变量 MEMORY_ENGINE_LEXICON=on|off(优先级高于本文件);调用方也可以显式传 lexiconEnabled。",
|
|
5
|
+
"enabled": false,
|
|
6
|
+
"topK": 20,
|
|
7
|
+
"floor": 0.3,
|
|
8
|
+
"ttlMs": 600000,
|
|
9
|
+
"k1": 1.2,
|
|
10
|
+
"b": 0.75
|
|
11
|
+
}
|