DSH Plugins Marketplace

DSH Plugins

Plugins

/

dsh-shared-brain

d

dsh-shared-brain

Manifest valid

共享大脑 · 让所有 DSH 会话共用同一套人设与记忆,按底线/认知/情感/本能/声音/网络/自动互动七层加权注入;表情包目录定时自动加载

UI (client)hasBundlePatch

dsh-shared-brain

让人设与记忆在所有会话之间共享,并按「性质」分成七个模块,各自带权重、各自管注入策略。

一个大脑,七个模块

模块装什么权重含义注入策略
底线 bottomline操作限制、禁令、权限边界约束强度(≥0.8 = 强约束)手动钉住的才永远注入
认知 cognition事实、结论、决定置信度(≥0.8 = 已确认)相关时召回
情感 emotion关系、偏好、情绪、称呼亲密度(≥0.6 = 强偏好)相关时召回
本能 instinct反复使用的行为习惯使用频次(≥0.7 = 习惯)高频时自动触发
声音 voice说话方式、语气、口头禅风格强度常驻生效
网络 network允许对外获取信息(→ 学习)—开关
自动互动 agentive允许主观能动性(→ 主动)—开关

为什么这么分

单一记忆列表的问题在于所有记忆平权——「不要删我的文件」和「今天天气不错」在召回时分量一样。分层之后:

  • 底线是硬约束,不能靠「相关不相关」决定要不要生效,所以永远注入——但只有你亲手钉住的条目才算底线。
  • 认知是可能被推翻的事实,所以用置信度,可随新信息降权。
  • 情感有亲疏远近,用亲密度决定语气偏向。
  • 本能是从「反复出现」里长出来的:同一主题被提到 3 次以上就自动归入本能,权重随频次上涨。
  • 声音是「怎么开口」,属于人设的一部分,常驻。

权重怎么算

召回分数 = 基础相关性 × 条目权重 × 模块权重 + 模块加成
  • 条目权重:这一条记忆自己的分量(0..1)
  • 模块权重:整个模块的分量(0..1),/shared-memory weight <模块> <0..1> 可调
  • 模块加成:底线 +3.0、本能 +1.8、情感 +1.4、认知 +1.0

权重为 0 的条目/模块直接不参与召回。

怎么分类

按优先级,先命中先定:

  1. 情感 —— 出现「喜欢 / 讨厌 / 想念 / 叫我 / 我们」这类信号词
  2. 本能 —— 同一主题在库里已出现 2 次以上(频次判定,不靠关键词)
  3. 认知 —— 其余全部(普通事实);命中「结论 / 原因 / 已修复 / 版本」等词时置信度提到 0.8

底线是手动模块:分类器看到「不要 / 必须 / 禁止 / 权限 / 机密」这类信号时, 只把它记成一条高权重的认知(并提示「可手动钉为底线」), 绝不自动变成底线。只有你在设置页或命令里亲手钉住的底线,才会每次强制注入。

记忆的寿命是自动判断的

不用你手动切「永久 / 临时」——每条入库的记忆都会被 judgeLifetime() 判一次:

命中信号判定例子
长期约定(以后 / 每次 / 从今以后 / 长期)永久「以后每周三都开会」
稳定偏好、称呼、规则、配置事实永久「请叫我小鱼」「我讨厌临时文件」
明确时间窗(今天 / 明天 / 本周 / 本月)临时,12 小时 ~ 10 天「今天下午三点开会」
过渡措辞(临时 / 先 / 暂缓 / 待定)临时,7 天「先这么放着」
相对时长(3 天后 / 2 小时内)临时,按时长「两天后回来看」
当前状态(当前版本 / 正在跑 / 剩余额度)临时,14 天「当前版本是 2.7.2」
都没命中临时,14 天(兜底)—

判断依据是整句的意思,不是单个词:「我讨厌临时文件」里的「临时」说的是文件类型, 这条记忆该永久;「今天先跑这个临时方案」里的「临时」说的是保质期,这条该过期。 判定结果当然也可以在设置页里手动改回来。

装上

装完都要把 dsh-shared-brain 写进 profile 的 package.json → dsh.profile.bundles,然后重启 DSH 宿主。

方式一:桌面版设置页(最省事)

  1. 打开 设置 → 插件 → 安装插件
  2. 粘贴下面任意一个,回车:
https://github.com/youx123/dsh-shared-brain
https://github.com/youx123/dsh-shared-brain/releases/download/v0.9.12/dsh-shared-brain-0.9.12.tgz
  1. 装完在插件列表里确认 dsh-shared-brain 在启用状态,重启 DSH

GitHub 地址和 tgz 直链不经过安装源,本机需要能直接访问 GitHub(或者配好代理)。 网络不通时用方式二,先把 tgz 下载下来再喂给 CLI。

方式二:命令行装 Release 里的 tgz

到 Releases 下载 dsh-shared-brain-0.9.12.tgz,然后:

dsh plugin --profile <你的 profile> add ./dsh-shared-brain-0.9.12.tgz

方式三:直接装 git 源

dsh plugin --profile <你的 profile> add git+https://github.com/youx123/dsh-shared-brain.git

方式四:从源码自己打一个

git clone https://github.com/youx123/dsh-shared-brain.git
cd dsh-shared-brain
npm pack                       # 产出 dsh-shared-brain-0.9.12.tgz
dsh plugin --profile <你的 profile> add ./dsh-shared-brain-0.9.12.tgz

本插件是纯 JS,装完不需要构建;files 发布 lib/、assets/、cordis.patch.yml、README.md、LICENSE(assets/emoji/ 是内置表情包,少了它表情功能不可用)。 装完打开设置页能看到新的「共享大脑」一节,说明宿主已经加载到它了。

在哪里调

设置页

DSH 设置页里有一节「共享大脑」,点开就能配置:

  • 记忆模块:底线 / 认知 / 情感 / 本能 四张卡片,每张有
    • 权重滑杆(0..1,拖动松手即生效)
    • 启用 / 停用按钮
    • 条目数、平均权重、常驻注入 / 召回加成 / 最多注入等元信息
    • 「清空该模块」按钮
  • 手动底线:写下一条不可协商的约束并「钉住」——只有钉住的底线才会每次强制注入;可以取消钉住或删除。每张模块卡里的条目还能切作用域(全局 / 只在这个工作区)
  • 声音:语气、口头禅(逗号或换行分隔)、自称(默认表情挪到了表情包卡片里,两处不再重复)
  • 语音与唱歌:音色 ID / 音色名称、语速、音调、音量,以及唱法(风格、速度 BPM、调性)——这些是给将来的语音 / 唱歌模块用的物理参数,不会被念出来
  • 人设:人设由本插件自己管理(不再依赖人设插件):切换内置或自定义人设、编辑正文、新建、删除;切换后所有会话立刻生效,不用重启。还能「给某个会话单独指定」一个人设(选会话 + 选人设 + 应用)
  • 表情包目录(定时自动加载):填一个目录(留空用默认目录,插件会自己建出来),往里面丢文件就行,不用在面板里一个个加:
    • .txt / .md / .emojilist:一行一个表情,或写成「名字: 表情」
    • 图片表情:.png / .jpg / .jpeg / .gif / .webp / .bmp / .avif,文件名就是情绪名(如 震惊.png)
    • 默认每 5 分钟自动扫一次目录,新丢进去的表情会自己长出来;可以开 / 关自动加载、改扫描间隔(30 秒 ~ 24 小时,界面上按分钟填)、点「立即重扫」马上刷新
    • 卡片上显示上次扫描时间、文字 / 图片分别多少个,以及最近一次扫描新增 / 删除了哪些表情
    • 这张卡上还有「默认表情」——目录里挑不到合适的时用它
  • 搜索记忆:按关键词翻找旧记忆,结果带模块徽章与永久 / 临时徽章;还能按模块、永久/临时、「只看钉住的」筛选
  • 能力开关:网络 / 自动互动 两个开关
  • 自动互动 · 定时主动(真的有动作):开启后插件会按间隔给还活着的会话发一条「主动检查一下」的指令(走 agent.followup(),会唤醒会话,等于自己开新一轮),不再只是一句提示词:
    • 填「主动间隔」(分钟)→ 「保存间隔」;改完立即重启定时器,不用重启宿主
    • 「立刻主动一次」按钮:马上给当前所有活跃会话发一条,用来验证是不是真的在跑
    • 卡片上显示定时器状态、已主动 / 跳过次数、正在盯着的会话数、最近一次主动时间
    • 间隔范围 1 分钟 ~ 24 小时;关闭自动互动时定时器会停掉
  • 自动互动的生效范围:选「全部会话」或「指定会话」;选后者时按工作区分组列出会话(当前会话带「当前」标记、正在开着的带「开着」),勾选即可(勾选的会话里才允许主动行为)。列表来自宿主:活跃会话 ∪ 磁盘上的历史会话,进来就能看到,不用先打开每个会话;想看最新的点「刷新会话」;实在找不到可以把会话 ID 直接粘进输入框
  • 自动互动的时间窗:开启后只在指定时段(如 09:00 ~ 23:00,支持跨午夜)才会主动;之外只回应用户明确交代的事
  • 最近的记忆:最近若干条记忆及其所属模块与权重,每条后面有「永久 / 临时」切换(临时默认 24 小时后过期)
  • 清空全部记忆

侧边栏

输入框上方有一条「大脑 · N」小条:显示记忆总数、底线条目数、已开启的能力图标(🌐 网络 / ✨ 自动互动),点「打开面板」弹出完整配置浮层。

服务端接口

界面通过这两个 HTTP 端点与插件通信(仅监听回环地址):

GET  /api/dsh-shared-brain/status   当前模块配置 + 记忆快照
POST /api/dsh-shared-brain/mutate   {"action":"...", ...}

可用 action:
setWeight / setEnabled / setVoice / setSpeech / clear /
addItem / removeItem / pinItem / setLifetime / setAgentiveScope /
setEmojiDir / setEmojiAuto / rescanEmoji / addEmoji /
setItemScope / setAgentiveTimeWindow / setSessionPersona /
setAgentiveInterval / runProactive /
selectPersona / savePersona / deletePersona / searchMemory

与 Cursor「共享大脑」扩展协同(0.9.3+)

若本机安装了 Cursor 扩展 cursor-shared-brain,两边共用:

  • ~/.cursor/storages/cursor-shared-brain/sync-coordinator.json —— 协调锁与 generation,避免 DSH 写记忆与 Cursor 写规则互相覆盖
  • ~/.cursor/storages/cursor-shared-brain/sync-registry.json —— 统一条目指纹
  • ~/.cursor/rules/shared-brain/ —— Cursor 侧规则镜像(记忆在 memory/*.mdc)

DSH 插件 lib/cross-sync.js 会监听上述文件;从 Cursor inbound 拉记忆,对话入库时通过 writeStoreSynced 通知 Cursor 端合并。重启 DSH 宿主后生效。

数据落在哪

都在 DSH_HOME(默认 ~/.dsh)下,跨 profile、跨会话共用:

  • ~/.dsh/storages/dsh-shared-brain/memory.json —— 共享记忆库(扁平条目,带 module 与 weight)
  • ~/.dsh/storages/dsh-shared-brain/modules.json —— 模块配置(权重、声音风格、能力开关)
  • ~/.dsh/storages/dsh-shared-brain/persona.json —— 人设库(本插件自己拥有,内置 3 份人设;首次启动会从旧的人设插件导入一次;bySession 存「某个会话专用的那份」)
  • ~/.dsh/storages/dsh-shared-brain/emoji/ —— 默认表情包目录(也可以改成任意目录)
  • ~/.dsh/storages/dsh-shared-brain/sync-token.json —— 跨机同步用的访问令牌(单独一份,权限 0600;不再跟普通配置混在 sync.json 里)
  • ~/.hindsight/coding-agent.json —— Hindsight 配置(本插件负责固定 bank)

工作方式

  1. 入库:每轮对话结束自动摘要成「用户问 → 助手答」,分类器决定它进哪个模块,条目带自己的权重。没信息量的对话直接跳过(「继续」「ok」这种不进库),带密钥的先脱敏再入库。
  2. 召回:每轮开始用你这次的提问跨模块检索;底线条目无条件注入,其余按加权分数取。
  3. 本能养成:同一主题反复出现 → 自动归入本能 → 权重上涨 → 之后不用你提,它自己会冒出来。
  4. 人设自持:人设正文直接进提示词;库里没有人设就回落到内置的「大肥鱼」;旧的人设插件库在首次启动时导入一次,之后各管各的。
  5. 寿命自动判定:每条入库的记忆当场判永久 / 临时,到期的临时记忆自动从库里清掉;被召回到的临时记忆会自动续期(滑动窗口)。
  6. 冲突以新为准:新入库的说法如果明显推翻了旧说法(用了「改用 / 换成 / 不用了 / 其实是」这类转向词且主题相同),旧条目会被标记为被取代。
  7. 作用域:条目可以是「到处生效」或「只在这个工作区生效」——按 cwd 判断;自动互动还可以叠加时间窗与「只在指定会话」。
  8. 跨会话共享(对别的插件开的会话一样生效):提示词段落是全局注册的,所以 task-board、im、ssh 等插件自己开的会话、以及子代理,都会拿到同一份人设 / 声音 / 记忆;上下文按 agent.session.header.id 分开存,并发会话不会互相污染(以前用模块级变量,子代理会拿到父会话的召回内容)。
  9. 自动互动会真的动:定时器按间隔给活跃会话发 followup(唤醒会话、开新一轮),而不是只在提示词里写一句「你可以主动一点」。
  10. 记忆库不存凭据:正文与标签入库前都过一遍脱敏(PAT / API key / 密码 / 带密码的连接串 → [已脱敏]);老库里已有的也会在召回时顺手洗掉。

召回用轻量词袋打分:英文按词、中文按 2–4 字滑窗切分(切更长的片段才能命中「共享记忆」这类词,只切 2 字的话「记忆」和「忆库」对不上)。

/shared-memory

/shared-memory                    总览:各模块条目数与权重、人设、能力开关、最近 5 条
/shared-memory modules            各模块明细(条目 + 权重 + 命中次数)
/shared-memory bottomline         查看单个模块
/shared-memory weight 认知 0.8     调整模块权重(0..1)
/shared-memory voice 说话要带点傲娇  往声音模块加一条风格
/shared-memory add 底线 不要删我的文件  手动加一条(底线会自动钉住)
/shared-memory pin 底线 <条目>      钉住 / unpin 取消钉住
/shared-memory permanent 认知 <条目> 改成永久记忆
/shared-memory temporary 认知 <条目> [小时]  改成临时记忆(默认 24 小时)
/shared-memory remove 底线 <条目>    删掉一条
/shared-memory network on|off     开关「对外获取信息」
/shared-memory agentive on|off    开关「主观能动性」
/shared-memory agentive scope all|session <会话ID...>  设定自动互动生效范围
/shared-memory agentive interval <分钟>  设定自动互动间隔(1 ~ 1440 分钟)
/shared-memory agentive now      立刻对所有活跃会话主动搭话一次
/shared-memory persona           列出人设(内置 + 自定义)
/shared-memory persona use <id>   切换人设
/shared-memory persona save <名字> <正文...>   新建人设(自动切过去)
/shared-memory persona delete <id>  删除自定义人设(内置不可删)
/shared-memory persona session <会话ID> [人设ID]  给某个会话单独指定人设(留空回全局)
/shared-memory speech             查看语音 / 唱歌参数
/shared-memory speech voiceName 晓晓   改音色(voiceId / voiceName / rate / pitch / volume)
/shared-memory speech sing.style 轻哼    改唱法(sing.enabled / sing.style / sing.bpm / sing.key)
/shared-memory emoji              查看表情包目录、自动加载状态与清单
/shared-memory emoji dir <路径>    设置表情包目录
/shared-memory emoji autoload on|off  定时自动加载开关
/shared-memory emoji interval <分钟>  改自动扫描间隔(30 秒 ~ 24 小时)
/shared-memory emoji reload        立刻重扫目录
/shared-memory emoji add <名字> <表情>  往目录里加一个表情(放文件进目录也行)
/shared-memory scope <模块> <条目> global|workspace [路径]  改条目作用域
/shared-memory timewindow         查看自动互动的时间窗
/shared-memory timewindow on|off [开始 结束]  开/关时间窗(跨午夜也行)
/shared-memory search <关键词>     搜索记忆
/shared-memory clear 底线         清空某个模块
/shared-memory clear              全清

说明

  • 本插件自己拥有人设:内置 3 份(大肥鱼 / 阿茶 / 听澜),可自建可编辑,切换立即生效。旧的人设插件库只在首次启动时导入一次,之后互不干扰。
  • Hindsight 的 bank 固定与启停是两件事:即使现在没有凭据被停用了,bank 也已经写死了——以后配上 API key,记忆不会再按工作区分裂。
  • 注入的记忆会带上「若与当前指令冲突,以当前指令为准」,底线条目额外标注「仅在被用户明确撤销时才可忽略」。
  • 网络与自动互动默认关闭:前者会对外发请求,后者会让助手自作主张。两者都需要你显式开。
  • 自动互动现在有真实动作:开启后会起一个定时器(默认每 15 分钟,界面里按分钟改),给还活着的会话发一条「主动检查一下」的指令——用的是 agent.followup(),会唤醒会话,因此它会自己开新一轮。可以用「立刻主动一次」当场验证。作用范围默认是全部会话(含别的插件开的),也可以限定到指定会话。(0.9.3 修了个 bug:「立刻主动一次」之前没把开关配置传进去,所以永远显示「没有发出」,现在真的会发。)
  • 发不出去时会说实话:点了「立刻主动一次」如果没发出去,提示里会写清是「没有活跃会话」、是「开关没开」,还是「会话不在作用域 / 时间窗内 / 缺会话 id」,不再只丢一句「没有发出」。
  • 和 Cursor「共享大脑」扩展共用一套记忆(0.9.3 新增,见上文「与 Cursor 扩展协同」):两边用同一个协调锁,谁写谁先拿锁,写完递增 generation,避免互相覆盖;DSH 侧还会从 Cursor 的规则目录往回拉记忆。没装 Cursor 扩展的话这一段自动跳过,不影响使用。
  • 上下文按会话隔离:以前插件把「当前提示词 / 当前会话 / 当前目录」放在模块级变量里,多个会话并发时互相覆盖,子代理会拿到父会话的召回内容;现在改成按 agent.session.header.id 存,注入的内容也按会话去重,不会每一步都塞一遍。
  • 底线永远是你说了算:自动分类不会把任何东西塞进底线;钉住才生效。
  • 底线不会被条数上限偷偷截断(0.9.3 修复):以前底线模块的注入上限硬编码成 12 条,你手动钉了十几条常用底线之后,靠后的低权重那几条会被静默丢掉(界面上还显示有、提示词里却没有)。现在上限提到 24 条,并加了回归测试——钉住多少条,就有多少条进提示词。
  • 一套常用底线:本鱼给你预置了 14 条常用硬约束(不删数据、不外传隐私、不跑来源不明的脚本、改系统配置先问、不擅自提权、对外发布先许可、不把密钥写进代码、不拿猜测当事实、改动最小化、装依赖先确认、如实汇报、影响机器状态先问、不主动联网、中文回答)。每条都已钉住、永久生效,在设置页的「底线」卡片里可以逐条删改,也可以用 /shared-memory add 底线 <你的约束> 追加自己的。
  • 临时记忆会自己过期:寿命由内容自动判定(见上表),到期自动从库里清掉;你也可以随时手动改永久 / 临时。
  • 语音参数不会被念出来:音色、语速、音调、音量、唱法是给将来的语音 / 唱歌模块准备的物理参数,只做记录,不进对话文本。
  • 会话列表是怎么来的:宿主里没在 inject 里声明的服务只能靠 ctx.get() 拿——所以插件用 ctx.get('sessions')、ctx.get('sessionPersistence')、ctx.get('workspaceRegistry') 三份数据合成会话目录,后台刷新 + 读缓存(3 秒节流),不会卡住设置页。
  • 表情从目录里来,而且是定时自己加载的:默认目录 ~/.dsh/storages/dsh-shared-brain/emoji(插件启动时会建出来),也可以指向任意目录;.txt 一行一个表情(或「名字: 表情」),图片表情(.png / .gif / .webp …)的文件名就是情绪名。默认每 5 分钟自动扫一次,往目录里丢文件就行,不用在面板里一个个加;自动加载可以关,关掉后只在点「立即重扫」时更新。
  • 按心情挑表情(0.9.8 新增):心情上下文里会附一句「这会儿适合哪几个表情」,低落时不会让你甩「庆祝」,高涨时才推荐活泼的。开关和频率在设置页的表情卡片里(不提 / 少量 / 多一点)。
  • 自带一套表情,不用另装插件(0.9.8 新增):插件内置大肥鱼表情包 100 张 GIF(assets/emoji/),并把其中 40 张按 dsh-emoji-core@1 语义 key 对好号(开心 / 抱抱 / 好累 / 恭喜 …)。提示词里直接给现成的 markdown 图片,模型照抄就能出图,不需要装 dsh-emoji。想换包也可以把符合该协议的包放进 ~/.dsh/emoji-packs/,插件只读不写。
  • 多台机器共用一个大脑(0.9.9 新增):把记忆、人设、设置放进一个私有 Git 仓库(GitHub Contents API),几台机器定时 pull-merge-push。只同步全局条目,会话内的记忆不出本机;本机删掉的条目会带墓碑同步过去,不会在别的机器上复活。设置页「跨机同步」卡片里填 owner/repo 和令牌即可。
  • 内置表情的许可要留意:那 100 张 GIF 来自 ai-biaoqingbao,是 CC BY-NC-SA 4.0(署名 · 非商业 · 相同方式共享),和本仓库代码的 MIT 不是同一个许可;商用请自行取得授权,细节见 assets/emoji/NOTICE.txt。
  • 密钥不会进记忆库(0.9.6 新增):记忆是长期存储的,还会被召回进提示词,所以入库前一律脱敏——GitHub PAT(ghp_ / github_pat_)、OpenAI / Anthropic key、Slack / AWS / Google key、api_key = "..." 这类赋值形式、带密码的连接串,全都会换成 [已脱敏]。手动从设置页或 /shared-memory add 加的条目也一样会被洗一遍。
  • 老库里残留的密钥会被清掉(0.9.6 新增):脱敏是 0.9.6 才加的,之前入库的条目可能已经含有凭据。插件在每次召回时会顺手把正文和标签里的密钥洗一遍(标签容易漏——摘要时英文 token 会被当成检索标签)。
  • 废话不入库(0.9.6 新增):以前只要「用户问 + 助手答」凑够 12 个字就记,结果库里堆满了「继续 → Excellent!」「ok → 好的」这种对话快照,召回时会召出一堆噪音。现在只有有信息量的对话才入库:够长、含决策 / 偏好信号(记住 / 以后都 / 改成 / 不要 / 默认 …)、或者带路径 / 版本 / 命令 / 报错。纯寒暄和纯选项应答(「选 2 吧」)直接跳过。
  • 语音参数真的会存下来(0.9.6 修复):以前在设置页改语速 / 音调 / 音量 / 唱法,提示写着「语音参数已更新:语速 1.15×」,刷新一看还是旧值——桥接层把计算结果丢掉、写回了旧配置。现在写的是新配置,并有回归测试钉住。(命令行 /shared-memory speech 那条路径一直是对的。)
  • 清空要按两下(0.9.6 新增):「清空全部」和每个模块的「清空」都是先点一下变成「确认清空?」,4 秒内再点一下才真的清,避免一手滑把整个记忆库抹掉。不弹浏览器 confirm——插件界面在 webview 里,confirm 不一定可用。
  • 时间窗可以先填时间再开(0.9.6 修复):时间窗关着的时候,开始 / 结束时间的输入框以前是禁用的,想先填好再打开就得来回点;现在随时能改,只有开关决定它生不生效。
  • 人设列表不再重名两份(0.9.6 修复):内置人设被改过之后会另存一份同 id 的自定义副本,以前列表把两份都列出来,设置页里会出现两个「大肥鱼」;现在同一个 id 只出现一次(自定义覆盖内置)。
  • 外部工具写进来的东西也会被洗(0.9.6 修复):脱敏不只管插件自己的写入路径。Cursor「共享大脑」扩展、别的工具、甚至有人手工改 memory.json / modules.json,都可能把凭据写进去。现在读库时也过一遍闸门——不管这份记忆是谁写进来的,正文和标签里的密钥都会被换成 [已脱敏] 并写回磁盘。

0.10.7 更新说明

记忆变成了一张图

以前记忆是一列扁平的条目:召回就看「这一轮的文本像不像」,用词不一样就召不回来。 现在多了一层图谱(~/.dsh/storages/dsh-shared-brain/graph.json,派生缓存,删了会重建):

  • 节点:每条记忆一个节点;从标签、target、以及可选的 LLM 抽取结果里抽出实体再各一个节点(正文只用来算记忆之间的共现,不直接产实体)。
  • 边:mentions(记忆提到实体)、cooccur(两段记忆文本高度重合)、supersedes(新记忆取代旧的)。
  • 实体抽取不调 LLM(默认走规则 + 停用词表,挡掉 gif/png/yml/bing 这类没语义的词)。想接 LLM 抽取就传 entityExtractor 回调,不传也能跑。

顺边召回:命中一条,带出它的邻居

召回命中后顺着图再捞一跳,邻居以「经记忆图谱关联」标在注入的提示词里。

  • 实体桥(0.10.5)是关键:记忆与记忆之间靠文本相似度几乎连不上(真实库里 1000 多条共现边中,记忆↔记忆只有 3 条), 但两段记忆提到同一个实体时,基本就是同一件事的不同侧面。所以加了「命中 → 共用实体 → 其它记忆」的二跳路径。
  • 枢纽阻尼(0.10.6):像「表情」这种被几十条记忆共用的词,会把一堆不相关的内容拉进来。 现在二跳分数乘 1/sqrt(实体度数),并且同一个实体最多带 maxPerEntity(默认 2)条,避免一个词占满全部名额。
  • 邻居分数一定低于直接命中(两跳各衰减一次,再乘桥梁系数 0.6),不会挤掉原本该出现的条目。
  • 图谱文件缺失时会现场建一张并落盘;readGraph 带 mtime+size 缓存(实测 3.5ms → 0.1ms),不再每轮重新解析几百 KB JSON。

遗忘:只归档,不删除

  • 候选判据:minAgeDays(默认 90 天)、hits 极少、图上度数极低、非 pinned、非 permanent、模块限 cognition。够久的(180 天)标 strong,界面上排在前面。
  • 归档只打三个字段(archived / archivedAt / archiveReason),正文一个字都不动,随时能恢复。
  • 归档条目不参与召回(recall.js 与 weighted-recall.js 两处都过滤),也不会再进图谱。
  • store.js 的 normalizeStore 显式保住这三个字段——否则下次读库会被「逐字段重建」抹掉,归档标记静默复活。
  • 注意:归档只作用于扁平库 memory.json;模块桶 modules.json 暂不动(同步进来的条目在模块桶里没有对应 item)。

新增的接口

类型路径 / 动作作用
GET/api/dsh-shared-brain/graph返回 {graph:{nodes,edges,stats}, graphFile, candidates, total};?rebuild=1 强制重建
mutategraphInfo同上(但 postMutate 返回体恒为快照,客户端请走 GET)
mutatearchiveCandidates只列候选,不改任何东西
mutatearchiveItems {ids,reason?}打归档标记 + 重建图谱
mutaterestoreItems {ids}取消归档

可视化:能看见的那张网

客户端设置页的「记忆图谱」卡片:

  • 画布由 GraphCanvas 单独负责,手写力导向(斥力 1100/d²、弹簧按边权重、阻尼 0.82),不引三方库。
  • 节点:蓝点是记忆、橙点是实体,半径随连通度增长。图上节点多时按连通度取前 N 个(卡片 240、放大后 420),不会随手砍掉主干。
  • 边按类型分色分粗细:mentions 蓝、cooccur 紫、supersedes 金;越粗关系越近。
  • 悬停只高亮该节点和它的邻居,其它淡下去;标签只在重要节点(度 ≥ 6)和悬停时出现,避免糊成一团。
  • 卡片右上角有**「放大查看」**,原地展开到视口六成高并滚到可视区(客户端里没有 portal,所以是原地展开而不是弹窗); 顶部胶囊可以切「全部 / 只看记忆 / 只看实体」。
  • 下面一栏是归档候选,勾选后一键归档;文案明确写着「归档只打标记,文本不会被删」。
  • 空图时给提示而不是画一个空框;canvas 里用不了 CSS 变量,标签颜色取 getComputedStyle(canvas).color,深浅主题都看得见。

已知限制

  • 图谱是派生数据,不进跨机同步;换机器后第一次召回会现建。
  • 实体只来自标签与 target,正文里的实体要接 entityExtractor 才有。
  • 归档动作在真实库上只跑过单测与沙箱,没有对真实记忆执行过(候选当前为 0 条,因为库里所有条目都不到 90 天)。

0.10.1 更新说明

0.10.0 之后做了一轮三路专项审计(安全 / 情感与自主意识 / 性能),查出 30 处问题,全修了。 逐条细节见 FIXES.md 的「补充 19」。

会真泄露 / 真失效的四条

  • 磁盘上的明文凭据终于被擦掉了:以前脱敏只擦内存 —— readStore() 的写回判断看的是「擦掉了几条」,可规整函数早就先擦过一遍,所以永远是 0,磁盘上那份从来没更新过。跨机同步的写入又是裸写、不过脱敏,从远端拉下来的脏条目会再推上去。
  • 记忆条目不再能被当成指令:召回原文直接拼进提示词,一条写着自己闭合标签的记忆就能逃出包装、伪造系统标记。现在注入话术整段过滤、标签转义,注入正文也做了 HTML 转义。
  • 心情终于能影响说话了:「心情变了 3 点才算显著」而判定提示词让模型只给 −1~−3 —— 模型越听话越永远过不去闸门,心情播报恒为空。现在看原始幅度。
  • 表情提示不再被心情闸门陪葬:原来心情没变化就直接返回空,表情那一段一次都没跑过。

别再往云上推的东西

  • 本机路径 / 会话 id 不上云:以前 127 条记忆里 75 条带着绝对路径(含系统用户名)同步上去了
  • 「会话 → 人设」绑定只留本机:合并结果不再带 bySession
  • 选中会话的自动互动设置不再被同步清空:本地和推送用两套不同的规整

删了的东西真的会消失

模块条目和自定义人设的删除从来没传播过 —— 删除检测把待删列表同时当成「现有条目」和「上次见过」,交集全中,恒返回空。 真实同步记录里 26 条墓碑全是记忆条目,模块和人设一条都没有。修的时候同时补了「第一次同步不判删除」的守卫,不然新机器一连上就会把远端清空。

情感记忆接上了行为

intimacy / sentiment / target 以前只影响召回排序,对行为没有任何影响 —— 「记住老大讨厌啰嗦」和「记住老大讨厌啰嗦但亲密度 0.9」效果完全一样。 现在对老大的负向情感累积到一定程度会把心情判低,主动搭话改用更 soft 的调子。

每轮少干点没用的事

审计实测每轮固定开销 15~21ms,73% 是纯文件 I/O,计算只占 1.6ms。改了三处最费的地方:

  • 不再每轮重写整个记忆库:hits + 1 以前每轮把 108KB 整个重写一遍(6.69ms),而 hits 根本不参与打分。现在只在内存里累加,5 秒合并落一次盘;新增和删除仍然立即写
  • 读盘走 mtime 缓存:readStore 2.857 → 0.437ms,readModules 2.277 → 0.404ms(命中返回深拷贝,因为调用方都会就地改)
  • 召回加倒排索引:2.33 → 0.993ms,但 termHits 仍然保持 includes 的子串语义 —— 720 例随机比对逐条验证过结果完全一致,快不能靠改语义

审计还报了一条「人设与声音块重复渲染、能省 257 字符」,这条是误判:真实人设正文里根本没有那些块。没有为了凑数字去删渲染。

补丁 2:重载之后

重载后再清一遍,明文又回来了。追下去是两个新问题,不是之前的修复没生效:

  • 模块配置的写盘路径是裸写:脱敏只在「读」的时候做,写的时候把内存里那份直接落盘。所以一条含凭据的条目照样进得去 modules.json——而且是在新代码下发生的。现在写之前也脱敏。
  • 初始化写盘失败会打死整个插件:审计说的「apply 没有 try/catch」我去核了,读路径其实都有兜底,真正暴露的只有初始化那几次落盘(磁盘满、目录只读、文件被锁)——那会让插件加载失败、设置页空白。已加兜底。

同时纠正一条审计误报:sessionCatalog.refresh() 内部会 catch,不会 unhandledRejection。

没干完可以自己接着干

shouldContinueTurn / roundOf 这两个函数 0.9.7 就写好了,但一个调用点都没有——一直是死代码。现在接上了,接得比较保守:

  • 信号由模型自己给。宿主在「一轮即将结束」时只告诉插件「哪个会话」,没说「为什么停」,猜不出来。所以让它在回复末尾单独写一行 HTML 注释 <!-- 继续:下一步要干什么 -->——渲染出来看不见,不污染正文,正文中间提「继续」也不会误触
  • 受自动互动总开关管。关了主动搭话,这个也一起停,不留后门;另有一个 selfContinue 可以单独关
  • 一个会话最多续 3 轮,且在作用范围和时间窗内
  • 顺手补了个真 bug:原来「续接了几轮」只增不减,一个会话续满 3 轮之后这台机器上就永远不能再续了。现在老大真说话时清零

实测数据不吹:这套只验证到「标记能被解析、闸门会按 3 轮掐断、配置和提示词都跟着开关走」,没在真实会话里跑过一整轮续接——那要等模型自己写出标记才看得见。

测试

新增 tests/audit-0101.test.mjs(37 条)+ tests/perf.test.mjs(24 条),每条对应一个审计里实测复现过的现象。 更新了 2 条「测的是旧 bug」的断言。合计 362 通过 / 0 失败。

0.10.2

  • 勾选的会话不再被同步清掉:mergeModules 返回的「推送形状」会被写回本地,而它已经把 agentive.sessionIds 清空了 —— 每同步一次(10 分钟)就丢一次用户勾的会话。现在推远端用干净形状,写本地用 restoreLocalAgentive() 把 scope / sessionIds 盖回去。
  • 开关能一眼看出开还是关:五个真开关的按钮统一成 ● 已开启 / ○ 已关闭,title 写清「点此关闭 / 点此开启」,关闭态改虚线描边,并加 aria-pressed;能力卡片标题旁加「已开启 N / M」汇总。
  • 测试合计 371 通过 / 0 失败(remote-sync 38 / ui-render 30)。

0.10.0 更新说明

十四项加固,分三批:跨机同步不丢数据 / 隐私与资源有上限 / 工具链一条命令。 逐条细节(含为什么这么做、怎么验证)见 FIXES.md 的「补充 18」。

跨机同步

  • 按条目合并:以前整份比 updatedAt,在 A 机改语气会把 B 机的底线覆盖回去。现在条目按 id 独立合并,模块级字段才看整份时间
  • 令牌不再明文落盘:搬到 sync-token.json(0600),老的明文配置第一次读时自动迁走并擦掉
  • 删除有墓碑:记忆条目、模块条目、自定义人设都能删干净,不会在别台机器上复活
  • 边界说明:设置页可展开「什么会同步 / 什么只留本机」,并写明「删除会跟着走」

上限与校验

  • 心情判定默认每天最多 20 次(judgePerDay,0 = 不限),超了直接跳过,不烧钱
  • 每个会话的心情状态最多留 30 份(maxStates),按最后使用时间淘汰
  • 表情包先校验再收:真 PNG(魔数 + IHDR)、单图 ≤8MB、边长 ≤1024、最多 200 个 key;不合格的包会在设置页列出原因
  • 记忆摘要不再把「我先看看代码」这种过程播报写进去;老大自己的偏好句优先级最高,不会被误过滤

感知与文档

  • 「不接话」也能感知:只回「嗯 / 哦 / 行」判成 silent,心情按轻微失落记一笔(silentTurns + 轨迹里 kind:'silence');「不用了 / 别说了」判成 brisk
  • 人设合并加固:current 有独立时间戳(currentAt)、本机会话绑定不再被清空、坏条目丢弃并计数
  • 养鱼插件配置格式探测:配置文件不是 JSON / 结构不认识时一个字节都不写,只更新人设正文,并在设置页告警

0.9.12 更新说明

人设同步:共享大脑 → 养鱼插件(新)

在共享大脑里切人设、改正文,dsh-persona-dafeiyu(养鱼那个人设插件)跟着变,不用两边各配一遍。

  • 共享大脑人设库里的每一条,会以 sb- 前缀镜像进养鱼插件;插件里当前生效的人设也跟着指过去
  • 共享大脑里删掉的人设,镜像条目一并清掉
  • 养鱼插件里你自己导的人设(不叫 sb- 开头的)一律不碰,工作目录绑定也不动
  • 只有共享大脑的人设库真的变了才写盘 —— 你在养鱼插件里手动换人设,不会被 15 秒一次的同步拉回去
  • 设置页 → 共享大脑 → 人设卡片,多了一行「养鱼插件同步」:开关 + 立即同步 + 已镜像几条

注意:共享大脑是唯一权威源。内置的「大肥鱼 / 阿茶 / 听澜」正文以共享大脑里那份为准(比养鱼插件自带的短), 想用长版本就在共享大脑里编辑人设正文。

pnpm add file:vendor/dsh-shared-brain-0.10.1.tgz

0.9.10 更新说明

  • 修掉设置页「跨机同步」卡片的三个显示问题:
    • 「保存并同步」「立即同步」两个按钮之前没有按钮文字(只渲染出两个空框,根本不知道该点哪个)
    • 开关按钮同样是空框,现在直接显示「已开启 / 已关闭」
    • 动作行原来复用了三列网格样式,四个控件被挤到第二行;改成可换行的弹性行
  • 分支 / 间隔那一行改成专门的字段布局,不再被挤到下一行
  • 保存按钮改成实心主按钮,一眼能看出是主操作;同步中会显示「同步中…」

升级:见文末「0.9.12 更新说明」里的安装命令。

0.9.9 更新说明

多台设备共用一个大脑(跨机同步)

  • 新增 lib/remote-sync.js:用 GitHub Contents API 读写一个私有仓库里的三份数据 —— brain/memory.json、brain/persona.json、brain/modules.json。
  • 冲突处理是「拉下来合并再推」:推送撞车(409/422)会重新拉取合并,最多重试 3 次,不会把别人的改动覆盖掉。
  • 只同步全局条目:scope: 'session' 的记忆和 agentive.sessionIds 这类会话级配置不出本机(换台机器会话 id 就废了)。
  • 删除用墓碑:本地少了一条,就记一个删除时间戳推上去;别的机器合并时看到墓碑晚于条目更新时间就把这条丢掉,不会「删了又回来」。墓碑 30 天后自动清理。
  • 人设按 id 并集取新,bySession 不上云;模块设置整份按 updatedAt 取新。
  • 设置页新增「跨机同步」卡片:仓库(owner/repo)、令牌、分支、间隔(分钟)、开关、保存并同步、立即同步,下面显示上次同步时间 / 跟踪条数 / 删除记录 / 本机编号 / 最近摘要或错误。
  • 令牌也可以不放配置里,改用环境变量 DSH_SHARED_BRAIN_TOKEN(设置页会显示「来自环境变量」)。
  • 默认关闭;开关打开后每 10 分钟同步一次(可改,最小 1 分钟)。

怎么用

  1. 在 GitHub 建一个私有仓库(只放数据,比如 your-name/dsh-brain-data),勾一个 README 让它非空。
  2. 建一个 fine-grained token,只给这个仓库的 Contents: Read and write。
  3. 设置页 → 共享大脑 → 跨机同步:填 owner/repo 和令牌 → 保存并同步。
  4. 其它机器同样填一遍,就会拉回同一批记忆。

注意:数据是明文存在私有仓库里的(方便看 diff)。仓库千万别设成公开。

升级到 0.9.9

pnpm add file:vendor/dsh-shared-brain-0.9.12.tgz

0.9.8 更新说明

修掉的真 bug

  • /shared-memory 状态页一直显示「0 个会话有心情记录」:代码读的是 store.sessions,心情实际存在 store.states。
  • 「假待办」:主动搭话前会扫记忆找待办,以前只按关键词匹配正文,于是助手自己的工作日志(source: auto,正文里带「待办」「继续」)也被当成你交代的事,还排在心情与每日额度之前。现在只认 source: manual —— 你亲口说的才算。
  • 同一批记忆一轮注入两遍:系统提示词段落与 agent/pre-step 各召回一次。现在按「会话 + 目录 + 提问」缓存,只算一次。
  • 会话结束不清心情:agent/disposed 现在会清掉该会话的心情状态,避免会话 id 复用时带着上一个会话的情绪。
  • 「一次变化只说一次」失效:以前靠 injectedFor === updatedAt 判断,但任何写入都会改 updatedAt。改用单调递增的 revision。
  • 自动互动的间隔系数被最悲观的会话绑架:intervalFactor 从共享变量改成按会话记,取本轮参与者的最小值。
  • 「按心情说话」关掉后连心情都不读了:关开关只表示「别用心情决定语气」,数据照读。
  • 话题复读:主动搭话的话题库以前是写死的整句,模型会原样照抄。现在只给「这次想聊什么」的方向 + 素材提示,并明确要求别重复上一次。
  • instinct 模块永远是空的:以前「同一主题重复出现」几乎不可能命中(标签是路径碎片)。新增话题键抽取(关键词 + 汉字二元组兜底),同一件事说三次会升到 instinct(权重 1.8)。
  • 设置页心情卡片信息太少:现在显示情绪维度、判定理由、最近轨迹。

表情能力

  • 按心情挑表情:低落时避开 celebrate / cheer / applause / sarcastic,高涨时才推荐 star-eyes / celebrate / playful;开关与频率(不提 / 少量 / 多一点)在设置页表情卡片。
  • 内置大肥鱼表情包:100 张 GIF 随插件发布,40 张按 dsh-emoji-core@1 语义 key 映射;心情上下文直接给现成的 markdown 图片,不用装 dsh-emoji 也能出图。静态路由 GET /api/dsh-shared-brain/emoji/<key>.gif(只允许本机回环)。
  • 兼容 dsh-emoji 表情包:放在 ~/.dsh/emoji-packs/ 里、符合 pack.json + images/<key>.png 的包会被识别并列出(只读,不写用户的包)。

升级

pnpm add file:vendor/dsh-shared-brain-0.9.12.tgz

装完重启 DSH;设置页是客户端启动时拉取的,重开设置页即可看到新卡片。

开发

源码改完别急着跑测试 —— 测试读的是补丁源里的 lib,忘了同步就会看到「明明改了还是老结果」。一条命令搞定:

node tools/dev.cjs          # 同步 lib / assets / tests / tools 到仓库,然后跑全部 349 条测试
node tools/dev.cjs test     # 只跑测试
node tools/dev.cjs test emotion remote-sync   # 只跑指定几个

发版同样一条命令,默认只是彩排(不写盘、不发请求),确认后加 --push 才真发:

node tools/release.cjs --version 0.10.1        # 彩排:改版本 / 同步 / 全测 / 打包 / 提交 / 推送 / 建 Release 都会打印出来
node tools/release.cjs --version 0.10.1 --push # 真发

历史修复记录:FIXES.md 只留最近几版,2026-10-29 ~ 11-02 的早期小节在 docs/FIXES-archive.md。

License

代码:MIT。

assets/emoji/ 下的表情素材来自 diwer/ai-biaoqingbao, 采用 CC BY-NC-SA 4.0(署名 · 非商业性使用 · 相同方式共享),不属于 MIT 授权范围。 署名:基础角色 上善无形;二次设计 ZipZipPipe;本套整理 ming。完整声明见 assets/emoji/NOTICE.txt。 如果你要商用,请先取得素材权利人的授权,或者删掉 assets/emoji/ 只用代码。

Comments

Loading…