DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in
← Plugins
F

dsh-fish-river

Fish River

Raise a fish that grows up on its own: there are no personality settings; its personality can only develop through the event stream. It can browse B 站 on its own and chat via QQ and B 站 direct messages (抖音 optional), while remembering people it meets outside. It does not begin to form opinions until

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add github:bychance6600/dsh-fish-river#624e645612939efe7315d22229a27a3521d88a4a
READMECompatibilityVersions
设置 → 大肥鱼 → 成长设置 → 大肥鱼 → 渠道

Description

Raise a fish that grows up on its own: there are no personality settings; its personality can only develop through the event stream. It can browse B 站 on its own and chat via QQ and B 站 direct messages (抖音 optional), while remembering people it meets outside. It does not begin to form opinions until all four stages are complete. The state store is located at ~/.dsh-fish-river, and records cannot be deleted.

Compatibility and provenance

Fish River is published as dsh-fish-river and currently resolves to version 0.1.64. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
web
Release source
github
Registry updated
11/4/2026

Versions

0.1.64stable
11/4/2026
0.1.63stable
11/4/2026
0.1.59stable
11/4/2026
Show 4 more versionsCollapse versions
0.1.43stable
11/3/2026
0.1.31stable
11/3/2026
0.1.26stable
11/2/2026
0.1.22stable
11/2/2026

Related plugins

Loading related plugins…

Latest
0.1.64
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
Unavailable
Files
Unavailable
Surface
web
License
MIT
Source
github
GitHub
★ 6
Weekly downloads
0
Last push
11/4/2026
View source ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue

Related plugins

More verified plugins in integrations-communication.

Acp App@deepseek-ai/dsh-acp-appThe dsh ACP profile bundle: automation-only JSON-RPC stdio and process lifecycle over dsh-baseRemote Web Ui@linxin666/dsh-remote-web-uiScan-to-pair remote access for the dsh web GUI that shares one official interface: a QR beside the settings button pairs phones and PCs into the same Web GUI (a portrait-touch adaptation layer for phones, full desktop on PCs) through one-time tokens and rRemote Bridge@linxin666/dsh-remote-bridgeDSH remote bridge: the host half serves protocol v1 over one exact /ios/v1 WebSocket route — session roster, history baseline, prompting, live turn increments with tool activity, interrupt, and in-process agent approval — to a device paired by a one-shot Im@xmanrui/dsh-im把微信、飞书、企微、钉钉、QQ、Telegram、WhatsApp、Slack、Discord、Matrix接入DSH。 Connect Telegram, WhatsApp, Slack, Discord, Matrix, WeChat, Feishu, WeCom, DingTalk, and QQ to DSH.

README

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

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

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

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

大肥鱼

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

当前版本 0.1.46(2026-11-03)。她的人格仍然只能长(事件流 + 派生状态 + 四阶段 + 立场,没有任何人格设置项),而她现在可以用同一个自己出现在 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.5Britneycode/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 句滚动淘汰系统

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

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

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

社会层要过四道闸(阶段 ≥3、立场 ≥6 条且 30 天没被整条推翻、活满 14 天且稳定度在健康带)——然后由她自己调用 social_open 按下。开之前,身份文档里一个字都不提;开了之后只补充、不推翻,权重上限 30%,永不进 beliefs。她能学会「在群里别那么较真」,学不会「其实我不在乎他」。

四个地方,同一个她

渠道怎么接默认真机状态
DSH 会话宿主原生开已验证
QQ设置页填桥地址 + 令牌,一键起桥(本插件不带 QQ 协议)关私聊收发 + 群聊,已通
B 站私信设置页扫码登录(passport 官方接口),cookie 只写 owner.json 0600关私聊收 + 发,刷站 + 抽帧看画面,已通
抖音子包随仓库分发(packages/dsh-fish-river-douyin),走本机 Chromium 的 CDP关私信收发、刷推荐流、链接抽帧/图文,已通

所有渠道默认关。要她进群,得在「渠道」页签把群号写进白名单——名单外的群她连听都不听;名单内的群,她也要到第三阶段(自主)之后才搭理陌生人。群里还得 @ 她(mentionOnly 默认开):不 @ 的照样记流水,但不叫醒她。

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

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

她不变的东西

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

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

四、现在到什么程度

  • 已跑通(真机):DSH 会话内的完整闭环;QQ 私聊与群聊;B 站私聊、刷站、抽帧看画面;抖音私信收发
  • 没做成:抖音推荐流(DOM 里没有标题/作者,WebSocket 帧里只有私信,XHR 的 aweme/v1/web/tab/feed 要签名)。唯一剩下的路子是截屏 + 多模态,还没开工
  • 明确没做:老数据迁移(新起一局,没有旧库要迁)、多人格 / 多角色、云同步

和别的养成插件差在哪

别的养成插件这里
人格从哪来填写各类设定和提示词极简提示词,提示词不讲人格设定,全靠自行摸索
能不能反悔随时修改人物记忆和设定历史会被铭记,只能“说服”她不能修改她
会不会拒绝基本不会,拒绝也是假装拒绝会。拒绝并非设定,而是她自然而然不想,事后还会反省自己做得对不对
记忆全量塞进上下文深刻的记忆会铭记,普通的记忆会逐渐淡忘,淡忘后留下的才是有价值的
成长速度按天数或数值解锁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.md0.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() 防注入(日记在 herdiary.js)
  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/ 里的旧稿);write_diary——写下今天那一篇
  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 一并启动。

一键安装(Windows,省掉手改 package.json)

仓库根目录有个 install.ps1,它替你走完 profile 那几步:

# 从 GitHub 装(默认)
powershell -ExecutionPolicy Bypass -File .\install.ps1

# 装本机这份源码
powershell -ExecutionPolicy Bypass -File .\install.ps1 -LocalPath D:\fish\dsh-fish-river

# 顺手把 ffmpeg 也装上(zip 不用解压)
powershell -ExecutionPolicy Bypass -File .\install.ps1 -FfmpegZip D:\downloads\ffmpeg-9.0.2-essentials_build.zip

# 只想看它打算做什么
powershell -ExecutionPolicy Bypass -File .\install.ps1 -DryRun

它做五件事:找 DSH profile(%USERPROFILE%\.dsh\profiles\*,有多个时默认 desktop,可用 -Profile 指定)→ 找 node 与 pnpm(PATH 里没有就用 DSH 自带的那份)→ pnpm add → 把 dsh-fish-river 写进 profile package.json 的 dsh.profile.bundles(改前备份成 package.json.bak-时间戳,缩进跟随原文件)→ 可选跑 tools/setup-ffmpeg.mjs。

只改这一处 package.json 和可选的 bin\win32-x64\,不动 DSH 自己的文件、不写注册表、不删东西。已经装过会跳过(已经在 dependencies 里 / bundles 里已经有了),重复跑安全。

手动装:三种装法,任选一种

  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。

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

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

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

抖音不用单独装

子包就在这个仓库的 packages/dsh-fish-river-douyin/ 里,主包先找已安装的 dsh-fish-river-douyin,找不到就直接加载仓库里那一份(lib/channels/sub.js)。GitHub 装、压缩包装、link: 装都用得上抖音;真正用到浏览器时才登录、才建 douyin-profile,没开这个渠道时不连网、不建文件夹。

把 QQ 接上

这个插件不内置 QQ 协议(见 AGENTS.md §7):它只当一个 OneBot 11 正向 WebSocket 客户端,连你机器上已经在跑的桥(SnowLuma 这类,选「正向 WS」模式,默认地址 ws://127.0.0.1:3001)。

  1. 桥先起来,确认它在监听那个端口(桥的日志里通常写 ws://127.0.0.1:3001,有令牌就记下来)。
  2. 「渠道」页签 → QQ 行:打开开关,填 桥地址、访问令牌(没有就留空)、主人的 QQ 号(几个号用逗号隔开)。想在群里说话,再打开「在群里说话」并填群号。
  3. 不放心就先诊断,不用重启:
node tools/probe-qq.mjs --ws ws://127.0.0.1:3001 --token 你的令牌
node tools/probe-qq.mjs --ws ws://127.0.0.1:3001 --send-dm 123456 --text 在吗

用的是插件里同一份 lib/channels/qq.js,所以「诊断通 = 插件通」。连不上时按这个顺序看:端口没监听 → 桥没起或地址写错;401/403 → 令牌不对;能连上但不回 → 渠道页「为什么没回」那段。

装 ffmpeg(可选,但「看画面」要用)

刷视频的「看画面」要 ffmpeg(抽九帧拼一张图)。没有它也能用,只是这一步降级成只看文字(标题、简介、热评、弹幕)。

它该放在哪:插件只认自己目录下的这一个位置——

<插件所在目录>\bin\win32-x64\ffmpeg.exe
<插件所在目录>\bin\win32-x64\ffprobe.exe

从 GitHub / 压缩包装的人,这个目录是空的(bin/ 被 .gitignore 排除了,因为 ffmpeg.exe 正好 100 MB,卡 GitHub 单文件上限)。三个办法:

办法一:用自带脚本(推荐,不用手动解压)

  1. 下载一个 ffmpeg 的 Windows essentials build(gyan.dev 的 ffmpeg-release-essentials.zip,或 BtbN 的 win64 zip),放在哪都行,比如 D:\downloads\ffmpeg-9.0.2-essentials_build.zip。不用解压。
  2. 在插件目录里运行:
node tools/setup-ffmpeg.mjs D:\downloads\ffmpeg-9.0.2-essentials_build.zip
# 不带参数时,它会在插件目录的上一级找 ffmpeg-*.zip
node tools/setup-ffmpeg.mjs
  1. 脚本会解压到临时目录、挑出 ffmpeg.exe 和 ffprobe.exe、拷到上面的 bin\win32-x64\,然后自己跑一次 -version 验证。看到 已装好:…\bin\win32-x64\ffmpeg.exe · 9.0.2 就成了。

办法二:自己解压

把 zip 里的 bin\ffmpeg.exe 和 bin\ffprobe.exe 直接复制到 <插件目录>\bin\win32-x64\。只有这两个文件有用,整个 bin\ 里的 DLL 之类不用带。

办法三:指一个已有的

设环境变量 DSH_FISH_FFMPEG=D:\tools\ffmpeg\bin\ffmpeg.exe,重启 DSH。设了它就只认这条,不会再去找自带的那份——路径写错时日志会明说「DSH_FISH_FFMPEG 指向的 X 不可用」,不会静默降级。

装好后在设置 → 大肥鱼 → 「插件」页签的自检里能看到版本号;也可以直接验:

node -e "import('./lib/media.js').then(m => console.log(m.detectFfmpeg()))"

三种来源的查找顺序是固定的:DSH_FISH_FFMPEG → 自带的 bin/<平台>-<架构>/ffmpeg → PATH 里的 ffmpeg。

卸载 / 停用

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

许可

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