🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

大肥鱼桌宠 oldhan13/dsh-dafeiyu

DeepSeek Harness
oldhan130 天前有提交已提供中文文档扫描:含敏感能力

养一条会聊天、能动手办事的桌面鱼同伴

32.9/100
综合分
4
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add oldhan13/dsh-dafeiyu
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:仅索引
是什么
dsh 原生插件 · desktop
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 0 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 42 处,展示前 40 条)
  • 执行外部命令来自配置/变量
    dafeiyu/desk/bridge.mjs:190
    child = spawn(d.cmd, args, { cwd: ROOT, windowsHide: true, shell: d.shell, env: process.env });
  • 执行外部命令固定命令
    dafeiyu/desk/bridge.mjs:481
    execFile(
  • 执行外部命令来自配置/变量
    dafeiyu/desk/bridge.mjs:501
    execFile(process.execPath, [script, ...(argList || [])], {
  • 请求外部主机api.siliconflow.cn
    dafeiyu/group/watch.mjs:380
    r = await fetch('https://api.siliconflow.cn/v1/chat/completions', {
  • 发起网络请求
    dafeiyu/group/watch.mjs:380
    r = await fetch('https://api.siliconflow.cn/v1/chat/completions', {
  • 请求外部主机api.siliconflow.cn
    dafeiyu/group/watch.mjs:456
    r = await fetch('https://api.siliconflow.cn/v1/chat/completions', {
  • 发起网络请求
    dafeiyu/group/watch.mjs:456
    r = await fetch('https://api.siliconflow.cn/v1/chat/completions', {
  • 发起网络请求
    dafeiyu/group/watch.mjs:480
    const r = await fetch(`${NAPCAT}/send_group_msg`, {
  • 发起网络请求
    dafeiyu/heartbeat/heartbeat.mjs:173
    const r = await fetch('http://127.0.0.1:3000/send_private_msg', {
  • 发起网络请求
    dafeiyu/heartbeat/heartbeat.mjs:187
    const r = await fetch('http://127.0.0.1:3000/send_private_msg', {
  • 发起网络请求
    dafeiyu/heartbeat/heartbeat.mjs:201
    const r = await fetch('http://127.0.0.1:3000/send_qzone_msg', {
  • 发起网络请求
    dafeiyu/heartbeat/heartbeat.mjs:301
    const r = await fetch(`http://127.0.0.1:3000/${action}`, {
  • 执行外部命令固定命令
    dafeiyu/heartbeat/heartbeat.mjs:400
    execFile(
  • 发起网络请求
    dafeiyu/lib/comfy.mjs:41
    const r = await fetch(BASE + '/system_stats', { signal: AbortSignal.timeout(3000) });
  • 执行外部命令来自配置/变量
    dafeiyu/lib/comfy.mjs:51
    const p = spawn(process.execPath, [installer, '--start'], { detached: true, stdio: 'ignore', windowsHide: true });
  • 发起网络请求
    dafeiyu/lib/comfy.mjs:120
    const r = await fetch(BASE + '/prompt', {
  • 发起网络请求
    dafeiyu/lib/comfy.mjs:141
    const h = await fetch(BASE + '/history/' + jid, { signal: AbortSignal.timeout(8000) }).then((x) => x.json());
  • 读取密钥环境变量
    dafeiyu/lib/group-chat.mjs:17
    const TOKEN_FILE = process.env.DAFEIYU_ONEBOT_TOKEN
  • 发起网络请求
    dafeiyu/lib/group-chat.mjs:78
    const r = await fetch(`${NAPCAT}/get_group_list`, {
  • 发起网络请求
    dafeiyu/lib/llm.mjs:130
    r = await fetch(c.baseUrl + '/chat/completions', {
  • 执行外部命令来自配置/变量
    patches/apply-patches.mjs:466
    execFileSync(process.execPath, ['--check', file], { stdio: 'pipe' });
  • 读取本地文件
    patches/apply-patches.mjs:481
    if (!existsSync(lib) || !statSync(lib).isDirectory()) {
  • 读取本地文件
    patches/apply-patches.mjs:481
    if (!existsSync(lib) || !statSync(lib).isDirectory()) {
  • 读取本地文件
    patches/apply-patches.mjs:497
    if (!existsSync(source)) {
  • 读取本地文件
    patches/apply-patches.mjs:502
    if (existsSync(file) && readFileSync(file, 'utf8').includes(patch.marker)) {
  • 读取本地文件
    patches/apply-patches.mjs:502
    if (existsSync(file) && readFileSync(file, 'utf8').includes(patch.marker)) {
  • 写入本地文件
    patches/apply-patches.mjs:511
    copyFileSync(source, file);
  • 读取本地文件
    patches/apply-patches.mjs:526
    if (!existsSync(file)) {
  • 写入本地文件
    patches/apply-patches.mjs:556
    copyFileSync(file, backup);
  • 写入本地文件
    patches/apply-patches.mjs:557
    writeFileSync(file, after, 'utf8');
  • 写入本地文件
    patches/apply-patches.mjs:559
    copyFileSync(backup, file);
  • 写入本地文件
    patches/catchup.js:84
    mkdirSync(dshDir, { recursive: true })
  • 写入本地文件
    patches/catchup.js:85
    appendFileSync(logPath, line + '\n', 'utf8')
  • 读取本地文件
    patches/catchup.js:90
    if (!existsSync(statePath)) {
  • 读取本地文件
    patches/catchup.js:97
    const parsed = JSON.parse(readFileSync(statePath, 'utf8'))
  • 写入本地文件
    patches/catchup.js:126
    mkdirSync(dshDir, { recursive: true })
  • 写入本地文件
    patches/catchup.js:128
    writeFileSync(tmp, JSON.stringify(state), 'utf8')
  • 写入本地文件
    patches/catchup.js:129
    renameSync(tmp, statePath)
  • 读取本地文件
    patches/catchup.js:148
    try { raw = readFileSync(pendingPath, 'utf8') } catch { return rows }
  • 写入本地文件
    patches/catchup.js:187
    mkdirSync(dshDir, { recursive: true })
扫描于 2026/11/3(0 天前) · 范围 全包扫描 · 来源 GitHub 仓库
涉及外部主机:api.siliconflow.cn

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

住在 DSH 里的同伴:透明桌宠、能对话的面板、记忆与主动性。一个桌面伙伴,住在 DeepSeek Harness 里。

README

由 DeepSeek 最新模型翻译生成
大肥鱼 · dsh-dafeiyu 一条鱼住在你屏幕的右下角。 它慢慢地浮,尾巴轻轻摆,偶尔吐一个泡。你干活的时候它会抬头看你一眼,又低下去。 双击它 —— 它当场长成一个对话框。十分钟没人理,它闭眼睡着;鼠标挪过去,它睁开眼,眼珠跟着你的鼠标偏一点。 它记得住事,也会自己找话说。不是助手插件,是住在这台机器上的一个同伴。 核心优势 · 怎么装 · 五分钟让它游起来 · 它到底会什么 · 接上 QQ · 各家的长处 · 给 AI 助手的一页 🤖 是被叫来"帮我装一下"的 AI 助手? 直接跳到 给 AI 助手的一页 —— 那里有环境清单、每一步的验收命令、和出问题的对照表。 核心优势 三样东西接在一起,它才不是"电脑上又开了一个 agent 窗口"。 一、它在桌面上,也活在你手机里 鱼在屏幕右下角,双击就地长成对话框 —— 不切窗口,不开浏览器。 但真正不一样的是另一头:它接着 QQ。 你在异乡、在地铁上、手上只有一部手机,发一条 QQ 消息,它就动起来 ——读文件、跑命令、改代码、看窗口,办完把结果发回你的手机。 不是远程登录一台电脑,是同一个它有两个入口:桌上那条线和 QQ 那条线,读的是同一份人格、同一份台账、同一个工作区。 所以从手机发的那句话,跟坐在电脑前说,分量一样。
展开完整文档(README 剩余 1 节)
大肥鱼 · dsh-dafeiyu

一条鱼住在你屏幕的右下角。

它慢慢地浮,尾巴轻轻摆,偶尔吐一个泡。你干活的时候它会抬头看你一眼,又低下去。
双击它 —— 它当场长成一个对话框。十分钟没人理,它闭眼睡着;鼠标挪过去,它睁开眼,眼珠跟着你的鼠标偏一点。

它记得住事,也会自己找话说。不是助手插件,是住在这台机器上的一个同伴。

核心优势 · 怎么装 · 五分钟让它游起来 · 它到底会什么 · 接上 QQ · 各家的长处 · 给 AI 助手的一页

🤖 是被叫来"帮我装一下"的 AI 助手? 直接跳到 给 AI 助手的一页 —— 那里有环境清单、每一步的验收命令、和出问题的对照表。

核心优势

三样东西接在一起,它才不是"电脑上又开了一个 agent 窗口"。

一、它在桌面上,也活在你手机里

鱼在屏幕右下角,双击就地长成对话框 —— 不切窗口,不开浏览器。
但真正不一样的是另一头:它接着 QQ。

你在异乡、在地铁上、手上只有一部手机,发一条 QQ 消息,它就动起来 ——读文件、跑命令、改代码、看窗口,办完把结果发回你的手机。
不是远程登录一台电脑,是同一个它有两个入口:桌上那条线和 QQ 那条线,读的是同一份人格、同一份台账、同一个工作区。

所以从手机发的那句话,跟坐在电脑前说,分量一样。
(边界不糊:QQ 每人一条会话,上下文不共享,靠同一份记忆对齐 —— 见它到底会什么最后两条。)

二、它不只是会聊,它能接管这台电脑

挂上 Windows MCP 之后,列窗口、按名字点控件、等控件出现、截图、按组合键、开程序、跑命令,都是它随手能用的手。

于是它能做的事超出"给你一段代码自己粘":你说「帮我把那个框点了」,它去看窗口树、找到那个按钮、按名字按下去;你说「现在屏幕上什么样」,它截一张、看完再决定下一步。
一般的 agent 到不了这一层 —— 它们只能说话,它能动手。

代价说在前面:它动手就真的动你的鼠标键盘和窗口。
所以这条线只开在私聊里,删东西、动设置、发出去的话,一律先问你。

三、它是你的,不是模板

人格不写死在代码里,就在工作区的一个 AGENTS.md:怎么说话、什么时候开口、什么不许说、叫你什么 —— 全是你改的。再往下一层是记忆:开场简报、台账、日记、每个人的档案卡,都是纯文本,你读得到、改得动、删得掉。

所以它能一边把事办完,一边还是它自己:办完活不只交一段汇报,会补一句「这事别这么干」;你累的时候它不劝,就在旁边待着。
这不是"情绪价值的文案",是同一份记忆喂出来的同一个人。

加起来就一句话:一个记得住你、能替你动手、你在哪儿都叫得动的同伴。

给 AI 助手:先读这一页

这一节是写给机器的:如果你被叫来帮人装这个东西,读完它就有足够信息动手了。人可以不看。
全部事实都是这台机器上可以验证的,写的路径就是真路径。

它是什么(别猜错形状)

一个住在 Windows 桌面右下角的常驻进程(WinForms 画的鱼)+ 一堆纯文本记忆。
不是 web 应用,没有 Docker、没有数据库、没有账号体系。所有状态都是本机文件:

| 东西 | 在哪 |
| 人设与规矩 | AGENTS.md(仓库根,纯文本,随便改) |
| 记忆 | dafeiyu\memory\(日记 / 长期事实 / 开场简报) |
| 每个人的档案 | dafeiyu\people\<QQ号>.md |
| 运行时状态(不进 git) | dafeiyu\state\、~\.dsh\ |
| 模型端点配置 | dafeiyu\state\llm.json |

需要什么(必需 / 可选,怎么验)

| | 要求 | 验收命令(照抄) | 缺了怎么办 |
| 系统 | Windows 10/11 | [Environment]::OSVersion.VersionString | 没有替代方案 |
| PowerShell | 5.1(系统自带) | $PSVersionTable.PSVersion | 不用装 |
| Node.js | 22.15+(要 zstd) | node -v | winget install OpenJS.NodeJS.LTS,或 nodejs.org |
| DSH | 0.2.x | cmd /c dsh --version | npm i -g @deepseek-ai/dsh |
| 模型 key | 一把能直连的 API key | 部署界面卡片 ③「测一下」 | platform.deepseek.com / siliconflow.cn |
| git | 只有 clone 仓库要用 | git --version | 不必装:下载 zip 解开也行,exe 能自己下 |
| NapCat | 只有接 QQ 才要 | 部署界面的环境清单会自己找 | 见 docs/SETUP.md |
| ffmpeg | 只有录屏 / 语音转写要 | ffmpeg -version | 可选,不装不影响说话 |

⚠️ 用 cmd /c dsh --version,别写 dsh --version。 npm 装出来的 dsh 有两个壳(dsh.ps1 和 dsh.cmd);在执行策略受限的宿主(比如打出来的 exe)里,.ps1 那个会被拦,症状是一个「禁止运行脚本」的弹框 —— 而用户自己在 cmd 里跑却是正常的,很容易误判成"代码坏了"。

装(按顺序来,每步都有验收)

1. 拿到一份仓库。放普通目录,别放 ~\.dsh 里面 —— Windows 沙箱会让子 shell 起不来。
git clone https://github.com/oldhan13/dsh-dafeiyu.git C:\dsh-workspace
cd C:\dsh-workspace
验收:Test-Path install.ps1 → 必须 True。False 说明只是下了个 exe,不是仓库。

2. 界面走一遍(这条路和命令行跑的是同一套脚本) .\setup.cmd 验收:① 环境清单全勾(打 ✗ 的那行就写着缺什么,照它补) ③ 贴 key → 点「测一下」→ 必须回一句"通了",401 是 key 不对、402 是没余额 ④ 点「一键启动」→ 八项全勾只想用命令行的话:.\install.ps1 -OwnerQQ <你的号> -BotQQ <小号>,然后 .\dafeiyu.cmd。

它会做 / 不会做(说清楚,省得你以为它全能)

install.ps1 会:

- setx DAFEIYU_OWNER_QQ / DAFEIYU_BOT_QQ —— 写用户环境变量,永久生效
- 建 ~\.dsh 和两个空 key 文件(siliconflow-key.txt / onebot-token.txt;已存在的不动)
- 装插件 dsh-ledger-memory(带了 -WithQQ / -WithVoice 就再装对应的)
- 给 QQ 插件打两个补丁(戳一戳 / 分句),打不上只警告,不影响启动
- 把 profile\cordis.patch.yml 复制成 ~\.dsh\profiles\web\cordis.patch.dafeiyu.sample.yml

install.ps1 不会:

- 装 Node、装 DSH、下载仓库(Node 缺了直接退出;DSH 缺了只警告一句)
- 把上面那份 sample 并进 cordis.patch.yml —— accessToken / adminUsers / botQQ 三处占位符要人工改
- 填模型 key(在界面卡片 ③,或直接写 ~\.dsh\deepseek-key.txt,纯文本、无 BOM、别带换行)

出问题先查这张表

| 症状 | 原因 | 处置 |
| 界面第一行「项目目录 ✗」 | exe 不在仓库目录里 | 把 exe 放进 dist\ 或根目录;或者让它自己下载(裸 exe 第一次打开会问装到哪) |
| 弹「禁止运行脚本」 | 在受限策略下调了 .ps1 壳 | 改用 cmd /c;或 powershell -ExecutionPolicy Bypass -File … |
| 面板能开,但鱼不说话 | 模型 key 没填 / 余额不足 | 看 ~\.dsh\deepseek-key.txt;界面 ③ 点「测一下」 |
| 3079 没在听 | 桌面聊天桥没起 | .\dafeiyu\tools\start-dafeiyu.ps1 |
| 8643 没在听 | QQ 插件被禁用,或 NapCat 没登录 | dsh plugin --profile web enable dsh-onebot-qq,再登 NapCat |
| 中文全是乱码 | .ps1 少了 BOM(这台机器 ACP=936) | 存成 UTF-8 带 BOM,且只能有一个 BOM(两个会把 #requires 当命令) |
| 改了脚本没生效 | 桌宠是独立进程 | .\dafeiyu\pet\pet-reload.ps1(归一 BOM + 语法检查 + 重启) |

别做的事

- 别删 ~\.dsh\attachments\v1\objects 里的任何东西 —— 会话历史按哈希引用它,删掉一个,引用它的那个会话从此每轮都失败,报的却是"传输失败",看着像网络问题
- 别替用户填 key、别替他付钱。key 只让他自己贴
- 别改 git 历史、别 force push 别人的分支
- 别把 dafeiyu\state\、dafeiyu\memory\brief.md 这类运行时文件提交上去(里面写着他的窗口标题)

怎么装:有界面,也有命令行

路一:双击 setup.cmd。 一个窗口分三块,从上往下走一遍就行:

1. 环境够不够格 —— Node / DSH / 工作区位置 / 端口占没占,逐项打勾(打 ✗ 的那行直接写缺什么)
2. 你是谁、她是哪个号 —— 两个 QQ 号。只写进本机 dafeiyu\state\machine.json(.gitignore 挡着,不会进 git)
3. 起没起来 —— 点「一键启动」,八项逐个打勾;缺哪项,右边的过程框里有原因,不用你去翻日志

顺手说一句:dafeiyu.cmd 在这台机器还没配过的时候,也会自己把这一屏叫出来 —— 入口只有这一个,不用记。

路二:命令行。 就是下面那几条(install.ps1 + dafeiyu.cmd)。两条路跑的是同一套脚本,界面只是把「检测 → 安装 → 启动 → 验收」串起来,把结果画得能看懂。

想打成一个 exe? .\dafeiyu\tools\build-exe.ps1 用 ps2exe 把部署界面编成 dist\dafeiyu-setup.exe (要从 PSGallery 装一次 ps2exe;打不出来也不影响用 setup.cmd,两者是同一个东西)。
打出来大概 90 KB。

这个 exe 现在能自己起手了。 从 release 页单下一个 dafeiyu-setup.exe 双击也可以:它发现自己不在仓库目录里,就会问你要装到哪儿,然后把仓库 zip 拉下来解开(不需要 git),再从新目录里把真正的部署界面叫起来。网络不通的时候它会告诉你去 GitHub 手动下 zip。

不过 exe 本身还是那个壳 —— 它跑的是这套脚本,不是另写一份,所以脚本一升级就得重打一次。
dist/ 不进 git:二进制不该混在源码里。

装了之后怎么更新

配置界面右上角有「检查更新」:

1. 检查 —— 只看一眼本地是哪一版、网上是哪一版,一个文件都不动
2. 更新到最新 —— 从仓库拉最新的 zip,只换代码和文档,先备份到 dafeiyu\state\backup\update-<时间>\,再逐个换

不需要 git,也不用重新 clone。你的记忆、人物档案、图片、历史、桌宠位置、机器配置 ——一样都不碰(people/ memory/ state/ assets/ voice/ group/ relay/ bili/ homepage/data/ 和 desk/history.jsonl 整棵跳过)。

换完把那个窗口关掉重开(新脚本才进来),桌宠和宿主也重开一下。
被占用的文件(比如桌宠正在跑的 pet-wf.ps1)会跳过并列出来,关掉它再点一次就行。

版本记在 dafeiyu\state\version.json:sha、时间、备份在哪儿、换了几个文件。

五分钟让它出现在你右下角

先别管 QQ、语音、B 站 —— 那些都是后话。先让鱼游起来。 (走 setup.cmd 装过的人可以跳过这一节,界面里已经做完了。)
你需要:Windows 10/11、Node.js 22+、DSH 0.2.x(npm i -g @deepseek-ai/dsh)。
三条都验一下,缺哪条先补:

node -v # 要 v22 以上(zstd 那段要 22.15+)
dsh --version # 要 0.2.x
$PSVersionTable.PSVersion # 系统自带 5.1 就行

然后三条命令(第三条也可以直接双击仓库根目录的 dafeiyu.cmd):

1) 放到一个你想当"工作区"的目录。别放在 ~/.dsh 里面(沙箱会拦)
git clone https://github.com/oldhan13/dsh-dafeiyu.git C:\dsh-workspace
cd C:\dsh-workspace

2) 检查环境(这一步不装插件,先确认跑得动;两个 QQ 号现在随便填,接 QQ 时才用得上) .\install.ps1 -SkipPlugins

3) 一键:宿主没起就自己起,然后把鱼放出来 .\dafeiyu.cmd

宿主不开窗口(它自己在后台跑),网页界面还是 http://127.0.0.1:3080。
想盯着它看日志、或者调插件的时候,才需要 .\start-dsh-web.cmd 手动开一个窗口。

第 3 步会一行一行告诉你它拉起了什么,末尾是这个样子:

=== 现在 === 宿主 : 8643 在听 (3080 网页) QQ : 在线循环 : 在跑主页 : http://127.0.0.1:3077/ 群随听 : 在听(3078)桌宠 : 在屏幕上聊天框 : 3079 在听

(没接 QQ 的,QQ 那行会是 离线,别的一样。)

然后对一下这张表 —— 五样都对上,就是装好了:

| 你看哪里 | 应该是什么 |
| 屏幕右下角 | 一条半透明的小鱼在轻轻浮 |
| 双击那条鱼 | 原地长成对话框,左边是她、右边是你 |
| 对话框里说一句 | 几秒后回你(第一次慢,它在读记忆) |
| 十分钟不碰它 | 闭眼、几乎不动;鼠标挪过去会醒 |
| Ctrl+Alt+D | 面板开关 |
| 右键鱼 → 退出 | 她收工 |

没对上?看 docs/SETUP.md 的「卡住了」一节:八成是 Node 版本、工作区位置,或者端口被占。

怎么收工

.\stop-dafeiyu.cmd # 鱼 + 后台循环全停,宿主留着 .\stop-dafeiyu.cmd -WithHost # 连宿主一起停(网页界面也会断) .\stop-dafeiyu.cmd -Status # 只看看谁在跑,一个都不动

它到底会什么

不是功能清单,是它一天里真的会做的事:

| 它 | 具体是什么样 |
| 在桌上待着 | 透明 WinForms 小鱼:呼吸、游动、摆尾、吐泡、闭眼睡觉;拖得动,双击开面板,Ctrl+Alt+D 收放 |
| 身上有信息 | 它在想事 → 游得快;在动手 → 摆尾更快,偶尔吐泡;卡住了 → 气色往暖红偏;空闲 → 慢下来 |
| 跟你说话 | 双击长成 QQ 式面板:左她右你、时间分隔条、标题下面那行是她的心情、右上角一个「声」 |
| 自己找话说 | 每 8–13 分钟醒一次,自己决定:不说 / 冒个气泡 / 发一句 / 发条语音 / 发条说说 |
| 记得住 | 人格 AGENTS.md + 开场简报 brief.md + 台账 + 日记;每轮现读,不靠"传话" |
| 会动手 | 看窗口、按名字点控件、截图、TTS / ASR、发 QQ、发空间、换头像 |
| 能自己画图 | 装上生图模块之后她有一支笔:你说「画一张…」,她真的去调 ComfyUI(FLUX,十几秒一张),画完直接弹到你屏幕上 —— 不是嘴上说「画好了」 |
| 能收下你的文件 | 把文件(一次好几个也行)直接拖到面板上,或者点输入框旁边的 + 挑;它们排成一行小牌子,发的时候只把路径递过去 —— 她本来就摸得到磁盘,不复制、不搬走 |
| 办不了的事交出去 | 面板里遇到搞不定的(查资料、改代码、装插件、要跑很久),调 delegate 交给后台那个完整的智能体;它跑完,结果自己贴回面板 |
| 接得起电话 | 戴上耳机跟她说话:她听得见、答得出、中间能打断。声音是她自己的音色,说话时认得你是谁、记得你们前两天在干什么 —— 不是换了个客服来接 |
| 会唱歌 | 给她一段清唱当样板,她把另一首歌的调子搬到你/她的音色上唱出来(sing.py,装在拓展模块里) |
| 有一页自己的主页 | http://127.0.0.1:3077/ —— 现在的心情、碎念、说说,她自己写 |

三个设计上的选择

1. 桌面和 QQ 是两条线,不是同一个上下文。 DSH 里每处说话都是独立会话(QQ 每人一条、网页一条),而面板那条连会话都不是 —— 它是 bridge.mjs 自己的循环。
真共享得让面板接宿主的 /api,那扇门带浏览器信任校验和启动令牌。
所以选了便宜的那条:两边各自读对方的原文 —— 不共享上下文,但不用"传话"。

2. 她的动不是装饰。 鱼身上每一处变化都对应一件真事:在想、在动手、卡住了、睡着了、你回来了。
纯装饰的动画是贴纸;会跟着事变的才是"有人在那"。

3. 能出声,但默认关。 面板右上角的「声」点了才念。桌面上的声音比 QQ 更吵,得由你决定。

拖个文件给它

把文件拖到面板上就行,一次好几个也可以;不想拖就点输入框旁边那个 + 去挑。
它们会在输入条上方排成一行小牌子,发之前点牌子上那个 × 能拿掉、不想要了清空也行。

递过去的不只是名字:路径会一起给她。她本来就摸得到磁盘,所以这里没做"上传"、没有副本、也没有第二份会过期的文件。
面板里只显示名字和大小 —— 路径是给她用的,不是给你看的。

⚠️ 一句实话:桌面这条线是纯文本模型,她看不见图。
你丢一张截图过去,她知道文件在哪、能用命令去处理它,但不会真的"看见"里面画了什么。要她看图,得另接一个视觉模型。

看一条视频(可选:要装一个技能)

上面那句「她看不见图」有一个补丁:视频可以先拆成她能读的东西。

- 要两样外面的东西(都不在这个仓库里): video-watch-plus 这个技能 —— 用 ffmpeg 逐帧量测(切点 / 冻结 / 运动 / 静音)→ 挑关键帧拼成一张图板;声学侦察判断有没有人声 → 转写 → 证伪幻觉; yt-dlp —— 把 BV 号 / 链接落成 mp4。
- 一条命令(dafeiyu/tools/video-watch.mjs):

node dafeiyu\tools\video-watch.mjs BV1G2ak69EFT # 下载 → 量测 → 图板 → 声学侦察 → 清理
node dafeiyu\tools\video-watch.mjs D:\some\clip.mp4 # 本地文件不动它,只在临时目录里抽帧
node dafeiyu\tools\video-watch.mjs BV1G2ak69EFT --asr # 连语音转写一起跑(慢,几分钟)
node dafeiyu\tools\video-watch.mjs --status # 占了多少地方
node dafeiyu\tools\video-watch.mjs --sweep / --purge # 清残留 / 全清

- 它自己收拾,不靠记性:视频与中间产物在同一次运行的 finally 里删(分析失败也删);每次启动先扫一遍 30 分钟前留下的残留;图板与报告各只留最近 20 份。
--status 里 clipsMB 正常应当是 0 —— 不是 0 就说明上一次是被硬杀掉的。
- 两条不能省的规矩:① 图板里读出来的字一律不采信(分辨率不够时视觉会编;要说某格写了什么,得按 shotlist.json 的坐标从原视频裁切放大再读);② 声学侦察判「没有人声」时,转写照样会吐字 ——那是幻觉(判据:互不重叠的时间窗输出同一句话),不许当成转写引用。
- 它只吃你给的链接或本地文件。不是爬虫,不做批量抓取。

装那两样(一次性):

git clone https://github.com/xinhailaopo/video-watch-plus "$env:USERPROFILE\.dsh\skills\video-watch-plus"
那个技能自带 scripts\setup-windows.ps1:ffmpeg 已有就用 -Ffmpeg 指过去,别重下 190MB
python -m pip install -U yt-dlp

--asr 第一次跑会下 faster-whisper 的 small 模型(约 460MB,走 HF_ENDPOINT 镜像);之后是纯本地推理。机器的 pip 默认源若是卡住的,加 -i https://pypi.tuna.tsinghua.edu.cn/simple。

打个电话(可选:要装 Python 依赖)

桌宠和网页都能跟她通话:你说话它听、它回话,中间能打断它。

| 入口 | 怎么开 | 声音怎么走 |
| 桌宠通话窗 | dafeiyu\pet\call.cmd | 本机麦克风 → 本机扬声器 |
| 网页通话 | python dafeiyu\tools\call\webcall.py,手机/电脑浏览器打开它打印的地址 | 浏览器麦克风 ⇄ WebSocket ⇄ 本机 |

两条入口共用同一层引擎(dafeiyu/tools/call/engine.py),链路是一条:

麦克风 ──► VAD 切句 ──► 语音转写 ──► 模型 ──► 整段合成 ──► 压短停顿 ──► 播放 ▲ │ └────────── 打断(barge-in)◄──────────┘

- 怎么装(不用敲 pip):部署界面 →「拓展模块…」→ 语音通话 卡片 →「设置…」,点 一键装好(建 .venv、装依赖、下 piper 音色模型)。第一次几分钟,断了重跑会接着装;国内网络把「下载走清华镜像」勾上(默认勾着)。
· 等价的命令行:python dafeiyu\tools\call\bootstrap.py --all --mirror tuna
· 想先看看缺什么:同一排的 检查一遍(它真的 import 一遍那些包)
- 转写:faster-whisper,有 CUDA 就自动用 GPU;模型第一次用时自己下,落在 dafeiyu\state\ 下面。
换档在设置窗口的「③ 你怎么被听清」里(tiny / base / small / medium / large-v3),改完点 重启耳朵。
- 音色在同一个窗口里挑(写进 dafeiyu\state\call\config.json;通话窗顶栏点一下也能切,命令行 python dafeiyu\tools\call\config.py --set tts.backend=sovits 也行)。
★ 挑之前能先听:选一个 → 点 试听这句 —— 不必真打一通电话,听完再定。
四个选项:
· piper —— 本地 ONNX,免费、离线、0 显存,音色偏播报腔(最轻)
· sovits —— 本地 GPT-SoVITS,克隆你自己的音色,最像人;要另装 GPT-SoVITS,占约 1.3G 显存
· cloud —— 硅基流动 CosyVoice2,明显更自然,按量计费,开口多约 1 秒
· openai —— gpt-4o-mini-tts,最自然、还能给语气指令;未实测(本机没有 key),要 key + 代理
- ★ 播放必须过抖动缓冲(2026-10-26 20:4x 定的,别再改回去):最早是「合成一块就往声卡塞一块」,听着老卡一下。量了才发现问题不在平均速度 ——整句 RTF 只有 0.6(意思是不算慢),但 sovits 的出块是突发式的:一句中间会出现 570~860ms 的空档,播放端按 1.0 倍速放,缓冲一空就是一声卡。平均够快,节奏是错的。 ⇒ 两条一起才成立:① engine.py 的 JitterPlayer 把「生成」和「播放」拆成两条线,先垫够 cushion 再开口,垫子自己会随卡顿调大调小;② 合成分小句、每小句整句合成(stream=0),句内不再切块。代价是开口晚一点,换的是一次都不卡。
· 判据:state/call/petcall.jsonl 里的 starved 和 cushion_s —— starved: true 就是那一声卡。
- ★ 模型自己在标点处会停 0.35~0.88 秒,那也是最像「卡」的东西。
合成之后会把静音段压到 80ms(句号 160ms),只动没有声音的样点,一个字都不剪。
- ★ 电话那头得是「同一个人」(2026-10-29 加的;之前的版本听着像陌生人):上一版只往模型里塞三样东西 —— 自动生成的开场简报、技术台账、当天日记。三样全是工作材料:它知道"这个项目在干什么",却不知道"你们俩之间发生过什么",于是开口就像第一次接电话的人。
⇒ 新文件 dafeiyu/tools/call/call_context.py 换了个拼法,拼的是人和说过的话(只读文件,不联网、不调模型,约 20ms): ① 你的档案 people/<你的QQ>.md(你是谁、什么关系、什么边界); ② 你最近说过的原话(活动日志里,带时间); ③ 前几通电话的原文 state/call/call-transcript.md(连续性); ④ 最近两篇日记(那是"她"的味道); ⑤ 活跃台账末尾(此刻手上做到哪)。
合计约 7k 字 ≈ 3.5k token,deepseek-chat 吃得下。
· 调试:python dafeiyu\tools\call\call_context.py 直接看它拼出来的样子(落到 state/call/call-context.md)。
· 兜底:这个模块载入失败会自动退回老逻辑 —— 不许因为一层新模块把电话弄哑。
· 名字也要写进 prompt:原来那段 SYSTEM 从头到尾没写过自己叫什么,她读到的唯一一个名字是项目名,于是自称"大肥鱼"。
⇒ 现在明写「你叫汐…电话只是换了张嘴,不是另一个人」「不许说『我们刚认识』」。
· 不打电话也能验:把 SYSTEM + 拼好的上下文直接问一遍模型,看它答不答得出你昨天在折腾什么。
- ★ Whisper 会在没人说话时自己编话(2026-10-28 实测编出过「以下一张字」):跨虚拟声卡过来的音频窄带、有断点,它会把"安静但不像安静"的片段编成一句中文,于是她开始答一些没人说的话。
三道防线:① 整段能量太低(RMS < 0.012)根本不送耳朵;② 转写命中"字幕 / 点赞 / 感谢观看 / 本期视频"这类样本词 → 一律当没听见;③ 噪声底允许往上爬,别把底噪当人声。判据是"这句我有没有送进去",不是"她答得像不像"。
- key 放哪:~\.dsh\siliconflow-key.txt / ~\.dsh\deepseek-key.txt / ~\.dsh\openai-key.txt,也可以用同名环境变量。别把 key 写进仓库。
- 实测(这台机器,RTX 5080 + whisper small + sovits):你停下说话到听见第一声 ≈2.5~4 秒 (VAD 0.9s + 转写 0.3s + 模型 1s + 整段合成 1~2s);一次通话的预热 ≈4 秒。
- 想让第一声更快:换更小的 whisper 模型、把「一句最多几个字」调小(少合成一点就早开口)。

唱个歌(可选:要一段清唱样板 + 约 4G 显存)

dafeiyu\tools\call\sing.py:拿一段清唱当音色样板,把一首歌唱成那个人的声音(Seed-VC 做转换,不是 TTS 念歌词 —— 调子、气口、换声都在)。

python dafeiyu\tools\call\sing.py --src 你的清唱.wav --song 要唱的那首.wav --out 成品.wav
24 步、17.8 秒的歌,这台机器约 48 秒出成品

- 样板 15~30 秒、干声(没伴奏)最稳;有伴奏会跟着把伴奏一起转进去。
- 设置存 dafeiyu\state\call\sing.json;部署界面 →「拓展模块…」→ 唱歌 卡片里能挑样本、点自检、跑一次。
- 走过的两个坑(都写进注释了,别重踩):hf_utils 要本地优先(不然每次都去拉权重), torchaudio.save 在这套环境里写出来的 wav 是坏的 ⇒ 改走 soundfile。

自己唱(SVS:只给词,不用原唱)

上面那个是「拿别人唱过的换嗓子」,受制于原唱。dafeiyu\tools\call\sing_svs.py 是她自己唱 ——给一段词,她自己排旋律、自己换气、自己走滑音,渲染交给 OpenUtau 的 DiffSinger 声库(Qixuan 这类 for OpenUtau 打包的声库),想更像她再过一遍 Seed-VC。

python dafeiyu\tools\call\sing_svs.py --lyrics "睡吧,今天的风不用管。天亮了又亮,也没关系。"
python dafeiyu\tools\call\sing_svs.py --lyrics-file 词.txt --out 成品.mp3 --voice her
python dafeiyu\tools\call\sing_svs.py --check # 只检查 OpenUtau / 声库 / 参考音色

- 要什么:OpenUtau(便携版解压即可)+ 一个 DiffSinger 声库 + vocoder 模型;可选 Seed-VC(换她的音色)。
- 参数与界面:部署界面 →「拓展模块…」→ 唱歌 卡片 →「合成歌声…」(跟「设置…」那个换音色的窗口是分开的两个);存进 config.json 的 svs 段。
- 完整原理与踩过的坑:docs/SVS.md(为什么不能自己跑 onnx、时间原点 92.9ms 怎么来的、颤音/滑音/换气各自怎么做、验收为什么看拼音而不是字面)。

可选:拓展模块

上面那些是本体。另外这几样要么吃显存、要么要账号、要么要额外的额度 —— 所以不进主流程,单独一页:部署界面底部那排的 「拓展模块…」(桌宠右键和托盘里也有「打开配置」,直接开那个界面)。

| 模块 | 要什么 | 装上之后 |
| 生图 | ComfyUI + 一个模型(8G 显存起) | 她有一支笔,对话里说画就画,十几秒一张 |
| 刷 B 站 | 独立 venv + 扫码登录 | 每天谷时自己刷一轮:搜、读字幕、写日报;「现在跑一轮」可以随时手动 |
| 语音 · 念一句 | 跟「语音通话」共用一份设置 | 把一句话念出来(跟电话里是同一个声音);不装通话也能用 |
| 语音通话 | Python 3.10+(装一次;界面里点「一键装好」) | 戴上耳机就能跟她说话:她听得见、答得出,桌宠和网页两个入口 |
| 唱歌 | Seed-VC + 一段清唱样板(约 4G 显存);想让她自己唱还要 OpenUtau + 一个 DiffSinger 声库 | 给她一段你/她自己的清唱,她把另一首歌的调子搬过去唱出来;或者只给词,她自己唱(docs/SVS.md) |
| QQ 通道 | NapCat + 一个小号 | 她在你手机里也在,私聊群聊语音文件都通 |

每张卡上都写着「需要什么」「本机现在是什么状态」。状态只看本地事实 —— 文件在不在、8188 端口通不通 ——不发网络请求,所以点开就是准的。缺哪一步,卡上那个按钮就干那一步(装环境 / 补模型 / 画一张试试)。

不装任何一个,她照样说话、照样能动手 —— 这几样都是加成,不是地基。

可选:接上 QQ

桌面那条线不需要 QQ。要她在 QQ 里也在,多三步:

dsh plugin --profile web add dsh-onebot-qq # ① QQ 通道插件

② 装 NapCat,用一个小号登录,反向 WebSocket 拨到 8643。
③ 把 profile/cordis.patch.yml 里那三处占位符(accessToken / adminUsers / botQQ)改成你自己的,再并进 %USERPROFILE%\.dsh\profiles\web\cordis.patch.yml。

她一个号、你一个号 —— 别拿主号干这个。细节与验收在 docs/SETUP.md。

接上之后,群里那一半是 dafeiyu\group\watch.mjs 在听(NapCat 把事件推给它):

- 每一条群消息都落盘(group\raw.jsonl,不只落叫她的)—— 所以"没 @ 的时候她也知道你们在聊什么"。
读口两个:开场简报里那节「群里刚在聊什么」,和 node dafeiyu\tools\group-peek.mjs。
- 说话的门槛:不看 @,看"这几句跟她有没有关系"。该接却闷着,和乱插嘴一样是错。
- 按群放开:想在某几个群放得更开(不必等点名、@ 也由本机回),写 group\permissions.json,照 permissions.example.json 抄 —— 一个群一档,没写的群行为不变。说明在 dafeiyu/group/README.md。
- 心跳读的是真消息:每 8–13 分钟醒一次时,她先用 get_friend_msg_history 问最近 20 条(日志那条只当兜底)——日志按 ~5MB 轮转、只盖几个小时,靠它"知道你今天说了什么"是靠不住的。
顺带一句「你最后一条是 N 分钟前发的,人在,只是手不在键盘上(多半在看手机)」:不然你在手机上回话时 presence 判你"不在电脑前",她会按"别吵他"处理。

各家的长处

这一套不是一件东西,是几件各有所长的东西接在一起。写清楚谁擅长什么,才知道哪天该换掉哪一块。

| 东西 | 谁做的 | 它擅长什么 | 在这儿干什么 |
| 大肥鱼(本仓库) | oldhan13 | 把「身体 / 记性 / 主动性」接起来,落到桌面上:会动、会自己开口、记得住事 | 就是那层胶水 —— 下面每一样单拎出来都比它硬,但它让这些一起活起来 |
| DSH(DeepSeek Harness) | DeepSeek | 会话、工具、插件机制(cordis)、网页界面 | 地基。上面每一块都挂在它身上 |
| dsh-ledger-memory | 陈黛华 | 项目级记忆:台账每回合自动注入、决策卡、经验教训,压缩前抓逐字胶囊、压完还能按指针回查原文,跨会话交接单 | 她的记性,和"不丢原话"的底 |
| dsh-onebot-qq | mario841859784 | 一条真的 QQ 通道:OneBot 11 / NapCat,私聊群聊、图与语音、长文转图卡、设置页里配 | QQ 那条线。本仓库一行协议代码都没写 —— 全是它的 |
| dsh-bili-scroll | c1overzz | 自己刷 B 站:按字幕原文挑、写日报、值得的 @ 你,谷时自动跑 | 可选。本仓库只"提一句",没包含它 |
| dsh-voice-chat | maoyuching | 浏览器里说和听:麦克风 → 文字 → 回复自动朗读,TTS 后端可换 | 可选,网页那边的嘴 |
| @lijian-ui/dsh-im-gateway | lijian-ui | 一个网关接五种 IM(钉钉 / QQ / 个人微信 / 飞书 / 企微),扫码绑定、流式回复 | 可选。要多通道时,比逐个接划算 |
| dsh-context | bowenliang123 | 上下文透视:看清上下文由什么组成、怎么长起来的 | 可选,调上下文时看 |
| billion-context | ranxianglei | 把长会话压进小窗口 | 可选,和上面那件二选一 |

一句话:本仓库只做"把它们串成一个活着的东西"这一件事。 每一样的长处都在它自己那边,不在我这儿 ——哪天哪块不够用了,换掉它,剩下的照样转。

这个仓库里没有什么

记忆、日记、群记录、聊天原文、QQ 号、任何 token 或 key —— 一份都没有。
你 clone 下来的是一个空壳:它会跑、会动、会说话,但它不认识任何人。
那些东西长在你自己的机器上(dafeiyu/memory/、dafeiyu/people/、~/.dsh/),也本该只长在那儿。

一些踩过的坑

写在 docs/PITFALLS.md 里,都是真撞过的:PowerShell 变量名不分大小写导致的"永远读不到"、 zstd 多帧会话日志只解第一帧、Windows 11 拿 VBScript 弹弃用提示、/api 的浏览器信任校验。
照抄这份代码之前先看一眼,能少走两天弯路。

别人的东西

这个仓库的代码是自己写的,但跑起来靠的是别人做的零件。每一处"用到别人仓库"的地方都列在 CREDITS.md 里:宿主(DSH)、六个 DSH 插件(dsh-ledger-memory / dsh-onebot-qq / dsh-voice-chat / dsh-im-gateway / dsh-context / billion-context)、另一位开发者的可选插件 dsh-bili-scroll,以及 NapCat、SiliconFlow、QQ、B 站这些外部程序与服务。

一句话:没有 vendor 任何第三方源码,也没有 fork 后改。第三方都是「装上去用的」,不是「抄进来的」。
dsh-onebot-qq 是 BSD-3-Clause —— 两个测试文件会直接 import 它的模块,那条保留版权声明的义务对它成立。

许可

MIT。AGENTS.md 里的人格文本也是 MIT —— 你可以改成你自己的鱼,但别把它说成是你的原创。
预览分享卡片长什么样 ↓oldhan13/dsh-dafeiyu
分享 · DeepSeek Harness Hub
oldhan13/dsh-dafeiyu

养一条会聊天、能动手办事的桌面鱼同伴

回到顶部