妻子记忆(dsh-wife-memory)
给 DSH 用一个插件:只记关于主人的事,然后像一个懂他的妻子那样,到点了主动说一句。
不是聊天总结器,也不是任务管理器。它记的是:主人的作息(几点睡、几点上班、几点下班)、喜欢什么讨厌什么、 什么会让他开心/烦、最近在忙什么、说过的话和约定、以及他希望你怎么跟他说话。技术细节、工具调用、助手自己 说的话,一律不收。
它做三件事
- 听:每轮对话结束,用当前会话的模型再问一次「这轮有什么关于主人的长期信息」, 抽出严格 JSON 合进记忆库(独立请求,不往会话里塞东西)。
- 记:记忆存在本机
$DSH_HOME/wife-memory/memory.json(原子写 + 自动合并去重 + 条数上限)。 每轮把一份 ≤ 几百字的摘要注入系统提示(order 89990,落在 DSH 自己那几段之后,不毁前缀缓存)。 - 说:每分钟看一次表,到点了就顺着「妻子心里冒出来的念头」主动开口。8 种由头: 早安、上班前、午饭、下班、晚饭、催睡、久没说话、难受时安抚。
不过不是只有到点才开口:你每发一句话,它都会顺手判断一次「这轮回话里要不要带一句关心」—— 不带单独的提示条、不另发消息,就合在它正常回你的话里(见下面「对话里也关心」)。
它还会看你回话的反应自己调:嫌烦就少说、夸它就多说、说「别提醒我吃饭」就把午饭那条关掉、 说「短一点」就记下你的口味。力度有上下界(一天 0–10 条、间隔 45–240 分钟),随时能一键恢复。
但字面不作数:它会拿记忆一起判断——你刚加班到很晚、最近情绪不好,嘴上说「烦死了」它先当你是在发泄; 「讨厌,抱抱我」它当撒娇;嘴硬说「不用你管」但你记忆里其实吃被关心这套,它也不降。拿不准的先记一笔, 同样的信号第二次出现才真的改(见下面「口是心非」)。
记忆攒多了会自己整合:同一件事合并成一条,前后冲突只留最新的(旧的进 archive,面板上还能翻)。
顺手也记一笔「替你对外说过的话」:B站私信/评论/动态、抖音私信/评论/分享卡片、QQ 空间动态
(工具调用会进记忆的 outbound,面板上看得到;对方回信它看不到,除非别的插件把它拉回会话)。
设置页怎么用(一页说明)
打开:DSH「设置 → 妻子记忆」。一般只动三处:
| 要调什么 | 在哪里 |
|---|---|
| 打开/关掉 | 「开关」:总开关、主动关心、聊天时顺口关心 |
| 多久说一次 | 「力度」:每天最多几条、两次至少隔几分钟(或点「恢复初始力度」) |
| 说哪些 | 「什么时候可以说」:早安/上班前/午饭/下班/晚饭/催睡/久没说话/难受时安抚,一条一个勾 |
其余都是自动的,看不懂可以不管;点「记录 / 进阶 → 展开看看」才出现:
- 「今天的情况」:现在记了多少、今天说了几次、上次什么时候抽的,以及三个手动按钮(现在就说一句 / 现在就抽一次 / 整合一次记忆)。
- 「聊天时顺口关心」:这一轮会不会顺口带一句、为什么、最近几次记录;输入一句话可以试触发(只判断、不发)。
- 「作息学到哪了」:六项作息现在是多少、是默认值还是你自己说的。
- 「模型每轮看到的记忆」「记住了什么」「我为什么这样理解你的话」:纯查看。
- 「谁在说话(认错了点一下就能改)」:会话里出现过哪些来源、我把谁当主人;不认识的先按主人算,点「是插件」写进配置;点「撤回它带来的没核实记忆」把认错收进来的记忆归档。
- 「会记账的「对外发送」动作」:内置认得的发送动作;新插件名字怪的话,在「新插件的「发送」动作名」「显示成哪个渠道」两行里补(不填也行,不认也不影响关心和记忆)。
- 「记忆整理」「我替你说的那些话」:自动记录,查看用。
设置页最下面一行只是提示:记忆存在本机 $DSH_HOME/wife-memory/,删 memory.json 就清空。
对话里也关心(每轮判断,不另发消息)
主动开口是「到点了我说一句」;这一层是「你说话的时候,我顺口带一句」。每收到你一句话, 它先做一次不花钱的判断(只看字面和记忆,不调模型),有该关心的信号就把一小段提示拼进这轮的 记忆摘要里,让模型在你这句话的正常回复里自然带出来:
| 信号 | 例子 | 它会在回复里 |
|---|---|---|
| 情绪/状态词 | 「今天好累」「烦死了」「加班到十点」 | 先接住情绪,再劝歇会儿/喝水 |
| 深夜还在聊 | 23:30 之后还在发消息 | 顺口催一句睡觉 |
| 饭点 | 12:10 你随口说了句别的 | 顺口问「吃了没」 |
| 记忆里最近很累 | 前两天记过 tired/anxious | 自然带一句关心(不直说「我记得」) |
| 在忙的事挺赶 | 记忆里在忙的事带「加班/上线/考试」 | 问进展、说句辛苦 |
| 开心事 | 「升职了」「太好了」 | 跟着高兴、夸你一句 |
| 聊太久 | 一天第 9 轮之后 | 提醒喝口水、起来动动 |
防止变话痨的闸:默认每小时最多 2 次、两次至少隔 25 分钟、同一种由头 6 小时内不重复、 静默时段(默认 23:30–07:30)不掺和(除非是强关心)。开关和这些数都在设置页「对话里也关心」那一栏; 面板上能看到「今天顺口关心了几次、这一轮准备为什么关心」,还能输入一句话试一下会不会触发。
这一层不额外调模型(判断是纯规则),所以不影响花费;真正说出口仍然由模型自己在回复里完成。
记忆多了会自动整合(compaction)
条数到阈值(默认 240 条)或者每抽 20 次,就让模型把记忆库整个捋一遍(独立请求,maxTokens 2400,temperature 0):
- 同一件事只留一条:说得重复的、换个说法的,合并成一句最完整的话;
- 冲突只留最新:看
lastSeen和原文里的「最近/现在/已经」,旧的那条进archive,不硬删; - observations 这类碎片压到最多 8 条,facts/emotions/focus 按
consolidateKeepRatio(默认 0.7)留; - 合并后尽量沿用原 id /
firstSeen/hits,让「记了多久、提过几次」不丢。
阈值和频率都能在设置里改;面板/工具都能手动触发一次(wife_memory_consolidate 或面板上的「整合一次记忆」)。
整合结果和上次整合做了什么,面板「整合 / 归档」区块里看得到。
口是心非:看字面,更看记忆
先把话里的信号分几类(夸奖 / 嫌烦 / 撒娇 / 气话 / 嘴硬 / 敷衍),再拿记忆校验:
| 表面 | 记忆里 | 它怎么理解 |
|---|---|---|
| 「烦死了,别说了」 | 最近 24 小时有 tired/anxious/sad/angry,且是刚被关心完的回话 | 气话:先记一笔、不动手(delta ×0.25) |
| 「讨厌,抱抱我」 | —— | 撒娇:当夸奖处理,不降力度 |
| 「不用你管」 | 之前记过「喜欢被关心」 | 嘴硬:先记一笔、不动手 |
| 「别提醒我吃饭」 | 不是气头上、明确说了 | 当真:关掉午饭那条(并且下次不再提醒) |
| 「嗯」「哦」 | —— | 敷衍:不算夸也不算烦,先不动 |
拿不准的时候会再问模型一次(deepRead,off / ambiguous / always 三档,默认 ambiguous;两次至少隔 300 秒),
让它看着记忆摘要 + 刚才那句关心 + 你的回话 + 最近情绪,判断是真心、气话还是撒娇。判断记录(含理由)
在面板「看眼色记录」里;「先记一笔」的信号攒到同方向两次(7 天内)才真的落地。
作息从聊天里学(QQ 里说也算)
作息不是只能靠你自己去设置页填:你在聊天里随口一说,它就当场记下来,纯规则、不调模型、不花钱。
| 你会说 | 它记成 | 可信窗口(这个点之外说的一律不算) |
|---|---|---|
| 「起床了」「早安」「刚起」 | 起床 | 04:00–11:00 |
| 「我上班了」「到公司了」「打卡」 | 上班 | 05:00–12:00 |
| 「吃午饭」「吃饭了」(上午) | 午饭 | 10:30–14:30 |
| 「下班了」「收工了」「回家了」 | 下班 | 15:00 之后 |
| 「吃晚饭」「夜宵」 | 晚饭 | 16:30–22:00 |
| 「睡了」「晚安」「困了」 | 睡觉 | 21:00–04:00 |
- 每类只留最近 30 个样本、只算 90 天内的,取中位数(按 5 分钟取整)——说一次就学个大概,说得越多越准;
- 「还没下班」「准备睡了」这种否定/计划不算;很长一段话里顺带提一句的也不算(怕把转述、玩笑当作息);
- 每个时间点在面板上都看得见是怎么来的:默认值 / 你自己说的(几次取中位)/ 从聊天里抽的;
- 想关掉就在设置里关「学作息」(
learnSchedule);关掉后一律用设置页里的值。
QQ 也算:dsh-onebot-qq / dsh-qq-bridge 把 QQ 消息转成 DSH 会话里的用户消息(source.kind 是
plugin:dsh-onebot / qq),wife-memory 挂在那条会话上,跟你在 Web 这头打字走的是同一条链路——
所以 QQ 里一句「我下班了」照样能学到,前提是那条 QQ 桥真的连着(没连上就没有会话可听)。
和其它插件一起用(B站 / 子代理 / QQ 桥)
- 别的插件(刷 B站、刷抖音……)调工具时,只要调的是对外发送类(B站私信/评论/动态、抖音私信/
评论/分享卡片、QQ 空间动态),妻子记忆就顺手记一笔到
outbound。这类观察走全局tools/result, 所以子代理里发的也算。 - B站插件自己塞进会话的唤醒语(
source.kind=bili-duty-scan/bili-duty-dynamic/bili-watch) 不算主人说的话:不抽记忆、不看眼色、不动力度。其它插件注入的上下文(plugin/speak-up/feiyu-mood/fish-memory/soul-md/douyin-duty)同理。 - 子代理会话(比如
bili_scan派出去刷视频的那个)默认只注入记忆摘要(让它知道你的口味), 不把它的对话当主人说的、不抽记忆、不开口。想彻底关掉:设置里把subagentMemory关掉。 - QQ 那边:onebot 桥把 QQ 消息转成正常会话消息,所以作息、喜好、情绪都能学(见上面「作息从聊天里学」)。
不用一个个加白名单(自动认来源 / 自动认发送工具)
以前是两张硬编码清单:哪些 source.kind 算「别的插件塞的」、哪些工具名算「对外发送」——装个新插件就得改一行。
现在改成三层:
- 对外发送工具:认识的(B站私信/评论/动态、抖音私信/评论/分享卡片、QQ 空间动态)用专门解析,句子更准;
不认识的按动词猜——名字里有
send / reply / post / publish / share / dm / comment / message / dynamic / forward / broadcast / announce,且不是查询类(status / gate / list / search / read / …), 就当「对外发送」,渠道名取前缀(bili_→ B站、mcp__server__tool→ MCP:server)。 新插件按这个习惯命名就零配置能记;要补/要排除,往配置outboundTools(小写工具名)里加,不用改代码。 动词和渠道前缀也能在设置里自己加:设置页「自动认的对外发送工具」里的「自己加发送动词」 (比如collect, like)和「自己加渠道前缀」(每行xhs=小红书),保存后写进配置outboundVerbs/outboundChannels,以后新插件按这些词/前缀认——面板上同样能改。 - 谁在说话:认识的注入源和认识的对话通道有默认值;不认识的来源先按「主人」算(宁可不漏听你的话),
同时记进
memory.sources,设置页「认人」一栏会列出来——点一下「是插件 / 其实是主人」,写进配置pluginSources/humanSources,以后都按这个算。另外整句只有一句括号话、或从「到点 / 该发 / 有人」 开头的,先按插件算(别的插件的到点提醒不会冒充你)。 - 所以:以后装多少插件都不用再来找我改代码;实在认错了,面板上点一下就行。
安全层:认错了也不怕(v0.4.0)
上面那些自动认的规则,总有认错的时候(新插件名字起得怪、QQ 桥换了 source、模型抽错一条……)。 所以这里的原则不是「一定认得准」,而是认错了也不出事:
- 收的时候不设门槛:只要是像人说的话就抽、就记,宁可不漏——错收一条,比漏掉一句真心话好。
- 每条记忆记着「谁说的 + 有多准」:
prov= 来源(qq/plugin:dsh-onebot/ 某个不认识的名字…)- 当时是不是猜的;来源没确认的条目会被标成**「没核实」**,置信度封顶 0.5。
- 用的时候分权:只有确认过的人类来源才能
- 改行为(调力度、关某条提醒、改说话方式)
- 学作息(
我上班了这种短句) - 把已核实的事实顶掉(
supersedes) 没核实的东西可以收,但不进摘要主段、不参与任何行为——摘要里单独一行写着「还有 N 条没核实的话, 先别当真」,模型知道别拿它去猜你。
- 错了能撤:设置页「认人」里每个来源后面有「撤回它带来的没核实记忆」,
一键把来源没核实的条目全部归档(不硬删,
archive里留着,出问题能翻)。 看眼色记录(feedback.decisions)里也会写下「来源没确认 → 先不动行为」,为什么没照做一目了然。
一句话:自动认是为了「不用配置」,分权是为了「配错了最多是个没核实条目,不会真的学坏」。
设置页那两行输入(「自己加发送动词」/「自己加渠道前缀」)到底啥意思
- 自己加发送动词:工具名里带这个英文词,就当成「往外发东西」。比如你以后装个新插件,它的工具叫
mastodon_like,而like不在内置动词里(内置的是 send / reply / post / publish / share / dm / comment / message / dynamic / forward / broadcast / announce / tweet / toot),你在这里填like,它就也会被记进 「替你对外说过的话」。逗号或空格分隔,填小写英文。 - 自己加渠道前缀:工具名开头的词 → 人话名字。比如
xhs=小红书,以后xhs_send_note就记成 「在 小红书 发了…」。每行一个前缀=名字。 - 两行都只是在默认值之外加,不覆盖内置的;填错了点面板上「保存这两项」重填就行,不动代码。
装
给别人用、或者装到别的机器上,三种装法(不依赖本机路径):
# ① 本地 tarball(npm pack 出来的那个文件)
dsh plugin --profile desktop add file:/abs/path/dsh-wife-memory-0.4.1.tgz
# ② 作者的 GitHub Release(里面放 npm pack 的 tgz 最好;整仓 zip 也行,但依赖要能装)
dsh plugin --profile desktop add https://github.com/<你>/dsh-wife-memory/releases/download/v0.4.0/dsh-wife-memory-0.4.1.tgz
# ③ 发布到 npm 之后
dsh plugin --profile desktop add dsh-wife-memory
市场上架:dshmarket 的目录来自 awesome-dsh-plugin, 去那边提一个 PR(加一条)就行,站点和市场会自动收录(一般一天内生效);市场安装优先用「经仓库验证的 npm 包」, 其次作者 GitHub Release 里的预构建 tarball。
本机已经在 desktop profile 里装好并启用(dsh-wife-memory),升级时用:
dsh plugin --profile desktop add file:E:/DSH/plugins/dsh-wife-memory
面板在 DSH 设置 → 妻子记忆(左侧栏);不想开设置页也可以直接开
http://127.0.0.1:43120/dsh-wife-memory/panel。
工具(模型自己会用)
| 工具 | 干什么 |
|---|---|
wife_memory_read |
读记忆摘要 / 按 kind 查事实 / 看情绪、在忙什么、称呼、作息、说话方式 |
wife_memory_save |
记一条长期事实(kind:profile / preference / like / dislike / promise / date / note) |
wife_memory_forget |
删记错或过时的(给 id 或关键词;不给就只列预览不删) |
wife_memory_consolidate |
手动整合一次记忆(force 可忽略阈值/间隔),返回整合前后条数与说明 |
设置项(面板上都能改;常用三项见上面「设置页怎么用」)
常用:
- 「开关」:总开关、主动关心、每轮自动记、演练模式(只记账不真发)、看反应自己调多问少问、学说话方式、 聊天时也顺口关心、从聊天里学作息、记下我替你说的话
- 「力度」:手动上限/间隔,或「恢复初始力度」
- 「作息」:起床/上班/午饭/下班/晚饭/睡觉 + 只在这几天上班时提醒上下班
- 「什么时候可以说」:8 条规则逐条开关;静默时段;「我刚说完话 N 分钟内不另外开口」
- 「现在」:今天说了几次、下次该说什么、上一轮什么时候抽的;按钮:现在就说一句 / 现在就抽一次 / 整合一次记忆 / 保存设置
- 「聊天时顺口关心」:这一轮会不会带一句、为什么、最近记录
记录 / 进阶(默认收起,点「展开看看」):
- 「模型每轮看到的记忆(摘要)」「记住了什么」「你的情绪」「你在忙什么」:查看用
- 「我为什么这样理解你的话」:看眼色决策记录 + 待确认信号
- 「谁在说话」:来源清单、改判按钮(是插件/其实是主人)、撤回没核实记忆
- 「会记账的「对外发送」动作」:内置清单、新插件的发送动作名、渠道显示名
- 「记忆整理」:上次整合、归档条数;「我替你说的那些话」:对外发送记录
- 看眼色深读档位(off / ambiguous / always)、对话里关心的每小时上限/间隔/静默、整合阈值/频率/保留比例/归档上限、 「没核实」条数
HTTP 接口(本机回环,面板用的就是这些):GET /dsh-wife-memory/state、GET|PUT /config、
POST /care、/extract、/consolidate、/remember、/forget、/sources、/retract、/care-inchat、
/routine-sniff,以及兜底网页 GET /panel。其中 POST /retract {kind, all?} 就是上面说的「一键撤回」。
文件
$DSH_HOME/wife-memory/
memory.json # 记忆库(删掉就清空;会自动重建),含 archive / consolidations / feedback / sources
config.json # 设置(面板保存的都在这里),含 pluginSources / humanSources / outboundTools / outboundVerbs / outboundChannels
diag.log # 自己的流水:启动、抽取收获、看眼色结论、整合前后、关掉了哪条规则、为什么没发、新认出来源/工具
代码在 E:\DSH\plugins\dsh-wife-memory\:index.js(挂载/抽取/关心/工具/路由)+ lib/(state 记忆库、
extract 抽取提示、care 主动关心、carenow 对话里关心、schedule 作息嗅探、adapt 字面信号、readmind 看眼色、
consolidate 整合、observe 自动认对外发送工具、sources 自动认消息来源、llm 模型调用、web 路由、panel 兜底页)
client.js(设置页)。
不做什么(边界,别踩)
- 不 import 任何
@deepseek-ai/*:profile 的 node_modules 里没有那些包,import 会让整个插件静默加载失败。 - 不改会话、不在会话里插消息 —— 主动说话只走
agent.followup(),消息带source.kind = 'wife-memory', 并用「框住内心话」的说明包住,模型不会把它当成主人说的话。 - 不发任何外部网络请求;除了
ctx.llm.stream(用当前会话的模型)之外不花钱。 - 对话里关心只是把一小段提示拼进本轮摘要(纯规则、不额外调模型),频率有闸:每小时 ≤2 次、间隔 ≥25 分钟、同由头 6 小时不重复。
- 抽取有闸:≥40 字、或者提到情绪/习惯/「我最近」这类才抽;安静 45 秒后合并成一抽;两次抽取至少隔 60 秒; 记忆库有上限(事实 300 / 情绪 200 / 在忙 60 / 观察 200)。
- 整合/深读也都走
ctx.llm.stream,同样有闸(整合:阈值 240 条或每 20 抽、最小间隔 10 分钟; 深读:默认只在拿不准时、最小间隔 300 秒)。 - 你聊插件/需求本身的长消息(出现「插件/代码/整合/面板」这类词且很长)不会被当成对你的反馈—— 免得把「这个功能要能识别『别提醒我吃饭』」误读成「他不想被提醒吃饭」。
已知边界
- 对方回你的 B站/抖音私信,插件看不到;除非「刷B站」那种监视把它拉回会话,它才当成对话的一部分。
- 作息和力度都是「先给一个默认值,再慢慢学」;学偏了在面板上手动改或点「恢复初始力度」。
- 记忆是本机 JSON,不做云同步;换机器就把
wife-memory/目录拷过去。
No comments yet. Be the first to write one.