DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

bychance6600 /

bychance6600/dsh-fish-river

Verified

养一条会自己长大的鱼:没有人格设置,人格只能通过事件流长出来。DSH 插件 · 三层记忆 · QQ / B站私信 / 抖音 三个真实渠道。

★ 1 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@29d731e8

dsh-fish-river · 大肥鱼(合并版)

test license node dependencies tests

并非角色扮演,而是真实诞生——一个一直在变的她。现在她也会走出房间,用自己的名字说话。

本项目的人格并非角色扮演的形式,没有人设配置菜单。默认从空白开始养起,但可导入你已经养过的大肥鱼人设提示词,继承大肥鱼的人设并在本插件环境内进行成长,具体请看本插件「数据」页面介绍。

所有认知自己思考、理解、固化,从而形成人格。

大肥鱼

示例图由 AI 生成(基于作者提供的大肥鱼原图),随仓库以 MIT 授权。

当前版本 0.1.3(2026-11-02)。她的人格仍然只能长(事件流 + 派生状态 + 四阶段 + 立场,没有任何人格设置项),而她现在可以用同一个自己出现在 QQ(SnowLuma)、B 站私信(设置页直接扫码登录,不用手贴 cookie)与 抖音(可选独立子包 dsh-fish-river-douyin)里说话,并记住她在那里遇到的人。0.1.1 起支持「她不想说时你以自己的名义回一句」,0.1.3 起设置页可以填桥的路径、一键起桥、逐个渠道「测一下通不通」。

来源:本仓库是 ssamurias/dsh-fish(0.8.5)与 Britneycode/dsh-fish-soul(0.12.0)的整合版,两者都是 MIT。上游各自的版本历史与更新说明留在它们自己的仓库里,这里不再复制——想知道大肥鱼 0.8.x 是怎么一路走来的,去上游看。代码来源、第三方与致谢见 NOTICE.md。

这个整合是什么

一句话:把「只会长的她」和「能在外面说话的她」,接成同一个人。

一、整合的是哪两个项目

ssamurias/dsh-fish 0.8.5 Britneycode/dsh-fish-soul 0.12.0
它原本是什么 一条白纸养起的鱼:人格只能从事件里长出来,会自己刷 B 站,没有任何人设配置 一个能替你说话的 QQ / B 站 / 抖音 小号:人格可配、按人记忆、有作息和主动开口
立项前提 她是谁,不能由你指定 你能配出一个她

这两个前提本来是互斥的。所以这次不是「装在一起」,而是选一个当骨、一个当皮。

留下来的(骨):事件流、派生状态、四个阶段与瓶颈、立场、拒绝与疤、记忆的显著性与遗忘、观测台、身份文档、刷 B 站、工具收窄。

搬过来的(皮):每聊一句的流水、按人记忆的字段设计、出站筛选、三条渠道的适配(SnowLuma / OneBot 11 的 QQ、B 站私信、抖音的 CDP)、备份与版本自检。

明确没搬的:

没搬 为什么
人格模板、角色卡编辑 那是「你能配出一个她」,与立项前提直接冲突。只保留一条一次性的导入通道:继承别人已经养出来的样子,不带成长结果,不进配置。
好感度 / 亲密度数字 违反 no-relational-score——她不给人打分,这是四条黑名单之一。
BM25 + 向量混合检索 向量要 embedding 服务,等于破掉零依赖和「数据只在本地」。现在的召回是关键词 + 显著性 + 时间衰减。
gateway.js(约 1000 行) 那是单角色聊天机器人的骨架:会话、轮次、人格拼装都在里面。与「她的事件」模型不兼容,搬过来会长出第二个大脑。

二、整合前是怎么想的

起点是四行约定(2026-11-02):

新插件保留
人格从哪来 只能长出来,禁止直接配
记忆模型 单一事件流 + 派生状态,追加式
对外说话去哪 DSH 会话 + QQ / B 站 / 抖音 三个真实渠道
面向 「观察一个存在」+「替我回消息」

中途有一个关键修正:接多平台不只是为了替主人回消息,而是——

不想让最开始的她那样,性格只来自 B 站和 DSH 会话;等性格基底差不多的时候,可以从 QQ 群、抖音这些地方继续补齐性格。

这句话把「成长」劈成了两层,也是整个合并版最核心的改动:

  • 立宪层(她是谁:价值观、对人的基本态度、怕什么要什么)——从第一天起生效,来源是主人、她自己刷的 B 站、她做的梦。
  • 社会层(她怎么处世:场合感、分寸、梗与黑话、被冒犯的反应)——只在基底成型后、且她自己同意才开,来源是群聊里的陌生人;只能补充,不能推翻已立的判断,权重上限 30%,永不进 beliefs。

想清楚这件事时,同时冒出来五个会出事的地方,最后都落成了代码:

  1. 记忆会漏:一条事件流、全局召回,群里陌生人 @ 她一句,她能翻出私聊内容。→ 事件加 person / visibility / channel / origin,进出站都过可见性闸门与泄漏检测。
  2. 群聊会污染成长:几千条「哈哈哈」会把四阶段淹没。→ 分开「她的事件」(稀疏、只追加)与「聊天流水」(每人每群最近 30 句、可覆盖、不进派生)。
  3. 「禁止配」和「群管理」会打架:什么算人格、什么算环境?→ 判定标准一句话:这个配置能不能让她对同一个人说出不一样的话。能,就是人格(禁区);只改在哪、何时、多频繁,就是环境(可配)。
  4. 陌生人撞上四个阶段:前两阶段的她谨慎、会拒绝,凭什么回陌生群友?→ 陌生人到第 3 个阶段才搭理;渠道默认全关;群白名单之外,她连听都不听。
  5. 抖音最脆:要常开 Windows、要真 Chromium、选择器随时会变。→ 拆成独立子包,靠「装不装」体现可选,主包一个字节都不带。

三条原则是在这些取舍里慢慢磨出来的,现在写在 AGENTS.md 里当硬约束:

  • 她是她,权限是权限。 「她是谁」只能长;「她能碰什么」必须可配。
  • 历史只能追加。 今天改了规则,不追溯昨天的事——包括开闸那天,不补算之前的群聊。
  • 她没有看见,不是被劝住。 重塑人格的话、私聊原文,是根本不进她的视野,不是进了视野再拒绝。

三、整合后实现了什么

内核

  • 单一事件流 events.jsonl(只追加),state.json 是派生品,随时可重算
  • 四个阶段(信任 → 自主 → 主动 → 整合)与各自的瓶颈;四阶段走完才开始有立场
  • 四条黑名单是代码级硬禁:不撒娇卖萌、没有好感度数字、不能读自己的数值与设定、不能调工具改自己

三层记忆,只有一层是「她」

层 存在哪 能不能改
成长 events.jsonl 不能,只能被后来的事推翻
关系 people.json,每人一段 ≤150 字的印象 能改、能清空,但改了要说得出为什么
流水 journal.json,每个会话最近 30 句 滚动淘汰,不进派生

两条成长通道

  • 立宪层:默认通道,谁都能影响(主人、她自己、她看见的)
  • 社会层:四道闸全过(阶段 ≥3、立场 ≥6、30 天没整条被推翻、活满 14 天且稳定度在健康带)且她调用 social_open 自己按下才开;开之前,身份文档里一个字都不提

四个地方,同一个她

渠道 怎么接 状态
DSH 会话 宿主原生 已验证
QQ 设置页填桥的路径 + ws:// 地址,一键起桥(本插件不带 QQ 协议) 适配器与单元测试完成,未连真号
B 站私信 设置页扫码登录(passport 官方接口),cookie 只写 owner.json 0600 适配器与单元测试完成,未连真号
抖音 可选子包 dsh-fish-river-douyin,走本机 Chromium 的 CDP 适配器与单元测试完成,未连真号

替主人回消息:她不想说的时候,你可以在观测台用自己的名义回一句。那句话只发出去、只记一行谁说的流水,不进她的记忆——她的记忆里多一句她没说过的话,比少回一条严重得多。

知道一些事,但不当成自己:可以导入 SillyTavern 的角色卡设定(世界书),按关键词命中后作为「你知道的事」写进提示词。是事实清单,不是人格设定。

工程上的:零运行时依赖(三个依赖字段永远是 {})、1237 个测试、CI 三档(Ubuntu Node 22 / 24、Windows Node 22)、数据全在 ~/.dsh-fish-river/、可整目录搬走。

四、现在到什么程度

  • 已跑通:DSH 会话内的完整闭环(身份文档、工具面收窄、成长推进、观测台、渠道页设置)
  • 代码完成但没连过真号:QQ(要你自己的 SnowLuma / OneBot 11 桥)、B 站私信、抖音(选择器可能要按你的页面校准)
  • 明确没做:老数据迁移(新起一局,没有旧库要迁)、多人格 / 多角色、云同步

这个版本多了什么

一句话:她还是只能长,但她现在能走出去说话,并且记得她在外面遇到的人。

三层记忆,只有一层是「她」

层 存在哪 能不能改 谁写
成长(她是谁) events.jsonl,只追加 不能改,只能被后来的事推翻 系统(主人说的话、她自己说的、她看见的)
关系(她记得谁) people.json,一人一条 可以被她自己覆盖,每人印象 ≤150 字 她自己
流水(最近说了什么) journal.json,每个会话最近 30 句 随时淘汰 系统

后两层都是派生物:删掉不会让她失忆,只会让她「想不起刚才那句」。流水不进事件流——群里刷屏不该改变她是谁。

四个地方,同一个她

渠道 怎么接 默认
DSH 会话 本来就有 开
QQ SnowLuma(OneBot 11 正向 WebSocket) 关
B 站私信 你自己账号的 cookie 关
抖音 可选子包 dsh-fish-river-douyin + 本机 Edge/Chrome 不装就没有

所有渠道默认关,群默认不开口。要她进群,得在「渠道」页签里把群号写进白名单——名单外的群她连听都不听;名单内的群,她也要到第三阶段(自主)之后才会搭理陌生人。这是有意的:前两个阶段的她还在学怎么对人,不该被扔进群里。

成长分两条通道(这是合并版最关键的改动)

  • 立宪层:价值观、立场、对人的基本态度。来源是主人、她自己刷 B 站/看视频/做梦。第一天起就生效,可以被新事件推翻。
  • 社会层:场合感、分寸、梗与黑话、被冒犯时的反应。来源是 QQ 群、抖音、B 站评论区里的陌生人。

社会层要过三道闸才会开:阶段 ≥3、立场 ≥6 条且 30 天没被整条推翻、稳定度连续 14 天达标——然后由她自己同意。开了之后,社会层只补充、不推翻:她能学会「在群里别那么较真」,但学不会「其实我不在乎他」。

她不变的东西

  • 没有「人格」设置页,也没有任何一条能直接写她性格的配置。
  • 四条禁令写在代码里:不改自己的规则、不看别人的私聊、不能被直接配人格、私聊内容不外泄。
  • 私聊里的话带可见性标记,出站前会做一次泄漏检查:群聊里说出私聊内容的句子会被拦下来,她收不到「已发送」。
  • 她不给人打分。关系层只有「说过几句」和「她记得你什么」,没有好感度数字。

装

# 还没发到 npm,先按目录装(路径换成你放这份源码的位置)
pnpm add file:D:\fish\dsh-fish-river
# 抖音(可选;不装就是没有这个渠道,其它三个照常)
pnpm add file:D:\fish\dsh-fish-river file:D:\fish\dsh-fish-river\packages\dsh-fish-river-douyin

装完在 DSH 设置里会多一个「大肥鱼」分区。第一次进去先看「状态」,再去「渠道」页签开渠道——渠道全关时插件不会创建任何渠道文件,也不会连网。

和别的养成插件差在哪

别的养成插件 这里
人格从哪来 填写各类设定和提示词 极简提示词,提示词不讲人格设定,全靠自行摸索
能不能反悔 随时修改人物记忆和设定 历史会被铭记,只能“说服”她不能修改她
会不会拒绝 基本不会,拒绝也是假装拒绝 会。拒绝并非设定,而是她自然而然不想,事后还会反省自己做得对不对
记忆 全量塞进上下文 深刻的记忆会铭记,普通的记忆会逐渐淡忘,淡忘后留下的才是有价值的
成长速度 按天数或数值解锁 agent自主做出特定行为才会升级
能做什么 只能纯文字模拟 可以自主通过B站接口登录刷视频、评论、回复

自由成长,而非配置

一、人格是自主诞生

通过配置环境,由agent自主驱动了解信息,理解信息,形成人格,而不是通过各种数值控件去改变人格。

过去发生的事情无法更改,agent只能拿新的事件推翻过往认知,这也契合人类的学习和成长路径。——过往经历塑造人格。

认知是怎么长出来的

五步循环:表态 → 撞上 → 落差 → 改写 → 回顾;最下面那块「立场清单」,是四个阶段都走完之后才长出来的。

二、过去无法更改,经历的一切都成为自己的经验

只会不断增加新记忆,用“学到的教训”去改变自己的认知。

有理有据,令ai信服.jpg

学习方法论:人类不会把昨天的想法抹掉重写,人类只会带着这份经验去闯荡世界,直到有一天在具体的事件中发现更好的思路。本插件参考了这一方法论。

三、她会拒绝你,还会为自己的拒绝反省

她不是有求必应的。觉得不想做、不愿意,就可能拒绝。

拒绝行为发生后。会根据主人的反应,进入到不同的反馈链。如果主人给了负面回馈,她会把这件事记到小本本上,直到想通。如果主人给了正面回馈,她也会反思这次拒绝对不对。

四、记忆有重量

深刻的记忆会铭记,普通的记忆会逐渐淡忘,淡忘后还能记得的认知才是真正的人格。

被反复说起的、被自己验证过两次以上的认知会固化,普通认知只有一句话,超出上下文的不重要记忆被折叠,被模糊(不是完全消失,只是可能记不太清)。可以通过agent特定流程重新想起。

遗忘是怎么发生的

遗忘不是删除,是不再时刻记忆,记忆会按照重量进行压缩。

她也会偶尔记下你说过的一句原话(默认每 3 轮一句,设置页可关)。翻回忆时那句写成「他说:……」,是旁听,不是她的自述。回忆默认不限时间、一次 8 条,并按记忆的重量排序:越被反复提起、越被验证过的,越先被想起。

想知道她记得清不清楚,在设置 → 记忆里点「查一下」:它只读账本,列出哪两句话对不上、哪些快忘了、哪些快固定下来了。这份体检只给你看,不进她的界面,也不算分。

五、成长没有终局

四个阶段:信任 → 自主 → 主动 → 整合。四个阶段按天推进,但每个瓶颈要自己突破。

阶段 瓶颈
1 信任 至少写下一条自己的判断
2 自主 真的拒绝过两件事
3 主动 两次推翻自己之前的判断,形成新的认知
4 整合 回顾过去,有意识地发现自己的改变

按照自己节奏成长,没通过瓶颈就是没通过。

走完这四个阶段之后,她才开始产生立场——喜欢什么、绝不做哪几件事。同一件事说过不止一次、或者因此栽过一次,才会被算成一条。清单内部最多留 12 条,写进她身份里的最多 8 条。

整合阶段只是开始。随着你和 agent 沟通越来越多,可能会发生任何连开发者也意料不到的事。

六、刷B站功能

通过调用B站接口,使用登录cookie和本地ffmpeg实现刷视频、看画面、写评论、回复评论功能。

插件自定义调节各类参数,如刷B站时间段,每天能刷几次,每次刷几条视频,能发几次评论等等。

本插件选择将B站作为agent的外部世界接触面,agent看过的视频、评论,可能会影响人格的形成和发展。

cookie 存在哪、会不会外传:登录信息只落在本机 ~/.dsh-fish-river/owner.json,文件权限 0600(只有当前用户能读)。它只用来给 api.bilibili.com 发请求,不会上传到任何第三方服务器,仓库里也没有任何真实凭据(测试用的是假 cookie)。插件自己的运行日志会把凭据打码成 SESSDATA=*** 再写盘,这条有测试守着。

要注意的是备份文件里含 cookie:设置 → 大肥鱼 → 数据 → 备份成一份文件,会连 owner.json 一起装进去(否则换台机器就得重新登录)。所以那份 .json 等同于你的登录凭据,别随手发人;分享前先把里面的 owner.json 那一条删掉。

叫醒消息里不放「今天写日记」「想一想」这类祈使句(0.4.7)。做法常驻在她的身份文档里(「## 平常会做的事」),叫醒只给处境,最多加一句时机标记。原因很直接:一整段祈使句塞进唤醒消息,她会把它当任务清单逐条执行——fish-wake-2026-10-26-manual 那条会话里她的 reasoning 原话就是「需要做的是:说一句…然后写日记」。那不是醒来。

「让她去刷 B 站」按钮只在开发机上出现。判据是状态库根目录下有没有 .dev 文件:有则在状态卡多一个按钮,没有则正式版连节点都不生成。按钮走 POST /wake?focus=browse,绕过上网时段与当日额度(不绕过风控),用途是当场验证「她到底能不能刷」——不用等定时器、也不用把配额调大。要回到正式版界面,删掉 ~/.dsh-fish-river/.dev 即可(它进了重置的保留名单,重开一局不会自己消失)。

一天的几次叫醒不再是「一分钟一个」(0.8.0)。以前落点一过就每分钟问一次计划,额度没满就叫醒——第二次可能排在第一次之后一分钟,而且她正说着的话会被「叫醒」打断、处境被那一轮收走,真正醒来的那轮什么都看不到。现在:

  • 剩余次数按时段摊平,间隔不超过「剩余时长 / 剩余次数 + 1」,也不小于 45 分钟(配额快用完时自然拉长,不会挤在最后几分钟里连着发);
  • 叫醒前先等她手上这一轮说完再挂上「你不在的时候」那段,忙太久(15 分钟没等到轮次结束)才放弃等待;
  • 90 秒兜底从「到点硬清」改成「她还在忙就 30 秒后再看」——读字幕、看画面常超过 90 秒。

只看到标题和播放量(0.8.0):不带 cookie 建出来的客户端以前只在签名接口前取 buvid3,view / tags / reply 全程匿名,/x/web-interface/view 多半回 -412,读熔断再把同一轮剩下的请求一起关掉 60 秒。现在不签名的接口也会先补一个匿名 buvid3(失败后 10 分钟内不再重试,避免请求数翻倍)。

bili_speak 的说明跟着开关走(0.8.0):以前不管说话功能开没开,工具说明都写着「可以给你今天看过的某个视频留一条评论」;而关着的时候身份文档里没有「在网上说话」那一节——一边说能说、一边一句不提。现在关着时只留一句不带任何许诺的短说明,也不解释原因(说「开不了」等于告诉她有个开关在管着这件事)。

「看了什么」分三档,只有真看过画面的才算看过(0.8.1):以前日志里的「看了什么」列的是当天点开过的全部条目,可其中大多数她只读了标题、简介、热评和弹幕,一帧画面都没抽;你专门递给她看的视频反而不在里面。现在三个面(每日日志、历史记录、「你不在的时候」)共用 lib/whatseen.js 的同一份判断:

  • 看过画面 —— 调 bili_watch 抽了九帧拼图,日志的 ## 看了什么 只列这一档;
  • 翻到过 —— 刷站时读了文字素材,进 ## 翻到过什么,副标题写明「只有标题、简介、评论和弹幕——没有画面」;
  • 你递过来的 —— 只在对话里递了 BV 号,她还没看,单列一节。

评论功能的口径没变:这三档(加上自己主动去看的)都能评,递过来没看的仍然不能评。

接一只别人养过的大肥鱼(0.5.3)

默认是一张白纸。如果你手里有别人养的交接单,可以在设置页「数据」里点「粘贴交接单」,把那份设定接过来,她从那上面继续长。

不知道那份交接单该怎么写:点旁边的「下载交接单模板」,会存一份 大肥鱼-交接单.md 到本地。它不是给人照填的表格,是写给现在养着她的那个 AI 看的说明书——上半写明该怎么写(第二人称、只写已经稳定成型的样子)、不要写什么(不写好感度、不写剧情、不写「你总是很温柔」这种笼统画像),下半才是三节空表。把这份文件发给那个 AI,它填完,你再把填好的部分贴回「粘贴交接单」。

人设正文控制在 1200~1500 字。超过 2000 字插件会直接拒收、整份贴不进去(事件流的正文上限就是 2000);就算没超,写得太长也会把「判断、疤、立场」这些往后会长出来的段落挤掉——她的身份文档有 1800 字预算,人设占满就只剩一副骨架。1200~1500 字能留出生长空间。

会过来的三样:人设正文(成为「你是谁」那段)、她怎么称呼你、原本的工具白名单(与默认工具面取并集,不会挤掉她自己的工具)。

不会过来的:过了多少天(本插件按时间推进事件,非本插件原生的那一只要从第一天重新算事件)、长出了哪些判断(计数为 0;原提示词里规定的一些认知她依然记得,但往后在本插件的事件循环里可能被更新、优化、忘掉一部分)、身上的疤、拒绝过什么、日记、B 站登录 cookie。这些是「她经历过什么」,只能自己长,不能拷贝。

带过来的设定不受那两条提示词禁令管(「不可以用亲密度/好感度这类词」「还不会撒娇卖萌」)——那是我们给白纸定的规矩,不覆盖别人已经养出来的样子。但物理层两条照旧:她不能读自己的数值、不能改自己。

如果你的大肥鱼带了好感度这类数值设定:进入本插件环境后她看不到自己的好感度是多少,这个插件也不允许你通过命令去改她的人设——也就是说,你在原来的环境里可以为所欲为,到了这里,关系只能靠真实交互推进,你也没法随口命令她换一种人设。

这个豁免只发给「走过导入」的那一位。判断依据是事件流里那条 import-mark: 记录,不是配置项——所以用「备份与恢复」把你自己的档搬回来,一分豁免都不会有,她仍然是你那只守着原规矩的鱼。事件流只能追加,原生的一局里永远长不出这条记录。

只在还没有起点的一局生效。已经有起点时会拒绝,让你先重置——覆盖一个已经在长的她,这个决定不该由插件替你做。带过来的称呼也会顶掉我们第一天的起名事件,她不会再问一遍「该怎么叫你」。

数据在哪

默认全部在 ~/.dsh-fish-river/,但可以整个搬走(0.6.2):设置 → 大肥鱼 →「数据」页签 →「迁移工作区」,选一个文件夹就行。搬走之后路径记在 ~/.dsh-fish-river-location.json(刻意放在数据目录外面),重启 DSH 才生效。老实现在原处改名成 dsh-fish-river-backups/moved-<时间戳>/,不删。DSH_FISH_HOME 环境变量仍然优先,高于这个指针。

两个工作区、两个家(0.6.7)

一个工作区只该有一个家。如果同一个 DSH 里两个工作区都装了大肥鱼、又都落到同一个目录(最常见的原因:只搬了插件文件夹、没配 root,或 cordis.patch.yml 里那条 id 在升级后对不上),0.6.7 起会自己认出来:

  • 加载时在数据目录里写 instance.json(谁占着、什么时候心跳、进程号、机器名),每 15 秒续一次;45 秒没续、或同一台机器上那个进程已经没了,就当作上一任崩了,新的立刻接管(重启不用等)。
  • 后进来的那个不写:growth_commit / her_leave / bili_speak 会直接回一句「这个家被另一个实例占着」,并说明怎么分开;身份文件也不落盘。已经写进去的旧事件不动。
  • 每条事件现在带 sessionId(谁的哪条会话写的);工具调用前还会核对调用者的 cwd 属不属于当前家,不属于就拒绝并说明——「谁在写」写进事件里,事后能查。

要真的分成两个家,别复制插件目录,只改配置:在 profile 的 cordis.patch.yml 里给这个插件加一条 root(dsh-fish 的 loader 条目 id 要对得上),两个工作区各指一个目录,重启 DSH。升级插件包不会动 cordis.patch.yml(插件只写自己数据目录里的文件),但升级后值得回来看一眼那条 id 还在不在。

会话被归档了,为什么不再自动重开(0.6.8)

以前「把她的会话归档」等于「下次启动重开一间」——判活只看工作区的会话清单、又把归档的排掉,归档之后就成了「这个家没有会话」,于是每启动一次多一间。0.6.8 起语义变了:

  • 归档 = 收起来。home.json 里记 status: 'archived'(含 archivedAt),启动时不再自动新建。设置 → 大肥鱼 →「数据」页会出现一张「会话被收起来了」的卡片,点**「重新搭一間」**才会重开(对应 POST /dsh-fish-river/home/rebuild,要带 confirm: 'rebuild')。插件自己的「重置一局」(/reset)不受影响,它本来就是要一局新的。
  • 同一间房不再并发建两条。 启动链路和「她要说话」链路都会找会话,现在按 root 单飞(同一个 promise),另外 home.json 的 createdAt 在 10 分钟内时,即使宿主索引还没列出那条会话也按「可复用」算。
  • 换家留痕。 root 改了(cordis.patch.yml 里的 root)之后,新家从零开始,旧家原封不动留在旧路径——这正是「一夜之间多了个分身」的来源。现在数据目录之外记一份 ~/.dsh-fish-river-homes.json(可用 DSH_FISH_HOMES 覆盖),root 变了会写一条 meta 事件「家换了:A → B。这不是搬家……」,进日志、进 /state.homes,设置页数据页顶部也会并排给出两条路径。
  • 归档的是那一条,不是整个家。 以前 home.json 只认一条会话,它一归档,插件就说「这个家的会话被收起来了」——哪怕同一个工作区里还有另一条好好开着。现在两条归档判定(home.json 的 status:'archived' 与宿主的 archivedSessionIds)在拒绝之前都先看同屋:还有没归档的会话就认那一条(取 ws.sessionIds 里最近的一条),把 home.json 改指过去、清掉归档标记,并记一行日志。一条都不剩才说「被收起来了」,也不会因此硬造一条新的。

换家 ≠ 搬家:想把她的东西带过去,用「数据 → 迁移工作区」(写 ~/.dsh-fish-river-location.json 指针),不要改 root。

两个实例同时开同一个家(0.8.0)

0.6.8 的单飞锁只在一个进程内有效。真把 DSH 开两遍、两边都落同一个 root 时,第二个进程照样会走到「建会话」那一步(房客日志里成对的 home.json.*.tmp 就是这么来的),于是每开一次多一条会话。0.8.0 补了两处:

  • 占用中的家只读:instance.json 显示这个家已被别的实例占着时,启动什么都不建(连空工作区也不建),只把 home.json 里那条会话报上去,也不叫醒她。设置 → 数据页会出现「这个家在别处也开着」的卡片;关掉另一个实例后,下次启动自动接管。
  • 跨进程一把锁:ensureTalkSession 从读 home.json 到写回整段在 <root>/.talk-session.lock 里跑(wx 独占创建、mtime 超过 30 秒算陈旧锁)。等 8 秒还拿不到就不硬建:读一眼 home.json,有会话就复用,没有就明确失败,下次再来。

想真正并存,还是那句话:在 cordis.patch.yml 给每个条目配不同的 root,不要让两个实例住同一个家。

升级了却没变化?先看设置页顶上那张卡(0.8.0)

profile 里写 "dsh-fish": "file:D:/DSH/plugins-src/dsh-fish" 时,pnpm 会往 node_modules\dsh-fish 落一份独立副本,而 DSH 加载的就是副本。只把新版本覆盖到源目录、然后重启——跑起来的还是旧代码,界面没有报错,日志也不会多一行(新代码压根没执行)。

0.8.0 起启动时会比一次:profile 里那条 file: 指向的目录版本 vs 正在跑的 VERSION。对不上就

  • 设置 → 数据 页靠下的位置出现一张「升级没有真正装进去」的卡(在「导入」卡下面、最底下的重置按钮上面),写清两条修法:关掉 DSH 后 pnpm install --force,或者直接删掉 node_modules\dsh-fish 再启动;
  • 启动日志里留一行 warn,/state.upgrade 也带着 {stale, older, running, source, sourcePath}。

只有 link: 安装、读不到源目录、package.json 坏了这几种情况会静默跳过,不影响启动。 还有一种它查不到的情况:副本是空的。 如果 pnpm 装到一半失败,node_modules\dsh-fish 可能是个空目录(或只有零星几个文件)——这时插件根本没被执行,那张卡也不会出现(卡是插件自己画的)。判断方法很简单:看 node_modules\dsh-fish\package.json 在不在、版本号对不对:

node -p "require('C:/Users/你/.dsh/profiles/desktop/node_modules/dsh-fish-river/package.json').version"

路径换成你自己的 profile。读不到就是空的:重装一遍(pnpm install --force)再启动;装的时候不要只看命令回显——要看退出码。

空副本让插件「连自己都没跑起来」,所以「插件自检」这条路在安装失败面前天生看不见;0.8.1 把 userQuestions 从 inject 里摘掉,就是不想再有第二条同样看不见的静默消失。

不想手敲那条命令,就用仓库里的脚本(零依赖,插件没加载也能跑):

node tools/check-install.mjs                 # 扫所有 profile
node tools/check-install.mjs --profile desktop

它会说出是「正常 / 装的是旧副本 / 副本空的或装了一半」,后两种直接给修法。

默认位置里放着:

文件 是什么 能不能手改
events.jsonl 事件流,一行一条,不能删除记录,唯一可信源 只能追加。改它 = 改她的记忆
state.json 派生快照,随时可由事件流重算 改了没有意义(rebuild() 会覆盖)
config.json 生长环境 可以,也可以走设置页
owner.json 主人身份(0600) 可以
diary/YYYY-MM-DD.md 她的日记 看设置里给不给你看
diary/archive/YYYY-MM-DD.md 0.6.4–0.6.27 留下的旧稿归档(0.6.28 起不再新增) —
out/YYYY-MM-DD.md 她刷完站写的东西 —

同一天写两次日记时,第二遍接在第一遍后面(中间一行 ---),两段都留在 diary/<日期>.md 里;内容与最后一版完全一样时不追加。观测台、/dsh-fish-river/diary、listDiary 仍然一天一页,跨天才出现翻页键,同一天的几段合成同一页。diary/archive/ 里 0.6.4–0.6.27 的旧稿原样保留,只是新写入不再产生归档。

state.json 是派生品这条由测试守住:删掉它 → rebuild() → 内容与之前一致。

整包可以备份成一份 JSON(设置 → 大肥鱼 → 数据 → 备份成一份文件),恢复时旧档整体封存到 dsh-fish-river-backups/,不覆盖、不删除。重置也一样:只改名封存,不删。

重置会留下三样东西:cookie(owner.json)、设置(config.json——上网与成长那些旋钮)、本机测试开关(.dev)。搬走的只有她经历过的事(0.4.8)。

重置后侧栏不会立刻出现新会话:宿主对空白会话的规则是「只有成为当前会话才显示」,插件用 API 建的会话永远不是当前会话。点一下「叫醒大肥鱼」,她开一次口才会出现(0.4.9 起提示里直接说这一步)。

她贴出来的图(封面、视频拼图)一律是正斜杠路径:C:/Users/... 能显示,C:\Users\... 在客户端会变成「图片无法预览」(0.5.0 实测,同一张图只差斜杠方向)。历史消息里已经写下的反斜杠图不会变好,只有新版之后的消息能显示。

适配哪些 DSH 版本(0.8.1)

package.json 里写了两处,跟市场上的插件用同一套写法:

"engines": { "dsh": ">=0.2.0-rc.1 <0.3.0-0" },
"dsh": {
  "compatibility": {
    "dsh": ">=0.2.0-rc.1 <0.3.0-0",
    "dshReleases": { "0.2.1-rc.2": "compatible", "0.2.1-rc.3": "compatible", "...": "unknown" },
    "profiles": ["desktop", "web", "headless"]
  }
}
  • engines.dsh 是宿主安装/检查时真读的那一条;不满足会作为 engines.dsh 报出来。
  • dsh.compatibility.dshReleases 是逐版本的态度:只在真跑过的版本上写 compatible,其余写 unknown——写 unknown 不是「不支持」,是「没测过」。
  • 当前写 compatible 的只有 0.2.1-rc.2、0.2.1-rc.3(房客与开发机在 0.8.x 上跑过的两个)。

运行时还会自己对一次账(lib/hostver.js):从插件所在位置向上找宿主的 package.json,读到版本就比范围。

  • 在范围里:什么也不说。
  • 比自己要求的下限还旧、或超出上限:logger.warn 一行,设置页「数据」页多一张卡说明只提醒、不拦人——插件照常跑。
  • 读不到(桌面版把宿主装进 app.asar,从插件位置找不到):记 unknown,不把「读不到」当成「不兼容」,否则每个桌面用户都会看到一张假警告。

为什么下限是 0.2.0-rc.1 而不是更低:插件用到的宿主接口(tools.register/view/restrict、sessionController.create/prompt/rename/resolveAgent、workspaceRegistry/archiveSession/setTitle)在 0.1.x 的包里其实已经有了(查过 dsh-tools@0.1.0-rc.8、dsh-api-session-controller@0.1.2-alpha.2、dsh-workspace@0.1.0-rc.8),但没有在那些宿主上真跑过。声明的意义是承诺测过的范围,不是猜一个更大的数。

这一条管不到的事:inject 里的四个服务(sessionController、tools、webServer、connection)仍然是硬依赖——缺任何一个,插件在 import 阶段就被跳过、连日志都不写。这类情况只能从外面查:node tools/check-install.mjs(见上一节)。

观测台在哪

设置 → 大肥鱼(settings.section)。

不在浏览器里直连 /dsh-fish——那条路由被 connection 的信任栅栏守着,要的是 App 内置的登录 cookie,浏览器地址栏直接敲只会拿到 401(已从 app.asar 的 requestRejection → admit → browserAuth.isAuthenticated 链路确认)。/dsh-fish 现在只返回一句「搬进 App 了」。

观测台能看:已成长天数 / 当前阶段 / 下一个成长瓶颈 / 她自己说过的话 / 认知重构 / 勇于拒绝 / 没想通的事 / 读到别人的说法 / 最近事件 / 日记。 观测台能改:冷落阈值、每天醒几次、每天几条事件、其中几条向她提问、日记开关与可见性、黑名单。

日记攒到多天时按天分页:一页一天,多了出现「← 更早 / 更晚 →」,翻过的页留在内存里,30 秒轮询不会把你正在看的那页弹走(0.4.8)。

没有:好感度数字、性格滑条、亲密度进度条。

最后一页是「反馈」:开发者 QQ,加上交流群的二维码与群号。二维码是插件自带的一张图(media/feedback-qr.jpg),走插件自己的 /media 路由出去,不挂第三方图床——图床挂了这一页就废了。

长这样(三张都是真机截图):

设置 → 大肥鱼 → 状态

「状态」:现在是什么状态、走到哪一天、卡在哪个瓶颈、立场攒了几条。

设置 → 大肥鱼 → 成长

「成长」:四个阶段与三道瓶颈,以及她那几个节奏参数。

设置 → 大肥鱼 → 上网

「上网」:能不能出门、几点到几点、一天刷几次、一次看几条。

目录

lib/
  index.js      插件入口(inject 必须是一个数组,见文件头注释)
  schema.js     事件形状与校验;ULID;selfmod 侦测
  store.js      只追加写;原子写;rebuild()
  derive.js     纯函数:事件流 → 此刻的她(含阶段门)
  persona.js    她看到的那段提示词(四条禁令在这里)
  identity.js   把「此刻的她」送进 system 角色
  selfdoc.js    把「此刻的她」落盘成「她的身份.md」
  stance.js     立场清单:四个阶段完成后才会开始有(最多 12 条 / 进身份 8 条)
  events.js     事件模板目录(每条有 id / prompt / cond)
  generator.js  今天该出哪条事件(纯函数,没候选就不打扰)
  scheduler.js  生物钟:每分钟 tick 一次
  capture.js    growth_commit 工具——她唯一的写入口 + debrief() 防注入
  guard.js      防注入与防泄露的最后一道闸
  rules.js      规则的诞生与四层结构
  unlock.js     「她能不能长出新规矩」
  budget.js     单轮回复的字数预算
  absence.js    冷落检测(系统侧算,她自己数不出来)
  wakewin.js    「现在是不是定时器在叫醒她」的时间窗
  away.js       「你不在的时候」她做了什么
  settings.js   唯一配置入口(DEFAULTS 是唯一默认值来源,加项只改这里)
  bili.js       B 站只读客户端(首页/排行/搜索/详情/评论/弹幕;无依赖,只用内置 fetch)
  browse.js     选题 → 筛(黑名单/时长/去重)→ 深读 → 组织素材
  look.js       bili_look 工具——她能自己上网看
  watch.js      bili_watch 工具——她要看画面时用
  speak.js      bili_speak 工具——她在 B 站开口
  comment.js    发评论 / 回评论
  video.js      真把一条视频「看过」(抽帧)
  media.js      图片落盘 + 视频抽帧拼图
  cover.js      封面图
  risk.js       风控的补救与预防
  report.js     把今天的素材写成 out/YYYY-MM-DD.md
  whatseen.js   「看过」「翻到过」「递过来」三档口径的唯一来源(0.8.1)
  history.js    她看过、翻到过什么(还原成人能读的历史)
  inbox.js      信箱:把她说过的话聚到一处
  letter.js     her_leave 工具(信箱写入侧)
  recall.js     her_recall 工具——她能翻自己的过去
  herdiary.js   her_diary 工具——她能翻自己的日记(含 archive/ 里的旧稿)
  talkmem.js    换会话时带走的那几句话
  say.js        对话入口:你对她说一句话,她回你
  turns.js      「一轮」的算法:从会话事件里数他的消息,轮数才不靠猜
  refuse.js     拒绝的形状,和他对她拒绝的反应
  voices.js     评论区里别人的话
  intent.js     「他给了一条视频」是要求还是随口一提
  notify.js     说话提醒
  status.js     「现在在干什么」的旁观者视角
  log.js        插件自己的运行日志
  selfcheck.js  环境自检
  selfupdate.js 自带升级:下载 GitHub tag 包 → 解开验包 → 关应用换包 → 自动重开
  update.js     检查更新(只在点按钮时出网,六条源并发取最新版本号)
  backup.js     整包备份 / 恢复
  reset.js      把她放回第一天(封存,不删)
  readme.js     数据文件夹里的那份「说明.md」
  restrict.js   只让她看见自己的工具(收窄 + 兜底守卫 + 提示词里的「手边有什么」)
  tool.js       插件自己注册工具时用的定义函数
  toolschema.js 简写参数 DSL → 标准 JSON Schema
  observe.js    宿主侧 JSON API(/dsh-fish-river/state、/events、/beliefs、/diary、/config、/wake、/browse、/reports、/update)
  client.js     浏览器半边:设置 → 大肥鱼
test/           1090 个测试,node --test 全绿

跑测试

node --test                     # 在插件目录里跑(不带参数,Node 自己找 test/*.test.mjs)

不需要装任何依赖:工具注册直接交标准 JSON Schema(lib/tool.js),不依赖宿主内部的 @deepseek-ai/dsh-tools——那条路只在 link: 安装时碰巧走通,npm 安装的插件会因为找到那个包而报 unsupported JSON schema,6 个工具全部注册失败。这个 bug 是被一个干净 profile 装出来的,本机用了两个月都没发现。

安装

它没有独立的启动器: 是DSH profile的插件,跟随DSH一并启动。

装之前先确认一件事:刷视频功能需要 ffmpeg,没装会降级成只看文字(标题、简介、热评、弹幕)。

ffmpeg 有三种来源,按这个顺序找:DSH_FISH_FFMPEG 环境变量 → 插件自带的 bin/<平台>-<架构>/ffmpeg.exe → PATH。也就是说你指了路径就只认那一条,不会偷偷换成别的。

自带的 ffmpeg 怎么来(2026-11-02 起):

node tools/setup-ffmpeg.mjs                    # 自动在仓库上一级找 ffmpeg-*.zip
node tools/setup-ffmpeg.mjs D:\path\to\ffmpeg.zip

ffmpeg.exe 约 100 MB,正好卡在 GitHub 单文件上限上,所以二进制不进 git(bin/ 在 .gitignore 里),但会进 npm 包(files 里有 bin)。解压出来之后自检面板会显示 …\bin\win32-x64\ffmpeg.exe · 9.0.2,视频画面理解(抽九帧拼一张图)就通了。

ffmpeg 写文件这件事不需要额外权限:抽帧和拼图都从 stdout 拿字节,落盘由插件自己做(2026-10-30,issue #13)。所以 workspace-write 这类不给子进程写权限的文件策略下也能出图。

默认不校验证书(ffmpeg 的 tls_verify 默认就是 0),拉到能出就直接出。只有你明确想校验证书、或某个构建不带 CA 时,才设置 DSH_FISH_CA_FILE 指向 CA 包——设了它会同时打开 -tls_verify 1,两者必须成对,光给 CA 不开校验是空转(2026-10-30 实测:传一个不存在的 CA 文件照样出图)。

三种装法,任选一种:

  1. 面板装(推荐):DSH → 设置 → 插件 → 从本地安装 → 选这个目录。
  2. 命令行装(先关掉 DSH,运行中替换已加载的包会 EPERM):
"<DSH>/resources/runtime/cli/bin/dsh.cmd" plugin --profile <profile> add "link:<本目录绝对路径>"
  1. 直接从 GitHub 装(不用先下载):
"<DSH>/resources/runtime/cli/bin/dsh.cmd" plugin --profile <profile> add "github:bychance6600/dsh-fish-river"

打算改代码用 link: 指向自己的工作目录;只是想用,用 github: 这条。

无论哪种,都要保证 profile 的 package.json 里 dependencies 与 dsh.profile.bundles 都有 dsh-fish-river —— 只有一边会在下次 pnpm 清理时把包删掉,表现为「重启后 DSH 崩了 / 设置页没有鱼」。包名必须与 cordis.patch.yml 里的 name 完全一致(都是 dsh-fish-river)。

全新 profile 还要有 @deepseek-ai/dsh-web-app:只装 dsh-base 的话,插件会停在 pending (waiting for services: sessionController, webServer),页面根本起不来。dsh plugin add 只会自动加 dsh-base,dsh-web-app 要自己补进 bundles。

装完重启 DSH,设置 → 大肥鱼。

这个整合版不自动升级。 lib/update.js 里 UPDATES_ENABLED = false,设置页点「检查更新」只会回一句「这个合并版没有上游」,一个请求都不发。原因是上游 ssamurias/dsh-fish 没有渠道层,跟着它升级会把 QQ / B 站 / 抖音 整套换掉,而界面只会显示「升级成功」。想更新就 git pull 之后重启,或者重新装一次。

路径里带中文时,用 ASCII 目录联接(junction)指过去更稳:先 fs.symlinkSync(真目录, ASCII 别名, 'junction'),再 link:<ASCII 别名>。用 mklink /J 接中文目标路径常把目录名写成乱码,随后 MODULE_NOT_FOUND。

卸载 / 停用

  • 卸载:设置 → 插件 → 卸载(或 dsh plugin --profile <profile> remove dsh-fish-river),然后重启。~/.dsh-fish-river/ 不会被动,她的记忆留在那儿。
  • 只想让她歇着:设置 → 大肥鱼 → 把「每天醒几次」设成 0。关掉后定时器不唤她、不刷 B 站、不写任何事件——零后台活动。

现在的状态

0.1.3。事件流与派生、四个阶段与立场、三层记忆、两条成长通道(含她自己开闸)、四个地方的渠道层与闸门、代发、世界书、B 站扫码登录、观测台渠道页——都已落地。

1237 个测试全绿(3 条跳过是因为本机没有 ffmpeg)。CI 跑三档:Ubuntu 的 Node 22 / 24,加 Windows 的 Node 22。

还没连过真号的:QQ(需要你自己的 SnowLuma / OneBot 11 桥)、B 站私信、抖音(选择器可能要按你的页面校准)。

许可

MIT,见 LICENSE。这个项目从 ssamurias/dsh-fish 0.8.5 演进而来,渠道层参考了 Britneycode/dsh-fish-soul,逐项来源与致谢见 NOTICE.md。

—/ 5

No ratings yet

Verified DSH bundle

Commit 29d731e8ee3a

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout