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

QQ 机器人接入 arrow1031-dev/dsh-qq-bot

DeepSeek Harnessv0.12.5
arrow1031-dev3 天前有提交已提供中文文档扫描:含敏感能力

把 QQ 接到 DSH Agent 的插件社区化维护:经 NapCat / OneBot 11 收发消息,每个 QQ…

37.7/100
综合分
4
GitHub 星标
2,448
npm 周下载
—/5
用户评分

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

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add @sputnikzaychik/dsh-qq-bot
npm 源 · @sputnikzaychik/dsh-qq-bot 的发布者账号已校验与本仓库一致

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · platform
装得上吗
本站已在 dsh 0.2.1-rc.2 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 3 天前
档位由哪些信号合成

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

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包@sputnikzaychik/dsh-qq-bot @ 0.12.5
✓Node 引擎要求 >=22.6 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/27 23:43:39

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

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

可执行外部命令读写/删除本地文件发起外部网络请求
查看扫描证据(共 27 处,展示前 27 条)
  • 发起网络请求
    lib/client/settings.js:1401
    return fetch(QQ_BIND_STATE, { credentials: 'same-origin' })
  • 发起网络请求
    lib/client/settings.js:1415
    fetch(QQ_BIND_REFRESH, { method: 'POST', credentials: 'same-origin' })
  • 发起网络请求
    lib/client/settings.js:1724
    return fetch(QQ_SETTINGS_PATH, { credentials: 'same-origin' })
  • 发起网络请求
    lib/client/settings.js:1738
    return fetch(QQ_DEFAULTS_PATH, { credentials: 'same-origin' })
  • 发起网络请求
    lib/shared/http.js:116
    return Promise.resolve(hooks.request({ parsed, addresses, options, headers, payload, maxBytes: limit, binary, deadline, started, left }));
  • 读取本地文件
    lib/tools/moli.js:35
    try { if (existsSync(candidate)) return candidate; } catch { }
  • 读取本地文件
    lib/tools/moli.js:40
    try { if (existsSync(candidate)) return candidate; } catch { }
  • 执行外部命令来自配置/变量
    lib/tools/moli.js:99
    execFile(exe, argv, {
  • 读取本地文件
    lib/config.js:38
    try { entries = readdirSync(dir, { withFileTypes: true }); } catch (error) { return []; }
  • 读取本地文件
    lib/config.js:51
    parsed = JSON.parse(readFileSync(fullPath, 'utf8'));
  • 读取本地文件
    lib/config.js:76
    try { valveEntries = readdirSync(join(HERE, 'valve'), { withFileTypes: true }); } catch (error) { valveEntries = []; }
  • 读取本地文件
    lib/config.js:145
    parsed = JSON.parse(readFileSync(fullPath, 'utf8'));
  • 读取本地文件
    lib/config.js:268
    try { valveEntries = readdirSync(join(HERE, 'valve'), { withFileTypes: true }); } catch (error) { valveEntries = []; }
  • 读取本地文件
    lib/config.js:356
    parsed = JSON.parse(readFileSync(fullPath, 'utf8'));
  • 读取本地文件
    lib/tools/index.js:334
    const buf = readFileSync(imgPath);
  • 发起网络请求
    lib/tools/index.js:1098
    const result = await flowAuthApi.request(purpose, entry, entry === null ? null : entry.currentFrame);
  • 写入本地文件
    lib/session/workspace.js:18
    try { mkdirSync(root, { recursive: true }); } catch { }
  • 执行外部命令固定命令
    lib/session/workspace.js:60
    const out = execFileSync('powershell.exe', ['-NoLogo', '-NoProfile', '-NonInteractive', '-Command',
  • 执行外部命令固定命令
    lib/session/workspace.js:66
    const out = execFileSync('osascript', ['-e', 'POSIX path of (path to documents folder from user domain without folder creation)'],
  • 执行外部命令固定命令
    lib/session/workspace.js:71
    const out = execFileSync('xdg-user-dir', ['DOCUMENTS'], { encoding: 'utf8', timeout: 10000 });
  • 执行外部命令来自配置/变量
    lib/gateway/artifact-collect.js:44
    child = spawn(command, args, {
  • 读取本地文件
    lib/shared/compat.js:90
    if (!existsSync(file)) return null;
  • 读取本地文件
    lib/shared/compat.js:91
    const pkg = JSON.parse(readFileSync(file, 'utf8'));
  • 读取本地文件
    lib/api/update-check.js:19
    return JSON.parse(readFileSync(fileURLToPath(new URL('../../package.json', metaUrl)), 'utf8')).version;
  • 发起网络请求
    lib/api/update-check.js:138
    const res = await fetch(url, { headers: { accept: 'application/json' }, signal: controller.signal });
  • 发起网络请求
    lib/valve/network/guard-proxy.js:293
    upstreamReq = http.request({
  • 发起网络请求
    lib/valve/network/guard-proxy.js:298
    upstreamReq = http.request({
扫描于 2026/11/2(1 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

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

依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/dsh-tools
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 转发QQ消息
  • 自动回复

DeepSeek Harness 插件,适配 QQ 机器人

v0.12.82026-10-30
chore(release): 0.12.8 0.12.8:P3 四项结构重构落地(群管理动作表收归、二维码/默认值一致性测试、gateway/plugin.js 拆到 812 行), 外加归档会话自动召回、删除会话后重建、适配器 spawn 修复与设置页排序修正。
v0.12.72026-10-28
· 0.12.7 —— 稳定性与体验优化(2026-10-28) · 原装抓取工具 moli_fetch 进默认 gatedTools(一道验证墙,不叠第二道) · moli_fetch 是 dsh-moli 注册的原生工具(能跟随跳转、写文件、eval 页面 JS),我们不改它的 实现,也不再在入口套一层公网校验/守卫代理——那是我们自己的渲染分支(qq_webfetch 的 moli 路径)负责的事,访客继续用它、安全由内网拒绝 + 守卫代理保证。 · 原装工具只设一道墙:默认 flowAuth.gatedTools 加上 moli_fetch,开关打开后拥有者私聊 调它要过一次 6 位动态码;没有第二层地址校验,内网地址也由拥有者用自己的码决定要不要抓。 · 名单仍与接管名单同一份(flowGate.names()),所以不需要新增任何遮蔽代码:flowAuth 的接管 块本来就会按名单把 moli_fetch 包一层。 · 测试:test-flow-wiring 加 3 条断言(没码被拦且不调原工具 / 内网目标同样被拦 / 拿码后原样转交)。 · 守卫代理:判定即钉住地址(DNS rebinding TOCTOU) · resolveGuardTarget 把判定阶段解析出的公网地址一并返回;CONNECT 直连与明文 HTTP 直连都用 这个 IP 去连,不再让 net.connect / http.request 按主机名重新解析一次(明文请求保留原 Host 头做虚拟主机路由;上游代理路径不动,目标主机名本来就交给代理侧解析)。 · 起因:0.12.6 的守卫代理是「先 dns.lookup 判公网、再按主机名连接」——两次解析之间的空窗里, TTL=0 的域名可以先答公网骗过判定、再答 127.0.0.1 打进内网,与 0.12.4 在 publicFetch 里封掉的是同一类 TOCTOU。 · 测试:test-guard-proxy 加 4 条断言(判定返回钉住的地址 / 一个目标只解析一次 / 解析出内网 拒绝 / 解析不了保留旧语义)。该套件行数记 38 → 47;实测改动前就是 43(既有漂移), 多出的 5 与新增的 4 都在 PR 正文里说明,没有在本次一并重算所有套件。 · 设置页:preset 三个字段标注「如果你不知道你在做什么,最好别动这个」 · agentPresetCustom / auth.ownerPreset / auth.guestPreset 的 hint 统一加前缀「如果你不知道你在做什么,最好别动这个」,并写清正确用法:加装了不影响安全的第三方扩展插件、却没进任何 preset 的,在自建 preset 的 plugins 里加插件行、再把 id 填到这里,而不是放宽访客白名单。 · 只改文案:字段位置(高级 → 会话与排错)、默认值、分流优先级都没变。 · 设置页新增「闸门区」:名单改动要一次动态码(填码解锁 → 保存即消费) · 设置页「高级」拆出第三个二级标签「闸门区」:flowAuth.gatedTools(哪些工具必须过动态码)与 flowAuth.gateEditCode(改名单用的 6 位码)放一起,不再和 TOTP 绑定/窗口参数混在流程授权里。 · 行为:动态码开着时名单默认锁定;填满 6 位码即解锁编辑,点「保存」才把码一起提交(服务端 用 decideGateEdit 验一次 TOTP,±1 窗口);保存成功即重新上锁并清空输入框,服务端也会把 gateEditCode 写回空串——码不落盘。不保存就不写入;离开设置页,解锁状态与未保存的改动 一并丢弃(都是组件内 state,没有持久化)。 · 开关本身也要码:把「流程授权」从开切到关(或反过来)和改名单一样需要一次码——否则拿到浏览器 的人可以「先关、改名单、再开」,名单保护就白设了;关掉闸门比改名单更狠,不能更容易。 · 服务端兜底:名单或开关变了但码不对(含没码、没配 secret),插件会把两者一起回滚到上一次接受的 值并清空码字段,日志里写明原因——设置页刷新一下就能看到改动静默回退;不会静默接受。 · 测试:新增 test-gate-guard.mjs(18 项:名单比较、关着不需要码、没码/错码/无 secret 拒绝、 对码接受、±1 窗口容差、开关变更要码、名单没变时顺手清码);test-client 断言高级页三个二级标签并覆盖锁定文案。 · 套件 30 → 31,总项 1204 → 1224。只防「绕闸门的人」:填错码只会让改动弹回去,不额外加确认步骤。 · 回复分条:Markdown 感知切分 · 以前长回答只按换行硬
v0.12.62026-10-28
截至 d9d5317(0.12.5)以来的变更:38 个文件,+2156 / −400,新增 lib/guard-proxy.js。 · 新增 渲染抓取 · qq_webfetch 接入 Moli 无头浏览器:可抓取需要 JS 渲染的页面,输出 markdown。引擎未启用或不可用时自动退回普通抓取。 · 新增 lib/guard-proxy.js:渲染期间 moli 的全部流量(主文档、3xx 重定向、页面子资源、页面 JS 发起的请求)经本地校验代理出站,回环与私网地址一律拒绝。判定语义是「确定是内网才拒」,本机解析不了的域名放行、交给上游代理解析。 · 渲染对 QQ 会话内的访客(群聊与别人的私聊)开放,由 moli.allowGuest 与 moli.guardProxy 两个开关共同控制。 moli 配置段(原来只有 enabled / proxy / exePath) | 键 | 默认 | 说明 | |---|---|---| | enabled | false | 总开关 | | exePath | '' | 留空 = 自动探测标准位置与 PATH | | proxy | '' | 留空 = 透传 webSearch.proxy | | defaultTimeoutMs | 30000 | 渲染超时(-t) | | maxChars | 200000 | 单次输出字符上限 | | insecureTls | true | 跳过 TLS 证书校验 | | guardProxy | true | 本地校验代理 | | allowGuest | true | 访客可否触发渲染 | | rateLimitPerMin | 5 | 每会话每分钟渲染上限(0 = 不限) | | maxConcurrentRenders | 2 | 跨会话全局并发上限(0 = 不限) | 设置页 · 新增 Moli 卡片:引擎检测、引擎路径、代理、超时、输出上限、TLS 校验、访客开关、频率限制。 · 卡片内「高级设置」的参数现在可以保存(此前改动不生效)。 · 变更 · auth.guestToolAllow 默认值移除 moli_fetch,访客的渲染需求由 qq_webfetch 的 moli 分支承担。 · 流程授权的 grant 必须携带工具名,空的 toolName 一律拒绝。 · 适配器凭据改从环境变量读取,命令行参数仍然优先。 · flowStore 移除 save 与 verify:流程脚本由使用者自行放入目录,插件既不写入也不执行。 · 换连接配置时等旧适配器真正退出后再启动新的。 · 非 QQ 会话(Web UI、任务板、子代理)仍然不可触发渲染。 · 修复 · 渲染失败不再返回成功:被防护拦下、上游代理不可达、目标 4xx/5xx 均明确报错。 · 守卫代理区分 403(策略拒绝)与 502(连接类失败)。 · dechunkBuffer 对负数 chunk-size 的同步死循环。 · SSRF 判定补齐 6to4(2002::/16)与 Teredo(2001::/32);修正 Teredo 取错分组的问题。 · 代理隧道:直连分支的 30 秒空闲超时、CONNECT 目标归一化、响应头 64KB 上限、请求头 CR/LF 校验。 · 守卫代理:start() 并发返回端口 0、ensureGuardProxy 并发重复创建、server 未 unref()、socks5 上游静默降级。 · OneBot 11 正向 WS 增加半开检测(3 个心跳周期无数据即断开重连);Satori 连续 3 次 PING 无 PONG 改为断开重连。 · writeToAdapter 返回真实写入结果;顺序锁 release 幂等并加 120 秒超时。 · 图片缓存的三条泄漏路径,新增每 6 小时按 mtime 清理。 · 产物打包从 execFileSync 改为异步 spawn;适配器 stdout 加 1MB 上限;cleanupStaleConfig 补异常处理。 · 会话池加上限 200,按最久未用淘汰。 · qq_draw:增加拥有者校验、支持取消、响应体 4MB 上限。 · qq_imgsearch:本地文件白名单增加当前会话维度。 · /grant 禁止自我授权,授出的权限必须自己已经拥有;/revoke 需要 grant 权限。 · 拥有者设置的确认码改用 crypto.randomBytes;恢复码不再受动态码锁定影响。 · 「拥有者私聊」判定改用回合快照,修复 perChatSession=false 时高危工具全部不可用。 · 直连抓取增加绝对 deadline。 · 升级 补丁
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
dsh-qq-bot —— 把 QQ 接到 DSH 把 QQ 机器人接进 DSH:QQ 消息进来 → 交给 DSH Agent → 回复发回 QQ。 - 一套插件接五种协议:OneBot 11(默认)/ OneBot 12 / Satori / Milky / QQ 官方机器人,换协议只是设置里的一个下拉。 - 每个群、每个人都有独立的 DSH 会话,会落盘、能像普通会话一样在 Web UI 里打开继续聊。 - 只有一条运行时依赖:@deepseek-ai/schemastery(DSH 配置 schema 的约定,必须与宿主同版本);其余全部走 Node 22 内置的 WebSocket / fetch,适配器是零依赖子进程。 版本 0.12.8 · 要求 DSH ≥ 0.2.0-rc.2(0.2.1 系列实测通过)设置入口:首页侧边栏 → 插件 → 已安装 → dsh-qq-bot。不再是「设置 → QQ 机器人」,现有配置无需迁移。
展开完整文档(README 剩余 1 节)
dsh-qq-bot —— 把 QQ 接到 DSH

把 QQ 机器人接进 DSH:QQ 消息进来 → 交给 DSH Agent → 回复发回 QQ。

- 一套插件接五种协议:OneBot 11(默认)/ OneBot 12 / Satori / Milky / QQ 官方机器人,换协议只是设置里的一个下拉。
- 每个群、每个人都有独立的 DSH 会话,会落盘、能像普通会话一样在 Web UI 里打开继续聊。
- 只有一条运行时依赖:@deepseek-ai/schemastery(DSH 配置 schema 的约定,必须与宿主同版本);其余全部走 Node 22 内置的 WebSocket / fetch,适配器是零依赖子进程。

版本 0.12.8 · 要求 DSH ≥ 0.2.0-rc.2(0.2.1 系列实测通过)设置入口:首页侧边栏 → 插件 → 已安装 → dsh-qq-bot。不再是「设置 → QQ 机器人」,现有配置无需迁移。
npm 包名 @sputnikzaychik/dsh-qq-bot · CHANGELOG · AI 生成声明 · MIT

它能做什么

| 能力 | 说明 |
| 收发消息 | 群聊(默认要 @)与私聊;回复 bot 也算 @;长回复按换行切成多条 ≤1200 字 |
| 五种协议 | OneBot 11/12、Satori、Milky、QQ 官方机器人,见协议与连接方式 |
| 群管理 | 禁言、踢人、撤回、改名片 / 群名、设管理员、全员禁言;指令与工具共用一套权限 |
| 联网与媒体 | qq_websearch 多引擎搜索、qq_webfetch 抓网页(可走代理)、qq_imgsearch 图片反搜、qq_draw 生图直接发群 |
| 看图 | 收到的图片用视觉模型识别后并入回答;群内近期图片由 AI 按需自取(不硬塞上下文) |
| 主动发言(可选) | 到点看一眼群里聊什么,自己决定要不要接话;判断与发言可分用两个模型 |
| 流程授权(可选) | 子代理 / 队友 / 工作流 / 第三方抓取(moli_fetch)被拦下时,私聊回一次 6 位动态码才放行;改「哪些工具要码」的名单、或开关流程授权本身,也要一次码(设置页「高级 → 闸门区」,填码解锁、保存即消费) |
| 人格 | 每个 QQ 会话可套自定义人格,或指定专属 agent preset |

快速开始

1. 起一个 OneBot 11 协议端(推荐 NapCat)

NapCat 官方 Docker 镜像(支持 amd64 / arm64):

docker run -d --name napcat --restart=always \
-e NAPCAT_GID=$(id -g) -e NAPCAT_UID=$(id -u) \
-p 3000:3000 -p 3001:3001 -p 6099:6099 \
mlikiowa/napcat-docker:latest

- WebUI:http://<主机IP>:6099/webui,手机 QQ 扫码登录;登录 token 默认在 docker logs napcat 里。
- 容器内 /app/napcat/config 与 /app/.config/QQ 建议挂卷,免得重启要重新扫码。
- NapCat 的「网络配置」里开 OneBot 11:HTTP 服务器 3000(发消息)+ 正向 WebSocket 3001(收事件)。端口默认就和本插件一致,不用改。

2. 安装插件

从 npm 装(推荐)
dsh plugin --profile web add @sputnikzaychik/dsh-qq-bot

跟随仓库 main(想用最新、未发版的内容)
dsh plugin --profile web add github:arrow1031-dev/dsh-qq-bot

升级已有安装
dsh plugin --profile web update

--profile 换成你实际在用的:桌面版是 --profile desktop,dsh web 部署一般是 --profile web。
也可以在界面里装:插件 → 添加插件,填上面那行包名。

装完重启 DSH(桌面版重启应用 / CLI 重启 dsh web)。lib/client/settings.js 是浏览器侧代码,重启后还要刷新页面。

3. 在 QQ 里试

- 群聊:默认要 @机器人;回复(引用)bot 的消息也算 @,带图照常识图。
- 私聊:默认直接回。
- 中间发言(默认开):长回合里模型先说的那几句(「我先查一下」)单独发一条、不引用也不 @;最终结论仍按规则引用你的消息。一个回合最多 4 条、两条间隔 2.5 秒,设置页可调,填 0 即关。
- 会话名固定为 群聊_<群号> / 私聊_<QQ号>,不让模型自动起标题(可在设置里关)。

4. 打开设置面板

插件 → 已安装 → dsh-qq-bot,面板分四档:连接 / 基本(准入与名单、发言与管理)/ 能力(识图与绘图、搜索与抓取、对话与人格)/ 高级。
改完点保存:连接项(模式 / 地址 / token)会自动重启适配器,其它项下一条消息生效。

- 白名单是逗号分隔的文本框;布尔是勾选框;provider / model 都是下拉。
- access_token 是只写字段:已设置时显示「已设置;留空则保持不变」,只有输入新值才覆盖。
- 连接状态不在面板里显示,在对话里让 Agent 调 qqbot(action=status)看。

协议与连接方式

| protocol | 协议端 | 传输 | 备注 |
| onebot11(默认) | NapCat / go-cqhttp / Lagrange | 正向/反向 WS + HTTP | 与 0.9 的配置完全兼容 |
| onebot12 | OneBot 12 实现端 | 正向/反向 WS + HTTP | @ 是 mention、图片只有 file_id |
| satori | Koishi(Satori 服务端) | WS /v1/events + HTTP RPC | 消息体是类 XHTML 元素串 |
| milky | NapCat / Yogurt / Lagrange.Milky | /event WS + /api HTTP | 无 echo;消息 id 是 message_seq |
| qq-official | QQ 开放平台机器人 | Webhook 或 WS 网关 | AppID/AppSecret 鉴权;无群管理动作 |

OneBot 11/12 的两种连接模式

| 场景 | 用哪个 | 配置 |
| 协议端与 DSH 同机 / 同容器 | 正向 WS(默认) | mode: forward,我们连 ws://127.0.0.1:3001 |
| 协议端在别的容器 / 只能它连出来 | 反向 WS | mode: reverse,协议端填 ws://<DSH主机IP>:6199/ |

反向 WS 默认监听 0.0.0.0:6199(只有配了 onebot.accessToken 才会真的对外监听:没有 token 时强制退回 127.0.0.1,避免一个无鉴权的 WS 口暴露在局域网上)。
鉴权:请求头 Authorization: Bearer <token> 或 URL 上的 ?access_token=<token> 都接受。
其它协议的连接参数(protocols.onebot12 / protocols.satori / protocols.milky / protocols.qqOfficial)在设置页同名分组里。

能力边界:qq-official 没有踢人/禁言/改名片这类群管理动作,qqgroup 会明确回「不支持」;onebot12 的图片段只有 file_id,要做视觉识别建议用 OneBot 11。

配置

方式一:设置面板(推荐)

见上文「4. 打开设置面板」。几个要点:

- 工作区自动管理:插件读取 DSH 默认工作区位置(<系统 Documents>/deepseek-harness),在同级建 QQ_msg 并注册为 Web UI 侧栏的「QQ 消息」分组,新 QQ 会话自动归入。带图消息的原图缓存在 QQ_msg/qq-images/<会话id>/,会话结算后删除。
- 升级后失效的旧配置会被自动清掉:插件启动时和每次保存后会比对 schema,把本版本已不存在的键摘掉再写回(只动自己那一段,删掉的键连同旧值打进日志)。
- 设置命名空间 = 条目 id(默认 dsh-qq-bot);条目被改名时客户端会回退匹配「以 qq-bot 结尾」的那个。

方式二:改 profile 的 cordis.patch.yml

不要改包内的 cordis.patch.yml(升级会覆盖)。写进你自己的 $DSH_HOME/profiles/<profile>/cordis.patch.yml,按 id 定向覆盖:

- id: dsh-qq-bot
name: '@sputnikzaychik/dsh-qq-bot' # 装 npm 包时用带作用域的包名;装 git 版也写它 config: protocol: onebot11 mode: forward onebot: wsUrl: 'ws://127.0.0.1:3001' httpUrl: 'http://127.0.0.1:3000' accessToken: '' group: enabled: true requireAt: true
allow: [] # 群号白名单,空 = 所有群
private:
enabled: true
allow: [] # 用户号白名单,空 = 所有人
auth:
owners: ['你的QQ号'] # 拥有者:全权限 + 永久授权
reply:
maxChars: 1200
interim: true # 中间发言

完整字段清单(含主动发言、流程授权、生图、图片反搜、moli 渲染等全部配置项)见 DEVELOPMENT.md。

权限与指令

权限是三路并行取并集,任一路给到即拥有(/whoami、/perms 会如实列出来源):

| 路径 | 谁 | 能做什么 |
| auth.owners | 拥有者 | 全部权限 + 永久授权 + 可走独立的拥有者会话。必填:留空时插件不驱动任何会话(fail-closed),私聊会回一句配置指引 |
| auth.superAdmins | 后台设定的高级管理员 | 群管理权限(不能永久授权) |
| 动态授权 | 被临时授权的人 | 仅被授予的项,仅限本群、带时限 |
| 本群身份 | 该群的管理员 / 群主 | 群管理权限(可用 admin.requireGroupAdmin 关掉) |

指令(不需要 @,但前缀必须在消息首位):

| 指令 | 权限 | 作用 |
| /help /whoami | — | 指令表 / 我的身份与权限来源 |
| /perms [@某人] /members /info | query | 权限来源 / 成员列表 / 群信息 |
| /grant @某人 权限[,权限] [分钟\|forever] /revoke /grants | grant | 本群动态授权(永久授权只有拥有者能给) |
| /mute /unmute /kick /recall | mute / kick / recall | 禁言、解禁、踢人、撤回(回复某条再发 /recall 可省 id) |
| /banall on\|off /card /admin /rename | settings | 全员禁言 / 名片 / 管理员 / 群名 |

第一次装好必须先填 auth.owners:留空时插件不驱动任何会话(只在你私聊它时回一句「还没配置拥有者」),这是为了避免「装上即对外开放」——默认白名单为空表示「不额外限制」,不先填拥有者,任何人都能拿它跑模型额度、当抓网页的跳板。填好之后再:打开 admin.dryRun 在群里试几条指令 → 关掉 dryRun,再按需加 superAdmins。
非拥有者默认不能用 DSH 内部工具(白名单 auth.guestToolAllow 之外一律拦),确实要放开再改 auth.guestDshTools。

智能体能用的工具

插件向 DSH 注册 9 个工具(访客只看到白名单里的):

| 工具 | 用途 |
| qqbot | 连接状态 / 主动发消息(可带一张本地图片)/ 中断回合 |
| qqgroup | 群管理:成员、群信息、禁言、踢人、撤回、名片、群名、管理员、戳一戳 |
| qq_websearch | 联网搜索(多引擎组合,走配置的代理) |
| qq_webfetch | 抓网页原文,内网地址一律拒绝;可切 Moli 无头浏览器渲染 SPA |
| qq_imgsearch | 图片反搜(公网 URL 或本地上传),支持 kind=comic 漫画模式 |
| qq_history | 读聊天记录(当前群/私聊直读,跨群按权限),窗口内图片自动识别 |
| qq_draw | 按描述生图并直接发到当前会话 |
| qqconfig | 仅拥有者私聊:在 QQ 里改白名单 / 发言参数等设置(两步确认) |
| qqauth | 仅拥有者私聊:流程授权的申请与状态查询 |

两条路径分工不同:qq_webfetch 的 Moli 渲染分支(含访客可用)由内网拒绝 + 本地校验代理保证安全;dsh-moli 的原装工具 moli_fetch 不在里面,它只设一道验证墙——默认进 flowAuth.gatedTools,开关打开后拥有者私聊调用要过一次 6 位动态码。

细节(每个工具的参数、引擎顺序、失败策略)见 DEVELOPMENT.md。

常见问题

| 现象 | 检查 |
| 日志一直「OneBot 未连接」 | NapCat 的「正向 WebSocket」是否开在 onebot.wsUrl;reverse 模式下 NapCat 是否指向 ws://<DSH>:6199/;防火墙;token 是否一致 |
| 能连上但收不到消息 | OneBot 版本要选 v11;不能只开 HTTP,必须开正向或反向 WS |
| 群里 @ 了也不回 | group.enabled、group.allow 白名单、self_id 是否一致(logAdapterFrames: true 看原始帧) |
| 回「(本轮没有产生文字回复:…)」 | 括号里是 turn/end 的原因,据此定位(模型路由、额度、工具报错) |
| 回「(发得太快了:一分钟内最多 N 条…)」 | 入站限流:同一会话里同一个人每分钟最多驱动 N 个回合(默认 20,设置页「每人每分钟最多几条」)。只有真正驱动回合的消息才计数,群里的闲聊不算 |
| 发送失败 | NapCat 的 HTTP 服务器(默认 3000)是否开着;accessToken;看日志里的「发送失败」 |
| 网页端整个插件报错 / 设置面板空白 | 0.12.1 的已知问题,升到 0.12.2 即可 |
| 日志在哪 | 插件前缀 [dsh-qq-bot],适配器前缀 [dsh-qq-bot]/adapter,都打到 DSH Host 的 stdout |

本插件不与 AstrBot 同时接:AstrBot 也是 OneBot 的消费方,两边一起接会让同一条消息被两个大脑各回一次。对端只要是 OneBot 11 实现(NapCat / OneDisc / Lagrange)就行。

升级与卸载

dsh plugin --profile web update # 升级
dsh plugin --profile web remove @sputnikzaychik/dsh-qq-bot # 卸载

- 升级不丢配置:配置在 profile 的 cordis.patch.yml 里,不随包走。跨版本删掉的老字段会被自动清理。
- 升级后要重启 DSH + 刷新页面(适配器是子进程、客户端是浏览器 bundle)。
- 任一版本的功能变化见 CHANGELOG.md。

更多文档

除本 README 与 CHANGELOG.md 外,其余文档都在 docs/ 下。

| 文档 | 内容 |
| DEVELOPMENT.md | 本地开发与测试(38 套件 / 1476 项)、完整配置参考、工具细节、文件清单、设计取舍、发布流程、部署备注 |
| ARCHITECTURE.md | 分层图、模块职责、关键数据流、身份契约、已知边界 |
| CHANGELOG.md | 0.1.0 → 0.12.8 的完整演进 |
| AI-DISCLOSURE.md | AI 生成方式、验证到什么程度、尚未验证清单(用前务必读) |
| SECURITY.md | 安全模型与漏洞报告方式(别在公开 issue 里贴细节) |
| CONTRIBUTING.md | 贡献流程与代码约定 |

许可与致谢

MIT,见 LICENSE。维护者 @arrow1031,核心贡献者 @nd002723。

- NapCat —— 提供灵感、OneBot v11 接口与文档。没有它把 NTQQ 的能力按 OneBot 规范暴露出来,这个项目根本无从起步。
- dsh-moli —— 本地 Moli 无头浏览器引擎与参数面。qq_webfetch 的渲染抓取(SPA / JS 渲染页、markdown 输出)直接借它的引擎,本插件的 moli 配置项也照它的命令行语义复刻(fetch / -t / -d markdown / --http-proxy / --insecure-disable-tls-host-verification / URL 放在 -- 之后)。
- @tencent-connect/dsh-qqbot —— 官方 QQ Bot 插件与扫码连接 SDK,部分能力参照其公开文档实现。
- 梁圣 —— 忠!橙!

问题、PR、Discussions 都在 arrow1031-dev/dsh-qq-bot;QQ 群 1091766276(报错请带上 [dsh-qq-bot] 开头的日志与复现步骤)。

记忆探索:让 QQ 机器人真的记得住

这一节不是功能清单,也不是一键方案。下面是我们自己在服务器上试的一套「人设 + 长期记忆 + 语义检索 + 压缩不遗忘」的组合,写出来给愿意折腾的人对照。本仓库不提供这套记忆栈的安装脚本;每一层都是独立插件,自己装、自己配、坏了自己修。

三层结构

| 层 | 做什么 | 落地形态 |
| 人设与长期记忆 | 人设卡注入系统提示词;分层记忆(core + topics/*.md);memory_append/read/rewrite/search、soul_read/update | dsh-soul-md 插件;记忆是明文 Markdown,落在 $DSH_HOME/soul-md/memory/<人设卡>/ |
| 语义检索 | 记忆按段切块 → 向量化 → 余弦检索,能命中同义、不同措辞的说法 | 自建 sidecar(Node,见下)+ 一个薄插件注册 memory_vsearch / memory_vindex / memory_vstatus |
| 压缩不遗忘 | 接管压缩摘要器:五段式交接摘要;被压掉的原文按段归档,可回查 | dsh-context-zip 插件;history_segments / history_read / history_search / history_find |

为什么这样分

- 记忆管跨会话(你是谁、喜欢什么、关系如何),压缩归档管本次会话的原文(刚才那句到底怎么说的)。时间尺度不同,谁也别替代谁。
- 语义检索不存新事实,只给 soul-md 的 Markdown 做只读索引——文本永远是唯一事实源,索引坏了删掉重建即可。
- 压缩摘要负责「交接」,不负责「记住」;长期有效的决定需要在压缩后主动提炼进记忆,否则下一次压缩还会再丢一次。

一个最小可跑的语义索引(思路,不是安装包)

sidecar 只需要三件事:切块、embed、余弦。

// 1) 切块:按标题/空行切,合并到 ~400–500 字符 // 2) 向量化:任意 OpenAI 兼容 /v1/embeddings const res = await fetch(EMBED_URL, { method: 'POST',
headers: { 'content-type': 'application/json', authorization: 'Bearer ' + KEY },
body: JSON.stringify({ model: 'BAAI/bge-m3', input: chunks }),
});
// 3) 存成 JSON:{ chunks: [{ topic, text, hash, vec }] } // 4) 查询时 embed(query),对 vec 做余弦,取 topK

我们的做法是让 sidecar 用 fs.watch 盯着 $DSH_HOME/soul-md/memory/<卡片>/,按内容 hash 只重嵌改过的片段;插件侧只暴露一个 HTTP 客户端。向量库是派生物,不是事实源。

把四件事串起来的提示词约定

真正让这套东西「像记得住」的不是向量,而是写进人设卡的几条规矩(我们的原文大意):

1. 压缩后先看五段交接摘要(Goal / Decisions / Current state / Next steps / Anchors)。
2. 细节不凭记忆猜:history_segments 看段号,history_read 读原文,history_search / history_find 搜关键词。
3. 摘要里长期有效的(约定、偏好、关系变化、反复出现的决定)用 memory_append 写进记忆,一次最多两条;一次性细节留在历史里。
4. 重要条目可带「细节见本会话第 N 段」,同会话回查,跨会话用 session_search。
5. 人设、好感、习惯不重复写进摘要;摘要只写这次的工作状态。
6. 写入后想立刻被语义搜到,再调一次 memory_vindex;平时 sidecar 自己重建。

坑与取舍(先看再动手)

- 不提供一键安装:soul-md、context-zip 是第三方插件;向量 sidecar 与薄插件是我们自己写的,未随本仓库发布。三者可独立安装,缺一层也能用,只是各少一块能力。
- embedding 要花钱、要服务:任何 OpenAI 兼容 /v1/embeddings 都行(我们用中转站的 BGE-M3,1024 维);没有 embedding 服务时,memory_search(关键词)仍然可用。
- 记忆是明文:$DSH_HOME/soul-md/memory/ 下就是 Markdown,备份、审查、迁移都直接操作文件;别把密钥写进去。
- 提炼规则决定质量:写入太勤会变成垃圾桶,写入太懒会失忆;我们的阈值是「长期有效 + 有依据」,一次最多两条。
- context-zip 接管的是压缩行:宿主升级后要重跑 install.mjs --check / 安装刷新重定向;桌面版的后端打包在 app.asar 里,安装器解析不到时需要自己从 asar 提一份。
- 上下文是有限预算:记忆注入(injectMaxChars)、召回段、交接摘要都吃 token;两边都要「少而精」,否则互补变成双倍开销。

定位:这套是实验田,不是产品。觉得哪一层值得固化,欢迎单独开 PR 讨论——但别指望一条命令把四层都装好。
预览分享卡片长什么样 ↓arrow1031-dev/dsh-qq-bot
分享 · DeepSeek Harness Hub
arrow1031-dev/dsh-qq-bot

把 QQ 接到 DSH Agent 的插件社区化维护:经 NapCat / OneBot 11 收发消息,每个 QQ…

这是你的仓库?认领条目

回到顶部