@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.
- package/dist/config.d.ts +2 -0
- package/dist/config.js +28 -0
- package/dist/config.js.map +1 -0
- package/dist/dream-engine.d.ts +17 -0
- package/dist/dream-engine.js +144 -0
- package/dist/dream-engine.js.map +1 -0
- package/dist/dream.d.ts +2 -0
- package/dist/dream.js +20 -0
- package/dist/dream.js.map +1 -0
- package/dist/init.js +4 -24
- package/dist/init.js.map +1 -1
- package/dist/llm-client.d.ts +10 -0
- package/dist/llm-client.js +55 -0
- package/dist/llm-client.js.map +1 -0
- package/dist/resources.d.ts +22 -8
- package/dist/resources.js +66 -20
- package/dist/resources.js.map +1 -1
- package/dist/retriever.js +12 -5
- package/dist/retriever.js.map +1 -1
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +2 -2
- package/dist/server.js +40 -6
- package/dist/server.js.map +1 -1
- package/dist/store.d.ts +23 -1
- package/dist/store.js +169 -4
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +41 -1
- package/docs/superpowers/plans/2026-05-16-llm-dream.md +973 -0
- package/docs/superpowers/plans/2026-05-16-memory-dreaming.md +626 -0
- package/openspec/changes/archive/2026-05-16-memory-dreaming/.openspec.yaml +2 -0
- package/openspec/changes/archive/2026-05-16-memory-dreaming/design.md +71 -0
- package/openspec/changes/archive/2026-05-16-memory-dreaming/proposal.md +32 -0
- package/openspec/changes/archive/2026-05-16-memory-dreaming/specs/compact-search/spec.md +16 -0
- package/openspec/changes/archive/2026-05-16-memory-dreaming/specs/dream-cycle/spec.md +38 -0
- package/openspec/changes/archive/2026-05-16-memory-dreaming/tasks.md +27 -0
- package/openspec/changes/llm-dream/.openspec.yaml +2 -0
- package/openspec/changes/llm-dream/design.md +84 -0
- package/openspec/changes/llm-dream/proposal.md +36 -0
- package/openspec/changes/llm-dream/specs/dream-cycle/spec.md +42 -0
- package/openspec/changes/llm-dream/specs/llm-client/spec.md +57 -0
- package/openspec/changes/llm-dream/specs/llm-dream-engine/spec.md +72 -0
- package/openspec/changes/llm-dream/tasks.md +32 -0
- package/openspec/specs/compact-search/spec.md +16 -0
- package/openspec/specs/dream-cycle/spec.md +38 -0
- package/package.json +3 -2
- package/src/config.ts +29 -0
- package/src/dream-engine.ts +162 -0
- package/src/dream.ts +20 -0
- package/src/init.ts +4 -24
- package/src/llm-client.ts +59 -0
- package/src/resources.ts +77 -21
- package/src/retriever.ts +9 -5
- package/src/schema.ts +2 -2
- package/src/server.ts +46 -7
- package/src/store.ts +198 -5
- package/src/types.ts +41 -1
- package/tests/dream-engine.test.ts +163 -0
- package/tests/llm-client.test.ts +105 -0
- package/tests/resource.test.ts +25 -23
- 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,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.
|
|
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
|
+
}
|