recallnest 1.4.1 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/.env +4 -0
  2. package/CLAUDE.md +7 -3
  3. package/README.md +202 -130
  4. package/README_CN.md +263 -190
  5. package/bin/recallnest.mjs +62 -0
  6. package/config.json +55 -0
  7. package/daily-recall.sh +23 -0
  8. package/integrations/setup-all.sh +31 -0
  9. package/logs/daily-recall.error.log +0 -0
  10. package/logs/daily-recall.log +30 -0
  11. package/logs/ingest-2026-04-11.log +110 -0
  12. package/logs/ingest-2026-04-12.log +90 -0
  13. package/logs/launchagent-stderr.log +0 -0
  14. package/logs/launchagent-stdout.log +0 -0
  15. package/package.json +10 -3
  16. package/src/access-tracker.ts +3 -2
  17. package/src/activity-counter.ts +109 -0
  18. package/src/adaptive-retrieval.ts +6 -5
  19. package/src/admission-control.ts +163 -0
  20. package/src/anchor-generator.ts +80 -0
  21. package/src/api-server.ts +10 -0
  22. package/src/audit-log.ts +122 -0
  23. package/src/auto-gc.ts +98 -29
  24. package/src/auto-recall.ts +33 -4
  25. package/src/capture-engine.ts +372 -23
  26. package/src/cascade-forget.ts +3 -2
  27. package/src/cli.ts +107 -5
  28. package/src/compat.ts +22 -0
  29. package/src/confidence-tracker.ts +210 -47
  30. package/src/conflict-store.ts +12 -3
  31. package/src/consolidation-engine.ts +296 -8
  32. package/src/context-collapse-renderer.ts +222 -0
  33. package/src/context-composer-stable-selection.ts +5 -1
  34. package/src/context-composer.ts +239 -4
  35. package/src/context-reconstructor.ts +404 -0
  36. package/src/conversation-importer.ts +441 -0
  37. package/src/data-checkup.ts +232 -0
  38. package/src/decay-engine.ts +84 -0
  39. package/src/distill-lock.ts +122 -0
  40. package/src/doctor.ts +8 -10
  41. package/src/dream-pipeline.ts +227 -0
  42. package/src/emotion-detector.ts +90 -0
  43. package/src/eval.ts +3 -2
  44. package/src/forget-engine.ts +269 -0
  45. package/src/frequency-tracker.ts +175 -0
  46. package/src/graph-export.ts +644 -0
  47. package/src/ingest.ts +36 -6
  48. package/src/interference-detector.ts +187 -0
  49. package/src/kg-extractor.ts +15 -24
  50. package/src/kg-store.ts +2 -1
  51. package/src/language-hook.ts +67 -0
  52. package/src/llm-client.ts +289 -6
  53. package/src/mcp-server.ts +707 -80
  54. package/src/memory-assets.ts +22 -5
  55. package/src/memory-evolution.ts +365 -0
  56. package/src/memory-lint.ts +429 -0
  57. package/src/memory-output.ts +69 -0
  58. package/src/memory-schema.ts +74 -2
  59. package/src/narrative-schema.ts +79 -0
  60. package/src/narrative-tagger.ts +164 -0
  61. package/src/pii-detector.ts +98 -0
  62. package/src/post-retrieval-filter.ts +76 -0
  63. package/src/prediction-engine.ts +253 -0
  64. package/src/preference-slots.ts +65 -2
  65. package/src/prospective-memory.ts +215 -0
  66. package/src/query-expander.ts +67 -0
  67. package/src/recall-governor.ts +183 -0
  68. package/src/retention-policy.ts +90 -0
  69. package/src/retriever.ts +828 -55
  70. package/src/rif.ts +34 -8
  71. package/src/runtime-config.ts +23 -8
  72. package/src/scope-inventory.ts +4 -3
  73. package/src/scope-policy.ts +4 -0
  74. package/src/session-distiller.ts +307 -0
  75. package/src/session-output.ts +40 -0
  76. package/src/session-schema.ts +37 -0
  77. package/src/session-store.ts +7 -2
  78. package/src/skill-engine.ts +208 -0
  79. package/src/skill-promotion.ts +293 -0
  80. package/src/skill-schema.ts +39 -0
  81. package/src/store.ts +96 -10
  82. package/src/topic-tag.ts +66 -0
  83. package/src/tracker.ts +3 -1
  84. package/src/ui-server.ts +80 -8
  85. package/src/workflow-observation-scope-review.ts +2 -1
  86. package/src/workflow-observation-store.ts +2 -1
  87. package/src/write-verifier.ts +126 -0
  88. package/.claude-plugin/CLAUDE.md +0 -29
  89. package/.claude-plugin/marketplace.json +0 -18
  90. package/.github/workflows/ci.yml +0 -28
  91. package/.mcp.json +0 -9
  92. package/CHANGELOG.md +0 -51
  93. package/DEMO.md +0 -94
  94. package/EVAL.md +0 -89
  95. package/FAILURES.md +0 -75
  96. package/OPERATIONS.md +0 -59
  97. package/ROADMAP.md +0 -198
  98. package/assets/recallnest-workbench.png +0 -0
  99. package/assets/ui/app.js +0 -807
  100. package/assets/ui/index.html +0 -154
  101. package/assets/ui/styles.css +0 -605
  102. package/assets/wechat-qr.jpg +0 -0
  103. package/daily-reflections/2026-03-05.md +0 -15
  104. package/daily-reflections/2026-03-06.md +0 -15
  105. package/daily-reflections/2026-03-07.md +0 -7
  106. package/daily-reflections/2026-03-08.md +0 -34
  107. package/daily-reflections/2026-03-09.md +0 -35
  108. package/daily-reflections/2026-03-10.md +0 -36
  109. package/daily-reflections/2026-03-11.md +0 -35
  110. package/daily-reflections/2026-03-12.md +0 -36
  111. package/daily-reflections/2026-03-13.md +0 -35
  112. package/daily-reflections/2026-03-14.md +0 -35
  113. package/daily-reflections/2026-03-15.md +0 -35
  114. package/daily-reflections/2026-03-16.md +0 -41
  115. package/daily-reflections/2026-03-17.md +0 -10
  116. package/daily-reflections/2026-03-18.md +0 -10
  117. package/daily-reflections/2026-03-19.md +0 -10
  118. package/daily-reflections/2026-03-20.md +0 -10
  119. package/daily-reflections/2026-03-21.md +0 -10
  120. package/daily-reflections/2026-03-22.md +0 -10
  121. package/daily-reflections/2026-03-23.md +0 -10
  122. package/daily-reflections/2026-03-24.md +0 -10
  123. package/daily-reflections/2026-03-25.md +0 -32
  124. package/daily-reflections/2026-03-26.md +0 -37
  125. package/daily-reflections/2026-03-27.md +0 -35
  126. package/daily-reflections/2026-03-28.md +0 -35
  127. package/daily-reflections/2026-03-29.md +0 -35
  128. package/daily-reflections/2026-03-30.md +0 -36
  129. package/daily-reflections/2026-03-31.md +0 -35
  130. package/daily-reflections/2026-04-01.md +0 -35
  131. package/docs/api-reference.md +0 -1291
  132. package/docs/architecture.md +0 -168
  133. package/docs/memory-boundary-contract.md +0 -226
  134. package/docs/memory-categories.md +0 -119
  135. package/docs/pipeline-audit-ultramemory.md +0 -177
  136. package/docs/plans/2026-03-08-query-expansion.md +0 -270
  137. package/docs/plans/2026-03-16-continuity-layer.md +0 -196
  138. package/docs/plans/mcp-tiering-plan.md +0 -126
  139. package/docs/research/recall-quality-audit-2026-03-22.md +0 -153
  140. package/docs/self-evolution.md +0 -198
  141. package/docs/superpowers/plans/2026-03-19-ingest-pipeline-upgrade.md +0 -782
  142. package/docs/superpowers/specs/2026-03-19-ingest-pipeline-upgrade-design.md +0 -77
  143. package/docs/superpowers/specs/2026-03-23-pull-claude-desktop-design.md +0 -95
  144. package/eval/continuity/results-2026-03-16.md +0 -117
  145. package/eval/continuity/results-2026-03-19.md +0 -453
  146. package/eval/continuity/runbook.md +0 -110
  147. package/eval/continuity/seed-cases.md +0 -54
  148. package/eval/reports/2026-03-06-abstract-query-baseline.md +0 -96
  149. package/eval/reports/2026-03-06-aws-ssh-baseline.md +0 -82
  150. package/eval/reports/2026-03-06-baseline.md +0 -82
  151. package/eval/reports/query-expansion.md +0 -138
  152. package/lm +0 -19
  153. package/plan-v1.2-distributable.md +0 -105
  154. package/scripts/archive-distilled-junk.ts +0 -116
  155. package/scripts/backfill-l0l1l2.ts +0 -98
  156. package/scripts/blind-scan.ts +0 -588
  157. package/scripts/cleanup-step1-exact-dups.ts +0 -117
  158. package/scripts/cleanup-step2-template-dups.ts +0 -113
  159. package/scripts/cleanup-step3-stale-content.ts +0 -149
  160. package/scripts/cleanup-step4-orphan-scopes.ts +0 -131
  161. package/scripts/delete-distilled-junk.ts +0 -88
  162. package/scripts/distill-facts.ts +0 -305
  163. package/scripts/health-check.ts +0 -343
  164. package/scripts/incremental-ingest.sh +0 -71
  165. package/scripts/mlp-health-check.ts +0 -382
  166. package/scripts/pull-claude-desktop.ts +0 -515
  167. package/scripts/re-ingest-cc.sh +0 -66
  168. package/scripts/rebalance-memory-health.ts +0 -181
  169. package/scripts/run-desktop-ingest.sh +0 -11
  170. package/scripts/start-server.sh +0 -12
  171. package/scripts/viz-dashboard.html +0 -423
  172. package/scripts/viz-export.ts +0 -703
  173. package/src/__tests__/access-tracker-novelty.test.ts +0 -228
  174. package/src/__tests__/auto-consolidation.test.ts +0 -168
  175. package/src/__tests__/auto-recall.test.ts +0 -131
  176. package/src/__tests__/batch-dedup.test.ts +0 -65
  177. package/src/__tests__/capture-engine.test.ts +0 -1142
  178. package/src/__tests__/capture-heuristic.test.ts +0 -171
  179. package/src/__tests__/cascade-forget.test.ts +0 -210
  180. package/src/__tests__/category-min-scores.test.ts +0 -33
  181. package/src/__tests__/confidence-tracker.test.ts +0 -196
  182. package/src/__tests__/conflict-advisor.test.ts +0 -206
  183. package/src/__tests__/conflict-escalation.test.ts +0 -149
  184. package/src/__tests__/conflict-lifecycle.test.ts +0 -83
  185. package/src/__tests__/conflict-output.test.ts +0 -85
  186. package/src/__tests__/conflict-store.test.ts +0 -374
  187. package/src/__tests__/consolidation-engine.test.ts +0 -181
  188. package/src/__tests__/context-composer-pins.test.ts +0 -64
  189. package/src/__tests__/context-composer-task-selection.test.ts +0 -24
  190. package/src/__tests__/context-renderer.test.ts +0 -91
  191. package/src/__tests__/continuity-eval.test.ts +0 -275
  192. package/src/__tests__/continuity-schema.test.ts +0 -97
  193. package/src/__tests__/core-recall-eval.test.ts +0 -235
  194. package/src/__tests__/core-summary.test.ts +0 -109
  195. package/src/__tests__/dedup-actions.test.ts +0 -58
  196. package/src/__tests__/deterministic-id.test.ts +0 -39
  197. package/src/__tests__/doctor.test.ts +0 -303
  198. package/src/__tests__/drill-down.test.ts +0 -108
  199. package/src/__tests__/embedder.test.ts +0 -122
  200. package/src/__tests__/entity-resolver.test.ts +0 -58
  201. package/src/__tests__/event-segmenter.test.ts +0 -153
  202. package/src/__tests__/hotness-blend.test.ts +0 -76
  203. package/src/__tests__/ingest.test.ts +0 -360
  204. package/src/__tests__/kg-extractor.test.ts +0 -184
  205. package/src/__tests__/kg-store.test.ts +0 -198
  206. package/src/__tests__/llm-consolidation.test.ts +0 -226
  207. package/src/__tests__/memory-boundaries.test.ts +0 -237
  208. package/src/__tests__/memory-health-rebalance.test.ts +0 -144
  209. package/src/__tests__/memory-output.test.ts +0 -215
  210. package/src/__tests__/multi-vector.test.ts +0 -242
  211. package/src/__tests__/ppr-traversal.test.ts +0 -256
  212. package/src/__tests__/preference-guard-smoke.test.ts +0 -211
  213. package/src/__tests__/preference-matcher.test.ts +0 -230
  214. package/src/__tests__/preference-slots.test.ts +0 -106
  215. package/src/__tests__/prospective-memory.test.ts +0 -283
  216. package/src/__tests__/query-entity-detector.test.ts +0 -171
  217. package/src/__tests__/query-expander.test.ts +0 -34
  218. package/src/__tests__/recall-mode.test.ts +0 -64
  219. package/src/__tests__/result-synthesizer.test.ts +0 -198
  220. package/src/__tests__/resume-context.test.ts +0 -4743
  221. package/src/__tests__/retrieval-trace.test.ts +0 -213
  222. package/src/__tests__/retriever-boundary-weight.test.ts +0 -136
  223. package/src/__tests__/retroactive-boost.test.ts +0 -223
  224. package/src/__tests__/scope-inventory.test.ts +0 -200
  225. package/src/__tests__/scope-policy.test.ts +0 -69
  226. package/src/__tests__/session-checkpoint.test.ts +0 -259
  227. package/src/__tests__/session-gc.test.ts +0 -215
  228. package/src/__tests__/store-scope-audit.test.ts +0 -81
  229. package/src/__tests__/temporal-parser.test.ts +0 -241
  230. package/src/__tests__/term-registry.test.ts +0 -121
  231. package/src/__tests__/tool-output-compressor.test.ts +0 -133
  232. package/src/__tests__/version-manager.test.ts +0 -213
  233. package/src/__tests__/workflow-observation.test.ts +0 -400
@@ -1,71 +0,0 @@
1
- #!/bin/bash
2
- # RecallNest 增量更新脚本 — LaunchAgent 调用
3
- # 只处理新增/修改的文件,已处理的自动跳过
4
- # 超时保护:最多运行 2 小时,超时自动 kill
5
-
6
- SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
7
-
8
- # Load API key from .env (CLI also reads .env, this is for LaunchAgent)
9
- if [ -f "$SCRIPT_DIR/.env" ]; then
10
- set -a
11
- source "$SCRIPT_DIR/.env"
12
- set +a
13
- fi
14
- export PATH="$HOME/.bun/bin:/opt/homebrew/bin:/usr/local/bin:$PATH"
15
-
16
- LOG_DIR="$SCRIPT_DIR/logs"
17
- mkdir -p "$LOG_DIR"
18
- LOG_FILE="$LOG_DIR/ingest-$(date +%Y-%m-%d).log"
19
-
20
- # 超时时间(秒):2 小时 = 7200 秒
21
- TIMEOUT=7200
22
-
23
- echo "=== $(date '+%Y-%m-%d %H:%M:%S') 增量更新开始 ===" >> "$LOG_FILE"
24
-
25
- cd "$SCRIPT_DIR" || exit 1
26
-
27
- # 用 timeout 命令限制运行时间(macOS 需要 gtimeout 或用 perl 替代)
28
- if command -v gtimeout &>/dev/null; then
29
- gtimeout "$TIMEOUT" bun run src/cli.ts ingest --source all >> "$LOG_FILE" 2>&1
30
- EXIT_CODE=$?
31
- elif command -v timeout &>/dev/null; then
32
- timeout "$TIMEOUT" bun run src/cli.ts ingest --source all >> "$LOG_FILE" 2>&1
33
- EXIT_CODE=$?
34
- else
35
- # macOS 没有 timeout,用后台进程 + kill 实现
36
- bun run src/cli.ts ingest --source all >> "$LOG_FILE" 2>&1 &
37
- INGEST_PID=$!
38
-
39
- # 监控进程
40
- ELAPSED=0
41
- while kill -0 "$INGEST_PID" 2>/dev/null; do
42
- sleep 60
43
- ELAPSED=$((ELAPSED + 60))
44
- if [ "$ELAPSED" -ge "$TIMEOUT" ]; then
45
- echo "⚠️ $(date '+%H:%M:%S') 超时 ${TIMEOUT}s,强制终止进程 $INGEST_PID" >> "$LOG_FILE"
46
- kill "$INGEST_PID" 2>/dev/null
47
- sleep 5
48
- kill -9 "$INGEST_PID" 2>/dev/null
49
- EXIT_CODE=124
50
- break
51
- fi
52
- done
53
-
54
- if [ -z "$EXIT_CODE" ]; then
55
- wait "$INGEST_PID"
56
- EXIT_CODE=$?
57
- fi
58
- fi
59
-
60
- if [ "$EXIT_CODE" -eq 124 ]; then
61
- echo "⚠️ $(date '+%Y-%m-%d %H:%M:%S') 增量更新超时(${TIMEOUT}s),已自动终止" >> "$LOG_FILE"
62
- elif [ "$EXIT_CODE" -ne 0 ]; then
63
- echo "❌ $(date '+%Y-%m-%d %H:%M:%S') 增量更新异常退出(exit code: $EXIT_CODE)" >> "$LOG_FILE"
64
- else
65
- echo "=== $(date '+%Y-%m-%d %H:%M:%S') 增量更新完成 ===" >> "$LOG_FILE"
66
- fi
67
-
68
- echo "" >> "$LOG_FILE"
69
-
70
- # 只保留最近 7 天的日志
71
- find "$LOG_DIR" -name "ingest-*.log" -mtime +7 -delete 2>/dev/null
@@ -1,382 +0,0 @@
1
- /**
2
- * memory-lancedb-pro 通用记忆体检脚本
3
- * 只读分析 LanceDB 数据,不写入任何内容
4
- * 兼容 RecallNest (camelCase) 和 memory-lancedb-pro (snake_case) metadata 格式
5
- *
6
- * 用法:
7
- * bun run scripts/mlp-health-check.ts <lancedb-dir> [bot-name]
8
- *
9
- * 示例:
10
- * bun run scripts/mlp-health-check.ts /tmp/mlp-healthcheck/antigravity/lancedb-pro AntiBot
11
- * bun run scripts/mlp-health-check.ts ./data/lancedb RecallNest
12
- */
13
-
14
- import lancedb from "@lancedb/lancedb";
15
-
16
- const DB_PATH = process.argv[2] || "./data/lancedb";
17
- const BOT_NAME = process.argv[3] || "未命名";
18
- const TABLE_NAME = "memories";
19
-
20
- interface MemoryRow {
21
- id: string;
22
- text: string;
23
- vector: number[];
24
- category: string;
25
- scope: string;
26
- importance: number;
27
- timestamp: number;
28
- metadata?: string;
29
- }
30
-
31
- /** 兼容 camelCase (RecallNest) 和 snake_case (memory-lancedb-pro) */
32
- function getAccessCount(meta: Record<string, any>): number {
33
- return meta.accessCount ?? meta.access_count ?? 0;
34
- }
35
-
36
- function getLastAccessed(meta: Record<string, any>): number {
37
- return meta.lastAccessedAt ?? meta.last_accessed_at ?? 0;
38
- }
39
-
40
- function getTier(meta: Record<string, any>): string {
41
- return meta.tier ?? "unknown";
42
- }
43
-
44
- function getSource(meta: Record<string, any>): string {
45
- return meta.source ?? meta.source_session ?? "";
46
- }
47
-
48
- function cosineSimilarity(a: number[], b: number[]): number {
49
- let dot = 0, normA = 0, normB = 0;
50
- for (let i = 0; i < a.length; i++) {
51
- dot += a[i] * b[i];
52
- normA += a[i] * a[i];
53
- normB += b[i] * b[i];
54
- }
55
- return dot / (Math.sqrt(normA) * Math.sqrt(normB));
56
- }
57
-
58
- function formatDate(ts: number): string {
59
- return new Date(ts).toISOString().slice(0, 10);
60
- }
61
-
62
- function percentBar(pct: number, width = 30): string {
63
- const filled = Math.round(pct / 100 * width);
64
- return "█".repeat(filled) + "░".repeat(width - filled);
65
- }
66
-
67
- async function main() {
68
- console.log(`\n🏥 记忆体检报告 — ${BOT_NAME}`);
69
- console.log("=".repeat(60));
70
- console.log(`📅 检查时间: ${new Date().toISOString().slice(0, 19)}`);
71
- console.log(`📂 数据路径: ${DB_PATH}\n`);
72
-
73
- const db = await lancedb.connect(DB_PATH);
74
- const table = await db.openTable(TABLE_NAME);
75
-
76
- // Fetch all rows (without vectors first for speed)
77
- console.log("⏳ 正在读取全量数据...");
78
- const allRows: MemoryRow[] = await table
79
- .query()
80
- .select(["id", "text", "category", "scope", "importance", "timestamp", "metadata"])
81
- .toArray() as any;
82
-
83
- const total = allRows.length;
84
- if (total === 0) {
85
- console.log("⚠️ 记忆库为空,无数据可分析。");
86
- return;
87
- }
88
- console.log(`✅ 读取完成: ${total.toLocaleString()} 条记忆\n`);
89
-
90
- // Parse metadata once
91
- const parsedRows = allRows.map(row => ({
92
- ...row,
93
- meta: row.metadata ? JSON.parse(row.metadata) : {},
94
- }));
95
-
96
- // ============================================
97
- // 1. Category 分布
98
- // ============================================
99
- console.log("📊 1. Category 分布");
100
- console.log("-".repeat(60));
101
- const catMap = new Map<string, number>();
102
- for (const row of parsedRows) {
103
- catMap.set(row.category, (catMap.get(row.category) || 0) + 1);
104
- }
105
- const catSorted = [...catMap.entries()].sort((a, b) => b[1] - a[1]);
106
- for (const [cat, count] of catSorted) {
107
- const pct = (count / total * 100).toFixed(1);
108
- console.log(` ${cat.padEnd(14)} ${count.toString().padStart(6)} (${pct.padStart(5)}%) ${percentBar(parseFloat(pct))}`);
109
- }
110
-
111
- // ============================================
112
- // 2. Tier 分布
113
- // ============================================
114
- console.log("\n📊 2. Tier 分布");
115
- console.log("-".repeat(60));
116
- const tierMap = new Map<string, number>();
117
- for (const row of parsedRows) {
118
- const tier = getTier(row.meta);
119
- tierMap.set(tier, (tierMap.get(tier) || 0) + 1);
120
- }
121
- const tierSorted = [...tierMap.entries()].sort((a, b) => b[1] - a[1]);
122
- for (const [tier, count] of tierSorted) {
123
- const pct = (count / total * 100).toFixed(1);
124
- console.log(` ${tier.padEnd(14)} ${count.toString().padStart(6)} (${pct.padStart(5)}%) ${percentBar(parseFloat(pct))}`);
125
- }
126
-
127
- // ============================================
128
- // 3. 死记忆分析
129
- // ============================================
130
- console.log("\n📊 3. 死记忆分析(accessCount = 0 或无记录)");
131
- console.log("-".repeat(60));
132
- let deadCount = 0;
133
- let aliveCount = 0;
134
- let totalAccess = 0;
135
- let maxAccess = 0;
136
- let maxAccessText = "";
137
- const accessDistribution = new Map<string, number>();
138
-
139
- for (const row of parsedRows) {
140
- const ac = getAccessCount(row.meta);
141
- totalAccess += ac;
142
-
143
- if (ac === 0) deadCount++;
144
- else aliveCount++;
145
-
146
- if (ac > maxAccess) {
147
- maxAccess = ac;
148
- maxAccessText = row.text.slice(0, 80);
149
- }
150
-
151
- let bucket: string;
152
- if (ac === 0) bucket = "0 (死记忆)";
153
- else if (ac <= 2) bucket = "1-2";
154
- else if (ac <= 5) bucket = "3-5";
155
- else if (ac <= 10) bucket = "6-10";
156
- else if (ac <= 50) bucket = "11-50";
157
- else bucket = "50+";
158
- accessDistribution.set(bucket, (accessDistribution.get(bucket) || 0) + 1);
159
- }
160
-
161
- const deadPct = (deadCount / total * 100).toFixed(1);
162
- const alivePct = (aliveCount / total * 100).toFixed(1);
163
- console.log(` 死记忆: ${deadCount.toLocaleString()} / ${total.toLocaleString()} (${deadPct}%)`);
164
- console.log(` 活记忆: ${aliveCount.toLocaleString()} / ${total.toLocaleString()} (${alivePct}%)`);
165
- console.log(` 总访问次: ${totalAccess.toLocaleString()}`);
166
- console.log(` 平均访问: ${(totalAccess / total).toFixed(2)} 次/条`);
167
- if (maxAccess > 0) {
168
- console.log(` 最热记忆: [${maxAccess}次] ${maxAccessText}...`);
169
- }
170
-
171
- console.log("\n 访问次数分布:");
172
- const bucketOrder = ["0 (死记忆)", "1-2", "3-5", "6-10", "11-50", "50+"];
173
- for (const bucket of bucketOrder) {
174
- const count = accessDistribution.get(bucket) || 0;
175
- const pct = (count / total * 100).toFixed(1);
176
- console.log(` ${bucket.padEnd(14)} ${count.toString().padStart(6)} (${pct.padStart(5)}%) ${percentBar(parseFloat(pct))}`);
177
- }
178
-
179
- // ============================================
180
- // 4. Importance 分布
181
- // ============================================
182
- console.log("\n📊 4. Importance 分布");
183
- console.log("-".repeat(60));
184
- const impBuckets = new Map<string, number>();
185
- let impSum = 0;
186
- for (const row of parsedRows) {
187
- const imp = row.importance || 0;
188
- impSum += imp;
189
- let bucket: string;
190
- if (imp < 0.2) bucket = "0.0-0.2 (低)";
191
- else if (imp < 0.4) bucket = "0.2-0.4";
192
- else if (imp < 0.6) bucket = "0.4-0.6 (中)";
193
- else if (imp < 0.8) bucket = "0.6-0.8";
194
- else bucket = "0.8-1.0 (高)";
195
- impBuckets.set(bucket, (impBuckets.get(bucket) || 0) + 1);
196
- }
197
- console.log(` 平均 importance: ${(impSum / total).toFixed(3)}`);
198
- const impOrder = ["0.0-0.2 (低)", "0.2-0.4", "0.4-0.6 (中)", "0.6-0.8", "0.8-1.0 (高)"];
199
- for (const bucket of impOrder) {
200
- const count = impBuckets.get(bucket) || 0;
201
- const pct = (count / total * 100).toFixed(1);
202
- console.log(` ${bucket.padEnd(16)} ${count.toString().padStart(6)} (${pct.padStart(5)}%) ${percentBar(parseFloat(pct))}`);
203
- }
204
-
205
- // ============================================
206
- // 5. Scope 分布
207
- // ============================================
208
- console.log("\n📊 5. Scope 分布");
209
- console.log("-".repeat(60));
210
- const scopeMap = new Map<string, number>();
211
- for (const row of parsedRows) {
212
- const s = row.scope || "(empty)";
213
- scopeMap.set(s, (scopeMap.get(s) || 0) + 1);
214
- }
215
- const scopeSorted = [...scopeMap.entries()].sort((a, b) => b[1] - a[1]);
216
- console.log(` 独立 scope 数: ${scopeMap.size}`);
217
- console.log(" Top 10:");
218
- for (const [scope, count] of scopeSorted.slice(0, 10)) {
219
- const pct = (count / total * 100).toFixed(1);
220
- const label = scope.length > 30 ? scope.slice(0, 27) + "..." : scope;
221
- console.log(` ${label.padEnd(32)} ${count.toString().padStart(6)} (${pct.padStart(5)}%)`);
222
- }
223
-
224
- // ============================================
225
- // 6. 年龄分布
226
- // ============================================
227
- console.log("\n📊 6. 年龄分布");
228
- console.log("-".repeat(60));
229
- const now = Date.now();
230
- const ageBuckets = new Map<string, number>();
231
- let oldest = Infinity, newest = 0;
232
- for (const row of parsedRows) {
233
- const ts = row.timestamp;
234
- if (ts < oldest) oldest = ts;
235
- if (ts > newest) newest = ts;
236
- const ageHours = (now - ts) / (1000 * 60 * 60);
237
- let bucket: string;
238
- if (ageHours < 24) bucket = "< 1 天";
239
- else if (ageHours < 24 * 7) bucket = "1-7 天";
240
- else if (ageHours < 24 * 30) bucket = "7-30 天";
241
- else if (ageHours < 24 * 90) bucket = "30-90 天";
242
- else bucket = "> 90 天";
243
- ageBuckets.set(bucket, (ageBuckets.get(bucket) || 0) + 1);
244
- }
245
- console.log(` 最早: ${formatDate(oldest)}`);
246
- console.log(` 最新: ${formatDate(newest)}`);
247
- const ageOrder = ["< 1 天", "1-7 天", "7-30 天", "30-90 天", "> 90 天"];
248
- for (const bucket of ageOrder) {
249
- const count = ageBuckets.get(bucket) || 0;
250
- const pct = (count / total * 100).toFixed(1);
251
- console.log(` ${bucket.padEnd(12)} ${count.toString().padStart(6)} (${pct.padStart(5)}%) ${percentBar(parseFloat(pct))}`);
252
- }
253
-
254
- // ============================================
255
- // 7. 近似重复检测(采样)
256
- // ============================================
257
- const sampleSize = Math.min(500, total);
258
- console.log(`\n📊 7. 近似重复检测(采样 ${sampleSize} 条,cosine > 0.95)`);
259
- console.log("-".repeat(60));
260
- console.log(" ⏳ 正在读取向量数据(采样)...");
261
-
262
- const sampleRows: MemoryRow[] = await table
263
- .query()
264
- .select(["id", "text", "vector", "category"])
265
- .limit(sampleSize)
266
- .toArray() as any;
267
-
268
- let dupPairs = 0;
269
- const dupExamples: Array<{sim: number, textA: string, textB: string}> = [];
270
-
271
- for (let i = 0; i < sampleRows.length; i++) {
272
- for (let j = i + 1; j < sampleRows.length; j++) {
273
- if (sampleRows[i].vector && sampleRows[j].vector) {
274
- const sim = cosineSimilarity(sampleRows[i].vector, sampleRows[j].vector);
275
- if (sim > 0.95) {
276
- dupPairs++;
277
- if (dupExamples.length < 3) {
278
- dupExamples.push({
279
- sim,
280
- textA: sampleRows[i].text.slice(0, 60),
281
- textB: sampleRows[j].text.slice(0, 60),
282
- });
283
- }
284
- }
285
- }
286
- }
287
- }
288
-
289
- const totalPairs = sampleSize * (sampleSize - 1) / 2;
290
- const dupRate = (dupPairs / totalPairs * 100).toFixed(3);
291
- console.log(` 采样对数: ${totalPairs.toLocaleString()}`);
292
- console.log(` 高相似对: ${dupPairs} (${dupRate}%)`);
293
- if (total > sampleSize) {
294
- console.log(` 推算全库: ~${Math.round(dupPairs / totalPairs * total * (total-1) / 2).toLocaleString()} 对潜在重复`);
295
- }
296
-
297
- if (dupExamples.length > 0) {
298
- console.log("\n 示例重复对:");
299
- for (const ex of dupExamples) {
300
- console.log(` [sim=${ex.sim.toFixed(3)}]`);
301
- console.log(` A: ${ex.textA}...`);
302
- console.log(` B: ${ex.textB}...`);
303
- }
304
- }
305
-
306
- // ============================================
307
- // 8. 综合诊断
308
- // ============================================
309
- console.log("\n" + "=".repeat(60));
310
- console.log(`🩺 综合诊断 — ${BOT_NAME}`);
311
- console.log("=".repeat(60));
312
-
313
- const issues: string[] = [];
314
- const healthy: string[] = [];
315
-
316
- // Total count check
317
- if (total < 10) {
318
- issues.push(`🟡 记忆总量仅 ${total} 条,数据量偏少`);
319
- } else {
320
- healthy.push(`✅ 记忆总量 ${total.toLocaleString()} 条`);
321
- }
322
-
323
- // Dead memory ratio
324
- const deadRatio = deadCount / total;
325
- if (deadRatio > 0.9) {
326
- issues.push(`🔴 ${deadPct}% 记忆从未被访问(死记忆率过高)`);
327
- } else if (deadRatio > 0.7) {
328
- issues.push(`🟡 ${deadPct}% 记忆从未被访问(死记忆率偏高)`);
329
- } else {
330
- healthy.push(`✅ 死记忆率 ${deadPct}% 在合理范围`);
331
- }
332
-
333
- // Category balance — check if any single legacy category dominates
334
- const factCount = catMap.get("fact") || 0;
335
- const decisionCount = catMap.get("decision") || 0;
336
- const legacyRatio = (factCount + decisionCount) / total;
337
- if (legacyRatio > 0.5) {
338
- issues.push(`🟡 legacy 类别(fact/decision)占 ${(legacyRatio*100).toFixed(1)}%,建议迁移到 6-category 体系`);
339
- }
340
- // Check structural category coverage
341
- const structuredCount = (catMap.get("entities") || 0) + (catMap.get("patterns") || 0) + (catMap.get("cases") || 0);
342
- if (structuredCount === 0 && total > 20) {
343
- issues.push(`🟡 无结构化记忆(entities/patterns/cases 均为 0)`);
344
- } else if (structuredCount > 0) {
345
- healthy.push(`✅ 结构化记忆 ${structuredCount} 条(entities+patterns+cases)`);
346
- }
347
-
348
- // Tier distribution
349
- const coreCount = tierMap.get("core") || 0;
350
- const unknownTier = tierMap.get("unknown") || 0;
351
- if (unknownTier / total > 0.5) {
352
- issues.push(`🟡 ${(unknownTier/total*100).toFixed(1)}% 记忆无 tier 标记`);
353
- }
354
- if (coreCount < 5 && total > 50) {
355
- issues.push(`🟡 core tier 仅 ${coreCount} 条,高价值记忆太少`);
356
- } else if (coreCount >= 5) {
357
- healthy.push(`✅ core tier 有 ${coreCount} 条高价值记忆`);
358
- }
359
-
360
- // Importance differentiation
361
- const modeImpBucket = [...impBuckets.entries()].sort((a, b) => b[1] - a[1])[0];
362
- if (modeImpBucket && modeImpBucket[1] / total > 0.9) {
363
- issues.push(`🟡 ${(modeImpBucket[1]/total*100).toFixed(1)}% importance 集中在 ${modeImpBucket[0]},区分度不足`);
364
- } else {
365
- healthy.push(`✅ importance 分布有区分度`);
366
- }
367
-
368
- // Dup rate
369
- if (parseFloat(dupRate) > 1) {
370
- issues.push(`🟡 采样重复率 ${dupRate}%,全库可能存在大量近似重复`);
371
- } else {
372
- healthy.push(`✅ 采样重复率 ${dupRate}%,重复可控`);
373
- }
374
-
375
- for (const h of healthy) console.log(` ${h}`);
376
- for (const i of issues) console.log(` ${i}`);
377
-
378
- console.log("\n" + "=".repeat(60));
379
- console.log("📋 体检完成\n");
380
- }
381
-
382
- main().catch(console.error);