dsh-layered-memory 0.8.12 → 0.9.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/README.en.md +63 -41
- package/README.md +44 -28
- package/assets/changelog/0.9.0/01-/350/256/260/345/277/206/350/212/257/347/211/207/347/272/247/350/201/224/350/217/234/345/215/225.png +0 -0
- package/assets/changelog/0.9.0/02-/345/267/245/344/275/234/345/217/260/346/200/273/350/247/210.png +0 -0
- package/assets/changelog/0.9.0/03-/350/256/260/345/277/206/345/272/223.png +0 -0
- package/assets/changelog/0.9.0/04-/346/264/236/345/257/237/346/264/273/345/212/250.png +0 -0
- package/assets/img/MemoryChip.png +0 -0
- package/assets/img/ui-dark.png +0 -0
- package/assets/img/ui-light.png +0 -0
- package/dist/client.js +2652 -2001
- package/dist/contract.d.ts +117 -1
- package/dist/hooks/recall.d.ts +2 -0
- package/dist/hooks/recall.js +2 -0
- package/dist/index.js +2 -0
- package/dist/stats.d.ts +2 -0
- package/dist/stats.js +41 -0
- package/dist/store/l1.d.ts +7 -1
- package/dist/store/l1.js +5 -1
- package/dist/store/persona.d.ts +2 -0
- package/dist/store/persona.js +9 -0
- package/dist/store/scenes.d.ts +3 -0
- package/dist/store/scenes.js +3 -2
- package/dist/store/session-modes.d.ts +14 -1
- package/dist/store/session-modes.js +37 -3
- package/dist/store/sqlite.d.ts +8 -1
- package/dist/store/sqlite.js +24 -4
- package/dist/workspace-aggregates.d.ts +44 -0
- package/dist/workspace-aggregates.js +262 -0
- package/package.json +1 -1
- package/assets/img/Modes.png +0 -0
- package/assets/img/ui-dark.jpg +0 -0
- package/assets/img/ui-light.jpg +0 -0
package/README.en.md
CHANGED
|
@@ -60,8 +60,8 @@ This package declares a `dsh.bundle` composition layer (`cordis.patch.yml`); aft
|
|
|
60
60
|
installation the **plugin entry is mounted automatically** — no need to hand-edit
|
|
61
61
|
`$DSH_HOME/profiles/web/cordis.patch.yml`. Then restart DeepSeek Harness and verify:
|
|
62
62
|
the appearance of `conversations/ records/ scenes/` and `memory.db` under
|
|
63
|
-
`~/.dsh/memory/` means the plugin applied successfully; the "Memory" page in settings
|
|
64
|
-
and the
|
|
63
|
+
`~/.dsh/memory/` means the plugin applied successfully; the "Memory" page in settings (the Memory Workspace with five zones)
|
|
64
|
+
and the memory chip in the input bar (`Memory · Auto`) mean the client half is ready.
|
|
65
65
|
|
|
66
66
|
> ⚠️ **Security note**: installing a plugin = running third-party code with your
|
|
67
67
|
> privileges. This plugin reads session content, writes files in its data directory,
|
|
@@ -112,7 +112,7 @@ also registers three model-callable memory tools: `memory_search` /
|
|
|
112
112
|
**Cost dashboard**: every distillation LLM call (extract / dedup / L2 / L3) writes its
|
|
113
113
|
token cost to a SQLite detail table keyed by `provider/model` (configurable retention,
|
|
114
114
|
default 365 days with rolling cleanup on write; accounting failures only log a warning
|
|
115
|
-
and never block distillation). Visualize it under Settings → Memory → the **Cost**
|
|
115
|
+
and never block distillation). Visualize it under Settings → Memory → Insights → the **Cost** sub-page:
|
|
116
116
|
per-model trend lines (day/week/month granularity + last-N-days window + L1/L2/L3 layer
|
|
117
117
|
filter), a layer × time-window table (calls / output & reasoning tokens / mean / median),
|
|
118
118
|
and per-model totals — distillation overhead at a glance. Input is counted in characters
|
|
@@ -146,45 +146,67 @@ trajectory view):
|
|
|
146
146
|
## Per-Session Memory Modes
|
|
147
147
|
|
|
148
148
|
<p align="center">
|
|
149
|
-
<img src="./assets/img/
|
|
150
|
-
alt="
|
|
149
|
+
<img src="./assets/img/MemoryChip.png" width="72%"
|
|
150
|
+
alt="Session memory chip and cascade menu in dark theme: in the composer-left cluster, right of the Read Only chip sits the "Memory · Auto ▾" chip; clicking opens a rounded popover menu upward with two rows "Memory scope Auto ›" / "Data flow Follow global ›", and hovering the data-flow row reveals a secondary panel listing Follow global ✓ / Read & write / Write only / Paused">
|
|
151
151
|
</p>
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
`
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
153
|
+
The conversation side is a **distributed memory surface** — each kind of information
|
|
154
|
+
lives in the native host seat designed for it; the plugin no longer owns a strip:
|
|
155
|
+
|
|
156
|
+
- **Memory chip** (composer-left cluster, right of the Read Only chip): a borderless
|
|
157
|
+
`Memory · {Auto|Personal|Work}` chip in official composer-chip grammar; the text is
|
|
158
|
+
the resolved truth — `Memory · write-only` (injection off), `Memory · paused`
|
|
159
|
+
(gray dot), `Memory · degraded` (amber dot); zh/en bilingual, following the host
|
|
160
|
+
language.
|
|
161
|
+
- **Cascade menu** (click the chip, opens upward): two rows, `Memory scope {value} ›`
|
|
162
|
+
and `Data flow {value} ›`; data-flow options live in a **hover-only secondary
|
|
163
|
+
panel** (Follow global / Read & write / Write only / Paused) — no click-pinning,
|
|
164
|
+
with bridge hot-zones so slow mouse travel never breaks the hover chain; full
|
|
165
|
+
keyboard path (arrow-key roving + focus reveal).
|
|
166
|
+
- **Inline slider** (click "Memory scope", grows in place): three stops
|
|
167
|
+
Personal / Work / Auto; crossing a stop updates the chip text **live** while
|
|
168
|
+
dragging; keyboard arrows/Home/End + `aria-valuetext`.
|
|
169
|
+
- **Pause-resume snapshot**: switching the data flow to "Paused" enters the off mode
|
|
170
|
+
and persists the pre-pause scope and injection override; resuming restores them
|
|
171
|
+
as-is. Per-session choices persist to `session-modes.json` (stacked with the
|
|
172
|
+
global switches — global is the master gate); L2/L3 are fully family-isolated.
|
|
173
|
+
- **Memory occupancy lives only in the official context meter panel**: opening the
|
|
174
|
+
official ring shows the "Memory" section (recall snippets / memory stable zone);
|
|
175
|
+
the composer area has zero occupancy UI — only a `N pending distill` telemetry
|
|
176
|
+
segment appended to the official stats line.
|
|
177
|
+
- **Write-only sessions (#38)**: pick "Write only" in the data-flow panel for a
|
|
178
|
+
**write-only session** — capture and distillation continue as usual (conversation
|
|
179
|
+
still settles into L0→L1→L2/L3), but nothing is injected into this session (recall
|
|
180
|
+
injection, the persona/navigation stable section and the tools guide all stop;
|
|
181
|
+
`memory_search` and the other read tools return a write-only notice). The override
|
|
182
|
+
persists per session; switching back to "Follow global" clears it to the recall
|
|
183
|
+
toggle in Automation. Ideal for debug/eval/sensitive sessions that should absorb
|
|
184
|
+
without interference. Orthogonal to Paused: paused is full stealth (capture off
|
|
185
|
+
too), while write-only keeps the "in" and gates the "out".
|
|
178
186
|
|
|
179
187
|
## UI Preview
|
|
180
188
|
|
|
181
189
|
<p align="center">
|
|
182
|
-
<img src="./assets/img/ui-dark.
|
|
183
|
-
alt="
|
|
184
|
-
<img src="./assets/img/ui-light.
|
|
185
|
-
alt="The same
|
|
190
|
+
<img src="./assets/img/ui-dark.png" width="49.5%"
|
|
191
|
+
alt="Memory Workspace overview in dark theme: five-zone task nav (Overview selected), health summary card (running normally + storage/vector/distill-queue subsystem chips + a pending-distill attention chip), recent-activity list (New/Updated verb tags + Memory/Scene kind tags + relative times), key-number tiles (memories / scenes / weekly distill output / last distill) and jump buttons to the other zones">
|
|
192
|
+
<img src="./assets/img/ui-light.png" width="49.5%"
|
|
193
|
+
alt="The same Memory Workspace overview in light theme: identical five-zone nav and health/activity/numbers layout on light card backgrounds with the same accent family, theme switch without reload">
|
|
186
194
|
</p>
|
|
187
195
|
|
|
196
|
+
Settings → Memory is the **Memory Workspace** (five-zone task nav, sticky tabs +
|
|
197
|
+
arrow-key roving):
|
|
198
|
+
|
|
199
|
+
- **Overview**: health summary + recent activity + key numbers + a guided empty state;
|
|
200
|
+
- **Library**: a **read-only asset activity feed** with L1 memories / L2 scenes /
|
|
201
|
+
L3 personas mixed by update time (search + type/scope/time filters + in-place
|
|
202
|
+
expansion + copy);
|
|
203
|
+
- **Automation**: basic switches + advanced disclosure (distill route chains and
|
|
204
|
+
budgets) + embedding-model disclosure;
|
|
205
|
+
- **Insights**: Cost / Activity (7-day asset activity + distill calls & failures) /
|
|
206
|
+
Recall (cumulative totals + disabled distribution);
|
|
207
|
+
- **Maintenance**: runtime health + diagnostic log + the danger-zone full rebuild
|
|
208
|
+
(confirm modal + progress + cancellable).
|
|
209
|
+
|
|
188
210
|
## Measured Comparison (DSH-MemBench: Automated Benchmark)
|
|
189
211
|
|
|
190
212
|
Screenshots show what the plugin looks like — this section answers "**what does enabling it actually buy you?**" with measured numbers from an **automated benchmark** ([`bench/`](./bench/), one command to reproduce). Method: the same scenario bank with verbatim-identical inputs runs in **Group A (memory on)** with 3 merged repetitions and **Group B (memory off)** with 1 repetition (a memory-off long task burns multiples of the tokens per scenario — a cost guardrail); the dialog track now runs Group A only (memory-off probes in independent sessions cannot succeed, so the control carries no information — retired). Dialog-track environment: DeepSeek official `deepseek-v4-flash`, plugin 0.8.5 (judge same-source as tested; every answer archived for manual audit), Windows; taxonomy adapted from [LongMemEval](https://github.com/xiaowu0162/longmemeval) / [LoCoMo](https://snap-research.github.io/locomo/) / [AMB](https://github.com/vectorize-io/agent-memory-benchmark), with the extended probe types and lifecycle track informed by [MemoryAgentBench](https://arxiv.org/abs/2507.05257) / [GoodAI LTM](https://github.com/GoodAI/goodai-ltm-benchmark) / BEAM.
|
|
@@ -288,13 +310,13 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
|
|
|
288
310
|
| `embedding.allowLocalModels` | `true` | Allow the local embedding tier (deployment ceiling; when off, no model downloads and no local tier in settings) |
|
|
289
311
|
| `embedding.mirror` | `https://hf-mirror.com` | Download mirror root for local models (can be changed back to `https://huggingface.co`) |
|
|
290
312
|
| `embedding.proxy` | `''` | Three-state download proxy: `''` (default) = auto-detect proxy env vars (`HTTPS_PROXY`/`ALL_PROXY` etc., honoring `NO_PROXY`); `none` = disable, always direct; any other value = proxy URL (e.g. `http://127.0.0.1:7890`). Direct connections to the mirror are intermittently unreachable on some networks (connect timeouts and poisoned bytes have both been observed) — keep the default auto-detection on machines with a proxy |
|
|
291
|
-
| `llm.provider/model` | empty | Static distillation route (deployment pin): when **both** fields are set the route is locked, outranking the settings-page runtime route chain and the default model (deployments can force distillation onto a specific route); when empty the route follows "settings-page route-chain primary → default model". At runtime, configure the primary route and fallback chain in the **route-chain editor** under Settings → Memory →
|
|
313
|
+
| `llm.provider/model` | empty | Static distillation route (deployment pin): when **both** fields are set the route is locked, outranking the settings-page runtime route chain and the default model (deployments can force distillation onto a specific route); when empty the route follows "settings-page route-chain primary → default model". At runtime, configure the primary route and fallback chain in the **route-chain editor** under Settings → Memory → Automation → Advanced routing and budgets (pick from **configured providers**, including custom ones added in dsh Settings → Models; the primary row may stay empty to follow the default model) — a non-empty chain takes over this static config wholesale, effective immediately with no restart |
|
|
292
314
|
| `llm.fallbacks` | `[]` | Distillation fallback chain: an ordered list of backup routes tried one by one when the primary route fails (error / cut-off / network error / **empty output**); each entry is `{provider, model, reasoningEffort?}` (a non-empty effort overrides the global `llm.reasoningEffort`, still clamped by model capability); entries identical to the primary route are skipped; **each route gets the full `timeoutMs`**; when all routes fail, the existing per-session backoff takes over. Empty list (default) = single-route behavior unchanged (see [Distillation fallback chain & slow-TTFT models](#distillation-fallback-chain--slow-ttft-models) below); a non-empty settings-page runtime chain (`distillChain`) takes over **both** the primary route and the fallback chain (a single-row chain = explicitly no fallbacks), empty = follow this config |
|
|
293
315
|
| `llm.layerRoutes` | `{}` | **Per-layer distillation routing** (#34): keys `l1`/`l2`/`l3`, each holding a **complete chain** (entries like `llm.fallbacks`, **head row must have both provider+model explicitly**). A non-empty chain **fully replaces** that layer's resolution (its primary and fallbacks all come from the layer chain; the global chain no longer participates); empty/missing = the layer follows the global chain. `l1` covers both extraction and dedup call sites. Layers can also be edited at runtime in the segmented panel under distillation parameters on the settings page (takes priority over this static config); a deployment pin does not disable static layer chains (same deployer-owned config as the fallback-chain precedent). Orthogonal to and composable with the fallback chain — one complete chain per layer (ADR-0005) |
|
|
294
|
-
| `llm.maxTokens` | `65536` | Fallback output cap for non-layered calls. Each distillation stage has its own budget (extraction 16k / dedup 8k / L2 32k / L3 16k; auto ×4 when the reasoning effort is high/xhigh/max, so thinking can't starve the text budget); the per-layer budgets are runtime-adjustable in Settings → Memory →
|
|
316
|
+
| `llm.maxTokens` | `65536` | Fallback output cap for non-layered calls. Each distillation stage has its own budget (extraction 16k / dedup 8k / L2 32k / L3 16k; auto ×4 when the reasoning effort is high/xhigh/max, so thinking can't starve the text budget); the per-layer budgets are runtime-adjustable in Settings → Memory → Automation → Advanced routing and budgets (empty/0 = built-in defaults) |
|
|
295
317
|
| `llm.reasoningEffort` | empty | Distillation reasoning effort: empty = **auto** (resolved from model capability: the model's default tier, else `high`); an explicit value (`off`/`none`/`minimal`/`low`/`medium`/`high`/`xhigh`/`max`) is only sent when the model declares support — effort vocabularies differ across providers (deepseek accepts `off`, OpenAI-style APIs use `none`, models that declare no tiers get nothing), and unsupported tiers degrade to not-sending with a one-time warning; output budgets auto-×4 at high/xhigh/max. At runtime, override the effort **per route** in the settings-page route-chain editor (per-row dropdown; the tier list follows each model's declared capability live, defaulting to this value) |
|
|
296
318
|
| `llm.temperature` | `0.3` | Distillation temperature |
|
|
297
|
-
| `llm.maxInputChars` | `700000` | Input character budget per distillation call (over-budget L1 inputs are chunked automatically); runtime-adjustable in Settings →
|
|
319
|
+
| `llm.maxInputChars` | `700000` | Input character budget per distillation call (over-budget L1 inputs are chunked automatically); runtime-adjustable in Settings → Memory → Automation → Advanced routing and budgets → input budget (empty/0 = follow this value) |
|
|
298
320
|
| `llm.timeoutMs` | `120000` | Per-call distillation timeout (ms) |
|
|
299
321
|
| `tokenCost.retentionDays` | `365` | Retention (days) for distillation cost details (the `token_cost` table); rows older than this are rolled away on write. `0` = keep forever. Also the upper bound of the cost dashboard's "last N days" window |
|
|
300
322
|
| `tools` | `true` | Whether to register model-callable memory tools |
|
|
@@ -304,7 +326,7 @@ the bundle layer appends and causes `duplicate loader entry id` startup failure)
|
|
|
304
326
|
|
|
305
327
|
Free/slow tiers of some inference providers have **first-token latencies (TTFT) upwards of 20 seconds**, while some upstream gateways cut a silent connection at ~20s — distillation calls then fail at a fixed ~20s (`llm aborted`) long before the plugin's 120s timeout could ever matter (the scenario measured in [#31](https://github.com/JunNanLYS/dsh-layered-memory/issues/31)). Three mitigations, pick as needed:
|
|
306
328
|
|
|
307
|
-
1. **Switch route** (most direct): change the primary route live in the route-chain editor under Settings → Memory →
|
|
329
|
+
1. **Switch route** (most direct): change the primary route live in the route-chain editor under Settings → Memory → Automation → Advanced routing and budgets (or move a fast route to the head of the chain), or pin `llm.provider`/`llm.model` statically.
|
|
308
330
|
2. **Fallback chain** (automatic demotion): when the primary route fails, backup routes are tried in order with no manual intervention:
|
|
309
331
|
|
|
310
332
|
```yaml
|
|
@@ -336,9 +358,9 @@ Free/slow tiers of some inference providers have **first-token latencies (TTFT)
|
|
|
336
358
|
reasoningEffort: high
|
|
337
359
|
```
|
|
338
360
|
|
|
339
|
-
Layers can also be edited at runtime in the **segmented panel** (global default / L1 / L2 / L3) under Settings → Memory →
|
|
361
|
+
Layers can also be edited at runtime in the **segmented panel** (global default / L1 / L2 / L3) under Settings → Memory → Automation → Advanced routing and budgets. In-layer priority: runtime layer chain > this static YAML layer chain > global default chain, falling back level by level.
|
|
340
362
|
|
|
341
|
-
Failure = error / cut-off / network error / **empty output** (stream ends normally with 0 characters — worthless for distillation since parsing always fails, so it is treated as a route failure rather than an empty return); caller-initiated cancellation does not demote; each route gets the **full** `llm.timeoutMs` (a shared budget would give a slow-TTFT fallback route less time than its real first-packet needs, defeating the chain); token costs are recorded per attempt (failed attempts get a row too, with whatever tokens arrived before the stream broke), and successful calls are attributed to the route that actually served. The route chain can also be adjusted at runtime in the route-chain editor under Settings → Memory →
|
|
363
|
+
Failure = error / cut-off / network error / **empty output** (stream ends normally with 0 characters — worthless for distillation since parsing always fails, so it is treated as a route failure rather than an empty return); caller-initiated cancellation does not demote; each route gets the **full** `llm.timeoutMs` (a shared budget would give a slow-TTFT fallback route less time than its real first-packet needs, defeating the chain); token costs are recorded per attempt (failed attempts get a row too, with whatever tokens arrived before the stream broke), and successful calls are attributed to the route that actually served. The route chain can also be adjusted at runtime in the route-chain editor under Settings → Memory → Automation → Advanced routing and budgets (no config edit or restart needed); the YAML below suits deployments that want to pin the static chain.
|
|
342
364
|
4. **Raise the timeout**: `llm.timeoutMs` only helps when the route is genuinely slow but the gateway doesn't cut; if the gateway kills at 20s, raising the plugin timeout is futile — use the first two layers.
|
|
343
365
|
|
|
344
366
|
## Storage Layout
|
|
@@ -354,7 +376,7 @@ local), switchable at runtime in the settings page — see the next section.
|
|
|
354
376
|
|
|
355
377
|
## Semantic Retrieval (Embedding Source)
|
|
356
378
|
|
|
357
|
-
Pick the embedding source in Settings → Memory →
|
|
379
|
+
Pick the embedding source in Settings → Memory → Automation → embedding models;
|
|
358
380
|
it takes effect immediately, no config edit or restart:
|
|
359
381
|
|
|
360
382
|
<p align="center">
|
package/README.md
CHANGED
|
@@ -54,7 +54,8 @@ Agent 应当返回安装结果,并明确告诉你配置中是否已经出现 `
|
|
|
54
54
|
本包声明了 `dsh.bundle` 组合包层(`cordis.patch.yml`),安装后会**自动挂载插件行**——
|
|
55
55
|
不需要再手改 `$DSH_HOME/profiles/web/cordis.patch.yml`。然后重启 DeepSeek Harness,
|
|
56
56
|
验证:`~/.dsh/memory/` 下出现 `conversations/ records/ scenes/` 目录和 `memory.db`
|
|
57
|
-
即插件 apply 成功;设置页出现"记忆"
|
|
57
|
+
即插件 apply 成功;设置页出现"记忆"页面(记忆工作台五区)、输入栏出现记忆芯片
|
|
58
|
+
(`记忆 · 智能`)即 client 半边就绪。
|
|
58
59
|
|
|
59
60
|
**卸载**:`dsh plugin --profile web remove dsh-layered-memory` + 重启。数据保留在
|
|
60
61
|
`~/.dsh/memory/`,不需要时手动删除整个目录即可。
|
|
@@ -81,7 +82,7 @@ npx tsc src/smoke.ts --outDir dist-smoke --module nodenext --moduleResolution no
|
|
|
81
82
|
|
|
82
83
|
**成本看板**:每次蒸馏 LLM 调用(抽取/去重/L2/L3)的 token 成本按 `provider/model` 写入
|
|
83
84
|
SQLite 明细表(保留期可配置,默认 365 天,写入时滚动清理;记账失败只告警、绝不阻塞蒸馏),
|
|
84
|
-
设置页 → 记忆 → **成本**
|
|
85
|
+
设置页 → 记忆 → 洞察 → **成本** 子页可视化:按模型分色的趋势折线(日/周/月粒度 + 近 N 天窗口 +
|
|
85
86
|
L1/L2/L3 层级过滤)、层级 × 时间窗口表格(调用数 / 输出与思考 token / 均值 / 中位数)、
|
|
86
87
|
按模型累计——蒸馏开销一目了然。输入按字符计(dsh 流式 usage 不含输入 token),
|
|
87
88
|
输出与思考按 token 计。
|
|
@@ -115,34 +116,49 @@ L1/L2/L3 层级过滤)、层级 × 时间窗口表格(调用数 / 输出与
|
|
|
115
116
|
## 会话级记忆档位
|
|
116
117
|
|
|
117
118
|
<p align="center">
|
|
118
|
-
<img src="./assets/img/
|
|
119
|
-
alt="
|
|
119
|
+
<img src="./assets/img/MemoryChip.png" width="72%"
|
|
120
|
+
alt="深色主题下的会话记忆芯片与级联菜单:输入栏左簇 Read Only 芯片右侧是「记忆 · 智能 ▾」芯片;点击向上弹出圆角浮层菜单,两行「记忆范围 智能 ›」「数据流 跟随全局 ›」,数据流行右侧悬停出二级子面板列出 跟随全局✓/读写/只写/暂停 四个选项">
|
|
120
121
|
</p>
|
|
121
122
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
123
|
+
会话侧是**分散式记忆面**——信息按类型住进宿主原生座位,插件不再有自有条带:
|
|
124
|
+
|
|
125
|
+
- **记忆芯片**(输入栏左簇、Read Only 芯片右侧):`记忆 · {智能|日常|工作}` 无边框
|
|
126
|
+
芯片(官方 composer chip 语法),文案是解析真值——`记忆 · 只写`(注入关)、
|
|
127
|
+
`记忆 · 暂停`(灰点)、`记忆 · 降级`(琥珀点);zh/en 双语随宿主语言切换。
|
|
128
|
+
- **级联菜单**(点击芯片向上展开):两行 `记忆范围 {值} ›` / `数据流 {值} ›`;数据流
|
|
129
|
+
选项在 **hover 二级子面板**(跟随全局/读写/只写/暂停),点击不固定、桥接热区保证
|
|
130
|
+
慢速移动不断链;键盘通路齐备(方向键巡游 + focus 揭示)。
|
|
131
|
+
- **内联滑条**(点「记忆范围」原地展开):三停点 日常/工作/智能,拖拽跨档时**芯片文字
|
|
132
|
+
实时联动**;键盘方向键/Home/End + `aria-valuetext`。
|
|
133
|
+
- **暂停恢复快照**:数据流切「暂停」进 off 档时记录暂停前范围与注入覆盖,恢复即原样
|
|
134
|
+
还原;每会话选择持久化到 `session-modes.json`(与全局开关叠加,全局是总闸);
|
|
135
|
+
L2/L3 完全分类,分类内容不渗透。
|
|
136
|
+
- **记忆占用只住官方上下文环面板**:点开官方环即可见「记忆」分项小节(召回片段 /
|
|
137
|
+
记忆稳定区);输入区没有任何占用 UI,只在官方统计行追加 `待蒸馏 N` 遥测段。
|
|
138
|
+
- **只写不读(#38)**:数据流选「只写」即**只写会话**——捕获与蒸馏照常(对话照常沉淀
|
|
139
|
+
为 L0→L1→L2/L3),但不向本会话注入任何记忆(召回注入、画像/导航稳定区、工具指南
|
|
140
|
+
一并停止;`memory_search` 等读工具返回只写提示)。覆盖按会话持久化,切回
|
|
141
|
+
「跟随全局」即清除、跟随自动化区的召回开关;适合调试/评测/敏感会话「只吸收不干扰」。
|
|
142
|
+
与暂停正交:暂停是完全隐身(连捕获都关),只写保留「进」关「出」。
|
|
136
143
|
|
|
137
144
|
## 界面预览
|
|
138
145
|
|
|
139
146
|
<p align="center">
|
|
140
|
-
<img src="./assets/img/ui-dark.
|
|
141
|
-
alt="
|
|
142
|
-
<img src="./assets/img/ui-light.
|
|
143
|
-
alt="
|
|
147
|
+
<img src="./assets/img/ui-dark.png" width="49.5%"
|
|
148
|
+
alt="深色主题下的记忆工作台总览:五区任务导航(总览选中),健康摘要卡(运行正常 + 存储/向量检索/蒸馏队列子系统标签 + 待蒸馏注意提示)、最近活动列表(新增/更新动词标签 + 记忆/场景层标签 + 相对时间)、关键数字瓦片(记忆资产/场景/本周蒸馏输出/上次蒸馏)与四区跳转按钮">
|
|
149
|
+
<img src="./assets/img/ui-light.png" width="49.5%"
|
|
150
|
+
alt="浅色主题下的同一记忆工作台总览:同款五区导航与健康摘要/最近活动/关键数字布局,浅色卡片底与同套品牌蓝强调色,主题切换无需重载">
|
|
144
151
|
</p>
|
|
145
152
|
|
|
153
|
+
设置 → 记忆 是**记忆工作台**(五区任务导航,sticky 标签 + 箭头键巡游):
|
|
154
|
+
|
|
155
|
+
- **总览**:健康摘要 + 最近活动 + 关键数字 + 引导式空状态;
|
|
156
|
+
- **记忆库**:L1 记忆 / L2 场景 / L3 画像按更新时间混排的**只读资产活动流**
|
|
157
|
+
(搜索 + 类型/范围/时间筛选 + 原位展开 + 复制);
|
|
158
|
+
- **自动化**:基础开关 + 高级披露(蒸馏路由链与预算)+ 嵌入模型披露;
|
|
159
|
+
- **洞察**:成本 / 活动(近 7 天资产活动 + 蒸馏调用失败)/ 召回(累计与停用分布);
|
|
160
|
+
- **维护**:运行健康 + 诊断日志 + 危险区全量重建(二次确认 + 进度 + 可取消)。
|
|
161
|
+
|
|
146
162
|
## 实测对比(DSH-MemBench:自动化基准)
|
|
147
163
|
|
|
148
164
|
图文回答"长什么样",这一节用**自动化基准**的实测数字回答"**开了到底有什么用**"([`bench/`](./bench/),一条命令可复现)。方法:同场景库、逐字相同输入,**A 组(记忆开)跑 3 次取合并值,B 组(记忆关)跑 1 次**(无记忆的长任务每场景要吞数倍 token,成本护栏);对话赛道只跑 A 组(B 组会话独立无记忆必然失败,对照无信息量,已下线)。对话赛道环境:DeepSeek 官方 `deepseek-v4-flash`、插件 0.8.5(判卷与被测同源,答案原文全部留痕可人工复核)、Windows;题型设计借鉴 [LongMemEval](https://github.com/xiaowu0162/longmemeval) / [LoCoMo](https://snap-research.github.io/locomo/) / [AMB](https://github.com/vectorize-io/agent-memory-benchmark),扩展题型与生命周期赛道参照 [MemoryAgentBench](https://arxiv.org/abs/2507.05257) / [GoodAI LTM](https://github.com/GoodAI/goodai-ltm-benchmark) / BEAM。
|
|
@@ -204,7 +220,7 @@ node bench/harness/retrieval-metrics.mjs <runDir> --flood 200,600
|
|
|
204
220
|
|
|
205
221
|
## 语义检索(嵌入源)
|
|
206
222
|
|
|
207
|
-
设置页(记忆 →
|
|
223
|
+
设置页(记忆 → 自动化 → 嵌入模型)选择嵌入源,即时生效、无需改配置重启:
|
|
208
224
|
|
|
209
225
|
<p align="center">
|
|
210
226
|
<img src="./assets/img/EmbeddingSource.png" width="70%"
|
|
@@ -284,10 +300,10 @@ ONNX 量化 **CPU 推理**——无需 API Key,数据不出本机)。本地
|
|
|
284
300
|
| `embedding.allowLocalModels` | `true` | 允许本地嵌入档(部署上限:关闭后设置页不能下载模型、不能切本地档) |
|
|
285
301
|
| `embedding.mirror` | `https://hf-mirror.com` | 本地模型下载镜像根地址(可改回官方 `https://huggingface.co`) |
|
|
286
302
|
| `embedding.proxy` | `''` | 模型下载代理三态:`''`(默认)= 自动探测代理环境变量(`HTTPS_PROXY`/`ALL_PROXY` 等,尊重 `NO_PROXY`);`none` = 禁用强制直连;其他值 = 代理 URL(如 `http://127.0.0.1:7890`)。镜像直连在国内网络间歇不可达(直连超时与污染字节交替出现过),开代理的机器建议保持默认自动探测 |
|
|
287
|
-
| `llm.provider/model` | 空 | 蒸馏模型静态路由(部署 pin):provider 与 model **双字段齐**时锁定蒸馏路由,优先于设置页的运行时路由链与默认模型(部署可强制蒸馏走指定路由);留空则跟随"设置页路由链主路由 → 默认模型"。运行时可在设置页 → 记忆 →
|
|
303
|
+
| `llm.provider/model` | 空 | 蒸馏模型静态路由(部署 pin):provider 与 model **双字段齐**时锁定蒸馏路由,优先于设置页的运行时路由链与默认模型(部署可强制蒸馏走指定路由);留空则跟随"设置页路由链主路由 → 默认模型"。运行时可在设置页 → 记忆 → 自动化 → 高级路由与预算的**蒸馏路由链编辑器**里配置主路由与回退链(从**已配置的供应商**(含 dsh 设置 → 模型里添加的自定义供应商)中选择,主路由行可留空跟随默认模型),非空即整体接管本静态配置,即时生效无需重启 |
|
|
288
304
|
| `llm.fallbacks` | `[]` | 蒸馏回退链:主路由失败(报错/被掐断/网络异常/**空输出**)后按条目顺序逐个降级尝试的备用路由列表,条目 = `{provider, model, reasoningEffort?}`(档位非空覆盖全局 `llm.reasoningEffort`,仍按模型能力钳制);与主路由完全相同的条目自动跳过;**每条路由各享全额 `timeoutMs`**;全部失败交既有按会话退避重试。空数组(缺省)= 单路由行为不变(详见下方[蒸馏回退链与慢 TTFT 模型](#蒸馏回退链与慢-ttft-模型));设置页运行时路由链(`distillChain`)非空时**整体接管**主路由与回退链(单行链 = 显式无回退),空 = 跟随本配置 |
|
|
289
305
|
| `llm.layerRoutes` | `{}` | 蒸馏**按层路由**:层键 `l1`/`l2`/`l3` 各配一条**完整链**(条目同 `llm.fallbacks`,**头行必须 provider+model 双显式**),非空即**完整替换**该层解析(该层主路由与回退都归层链管,全局链对该层不参与),空/缺省 = 该层跟随全局;`l1` 同管抽取+去重两个调用点。运行时可在设置页「蒸馏参数」分段面板里按层编辑(优先于本静态配置);部署 pin 不废静态层链(同为部署配置,同回退链先例)。与回退链正交可组合——每层各自一条链(ADR-0005) |
|
|
290
|
-
| `llm.maxTokens` | `65536` | 未分层调用的兜底输出总闸。各蒸馏层有独立预算(抽取 16k / 去重 8k / L2 32k / L3 16k;思考档 high/xhigh/max 时自动 ×4,防 reasoning 吃光预算),分层预算可在设置页 → 记忆 →
|
|
306
|
+
| `llm.maxTokens` | `65536` | 未分层调用的兜底输出总闸。各蒸馏层有独立预算(抽取 16k / 去重 8k / L2 32k / L3 16k;思考档 high/xhigh/max 时自动 ×4,防 reasoning 吃光预算),分层预算可在设置页 → 记忆 → 自动化 → 高级路由与预算运行时调整(留空/0 = 跟随内置默认) |
|
|
291
307
|
| `llm.reasoningEffort` | 空 | 蒸馏思考档位:空串 = **自动**(按模型能力解析:模型默认档 → `high`);显式值(`off`/`none`/`minimal`/`low`/`medium`/`high`/`xhigh`/`max`)仅在该模型声明支持时发送——跨供应商 effort 词汇表不同(deepseek 认 `off`,OpenAI 系是 `none`,未声明档位的模型不传),不支持的档位自动降级为不传并告警一次;思考档 high/xhigh/max 时输出预算自动 ×4。运行时可在设置页路由链编辑器里**逐路由**覆盖档位(行内下拉,词表按各模型声明的能力实时显示,缺省跟随本值) |
|
|
292
308
|
| `llm.temperature` | `0.3` | 蒸馏温度 |
|
|
293
309
|
| `llm.maxInputChars` | `700000` | 单次蒸馏输入字符预算(超限的 L1 输入自动分块抽取);运行时可在设置页 → 蒸馏参数 → 输入预算调整(留空/0 = 跟随本值) |
|
|
@@ -300,7 +316,7 @@ ONNX 量化 **CPU 推理**——无需 API Key,数据不出本机)。本地
|
|
|
300
316
|
|
|
301
317
|
部分推理供应商的免费/慢速档位**首 token 延迟(TTFT)可达 20 秒以上**,而部分上游网关会在连接静默约 20 秒时掐断——蒸馏调用以固定 ~20s 失败(`llm aborted`),插件侧 120s 超时根本轮不到生效([#31](https://github.com/JunNanLYS/dsh-layered-memory/issues/31) 的实测场景)。三层缓解按需取用:
|
|
302
318
|
|
|
303
|
-
1. **换路由**(最直接):设置页 → 记忆 →
|
|
319
|
+
1. **换路由**(最直接):设置页 → 记忆 → 自动化 → 高级路由与预算的路由链编辑器即时改主路由(或把快路由排到链首),或静态 pin `llm.provider`/`llm.model`。
|
|
304
320
|
2. **回退链**(自动降级):主路由失败时按序自动换备用路由,无需人工干预:
|
|
305
321
|
|
|
306
322
|
```yaml
|
|
@@ -334,11 +350,11 @@ ONNX 量化 **CPU 推理**——无需 API Key,数据不出本机)。本地
|
|
|
334
350
|
reasoningEffort: high
|
|
335
351
|
```
|
|
336
352
|
|
|
337
|
-
也可在设置页 → 记忆 →
|
|
353
|
+
也可在设置页 → 记忆 → 自动化 → 高级路由与预算的**分段面板**(全局默认 / L1 / L2 / L3)
|
|
338
354
|
里按层运行时编辑,层内优先级:运行时层链 > 本 YAML 静态层链 > 全局默认链,
|
|
339
355
|
逐级兜底。
|
|
340
356
|
|
|
341
|
-
失败 = 报错 / 被掐断 / 网络异常 / **空输出**(流正常结束但 0 字符——对蒸馏而言必然在解析阶段报废,改判为该路由失败而非返回空串);调用方主动取消不降级;每条路由各享**全额** `llm.timeoutMs`(共享预算会让慢 TTFT 的回退路由拿到的窗口小于它真实需要的首包时间,回退链形同虚设);token 成本逐次尝试记账(失败尝试也计一行,含流中断前已到的 token),成功调用归因到实际服务的路由。路由链也可在设置页 → 记忆 →
|
|
357
|
+
失败 = 报错 / 被掐断 / 网络异常 / **空输出**(流正常结束但 0 字符——对蒸馏而言必然在解析阶段报废,改判为该路由失败而非返回空串);调用方主动取消不降级;每条路由各享**全额** `llm.timeoutMs`(共享预算会让慢 TTFT 的回退路由拿到的窗口小于它真实需要的首包时间,回退链形同虚设);token 成本逐次尝试记账(失败尝试也计一行,含流中断前已到的 token),成功调用归因到实际服务的路由。路由链也可在设置页 → 记忆 → 自动化 → 高级路由与预算的「蒸馏路由链」编辑器里运行时调整(无需改配置重启);本 YAML 适合部署者固化静态链。
|
|
342
358
|
4. **调高超时**:`llm.timeoutMs` 只在路由确实慢但网关不掐时有用;网关 20s 掐断的场景调插件超时无效,请用前两层。
|
|
343
359
|
|
|
344
360
|
## 日志与故障排查
|
|
Binary file
|
package/assets/changelog/0.9.0/02-/345/267/245/344/275/234/345/217/260/346/200/273/350/247/210.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|