@morningljn/mnemo 0.2.0 → 0.3.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.
Files changed (60) hide show
  1. package/dist/config.d.ts +2 -0
  2. package/dist/config.js +28 -0
  3. package/dist/config.js.map +1 -0
  4. package/dist/dream-engine.d.ts +17 -0
  5. package/dist/dream-engine.js +144 -0
  6. package/dist/dream-engine.js.map +1 -0
  7. package/dist/dream.d.ts +2 -0
  8. package/dist/dream.js +20 -0
  9. package/dist/dream.js.map +1 -0
  10. package/dist/init.js +4 -24
  11. package/dist/init.js.map +1 -1
  12. package/dist/llm-client.d.ts +10 -0
  13. package/dist/llm-client.js +55 -0
  14. package/dist/llm-client.js.map +1 -0
  15. package/dist/resources.d.ts +22 -8
  16. package/dist/resources.js +66 -20
  17. package/dist/resources.js.map +1 -1
  18. package/dist/retriever.js +12 -5
  19. package/dist/retriever.js.map +1 -1
  20. package/dist/schema.d.ts +1 -1
  21. package/dist/schema.js +2 -2
  22. package/dist/server.js +40 -6
  23. package/dist/server.js.map +1 -1
  24. package/dist/store.d.ts +23 -1
  25. package/dist/store.js +169 -4
  26. package/dist/store.js.map +1 -1
  27. package/dist/types.d.ts +41 -1
  28. package/docs/superpowers/plans/2026-05-16-llm-dream.md +973 -0
  29. package/docs/superpowers/plans/2026-05-16-memory-dreaming.md +626 -0
  30. package/openspec/changes/archive/2026-05-16-memory-dreaming/.openspec.yaml +2 -0
  31. package/openspec/changes/archive/2026-05-16-memory-dreaming/design.md +71 -0
  32. package/openspec/changes/archive/2026-05-16-memory-dreaming/proposal.md +32 -0
  33. package/openspec/changes/archive/2026-05-16-memory-dreaming/specs/compact-search/spec.md +16 -0
  34. package/openspec/changes/archive/2026-05-16-memory-dreaming/specs/dream-cycle/spec.md +38 -0
  35. package/openspec/changes/archive/2026-05-16-memory-dreaming/tasks.md +27 -0
  36. package/openspec/changes/llm-dream/.openspec.yaml +2 -0
  37. package/openspec/changes/llm-dream/design.md +84 -0
  38. package/openspec/changes/llm-dream/proposal.md +36 -0
  39. package/openspec/changes/llm-dream/specs/dream-cycle/spec.md +42 -0
  40. package/openspec/changes/llm-dream/specs/llm-client/spec.md +57 -0
  41. package/openspec/changes/llm-dream/specs/llm-dream-engine/spec.md +72 -0
  42. package/openspec/changes/llm-dream/tasks.md +32 -0
  43. package/openspec/specs/compact-search/spec.md +16 -0
  44. package/openspec/specs/dream-cycle/spec.md +38 -0
  45. package/package.json +3 -2
  46. package/src/config.ts +29 -0
  47. package/src/dream-engine.ts +162 -0
  48. package/src/dream.ts +20 -0
  49. package/src/init.ts +4 -24
  50. package/src/llm-client.ts +59 -0
  51. package/src/resources.ts +77 -21
  52. package/src/retriever.ts +9 -5
  53. package/src/schema.ts +2 -2
  54. package/src/server.ts +46 -7
  55. package/src/store.ts +198 -5
  56. package/src/types.ts +41 -1
  57. package/tests/dream-engine.test.ts +163 -0
  58. package/tests/llm-client.test.ts +105 -0
  59. package/tests/resource.test.ts +25 -23
  60. package/tests/store.test.ts +130 -2
@@ -0,0 +1,32 @@
1
+ ## Why
2
+
3
+ mnemo 的记忆库随着长期使用会积累大量冗余、重叠、过长的 fact,导致检索精度下降、token 浪费。当前 `runLearning()` 只调 trust 分,不整理内容。需要一套后台自动整理机制(类似 OpenClaw Dreaming),定期合并去重、压缩摘要、分类修正,保持数据库精炼。
4
+
5
+ ## What Changes
6
+
7
+ - 新增 `dream` action(`fact_store(action="dream")`),执行三阶段整理:Collect → Consolidate → Evaluate
8
+ - 新增 `mnemo dream` CLI 命令,支持 cron 定时触发
9
+ - 合并 Jaccard > 0.6 的重叠 fact(保留最完整的,其余删除)
10
+ - 长 fact(content > 200 字且无 summary)自动提取关键句生成 summary
11
+ - 分类自动修正:按关键词规则将误分类的 fact 挪到正确 category
12
+ - 输出 dream report:合并/删除/压缩了什么,健康评分多少
13
+ - 搜索结果精简格式:返回 summary(优先)或 content 前 100 字,减少 token 消耗
14
+
15
+ ## Capabilities
16
+
17
+ ### New Capabilities
18
+ - `dream-cycle`: 后台定期整理记忆库(合并去重、摘要压缩、分类修正、健康评分)
19
+ - `compact-search`: 搜索结果精简格式(summary 优先、content 截断、限制条数)
20
+
21
+ ### Modified Capabilities
22
+
23
+ (无已有 spec 需要修改)
24
+
25
+ ## Impact
26
+
27
+ - `src/store.ts` — 新增 `runDream()` 方法(合并、压缩、分类修正、报告)
28
+ - `src/retriever.ts` — 搜索结果格式精简(返回 summary 而非完整 content)
29
+ - `src/server.ts` — `fact_store` 新增 `dream` action
30
+ - `src/init.ts` — `mnemo init` 可选配置 cron 定时 dream
31
+ - `tests/store.test.ts` — dream 相关测试
32
+ - 数据库 — dream 可能删除/合并 fact,不可逆操作需谨慎
@@ -0,0 +1,16 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: 搜索结果精简格式
4
+ 搜索返回结果 SHALL 优先返回 summary 而非完整 content,减少 token 消耗。
5
+
6
+ #### Scenario: 有 summary 的 fact
7
+ - **WHEN** 搜索结果中的 fact 有 summary 字段且非空
8
+ - **THEN** 返回 summary 作为 display 字段,不返回完整 content
9
+
10
+ #### Scenario: 无 summary 的 fact
11
+ - **WHEN** 搜索结果中的 fact 的 summary 为 NULL
12
+ - **THEN** 返回 content 前 100 字 + "..." 作为 display 字段
13
+
14
+ #### Scenario: 返回字段精简
15
+ - **WHEN** 搜索结果返回给调用方
16
+ - **THEN** 每条结果包含 factId、display(精简内容)、category、trustScore、score,不包含完整 content、keywords、tags 等冗余字段
@@ -0,0 +1,38 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: Dream action 整理记忆库
4
+ 系统 SHALL 提供 `fact_store(action="dream")` 操作,执行三阶段整理:Collect → Consolidate → Evaluate。
5
+
6
+ #### Scenario: 合并重叠 fact
7
+ - **WHEN** 同 category 内两条 fact 的 Jaccard 相似度 > 0.6
8
+ - **THEN** 系统保留 content 更长的 fact,将另一条标记删除,并在 dream report 中记录合并对
9
+
10
+ #### Scenario: 压缩长 fact
11
+ - **WHEN** fact 的 content 长度 > 200 字且 summary 为 NULL
12
+ - **THEN** 系统从 content 提取前 2 个完整句子(总长 ≤ 150 字)写入 summary 字段
13
+
14
+ #### Scenario: 分类修正
15
+ - **WHEN** fact 的 category 与内容不匹配(如 identity 类 fact 内容包含"编码规范")
16
+ - **THEN** 系统根据关键词规则表将 fact 挪到正确 category
17
+
18
+ #### Scenario: Dream 前备份
19
+ - **WHEN** dream action 被触发
20
+ - **THEN** 系统在执行任何修改前,自动将数据库备份到 `~/.mnemo/backup/dream-<timestamp>.db`
21
+
22
+ #### Scenario: 输出 dream report
23
+ - **WHEN** dream 整理完成
24
+ - **THEN** 系统返回 JSON 报告,包含 merged、compressed、reclassified、deleted 计数和 health 统计
25
+
26
+ ### Requirement: CLI dream 命令
27
+ 系统 SHALL 提供 `mnemo dream` CLI 命令,手动触发 dream 整理。
28
+
29
+ #### Scenario: 手动执行 dream
30
+ - **WHEN** 用户运行 `npx mnemo dream` 或 `mnemo dream`
31
+ - **THEN** 系统执行完整 dream cycle 并输出 report 到 stdout
32
+
33
+ ### Requirement: 高频 fact 保护
34
+ Dream 整理 SHALL 保护检索次数 > 100 的 fact 不被删除。
35
+
36
+ #### Scenario: 高频 fact 不被合并删除
37
+ - **WHEN** 两条 fact 满足合并条件,但其中一条 retrieval_count > 100
38
+ - **THEN** 系统保留高频 fact,仅删除另一条低频 fact
@@ -0,0 +1,27 @@
1
+ ## 1. 搜索结果精简
2
+
3
+ - [ ] 1.1 修改 `ScoredFact` 类型定义,新增 `display` 字段(精简内容)
4
+ - [ ] 1.2 修改 `retriever.ts` search 方法:有 summary 用 summary,无 summary 截取 content 前 100 字
5
+ - [ ] 1.3 修改 `server.ts` search/probe/related/reason 响应格式:返回 display 而非完整 content
6
+ - [ ] 1.4 更新 `tests/retriever.test.ts` 验证精简格式
7
+
8
+ ## 2. Dream Cycle - Store 层
9
+
10
+ - [ ] 2.1 实现 `mergeOverlappingFacts()` — 同 category 内 Jaccard > 0.6 的 fact 合并,高频(retrieval > 100)保护
11
+ - [ ] 2.2 实现 `compressLongFacts()` — content > 200 字且无 summary 的 fact 自动提取前 2 句
12
+ - [ ] 2.3 实现 `reclassifyFacts()` — 按关键词规则表修正 category
13
+ - [ ] 2.4 实现 `backupDatabase()` — dream 前备份到 `~/.mnemo/backup/dream-<timestamp>.db`
14
+ - [ ] 2.5 实现 `runDream()` — 编排以上步骤,生成 dream report(merged/compressed/reclassified/deleted + health)
15
+ - [ ] 2.6 新增 `dream` action 到 `server.ts` 的 fact_store handler
16
+
17
+ ## 3. CLI 命令
18
+
19
+ - [ ] 3.1 新增 `src/dream.ts` CLI 入口,执行 `store.runDream()` 并输出 report
20
+ - [ ] 3.2 在 `package.json` 添加 `mnemo dream` bin 入口
21
+
22
+ ## 4. 测试
23
+
24
+ - [ ] 4.1 `tests/store.test.ts` — mergeOverlappingFacts 合并 + 高频保护测试
25
+ - [ ] 4.2 `tests/store.test.ts` — compressLongFacts 提取 summary 测试
26
+ - [ ] 4.3 `tests/store.test.ts` — reclassifyFacts 分类修正测试
27
+ - [ ] 4.4 `tests/store.test.ts` — runDream 端到端测试(备份数据库 + 整理 + report)
@@ -0,0 +1,2 @@
1
+ schema: spec-driven
2
+ created: 2026-05-16
@@ -0,0 +1,84 @@
1
+ ## Context
2
+
3
+ mnemo-mcp 是一个 SQLite 驱动的记忆管理 MCP 服务器,当前 dream cycle 通过硬编码规则(Jaccard 词频、关键词匹配、截断压缩)整理记忆。用户有 71 条事实,规则引擎几乎不产生效果(merge=0, compress=0, reclassified=反复震荡)。
4
+
5
+ 用户本地有 Ollama(HomeUbuntu 上运行 qwen3:8b 等),也有云端 API(智谱/DeepSeek/Kimi)。触发方式保持手动(CLI `mnemo-dream` 或 MCP `dream` action)。
6
+
7
+ ## Goals / Non-Goals
8
+
9
+ **Goals:**
10
+ - LLM 理解语义后做合并、摘要、分类,产生实际可感知的整理效果
11
+ - 本地优先:默认 Ollama,零成本
12
+ - 云端可插拔:支持 OpenAI 兼容 API(智谱/DeepSeek/Kimi 等)
13
+ - 安全:硬编码安全层保护高信任/高频 fact
14
+ - 降级:LLM 不可用时回退到规则引擎
15
+
16
+ **Non-Goals:**
17
+ - 自动定时触发(保持手动触发)
18
+ - 流式输出 / 进度回调
19
+ - 多轮对话式确认(全自动,触发即执行)
20
+ - 训练/微调模型
21
+
22
+ ## Decisions
23
+
24
+ ### Decision 1: 统一使用 OpenAI 兼容 `/v1/chat/completions` 接口
25
+
26
+ **选择**: 只实现一个 OpenAI 兼容客户端。Ollama 本地(localhost:11434/v1)和 Ollama 云端(ollama.com/v1)以及所有国产模型 API(智谱/DeepSeek/Kimi)都走 `/v1/chat/completions`
27
+
28
+ **备选**: 分别实现 Ollama 原生 API(/api/chat)和 OpenAI API 两个客户端
29
+
30
+ **理由**:
31
+ - Ollama 已原生支持 OpenAI 兼容接口(`/v1/chat/completions`)
32
+ - 用户可配置 baseUrl 指向:本地 Ollama / ollama.com / 智谱 / DeepSeek / 任意 OpenAI 兼容 API
33
+ - 只需一个客户端实现,零 SDK 依赖,用 Node.js 原生 `fetch()`
34
+ - 配置示例:`{ baseUrl: "http://localhost:11434/v1" }` 或 `{ baseUrl: "https://ollama.com/v1", apiKey: "..." }`
35
+
36
+ ### Decision 2: 批量处理,每批 20 条 fact 送 LLM
37
+
38
+ **选择**: 同 category 的 facts 按每批 20 条分组,一次性送 LLM 分析
39
+
40
+ **备选**: 逐条送 LLM / 全部一次性送
41
+
42
+ **理由**:
43
+ - 逐条:token 浪费(每次都传 system prompt),延迟高
44
+ - 全部一次:71 条 fact 的 content 总长约 40K 字,超出小模型上下文
45
+ - 每批 20 条:约 5-10K 字 input,适合 8B 模型(如 qwen3:8b 的 32K 上下文)
46
+
47
+ ### Decision 3: 配置文件可选,无配置时用默认值
48
+
49
+ **选择**: `~/.mnemo/config.json` 为可选文件。不存在时默认 `ollama/localhost:11434/qwen3:8b`
50
+
51
+ **备选**: 必须配置才能使用 LLM dream
52
+
53
+ **理由**:
54
+ - 用户 HomeUbuntu 上已有 Ollama 运行,开箱即用
55
+ - 零配置降低使用门槛
56
+ - macOS 本地无 Ollama 时自动降级到规则引擎
57
+
58
+ ### Decision 4: 安全层在 LLM 输出之后、数据库操作之前执行
59
+
60
+ **选择**: LLM 返回操作建议 → 安全层校验(信任度/数量/格式) → 执行数据库操作
61
+
62
+ **备选**: 在 LLM prompt 中加入安全约束
63
+
64
+ **理由**:
65
+ - LLM 可能不遵守 prompt 约束(尤其是小模型)
66
+ - 安全长用硬编码 TypeScript 保证不会误删
67
+ - 职责分离:LLM 负责理解语义,代码负责安全边界
68
+
69
+ ### Decision 5: Prompt 用中文,适配中文记忆内容
70
+
71
+ **选择**: 所有 LLM prompt 使用中文指令
72
+
73
+ **理由**:
74
+ - mnemo 的 fact 内容主要是中文
75
+ - 中文 prompt 对中文内容的理解更准确
76
+ - qwen3 对中文支持好
77
+
78
+ ## Risks / Trade-offs
79
+
80
+ - **[小模型幻觉风险]** qwen3:8b 可能输出格式错误的 JSON → 用 try-catch 解析,解析失败丢弃该批结果,降级到规则引擎处理该批
81
+ - **[Ollama 连接失败]** 用户本地未启动 Ollama → 自动降级到规则引擎,dream report 中标记 `fallback: true`
82
+ - **[Token 成本]** 使用云端 API 时每次 dream 消耗 token → 配置中可设 provider,默认 Ollama 零成本
83
+ - **[删除误伤]** LLM 可能错误判断"语义重复" → 安全层限制:单次最多删除 10% fact,信任度 > 0.8 禁止删除
84
+ - **[处理速度]** 71 条 fact 批量送 LLM,每批 20 条约 3 批,总耗时 10-30 秒(取决于模型速度) → 可接受,dream 本就不频繁
@@ -0,0 +1,36 @@
1
+ ## Why
2
+
3
+ 当前 dream cycle 使用硬编码规则(Jaccard 词频合并、关键词重分类、截断压缩),无法理解语义。导致:
4
+ - 两条用词不同但语义相同的 fact(如"喜欢VS Code" vs "偏好Visual Studio Code")永远无法合并
5
+ - 分类只靠关键词匹配,容易震荡(同一条 fact 反复换分类)
6
+ - 摘要只是截取前两句,不是真正的信息提炼
7
+
8
+ 需要引入 LLM 做语义级别的记忆整理,让 dream 产生实际效果。
9
+
10
+ ## What Changes
11
+
12
+ - 新增 LLM 客户端抽象层,支持 Ollama(默认)和 OpenAI 兼容 API
13
+ - 新增配置系统(`~/.mnemo/config.json`),支持 LLM provider/model/参数配置
14
+ - 改造 dream cycle 三个核心任务为 LLM 驱动:
15
+ - **语义合并**:LLM 判断同 category facts 是否语义重复,输出合并建议
16
+ - **智能摘要**:LLM 提取长 fact 的核心信息作为 summary
17
+ - **智能分类**:LLM 判断 general 分类的 fact 应归属哪个 category
18
+ - 新增安全验证层(硬编码规则,不经过 LLM):信任度保护、删除数量上限、备份
19
+ - Ollama 不可用时自动降级到当前规则引擎
20
+
21
+ ## Capabilities
22
+
23
+ ### New Capabilities
24
+ - `llm-client`: LLM 客户端抽象层,支持 Ollama 和 OpenAI 兼容 API,包含配置加载和健康检查
25
+ - `llm-dream-engine`: LLM 驱动的 dream engine,包含语义合并、智能摘要、智能分类三个任务,安全验证层,降级策略
26
+
27
+ ### Modified Capabilities
28
+ - `dream-cycle`: runDream() 从纯规则引擎改为调用 LLM dream engine,保留规则引擎作为降级方案
29
+
30
+ ## Impact
31
+
32
+ - **新增文件**:`src/llm-client.ts`(LLM 客户端)、`src/dream-engine.ts`(LLM dream engine)
33
+ - **修改文件**:`src/store.ts`(runDream 集成 dream engine)、`src/types.ts`(新增配置类型)、`src/server.ts`(dream action 传递配置)
34
+ - **新增依赖**:无(使用 Node.js 原生 fetch 调用 Ollama/OpenAI API)
35
+ - **配置文件**:新增 `~/.mnemo/config.json` 支持可选配置
36
+ - **向后兼容**:Ollama 不可用时自动降级到规则引擎,不影响现有用户
@@ -0,0 +1,42 @@
1
+ ## MODIFIED Requirements
2
+
3
+ ### Requirement: Dream action 整理记忆库
4
+ 系统 SHALL 提供 `fact_store(action="dream")` 操作,优先使用 LLM 做语义级整理(合并、摘要、分类),LLM 不可用时降级到规则引擎。整理前自动备份数据库。
5
+
6
+ #### Scenario: LLM 驱动的语义合并
7
+ - **WHEN** 同 category 内存在语义重复的 fact(由 LLM 判断)
8
+ - **THEN** 系统合并重复 fact,保留内容更完整的,在 dream report 中记录合并对和原因
9
+
10
+ #### Scenario: LLM 驱动的智能摘要
11
+ - **WHEN** fact 的 content 长度 > 200 字且 summary 为 NULL
12
+ - **THEN** 系统由 LLM 生成精准摘要(≤ 150 字)写入 summary 字段
13
+
14
+ #### Scenario: LLM 驱动的智能分类
15
+ - **WHEN** fact 的 category 为 "general" 但内容属于其他 category
16
+ - **THEN** 系统由 LLM 判断正确分类并更新
17
+
18
+ #### Scenario: 降级到规则引擎
19
+ - **WHEN** LLM 服务不可用(连接失败/超时)
20
+ - **THEN** 系统自动降级到规则引擎(Jaccard 合并、截取摘要、关键词分类),report 中标记 fallback: true
21
+
22
+ #### Scenario: Dream 前备份
23
+ - **WHEN** dream action 被触发
24
+ - **THEN** 系统在执行任何修改前,自动将数据库备份到备份目录
25
+
26
+ #### Scenario: 输出 dream report
27
+ - **WHEN** dream 整理完成
28
+ - **THEN** 系统返回 JSON 报告,包含 merged、compressed、reclassified、deleted 计数、health 统计、fallback 标记
29
+
30
+ ### Requirement: CLI dream 命令
31
+ 系统 SHALL 提供 `mnemo-dream` CLI 命令,手动触发 LLM 驱动的 dream 整理。
32
+
33
+ #### Scenario: 手动执行 dream
34
+ - **WHEN** 用户运行 `mnemo-dream`
35
+ - **THEN** 系统执行 LLM 驱动的 dream cycle 并输出 report 到 stdout
36
+
37
+ ### Requirement: 高频 fact 保护
38
+ Dream 整理 SHALL 保护检索次数 > 100 或信任度 > 0.8 的 fact 不被删除,无论 LLM 是否建议删除。
39
+
40
+ #### Scenario: 高频/高信任 fact 不被合并删除
41
+ - **WHEN** LLM 建议删除 retrieval_count > 100 或 trust_score > 0.8 的 fact
42
+ - **THEN** 系统拒绝该删除操作
@@ -0,0 +1,57 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: OpenAI 兼容 LLM 客户端
4
+ 系统 SHALL 提供统一 LLM 客户端,使用 OpenAI 兼容的 `/v1/chat/completions` 接口。通过配置 baseUrl 支持 Ollama 本地、Ollama 云端(ollama.com/v1)、智谱、DeepSeek 等任何 OpenAI 兼容 API。
5
+
6
+ #### Scenario: 连接 Ollama 本地
7
+ - **WHEN** config.baseUrl 为 "http://localhost:11434/v1"
8
+ - **THEN** 系统向 `localhost:11434/v1/chat/completions` 发送请求,无需 apiKey
9
+
10
+ #### Scenario: 连接 Ollama 云端
11
+ - **WHEN** config.baseUrl 为 "https://ollama.com/v1" 且提供 apiKey
12
+ - **THEN** 系统向 `ollama.com/v1/chat/completions` 发送请求,附带 Authorization header
13
+
14
+ #### Scenario: 连接第三方 OpenAI 兼容 API
15
+ - **WHEN** config.baseUrl 为 "https://open.bigmodel.cn/api/paas/v4" 且提供 apiKey
16
+ - **THEN** 系统向对应 `/chat/completions` 端点发送请求
17
+
18
+ ### Requirement: LLM 聊天接口
19
+ 系统 SHALL 提供 `chat(messages, options)` 方法,返回 LLM 文本响应。
20
+
21
+ #### Scenario: 成功调用返回文本
22
+ - **WHEN** 调用 chat([{ role: "user", content: "..." }], { temperature: 0.1 })
23
+ - **THEN** 系统返回 LLM 生成的文本内容
24
+
25
+ #### Scenario: 连接失败抛出错误
26
+ - **WHEN** LLM 服务不可用(连接拒绝/超时)
27
+ - **THEN** 系统抛出 LLMConnectionError,包含原始错误信息
28
+
29
+ #### Scenario: JSON 响应解析
30
+ - **WHEN** LLM 响应内容可解析为 JSON
31
+ - **THEN** 系统返回解析后的 JSON 对象
32
+
33
+ #### Scenario: JSON 解析失败
34
+ - **WHEN** LLM 响应不是有效 JSON
35
+ - **THEN** 系统抛出 LLMResponseError,包含原始响应文本
36
+
37
+ ### Requirement: LLM 健康检查
38
+ 系统 SHALL 提供 `isAvailable()` 方法,检测 LLM 服务是否可达。
39
+
40
+ #### Scenario: 服务可用
41
+ - **WHEN** 调用 isAvailable()
42
+ - **THEN** 系统向 baseUrl/models 端点发送 GET 请求,成功返回 true
43
+
44
+ #### Scenario: 服务不可用
45
+ - **WHEN** 调用 isAvailable()
46
+ - **THEN** 连接失败返回 false,不抛出错误
47
+
48
+ ### Requirement: 配置加载
49
+ 系统 SHALL 从 `~/.mnemo/config.json` 加载 LLM 配置。文件不存在时使用默认配置。
50
+
51
+ #### Scenario: 配置文件存在
52
+ - **WHEN** `~/.mnemo/config.json` 存在且包含 llm 字段
53
+ - **THEN** 系统使用配置文件中的 baseUrl/model/apiKey/temperature
54
+
55
+ #### Scenario: 配置文件不存在
56
+ - **WHEN** `~/.mnemo/config.json` 不存在
57
+ - **THEN** 系统使用默认配置:`{ baseUrl: "http://localhost:11434/v1", model: "qwen3:8b", temperature: 0.1 }`
@@ -0,0 +1,72 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: LLM 语义合并
4
+ 系统 SHALL 将同 category 的 facts 按每批 20 条送 LLM,由 LLM 判断语义重复并输出合并建议。
5
+
6
+ #### Scenario: LLM 识别语义重复
7
+ - **WHEN** 同 category 内存在两条用词不同但语义相同的 fact(如"喜欢VS Code"和"偏好Visual Studio Code")
8
+ - **THEN** LLM 返回 merge 建议 `{"kept": factId, "removed": factId, "reason": "..."}`,系统执行合并
9
+
10
+ #### Scenario: LLM 判断不重复
11
+ - **WHEN** 同 category 内两条 fact 语义不同
12
+ - **THEN** LLM 不输出合并建议,系统不操作
13
+
14
+ #### Scenario: LLM 输出格式错误
15
+ - **WHEN** LLM 返回的 JSON 无法解析或缺少必需字段(kept/removed)
16
+ - **THEN** 系统丢弃该批合并建议,不执行任何操作
17
+
18
+ ### Requirement: LLM 智能摘要
19
+ 系统 SHALL 将 content > 200 字且 summary 为 NULL 的 fact 送 LLM 生成精准摘要。
20
+
21
+ #### Scenario: LLM 生成摘要
22
+ - **WHEN** fact 的 content 长度 > 200 且 summary 为空
23
+ - **THEN** LLM 返回 `{"summary": "核心信息..."}`,系统写入 summary 字段,摘要长度 SHALL ≤ 150 字
24
+
25
+ #### Scenario: 已有摘要跳过
26
+ - **WHEN** fact 已有 summary
27
+ - **THEN** 系统不发送给 LLM,跳过该 fact
28
+
29
+ #### Scenario: LLM 摘要超长
30
+ - **WHEN** LLM 返回的 summary 长度 > 150 字
31
+ - **THEN** 系统截断到 150 字
32
+
33
+ ### Requirement: LLM 智能分类
34
+ 系统 SHALL 将 category 为 "general" 的 facts 送 LLM 判断正确分类。
35
+
36
+ #### Scenario: LLM 正确分类
37
+ - **WHEN** general 分类中的 fact 内容属于 identity/coding_style/tool_pref/workflow
38
+ - **THEN** LLM 返回 `{"fact_id": id, "to": "target_category"}`,系统更新 category
39
+
40
+ #### Scenario: LLM 判断应保持 general
41
+ - **WHEN** fact 内容不属于其他四个 category
42
+ - **THEN** LLM 返回 `{"fact_id": id, "to": "general"}`,系统不操作
43
+
44
+ #### Scenario: LLM 返回无效 category
45
+ - **WHEN** LLM 返回的 target 不在 [identity, coding_style, tool_pref, workflow, general] 中
46
+ - **THEN** 系统丢弃该分类建议
47
+
48
+ ### Requirement: 安全验证层
49
+ 系统 SHALL 在执行 LLM 建议的数据库操作前,进行硬编码安全校验。
50
+
51
+ #### Scenario: 高信任 fact 禁止删除
52
+ - **WHEN** LLM 建议删除 trust_score > 0.8 的 fact
53
+ - **THEN** 系统拒绝该删除操作
54
+
55
+ #### Scenario: 单次删除数量限制
56
+ - **WHEN** LLM 建议删除的 fact 数量超过总量的 10%
57
+ - **THEN** 系统只执行前 10% 的删除,丢弃多余建议
58
+
59
+ #### Scenario: 高频 fact 保护
60
+ - **WHEN** LLM 建议删除 retrieval_count > 100 的 fact
61
+ - **THEN** 系统拒绝该删除操作
62
+
63
+ ### Requirement: 降级策略
64
+ 系统 SHALL 在 LLM 不可用时自动降级到规则引擎。
65
+
66
+ #### Scenario: Ollama 不可用自动降级
67
+ - **WHEN** Ollama 连接失败或超时
68
+ - **THEN** 系统自动使用当前硬编码规则引擎执行 dream,report 中标记 `fallback: true`
69
+
70
+ #### Scenario: 降级后 report 包含 fallback 标记
71
+ - **WHEN** dream 执行了降级路径
72
+ - **THEN** DreamReport 中 `fallback` 字段为 true,`fallbackReason` 字段记录原因
@@ -0,0 +1,32 @@
1
+ ## 1. 类型定义与配置
2
+
3
+ - [ ] 1.1 在 `src/types.ts` 新增 LLM 相关类型:LLMConfig(baseUrl/model/apiKey/temperature)、LLMMessage(role/content)、DreamReport 新增 fallback/fallbackReason 字段
4
+ - [ ] 1.2 新增 `src/config.ts`:loadConfig() 函数,从 `~/.mnemo/config.json` 读取配置,文件不存在时返回默认值(localhost:11434/v1, qwen3:8b, temperature 0.1)
5
+
6
+ ## 2. LLM 客户端
7
+
8
+ - [ ] 2.1 新增 `src/llm-client.ts`:实现 OpenAI 兼容 `/v1/chat/completions` 客户端,包含 chat(messages, options) 和 isAvailable() 方法
9
+ - [ ] 2.2 chat() 方法:POST 请求到 baseUrl/chat/completions,解析 choices[0].message.content,支持 JSON 响应提取
10
+ - [ ] 2.3 isAvailable() 方法:GET baseUrl/models,成功返回 true,失败返回 false(不抛错)
11
+ - [ ] 2.4 错误处理:LLMConnectionError(连接失败)、LLMResponseError(响应解析失败)
12
+
13
+ ## 3. LLM Dream Engine
14
+
15
+ - [ ] 3.1 新增 `src/dream-engine.ts`:DreamEngine 类,接收 LLMClient 和 MemoryStore 实例
16
+ - [ ] 3.2 实现 llmSemanticMerge(category, facts):将同 category 的 facts 按每批 20 条送 LLM,prompt 要求输出 JSON 格式的合并建议,解析后返回操作列表
17
+ - [ ] 3.3 实现 llmSmartCompress(facts):将长 fact(content > 200, summary 为空)送 LLM 生成摘要,返回摘要操作列表
18
+ - [ ] 3.4 实现 llmSmartReclassify(facts):将 general 分类的 facts 送 LLM 判断正确分类,返回分类操作列表
19
+ - [ ] 3.5 实现安全验证层 validateOperations(operations, totalFacts):过滤掉 trust_score > 0.8、retrieval_count > 100 的删除操作,限制删除总数 ≤ 10%
20
+ - [ ] 3.6 实现降级逻辑:LLM 不可用时(isAvailable() 返回 false 或 chat 抛错),调用现有规则引擎方法
21
+
22
+ ## 4. 集成到 Dream Cycle
23
+
24
+ - [ ] 4.1 修改 `src/store.ts` 的 runDream():加载配置 → 创建 LLMClient → 创建 DreamEngine → 优先 LLM 整理 → 降级规则引擎
25
+ - [ ] 4.2 修改 `src/server.ts` 的 dream action:传递配置,处理 fallback 标记
26
+ - [ ] 4.3 修改 `src/dream.ts` CLI:确保 CLI 也使用新的 dream engine
27
+
28
+ ## 5. 测试
29
+
30
+ - [ ] 5.1 新增 `tests/llm-client.test.ts`:测试 chat() 成功/失败/JSON 解析、isAvailable() 可用/不可用
31
+ - [ ] 5.2 新增 `tests/dream-engine.test.ts`:测试语义合并/智能摘要/智能分类(mock LLM 响应)、安全验证层、降级策略
32
+ - [ ] 5.3 更新 `tests/store.test.ts`:dream 测试用例适配新的 DreamReport 字段(fallback/fallbackReason)
@@ -0,0 +1,16 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: 搜索结果精简格式
4
+ 搜索返回结果 SHALL 优先返回 summary 而非完整 content,减少 token 消耗。
5
+
6
+ #### Scenario: 有 summary 的 fact
7
+ - **WHEN** 搜索结果中的 fact 有 summary 字段且非空
8
+ - **THEN** 返回 summary 作为 display 字段,不返回完整 content
9
+
10
+ #### Scenario: 无 summary 的 fact
11
+ - **WHEN** 搜索结果中的 fact 的 summary 为 NULL
12
+ - **THEN** 返回 content 前 100 字 + "..." 作为 display 字段
13
+
14
+ #### Scenario: 返回字段精简
15
+ - **WHEN** 搜索结果返回给调用方
16
+ - **THEN** 每条结果包含 factId、display(精简内容)、category、trustScore、score,不包含完整 content、keywords、tags 等冗余字段
@@ -0,0 +1,38 @@
1
+ ## ADDED Requirements
2
+
3
+ ### Requirement: Dream action 整理记忆库
4
+ 系统 SHALL 提供 `fact_store(action="dream")` 操作,执行三阶段整理:Collect → Consolidate → Evaluate。
5
+
6
+ #### Scenario: 合并重叠 fact
7
+ - **WHEN** 同 category 内两条 fact 的 Jaccard 相似度 > 0.6
8
+ - **THEN** 系统保留 content 更长的 fact,将另一条标记删除,并在 dream report 中记录合并对
9
+
10
+ #### Scenario: 压缩长 fact
11
+ - **WHEN** fact 的 content 长度 > 200 字且 summary 为 NULL
12
+ - **THEN** 系统从 content 提取前 2 个完整句子(总长 ≤ 150 字)写入 summary 字段
13
+
14
+ #### Scenario: 分类修正
15
+ - **WHEN** fact 的 category 与内容不匹配(如 identity 类 fact 内容包含"编码规范")
16
+ - **THEN** 系统根据关键词规则表将 fact 挪到正确 category
17
+
18
+ #### Scenario: Dream 前备份
19
+ - **WHEN** dream action 被触发
20
+ - **THEN** 系统在执行任何修改前,自动将数据库备份到 `~/.mnemo/backup/dream-<timestamp>.db`
21
+
22
+ #### Scenario: 输出 dream report
23
+ - **WHEN** dream 整理完成
24
+ - **THEN** 系统返回 JSON 报告,包含 merged、compressed、reclassified、deleted 计数和 health 统计
25
+
26
+ ### Requirement: CLI dream 命令
27
+ 系统 SHALL 提供 `mnemo dream` CLI 命令,手动触发 dream 整理。
28
+
29
+ #### Scenario: 手动执行 dream
30
+ - **WHEN** 用户运行 `npx mnemo dream` 或 `mnemo dream`
31
+ - **THEN** 系统执行完整 dream cycle 并输出 report 到 stdout
32
+
33
+ ### Requirement: 高频 fact 保护
34
+ Dream 整理 SHALL 保护检索次数 > 100 的 fact 不被删除。
35
+
36
+ #### Scenario: 高频 fact 不被合并删除
37
+ - **WHEN** 两条 fact 满足合并条件,但其中一条 retrieval_count > 100
38
+ - **THEN** 系统保留高频 fact,仅删除另一条低频 fact
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@morningljn/mnemo",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Structured fact memory MCP server — SQLite + FTS5, trust scoring, entity graph, bilingual retrieval for Claude Code & Codex",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",
7
7
  "bin": {
8
8
  "mnemo": "dist/server.js",
9
- "mnemo-init": "dist/init.js"
9
+ "mnemo-init": "dist/init.js",
10
+ "mnemo-dream": "dist/dream.js"
10
11
  },
11
12
  "publishConfig": {
12
13
  "access": "public"
package/src/config.ts ADDED
@@ -0,0 +1,29 @@
1
+ import { existsSync, readFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import { homedir } from 'node:os'
4
+ import type { LLMConfig } from './types.js'
5
+
6
+ const DEFAULT_CONFIG: LLMConfig = {
7
+ baseUrl: 'http://localhost:11434/v1',
8
+ model: 'qwen3:8b',
9
+ temperature: 0.1,
10
+ }
11
+
12
+ export function loadConfig(): LLMConfig {
13
+ const configPath = join(homedir(), '.mnemo', 'config.json')
14
+ if (!existsSync(configPath)) return { ...DEFAULT_CONFIG }
15
+
16
+ try {
17
+ const raw = readFileSync(configPath, 'utf-8')
18
+ const parsed = JSON.parse(raw)
19
+ const llm = parsed.llm ?? {}
20
+ return {
21
+ baseUrl: llm.baseUrl ?? DEFAULT_CONFIG.baseUrl,
22
+ model: llm.model ?? DEFAULT_CONFIG.model,
23
+ apiKey: llm.apiKey,
24
+ temperature: llm.temperature ?? DEFAULT_CONFIG.temperature,
25
+ }
26
+ } catch {
27
+ return { ...DEFAULT_CONFIG }
28
+ }
29
+ }