Skip to content
dsh.fish
Bundle

wangdefa-memory

本地优先的 Agent 五层记忆体组件 - DSH 插件

Source
VinsonWild
stars
3 stars
License
Apache-2.0
Updated
Updated 4 hours ago

Readme


[中文](./README.md) | [English](./README_EN.md)

![Wangdefa.Memory Banner](./docs/images/WangdefaMemory_banner.png)

## Wangdefa.Memory

**本地优先的 Agent 五层记忆体组件**

[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)
[![.NET](https://img.shields.io/badge/.NET-10.0-purple.svg)](https://dotnet.microsoft.com/)
[![NuGet](https://img.shields.io/badge/NuGet-v1.1.7-orange.svg)](https://www.nuget.org/packages/Wangdefa.Memory/)
[![DSH Plugin](https://img.shields.io/badge/DSH-Plugin-blue.svg)](https://github.com/topics/dsh-plugin)

---

## 📄 更新说明

详见 [CHANGELOG.md](./CHANGELOG.md)

---

## 📖 项目简介

Wangdefa.Memory 是一个为本地数字分身 Agent 设计的“理解型”五层记忆体组件,数据完全保留在本地,不依赖云端,目标达到轻量部署、白盒可控、可解释、置信可管,未来将进一步往企业级原生记忆体方向拓展。

Wangdefa.Memory 选择了无向量记忆体方向(不排除未来有弱向量辅助),将记忆模拟人类思考结构,分为**认知、特征推演、思考、阅历、传递**五层。

我们认为记忆来源于对事件特征的识别与记录,特征记忆是人类与机器之间能找到的记忆共性,而机器的优势在于能记住大量特征标签,所以这个项目希望以特征记忆能力为主要核心,让 Agent 趋向「像人一样理解用户,记住用户」的能力。

> 记忆体负责存储、检索和演化长期记忆及自我沉淀,并实现自我清理迭代,通过长期累计配合,让你的 Agent 用得越久越理解你,可以更好的理解你的潜在需求,逐渐成为你的本地“数字分身”。

> **状态:早期阶段(Early Stage)** - 核心功能已完成,正在优化推演逻辑。欢迎试用和反馈。


---

## Wangdefa.Memory 设计思路


### 大家都在卷什么

现在的记忆体越来越多,不管插件还是框架,卷的方向算高度一致:召回精确,精准识别。

怎么召得更准,怎么认得更精。我毫不怀疑这方面技术未来会越来越完善。

然而,**召回的准确,是否等同于召回的"有用"?**

精确召回的本质是搜索。搜得再准,它也是搜索。你问一句话,系统把"相关"的内容全塞进来,这错了吗?并没有。但需要吗?不好说。

什么是人真正想要的?

我个人认为,是LLM会像人类一样,不是召回就塞入大量的信息,这不仅显得信息过量,同时也消耗了大量的无效token,而根据聊天的需要召回“有效、有关联”的东西,个人认为才是真正的记忆体。


### 人的记忆怎么进行的?

人的记忆核心是**感知驱动**。

永远是先感知对方的需求和意图,再决定调用什么层次的记忆来回应。

如果你在与闲聊的时候,对方问了你大概,这时候应该是用认知进行第一反应回复;而如果你塞了一堆起因经过结果,的事件背景给对方,这听起来就很扯淡了。

所以人在回忆某件事的时候,脑子里先出来的是个大概轮廓,不是全文。

只有深入讨论的时候,才需要把完整细节调出来。

按**浅、中、深,三层记忆深度提取记忆。**这才应该是人类记忆的响应:

- 浅:只要给到意图,通过一个认知摘要回复即可。
- 中:需要事件的概要和内容的概览
- 深:代表需要提取整体事件的完整内容

人从来不是先"检索"再"回答"的。人是先"感知"再"驱动"。


### 记忆是什么

我认为是记忆特征。

人类对记忆的检索都是基于特征获取的。

你记住的一个画面的细节,或者记住一个对话的关键词,乃至于你记住的一串独特数字,都是这个记忆的特征,不同维度的特征组成了一张记忆画面。

比如某个下午的会议,时间、空间、参与人、主题、起因、经过、结果,乃至于天气如何、桌上摆着一瓶花,会议屏幕长什么样?遥控是否坏了?都是这个时空下的记忆特征。

由这些记忆特征共同构建了一个事件画面,人从这个画面中提取了大概的过程,又接着在脑子里提取了一份会议事件的摘要。

这就是整个记忆的组成结构。

恰巧,记忆对于特征标签的记忆与人类的记忆特性是一致的,

而机器的优势在于,他能记住人类所无法记住的大量各异标签。


### 为什么不用向量

因为人脑子里没有余弦相似度。

人想起一件事,永远是特征触发的。一个声音、一张脸、一个味道,"想起来了"。跟向量没有半毛钱关系。

向量是把高维特征压缩成低维向量。但你已经用特征了,就不需要向量了。

另外在事件的关联性上,人类的脑子是更微妙的直观感知记忆,通过认知直接焊死了两者的关联性。

我认为就是人脑子里的特征标签在自动处理推演。

所以特征标签,可解释、可编辑、轻量。

你知道为什么召回,人可以改,不需要 Embedding 模型和向量库。


### LLM 该干什么

LLM 的语言理解能力已经到一定水平了。准确理解人类语言,即便现在还有瑕疵,未来也会越来越好。

那么LLM需要的就不会再是一个精准的记忆搜索工具,而是用户的意图推演修正辅助;

而记忆体真正该做的,就是作为**意图推演+记忆思路+用户偏好**的修正辅助而存在。

读懂用户——什么状态、什么情绪、什么意图、需要哪个层次的信息。然后把感知结果交给引擎去匹配特征、召回记忆。

语义理解交给 LLM,特征匹配交给引擎。

各司其职。


### 我要做什么

基于对 LLM 未来能力的预期,设计了 Wangdefa.Memory。

两个核心理念:

**感知驱动理解** —— 先感知意图,再决定调哪个层次的记忆。不盲目塞信息,尽量不浪费 token。

**记忆资产沉淀** —— 每一次对话都是一次积累。记忆越用越多,系统越来越懂你。最终是一个本地数字分身,同时沉淀属于你自己的个人记忆资产。




### 关于WangdeMemory如何运作

目前以ABC三条线路进行运行;

A线:意图感知 + 语义解析

1. 先判断输入文体。(人类语言离不开记叙、议论、说明、意识流、散文等主要文体),文体判断不是装饰,它决定意图推测的方向。
2. LLM 做意图分析,输出感知信息(场景/情绪/状态/语境)、路由决策(浅/中/深)、结构化标签(必须带语义定义,因为一词多意是常态)。
3. 标签和语义交给特征推演引擎。存在且匹配的直接取用,不存在的创建,标记"待审",C线审。
4. 引擎做多轮拓展匹配,找出潜在关联的认知卡片,按置信度推给 B 线。
5. B 线开始前,A 线先建一张认知卡片框架占位。B 线完成后,C 线补全。

B线:内容生成

接收 A 线的感知结果、路由层级、关联记忆、用户偏好,LLM 生成回复,流式输出。


C线:学习与沉淀(异步,不阻塞用户)

1. 记录完整事件
2. 写概览与概要
3. 补全卡片标签、摘要、指针
4. 检查新增标签准确度,同释义合并
5. 修正偏好

---

## ✨ 核心特性

| 特性 | 说明 |
|------|------|
| **五层记忆架构** | 认知层 / 特征推演 / 思考层 / 阅历层 / 传递层 |
| **特征推演引擎** | 标签池 + 密码簿 + 特征统计 + 时间衰减,让记忆通过认知驱动 |
| **两阶段写入** | 先写框架(pending),后补全(completed),支持状态标记 |
| **自我迭代** | 权重衰减 + 定期清理 + 标签演化,高频记忆自然沉淀,低频记忆自动遗忘 |
| **偏好闭环** | 用户反馈自动转化为偏好,持续学习 |
| **意图驱动检索** | 根据意图决定记忆注入深度(shallow / medium / deep) |
| **标签演化** | 合并 / 分裂 / 弃用,标签自动优化 |
| **本地优先** | 所有数据存储在本地 SQLite + JSON |
| **轻量依赖** | 仅依赖 SQLite + System.Text.Json |
| **MCP 适配** | 支持通过 MCP 协议接入 DSH,提供 ProcessMessage / SaveMemory 工具 |
| **A线近期记忆参考** | 意图分析时自动注入最近10张认知卡摘要和标签,提升标签提取准确性 |

---

## 📦 NuGet 安装

```bash
dotnet add package Wangdefa.Memory
```

---

## 🔌 DSH 一键安装

在 DSH 环境中执行以下命令即可完成安装:

```bash
dsh plugin add github:VinsonWild/Wangdefa.Memory
```

安装后启动 DSH,记忆体将自动工作:
- 对话时自动检索历史记忆并注入上下文
- 对话结束后自动保存记忆

首次启动会自动下载引擎,无需额外配置。

### 前置条件

- [.NET 10.0+](https://dotnet.microsoft.com/download)
- DSH 已配置 `DEEPSEEK_API_KEY`(插件会自动复用)

### 使用示例

**第一次对话(写入记忆):**

```
你:我喜欢用简洁的代码风格,变量名要清晰。
DSH:好的,已记录你的偏好。
```

**后续对话(自动召回记忆):**

```
你:帮我重构一下这个项目的代码。
DSH:好的,根据你偏好的简洁风格,我建议...
```

记忆体自动完成检索、注入和保存,无需手动调用任何工具。

**2. 补全记忆(填内容)**

拿到 `frameId` 后,调用 `save_memory` 补全:

```
mcp__WangdefaMemory__save_memory 好的,已记录你的偏好 认知_20260819_143022 completed
```

返回示例:
```json
{
  "success": true,
  "message": "记忆已补全并保存,cardId: 认知_20260819_143022,状态: completed"
}
```

**3. 查询记忆**

下次对话时,记忆体会自动检索相关记忆:

```
mcp__WangdefaMemory__process_message 写代码时要注意什么
```

如果命中,返回的 `hasMemory` 为 `true`,`memory` 字段包含摘要和标签。

### 状态说明

| 状态 | 含义 |
|------|------|
| `pending` | 框架已建,内容待补全 |
| `completed` | 已补全,可被检索 |
| `interrupted` | 补全中断 |
| `failed` | 补全失败 |

---

## 🚀 快速开始(.NET 开发者)

### 1. 初始化记忆体

```csharp
using Wangdefa.AgentMemory;
using Wangdefa.AgentMemory.Models;
using Wangdefa.Contracts;

// 如果不需要内置 A线,可传入 null 或 Mock 实现
var chatService = new MyChatService();
var basePath = Path.Combine(Directory.GetCurrentDirectory(), "memory");

ServiceRegistry.Initialize(chatService, basePath);
var memory = ServiceRegistry.GetWangdefaMemory();
```

### 2. 写入记忆(两阶段)

```csharp
// 阶段一:写框架(自动提取标签)
var frameId = await memory.WriteMemoryFrame(
    topicId: "demo",
    userInput: "我喜欢用简洁的风格写代码",
    perception: new PerceptionModel { Scene = "工作" },
    tags: new List<string> { "代码风格", "简洁" },  // 可传空,由 A线 自动提取
    route: "shallow"
);

// 阶段二:补全
await memory.CompleteMemory(
    cardId: frameId,
    agentResponse: "好的,已记录你的偏好",
    status: "completed"
);
```

### 3. 查询记忆

```csharp
// 不传 semanticTags 时,记忆体自动调用 A线 提取标签
var result = await memory.CognitiveMatch(
    input: "写代码时要注意什么",
    semanticTags: null  // 自动提取
);

if (result != null)
{
    Console.WriteLine($"匹配到记忆: {result.Summary}");
}
```

---

## ⚙️ 核心机制:特征推演引擎

记忆体的核心是 **特征推演引擎(FeatureEngine)**,负责记忆的匹配和排序。

### 特征推演三件套

| 组件 | 存什么 | 回答什么问题 |
|------|--------|-------------|
| **标签池(TagDictionary)** | 所有标签 + 定义 + 近义词 | "这个标签存在吗?它的 code 是什么?" |
| **密码簿(PasswordBook)** | code → 卡片ID 列表 | "这个标签关联了哪些卡片?" |
| **特征统计(FeatureStats)** | 每张卡片 → 它有哪些标签 | "这张卡片有哪些标签?" |

推演流程:
用户输入 → 提取标签 → 查标签池拿到 code → 查密码簿拿到卡片ID → 通过特征池确认卡片有哪些标签 → 多轮拓展推演关联 → 计算匹配强度

### 匹配流程

1. **精准匹配**:用 `tag + dimension` 查标签池,直接命中 `code`
2. **近义匹配**:用 `synonyms` 扩展匹配范围(作为兜底)
3. **密码簿查询**:用 `code` 查密码簿,拿到卡片ID列表
4. **特征池匹配**:用卡片ID查特征池,确认卡片实际包含哪些标签,计算匹配强度
5. **时间衰减**:匹配强度 × `exp(-0.05 × 天数)`,新记忆优先
6. **状态过滤**:只返回 `completed` 状态的卡片,过滤 `pending` 空卡
7. **排序返回**:按最终权重降序返回 TopN

---

## 🏗️ 架构图

```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                                     记忆体架构                                     │
├─────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                     │
│  ┌─────────────────────────────────────────────────────────────────────────────┐   │
│  │                         对外接口(IWangdefaMemory)                         │   │
│  │                                                                             │   │
│  │   SinkAsync()          CognitiveMatch()          AddTagWithSynonyms()       │   │
│  │   WriteMemoryFrame()   CompleteMemory()                                     │   │
│  └─────────────────────────────────────────────────────────────────────────────┘   │
│                                    │                                               │
│                                    ▼                                               │
│  ┌─────────────────────────────────────────────────────────────────────────────┐   │
│  │                         核心:特征推演引擎(FeatureEngine)                   │   │
│  │                                                                             │   │
│  │   ┌───────────────┐    ┌───────────────┐    ┌───────────────┐              │   │
│  │   │   标签池       │    │   密码簿       │    │   特征统计     │              │   │
│  │   │ TagDictionary │    │ PasswordBook  │    │ FeatureStats  │              │   │
│  │   │               │    │               │    │               │              │   │
│  │   │ tag → code    │    │ code → 卡片ID │    │ 命中次数      │              │   │
│  │   │ synonyms      │    │               │    │ 最后命中时间   │              │   │
│  │   │ definition    │    │               │    │               │              │   │
│  │   └───────────────┘    └───────────────┘    └───────────────┘              │   │
│  │                                                                             │   │
│  │   匹配流程:                                                                 │   │
│  │   标签输入 → 精准匹配 → 近义匹配 → 时间衰减排序 → 状态过滤 → 返回卡片ID     │   │
│  │                                                                             │   │
│  └─────────────────────────────────────────────────────────────────────────────┘   │
│                                    │                                               │
│                                    ▼                                               │
│  ┌─────────────────────────────────────────────────────────────────────────────┐   │
│  │                         认知层(CognitiveReader)                            │   │
│  │                                                                             │   │
│  │   特征推演返回的卡片ID → 加载认知卡片 → 返回 CognitiveMatchResult           │   │
│  │                                                                             │   │
│  └─────────────────────────────────────────────────────────────────────────────┘   │
│                                    │                                               │
│                                    ▼                                               │
│  ┌─────────────────────────────────────────────────────────────────────────────┐   │
│  │                         存储层(L2 + L3)                                    │   │
│  │                                                                             │   │
│  │   ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐        │   │
│  │   │  思考层          │    │  阅历层          │    │  知识层          │        │   │
│  │   │ ThinkingStore   │    │  EventStore     │    │ KnowledgeStore  │        │   │
│  │   │                 │    │  MemorySink     │    │                 │        │   │
│  │   │ 分流索引         │    │  事件存储        │    │  概览+摘要        │        │   │
│  │   └─────────────────┘    └─────────────────┘    └─────────────────┘        │   │
│  └─────────────────────────────────────────────────────────────────────────────┘   │
│                                                                                     │
└─────────────────────────────────────────────────────────────────────────────────────┘
```

---

## 🧩 各层职责

| 层级 | 名称 | 核心组件 | 职责 |
|------|------|----------|------|
| **L1** | 认知层 | `CognitiveReader` | 负责语义提取后快速读取认知卡片,通过特征推演检索记忆 |
| **L2** | 思考层 | `ThinkingStore` | 负责考虑内容深度和学习存储,进行分流索引,并记录「去哪找」 |
| **L3** | 阅历层 | `EventStore`、`KnowledgeStore`、`MemorySinkService` | 存储每一次交互的事件、知识的完整内容、概览和概要,并进行认知卡片的写入 |
| **L4** | 特征推演 | `FeatureEngine`(标签池 + 密码簿 + 特征统计) | 标签匹配、近义扩展、时间衰减排序 |
| **L5** | 传递层 | 内置于 `Middleware` | 根据 `route` 决定记忆注入深度(shallow / medium / deep) |

---

## 📂 存储目录结构

```
memory/
├── chat_history.db                         ← 聊天历史
├── wangdefa_memory.db                      ← SQLite 备份
├── feature_pool.db                         ← 标签池 + 密码簿 + 特征统计
├── cognitive/
│   └── records/
│       └── 认知_xxx.json                   ← L1 认知层(含 Status 状态标记)
├── experience/
│   ├── events/
│   │   └── 2026-08-10/
│   │       └── 事件_xxx.json              ← L3 阅历层(事件)
│   └── knowledge/
│       └── {topicId}/
│           ├── 概览_xxx.json              ← L3 阅历层(知识)
│           └── 摘要_xxx.json              ← L3 阅历层(知识)
└── thinking/
    └── chat/
        └── {topicId}/
            └── 记录_xxx.json              ← L2 思考层(分流索引)
```

---

## 🔁 数据流

### 写入流程(两阶段)

```
阶段一:写框架(WriteMemoryFrame)
用户输入 → A线 提取标签 → 中间件 → 写框架(Status = pending)
    ├── 创建认知卡片(标签 + 感知信息)
    ├── 写入密码簿(code → 卡片ID)
    └── 返回 frameId

阶段二:补全(CompleteMemory)
Agent 生成回复 → 调用 SaveMemory(frameId, agentResponse)
    ├── 填充 Summary
    ├── 更新 Status → completed / interrupted / failed
    ├── 更新特征统计(提高检索权重)
    └── 记忆可被检索
```

### 查询流程

```
用户输入 → A线 提取标签(参考最近 10 张认知卡)→ 中间件
    ├── 特征推演检索(标签匹配 + 时间衰减)
    ├── 状态过滤(只返回 completed 卡片)
    └── 返回 CognitiveMatchResult
```

---

## 📝 接口说明

### IWangdefaMemory

| 方法 | 说明 |
|------|------|
| `CognitiveMatch()` | 根据语义标签匹配记忆(`semanticTags` 可空,空则自动提取) |
| `CognitiveMatchByCodes()` | 根据标签 code 匹配记忆 |
| `CognitiveMatchTopN()` | 匹配多条记忆,返回 TopN |
| `WriteMemoryFrame()` | 写框架(状态 pending),返回 frameId |
| `CompleteMemory()` | 补全卡片,更新状态和内容 |
| `SinkAsync()` | 一次性写入(兼容旧模式) |
| `AddTag()` | 添加标签 |
| `AddTagWithSynonyms()` | 添加标签(含近义词) |
| `GetTagCode()` | 获取标签 code |
| `GetTagEntryByCode()` | 获取标签条目 |
| `ExecuteEvolutionAsync()` | 执行标签演化(合并 / 分裂 / 弃用) |
| `CleanMemoryAsync()` | 清理低权重记忆 |
| `GetOverview()` | 获取概览 |
| `GetFullText()` | 获取原文 |
| `DeepSearch()` | 深度检索 |

---

## 🤝 贡献

欢迎贡献!请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 了解详情。

1. Fork 本仓库
2. 创建你的分支 (`git checkout -b feature/amazing-feature`)
3. 提交你的修改 (`git commit -m 'Add some amazing feature'`)
4. 推送到分支 (`git push origin feature/amazing-feature`)
5. 提交 Pull Request

### 要求
- 所有测试必须通过 (`dotnet test`)
- 新功能需要包含测试
- 保持代码风格与现有代码一致

---

## 📄 License

Apache License 2.0 © 2026 Wangdefa Memory Contributors

See [LICENSE](LICENSE) for details.
```

---

Install

dsh plugin --profile web add github:VinsonWild/Wangdefa.Memory

Profile: web

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