Skip to content
dsh.fish
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](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![node](https://img.shields.io/badge/node-%3E%3D22-brightgreen.svg)](package.json)
[![tests](https://img.shields.io/badge/tests-19%20passing-brightgreen.svg)](./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

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source