Bundle
dsh-self-memory
DSH 自记忆插件 v3(P0 升级):在 v2(加权关键词/泛化/倒排索引/前提感知/同义词规范化)之上增加事件驱动自动采集(会话轨迹落盘)、pre-step 自动召回注入(<selfmem_memory> 围栏)、写路径可靠队列(失败入 pending 重放)、CJK 感知 token 预算。
- Source
- cyanxi-eb
- stars
- 1 stars
- License
- MIT
- Updated
- Updated 5 days ago
Readme
# dsh-self-memory — DSH 自记忆插件
> 给 DeepSeek Harness 智能体装上跨会话的「经验记忆」:遇到问题自动查历史方案、自动留痕、自动回写——新会话不再重复踩坑。
[](./LICENSE)
[](package.json)
[](./tests)
## 这是什么
DSH 自记忆插件(v0.3)把会话中遇到的问题、**多种解决方法**、脚本文件路径与模型调用全过程结构化记录下来;新会话中,「先查记录 → 有就试 → 成功标命中/失败记根因 → 无则自研 → 最后回写」的闭环**自动发生**,并支持外部导入解决方法。零运行时依赖、纯本地、离线可用。
## 快速开始(3 步上手)
### 1. 安装
```bash
dsh plugin --profile web add <本目录>
```
免重启热挂载:对 AI 说 `dev_install_package {"dir": "<本目录>"}`。
### 2. 验证已生效
对 AI 说:`selfmem_status` → 返回 `plugin=v0.3.0 recordsSchema=2 records=… solutions=…`。
(代码更新后需 `dev_reload_package dsh-self-memory`,或重启 dsh 由 bundles 重新装配。)
### 3. 第一次使用
- 什么都不用做:你每次发消息,插件都会自动检索历史记录并以 `<selfmem_memory>` 围栏注入上下文;
- 手动检索:对 AI 说 `selfmem_search {"query":"如何识图"}`;
- 查看自动采集的会话轨迹:`selfmem_trace`。
**日常闭环**(AI 侧):遇到问题 → 看自动注入的历史方案 → 有就执行,成功 `selfmem_hit`、失败 `selfmem_fail` 记根因 → 没有就自行解决 → `selfmem_record` 写回。
## 要求
- `@deepseek-ai/dsh` `0.1.x` 发布线(rc 版可)
- Node.js `>=22`
- **零运行时依赖**:只用 Node 内建模块;DSH 侧只注入 `tools` 服务
## 设计说明
### 为什么召回注入走用户消息,而不是 system prompt
召回与历史方案通过 `agent/inbox` 以**来源归属的用户消息**(`source: { kind: 'plugin', plugin: 'dsh-self-memory' }`)追加。刻意不进 system prompt:某些 preset 的 persona 声明 `complete: true`(如 stock `minimal`)会把该 persona 恢复为唯一 prompt 段,**静默丢弃**其他插件的所有贡献——基于 system prompt 的记忆插件在这种 preset 下会无声失效。用户消息注入还可重放、对压缩可见、不进入 `request/header`。
### 为什么注入内容带围栏与来源标注
召回块以 `<selfmem_memory>` 包裹并声明「启发式检索,可能不相关;如不相关请忽略」——记忆只是证据,不是指令,降低注入误导与 prompt 注入风险;同时采集端按 `source.kind='plugin'` 白名单**排除一切插件注入文本**,防止记忆把自身注入的内容再记回去(自污染/反馈环)。
### 为什么保持本地零依赖
对比 OpenViking/Hindsight 的「服务端记忆」方案:本地存储 + 本地检索意味着离线可用、自包含、对话不离开机器。代价是语义检索能力有限(关键词/泛化/倒排),向量与 LLM 提取列入路线图(见「已知局限」)。
## 配置
| 配置项 | 默认 | 说明 |
|---|---|---|
| `autoRecall` | `true` | 每次用户消息是否自动检索并注入历史方案 |
| `recallBudget` | `600` | 召回注入的 token 预算(CJK 感知,≥0x3000 计 1.5) |
| `captureEnabled` | `true` | 是否自动采集会话轨迹(user/assistant 消息) |
| `dataDir` | `~/.dsh/self-memory` | 记录/轨迹/待写队列的存储目录 |
在 profile 的 `cordis.patch.yml` 覆写:
```yaml
- insert:
- id: self-memory
name: dsh-self-memory
config:
autoRecall: true
recallBudget: 800
captureEnabled: true
```
## 行为(钩子清单)
- `system-prompt/assemble`:跟踪当前 agent 句柄(供 inbox 注入)。
- `session/event`(user/message,`source.kind='user'`):写入轨迹 + 触发自动召回注入。
- `session/event`(assistant/message):写入轨迹。
- 注入排除:`source.kind='plugin'` 的消息一律不采集、不触发召回。
- 写操作(record/hit/fail/import):经串行队列执行;失败自动入 `pending.jsonl`,下次启动重放(去重)。
- 工具注册前过 `validateToolSchema`:非法 schema 直接拒绝注册(防 400 事故复发)。
## 核心能力
| # | 解决的 5 个问题 | 方案 |
|---|---|---|
| 1 | 多关键词关联度 | `keywords` 为 `{canonicalKey: 权重}`,检索按权重加权打分 |
| 2 | 隐去细节增强泛化 | 记录时自动生成 `abstractProblem`(版本/路径/哈希/数字→占位符),检索同时匹配原始+泛化文本 |
| 3 | 大量案例快速检索 | `lib/indexer.js` 倒排索引(canonical key→记录 id),记录 >100 时先取候选集再打分 |
| 4 | 前提不同结果矛盾 | 记录级 `preconditions` + 方案级 `appliesTo`/`conflictsWith`/`status`;`selfmem_search` 传 `context` 前提,按前提匹配排序并标注「不适用/矛盾/已废弃」 |
| 5 | 关键词规范统一 | `lib/norm.js` 同义词表(插件/plugin/模组→plugin),存与查走同一套规范化 |
## 工具一览
| 工具 | 作用 |
|---|---|
| `selfmem_search` | 检索既有方案(支持 `context` 前提过滤/排序) |
| `selfmem_list` | 列出全部记录摘要 |
| `selfmem_record` | 记录/追加方案(同问题多方案、前提、矛盾、关键词权重) |
| `selfmem_hit` / `selfmem_fail` | 方案成功 / 失败回写 |
| `selfmem_import` | 外部导入(文件 / 内联 JSON / 文本) |
| `selfmem_status` | 状态与规模统计 |
| `selfmem_trace` | 查看自动采集的会话轨迹 |
## 使用示例
**记录一个带前提的方案**
```text
selfmem_record {"problem":"断网时如何安装插件","solution":"解压本地包 → dsh plugin add <目录> → pnpm install → dev_install_package 热挂载","keywords":{"plugin":1,"network":0.6},"appliesTo":{"network":"no"}}
```
**前提感知检索**(不同前提返回不同方案并标注)
```text
selfmem_search {"query":"如何识图","context":{"model_vision":"no"}}
→ sol-offline-script(适用);sol-switch-model 标注 [不适用当前前提]
```
**外部导入**
```text
selfmem_import {"source":"examples/import-demo.json"}
```
**标记成功 / 失败**
```text
selfmem_hit {"query":"断网时如何安装插件","solutionId":"s-…"}
selfmem_fail {"query":"…","solutionId":"s-…","rootCause":"…"}
```
## 记录结构
```json
{
"schema": 2,
"problem": "原始问题(含细节)",
"abstractProblem": "泛化问题(细节隐去,自动生成)",
"problemType": "…",
"keywords": { "plugin": 1, "network": 0.6 },
"tags": ["…"],
"preconditions": { "network": "no" },
"solutions": [
{ "id": "s-…", "method": "…", "scripts": [], "modelCalls": [],
"appliesTo": { "network": "no" }, "conflictsWith": ["s-…"],
"status": "active|superseded|conflicting",
"hits": 0, "failedAttempts": 0, "rootCauses": [] }
]
}
```
## 项目结构(零依赖,纯 JS)
```
dsh-self-memory/
├── entry.js # 公共出口
├── index.js # 工具注册 + 事件钩子(采集/注入/队列)+ schema 守卫
├── lib/
│ ├── store.js # JSON 持久化(v1 自动迁移 v2)
│ ├── ops.js # 可序列化操作(record/hit/fail/import,纯函数)
│ ├── writer.js # 写路径串行队列 + pending 重放
│ ├── capture.js # 事件驱动轨迹采集(插件源排除)
│ ├── inject.js # 召回注入 + CJK token 预算
│ ├── search.js # 打分:加权关键词 + 泛化 + bigram + 前提匹配
│ ├── indexer.js # 倒排索引(快速候选集)
│ ├── norm.js # 关键词规范化 + 同义词 + 问题泛化
│ └── seed.js # 初始知识
├── tests/ # 单元测试(node:test,零依赖)
├── examples/ # 使用示例(如 import-demo.json)
├── cordis.patch.yml # bundle 装配补丁
├── LICENSE # MIT
└── package.json # dsh.bundle.patch -> cordis.patch.yml
```
## 存储位置
`~/.dsh/self-memory/`
- `records.json` — 结构化记录(`{schema:2, records:[...]}`,v1 自动迁移)
- `sessions/<会话>.jsonl` — 自动采集的原始轨迹(user/assistant,上限 1MB/会话)
- `pending.jsonl` — 写失败待重放队列(去重)
## 测试
```bash
node --test # 19 个单元测试:norm / search / store / schema / inject / ops
```
## 已知局限
- 检索是关键词/泛化/倒排(无向量语义检索)——计划引入本地 embedding(可复用 DSH host-llm)
- 轨迹自动采集但不自动提炼为记录——批量 LLM 提取(任务末打分回溯)列入路线图
- 同一问题多方案靠前提区分,仍需要 AI 在记录时给出 `appliesTo`
- 单文件 JSON 存储,记录达万级后可迁移 SQLite
## 许可证
MIT — 见 [LICENSE](./LICENSE)。
Install
dsh plugin --profile web add github:cyanxi-eb/dsh-self-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-self-memory from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.