@modusensus/dsh-mneme 0.1.5 → 0.1.6
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/README.md +26 -7
- package/lib/index.js +1 -1
- package/lib/tools.js +24 -4
- package/package.json +2 -2
- package/src/index.js +1 -1
- package/src/tools.js +24 -4
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@modusensus/dsh-mneme)
|
|
4
4
|
[](LICENSE)
|
|
5
5
|
[](https://github.com/awesome-dsh-plugin/awesome-dsh-plugin)
|
|
6
|
-
[](https://github.com/modusensus/dsh-mneme)
|
|
7
7
|
|
|
8
8
|
> 给 DeepSeek Harness 的跨会话记忆插件:让 Agent 记住你、记住项目、自动整理记忆。**Mneme**(Μνήμη)——希腊记忆女神 Mnemosyne 之名,掌管记忆与梦境,正如 autoDream 在后台巩固记忆。
|
|
9
9
|
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
| 工具 | 功能 |
|
|
23
23
|
|------|------|
|
|
24
24
|
| `memory_save` | 记录一条记忆(自动按标题去重合并) |
|
|
25
|
-
| `memory_search` |
|
|
25
|
+
| `memory_search` | 全文搜索(中文子串友好,可启用向量语义搜索) |
|
|
26
26
|
| `memory_list` | 按类型分页列出 |
|
|
27
27
|
| `memory_update` | 修改已有记忆 |
|
|
28
28
|
| `memory_delete` | 删除记忆 |
|
|
@@ -45,11 +45,11 @@
|
|
|
45
45
|
|
|
46
46
|
### Web 记忆面板
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
官方设置面板 → 「记忆库设置」→「记忆」标签:按类型浏览、全文搜索;启用向量搜索后可用「语义」切换做向量召回。
|
|
49
49
|
|
|
50
50
|
### 用户设置(画像 / 规则)与自定义指令 ⚙️
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
官方设置面板 → 「记忆库设置」标签:
|
|
53
53
|
|
|
54
54
|
- **用户画像**:一段自由文本描述用户自己(角色、背景、偏好),**每轮注入**到系统提示,让 Agent 始终遵循
|
|
55
55
|
- **规则**:Agent 必须遵守的行为规则列表(如"回答先给结论"),同样每轮注入
|
|
@@ -57,6 +57,24 @@
|
|
|
57
57
|
|
|
58
58
|
> 画像与规则通过独立的 `[用户设置]` 注入区块(优先级高于记忆库),即使记忆为空也会注入。
|
|
59
59
|
|
|
60
|
+
### 向量搜索(语义搜索)🔎
|
|
61
|
+
|
|
62
|
+
可选能力:接入 OpenAI 兼容的 embeddings API,让搜索能命中**字面不同但语义相近**的记忆。
|
|
63
|
+
|
|
64
|
+
**配置**:官方设置 → 「记忆库设置」→ 滚动到底部「向量搜索」区块:
|
|
65
|
+
|
|
66
|
+
| 字段 | 说明 |
|
|
67
|
+
|------|------|
|
|
68
|
+
| `启用向量搜索` | 总开关;开启后记忆面板出现「语义」切换 |
|
|
69
|
+
| `API 地址 (Base URL)` | OpenAI 兼容端点,如 `https://api.openai.com/v1`;也支持 SiliconFlow、智谱、本地 Ollama 等 |
|
|
70
|
+
| `API Key` | 对应服务的密钥 |
|
|
71
|
+
| `模型名` | embedding 模型,如 `text-embedding-3-small`、`text-embedding-v3`、`bge-m3` 等 |
|
|
72
|
+
|
|
73
|
+
保存配置后点「重建索引」,为已有记忆批量补建向量(新写入的记忆会自动嵌入)。之后在记忆面板输入查询并点「语义」,即可用向量召回语义相关结果;向量服务不可用时自动回退全文搜索。
|
|
74
|
+
|
|
75
|
+
> ⚠️ 密钥仅保存在本机 `~/.dsh/memory/memory.db` 的 `user_settings` 表,不会上传,也不会写入代码仓库。
|
|
76
|
+
> 需要 embedding 而非 rerank 模型:如阿里云 `text-embedding-v3` 可用,`qwen3-vl-rerank` 是 rerank 模型(不走 `/embeddings`)。
|
|
77
|
+
|
|
60
78
|
## 📦 安装
|
|
61
79
|
|
|
62
80
|
### 前置条件
|
|
@@ -138,7 +156,7 @@ dsh web
|
|
|
138
156
|
│ autoDream:阈值调度 → LLM 决策清单 │
|
|
139
157
|
│ → 校验(fail-safe)→ 应用 → 摘要 │
|
|
140
158
|
├─────────────────────────────────────────────────┤
|
|
141
|
-
│ Web
|
|
159
|
+
│ Web 面板:设置面板内嵌 + 浏览/搜索(含向量) │
|
|
142
160
|
└─────────────────────────────────────────────────┘
|
|
143
161
|
```
|
|
144
162
|
|
|
@@ -155,12 +173,13 @@ src/
|
|
|
155
173
|
├── summarize.js # 会话结束 LLM 摘要
|
|
156
174
|
├── dream.js # autoDream 调度 + runDream(LLM 决策 + 摘要)
|
|
157
175
|
├── dream/decisions.js# 决策校验(fail-safe)+ 决策应用
|
|
176
|
+
├── embedding.js # OpenAI 兼容 embeddings 客户端 + 向量检索
|
|
158
177
|
├── api.js # HTTP 路由(Web 面板数据通道)
|
|
159
178
|
└── index.js # 插件接线
|
|
160
179
|
lib/
|
|
161
180
|
├── client.js # Web 面板(手写 ModuleLoader bundle)
|
|
162
181
|
└── *.js # src 的同步分发产物
|
|
163
|
-
test/ #
|
|
182
|
+
test/ # 136 个 node:test 测试
|
|
164
183
|
```
|
|
165
184
|
|
|
166
185
|
## 🧪 开发
|
|
@@ -168,7 +187,7 @@ test/ # 129 个 node:test 测试
|
|
|
168
187
|
```bash
|
|
169
188
|
cd dsh-mneme
|
|
170
189
|
npm install # 安装 peer 依赖(以 devDependencies 形式,用于本地测试)
|
|
171
|
-
npm test # 运行
|
|
190
|
+
npm test # 运行 136 个测试(--test-isolation=none 用于受限沙箱,禁止子进程 spawn)
|
|
172
191
|
npm run sync # 把 src/ 同步到 lib/(发布时由 prepack 钩子自动执行)
|
|
173
192
|
```
|
|
174
193
|
|
package/lib/index.js
CHANGED
|
@@ -91,7 +91,7 @@ export const apply = (ctx, config) => {
|
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
ctx.inject(["tools"], (toolsCtx) => {
|
|
94
|
-
disposers.push(createTools(toolsCtx, service, cfg));
|
|
94
|
+
disposers.push(createTools(toolsCtx, service, cfg, embedder));
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
const summarizer = createSummarizer(ctx, service, cfg);
|
package/lib/tools.js
CHANGED
|
@@ -21,7 +21,7 @@ const MEMORY_ITEM_SCHEMA = {
|
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
export function createTools(ctx, service, config) {
|
|
24
|
+
export function createTools(ctx, service, config, embedder) {
|
|
25
25
|
const tools = [
|
|
26
26
|
defineTool({
|
|
27
27
|
name: "memory_save",
|
|
@@ -63,10 +63,12 @@ export function createTools(ctx, service, config) {
|
|
|
63
63
|
|
|
64
64
|
defineTool({
|
|
65
65
|
name: "memory_search",
|
|
66
|
-
description: "
|
|
66
|
+
description: "Search the cross-session memory store. Use when you need past context: how a problem was solved, user preferences, project decisions. Substring-matches title/content/tags, and optionally augments results with semantic (vector) recall when an embeddings provider is configured. Returns matching entries with source and timestamps.",
|
|
67
67
|
parameters: {
|
|
68
68
|
query: { type: "string", required: true, description: "Search text; substring match over title/content/tags" },
|
|
69
|
-
limit: { type: "integer", description: "Max results (default 20)" }
|
|
69
|
+
limit: { type: "integer", description: "Max results (default 20)" },
|
|
70
|
+
mode: { type: "string", enum: ["auto", "keyword", "vector"], description: "auto (default) = keyword hits first + vector fill when enabled; keyword = text only; vector = semantic recall first (falls back to keyword)" },
|
|
71
|
+
semantic: { type: "boolean", description: "Shorthand: enable semantic (vector) recall (same as mode=vector when true)" }
|
|
70
72
|
},
|
|
71
73
|
output: {
|
|
72
74
|
schema: {
|
|
@@ -82,7 +84,25 @@ export function createTools(ctx, service, config) {
|
|
|
82
84
|
render: (_args, value) => TEXT_OUTPUT(`Found ${value.items.length} memory entr${value.items.length === 1 ? "y" : "ies"}.`)
|
|
83
85
|
},
|
|
84
86
|
async execute(args) {
|
|
85
|
-
const
|
|
87
|
+
const limit = args.limit ?? 20;
|
|
88
|
+
const rows = service.toApiList(service.search(args.query, { limit }));
|
|
89
|
+
const wantVector = args.mode === "vector" || args.semantic === true || args.mode === "auto";
|
|
90
|
+
if (wantVector && embedder) {
|
|
91
|
+
try {
|
|
92
|
+
const vector = await embedder.embed(args.query);
|
|
93
|
+
if (vector) {
|
|
94
|
+
const scored = service.toApiList(service.searchVector(vector, { limit }));
|
|
95
|
+
const seen = new Set(rows.map((m) => m.id));
|
|
96
|
+
for (const m of scored) {
|
|
97
|
+
if (rows.length >= limit) break;
|
|
98
|
+
if (!seen.has(m.id)) {
|
|
99
|
+
seen.add(m.id);
|
|
100
|
+
rows.push(m);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch { /* vector unavailable: keep keyword results */ }
|
|
105
|
+
}
|
|
86
106
|
return { items: rows };
|
|
87
107
|
}
|
|
88
108
|
}),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modusensus/dsh-mneme",
|
|
3
|
-
"description": "Cross-session memory plugin for DeepSeek Harness with autoDream consolidation: SQLite store, Markdown mirrors, 6 model tools, automatic injection, session summarization, and a Web GUI panel",
|
|
4
|
-
"version": "0.1.
|
|
3
|
+
"description": "Cross-session memory plugin for DeepSeek Harness with autoDream consolidation: SQLite store, Markdown mirrors, 6 model tools, automatic injection, session summarization, user profile/rules, custom slash commands, vector (semantic) search, and a Web GUI panel",
|
|
4
|
+
"version": "0.1.6",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "lib/index.js",
|
package/src/index.js
CHANGED
|
@@ -91,7 +91,7 @@ export const apply = (ctx, config) => {
|
|
|
91
91
|
});
|
|
92
92
|
|
|
93
93
|
ctx.inject(["tools"], (toolsCtx) => {
|
|
94
|
-
disposers.push(createTools(toolsCtx, service, cfg));
|
|
94
|
+
disposers.push(createTools(toolsCtx, service, cfg, embedder));
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
const summarizer = createSummarizer(ctx, service, cfg);
|
package/src/tools.js
CHANGED
|
@@ -21,7 +21,7 @@ const MEMORY_ITEM_SCHEMA = {
|
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
export function createTools(ctx, service, config) {
|
|
24
|
+
export function createTools(ctx, service, config, embedder) {
|
|
25
25
|
const tools = [
|
|
26
26
|
defineTool({
|
|
27
27
|
name: "memory_save",
|
|
@@ -63,10 +63,12 @@ export function createTools(ctx, service, config) {
|
|
|
63
63
|
|
|
64
64
|
defineTool({
|
|
65
65
|
name: "memory_search",
|
|
66
|
-
description: "
|
|
66
|
+
description: "Search the cross-session memory store. Use when you need past context: how a problem was solved, user preferences, project decisions. Substring-matches title/content/tags, and optionally augments results with semantic (vector) recall when an embeddings provider is configured. Returns matching entries with source and timestamps.",
|
|
67
67
|
parameters: {
|
|
68
68
|
query: { type: "string", required: true, description: "Search text; substring match over title/content/tags" },
|
|
69
|
-
limit: { type: "integer", description: "Max results (default 20)" }
|
|
69
|
+
limit: { type: "integer", description: "Max results (default 20)" },
|
|
70
|
+
mode: { type: "string", enum: ["auto", "keyword", "vector"], description: "auto (default) = keyword hits first + vector fill when enabled; keyword = text only; vector = semantic recall first (falls back to keyword)" },
|
|
71
|
+
semantic: { type: "boolean", description: "Shorthand: enable semantic (vector) recall (same as mode=vector when true)" }
|
|
70
72
|
},
|
|
71
73
|
output: {
|
|
72
74
|
schema: {
|
|
@@ -82,7 +84,25 @@ export function createTools(ctx, service, config) {
|
|
|
82
84
|
render: (_args, value) => TEXT_OUTPUT(`Found ${value.items.length} memory entr${value.items.length === 1 ? "y" : "ies"}.`)
|
|
83
85
|
},
|
|
84
86
|
async execute(args) {
|
|
85
|
-
const
|
|
87
|
+
const limit = args.limit ?? 20;
|
|
88
|
+
const rows = service.toApiList(service.search(args.query, { limit }));
|
|
89
|
+
const wantVector = args.mode === "vector" || args.semantic === true || args.mode === "auto";
|
|
90
|
+
if (wantVector && embedder) {
|
|
91
|
+
try {
|
|
92
|
+
const vector = await embedder.embed(args.query);
|
|
93
|
+
if (vector) {
|
|
94
|
+
const scored = service.toApiList(service.searchVector(vector, { limit }));
|
|
95
|
+
const seen = new Set(rows.map((m) => m.id));
|
|
96
|
+
for (const m of scored) {
|
|
97
|
+
if (rows.length >= limit) break;
|
|
98
|
+
if (!seen.has(m.id)) {
|
|
99
|
+
seen.add(m.id);
|
|
100
|
+
rows.push(m);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} catch { /* vector unavailable: keep keyword results */ }
|
|
105
|
+
}
|
|
86
106
|
return { items: rows };
|
|
87
107
|
}
|
|
88
108
|
}),
|