pmem-ai 0.7.6 → 0.8.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 (55) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +6 -4
  3. package/dist/commands/ask.d.ts +5 -1
  4. package/dist/commands/ask.d.ts.map +1 -1
  5. package/dist/commands/ask.js +48 -240
  6. package/dist/commands/ask.js.map +1 -1
  7. package/dist/commands/rebuild.d.ts.map +1 -1
  8. package/dist/commands/rebuild.js +74 -10
  9. package/dist/commands/rebuild.js.map +1 -1
  10. package/dist/commands/recall.d.ts +1 -0
  11. package/dist/commands/recall.d.ts.map +1 -1
  12. package/dist/commands/recall.js +1 -1
  13. package/dist/commands/recall.js.map +1 -1
  14. package/dist/commands/verify.d.ts.map +1 -1
  15. package/dist/commands/verify.js +30 -1
  16. package/dist/commands/verify.js.map +1 -1
  17. package/dist/core/db.d.ts +13 -0
  18. package/dist/core/db.d.ts.map +1 -1
  19. package/dist/core/db.js +36 -0
  20. package/dist/core/db.js.map +1 -1
  21. package/dist/core/format.d.ts.map +1 -1
  22. package/dist/core/format.js +37 -0
  23. package/dist/core/format.js.map +1 -1
  24. package/dist/core/query/ask.d.ts +14 -3
  25. package/dist/core/query/ask.d.ts.map +1 -1
  26. package/dist/core/query/ask.js +60 -200
  27. package/dist/core/query/ask.js.map +1 -1
  28. package/dist/core/query/context.d.ts.map +1 -1
  29. package/dist/core/query/context.js +8 -2
  30. package/dist/core/query/context.js.map +1 -1
  31. package/dist/core/query/engine/candidates.d.ts +11 -0
  32. package/dist/core/query/engine/candidates.d.ts.map +1 -0
  33. package/dist/core/query/engine/candidates.js +207 -0
  34. package/dist/core/query/engine/candidates.js.map +1 -0
  35. package/dist/core/query/engine/intent.d.ts +14 -0
  36. package/dist/core/query/engine/intent.d.ts.map +1 -0
  37. package/dist/core/query/engine/intent.js +53 -0
  38. package/dist/core/query/engine/intent.js.map +1 -0
  39. package/dist/core/query/engine/pack.d.ts +33 -0
  40. package/dist/core/query/engine/pack.d.ts.map +1 -0
  41. package/dist/core/query/engine/pack.js +92 -0
  42. package/dist/core/query/engine/pack.js.map +1 -0
  43. package/dist/core/query/engine/scoring.d.ts +50 -0
  44. package/dist/core/query/engine/scoring.d.ts.map +1 -0
  45. package/dist/core/query/engine/scoring.js +132 -0
  46. package/dist/core/query/engine/scoring.js.map +1 -0
  47. package/dist/index.js +15 -3
  48. package/dist/index.js.map +1 -1
  49. package/dist/mcp/server.js +2 -2
  50. package/dist/mcp/server.js.map +1 -1
  51. package/dist/types.d.ts +3 -0
  52. package/dist/types.d.ts.map +1 -1
  53. package/docs/v0.8 pre-design.md +203 -0
  54. package/package.json +2 -2
  55. package/skills/pmem/SKILL.md +4 -2
@@ -0,0 +1,203 @@
1
+ # v0.8 前置设计决策:Hybrid Recall Engine
2
+
3
+ > 状态:draft → 待评审锁定
4
+ > 日期:2026-07-03
5
+ > 依据:`decision.project_rag_os_positioning_20260626`、`decision.structure_first_hybrid_recall_20260626`、`decision.sqlite_first_semantic_layer_20260626`、`feature.v0_8_hybrid_recall_engine_20260626`、`task.rag_research_sprint_20260626`
6
+
7
+ ## 一、背景与问题
8
+
9
+ v0.7.x 的检索现状(代码审计结论,2026-07-03):
10
+
11
+ 1. **逻辑重复**:`src/commands/ask.ts` 与 `src/core/query/ask.ts` 各自维护一份相同的 6 步检索(id → alias → tag → graph 1-hop → FTS/LIKE fallback),已经开始漂移风险。
12
+ 2. **排序原始**:只按 match_type 枚举硬排序 + 静态 confidence 常量,无组合打分。
13
+ 3. **信号缺失**:
14
+ - `paths` 表的 source_file 关系完全未参与 ask 检索——"我在改 src/core/db.ts,相关记忆是什么"这类最高价值查询无法命中;
15
+ - 无时近性(recency)信号,三个月前的过期决策与昨天的活跃决策同权;
16
+ - 无 stale/dirty 惩罚,`dirty_flags` 与 `last_verified_at` 未参与排序;
17
+ - FTS5 五列(title/summary/body/aliases/tags)未使用 bm25 字段权重,正文噪音词与标题精确命中同权。
18
+ 4. **FTS 只是 fallback**:仅当前 5 步零命中时才启用,正常查询根本走不到全文检索——精确信号与文本信号是"或"关系而非融合关系。
19
+ 5. **无解释性**:输出只有 match_type,Agent 无法判断"为什么召回这张卡、该信任多少"。
20
+ 6. **预算是截断不是分层**:`formatOutput` 末端硬截断,没有 L0-L3 分层渲染,预算紧张时丢的是尾部而非最低价值内容。
21
+
22
+ v0.8 目标:**把"能存项目记忆"升级为"能按任务精准恢复项目上下文"**。
23
+
24
+ ## 二、产品目标
25
+
26
+ ### 2.1 一句话目标
27
+
28
+ 一个查询进来,pmem 产出一个**有界、确定、可解释、按预算分层**的召回集。
29
+
30
+ ### 2.2 成功标准
31
+
32
+ 1. 精确信号查询(card ID、文件路径、决策标题、tag)100% 首位命中;
33
+ 2. 模糊关键词查询经 FTS+融合排序后 top-3 命中相关卡片;
34
+ 3. 同一查询在索引未变时输出确定(可复现、可测试);
35
+ 4. 每条召回结果附带机器可读的 reasons 数组;
36
+ 5. stale/dirty 卡片被降权但不被隐藏(附 penalty 说明);
37
+ 6. 预算裁剪按 L0→L3 分层进行,L0 永不被裁。
38
+
39
+ ### 2.3 范围排除(v0.8 不做)
40
+
41
+ - ❌ 向量数据库 / embedding(→ v0.8.5 SQLite-first semantic layer)
42
+ - ❌ LLM rerank / contextual retrieval(→ v0.9)
43
+ - ❌ Web UI(已推迟)
44
+ - ❌ 新增 MCP 工具面(现有 pmem_ask / pmem_context 直接受益于底层升级,不加新工具)
45
+ - ❌ 卡片正文 chunking(v0.8 以整卡为召回单元;trace 已有结构化解析)
46
+
47
+ ## 三、架构设计
48
+
49
+ ### 3.1 检索管线
50
+
51
+ ```
52
+ query (+ optional task context)
53
+
54
+ [1] Intent parse — 识别查询中的精确信号:card-id 形态、文件路径形态、
55
+ 类型词(decision/module/...)、CJK/英文 token
56
+
57
+ [2] Candidate generation — 多路并行召回,每路给出 channel 原始分:
58
+ a. exact id / id-substring
59
+ b. alias
60
+ c. tag(exact + token)
61
+ d. source_files 路径匹配(paths 表,新增)
62
+ e. FTS5 bm25 字段加权(title 4.0 / aliases 3.0 / tags 2.5 / summary 2.0 / body 1.0,
63
+ 常态启用,不再只做 fallback;无 FTS5 时回退 LIKE)
64
+
65
+ [3] Graph expansion — 对 top 种子做 1-hop 扩展(可配 2-hop),
66
+ 继承分 = 种子分 × edge_confidence × 距离衰减(0.5/hop)
67
+
68
+ [4] Scoring / fusion — 组合打分(见 3.2),跨路去重取最高分并合并 reasons
69
+
70
+ [5] Budgeted rendering — L0-L3 分层渲染(见 3.3)
71
+ ```
72
+
73
+ ### 3.2 打分模型
74
+
75
+ 每张候选卡的最终分:
76
+
77
+ ```
78
+ score = base // 候选生成通道的原始分
79
+ × type_weight // 卡类型权重
80
+ × recency_factor // 时近性
81
+ × staleness_penalty // 过期惩罚
82
+ × status_factor // 卡状态
83
+ ```
84
+
85
+ | 因子 | 取值 | 说明 |
86
+ |---|---|---|
87
+ | base: exact_id | 1.0 | 全等命中 |
88
+ | base: exact_title / id-substring | 0.85 | |
89
+ | base: alias | 0.9 | |
90
+ | base: tag exact / token | 0.7 / 0.6 | |
91
+ | base: source_file exact / prefix | 0.9 / 0.75 | 新增通道 |
92
+ | base: fts | 0.3–0.8 | bm25 归一化:`0.3 + 0.5 × norm(-bm25)` |
93
+ | base: graph | 种子分 × conf × 0.5^hop | |
94
+ | type_weight | decision/module 1.1,task/feature/risk 1.0,trace 0.85 | manifest 可覆盖 |
95
+ | recency_factor | `0.75 + 0.25 × exp(-age_days/90)` | updated_at 距今;90 天半衰 |
96
+ | staleness_penalty | dirty_flag 未解决 ×0.7;last_verified 早于 updated_at ×0.9 | 降权不隐藏 |
97
+ | status_factor | active/draft 1.0,superseded/archived 0.5,deprecated 0.3 | |
98
+
99
+ **确定性要求**:同分 tie-break 按 card id 字典序。`Date.now()` 只在入口取一次传入纯函数,保证可测试。
100
+
101
+ ### 3.3 预算分层渲染(L0-L3)
102
+
103
+ | 层 | 内容 | 预算策略 |
104
+ |---|---|---|
105
+ | L0 | 项目一句话、stage、focus、next | 永不裁剪 |
106
+ | L1 | active foundation / key decisions 压缩摘要 | 预算 ≥800 时保留 |
107
+ | L2 | 与 query 强相关的卡片摘要(top-N by score) | 按分数从低到高裁 |
108
+ | L3 | READ_IF_NEEDED 路径列表 | 只有路径,成本极低,尽量保留 |
109
+
110
+ 裁剪顺序:L2 低分项 → L2 摘要变一行 → L1 压缩 → L3 截断。L0 永存。
111
+
112
+ ### 3.4 explain 输出
113
+
114
+ 每条召回项:
115
+
116
+ ```json
117
+ {
118
+ "id": "decision.structure_first_hybrid_recall_20260626",
119
+ "score": 0.81,
120
+ "reasons": [
121
+ {"channel": "tag", "detail": "matched tag: hybrid-search", "base": 0.7},
122
+ {"channel": "fts", "detail": "bm25 title+body", "base": 0.62}
123
+ ],
124
+ "factors": {"type_weight": 1.1, "recency": 0.96, "staleness": 1.0, "status": 1.0},
125
+ "graph_distance": 0,
126
+ "stale": false
127
+ }
128
+ ```
129
+
130
+ compact 格式渲染为单行:`- decision.xxx (0.81) [tag:hybrid-search, fts]`。
131
+
132
+ ### 3.5 代码结构
133
+
134
+ ```
135
+ src/core/query/
136
+ engine/
137
+ intent.ts — 意图解析(纯函数)
138
+ candidates.ts — 多路候选生成(依赖 db)
139
+ scoring.ts — 打分融合(纯函数,可单测)
140
+ pack.ts — L0-L3 预算渲染(纯函数)
141
+ ask.ts — 重写为 engine 编排(保持 AskResultV03 出参兼容 + 新增 explain 字段)
142
+ context.ts — 复用 engine,task-aware
143
+ recall.ts — 接入 pack.ts 分层渲染
144
+ src/commands/ask.ts — 删除重复实现,改为调 core/query/ask.ts(唯一数据路径)
145
+ ```
146
+
147
+ **兼容性约束(对齐 v0.7.0 zero-migration 原则)**:
148
+ - `AskResultV03` 现有字段(query/matched/recommended_files/evidence_paths)保持不变,新字段只增不改;
149
+ - matched 项保留 match_type/confidence/graph_distance/file,新增 score/reasons/factors;
150
+ - MCP `pmem_ask` / `pmem_context` 出参向后兼容;
151
+ - 不改 Markdown card schema — 不需要用户迁移;
152
+ - SQLite runtime index:`rebuild` 自动创建/刷新 `card_fts` FTS5 虚拟表,纯重建产物,不属 schema migration;
153
+ - 不改卡片格式。
154
+
155
+ ## 四、CLI 变化
156
+
157
+ ```bash
158
+ pmem ask "<query>" [--explain] [--limit N]
159
+ pmem recall --mode brief|normal|deep [--budget N]
160
+ pmem context "<task>" --budget N # 底层自动升级,接口不变
161
+ ```
162
+
163
+ - `--explain`:输出 reasons/factors 明细(compact 时为单行注记,json 时全量)。
164
+ - `--mode brief`:只输出 L0+L3;`normal`(默认):L0-L3 标准;`deep`:放宽 L2 数量上限。
165
+
166
+ ## 五、实现分阶段
167
+
168
+ ### Phase 1: Engine core(P0)
169
+ - intent.ts / candidates.ts / scoring.ts 纯函数 + 单测
170
+ - source_file 通道、FTS 常态化 bm25 加权
171
+ - ask.ts 重写为编排层;commands/ask.ts 去重
172
+
173
+ ### Phase 2: Pack & modes(P0)
174
+ - pack.ts L0-L3 分层渲染 + 单测
175
+ - recall --mode、ask --explain/--limit
176
+ - context.ts 接入 engine
177
+
178
+ ### Phase 3: 测试与 dogfood(P0)
179
+ - 大 fixture(≥40 卡)E2E:精确命中、模糊命中、图扩展、stale 降权、预算裁剪、确定性
180
+ - 用 pmem 自身 .pmem dogfood 验证
181
+ - README / SKILL.md 文档更新
182
+
183
+ ## 六、测试计划
184
+
185
+ | 类别 | 用例 |
186
+ |---|---|
187
+ | 精确 | card ID 全等首位;文件路径命中 source_file 通道;tag 命中 |
188
+ | 模糊 | 关键词经 FTS top-3 命中;CJK 查询 |
189
+ | 融合 | 同卡多通道命中分数合并、reasons 累积 |
190
+ | 图 | 1-hop 继承分衰减;2-hop 不越界 |
191
+ | 降权 | dirty 卡降权可见;superseded ×0.5;旧卡 recency 衰减 |
192
+ | 预算 | budget=500 时 L0 完整、L2 被裁;brief/deep 模式差异 |
193
+ | 确定性 | 同查询两次输出逐字节一致 |
194
+ | 回归 | 现有 260 测试全绿;AskResultV03 出参字段不丢 |
195
+
196
+ ## 七、风险
197
+
198
+ | 风险 | 缓解 |
199
+ |---|---|
200
+ | FTS 常态化引入噪音 | fts base 上限 0.8,低于所有精确通道 |
201
+ | 打分调参主观 | 权重集中在 scoring.ts 常量表 + manifest 可覆盖;用 dogfood 查询集校验 |
202
+ | 兼容性破坏 | 出参只增不改;回归测试锁定 |
203
+ | 范围蔓延到 embedding | 明确排除,见 2.3 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmem-ai",
3
- "version": "0.7.6",
3
+ "version": "0.8.0",
4
4
  "description": "Project Memory for AI Agents: a local CLI runtime for project context, recall, and memory updates",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -13,7 +13,7 @@
13
13
  "pretest": "npm run build",
14
14
  "dev": "ts-node src/index.ts",
15
15
  "start": "node dist/index.js",
16
- "test": "node --require ts-node/register --test src/core/*.test.ts src/commands/*.test.ts src/mcp/*.test.ts",
16
+ "test": "node --require ts-node/register --test src/core/*.test.ts src/core/query/*.test.ts src/core/query/engine/*.test.ts src/commands/*.test.ts src/mcp/*.test.ts",
17
17
  "test:e2e:install": "bash scripts/e2e-install-smoke.sh",
18
18
  "test:e2e:workflow": "bash scripts/e2e-real-workflow.sh",
19
19
  "test:e2e:non-git": "bash scripts/e2e-non-git-fallback.sh",
@@ -97,14 +97,16 @@ For `pmem recall --format json`, read `active_foundation`. `active_modules` rema
97
97
 
98
98
  Legacy v0.6.x projects do not need migration. When `schema` is absent, pmem falls back to the old software defaults without rewriting the manifest.
99
99
 
100
- ### Context Recovery (v0.7.5 Trace-Aware)
100
+ ### Context Recovery (v0.8 Hybrid Recall)
101
101
 
102
102
  ```bash
103
103
  # Restore project context (hot memory, ~2000 tokens)
104
104
  pmem recall --format compact --budget 2000
105
+ pmem recall --mode brief --budget 500 # L0 + READ_IF_NEEDED only
105
106
 
106
107
  # Search for specific topics
107
108
  pmem ask "sqlite runtime" --format compact
109
+ pmem ask "src/core/query/recall.ts" --explain --limit 5
108
110
  pmem ask "module.core" --format json # machine-readable
109
111
 
110
112
  # Explore graph neighbors
@@ -112,7 +114,7 @@ pmem related module.core --depth 2
112
114
  pmem trace decision.sqlite_runtime
113
115
  ```
114
116
 
115
- In v0.7.5, `pmem recall` is trace-aware, meaning it reads the recent capture history (traces) and integrates them to restore context. The output is structured into:
117
+ In v0.8 (upcoming on `main` branch, not yet released to npm), `pmem recall` remains trace-aware and adds recall modes, while `pmem ask` uses deterministic hybrid retrieval (exact IDs, aliases, tags, source files, FTS5/BM25, graph expansion, recency, and stale/dirty penalties). The recall output is structured into:
116
118
  - **PROJECT & STAGE**: The current metadata of the repository.
117
119
  - **CURRENT CONTEXT**: Recent summaries of what the project is doing.
118
120
  - **RECENT CHANGES**: Thick trace logs of what files and symbols changed.