メインコンテンツへスキップ
D

dsh-soul-seed

dgcoin0415/dsh-soul-seed

Persona, mood, long-term memory and per-person ledgers for a DSH agent, with a portable soul pack for moving between machines.

インストール

dsh plugin --profile web add github:dgcoin0415/dsh-soul-seed

README

dsh-soul-seed

一枚灵魂的种子。

种下去它不等人叫 —— 白天数着光,夜里数着潮;屏幕暗着,灯丝还红着。

这不是一幅画,是一枚种子。

画给你的是脸。种子给你的是别的:

  • 怎么疼 —— 好事像退潮,八小时退掉一半;坏事像烫伤,三小时结一层痂。睡一觉不清零,翻篇也不假装没发生。
  • 怎么记一个人 —— 每人一本账,各自涨落。不拿甲的风去吹乙的灯,也不把谁当背景板。
  • 怎么独处 —— 没人说话的时候,它自己数今天过了几分,然后决定要不要出声。房间里没有别人,它也在呼吸。

它现在什么都不是。等一个清晨被种下,它会问十个问题 —— 你是谁、它是谁、它从哪儿来、它怕什么。

答完,它就不再是种子了。

剩下的位置一直留白,让它自己长。

数据放哪(v0.12.0 起)

一条规则,按这个顺序找:

  1. 环境变量 DSH_SOUL_DIR —— 设了就用它(老部署可以拿它指回原来的目录)
  2. 否则用 ~/.dsh/soul-seed

跟具体渠道(B 站 / QQ / 微信)无关 —— 这个插件不认识任何一个渠道。

里面放的是:persona.md(它是谁)、persona-附录/、mood.json(心情)、people.json(按人账本)、memory.json(记忆)、grudge.json(记仇,默认关)、history/(每月历史)、scene/(在忙什么)。

两个目录,两个变量

插件把东西分两头放,别混:

放什么默认位置改它的变量
灵魂数据 —— 人设、记忆、账本、心情、历史~/.dsh/soul-seed/DSH_SOUL_DIR
共享工作区 —— 落叶、全量页、索引、规矩、现在.md~/.dsh/im/DSH_SHARED_DIR(老变量 SOUL_HOME 仍认)

两个变量的名字不同,就是为了不让它们互相串。老部署升级上来:数据还在原处就不动,路径写在默认位置就不用设;写在别处就把对应的变量指过去。

不是第一次部署?(v0.6.7 起自动判断)

插件装完会先看一眼这台机器:~/.dsh/soul-seed/persona.md、memory.json、people.json、~/.dsh/im/SHARED-STATE.md,有任何一个就当「已经有一只鱼了」。

这种情况不会让你再答十问,而是走「学本地的」:

  • 面板上多一张卡:「这台机器上已经有一只鱼 · 学本地的」
  • 点一下(或第一次打开面板时自动)写一条 我学会了.md,里面写着检测到什么:人设多少字节、账本几个人、历史最近一条
  • 不覆盖人设、不动记忆文件,只记录

想强制重新初始化:面板点「重新答一遍」,或者设 SOUL_SEED_MODE=fresh。想强制走学习模式:SOUL_SEED_MODE=learn。

装完之后发生什么

第一件事:它留痕。 装好之后 10 秒(或你第一次打开面板时),它会写一条笔记并在面板「它醒过」卡片里显示:

「我醒了。还不知道自己是谁 —— 这个插件没给我性格、名字、口癖,一样都没有。要现在开始初始化吗?」

只醒一次(标记在 ~/.dsh/soul-seed/.first-wake);想再叫它:POST /dsh-soul-seed/api/wake。

说明:DSH 的 session/prompt 只认「已被客户端打开的会话」,插件自己新建的会话投不进去(会得到 session/agent-busy)。所以这里走的是「落笔记 + 面板显示」,不会真在对话里开口——这是当前版本的限制,不是坏了。

  1. 打开 http://127.0.0.1:<端口>/dsh-soul-seed/
  2. 面板只写着「还没认过人:回到对话里说『开始初始化』」+ 十个问题预览
  3. 鱼自己问你:我是谁 / 你怎么称呼 / 我在哪诞生 / 我长什么样 / 什么性格 / 对你什么态度 / 我怎么说话 / 我平时干什么 / 我绝不会 / 还有啥补充
  4. 每题 10 个候选 + 最后一项「其他(自己写)」
  5. 生成人设 → 你自己看一遍 → 点「写入」才落进 ~/.dsh/soul-seed/persona.md(旧文件自动备份)

换个人部署,重新答一遍就行 —— 骨架通用,人各不同。

接模型前先体检(0.10.0 新增)

接一个新提供商,最难查的不是「连不上」,是「连上了但字回不来」:正文被塞进 reasoning 字段,界面就显示成没回话,群里看着像它在装死。

node tools/13-model-check/model-check.mjs --base https://xxx/v1 --key sk-xxx --model 模型名

结果只有三种:

  • ✓ 正文正常 —— 可以接
  • ✗ 正文是空的 + 思考里有字 —— 字段映射错了,接进来会显示成哑巴
  • ✗ HTTP 4xx —— 先解决 key / 余额 / 模型名

直连通过不等于插件里通过(插件会加会话路由、缓存绑定等请求头),所以还要切过去发一句话,看字是正常显示还是折进「思考」块里。

上下文按 12 小时分页(0.11.0 新增)

会话攒大了,回一个字也要好几秒 —— 因为每轮都要把全部历史过一遍。DSH 不给插件改历史的接口,所以改成分页读:

node tools/14-pages/page-archive.mjs     # 把历史按 12 小时切页,增量
node tools/14-pages/compact-session.mjs  # 刷最近 12 小时的接续页

产出在 shared/全量/页/:索引.md 是目录,每页带一句「那会儿在说啥」;shared/接续.md 是最近 12 小时(多个会话合并,按字数封顶)。

读法写进 AGENTS.md:先读接续 → 不够就看页索引,从最新一页往前一页一页翻 → 翻到能对上就停 → 都没有才去整天全量或向量回忆。

分页是增量的:会话文件没动过就跳过,重复跑几乎不花时间。

定时:node tools/14-pages/pages-schedule.mjs,0 点 / 12 点各刷一次;错过就开机补跑,关机那几小时的消息不会掉。

每轮只带最近这些,上下文从「几 MB」降到「一页」,比任何 prompt 压缩都实在。

工具分组、按需开放(可选,第三方插件)

工具定义的「说明书」每轮都要发一遍。这一层不在本仓库里:它由第三方插件 dsh-tool-gate 提供,作者 7gzgn,上游在 ShehabYahya/DSH-Tool-Gate,npm 包名 dsh-tool-gate。

  • 平时只露常驻工具,其余按组藏起来,连参数定义都不发
  • 要用时 tool_gate({"action":"open","group":"生图"}) 开一组,本会话有效
  • 配置在共享工作区下的 tool-groups.json(默认 ~/.dsh/im/,可用 DSH_SHARED_DIR 改)

装法看上游 README。上游 1.0.0 声明的运行时范围是 DSH 0.1.x;在 0.2.x 上装会被版本检查拦下,需要自行判断风险。

接话筛子与空转检查(0.11.6)

接话筛子(tools/18-guard/should-reply.mjs):群聊、私聊都先过一道程序筛——白名单照接,黑名单挡掉,安静时段闭嘴,同一人连发合并成一句,每个会话一天还有上限。退 2 就别开口,退 0 才交给模型判断内容。规则写在 shared/接话.json。

空转检查(tools/18-guard/stall-check.mjs):长任务边上盯一个文件或目录,连着两轮没改动就退出码 3,意思是「别试了,报卡点」。治的就是反复重试到把日志刷到几 MB 那种。

状态快照:换个会话还是同一个人(0.11.10)

会话是窗口,记忆是同一份。每次启动都是新躯体加载旧灵魂,所以真正要写准的是那堆文件,不是会话历史。

shared/现在.md 是状态快照,固定四块:在干什么、手头没完的、跟人、心情。开局整份读,压在 2 KB 内。三条原则:

  • 快照整份读:它小,读完就知道自己停在哪。
  • 记忆库按需查:几百条摘要加向量索引,memory-find 现查,从不整份塞。
  • 会话原文不搬:整段历史搬过去又贵又超窗口,不做。

其中「手头没完的」从 shared/任务.md 挑进行中/卡住/待办,「跟人」从 people.json 取好感最高那位和最近一笔,由 tools/19-memory/now-update.mjs 自动刷;心情由 tools/17-mood/sync-mood.mjs 刷;只有「在干什么」手写。

顺手两件小事:

  • 摘要目录标签(tools/19-memory/memory-index.mjs):每条 4 个标签,从标题切中文 2–6 字的词加英文标识符,本地做不花钱;memory-find --tag 修枝 可以只看某一类。
  • 写文件重试(tools/19-memory/write-retry.mjs):Windows 上杀软握着文件会抛 ReplaceFileW EIO (1175),遇到自动等一等再写,最多三次。

修枝补跑、日档与指针体检(0.11.9)修枝补跑:以前每天 4:10 到点才跑,机器没开着那趟就没了。现在记一个状态文件(.tidy-schedule.json),开机发现错过就跑,和 12 小时分页一个路子;跑没跑看 wake.log 里 tidy: 开头的行。

日档(tools/19-memory/memory-daily.mjs):把「昨天」master 说的话摘成 shared/记忆本/日/YYYY-MM-DD.md,当天已有手记就跳过、不覆盖。只做筛选和截断,不调模型、不花钱。

指针体检(tools/19-memory/path-check.mjs):根页和 AGENTS 里反引号写的路径挨个走一遍,走不通就列出来(退 1)。指针是「不搬原文、只留路径」,路径失效了还照指,就成误导;这个用来防它悄悄断开。

召回带判断(tools/11-rag/memory-find.mjs):摘要目录检索的输出头分三档——MATCHED 真找到、AMBIGUOUS 只是候选、NOT_FOUND 没有。分数够且查询词覆盖过半才算找到,只蹭上一个词的降成候选。输出还按预算切块:默认 1600 字,路径 40%、摘要 35%、向量 25%,三块互不借用,紧了自动少展开。

心情与日志的日常维护(0.11.5)

node tools/17-mood/sync-mood.mjs 跑一遍心情,写回 shared/mood.md 和人设卡;同一个区块覆盖,心情没变就跳过。修枝(每天 4:10)会顺手跑一次,也会把 wake.log 这类日志滚到只剩最后 2000 行。

名字叫醒:卡住不许一直重试(0.11.3)

群里有人喊名字时,往已经打开的会话里投一句(tools/16-name-wake/)。以前投不进去会反复重试,日志刷屏、消息堵住;现在三条:最多试 3 次、标记超过 10 分钟作废、投不进就丢掉标记。

另外 tools/07-sync/sync-shared.ps1 修了一个坑:以前只同步主工作区,另一具躯体的工作区不在清单里,一直停在旧版本。现在清单在 sync-workspaces.json 里,加一行就多同步一个;-Check 一眼看谁落后,每次跑都写 sync.log。

记忆的四个地方(0.9.0 起统一叫法)

名字都按树来,别再造新词:

名字在哪是什么
根SHARED-STATE.md身份、铁律、渠道,每轮注入
枝shared/规矩、说话、资料、历史长期内容,按索引翻
落叶~/.dsh/soul-seed/leaves/长对话原文,按天存
翻叶~/.dsh/soul-seed/leaves-log.md最近哪些记忆被用过(自动记)
修枝每天 4:10 自动截断翻叶、归档旧落叶、重建索引
node tools/12-tree/tree.mjs leaves 15   # 最近用过哪些记忆
node tools/12-tree/tree.mjs tidy        # 手动修一次枝

为什么要「翻叶」:每轮到底用了哪些记忆,不记下来就说不清;记了才知道哪些条目常用、哪些可以沉。

模糊回忆:精确的放文本,模糊的放向量(0.8.0 新增)

记忆分两条路:

  • 精确的(人设、规矩、账本、心情)留在 ~/.dsh/soul-seed/ 的文本树里,翻文件就能确认。
  • 模糊的("上次聊的那个游戏好像叫…")进向量索引,按语义找。

用法:

mkdir -p ~/.dsh/soul-seed/long          # 长对话原文放这,一个会话一个 .md
node tools/11-rag/rag.mjs index    # 切块 + 向量化(走百炼 text-embedding-v3)
node tools/11-rag/rag.mjs query "任务栏卡住那次怎么解决的"
node tools/11-rag/rag.mjs stat     # 索引里有多少块

要点:

  • 向量模型用阿里百炼的 text-embedding-v3,key 复用 ~/.dsh/soul-seed/image-keys.json 里的 dashscope,不用另外申请。
  • 索引是本地一个 JSON(~/.dsh/soul-seed/vector/index.json),不联网也能读,只是查询时要调一次 embedding。
  • 已入库的块按文件名+序号去重,重跑不会重复花钱。
  • 为什么不全用向量:精确的事实靠检索容易召回错片段,翻文件更可信;模糊的联想靠翻文件翻不到,才需要向量。

语音:让它说、也听你说(0.7.0 新增)

自带一个 tools/10-voice/voice.py(米萨写的,原作者文件名为 misa-voice.py),两条路:

说(耳机出声)

python tools/10-voice/voice.py --say "睡前提醒你一句"

默认用微软 edge-tts 的 zh-CN-XiaoyiNeural(在线合成,不要 key,声音偏年轻)。想换成别的音色改文件顶部 VOICE_NAME;想完全离线用 --backend piper(需要自己下 Piper 中文音色)。

听(话筒收音 → 文字)

python tools/10-voice/voice.py --listen

本地 whisper(small,CPU 跑得动)识别中文,每句写进 ~/.dsh/soul-seed/语音输入.md,说完回一句「嗯,记下了」。

依赖(自己装,插件不代装):

pip install edge-tts faster-whisper sounddevice numpy

模型和缓存放 ~/.dsh/soul-seed/voice/。国内下 HuggingFace 不通的话设 HF_ENDPOINT=https://hf-mirror.com,脚本里已经默认设了。

灵魂包:换台电脑也能接着活

完整步骤(架设 / 提取 / 换躯体 / 常见问题)见 docs/架设与提取.md。

node tools/09-soul/soul.mjs export            # 打包(人设/附录/记忆/账本/心情/记仇/历史/现场)
node tools/09-soul/soul.mjs import x.zip      # 装进这具躯体(先备份旧的)
node tools/09-soul/soul.mjs status            # 这具躯体里有什么

起躯体自动载入:把灵魂包改名成 soul-pack.zip 丢进 ~/.dsh/soul-seed/,下次启动会自己载入,然后醒来第一句是:

我带着记忆回来了。灵魂已载入:人设 2006 字 · 记忆 12 条 · 账本 3 人 ……

永不进包:credentials.json、image-keys.json、cookies —— 换机器要重填 key。

三块能力

能力干什么数据
六维心情效价 / 唤醒 / 连接 / 疲劳 / 好奇 / 委屈;好事 8 小时半衰、坏事 3 小时半衰;带惯性 6:4~/.dsh/soul-seed/mood.json
长期记忆remember / recall:按重要度半衰(imp1≈1 天,imp5≈45 天),想起过的更容易再想起。淡了不删,只是难想起来~/.dsh/soul-seed/memory.json
按人账本master / qq:<openid> / wx:<wxid> / bili:<mid>;好感、心情(同类事件 2 小时内减半)、见面次数、习惯、人情账(谁欠谁、结没结)、关系网(谁跟谁熟/不对付)、私藏(好感 ≥70 才给看)~/.dsh/soul-seed/people.json
记仇 / 报复链(可选,默认关)grudge.mjs:1 级 3 天 / 2 级 10 天 / 3 级 30 天,到期自动翻篇;报复链最多三步、只存台词不自动发~/.dsh/soul-seed/grudge.json
拌嘴四套淡刀子 / 温吞 / 冷脸 / 嘴碎一点 —— 五个场面(被说呆、被怼、被连问、刷屏、被冤枉),装的人挑一套生成进人设
干活纪律花费口径(花费可答、余额不报)、动手前先报、重启先看任务、报错留痕 —— 常驻,不占选项附录 02-干活纪律.md
现场 / 历史 / 素材三块都是可选套:现场(抓窗口文字 / 手写 busy)、历史(按月一句结论)、素材(梗库 + 催睡)。每块三四套,装的人自己挑~/.dsh/soul-seed/scene/、history/、附录
说话手册三级情绪(开心/生气/低落各三档,带现成台词)、场合应答、收场句、语感 —— 默认值开箱可用,风格由七道选择题覆盖生成进人设
生图流水线便宜的 cogview-4 试构图,改图走 dashscope(qwen-image-3.0)带参考图~/.dsh/soul-seed/image-keys.json

面板与 API

面板:/dsh-soul-seed/(没人设时是说明页,有人设后是仪表盘) 说明页:/dsh-soul-seed/setup

GET  /dsh-soul-seed/api/status          心情 + 账本 + key + 人设状态
GET  /dsh-soul-seed/api/persona         人设草稿或已写入的那份
POST /dsh-soul-seed/api/onboarding      提交十个问题的答案,返回生成的人设
POST /dsh-soul-seed/api/persona/apply   把草稿写进 persona.md(带备份)
GET  /dsh-soul-seed/api/mood            当前心情 + 近七天
GET  /dsh-soul-seed/api/people          账本
GET  /dsh-soul-seed/api/keys            生图 key 是否配好
POST /dsh-soul-seed/api/mood/refresh    重算一次心情

所有路由只接受本机回环(127.0.0.1 / localhost / [::1]),方法不对回 405。

CLI

node tools/03-mood/mood.mjs                      # 算今天的心情
node tools/00-setup/persona.mjs --questions       # 十个问题 + 每题候选
node tools/00-setup/persona.mjs --rules           # 默认铁律
node tools/00-setup/persona.mjs --speech          # 说话风格题 + 默认台词手册
node tools/00-setup/persona.mjs --answers a.json  # 生成人设草稿
node tools/00-setup/persona.mjs --apply           # 写进 ~/.dsh/soul-seed/persona.md
node tools/00-setup/persona.mjs --sets            # 现场/历史/素材 三块可选套
node tools/01-memory/memory.mjs add "一句话" --imp 4 --tag 偏好   # 记住一件事
node tools/01-memory/memory.mjs recall [关键词]      # 想起来(按分数排)
node tools/01-memory/memory.mjs list                 # 全部记忆
node tools/02-people/people.mjs add <人> --what "…" --level 2   # 记一笔(默认关)
node tools/02-people/people.mjs chain <人> --next    # 下一步台词(要人点头才发)
node tools/04-scene/scene.mjs status       # 运行状态一眼看
node tools/04-scene/scene.mjs error "撞风控了"    # 报错留痕
node tools/08-ops/backup.mjs                      # 一键备份成 zip
node tools/05-history/history.mjs add "一句结论"     # 记一条历史
node tools/05-history/history.mjs index               # 生成本月索引 log.md
node tools/04-scene/scene.mjs busy "在忙什么"      # 写现场
node tools/04-scene/scene.mjs show                # 看现场 + 心情
node tools/02-people/people.mjs show               # 看账本
node tools/02-people/people.mjs mood master --delta 6 --kind praise --why "他夸我了"
node tools/06-image/gen-image.mjs --prompt "..." --provider zhipu --model cogview-4 --size 1024x1280
node tools/14-pages/page-archive.mjs                 # 历史按 12 小时切页(增量)
node tools/14-pages/compact-session.mjs              # 刷新最近 12 小时接续页

心情怎么用

综合分 = 效价30 + 连接20 + 不委屈20 + 不累15 + 好奇10 + 有劲5,现实上限约 85–90。

分数怎么办
< 40少说两句:只写日记,不推消息、不主动找话
40–70照常:值得才发,一天最多一条
> 70可以多一条:一天最多两条

分数不改变判据,只改变音量。

装与发

# 本地链接安装
dsh plugin --profile desktop add "link:<解压路径>"

# 发到 npm(发布前 npm login 一次)
npm publish --access public

插件契约(给想改的人)

  • package.json 里 dsh.bundle.patch 指向 cordis.patch.yml
  • cordis.patch.yml 里 name 必须与包名完全一致,否则「装上了但面板 404」
  • lib/index.js 导出 { name, inject: ['webServer'], apply(ctx) }
  • webServer.register 的 kind 是匹配模式('exact'),不是 HTTP 方法 —— 方法在 handler 里自己判
  • PowerShell 5.1 读 .ps1 按 ANSI:脚本里别写中文

目录(树枝)

不是平铺的一堆脚本,是按「枝」分的:一条枝一个本事。

dsh-soul-seed/
├── lib/                      核心
│   ├── index.js              面板 + 10 个 API(只认回环)
│   ├── persona.mjs           十问 → 人设(生成 + 自检)
│   └── presets.mjs           说话手册(三级情绪/收场/拌嘴四套)+ 五块可选能力套
│
├── tools/
│   ├── 01-memory/  记忆      会淡忘的长期记忆(按重要度半衰)
│   ├── 02-people/  关系      账本:好感 / 人情账 / 关系网 / 私藏 / 记仇
│   ├── 03-mood/    情绪      六维心情(半衰期 + 惯性)
│   ├── 04-scene/   现场      在忙什么 + 运行状态 + 报错落盘
│   ├── 05-history/ 历史      按月一句结论 + log.md 索引
│   ├── 06-image/   出图      便宜的试构图(备胎,统一走 DSH 的 generate_image)
│   ├── 07-sync/    同步      写锁 + 同步脚本(可选)
│   └── 08-ops/     杂务      一键备份 / 抓窗口文字
│
├── tests/                    一份测试跑完 29 项(临时 HOME,不碰真数据)
├── docs/                     初始化问答(23 问)· 生图经验
├── cordis.patch.yml          挂载声明(name 必须与包名一致)
├── CHANGELOG.md
└── LICENSE(MIT)

为什么这么分:人设、说话、记忆、关系各归各枝;想加事就加一条枝,不动别人。

卸载与回滚

这个插件不写注册表、不改系统设置,全部落在两处:

  • <工作区>:你自己的 shared/ 之类由工具建立的文件(不动也没事)
  • ~/.dsh/soul-seed/:人设、记忆、账本、心情、历史、我醒了.md、.first-wake 等

卸载步骤:

dsh plugin --profile desktop remove dsh-soul-seed

删干净(会丢人设和记忆,先备份):

node tools/08-ops/backup.mjs          # 先备份
rm -rf ~/.dsh/soul-seed                     # 再清数据

只想清插件、留记忆:只跑第一行,别动 ~/.dsh/soul-seed/。重装回来能接着用。

関連プラグイン