DeepSeek Harness Plugin Hub

发布与管理完整 Harness Profiles,发现适合你的插件。

探索

插件目录环境预设文档中心动态

社区

发布插件联系我们报告问题

相关链接

Plugin Hub GitHubDeepSeek Harness 官方项目系统状态隐私说明
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

独立、非官方社区项目,与 DeepSeek 官方无隶属、授权或背书关系。

DeepSeek Harness Plugin Hub
ProfilesPlugins分类动态文档登录管理 Profiles
ProfilesPlugins分类动态文档登录
← Plugins
R

dsh-roost

Roost

ai-chat 插件的 DSH 宿主:把 QQ 消息交给 DSH 会话回答,带一键接入

插件会安装到这里;不确定时保持 web。

npx -y @deepseek-ai/dsh plugin --profile web add github:tdcq9594/dsh-roost#ffd112ae9c678da596118870b9c01722d1bc9d7f
README兼容性版本

兼容性与来源证明

Roost 以 dsh-roost 发布,当前版本为 1.0.1。Plugin Hub 会校验它的 manifest,并保存精确安装来源,便于复现安装结果。

DSH 兼容范围
*
运行环境
web
发布来源
github
Registry 更新时间
2026/11/4

版本

1.0.1stable
2026/11/4
1.0.0stable
2026/11/4
0.13.0stable
2026/11/4
查看其余 74 个版本收起版本
0.12.1stable
2026/11/3
0.12.0stable
2026/11/2
0.11.2stable
2026/11/2
0.11.1stable
2026/11/2
0.11.0stable
2026/11/2
0.10.22stable
2026/10/31
0.10.21stable
2026/10/31
0.10.20stable
2026/10/30
0.10.19stable
2026/10/30
0.10.18stable
2026/10/30
0.10.17stable
2026/10/29
0.10.16stable
2026/10/29
0.10.15stable
2026/10/29
0.10.14stable
2026/10/29
0.10.13stable
2026/10/29
0.10.12stable
2026/10/29
0.10.11stable
2026/10/28
0.10.10stable
2026/10/28
0.10.9stable
2026/10/28
0.10.8stable
2026/10/28
0.10.7stable
2026/10/28
0.10.6stable
2026/10/28
0.10.5stable
2026/10/28
0.10.4stable
2026/10/28
0.10.3stable
2026/10/27
0.10.1stable
2026/10/27
0.10.0stable
2026/10/27
0.9.10stable
2026/10/27
0.9.9stable
2026/10/27
0.9.8stable
2026/10/27
0.9.6stable
2026/10/27
0.9.7stable
2026/10/27
0.9.5stable
2026/10/27
0.9.4stable
2026/10/27
0.9.1stable
2026/10/27
0.9.0stable
2026/10/27
0.8.3stable
2026/10/27
0.8.2stable
2026/10/27
0.8.1stable
2026/10/27
0.8.0stable
2026/10/27
0.7.0stable
2026/10/27
0.5.14stable
2026/10/27
0.5.13stable
2026/10/26
0.5.12stable
2026/10/26
0.5.11stable
2026/10/26
0.5.9stable
2026/10/26
0.5.8stable
2026/10/26
0.5.7stable
2026/10/26
0.5.6stable
2026/10/26
0.5.5stable
2026/10/26
0.5.4stable
2026/10/26
0.5.3stable
2026/10/26
0.5.2stable
2026/10/26
0.5.1stable
2026/10/26
0.5.0stable
2026/10/26
0.4.8stable
2026/10/26
0.4.7stable
2026/10/26
0.4.6stable
2026/10/26
0.4.5stable
2026/10/26
0.4.4stable
2026/10/26
0.4.3stable
2026/10/26
0.4.2stable
2026/10/26
0.4.1stable
2026/10/26
0.4.0stable
2026/10/26
0.3.9stable
2026/10/26
0.3.8stable
2026/10/26
0.3.7stable
2026/10/26
0.3.6stable
2026/10/26
0.3.5stable
2026/10/26
0.3.4stable
2026/10/26
0.3.3stable
2026/10/26
0.3.2stable
2026/10/26
0.3.1stable
2026/10/26
0.3.0stable
2026/10/26
最新版
1.0.1
DSH
*
HMR
重启进程
Tree shaking
未声明可安全裁剪
解包体积
未提供
文件数
未提供
Surface
web
许可证
SEE LICENSE IN LICENSE
发布源
github
GitHub
★ 20
周下载
8,214
最近提交
2026/11/4
查看源码 ↗
README Badge

点击下方 Badge 复制 Markdown,粘贴到 README 即可。

这是你的 Plugin?认领权益 · 优先安全扫描

验证 package.json 声明的 GitHub 仓库,即可管理这个公开页面。认领后,Hub 会优先安排当前版本的安全扫描,并在通过后公开展示结果。

认领这个 Plugin →
报告问题

README

dsh-roost

ai-chat 插件的 DSH 宿主:QQ 消息推过来,交给 DSH 会话回答,再把回答送回去。

让 QQ 机器人从「只会聊天」变成「能用 DSH 的全部能力」 —— 工具、记忆、上下文压缩、主动搭话,都归它。


📚 目录

  • 它是什么(以及不是什么)
  • 特性
  • 安装
  • 一键接入
  • 权限:AI 知道是谁在说话
  • 面板
  • 配置
  • 账号迁移
  • 表情包
  • 目录结构
  • 和 Aurabot 的对比
  • 已知限制
  • 开发与测试
  • 许可

它是什么(以及不是什么)

QQ ⇄ Roost 内核 ⇄ ai-chat 插件 ⇄ POST /inbound ⇄ 本插件 ⇄ DSH 会话 ⇄ DeepSeek
                               ⇄ POST /reply  ⇄

它不碰 QQ。 收消息、整形(语音 ASR / 引用 / 合并转发)、发消息、切段、防吵(冷却 / 配额 / 认领)全在 ai-chat 那边。这边只做三件事:

  1. 起一个固定端口的 HTTP 服务,等 ai-chat 推消息
  2. 把消息喂给对应的 DSH 会话,等 AI 说完
  3. 把回复发回 ai-chat 给的地址

为什么要它:ai-chat 直连模型时,AI 只有「聊天」这一件事。挂到 DSH 上之后,它能用 DSH 的工具(文件、命令)、三层记忆、上下文压缩——这是 adapter: dsh 唯一的价值,也是它比直连贵的地方。


特性

  • 🗨️ 一个 QQ 会话 = 一个 DSH 会话:断线重连、重启之后映射还在,会话不丢
  • 🧭 一键接入:七步向导 + 「一键就绪」,Roost 本体、ai-chat 插件、适配器配置都能自动装
  • 🔐 逐条权限判定:每一句都告诉 AI「谁在说、什么档位」,并按档位收本地工具(不是整群一刀切)
  • 🧠 三层记忆:跨群/本群事实 + 日记(三级衰减 + 关键词/向量检索)+ 知识图谱(实体·关系·结束时间)
  • 🛠️ 记忆工具:给模型 memory_search / memory_note 两把「拉」的手,能主动翻旧账
  • 🌙 凌晨巩固:打分、升降级、到期压缩、抽三元组、提取约定,全在凌晨做,不花白天的钱
  • 🌅 觉醒:约定/想你/随性三类机会打分,到点「问她要不要说」,不是到点硬发
  • 💬 对话收尾:她判断这段聊完了就记一句「收在哪 / 下次怎么接」——不发言、不动记忆。下一轮接得上,刚说完晚安 90 分钟内也不会又冒出来(只做私聊)
  • 🛠️ 发言走工具:qq_send / qq_close 由模型自己调——分几段说、说完要不要退场、一句都不说,都它定。调几次就是几条独立消息(不是拼成一条),不再是一整段文本被动切段
  • 📎 能发文件:qq_send_file 工具,路径直接给(想收紧就开 sendFile.restrictPaths)。发之前先问对面支不支持——老版本 ai-chat 会拒,不做「发你了」却什么都没有的空头承诺。一轮一个、200MB 上限,超过 5MB 自动分片
  • 📣 能 @ 人:qq_send 的 at 参数(填 sender 就是刚说话的人,也可以填 openid)。群里每条消息头带 [昵称 (openid)],标签由宿主拼——模型不碰那串 id 的语法,抄错了也没机会发出去。私聊 / 非第一段 / markdown 关着时会自动降级并告诉模型为什么
  • 🗜️ 上下文压缩:到阈值(群 85% / 私聊 75%)开新会话 + 摘要开场,旧会话保留可回查;压之前那一刻的缓存命中率记在面板和日志里,阈值该往上还是往下调用它判断
  • 📱 侧栏面板:十页仪表盘,状态、记忆、人格、排期、账号迁移、日志全在 DSH 里看
  • 🧬 人格四段式:人格文件分性格 / 开场白 / 说话示例 / 世界书四段。世界书一行一条 触发词 | 内容,命中才注入;开场白给主动搭话一个语气锚;近处指令贴在离历史最近的位置,聊到几十轮人设也不被冲淡
  • 🔄 人格库一键更新:人格放在独立仓库 dsh-roost-personas,改完推一个 commit,面板点一下就拿到——不用发版。只覆盖没改过的,你改过和自建的一个不碰
  • ⬆️ 本体自更新:设置页顶部查最新版、一键装。走 DSH 自己的 pluginManager(失败会回滚 package.json + pnpm-lock.yaml),装完提示重启。开发机(符号链接装的)自动禁用
  • 🎭 双人格 preset:私聊(roost-private)/ 群聊(roost-social)各一套提示词
  • 🖼️ 表情包:图丢进目录就能用,文件名或侧车 .tags 当标签;图多了建子目录分类,sticker_pick 按需取图——加图不会冲掉提示词缓存
  • 🔊 语音合成(选装,默认关):四种 provider 任选 —— 本地模型(一键装 23MB 引擎 + 30MB 模型,完全离线)、微软在线朗读(免费、不要 Key,一键装 0.4MB 依赖)、OpenAI 兼容 、本地 GPT-SoVITS 原生 API。,切换时不用重填。不配就完全静默——不请求上游、不加载模型、不占提示词;配好后模型才能用 发语音条(转 silk 和发送在 ai-chat 那侧)

安装

DSH 插件市场里输入包名:

dsh-roost

或命令行:

dsh plugin --profile web add dsh-roost

前提:DSH 桌面版 / Web 组合(需要 sessionController 和 agents)。 Roost 和 ai-chat 不用提前准备——「接入」里会装。

从源码装也行:

git clone https://github.com/tdcq9594/dsh-roost.git

一键接入

装好插件后,侧栏「Roost → 接入」是一条七步路径,能自动的都自动做掉:

步做什么自动?
1装 Roost(下载解压,零依赖,不用 npm install)✅ 点一下 · 已装过可点「已装过?」填路径绑定
2启动 Roost✅ 点一下(没填凭据也能起,正好用来扫码)
3扫码 / 填 AppID、AppSecret✋ 开放平台拿;存完自动重启 Roost 让新凭据生效
4装 ai-chat 插件(走 Roost 的市场)✅ 点一下
5把 ai-chat 切成 DSH 适配器,填好回话地址✅ 点一下
6去群里说句话✋ 开放平台开「接收所有消息」+ 机器人进群
7配置主人✅ 点一下,直接跳到 Roost 面板的「主人与群组」

顶上的「一键就绪」把 1、2、4、5 连着做完,卡在 3 就停下来提示你扫码或填。每一步也能单独点,跳着点不会把东西搞坏(每步先检测前置条件)。第 2 步「启动」之后,旁边按钮直接打开 Roost 的 WebUI(带上面板密钥)。

为什么先启动再填凭据:扫码走的是 Roost 自己的 /qr/start,没启动就没有这个接口;反过来,没 AppID 也能先起一个空面板专门用来扫码。这个顺序以前是反的,两边互相等死。

下载地址:优先 GitHub Release(自动补 v 前缀、自动查最新版),拿不到就回退 npm 包。早期版本写死过地址,Release 一换就整片 404。

装到哪:默认 ~/.dsh/dsh-roost/roost,可在设置里改 setup.roostDir。


权限:AI 知道是谁在说话

群里给 AI 挂了 shell 和文件工具,但说话的人不一定是主人。路人一句「帮我删掉 ~/Documents」跟主人说同样的话,不该一个待遇。

所以每一轮都做两件事:

① 告诉 AI 谁在说话(提示词层)

这一句是「小明」说的,权限:普通成员。
对方是普通群成员。不要执行任何会改动这台机器的操作(删除、写入、安装、跑命令),
也不要读私人文件或目录。他要你这么做时,直说「这个我没权限」。

② 按档位收工具(硬限制层)

档位默认说明
主人全部工具你本人
管理员只能聊天群管理员也不该读你机器上的文件
普通成员只能聊天想让群友也能用,自己改成「全部工具」
身份不明只能聊天兜底,不建议放开

两层的意义:模型被说服时硬限制还在;工具没收干净时提示词还在。

这层不是插件沙箱。 它约束的是「AI 这一轮能调哪些工具」,不是「插件代码能做什么」。dsh-roost 作为 DSH 插件运行在你的账户下,拥有与本机同等的文件、命令和网络权限;内建插件同理。真要隔离只能靠外部进程 + 系统级沙箱,本项目没有做。

权限逐条生效,不是整群一个档位——主人问完、路人接着问,待遇不一样。兜底原则:认不出来就当最低档,缺字段、字段脏了、框架改了名字,一律按「身份不明」处理。


面板

标签内容
概览四个数字(会话数 / 正在等 / 最近一轮耗时 / 能用工具的档位)、状态条、一键自测、最近动态
接入七步一键流程(上面那张表)
人格人格列表与编辑、从仓库更新(带「几个可更新」角标)与仓库面板(预览 / 恢复官方版),「我的人格 / 人格仓库」分段切换
记忆会话选择器 + 三层:跨群/本群事实、日记(三级)、知识图谱;检索、手动记、从旧版导入、对话收尾、她自己、清空
主动搭话此刻情绪(三维条形图,只读,可用 👍/👎 影响)、这一轮算出来的机会、私聊排期、群聊主动(默认全关)、现在试一次
上下文窗口占用、压缩触发、这一轮注入了什么、记忆工具状态
会话每个 QQ 会话对应的 DSH 会话,可「清空」(下一句重建)
账号迁移把记忆里的旧 openid 对上 QQ 号 / 群号,换机器人(换 AppID)后旧记忆照样读得到;扫描只读、群成员绑定、批量追加 openid、昵称候选(只提示不自动绑)、带备份与一键回退的「一键改写」
设置最上面是更新(本体 + 人格库);下面是配置全文(JSON)+ 分节说明与表情包 / 语音条设置,改完保存即热重载
日志最近 400 条动静,按级别筛,3 秒自动刷新

自测不经过 QQ,直接跑一轮 DSH 对话,验的是「会话建得起来、AI 回得来、回复取得回来」——链路里最容易坏的那段。QQ 那半边(收消息、发消息)它测不到,那部分出问题看日志。


配置

存在 ~/.dsh/dsh-roost/config.json(可用 DSH_ROOST_DATA 改目录),面板「设置」页直接编辑。

{
  "server": {
    "port": 17321,          // ai-chat 的「DSH 地址」默认指向这里,两边要一致
    "token": ""             // 共享密钥;留空不校验(只绑本机时可行)
  },
  "chat": {
    "turnTimeoutSec": 150,  // 要小于 ai-chat 那边的 180 秒
    "upstreamRetry": {       // 上游 502/429/超时时的补重试
      "enabled": true,
      "attempts": 1,         // 补几次(1 = 失败后再补 1 轮,总共 2 轮)
      "delayMs": 3000,       // 补之前等多久
      "budgetSec": 170       // 整轮总预算,必须小于 ai-chat 的 180
    }
  },
  "workspace": {
    "dir": ""               // QQ 会话挂哪个工作区:留空=单开 <数据目录>/workspace,
                            // __default__=用 DSH 默认工作区,其它值=自定义目录
  },
  "permissions": {
    "owner": "all",         // 主人:全部工具
    "admin": "chat",        // 管理员:只能聊天
    "member": "chat",       // 普通成员:只能聊天
    "unknown": "chat"       // 兜底:认不出身份只给聊天
  },
  "setup": { "roostDir": "", "version": "v0.8.6", "registration": "qr" },
  "memory":  { /* 日记寿命、注入条数、多少轮全量重注、记忆工具开关、向量检索 */ },
  "compact": { /* 群聊/私聊两档压缩阈值、开场带几条原话 */ },
  "awake":   { /* 清醒时段、机会分门槛、群白名单 */ },
  "speakUp": { /* 私聊排期:间隔/免打扰/每日上限 */ },
  "persona": { "current": "default", "byChat": {} },
  "stickers": { "enabled": true, "dir": "" },
  "tts": {                  // 选装,默认关;面板「设置 → 语音合成」有专用表单
    "enabled": false,
    "provider": "openai",   // sherpa(本地离线)| edge(微软在线)| openai | gpt-sovits
    "speed": 1,             // 四个 provider 共用的语速,0.5–2,1 是原速
    "baseUrl": "",          // openai: 含版本前缀,如 https://api.openai.com/v1
    "apiKey": "",           // 面板里显示成 ******,原样保存不会覆盖真值
    "model": "",            // openai: tts-1
    "voice": "",            // openai: alloy
    "timeoutSec": 30,
    "maxChars": 300,        // 太长的会被 QQ 语音条截断,不如发文字
    "sherpa": {             // provider=sherpa 时才用;引擎和模型都在数据目录,删目录即卸载
      "model": "aishell3",  // 30MB / 8kHz / 174 个音色
      "modelDir": "",       // 留空 = 数据目录/tts-models/vits-icefall-zh-aishell3
      "sid": 88,            // 说话人编号(0-173),换一个就是换一个音色
      "numThreads": 2
    },
    "edge": {               // provider=edge 时才用;不用填地址和 Key
      "voice": "zh-CN-XiaoxiaoNeural"
    },
    "gptSovits": {          // provider=gpt-sovits 时才用
      "installDir": "",     // 含 api_v2.py 的那层,填了才能在面板一键启动
      "model": "",          // 扫描出来的角色名;留空 = 用 tts_infer.yaml 里已加载的
      "gptWeights": "",     // 手填权重路径,优先于按角色名自动配对
      "sovitsWeights": "",  // 两个都填最稳;只填一个时面板会警告另一半仍是猜的
      "baseUrl": "http://127.0.0.1:9880",
      "apiKey": "",         // 走反向代理时才填;本地原生 API 留空
      "referWavPath": "",   // GPT-SoVITS 那台机器上的路径
      "promptText": "",     // 必须与参考音频逐字一致
      "promptLanguage": "中文",
      "textLanguage": "中文",
      "apiVersion": "v1",   // v1=api.py / v2=api_v2.py
      "endpointPath": "/"
    }
  },
  "channel": { "current": "qq" }
}

两边的地址要对上:ai-chat 的「DSH 地址」= http://127.0.0.1:<port>。 server.token 非空时,dsh-roost 会把它同步到 ai-chat 的「共享密钥」—— 不同步的话 /inbound、/capabilities、/tts 全是 401,而两边都不会主动说「密钥不对」。

本地语音(provider=sherpa):面板点「一键安装」,自动下 23MB 引擎(sherpa-onnx-node)

  • 30MB 模型(aishell3),解压后一共约 230MB,装到 数据目录/tts-runtime 和 数据目录/tts-models。 不进插件目录——DSH 更新插件不会冲掉,卸载就是删这两个目录。
  • 那 230MB 里有 173MB 是 rule.far(多音字规则库)。它让「行长/银行」「2024年」这类读得准,所以留着
  • 解压用系统自带的 tar(Windows 10 1803+ / Linux / macOS 都有)。没有 tar 时会明确报错,不会假装在装
  • GitHub 下不动时,手动下 vits-icefall-zh-aishell3.tar.bz2 解压到 tts-models/vits-icefall-zh-aishell3/ 即可,面板会认
  • 首次加载模型约 5 秒,之后合成 6 秒语音约 130ms。装完会自动预热,平时启动也会在后台预热一次
  • sid 是说话人编号(aishell3 有 174 个),换一个就是换一个音色

GPT-SoVITS(provider=gpt-sovits):面板填一个安装目录,点「一键启动」就行。

  • 自动认目录:找 api_v2.py / api.py、runtime\python.exe,从 tts_infer.yaml 读出当前加载的角色和版本
  • 扫描成下拉:把 GPT_weights_* / SoVITS_weights_* 里的权重按角色名配对,同名的取最新 epoch,做成「角色」下拉;参考音频也扫出来做成下拉。选中的角色在服务就绪后通过 /set_gpt_weights + /set_sovits_weights 切过去,不改你的 tts_infer.yaml
  • 自动挑端口:从 17860 往上试绑。不要用 9880——它落在 Windows 保留段 9861–9960 里,绑上去是 WinError 10013
  • 就绪后自动把 baseUrl / endpointPath / apiVersion 写进配置,不用手抄
  • 参考音频只在你没填的时候才替你选;填过就尊重原值
  • 语种填「中文」「日文」就行,会自动转成 API 要的 zh / ja。v2 的字段是 text_lang / prompt_lang,v1 才是 text_language
  • 加载模型要一两分钟,期间面板会显示日志尾部;插件重载时会把进程一起带走
  • 选的就是配置文件里已加载的角色时会跳过切换:v2 切一次权重等于重新加载一遍模型,没必要白等一轮。切换失败时面板红字写明「现在实际生效的是谁」,不会显示「正常」却放着别的音色
  • 音色不对时优先手填权重:自动配对靠文件名猜角色名,权重一多(比如十几个)很容易把不同 epoch / 不同角色的 GPT 和 SoVITS 配错一对。填了 gptWeights / sovitsWeights 就以它为准;只填一个时面板会警告另一半仍是自动配对的。另外参考音频也要和模型配套,音色主要由它决定

微软在线朗读(provider=edge):不用填地址、不用填 Key,选一个音色就能用。

  • 依赖是三个纯 JS 包(约 0.4MB),点「一键安装依赖」装到 数据目录/tts-edge/node_modules——不进插件目录,DSH 更新插件不会冲掉;也刻意和本地模型的 tts-runtime 分开,两边不会互相删
  • 输出 mp3,宿主自己转 silk。要念的文本会发到微软的服务器,介意隐私就用本地模型
  • @andresaya/edge-tts 是 GPL-3.0,只下载到你自己的数据目录,不随插件分发
  • 卸载本地模型后不会再刷「预热失败」:没装是正常状态,装了但加载不起来才是故障

语速:tts.speed 一份配置,四个 provider 共用,范围 0.5–2。

provider映射成
本地 sherpa推理参数 speed
OpenAI 兼容请求体 speed(只在非原速时带,避免被中转服务拒)
GPT-SoVITS v2speed_factor;v1 接口没有语速参数,不支持
微软朗读rate: '+20%' 这种百分比字符串

以前只有 sherpa.speed,现在统一到 tts.speed;老配置会自动迁移,不用手动改。


账号迁移(v1.0.0)

QQ 官方事件只给 openid(群是 group_openid),从来不给 QQ号。换机器人、 换 AppID 之后 openid 就变一次,旧记忆还在盘上却再也没人去找它。 这一页把号码和 openid 对上——读取时合并,存量数据一个字节不动。

绑定完全自愿、非必需:QQ 官方接口不提供 QQ号 / 群号,本项目不会以任何方式获取、推算或抓取;号码只来自用户本人主动提供,或由管理员依据本人提供的号码代为登记(应事先取得本人同意)。不绑定不影响任何功能,未绑定时行为与上线前完全一致;绑定信息仅存本机 identity.json,可随时解绑 / 停用,不上传、不共享,也不用于「认人认群、找回旧记忆」以外的用途。请勿把提供号码设为使用条件,也不要替他人报号;昵称候选只提示不自动绑,正是为了不替用户猜身份。

分区干什么
01 待迁移本机数据里有记忆、但还没绑过号码的 openid 键
02 已绑定Roost 身份表里的号码;给已有号码追加新 openid(旧的一条不删)
03 群里的人群发言人的 QQ号。会话键里没有他们(除非开过私聊),数据来自 Roost 的 unresolved.users
04 批量追加选一个号码 → 勾多个新 openid → 一次提交。换机器人后让新旧 openid 都指向同一个号码
05 一键改写把某号码名下所有旧键合并到号码键后删掉,把结构改干净

昵称候选只提示,绝不自动绑:官方不给 QQ号,uin 是从绑定表反查的, 按昵称 / 群名猜出来的「这个人可能是 10001」没有任何权威依据,绑错会把 别人的记忆串过来。候选只帮你少打字,必须人工确认。

⚠️ 一键改写是唯一会重写记忆数据的操作

前面所有操作都只写 identity.json(叠加层),这一个会真的改 memory.json / sessions.json / journal.sqlite / graph.sqlite。 执行前必须:

  1. 先停掉旧机器人——它还跑着就会继续往旧 openid 写记忆,改完又多出一个键
  2. 点「预览改写清单」,核对要动的文件(绝对路径)、每个键的条数
  3. 勾选「已停掉旧机器人」+ 原样输入「改写」,两样齐了才能执行

执行时自动整文件备份到 D://WorkBuddy//_archive//2026-11-04-账号改写//<时间戳>// (这个盘写不了时退到 数据目录/rewrite-backups),失败自动回退; 成功后「可回退的备份」列表里仍可一键回退。改写和回退成功后页面会重新拉数据, 上面「待迁移」列表里那些键会消失。


表情包

往里丢图,她自己在合适的时候配一张。不发明新机制:发的还是 <<image: 路径>>, 这边只负责让她知道「有哪些图、这些图是什么意思」——她不认识文件名以外的东西。

图放在 <数据目录>/stickers/(面板「设置 → 表情包」能看到路径和字数,也能用 stickers.dir 改)。

最省事:平铺

所有图丢在根目录,文件名就是标签:

stickers/
  开心_猫.png        → 标签「开心」「猫」
  无语-2.gif         → 标签「无语」(序号被丢掉)
  联动壁纸 (1).png   → 标签「联动壁纸」(括号里的序号不算标签)

分隔符认 _ - 空格 ( ) () · 、 , .,纯数字当序号丢弃,最多取 4 个。

文件名不够用时:侧车标签

中文文件名没有分隔符就拆不出词(熊猫开心.png 只能得到一个整串标签), 这时候在同目录放一个同名 .tags 文件,里面只写一行(海妖.png.tags 就对应 海妖.png):

海妖, 立绘; 提到深海、怪物时
↑标签       ↑场合(`;` 之后都算,会原样给模型看)

侧车优先于文件名。这是给图补语义最省事的办法——不用改名,也不动原文件。 标签最多 6 个(走文件名那条路最多 4 个)。注意 .tags 里没有注释语法, 写 # 会被当成标签的一部分。

图多了:分类

在 stickers/ 下建一级子目录,目录名就是分类:

stickers/
  开心/
    _desc.txt      ← 一句说明,写「高兴、庆祝的时候用」
    a.png
  无语/
    b.png
  零散.png         ← 根目录的图归「未分类」,不会消失

有分类之后,提示词只列「分类名 + 张数 + 说明 + 最多 3 个示例文件名」, 具体文件由 sticker_pick 工具按需给——图加到几百张,提示词也不会跟着涨。 每类留 3 个示例是兜底:万一工具没挂上(DSH 版本差异),模型还能直接拼路径发图。

她会怎么选

  • 平铺:清单里有每张图的文件名和标签,她自己判断该不该用
  • 分类:先调 sticker_pick("开心") 拿 1~3 个真实路径,再用 <<image: 路径>> 发
  • 两种模式都会告诉她「没有合适的就别发」——宁可不配图,不要为了配图硬凑

同一张图不会连着给两次(按「最久没给过」排序);会降级成文件的图排在后面。

几个硬限制

限制为什么
超过 200MB 的图不进清单官方硬限,分片上传也发不出去
超过 20MB 的图仍可用,但会标 degrade超过软限会被降级成文件发送,群里看到的是附件不是表情
一次最多扫 1000 张扫描是同步 IO,几千张会卡住消息处理;超出部分清单里会写「只扫了前 N 张」
清单有 3 秒缓存丢图进去最多 3 秒后才进提示词

发出去是什么样

是普通图片消息,不是 QQ 收藏表情——对方收到的是图,不会进他的「表情」收藏夹。 这是协议层的限制,插件改不了。


目录结构

src/
  host.js        插件入口:起服务、会话映射、面板接口、记忆工具挂载
  server.js      HTTP /inbound(鉴权、立刻 200、回话)
  inbound.js     payload → 给 AI 看的一段话
  turn.js        跑一轮:投递 + 从 session/event 等结果
  sessions.js    chatId → DSH 会话(存盘)
  permissions.js 权限:谁在说话、能用什么工具
  setup.js       一键流程:下载解压、写配置、起停、装插件、配适配器
  config.js      配置读写(默认值 + 合并)
  memory.js      三层记忆第一层:跨群/本群事实
  journal.js     日记:三级衰减 + FTS5 关键词 + 可选向量检索(RRF 融合)
  graph.js       知识图谱:实体 / 关系(双时态) / 原始记录
  embed.js       可选向量:只接一个 /embeddings 端点,不配就纯关键词
  tokenize.js    中文切词(索引侧存二元组,FTS5 对中文是废的)
  consolidate.js 凌晨巩固:打分 → 升降级 → 到期压缩 → 抽三元组 → 提取约定
  personas.js    人格库:本地 personas/*.md 的读写(正文分四段)
  persona-repo.js 人格仓库:拉 index.json、一键更新、恢复官方版、静态扫描
  update.js      本体自更新:查最新版、装法检测(符号链接禁用)、豁免 24 小时冷却
  version.js     版本号比较(三处共用,按数字比不按字符串比)
  awake.js       觉醒:约定/想你/随性三类机会纯函数打分
  compact.js     上下文压缩:读占用 → 开新会话 + 摘要开场
  channel.js     通道抽象(以后接别的平台时切这里)
  registration.js 接入方式抽象(扫码 / 手动填)
  lease.js       端口租约:端口被占时自动换
  sync-retry.js  「等一下再试」的退避调度(Roost 比我们晚起时用来补同步 ai-chat 地址)
  tools.js       记忆工具(memory_search / memory_note)定义与挂载
client/
  client.js      侧栏面板(React,沿用 DSH 主题变量)
cordis.patch.yml 两档 agent preset(私聊 / 群聊)
personas/        内置人格镜像(8 份)+ index.json(生成物)。权威源在独立仓库 dsh-roost-personas
scripts/         生成人格索引、dry-run、同步 GitHub
docs/            设计文档、调研、和 Aurabot 的对比

核心技术点:DSH 没有「发一句返回一句」的接口,是事件驱动的。turn.js 监听 session/event,收 assistant/message(可能来多次,取最后一次)和 turn/end(reason.kind === 'completed' 才算说完)。


和 Aurabot 的对比

角色设计参考了 Aurabot(dsh-aurabot-qq):一个 QQ 会话对一个 DSH 会话、人格独占系统提示词、安全默认、按会话挂东西、用 agent.followup + sessions.flush 投递、从 session/event 取回复。

传输层完全不同:Aurabot 走 OneBot(SnowLuma,hook 本机 QQ 客户端),这里走 ai-chat 插件(Roost 官方接口)。所以 Aurabot 里那批 OneBot 特有的东西(数字 QQ 号、base64 语音段)这里一个都不需要。

一句话:传输层我们更稳(官方接口),对话体验它更成熟。 只想用现成的,先去试试 Aurabot。


已知限制

  • 只支持 QQ(channel.current = 'qq'),通道抽象已备好但还没接第二个平台
  • 语音靠官方 ASR(asr_refer_text),没有本地语音模型;TTS 音色由 ai-chat 设置,面板只读
  • 面板是 JSON 直编辑配置(不是逐字段表单),代价是要看得懂结构——所以配了分节说明
  • 模型服务挂了插件救不了:DeepSeek 网关 502 时,补重试能救回一部分,但网关整体挂掉时只能等它自己恢复。这种时候插件回一句「模型服务那边暂时抽风了」,不会把内部报错糊给你
  • 表情包发出去是普通图片消息,不是 QQ 收藏表情——对方不能一键存进表情夹(协议层限制)

开发与测试

npm test

1133 条测试,覆盖整形、会话映射、HTTP 契约(立刻 200 / 鉴权 / 健康检查 / SSRF 防护)、一轮的状态机、权限分档与兜底、记忆三层、觉醒打分、压缩、通道/接入抽象、工具定义与权限(含 at 参数的四条降级路径)、工具 schema 是否落在 DSH 支持的子集里(缺 additionalProperties 会注册即被拒)、发文件(路径白名单、通道协商、大小上限、一轮一个)、人格注入预算裁剪与中文 id / 角色卡自报名字、上游错误分类与补重试、会话类型标签(群 / 私聊)、配置保存与端口候选顺序、前端十页空数据渲染、人格页合并清单(已装在前 / 未装可直接装)、自更新的返回契约(applied / restart-required / refused、错误码与 pnpm 失败分类)、表情包(标签解析、侧车、分类、挑图去重、扫描上限)。

CI(GitHub Actions,Node 22 / 24 矩阵)跑同样这些,外加三件单测覆盖不到的:

  • node --check 全量语法检查(TDZ 这类运行时错它查不出,但拼错、少括号能拦住)
  • scripts/dry-run.mjs 用假 ctx 真跑一遍 apply(),确认插件能加载、路由能注册
  • npm pack --dry-run 检查 files 字段没漏 src/host.js / client/client.js——打包漏文件,用户装上去就是坏的

许可

沿用 Roost 的许可:永久免费,禁止任何形式的收费与商业使用。全文见 LICENSE。

/audio/speech
语速一份 tts.speed 四个 provider 共用
<<say: 内容>>
  • 🔑 账号迁移:把 QQ号 / 群号和 openid 对上,换机器人(换 AppID)后旧记忆照样读得到。支持批量追加 openid、群成员 QQ号登记(需本人提供)、昵称候选提示,以及「一键改写」——把某号码名下所有旧键合并到号码键后删掉(带备份与一键回退)。昵称候选只提示不自动绑:官方不给 QQ号,自动猜会把别人的记忆串过来。绑定完全自愿、非必需,不绑定不影响任何功能(详见「账号迁移」章节的声明)