← Back to home@ZelotsNullzwei

whale-pod

DSH 插件:给 QQ 群加个会看场合的聊天机器人(NapCat 协议端 + OneBot v11 + LLM 网关)

Stars
0
Language
JavaScript
Created
Oct 18, 2026
Updated
Oct 20, 2026

Introduction

WhalePod

WhalePod

简体中文 | English

license dsh node status

给 DSH 装一个 QQ 聊天机器人。
它是 DSH Desktop 的插件,不是独立程序 —— 没有 DSH 跑不起来。


先说清楚:这是个 DSH 插件

WhalePod 依附于 DSH Desktop (DeepSeek Harness 桌面端)。

它在 DSH 里做三件事:

它提供依赖 DSH 的什么
QQ 桥接(OneBot v11)—
LLM 网关(OpenAI 兼容)DSH 的渠道包 + 凭据
侧栏面板(启停 / 配置 / 用量)DSH 的插件宿主 + Web 服务

主要用法是装进 DSH。 宿主进程、HTTP 服务、侧栏面板、模型渠道全都 来自 DSH —— 这是它设计的立足点。

代码里也留了「不装 DSH 直接 node src/index.js」的口子(见 独立跑),但那只是命令行跑网关 + 桥接,没有侧栏面板, 模型渠道要自己配。99% 的人应该装插件,不是走那条路。

30 秒判断

✅ 适合你,如果

  • 你已经在跑 DSH Desktop
  • 你有自己的模型凭据(DeepSeek 官方 Key,或任何 OpenAI 兼容端点)
  • 你想要一个会看场合的群聊机器人,而不是「问一句答一句」的客服
  • 你不介意它是 beta —— 会有 bug,但你会拿它当自己用的工具

❌ 不适合,如果

  • 你没有 DSH —— 先去把 DSH 装上,这里没有替代方案
  • 你以为是一键脚本 —— 还要装 NapCat、配 QQ 号、扫码登录
  • 你想要开箱即用的成品 —— 现在还在 beta
  • 你想要「自动回复机器人」—— 它经常故意不说话,这是设计

它刻意做成了 DSH 的一部分 —— 复用宿主的渠道、凭据、面板,比自己 重造一套省事得多。所以"我不想装 DSH"不是个可以绕开的问题。


它会先判断该不该说话,再决定说什么。
真人不会每条消息都回 —— 他们会看场合、看气氛、看刚才是不是自己说的。

---

它能做什么

┌──────────────────────────────────────────────────────────┐
│ WhalePod(托盘 / 后台)                                   │
│  ├─ LLM 网关  OpenAI 兼容,内置 DeepSeek + 自定义供应商     │
│  ├─ QQ 桥接   消息 → 决策 → 生成 → 发言                    │
│  ├─ 决策层    Jev / 规则 / 自定义(可插拔)                 │
│  ├─ 记忆层    短期上下文 + 长期摘要                        │
│  ├─ 人格层    角色提示词(可编辑)                          │
│  ├─ 组件管理  内置下载 NapCat(含协议端托管)               │
│  └─ 管理界面  Web UI(10 个页面,可折叠侧栏 + 线性图标)    │
└──────────────────────────────────────────────────────────┘

当前可用的运行模式:

模式谁接 QQ状态
whale-podWhalePod 自己✅ 可用
astrbotAstrBot⚠️ 有框架,未完善

关于 AstrBot 模式

代码里保留了完整的架子(后端定义、uv 安装流程、启停、模式切换、 网关接入提示都已经写了),但尚未在真机上跑通验证: initArgs 从未被调用、启动后不做就绪校验、OneBot 侧的对接也还是照 NapCat 的逻辑推的。现在不要指望切过去就能用。

保留它的原因是安装与托管这套逻辑(自带 uv + Python)本身有复用价值, 以后要补的是 AstrBot 专属的那部分 —— 而不是从零重写。

只想要一个能跑的 QQ 机器人,用 whale-pod 模式就够了。


要花钱吗

看你用哪个模型。

WhalePod 本身不收费,钱花在你选的模型上。它支持两类来源:

来源怎么计费
官方 API(默认)按模型厂商官网价,从你的 API Key 余额扣
自备渠道(Ollama / 自建中转 / 别的 API)看你的渠道 —— 本地模型免费,中转看你买的套餐

默认按官方价计费

面板上的「折合费用」就是按模型官网挂牌价算的:

费用按各厂商官网挂牌价折算,与实际渠道无关

它回答的是「这些用量按原价买要多少钱」—— 换渠道时好做对比。 但它不是你的账单:如果你走的是免费/包月渠道,实际扣费会更少甚至为 0。 要核账请去对应厂商的后台。

搜索是唯一固定成本

项目单价
Search API每 1,000 次成功请求 ¥5 → ¥0.005/次
总结那一步几百 token → 按你选的模型官网价

按群里一天搜十几次算,一个月大概 ¥2~3(用便宜模型的话)。

余额低于 ¥2 时它会自己提一句(大肥鱼会念叨 Token,这是她的性格)。

想完全不花钱: · 搜索可以在设置里关掉 —— 之后遇到新番、新人名它会说「不太确定」而不是硬编 · 模型换成本地 Ollama 或任何免费渠道,聊天成本就是 0

怎么控制成本

  • 看面板:实时用量、按模型/渠道分开记,能看出钱花在哪
  • 换便宜模型:flash 档比 pro 档便宜一个数量级,闲聊够用
  • 关掉搜索:不查实时信息就不花钱
  • 设路由策略:不想意外走付费渠道时,用「只用免费」或「只用自备」

为什么做这个

QQ 机器人常见的两个痛点,WhalePod 各解决一个:

痛点常见做法WhalePod
要开一堆窗口NapCat + 框架 + 桥接,2-3 个 cmd/node 挂着一个托盘进程,Web 管所有
烧钱每条消息都调付费 API免费渠道优先,付费仅兜底
说话很呆逢消息必回决策层先判断该不该说
装组件要自己折腾去 GitHub 找 Release、装 Python、配环境内置下载,一键装 NapCat

第三点是关键。真人不会每条消息都回 —— 他们会判断「这是在问我吗」 「我刚说过话要不要歇会」「现在插话合适吗」。WhalePod 把这件事拆成两层:

决策层(便宜、快)→ 该不该说话?概率多少?
表达层(贵、慢)  → 说什么

省钱和活人感是同一个设计带来的。


内置组件下载

WhalePod 能自己把协议端装好,不需要用户去 GitHub 翻 Release:

组件许可自动安装说明
NapCatLimited Redistribution License✅ 下载官方 ReleaseOneBot v11 协议端,约 0.9 MB
AstrBotAGPL-3.0⚠️ 框架已有,未完善见上文说明,当前不可用
SnowLuma源码可见非商业许可❌ 刻意不支持EULA 禁止自动化部署

自带运行时(为 AstrBot 预留)

AstrBot 需要 Python,而用户机器上不一定有。为了让「选这个模式」不变成 「先去装 Python」,代码里做了一套自带的隔离运行时:

WhalePod 下载 uv(14.6 MB,单文件)
   └─ uv python install 3.12   → 自动下载 Python(约 60 MB)
        └─ uv tool install astrbot  → 隔离环境里装好
             └─ uv tool run astrbot run  → 跑起来

设计目标是不碰系统 Python / pip / PATH:

位置内容
~/.whale-pod/runtime/uv/uv 本体
~/.whale-pod/runtime/python/uv 管理的 Python 3.12
~/.whale-pod/runtime/uv-tools/装好的工具

卸载 = 删 ~/.whale-pod/runtime/。

⚠️ 这套流程尚未端到端验证过。whale-pod 模式不需要它, 所以日常使用不会碰到。要启用 AstrBot 模式之前,先按上面的说明做好 「可能跑不通」的准备。

为什么 SnowLuma 不做自动安装

SnowLuma 的 EULA 明确规定「将其并入第三方安装包或 Docker 镜像、 通过自动化脚本部署」须事先取得书面授权。同类项目 qq-bridge 也因此移除过该功能。WhalePod 只探测、不部署 —— 安装与扫码由用户自己完成。

许可合规

「内置下载」与「打包分发」是两件不同的事:

  • 下载官方 Release → 用户主动从上游获取,不是 WhalePod 再分发 ✅
  • 打进 WhalePod 发行包 → 属于再分发,受各自许可约束 ⚠️

所以 WhalePod 只做下载器 + 进程托管,不把协议端代码放进自己的仓库。 下载前 UI 会强制展示许可名称、链接与要点,用户显式确认后才开始; 下载后自动把上游 LICENSE 与来源说明 (WHALE-POD-NOTICE.md) 一并存下来。


快速开始

先选一条路

你想做什么走哪条
在 DSH 里用(推荐,也是这个插件的主用途)↓ 装成 DSH 插件
只要那个 LLM 网关(用 OpenAI 客户端接)↓ 独立跑

两条路互不冲突:装成插件之后,网关也照样能用。


装成 DSH 插件

假设你已经装好 DSH Desktop。

① 下载 Release 解压

到 Releases 下最新的 dsh-whale-pod-vX.Y.Z.zip,解压到任意目录(下面用 <解压目录> 代指)。

② 挂进 profile

dsh plugin --profile desktop add link:<解压目录>/plugin

③ 重启 DSH

侧栏会出现「鲸舱」。第一次点「一键启动」,它会:

  • 自己拉 WhalePod 进程
  • 自己下载 NapCat(如果还没有)
  • 弹二维码让你扫码登录

④ 扫码登录 QQ

登录成功后在群里 @ 它就能用。默认只在被 @ 或提到名字时说话。

装不上怎么办:先看 已知限制。如果卡在某一步,去 Issues 说清楚卡在哪, 附上 DSH 的日志(~/.dsh/logs/host/ 下最新的那个)。


独立跑(少数情况)

⚠️ 这不是推荐路径。 只有你明确知道自己要什么时再走 —— 没有侧栏面板、没有 DSH 的渠道包,模型要自己配。绝大多数人应该装插件。

不用 DSH,直接跑这个仓库(能拿到 LLM 网关 + QQ 桥接,但没有插件面板和 channel-pack 复用):

cd whale-pod
node src/index.js

启动后会打印:

[INF] 已注册 provider N 个:<你配了哪些渠道…>
[INF] 决策层:order = [rule],阈值 0.5
[INF] LLM 网关:http://127.0.0.1:18899/v1
[INF] 网关 key:gdn-xxxxxxxx
[INF] 管理界面:http://127.0.0.1:18898?token=gdn-yyyyyyyy
[INF] WhalePod 就绪

渠道列表取决于你装了哪些、配了哪些。一个都没有时也能跑起来, 只是要先在设置里加一个(官方 API Key 或自备端点)。

前置

依赖说明
DSH Desktop装成插件时必需(复用它的 channel-pack 和凭据);独立跑时可选
Node 22+独立跑必需;用 DSH 自带的运行时也行
QQ 协议端NapCat / SnowLuma —— 可以在「组件」页一键装,不用手动折腾

立刻用起来(任何 OpenAI 客户端)

curl http://127.0.0.1:18899/v1/chat/completions \
  -H "Authorization: Bearer gdn-xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "auto",
    "messages": [{"role":"user","content":"你好"}]
  }'

兼容性:这是标准 OpenAI 协议,所以 Cherry Studio / Continue / AstrBot / 任何 OpenAI SDK 都能直接接:

from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:18899/v1", api_key="gdn-xxxxxxxx")

命令行参数

参数说明
--home <dir>数据目录(默认 ~/.whale-pod)
--port <n>LLM 网关端口(默认 18899)
--web-port <n>管理界面端口(默认 18898)
--no-web不启动管理界面
--no-tray不创建托盘图标
--dsh-app <dir>手动指定 DSH 安装目录
--profile <name>用哪个 DSH profile 的 channel-pack
-h, --help帮助

模型路由

客户端给的 model 字段支持这些写法:

写法含义
auto按策略自动选(推荐)
free/gemini/gemini-3.8-flash指定免费渠道 + 模型
free/gemini-3.8-flash在所有免费渠道里找这个模型
paid/deepseek-chat指定付费兜底
<provider>/<model>直接指定(透传)
<裸模型名>按已发现的模型目录匹配

auto 的行为:按「免费渠道 → 付费兜底」的顺序依次尝试, 第一个成功的就用它。失败会自动跳到下一个,客户端无感。

响应头里会带上实际用的是谁:

x-whale-pod-provider: gemini
x-whale-pod-model: gemini-3.8-flash

决策层(活人感)

默认用内置规则(零成本,永远可用):

被 @          → 95% 回复
刚说过话      → 冷却期内不说
连说 3 轮     → 主动收尾
群聊没人理我  → 4% 概率随机插话
私聊          → 默认回复

升级选项(改 ~/.whale-pod/config.json):

Jev(TypeSafe System One)—— 真正的活人感

{
  "decision": {
    "provider": "jev",
    "threshold": 0.5,
    "jev": { "enabled": true, "credentialRef": "TYPESAFE_API_KEY" }
  }
}

把 TYPESAFE_API_KEY 加进 ~/.dsh/.credentials.yaml 的 refs 里。

成本:约 $0.00002/次(社区数据),按每天 500 次判断算 ≈ ¥0.07/天。

Jev 是可选增强,不是依赖 —— 关掉它、key 失效、服务超时, 都会自动降级到内置规则,机器照常跑。

自定义 HTTP 决策服务

{
  "decision": {
    "provider": "custom",
    "custom": { "enabled": true, "url": "http://127.0.0.1:5000/decide" }
  }
}

WhalePod 会 POST:

{
  "scene": { "kind": "group", "groupId": "123" },
  "self": { "qq": "10001", "nickname": "WhalePod" },
  "sinceMyLastMessageMs": 45000,
  "consecutiveTurns": 1,
  "messages": [{ "sender": "小明", "text": "在吗", "mentionsMe": true }]
}

期望返回(宽松解析):

{ "reply": true, "probability": 0.8, "reason": "被点名了", "delayMs": 1200 }

降级链

[jev] → 失败 → [custom] → 失败 → [rule](永远可用)

任何一层出问题都不会让机器人失联,最差退回规则判断。


QQ 配置

{
  "qq": {
    "enabled": true,
    "onebot": {
      "wsUrl": "ws://127.0.0.1:3001",
      "httpUrl": "http://127.0.0.1:3000",
      "accessToken": ""
    },
    "allow": {
      "groups": [123456789],
      "private": [987654321],
      "allowAllWhenEmpty": false
    },
    "reply": { "segment": true, "maxChars": 500 },
    "model": "auto"
  }
}

⚠️ 白名单是 fail-closed 的:groups/private 都为空且 allowAllWhenEmpty: false 时,WhalePod 不响应任何消息。 这是刻意的 —— 防止误配置导致机器人在陌生群里乱说话。

表情与图片

模型可以在回复里写标记,WhalePod 会把它转成真正的表情段:

标记效果
[表情:0]QQ 内置小黄脸(id 见 src/qq/faces.json,含全部 105 个经典表情)
[表情包]随机发一张(优先用你账号里收藏的表情)
[表情包:猫猫]按名字挑 —— 先查 faces.json 按名匹配,挑不到再退到收藏表情

关于"按关键词搜表情":QQ 没有公开的表情搜索 API(实测候选接口全 404), 所以 WhalePod 在本地完成匹配 —— src/qq/faces.json 是从 NapCat 内嵌的 QQ face_config 导出的表情索引(105 个经典小黄脸 + 动画表情)。

如果你下载过 QQ 表情商城的表情包,可以生成自己的索引:

node scripts/extract-stickers.mjs [QQ数据目录]

它会读 marketface/json/*.jtmp(QQ 自己存的表情包清单,带每个表情的名字和关键词), 输出 src/qq/stickers.json。这个文件是你个人的数据,已在 .gitignore 里。

别人发来的表情/图片

WhalePod 会把送进模型的文本里的 [表情包:xx]/[图片] 改写成 「(对方发了一个表情包)」这类行为说明,并附上图片本体。 人格提示里明确要求模型不要去描述图里有什么 —— 真人收到梗图不会说 「这是一张猫的图片」,而是直接回个表情或接一句情绪话。

被戳一戳

别人戳它,默认戳回去(qq.poke.chance 控制理会概率,默认 0.75), 并按 textChance 概率补一句话。真人在群里被戳也很少正经回文字。

{
  "qq": {
    "poke": { "enabled": true, "chance": 0.75, "textChance": 0.25 }
  }
}

谁在跟它说话

三种情况它会认为「这是对我说」:

  1. 被 @ 了(mentionsMe)
  2. 叫了它的名字 —— 昵称会被切成 2~4 字片段去匹配(蓝色大肥鱼 → 蓝色/肥鱼/大肥)
  3. 引用了它的消息 —— 有人回复它刚说的话,也算(靠比对被引用消息 id)

三者都走同一条"几乎必回"的分支,不受冷却限制。

协议端

WhalePod 只连接协议端,不下载、不安装。 你需要自己装好 NapCat 或 SnowLuma,在里面开启:

  • WebSocket 服务端(正向 WS)→ 填到 wsUrl
  • HTTP API → 填到 httpUrl

NapCat 与 SnowLuma 的关系:NapCat 官方 README 推荐用 SnowLuma 作为 GUI 替代品。两者都实现 OneBot v11,WhalePod 都支持。

⚠️ SnowLuma 的 EULA 对「通过自动化脚本部署」有限制, 所以 WhalePod 刻意不做自动安装。


人格

角色文件在 ~/.whale-pod/persona/roles/<name>.md,直接编辑或从 Web UI 改。

首次启动会写入两个模板:

角色风格
default.md普通群友(短句、口语、不客套)
quiet.md潜水版(很少说话)

人格提示词的关键:写具体的说话习惯,不要写形容词。 「你很幽默」没用;「偶尔只发个『草』或者『?』」才有用。

Web UI 的「人格」页可以直接编辑并切换当前角色。


记忆

层作用成本
短期每个会话最近 N 条上下文(内存)¥0
长期滚动摘要(谁在群里、在聊什么)一次便宜模型调用

长期记忆默认关闭。开启:

{ "memory": { "long": { "enabled": true, "summarizeAfter": 200 } } }

它不用向量库 —— 群聊的长期记忆价值在「最近发生的事」, 滚动摘要比语义检索更合适,而且零额外依赖。


管理界面

http://127.0.0.1:18898?token=<启动日志里的 token>

页内容
总览运行状态、渠道、用量、凭据
QQ连接状态、消息统计、最近交互、暂停开关
模型路由策略 / 渠道优先级 / 默认模型 / token 上限 / 试跑
渠道自备渠道管理(添加、测试连接、删除)
组件模式切换 + 内置下载 NapCat + 启停 + 日志(AstrBot 为未完善)
决策提供者可用性、在线试判断(不真的发消息)
人格编辑角色、切换当前角色
记忆各会话上下文、长期摘要、清空
日志实时日志流(SSE)
配置当前配置(密钥脱敏)

界面约定:长表单与长列表默认折叠(点标题展开,状态记在浏览器里), 图标统一用单色线性 SVG(跟随主题),删除/清空等危险操作走二次确认弹层。

「模型」页能调什么

四组设置改完立即生效,不需要重启:

设置说明
路由策略free-first 免费优先 / free-only 完全不花钱 / self-only 只用自备
渠道优先级与开关拖整行排序(或用箭头微调)+ 开关控制是否参与路由;改动自动保存
默认模型输入 + 分组下拉共存;只填模型名,厂商由上面优先级决定
token 上限带推理的模型要给够(建议 ≥1024),否则推理吃光预算、正文变空
模型清单按模型归纳,渠道跟在模型名后面;点渠道 chip 一步选定;带「刷新目录」按钮

默认模型选择器是自定义组合框(不是原生 datalist): 可以直接打字过滤,也可以展开按系列浏览。分组按模型的系列名—— deepseek-* 一组、openai/* 一组;常用厂商(deepseek、openai、google、 anthropic、qwen…)排在前面,其余按模型数量降序。每项右侧标出哪几家渠道有它。 输入框保留手输,列表里没有的模型名也能直接填。

渠道优先级与开关都是真·生效的:顺序写进 gateway.freeProviders、 开关写进 gateway.disabledProviders,由 router.applyFreeOrder() 与 禁用过滤在每次请求时应用。首次启动会按内置的 DEFAULT_FREE_ORDER (gemini 优先,已验证最稳)初始化一次。

⚠️ 顺序与开关是两个独立关注点:顺序只管顺序,开关只管开关。 把状态混进顺序数组会让读写都容易出错,所以单开了一个列表。


能用本地模型吗?

能。 走 Ollama 或任何 OpenAI 兼容端点,聊天成本降到 0。

接 Ollama(最省事)

  1. 先让 Ollama 跑起来,并拉一个中文够用的模型:

    ollama pull qwen2.5:7b
    ollama serve
    
  2. 打开管理界面 → 渠道页 → 添加渠道,预置里选 Ollama(本地):

    字段填什么
    预设Ollama(本地)
    baseUrlhttp://127.0.0.1:11434/v1(预置自带)
    API Key留空 —— 本地不需要
    模型你 ollama pull 过的名字,例如 qwen2.5:7b
  3. 点测试连接。它会打 /v1/models;通了会列出现有模型,再顺手试一次 /v1/chat/completions 确认能真出字。

  4. 去 模型页把路由策略切成 self-only(只用自备), 或把 Ollama 渠道拖到优先级最上面用 free-first。

接其它端点

预置里还有 OpenAI / DeepSeek / 硅基流动 / 月之暗面 / 智谱 / 阿里百炼, 以及 自定义端点(任何 OpenAI 兼容服务,包括中转和 vLLM、LM Studio 这类本地服务)。 自定义端点的 baseUrl 要带 /v1。

挑模型的两个注意点

注意原因
token 上限要给够(建议 ≥ 1024)带推理的模型会先吃掉预算再出正文。给小了就是「推理写满、正文为空」或者硬挤几个字 —— 看着像在胡说
群聊不需要大模型决策层已经挡掉了大部分「不该接」的消息,轮到模型时都是短回复。7B 量级够用,越大越慢

本地模型不支持联网搜索的总结质量会差一些 —— 搜索本身走的是外部 API, 但把结果揉成一句话那步是模型干的。介意的话在设置里关掉搜索。


数据与安全

文件说明
~/.whale-pod/config.json配置(含网关 key、Web token)
~/.whale-pod/credentials-overlay.json续期后的凭据(不碰 DSH 的文件)
~/.whale-pod/persona/roles/*.md人格
~/.whale-pod/memory-long.json长期摘要
~/.whale-pod/whale-pod.log日志

凭据的 overlay 设计

WhalePod 读 DSH 的 ~/.dsh/.credentials.yaml,但写只写自己的 credentials-overlay.json。原因是 DSH 和 WhalePod 可能同时活着, 两边都写同一个文件会互相覆盖。

读取时 overlay 优先,缺失回落到 DSH 文件 —— 所以你在 DSH 里重新登录, WhalePod 也能立刻用上新凭据。

模式可在配置里改:

mode行为
overlay(默认)读合并、写自己的文件
direct读写都走 DSH 文件(原子写 + 备份)
readonly只读(续期会失败,但不会崩)

其它

  • 只绑回环地址(127.0.0.1),不对外暴露
  • 密钥不回传明文(Web API 只给前缀)
  • 凭据值不进日志
  • Web/网关各自有独立 token

架构

仓库布局

本仓库同时包含核心运行时与DSH 插件两部分,它们是一体的:

whale-pod/
├── src/                 核心运行时(独立进程,可脱离 DSH 单独跑)
├── plugin/              DSH 插件(宿主适配层:透传 API + 侧栏面板 + 进程托管)
│   ├── index.js         Host 半身:/api/whale-pod/* 透传 + 启停本地处理
│   ├── client.js        客户端半身:侧栏仪表盘 + 设置页
│   └── daemon.js        拉核心进程 / 心跳 / 彻底关闭
├── assets/              图标(多尺寸,打包用)
├── scripts/             辅助脚本(提取表情索引、启动器等)
└── tests/               自检脚本

为什么放一个仓库:插件本身没有业务逻辑,它只做三件事 —— 把 HTTP 透传给核心、 把 UI 挂到 DSH 侧栏、保证核心进程活着。两者版本必须同步,分仓会导致 「插件 0.2 + 核心 0.1」这种错配。插件通过相对路径 ../src/index.js 定位核心, 仓库内直接可用。

核心模块

src/
├── index.js              主入口(组装 + 优雅退出)
├── logger.js             环形缓冲日志
├── config/
│   ├── store.js          配置(原子写 + 深合并 + 损坏自愈)
│   └── credentials.js    凭据(overlay 读写 + YAML 解析)
├── gateway/
│   ├── llm-core.js       cordis Context + channel-pack 装载
│   ├── router.js         路由策略(渠道选择与兜底)
│   ├── openai.js         OpenAI 协议 ↔ dsh-llm 转换
│   └── server.js         HTTP 端点(/v1/chat/completions)
├── decision/
│   ├── provider.js       接口 + 降级注册表
│   ├── rule.js           内置规则(零成本兜底)
│   ├── jev.js            Jev(可选,付费)
│   ├── custom.js         自定义 HTTP
│   └── index.js          装配
├── memory/
│   ├── short.js          会话短期上下文
│   └── long.js           滚动摘要
├── persona/store.js      角色提示词
├── qq/
│   ├── onebot.js         OneBot v11 客户端
│   ├── protocol.js       协议端托管(下载、启停、端口探测)
│   ├── runtime.js        自带 uv + Python(AstrBot 预留,未完善)
│   ├── modes.js          运行模式(whale-pod 可用 / astrbot 未完善)
│   └── bridge.js         消息 → 决策 → 生成 → 发送
├── web/
│   ├── server.js         HTTP 端点 + 页面骨架
│   ├── assets.js         前端 CSS/JS(内嵌字符串)
│   ├── api.js            插件兼容层用的 API
│   └── icons.js          线性图标库(内联 SVG,48 个)
└── tray/index.js         托盘(可选,优雅降级)

关键技术点

复用 DSH 的渠道适配器:WhalePod 不重新实现 Gemini/EAC/账号渠道, 而是用 cordis 的真实 Context 装载宿主的 channel-pack:

const ctx = new Context()
new LlmRuntime(ctx)                     // ctx.llm 自动注册
new MinimalCredentials(ctx)             // 读 ~/.dsh/.credentials.yaml
pack.apply(ctx, {})                     // 13 个渠道就绪

只需要补三个最小服务(credentials/commands/tools),各有几行代码。


测试

# 用 DSH 自带运行时跑(本机没装独立 Node 时)
ELECTRON_RUN_AS_NODE=1 "D:/DSH Desktop/DSH Desktop.exe" tests/check-decision.mjs
ELECTRON_RUN_AS_NODE=1 "D:/DSH Desktop/DSH Desktop.exe" tests/check-qq.mjs
ELECTRON_RUN_AS_NODE=1 "D:/DSH Desktop/DSH Desktop.exe" tests/check-boot.mjs
测试覆盖
check-decision.mjs决策层 31 项(降级、阈值、规则分支)
check-qq.mjsQQ 桥接 43 项(CQ 码、白名单、端到端、记忆)
check-boot.mjs启动自检 13 项(各服务、API、鉴权)
check-gateway.mjs网关端到端(含真实 Gemini 调用)
check-overlay.mjs凭据 overlay 写隔离
check-llm-core.mjschannel-pack 装载
check-providers.mjsprovider / 模型枚举

English

WhalePod

A QQ chat bot that lives inside DSH.
One tray process handles everything — gateway, bridge, decision layer, persona, protocol host.

You install the plugin, open the sidebar, and press Start. No command line, no manual process juggling — after the one-line install.

Before you start — is this for you?

This is a DSH plugin, not a standalone app.

  • ✅ Yes, if you already run DSH Desktop, you have a DeepSeek API key, and you want a group-chat bot that knows when to stay quiet.
  • ❌ No, if you don't have DSH, you expected a one-click script (you'll still configure NapCat and scan a QR code), or you want a "reply to everything" bot — this one deliberately stays silent most of the time.

Does it cost anything?

WhalePod itself is free. Money goes to whichever model you point it at:

  • Official APIs (default) — billed at the vendor's list price, deducted from your API key balance.
  • Your own channels — Ollama, a self-hosted endpoint, a subscription relay — whatever that costs. Local models are free.

The panel's "converted cost" is the list price of the tokens (what they'd cost from the vendor directly), regardless of which channel served them. That's useful for comparing channels, but it isn't your bill — check the vendor's dashboard for that.

Web search is the one fixed cost: about ¥0.005 per lookup (billed per request) plus a few hundred tokens to summarize — roughly ¥2–3/month for a typical group, using a cheap model.

Turn search off and chat keeps working; it'll just say "not sure" instead of looking things up. Point it at a local model and cost drops to zero.

What it does

  • LLM gateway (OpenAI-compatible). Ships with DeepSeek account credentials; any OpenAI-compatible endpoint can be added as a custom provider.
  • QQ bridge. OneBot v11 in and out. A decision layer first asks should I speak at all, then the model decides what to say — which is both cheaper and more human than replying to everything.
  • Protocol host. Downloads and supervises NapCat for you, including QR-less quick login.

Modes

ModeWho talks to QQStatus
whale-podWhalePod itself✅ Works
astrbotAstrBot⚠️ Scaffolded, not finished

The AstrBot path has the full skeleton (backend definition, uv install flow, start/stop, mode switching, gateway hint) but has never been verified end-to-end: initArgs is never called, there is no readiness check after spawn, and the OneBot wiring is inferred from the NapCat path. Do not expect it to work yet. Use whale-pod mode.

Can I use a local model?

Yes. Point it at Ollama or any OpenAI-compatible endpoint and chat costs drop to zero.

  1. Run Ollama and pull a model: ollama pull qwen2.5:7b, then ollama serve.
  2. Open the panel → Channels → add a channel → preset Ollama (local). baseUrl http://127.0.0.1:11434/v1, API key left empty, model set to whatever you pulled.
  3. Hit Test connection — it probes /v1/models and then a real /v1/chat/completions.
  4. On the Models page set the routing strategy to self-only, or drag the Ollama channel to the top under free-first.

Any other OpenAI-compatible service works the same way via the Custom endpoint preset (the baseUrl must include /v1). Presets also cover OpenAI, DeepSeek, SiliconFlow, Moonshot, Zhipu, and DashScope.

Two things to get right: give the token limit room (≥ 1024) — reasoning models spend budget before emitting any answer, and too small a limit shows up as an empty or truncated reply; and a 7B-class model is plenty for group chat, since the decision layer already filters out most messages.

Design notes

  • Reuses the host's installed channel pack and credentials instead of reimplementing each vendor adapter.
  • Decision and expression are separate layers — that separation is what makes it feel less like a bot.
  • Data lives in ~/.whale-pod; deleting that directory uninstalls everything.

Requirements: DSH Desktop · Node 22.19+ · a QQ protocol host (NapCat can be installed from the UI)

See the Chinese sections above for configuration details, the full CLI reference, and the API surface.


已知限制

  • reverse WS 未实现 —— 只支持 WhalePod 主动连协议端(forward)。 NapCat/SnowLuma 都支持正向 WS,够用。
  • 图片只记占位 —— 收到图片存 [图片],不送多模态(可扩展)。
  • 托盘需要额外包 —— 未装 systray2 时回退控制台模式,核心功能不受影响。
  • 长期记忆用摘要而非向量检索 —— 见上文取舍说明。
  • Jev 的 HTTP 契约按社区插件描述实现 —— 官方 schema 未公开稳定, 若不符只需改 src/decision/jev.js 的两个方法。

合规

  • WhalePod 不下载、不安装、不修改 QQ 客户端或协议端。
  • 使用第三方协议端(NapCat / SnowLuma 等)请自行阅读其许可与 《QQ 用户协议》。建议使用小号,账号风险自负。
  • WhalePod 本身 MIT 许可,不含任何腾讯专有代码。

许可

MIT