Bundle
dsh-plugin-semantic-memory
Semantic long-term memory for DeepSeek Harness: embedding-based retrieval over a persistent cross-session memory store, with model-facing tools, proactive per-question recall, automatic conversation summarization, and auto-selected embedding provider (apiKey present → API, otherwise local).
- Source
- chenkezhen480
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 11 days ago
Readme
# dsh-plugin-semantic-memory
**中文**([README.zh.md](README.zh.md),推荐) | [English](README.md)
Semantic long-term memory for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness).
A `dsh-plugin` (Cordis plugin) that gives the model a persistent, embedding-based
memory across sessions — unlike the harness's built-in `session_query` (literal
FTS5), this store retrieves by *meaning*.
**Zero-config out of the box**: install, restart, and start a new session. The
local embedding model downloads itself on first use (~100 MB); the four tools,
per-question recall, and 5-turn auto-summarization all work with defaults. You
only configure when you want something different (see [Configuration](#configuration)).
## Features
- **Cross-session semantic memory** — facts, decisions, preferences, and notes
persisted as JSONL under `$DSH_HOME/memories/memories.jsonl`.
- **Embedding retrieval** — cosine similarity over normalized vectors; provider
is pluggable:
- `local` (default): ONNX inference via `@huggingface/transformers` with
`Xenova/bge-small-zh-v1.5` (offline, ~100 MB model, cached in
`~/.cache/huggingface`).
- `api`: any OpenAI-compatible `/embeddings` endpoint (e.g. SiliconFlow,
Zhipu, DashScope).
- **Memory decay & strengthening** — each entry's effective strength halves
over the configured half-life since its last access; searching an entry
refreshes it. Importance (1–5) sets the base strength.
- **Model-facing tools**:
- `memory_write` — persist a fact / decision / preference / note (content
hash dedup, repeats update in place).
- `memory_search` — semantic top-k recall with kind/tag/workspace filters.
- `memory_forget` — delete by id.
- `memory_stats` — store summary.
- **Automatic injection** — the plugin watches the session event stream: every
new user message is embedded and searched asynchronously, and the freshest
per-session recall is rendered into the system prompt **before the turn's
prompt assembly** (question-aware). With no fresh recall yet, the strongest
resident memories are injected as a fixed-size fallback.
- **Proactive writing guidance** — the injected prompt tells the model to call
`memory_write` on its own when the user states a durable preference, an
established fact, or an explicit decision (no need to say "remember").
- **Auto-summarization** — every N user messages (default 5), the plugin asks
the harness LLM to distill the recent transcript into memory entries and
writes them (tagged `auto`). One in-flight summary per session; silent on
failure; only active when `llm` and `agentDefaultModel` services exist.
- **Workspace tagging** — entries record the caller session's cwd; search
scopes to that workspace by default and can opt into cross-workspace recall.
## Install into a DSH profile
The package ships an in-package `cordis.patch.yml` declared via
`dsh.bundle.patch`, so the plugin command mounts it automatically with no
manual profile edits. DSH delegates plugin installation to `pnpm`, so `pnpm`
must be available on `PATH` regardless of how DSH itself is launched.
Use the command matching your DSH launcher:
```sh
# DSH run through npx (no global `dsh` command)
npx @deepseek-ai/dsh plugin --profile web add dsh-plugin-semantic-memory
# DSH run from a deepseek-harness source checkout (run from that repo root)
pnpm dsh plugin --profile web add dsh-plugin-semantic-memory
# DSH installed with a global `dsh` command
dsh plugin --profile web add dsh-plugin-semantic-memory
```
For a local checkout, build it first, then use the same launcher prefix:
```sh
cd C:/path/to/dsh-semantic-memory
npm install
npm run build
npx @deepseek-ai/dsh plugin --profile web add file:C:/path/to/dsh-semantic-memory
```
Then restart the Web profile with the same launcher
(`npx @deepseek-ai/dsh web`, `pnpm dsh web`, or `dsh web`) and start a new
session. All knobs have schema defaults; the in-package `cordis.patch.yml` is
the deployment config source — **in-package config overrides outer layers**
(settings.yaml and user patch rows only fill keys the package does not declare,
they do not override it).
> **Applying local configuration changes:** the Web profile uses a copied
> snapshot for a `file:` dependency rather than reading the checkout live.
> After changing `cordis.patch.yml` or rebuilding the plugin, delete
> `<DSH_HOME>\profiles\web\node_modules\dsh-plugin-semantic-memory`, run
> `<your DSH launcher> plugin --profile web install`, and restart the Web
> profile.
Manual equivalent (for older installs): add the dependency to the profile's
`package.json`, insert a mount row — new entries must be **inserted** (a bare
`- id:` row only overrides an existing bundle id and is silently ignored):
```yaml
- insert:
- id: semantic-memory
name: 'dsh-plugin-semantic-memory'
```
Leave `mode`/`provider` unset unless you need an explicit switch: selection is
automatic (see below).
## Usage
### Provider selection
The embedding provider is chosen by `mode` (explicit deployment switch), falling
back to the automatic selection:
| Configuration | Provider |
|---|---|
| `mode: 'cloud'` | **API** (OpenAI-compatible `/embeddings` endpoint); requires `apiKey` |
| `mode: 'local'` | **local** (ONNX via `@huggingface/transformers`, offline), even with an `apiKey` set |
| no `mode`, `apiKey` present (non-empty) | **API** |
| no `mode`, no `apiKey` | **local** |
| `provider: 'local'` (explicit) | local, even with an `apiKey` set |
| `provider: 'api'` (explicit) | API; requires `apiKey` |
Switching deployment mode means editing `mode` in the in-package
`cordis.patch.yml` and restarting the Web profile with the same DSH launcher —
**in-package config overrides outer layers** (settings.yaml
or user profile patch rows only fill keys the package does not declare; they do
not override it). A restart is needed after patch-file changes; the settings
document (`~/.dsh/settings.yaml`, `semantic-memory:` section) hot-reloads for
the keys it is allowed to supply. The first local embed downloads the model
(~100 MB, cached in `~/.cache/huggingface`; use `remoteHost` for a mirror).
### Verify the plugin is live
Open a **new session** (existing sessions keep their original tool set) and ask
the model: *"Do you have memory_* tools?"* — it should list `memory_write`,
`memory_search`, `memory_forget`, and `memory_stats`. The system prompt also
carries a `## Long-term memory` section once memories exist.
### What the model can do
- **Persist on its own** — state a durable preference, fact, or decision; the
injected guidance makes the model call `memory_write` without being asked.
- **Ask it to remember** — *"记住:我在用硅基流动的 API"* → `memory_write`.
- **Recall** — *"我之前对回答风格有什么偏好?"* → the per-turn semantic recall
surfaces relevant memories automatically; `memory_search` digs deeper
(supports `kind`, `tags`, `workspace`, `limit`, `min_score`).
- **Manage** — `memory_forget <id>` deletes; `memory_stats` summarizes the store.
### Automatic behaviors
| Trigger | Behavior |
|---|---|
| Every user message | Asynchronous embedding + search; the freshest per-session hits are injected into the next prompt assembly (`## Long-term memory (recalled for your current question)`) |
| Every N user messages (default 5) | The harness LLM distills **only the messages since the last summary** (per-session seq cursor — no re-digesting, nothing skipped) into memory entries, written with the `auto` tag; the cadence can be set with the `DSH_SEMANTIC_MEMORY_SUMMARIZE_EVERY` environment variable (`0` disables, overrides the config document) |
| Prompt assembly, no fresh recall | Strongest memories (importance × recency × access) injected as fallback |
### Where the data lives
- Store: `$DSH_HOME/memories/memories.jsonl` (one JSON line per entry, vectors
included; edit/backup freely).
- Settings: in-package `cordis.patch.yml` (deployment source of truth — package
config overrides outer layers); `~/.dsh/settings.yaml` under `semantic-memory:`
only fills keys the package does not declare (hot-reloaded).
### Troubleshooting
- **No memory_* tools in a session** — the session predates the plugin; start a
new one.
- **First local embed is slow / fails** — the model downloads on first use; set
`remoteHost: https://hf-mirror.com` in restricted networks.
- **`api` provider errors** — confirm `mode`/`apiKey` are set and `apiBase`
points at an OpenAI-compatible endpoint (a `/v1` base gets `/embeddings`
appended).
- **Auto-summary never fires** — it needs the `llm` and `agentDefaultModel`
services (present in the standard web profile) and `autoSummarizeEvery > 0`.
## Configuration
| Key | Default | Meaning |
|---|---|---|
| `mode` | *(unset)* | Deployment switch: `local` forces the local model, `cloud` forces the API (requires `apiKey`). Unset keeps the automatic selection. |
| `provider` | `auto` | `auto` selects by apiKey (non-empty → `api`, else `local`); explicit `local`/`api` overrides. An explicit `mode` overrides both. |
| `localModel` | `Xenova/bge-small-zh-v1.5` | Local transformer model id. |
| `remoteHost` | `https://huggingface.co` | Model download host; set `https://hf-mirror.com` in restricted networks. |
| `apiBase` | `https://api.siliconflow.cn/v1` | API base URL (an `/embeddings` route is appended). |
| `apiKey` | `''` | API key. When non-empty and provider is not explicitly `local`, the API provider is used. |
| `apiModel` | `BAAI/bge-m3` | API embedding model name. |
| `memoryPath` | `$DSH_HOME/memories/memories.jsonl` | Store file path. |
| `promptTopK` | `3` | Memories injected per system-prompt assembly (0 disables). |
| `maxSearchResults` | `10` | Default `memory_search` hit cap. |
| `minScore` | `0.35` | Default minimum relevance for search hits. |
| `halfLifeMs` | 30 days | Memory strength half-life. |
| `autoSummarizeEvery` | `5` | Auto-summarize every N user messages (0 disables; needs llm + agentDefaultModel). The `DSH_SEMANTIC_MEMORY_SUMMARIZE_EVERY` env var overrides this (0..100). |
| `summarizeWindow` | `12` | Most recent messages included in one auto-summary. |
| `summarizeMaxTokens` | `800` | Token budget for the summary call. |
| `summarizeTemperature` | `0.2` | Sampling temperature for the summary call. |
## Memory model
```ts
interface MemoryEntry {
id: string // sha1(kind + content), 16 hex chars — upsert key
kind: 'fact' | 'decision' | 'preference' | 'note'
content: string // one-sentence, self-contained text
tags: string[]
workspace?: string // caller session cwd at write time
source?: { sessionId: string; seq: number }
importance: number // 1..5
embedding: number[] // normalized vector
createdAt: number
updatedAt: number
accessCount: number
lastAccessAt: number
}
```
Effective strength = `importance / 5 × 0.5^(age / halfLife)`;
search rank = `cosine(query, entry) × strength`.
## Known Limitations
- **Recall is best-effort and async** — the user-message listener embeds in the
background; on a cold start (model still downloading) or with a slow API the
first recall may arrive one step late, and the strength-ranked fallback
covers that turn. Recall caches are per-session and stale after 60 s.
- **Sync prompt injection** — the injected section renders from resident data
only; the store is loaded lazily on first tool call, so a brand-new process
may start with an empty injection for the first assembly.
- **No embedding persistence cache** — vectors are stored inside each entry,
so no separate index file is needed, but full re-embedding never happens
either (entries keep their vectors forever).
- **Brute-force search** — O(n) cosine over all entries per query; fine for
personal-scale stores (thousands), not for millions of entries.
Install
dsh plugin --profile web add github:chenkezhen480/dsh-semantic-memory
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-plugin-semantic-memory from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.