dsh-rag-memory:QQ 机器人的本地 RAG 长期记忆

前言

给 QQ 机器人接大模型时,上下文窗口只能带最近几轮对话,群里的约定、用户说过的事实、前几天的讨论很难稳定进 prompt。常见做法是手写 Redis 或全文搜索,中文分词和语义召回往往要各搞一套,还要和 DSH 进程里的插件生命周期对齐。

下面介绍维护者 Yslehui 在 DSH 插件生态里发布的 dsh-rag-memory:把群聊/私聊消息、事实与摘要存进本地 SQLite,用中文友好的 BM25 与 Embedding 混合检索,可选 Rerank 精排,再通过独立 HTTP 接口供机器人调用。数据不上传,未配置向量或 Rerank 时会自动降级为纯 BM25,插件仍可用。

这是什么

dsh-rag-memory 是 DSH 分类「记忆」下的插件(npm 包名 dsh-rag-memory,当前版本 0.1.1,MIT 许可证)。定位是:面向 DeepSeek Harness(DSH)QQ 机器人的本地 RAG 长期记忆——存储与检索都在本机完成,经本地 HTTP 暴露 API,不经过 DSH Web 的登录认证。

实现上依赖 Node 内置 node:sqlite,无原生编译步骤,README 说明可在桌面与 Android(Node 环境)上使用。引擎要求:Node >=22.5.0,DSH >=0.2.1-rc.1。

核心功能

  1. 持久化:消息、事实、画像、笔记等写入 SQLite(默认 WAL),默认路径为 $DSH_HOME/dsh-rag-memory/memory.db。
  2. 按 scope 隔离:建议 group:<群号>、private:<QQ号>,检索与写入均带 scope。
  3. 混合检索:中文分词 BM25 + Embedding 向量召回;开启 rerank 时可再精排。返回结果中 sources 标明命中通道(bm25 / vector / rerank),degraded 列出降级原因。
  4. 直接产出注入文本:/context 接口返回已格式化的记忆块,可拼进 system prompt。
  5. 可选鉴权:配置非空 token 时,请求需带 x-memory-token 头。

Embedding 与 Rerank 兼容 OpenAI 风格的 /v1/embeddings 与 /v1/rerank(文档举例硅基流动、Jina、Cohere 等)。API Key 写在 cordis.patch.yml 中,勿提交到公开仓库。

安装与启用

从 npm 安装(README 给出的命令):

dsh plugin --profile web add dsh-rag-memory

本地目录开发安装:

dsh plugin --profile web add ./dsh-rag-memory

装完后服务前缀为 http://127.0.0.1:39001/rag-memory(独立端口;被占用时自动递增到 39020)。README 写明装上即生效,无需重启引擎。

在 cordis.patch.yml 的 config 中可调整数据库路径、默认检索条数、Embedding 与 Rerank 开关及模型端点,例如:

config:
  dbPath: ''            # 空 = $DSH_HOME/dsh-rag-memory/memory.db
  token: ''             # 非空时,请求需带 x-memory-token 头
  defaultK: 8
  embedding:
    enabled: true
    baseURL: 'https://api.siliconflow.cn/v1'
    apiKey: 'sk-...'
    model: 'BAAI/bge-m3'
  rerank:
    enabled: true
    baseURL: 'https://api.siliconflow.cn/v1'
    apiKey: 'sk-...'
    model: 'BAAI/bge-reranker-v2-m3'

HTTP 接口概览

方法 路径 作用
GET /health 健康检查与能力状态
GET /stats 各 scope 条数、向量条数
POST /ingest 批量写入消息
POST /search 混合检索
POST /context 检索并拼好可注入的文本块
POST /save 直接存一条事实/画像/笔记
GET /profile?scope= 列出事实类记忆
POST /delete 按 id 删除
POST /clear 按 scope 清空(需 confirm: true)

典型用法

写入与检索

收到消息后异步入库,避免阻塞回复;调模型前按用户输入取相关记忆。

写入示例(POST /ingest):

{
  "scope": "group:123456",
  "items": [
    { "text": "小明说他下周三去杭州出差", "speaker": "小明", "ts": 1700000000000 },
    { "text": "群里在讨论周末去爬山", "kind": "message" }
  ]
}

kind 可为 message(默认)、fact、profile、note、event。

检索示例(POST /search):

{ "scope": "group:123456", "query": "小明什么时候去杭州", "k": 8 }

需要可直接注入 prompt 的文本时,调用 POST /context:

{ "scope": "group:123456", "query": "小明出差", "k": 6, "maxChars": 2000 }

返回的 context 形如带序号与时间的发言摘要,README 建议拼在机器人当轮的 system 段末尾。

Node 接入片段

README 中的最小接入方式如下(不配 token 时无需额外头):

const MEMORY = 'http://127.0.0.1:39001/rag-memory'
const scope = `group:${groupId}`

// 收到消息:先入库(不阻塞回复)
fetch(`${MEMORY}/ingest`, {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ scope, items: [{ text, speaker: nickname, ts: Date.now() }] }),
}).catch(() => {})

// 调模型前:取相关记忆
const res = await fetch(`${MEMORY}/context`, {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ scope, query: userText, k: 8 }),
})
const { context } = await res.json()
const system = context ? `${basePrompt}\n\n${context}` : basePrompt

适用场景与注意

适合:需要在 QQ 群/私聊场景做长期记忆、又希望数据和索引留在本机的 DSH 机器人作者;愿意按需配置 Embedding/Rerank 以提升中文问句召回,或接受纯 BM25 降级。

注意:

  • 插件以当前 DSH 进程权限运行,安装前请阅读 GitHub 源码 与 MIT 许可证,自行评估安全与合规。
  • 默认 HTTP 监听本机;若暴露到公网需自行加固网络与 token。
  • 备份可复制 $DSH_HOME/dsh-rag-memory 目录,或使用 SQLite 的 VACUUM INTO;按 scope 清空前建议先 GET /stats 确认条数。

DSH 社区插件目录(如 SkillHub 条目)为独立索引站点,与 DeepSeek / 幻方无官方从属关系;发现插件通常通过 DSH「插件 → 发现」添加市场索引源,而非官方应用商店。

结尾

经过上面的步骤,你可以把 dsh-rag-memory 当作机器人侧的本地记忆层:入库走 /ingest,进模型前走 /context,检索链路在资料中已写清降级行为。进一步细节以仓库 README 为准。

  • 目录页:https://www.skillhub.cn/plugins/Yslehui/dsh-rag-memory
  • GitHub:https://github.com/Yslehui/dsh-rag-memory
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜