中文EN

dsh-agent-soul

memory已测通过npmMIT

给 agent 加人格注入、会衰减的记忆卡、心情状态和每日日记,让它跨会话保留身份和历史。

下载量 / Downloads
1,894
依赖体积 / node_modules
2.18 MB
测试时间 / Tested
2026-11-06
DSH 版本 / Version
0.2.2-rc.1

安装

dsh plugin --profile web add dsh-agent-soul

沙箱测试结果

阶段结果
1 安装✓
2 列装✓
3 挂载✓
4 启动—

四阶段在隔离沙箱里跑:安装(有网)→ 列装 → 挂载 → 启动(后三步断网)。

README

dsh-agent-soul

给 DSH 的 agent 一份会忘事的记忆、一套会变的人格、一份会起伏的心情,和一个她自己写的笔记本。

它做六件事:

· 解决什么

人格 · 用 Markdown 写「你是谁」。项目人格只覆盖同名小节,全局的照常生效 —— 不会因为某个目录里有 AGENT.md,你在全局写的话就整份失效

记忆 · 值得记的事自己进来,随时间变模糊:清晰 → 清楚 → 模糊 → 只剩印象 → 遗忘。忘了不是删掉,是先变得说不清

目的 · 从记忆里长出一份「心里惦记的事」,让她的主动有连续性 —— 不是定时问候,是主动想起

心情 · 每轮对话后自己判一次起伏(心情值 + 七情 + 时间线),不注入提示词。记下来的情绪有两个出口:当日记的底色、推着目的机制的驱动

日记 · 开机时把前一天沉淀成两篇:一篇公开(可以拿给你看),一篇私密(只写给自己)。先投消息让她用 soul_diary 自己写,文件没出现才由插件 llm.stream 兜底生成

笔记 · 她自己写的 md:一篇一个主题、自由格式、永不衰减。提示词里只给索引,正文用 soul_note 按需读

不管 B 站、不管 QQ、不管桌宠。这个插件只关心「她是谁」和「她记得什么」。


日常怎么用

装好之后基本不需要操作。想干预时有三个入口:

1. 什么都不做(大多数时候)

  • 你说话,值得记的事自动进记忆卡
  • 每天第一次打开,它会把不重要的事降一格清晰度
  • 她答应过的事、没弄明白的事,自动变成「心里惦记的事」
  • 开机时自动补写前一天的日记

2. 设置 → 灵魂(八个标签)

标签 · 用来干什么

概览 · 一眼看「到底生效没有」:人格来源、记忆条数与容量、惦记几件事、最近(当前心情 + 昨天日记写没写)

人格 · 直接编辑全局 / 项目人格。存完下一个回合生效

记忆 · 搜、看清晰度和重要度、固定(永不遗忘)、删、手动加

目的 · 四个驱动读数 + 惦记清单,可以「放下了」

心情 · 心情值 + 七情 + 时间线;可重置、可手动跑一次平复

日记 · 开关、兜底检查间隔、公开/私密语气、手动触发(可带备注)、最近日记列表

笔记 · 她自己写的 md:看、改、删、新建;也能关掉索引注入

设置 · 注入预算、叠加还是互斥、各项开关、立即整理一次

3. 直接跟她说话

「记住:我下周一开始做蓝桥杯的题」
「你还记得我之前说过什么关于 STM32 的事吗」
「把那条关于数学建模的记忆忘掉」
「你最近在惦记什么」

工具是四个:soul_memory(write/recall/forget/pin/list)、soul_goal(add/done/list)、soul_note(list/read/write/append/rename/delete)、soul_diary(write/look)。

文件全是纯文本,也可以直接改:

~/.dsh/AGENT.md                   全局人格
~/.dsh/agents/memory/global.json  记忆卡(改完刷新页面生效)

人格注入

三层来源与优先级

层 · 文件 · 怎么改

1 · <cwd>/AGENT.md · 自己的编辑器(跟着仓库走,可提交进 git)

2 · ~/.dsh/agents/projects/<slug>.md · 设置 → 人格 → 项目人格(本机)

3 · ~/.dsh/AGENT.md · 设置 → 人格 → 全局人格

项目层只取 1、2 里优先级最高的那一份(不是两份合并)——两份都写着 # 你是谁 时,模型会各听一半,最后谁也不像。

叠加还是互斥

· 互斥(exclusive) · 叠加(stack,默认)

有项目人格时全局怎么办 · 整份不注入 · 按小节叠加:项目覆盖同名小节,全局其余保留

你在全局写的通用约定 · 某些工作区完全不遵守,像没读到 · 到处生效,除非项目里显式覆盖同名小节

什么时候注入

会话第一轮注入一次,之后每 15 轮补一次。

不每轮重发(那是纯浪费),也不注入一次就再也不管 —— 上下文压缩之后会把人格正文压掉,那时她会「记得聊过什么,但不记得自己是谁」。15 轮补一次,成本可以忽略,压缩后能自己找回来。

⚠️ 如果 <cwd>/AGENT.md 存在,它会盖住设置页编辑的第 2 层。你在设置页改了、保存了、提示「已保存」,但其实没生效。页面会亮黄框提示。

主人是谁:没设就不注入

这个包默认不知道你在叫谁 —— ownerName / ownerGender / relationship 三项默认都是空串,安装后不会有人告诉你「你是某某的伴侣」「某某是男生」。

配置 · 空着时提示词里会怎样

ownerName · 「你是「小糖」,是X 的伴侣」「X 是男生」这类句子整句省略,只留「你是「小糖」。」心情判定里统一用中性的「对方」

ownerGender · 「⚠️ X 是男生 / 女生,写出「她 / 他」即算判错」那一整行不写(不猜)

relationship · 「是 X 的伴侣」这半句不写,只留名字

不会因此让插件失效:记忆、笔记、日记、目的机制都不读 ownerName,照常工作。只是提示词里「关于谁」的那几句不在了。

在 设置 → 灵魂 → 设置 → 主人是谁 里填这三项即可恢复;ownerName 没填时页面上有醒目提示,soul.log 里也会每小时留一行「未设置 ownerName,相关段落已跳过」。

迁移:0.5.2 及以前的配置只有 moodOwnerName(旧版默认写死 树树)。升级后如果配置里没有 ownerName 键,插件会把 moodOwnerName 的值搬过去(没有就沿用旧版默认值 树树)并写回 persona.json,老用户的体验不变。

记忆

什么会被记住

自动提取分三步:过滤 → 提炼 → 打分入库。

1. 过滤(纯规则):只收「人在说话」。丢掉宿主注入的上下文、桥接层的触发模板、信封前缀(「(QQ 私聊·用户本人 [QQ号])」),以及她自己被桥回显的话。

信封前缀有 20 多字,会把「起了起了」这种 4 字短句凑够 8 字门槛,长度过滤直接失效。而回显的那条更糟:她的每句自言自语都会变成记忆,是自我强化的死循环。

2. 提炼(模型):过了过滤的原话不直接入库,先交给模型改写成记忆。

原话:(QQ 私聊·用户本人 [QQ号])今早上7点多才起来。昨晚上在忙着给你做插件,睡的晚了。
记忆:树树今早七点多才起,昨晚熬夜在做插件

提示词里写死三条硬约束:写成「关于树树的一条记忆」而不是复述对话、不许编他没说过的事、没有信息量就输出空数组(宁可少记)。提炼失败一律退回原话直存,不丢信息。

3. 打分入库:过线才写进卡,门槛按 kind 分档 —— 一次性事件(event)要 70,事实 / 偏好 / 约定这类画像(fact / preference / promise)是 42。

你说的 · 结果

「今晚吃牛肉丸子汤」 · 不记 —— 一次性日常,记下来只占位置

「我手上有一块 F103 开发板」 · 记

「我目标是当嵌入式工程师」「我是烟台大学大二」 · 记,且额外写一份进全局卡(换工作区也认得你)

打分是纯规则 + 数值,不调模型 —— 什么时候该记、记多久,确定性比聪明重要。模型只负责把话改写成记忆,不参与「要不要记」。

会忘记

清晰 ──→ 清楚 ──→ 模糊 ──→ 只剩印象 ──→ 遗忘
200字     130字    42字+    只剩标题     移出提示词
                  「记不清了」「好像有…」  进 archive.jsonl

关键不在阶梯,在注入时的措辞:模糊的写成「……(细节记不清了)」,只剩印象的写成「好像有「xx」这件事…(想不起来了)」。模型看到的是「我隐约记得」,而不是「查无此事」或者「细节完备但其实已经不可靠」。

  • 一天只降一档:慢慢模糊,不是某天突然想不起来
  • 重要的事降得慢:普通记忆每天 -0.35 留存,和主人相关的只有 -0.12
  • 永远不会忘的:固定的,和「第一次」类的事
  • 提起来会变清楚:被再次提及就刷新时间、召回 +1,一天回升一档

入库前脱敏(密钥 / 手机号 / 身份证 / 卡号 / 链接 / QQ 号),太短的不记。

注入策略

  • 第一轮:整张卡 + 一段「最近」(见下节)
  • 之后:按当前这句话召回相关几条,同一话题没变就复用上次的文本,不重拼
  • 近似重复:新记忆和已有条目字面相近时,只记一行日志提醒,不自动替换 —— 让字符串算法决定「哪句是真的」太危险

「最近」:跨对话的那条通路

新会话第一轮,除了记忆卡,还会带上:

### 前几天的日记
(最近 2 天的公开日记,超 6000 字留最近的)

### 今天到目前为止(可能来自别的对话)
[14:20] 树树:……
[14:21] 我:……

为什么需要:人不会在一个对话里做完所有事 —— 修插件、QQ 聊天、闲聊是分开的会话。记忆卡只有零碎事实,日记只写到昨天,所以新开一个对话时她不知道今天早些时候在别的对话里做过什么。

数据来源是 conversations/<今天>.jsonl —— 这个流水本来就是所有会话共同汇总的(原来只用来生成日记,从没读回过会话)。

配置 memoryRecentDiaryDays / memoryRecentChatChars 控制,设 0 关闭。私密日记默认不进(memoryRecentDiaryIncludePrivate)。


目的机制:「主动想起」而不是「主动发消息」

四个驱动维度,各 0~1,8 小时落一半:

维度 · 被什么推高

想弄明白 · 疑问句、看不懂的东西

在乎 · 累、难受、熬夜

想分享 · 有意思的发现、推荐

想搞懂 · 学会了、跑通了、踩坑了

再加上从记忆里长出来的「没闭合的事」,渲染成很短的一段(默认 ≤ 320 字,最多 3 条)。里面明确写着「这不是任务清单,不需要每轮都提」。

心情会推着它走:担心/心疼 → 在乎 涨,开心/甜蜜 → 想分享 涨。不是每轮往上加,而是让驱动向情绪推出的目标靠拢(每轮 15%)—— 累加式写法几十轮后会把所有驱动顶到 1,等于没有状态;靠拢则天然有收敛点。

说出去了才算完:渲染出去的目标会被记下来,下一轮看她有没有真的说出口。说到了 → 驱动 ×0.45;超过 6 小时没说到 → ×0.85,慢慢淡了。只判一次,判完移出列表。

本地 DSH 没有推送渠道(你不打开就没有回合),所以「主动」只能是主动想起。真正主动发消息属于 QQ 那边的事。

心情

心情是全局一份(agents/mood/global.json),不按工作区拆。

2026-10-31 改的。原来每个工作区一份,实测四份分数从 71 到 99.95 —— 同一个人换个目录就换一份心情,会出现「刚在 A 里说累、切到 B 又满血」。心情是「这个人现在的状态」,不是「这个文件夹的状态」。
迁移是自动的:global.json 不存在时,从旧文件里挑记录最完整的那份搬过来,旧文件保留不删。

七情的满格是 20 不是 100:判定单次上限 ±10,又被 90 分钟半衰期往回磨,实测一整天峰值只到 13。所以设置页和日记都按 20 当 100% 折算 —— 「踏实 65%」是很踏实,不是刚过一半。

判定用哪个模型:每回合判一次,是全插件最贵的一处固定开销。两条路:

  • 留空 → 跟会话模型(贵,但不用配)
  • moodJudgeProvider: siliconflow + moodJudgeModel: Qwen/Qwen2.5-7B-Instruct → 走硅基流动的小模型

实测(同一批 6 个样本,方向判断 6/6 一致):输入 token 2015 → 1182,延迟 1.5~2s,成本降到零头。提示词也一起压过(2875 → 1646 字),压

…(truncated)

以上内容来自 npm 上的 README,我们只做格式化,不改内容。

版本

npm 上共发布:12,最后更新 2026-11-06

链接

GitHub · Homepage · npm

同类插件