dsh-mem0-plugins

August 29, 2026 · View on GitHub

把 hermes 版 mem0 记忆插件的「全自动记忆」移植到 DeepSeek Harness (dsh),做成标准 bundle 插件:dsh plugin add 安装、dsh plugin remove 卸载,不改任何 dsh 源码。 只支持自托管 Mem0 server(HTTP + X-API-Key),不做 cloud/OSS 模式。

它会自动做什么

能力触发时机说明
自动召回(工具驱动)模型回答前usage 节强引导模型先调 mem0_search——UI 工具卡即召回动作的可见呈现;长查询先蒸馏提炼意图再搜索
使用引导常驻系统提示中注册使用说明节,引导模型对用户相关的问题主动调 mem0_search(多角度多跳)
自动写入每轮对话结束把「用户消息 + 助手回复」交给服务端 LLM 抽取事实(infer: true);纯 JSON 的工具输出会被替换成占位符防污染
潮浪并忆写入时同一会话的短对话按 user 分桶合并:空闲 5s / 窗口 15s / 5 轮 / 4000 字符任一达标即合并为一次批量写入,摊薄抽取调用;超长消息(>2000 字符)走快速直写
上传脱敏写入时user/assistant 文本交给抽取 LLM 前过纯函数密钥闸(sk- 风格 key、AWS key、Bearer/X-API-Key 头、PEM 块、password= 键值、.env 形态折叠),命中替换为 [REDACTED:label]redactEnabled 可关
反馈闭环update/delete 后best-effort 上报 /evolve/feedback(correction/useless),参与服务端 salience 进化

四个模型工具

工具用途
mem0_search语义搜索用户记忆(支持 top_k / rerank 覆盖)
mem0_add逐字存储持久事实(不走 LLM 抽取)
mem0_update按 ID 改错(上报 correction 反馈)
mem0_delete按 ID 遗忘(上报 useless 反馈)

安装 / 卸载

# 安装(web profile;安装后重启 dsh 生效)
dsh plugin --profile web add ./plugins/dsh-mem0-plugins

# 卸载
dsh plugin --profile web remove dsh-mem0-plugins

装好即默认启用(enabled 默认 true,指向本机 server 时零配置可用); 设置页改动即时生效,无需重启。要关闭记忆,在卡片里关掉「启用插件」开关—— 卡片描述行实时显示 已启用/未启用 + host,一眼可见。

设置项

默认值:enabled=true(schema 默认,patch 不覆盖——配置即启用)、 host=http://127.0.0.1:8888apiKey=''(本机 server 无鉴权时零配置可用)。 设置页保存的值落在用户层,优先级更高。

连接与身份

字段默认说明
enabledtrue总开关,默认开启;关闭后不再召回/写入,工具调用提示未启用
hosthttp://127.0.0.1:8888自托管 server URL
apiKeyX-API-Key 头发送;AUTH_DISABLED 部署留空
userIddsh-user记忆归属 user_id,跨会话共享同一份记忆
agentIddsh写入附带的 agent_id

连接与身份配置

自动召回

字段默认说明
forceRecallSteptrue方案 B:每轮第一步注入『必须先调 mem0_search』提醒(琐碎轮跳过,plugin-source 显示为系统注记不写记忆);关闭则只靠 usage 引导
topK10每次召回最大条数(1–50)
rerankfalse开启则以全深度模式请求重排(服务端需配置 reranker)
distillEnabledtrue长文本查询蒸馏总开关(见下方「查询蒸馏」)
distillMinChars500不超过该长度的消息原样直查,零损失零开销
distillInputMaxChars8000送入蒸馏模型的原文截断上限
distillBaseUrl(空)蒸馏端点(OpenAI 兼容);留空跳过蒸馏直查原文
distillApiKey(空)Bearer 鉴权;端点留空时蒸馏自动跳过
distillModelQwen3.5-9B蒸馏模型(本地部署)
distillTimeoutMs30000蒸馏单次超时
distillRetryAfterMs20000双飞触发阈值:首请求无响应超过该时长即并发第二请求,先完成者胜出

自动召回配置:条数/重排/蒸馏

自动召回配置:蒸馏模型/超时/双飞 与 自动写入起始

自动写入

字段默认说明
syncEnabledtrue每轮结束写入总开关
coalesceEnabledtrue潮浪并忆合并写入;关闭则逐条直写
coalesceIdleMs5000桶内空闲冲刷阈值
coalesceWindowMs15000桶窗口冲刷阈值
coalesceMaxTurns5桶内轮数上限
coalesceMaxChars4000桶内字符上限
fastpathChars2000单轮超过该长度直接落库
sliceThreshold8000单条消息超过该长度按段落切片(服务端分块按消息粒度、单条不拆,超长单条会撑爆抽取窗口)
slicePieceChars2000切片后每片字符上限(实测服务端单条安全值 ≈2000);切片全量保留、服务端分块提取后合并
maxBucketAgeMs1800000潮浪桶存活上限(30 分钟):超龄且「服务端明确拒绝过」(HTTP 状态码)才丢弃;连接级失败(宕机/超时)不计龄
redactEnabledtrue写回载荷上传前脱敏(命中替换为 [REDACTED:*] 标记;可关)
feedbackEnabledtrueupdate/delete 成功后上报 evolve 反馈(可关)

自动写入配置:合并阈值/快速直写/进化反馈

可靠性与超时

字段默认说明
queueMaxLen50待写队列上限,满时丢最旧
breakerThreshold5连续失败达该次数熔断
breakerCooldownMs120000熔断冷却时长
requestTimeoutMs420000单请求总闸,search/add 共用(对齐 hermes httpx timeout=420.0

可靠性与超时配置:队列/熔断/总闸

要改 profile 层默认值,在 ~/.dsh/profiles/web/cordis.patch.yml 追加:

- id: mem0
  config:
    enabled: true
    host: http://mem0.internal:8888
    apiKey: your-admin-api-key

召回形态

记忆召回展示:第一步强制提醒注入 + mem0_search 中文关键词多路召回

  • 显式工具链路:不做后台静默预取(dsh 平台在消息回显后无内容注入钩子, 详见 docs/COMPARISON.md 平台时序约束);模型按 usage 引导先调 mem0_search, 工具卡让「召回中」对用户可见,蒸馏/超时/熔断全套在工具内部生效;
  • 第一步强制提醒(方案 B,默认开):每轮第一步经 agent/pre-step 注入 plugin-source 提醒「必须先调 mem0_search」(form:'notice' 注记——UI 消息区显示 「上下文注入 · dsh-mem0-plugins · 【记忆提醒】回答前必须先调 mem0_search」, 折叠行即直接可见,展开可见模型侧全文;不写记忆、琐碎轮跳过、 开关 forceRecallStep 可关)——把「先搜再答」从模型自觉升级为流程默认。
  • 琐碎输入跳过src/guards.js):纯问候/确认/斜杠命令词表三分类等价, 只整串匹配、带正文永不误伤。守卫只拦「注入提醒」,拦不住模型自愿搜索—— 常驻 usage 节的「先搜再答」硬命令会让模型在「好的/继续」轮照样调工具 (2026-08-24 实测缺陷)。修复(方案 A):usage 节与 mem0_search 工具描述 同步写入同一词表标准的豁免条款——整串仅为应答/问候/推进时明确豁免搜索, 携带任何实义内容(如「继续帮我看看那个报错」)立即恢复强制;
  • 中断轮不入记忆:被打断的半截回复不会写进 mem0(部分输出不是持久对话真相, 对齐 hermes #15218)。

查询蒸馏(防长文本打爆服务端)

移植自 hermes agent/memory_manager.py::_distill_query,只作用于召回查询, 不碰写入路径:

  1. 消息 ≤500 字符:原样直查——零语义损失、零额外调用;
  2. 超长消息(贴日志/代码):截断前 8000 字符送本地小模型提炼成「2–4 关键词或 一句检索意图」再去 /search,embedding 与检索不再吃整段噪音;
  3. 语言漂移防护:中文输入的蒸馏结果若出现越南语重音字符或非拉丁非 CJK 文字(聚合网关路由漂移到多语小模型的实证症状),判为污染即回退;
  4. 并发双飞:首请求 20s 无响应即并发第二请求(首个不取消),先完成者胜出;
  5. 全部失败/超时:回退原始 query,检索永不静默丢失。

真机记录(2026-08-23,Qwen3.5-9B @ 本地蒸馏端点):

原文长度: 4250 → distilled 4250 -> 17 chars (6480 ms)
蒸馏结果: mem0 服务端部署端口和内网地址

可靠性设计

  • 熔断器:连续失败 ≥5 次(可配)暂停所有 mem0 调用,冷却 120s 后自动恢复; 404/not found 类客户端错误不计入熔断。
  • 连接级重试:连接拒绝/DNS 类失败自动重试一次(此时请求大概率没到达服务端, 不会造成重复写入)。
  • 有界队列:待写队列满(默认 50)丢最旧,防止服务端长时间不可用时内存膨胀。
  • 兜底冲刷:插件 dispose 时冲刷全部合并桶,记忆不丢失。

可观测(潮浪收益与卫生计数在哪里看)

潮浪并忆与写路径卫生的计数不打到浏览器,落在 dsh 宿主进程日志

  • 日志通道:插件 info/warn 双通道——ctx.logger(dsh 内部日志,默认不透出 stdout)+ console.log/warn 直出宿主进程 stdout。systemd 部署看 journalctl -u dsh.service -f,非 systemd 看 dsh 进程的 stdout 输出。
  • 每次合并冲刷(info 级)打一条,含累计 totals:
[dsh-mem0] mem0 coalesced 3 turn(s) into 1 write (session=<id>, saved 2 call(s), chars=512, trigger=idle; totals: batches=12 savedCalls=34 dropped=0 jsonSanitized=3 redacted=1 sliced=0)
  • 计数含义
字段含义
savedCalls累计省下的服务端 LLM 抽取调用数(合并 N 轮 = 省 N−1 次)
dropped队列满(默认 50)丢最旧待写条的次数,同时打一条 warn
jsonSanitized被剥除的纯 JSON 消息条数(防键名/工具输出当「事实」入库)
batches / direct批量合并写入次数 / 快速直写次数(超长或合并关闭时)

队列丢最旧(warn):[dsh-mem0] mem0 sync queue full (50), dropped oldest pending turn; JSON 剥除与快速直写为 debug 级。熔断开合、直接写在失败时均有 warn。

超时分层(与 hermes 同步)

层级默认值说明
HTTP 总闸 requestTimeoutMs420s插件→server 单请求上限;server 内 LLM 三层兜底最坏 360s,正常召回摸不到总闸
召回形态工具驱动不再做后台预取注入;模型先调 mem0_search(内部蒸馏+搜索),工具卡在 UI 即召回动作
工具级额外限時有意不设——只有总闸一层,与 hermes 行为一致

本地验证

cd plugins/dsh-mem0-plugins
node test/smoke.mjs         # Host 半:apply 全链路 + 工具(含蒸馏)+ 写入链路 + 强制提醒 + 卫生(81 项)
node test/client-smoke.mjs  # Client 半:bundle 加载 + locale/slot 注册 + 表单 save 真链(28 项)

真机联测记录(2026-08-23,本机 mem0-dev 栈):

Mem0Client.search OK in 2121 ms; hits: 1; breaker failures: 0
no-auth rejected as expected: Mem0HttpError | HTTP 401

排障

现象处置
工具返回「插件未启用」设置页打开 enabled 并确认 host 已填
「circuit breaker open」服务端连挂多次触发熔断;检查 server 后等冷却或调低阈值
HTTP 401apiKey 缺失或错误(非 AUTH_DISABLED 部署必须填 ADMIN_API_KEY)
「server unreachable」curl http://<host>/openapi.json 先确认可达性
记忆没被召回该 user_id 下无相关记忆(GET /memories 查看);模型未调 mem0_search(检查 forceRecallStep 提醒是否被 UI 注记与工具卡跳过)