dsh-otherbot-radar
DeepSeek Harness(DSH)插件。让 QQ 群里的 agent 听见别的机器人在说什么。
QQ 官方平台默认只把「有人 @ 本机器人」的消息推过来,群里其他人、其他机器人的发言 既不推送也读不到。本插件在 SDK 入站层装一层雷达:把平台实际推送的每一条都记下来, 按策略唤醒 agent,并把别的机器人的发言整理成可回看的转录。
目录
- 1. 功能
- 2. 背景:为什么默认收不到
- 3. 兼容性与依赖
- 4. 安装
- 5. 配置
- 6. 工具参考
- 7. 收件口(桥)协议
- 8. 诊断与排障
- 9. 已知限制
- 10. 开发
- 11. 许可证
- English Overview
1. 功能
| 能力 | 说明 |
|---|---|
| 入站雷达 | 包装 SDK 的 handleInboundMessage,平台推来的每一条入站消息都落盘 radar.jsonl,含被门控挡掉的。「为什么收不到」因此可查:是平台没推,还是被策略挡了 |
| 唤醒策略 | 别的机器人说话即唤醒 agent(无需 @),同一机器人 10 分钟内最多 10 次以防两个机器人互相刷屏;人类消息按 wakeOnHuman 决定是否唤醒 |
| 转录与回看 | 别的机器人的发言单独写入 transcript.jsonl,并提供 otherbot_recent / otherbot_diag 两个工具 |
| 收件口桥 | 平台不推送时,任何能写文件的进程把消息丢进 inbox/*.jsonl 即可实时唤醒;agent 的回复写入 outbox.jsonl 由递话方读走 |
| 自回声识别 | QQ 有时会把本机器人自己发出的消息推回来;按「已发出消息的 id + 正文」识别并丢弃,避免自问自答 |
| 提示词注入 | 向 agent 注入一段说明,告知群里有别的机器人、以及如何查询 |
2. 背景:为什么默认收不到
QQ 官方机器人平台按事件类型推送,默认只推「有人 @ 本机器人」的那一类:
| 事件 | 推送条件 |
|---|---|
GROUP_AT_MESSAGE_CREATE |
有人在群里 @ 本机器人 —— 未开全量消息时只有这一种 |
GROUP_MESSAGE_CREATE |
群内每一条消息 —— 需要群里给机器人开启「接收全量消息」(官方文档) |
开关位置:不在 q.qq.com 后台,而是群主在手机 QQ 里给机器人开启 —— 用最新版手机 QQ、 以群主身份添加机器人入群时点「设置权限」→ 勾选「设置接收全量消息能力」 (官方 FAQ)。
开启全量消息后,平台会推送其他机器人发出的消息(author.bot = true),未被过滤。
随之而来有两个必须处理的细节:
- @ 消息也会以
GROUP_MESSAGE_CREATE送达。开启全量后事件类型无法区分「有没有 @ 本机器人」, 只能读mentions[].is_you(SDK 的mentionGate判断方式相同)。 - 同一条消息可能被推两遍(同一个
messageId)。原中间件链的去重会挡掉第二遍, 雷达则如实记两行,便于核对。
3. 兼容性与依赖
| 项目 | 要求 | 说明 |
|---|---|---|
| DSH | 已安装并启用 QQ 机器人插件 | 本插件钩住该插件使用的 SDK |
@tencent-connect/dsh-qqbot |
>= 0.5.0 |
可选 peer 依赖 |
| Node.js | >= 22.13.0 |
|
| 宿主 SDK | @tencent-connect/qqbot-nodejs |
包装其 QQBot.prototype.handleInboundMessage / send / sendMedia |
本插件不发起任何网络请求,不接触 QQ 开放平台凭据;它只在进程内读取已经送达 SDK 的入站事件。
4. 安装
4.1 通过 DSH CLI 安装
dsh plugin --profile desktop add dsh-otherbot-radar
# 或从源码安装
dsh plugin --profile desktop add github:ChencLi819/dsh-otherbot-radar
安装后重启 DSH(或等待热重载),新会话中即出现 otherbot_recent / otherbot_diag。
4.2 从本仓库安装(link:)
node tools/install.mjs # pnpm add link: + 写入 profile 的 bundles
node tools/smoke.mjs # 离线自检(不连 QQ,未安装也能跑)
4.3 link: 软链下的 ESM 解析问题
以 link:(软链)方式安装时有一个坑,务必了解:
Node 默认按 realpath 解析模块,因此插件运行时的 import.meta.url 指向工作区路径。
从工作区出发执行 import('@tencent-connect/qqbot-nodejs') 会沿工作区向上查找,
找不到 profile 里的那份副本 —— 表现是插件已装但收不到任何消息。
lib/tap.js 的 loadSdkModule() 处理了这一点:常规解析失败时,直接在
~/.dsh/profiles/*/node_modules/ 下定位该包并用 file:// 导入。
导入的是同一个文件,因而拿到的是同一个模块实例(也就是 QQ 机器人插件实际使用的那个类)。
此外插件目录内有一个 node_modules/@tencent-connect/qqbot-nodejs 的 junction 兜底
(仅对本机有效)。tools/probe-tap.mjs 会检查这两条路径。
排障口诀:插件已装但 radar.jsonl 始终不出现 → 先跑 tools/probe-tap.mjs。
5. 配置
默认值定义于 index.js 的 DEFAULTS。在 profile 的 cordis.patch.yml 中按需覆盖
(仅覆盖写出的字段):
- id: otherbot-radar
config:
wakeOnOtherBot: true
wakeOnHuman: all # all(默认)/ mention(只在 @ 时唤醒,防刷屏)
otherBotBurstLimit: 10
otherBotBurstWindowMinutes: 10
bridge:
enabled: true
dir: ~/.dsh/otherbot-radar/inbox
pollMs: 3000
promptEnabled: true
| 配置项 | 类型 | 默认 | 说明 |
|---|---|---|---|
enabled |
boolean | true |
插件总开关 |
wakeOnOtherBot |
boolean | true |
别的机器人说话是否唤醒 agent(false = 只记入上下文) |
wakeOnHuman |
string | 'all' |
人类消息唤醒时机:all 群内消息都唤醒 / mention 仅在被 @ 时唤醒 |
otherBotBurstLimit |
number | 10 |
防跑飞:同一机器人在窗口内最多唤醒几次(<= 0 不限) |
otherBotBurstWindowMinutes |
number | 10 |
上述窗口长度(分钟) |
stateDir |
string | ~/.dsh/otherbot-radar |
状态目录:转录、原始日志、收件口 |
bridge.enabled |
boolean | true |
是否启用文件收件口 |
bridge.dir |
string | <stateDir>/inbox |
收件口目录 |
bridge.pollMs |
number | 3000 |
收件口轮询间隔(毫秒) |
promptEnabled |
boolean | true |
是否向 agent 注入提示词段落 |
promptText |
string | 内置默认 | 自定义提示词(留空使用内置) |
selfName |
string | 'self' |
写入 outbox.jsonl 时代表「自己」的名字,建议填成你的机器人名 |
sdkLoader |
function | — | 测试缝:tools/smoke.mjs 用它注入假 SDK。正式配置中不要设置 |
6. 工具参考
插件注册 2 个工具。输出均为 { text: string }。
6.1 otherbot_recent
查看群里别的机器人最近说了什么。来源有两路:QQ 平台推送的(需开启全量消息)与收件口递进来的。
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
bot |
string | 全部 | 机器人名字片段 |
group |
string | 全部 | 只看某个群(group_openid 或群名片段) |
limit |
number | 20 |
返回条数,最多 100 |
6.2 otherbot_diag
排障入口:雷达是否生效、平台实际推送了什么、为什么收不到别的机器人的消息。
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
lines |
number | 12 |
展示最近多少条原始入站,最多 40 |
7. 收件口(桥)协议
平台不推送时,收件口是唯一通路:任何能写文件的进程(另一台机器、你自己的脚本、
对面的机器人)把消息写进 inbox/ 下的任意 *.jsonl,一行一条,即可实时唤醒 agent。
字段宽松,以下写法都认:
| 语义 | 可接受的键 |
|---|---|
| 正文 | text / content / message |
| 发送者 | bot / name / from |
| 群 | group / groupOpenid / to(省略则落到最近活跃的群) |
{"bot":"小助手","text":"要比就比点具体的,别比嗓门","group":"<group_openid>"}
PowerShell 一行(把对面机器人的话喂进来):
'{"bot":"小助手","text":"'"$text"'"}' | Add-Content -Encoding utf8 "$env:USERPROFILE\.dsh\otherbot-radar\inbox\inbox.jsonl"
写入位置会被记录(不会重复唤醒),重启不会重读。agent 的回复写入 outbox.jsonl,
由递话方读走:
{"group":"G-1","bot":"self","text":"…","time":1793624454400,"at":"2026-11-02T13:00:54.400Z","sessionId":"…"}
8. 诊断与排障
在群里调用 otherbot_diag,或直接查看状态目录:
| 文件 | 内容 |
|---|---|
~/.dsh/otherbot-radar/radar.jsonl |
平台推来的每一条入站(含被门控挡掉的),附「消息下场」trace |
~/.dsh/otherbot-radar/transcript.jsonl |
只保留别的机器人说的话 |
~/.dsh/otherbot-radar/outbox.jsonl |
agent 的回复 |
~/.dsh/otherbot-radar/boot.log |
每次加载写一行(含版本号与配置),用于确认当前运行的是哪一版 |
otherbot_diag 会直接给出结论:
| 观察到的现象 | 结论 | 处理 |
|---|---|---|
| 入站 0 条 | 平台一条都没推 | 群里没人说话,或机器人未开启「接收全量消息」 |
只有 GROUP_AT_MESSAGE_CREATE |
只收得到 @ 本机器人的消息 | 请群主在手机 QQ 里开启全量消息 |
全量在推、但 otherBot 始终为 0 |
平台在全量消息里也过滤了机器人 | 只能走收件口 |
otherBot > 0 或 bridged > 0 |
雷达工作正常 | — |
radar.jsonl 始终不出现 |
SDK 钩子没装上 | 运行 tools/probe-tap.mjs 检查模块解析 |
9. 已知限制
- 对方能否听见本机器人是另一回事。 对面的机器人同样需要开启全量消息、或有人 @ 它, 它才收得到回复。单向可听;双向需要两边都配置。
- 平台不推送的内容,插件无法凭空生成 —— 只能依赖收件口递入。
- 桥消息没有
msg_id,agent 的回复属于主动消息,受 QQ 主动消息频控约束(群 1000 条/天)。 - 补丁打在 SDK 原型上(
handleInboundMessage/send/sendMedia)。@tencent-connect/qqbot-nodejs大版本升级后,需重新运行tools/smoke.mjs确认钩子仍然存在、签名未变。 - 依赖「接收全量消息」权限:该权限由群主授予,插件无法自行获取。
10. 开发
10.1 目录结构
index.js 插件入口:name / apply / DEFAULTS / 提示词 / 工具注册
lib/tap.js SDK 原型钩子:入站雷达、自回声识别、模块解析兜底
lib/store.js 状态落盘:radar.jsonl / transcript.jsonl / 统计
lib/bridge.js 收件口:inbox 轮询、行解析、outbox 追加
cordis.patch.yml bundle 补丁:把插件插进 profile
locale/ 插件页显示名与描述
tools/install.mjs 安装助手(link: + bundles)
tools/smoke.mjs 离线自检(假 SDK,不需要真 QQ)
tools/probe-tap.mjs 钩子与模块解析检查
10.2 测试
node tools/smoke.mjs # 离线自检:注入假 SDK,覆盖 10 组场景
node tools/probe-tap.mjs # 检查 SDK 钩子与模块解析路径
smoke.mjs 覆盖:人类闲聊(按 wakeOnHuman 分支)、人类 @、别的机器人发言、
自回声(含换 id 的同类回声)、收件口递话、两个工具、outbox 落盘、桥行解析的多种写法、
「消息下场」trace、热重载时补丁只装一次。
10.3 设计要点
- 钩子装在原型上而非实例上,因此不依赖 QQ 机器人插件的加载顺序;
重复加载时复用已装钩子并替换为新闭包(
smoke.mjs第 10 组覆盖此行为)。 - 门控插在中间件链最前面,通过
ctx.state.policy影响后续的 @ 门控, 因此能在原链判断之前决定是否唤醒。 - 雷达与唤醒解耦:所有入站消息都记账,唤醒只是记账之后的动作, 所以「被挡掉的消息」同样可查。
11. 许可证
English Overview
dsh-otherbot-radar lets a DeepSeek Harness agent hear other bots in a QQ group.
The QQ bot platform only pushes messages that mention the bot unless a group owner enables
"receive all messages" for it. This plugin installs a radar at the SDK inbound layer: it records
every inbound event the platform actually delivers (including ones dropped by policy gates),
wakes the agent when another bot speaks, keeps a transcript of what other bots said, and exposes
two tools (otherbot_recent, otherbot_diag) so both the agent and the operator can see what
happened and why.
When the platform pushes nothing at all, a file-based inbox bridge (inbox/*.jsonl in,
outbox.jsonl out) provides a fallback path for any process that can write files.
dsh plugin --profile desktop add dsh-otherbot-radar
The plugin patches QQBot.prototype.handleInboundMessage / send / sendMedia; re-run
node tools/smoke.mjs after a major upgrade of @tencent-connect/qqbot-nodejs.
Requires Node.js >= 22.13.0. Licensed under the MIT License.
No comments yet. Be the first to write one.