dsh-qq-bot
把 QQ 接到 DSH Agent 的插件(社区化维护):经 NapCat / OneBot 11 收发消息,每个 QQ 会话一个 DSH Agent。 适合需要通过QQ群聊或私聊与DSH Agent交互的社区用户。
同名パッケージの別リポジトリ
インストール
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:arrow1031-dev/dsh-qq-botドキュメント
README 全文を読む ↗四、配置
方式一:在 DSH 设置里改(推荐)
重启 dsh web 后,打开 设置 → QQ 机器人,所有配置都在这一个选项卡里:
连接方式、WS/HTTP 地址、access_token、群聊开关与 @ 要求、白名单、命令前缀、
身份权限(QQ 号、preset 分流、工具闸)、群管理、会话(独立会话、固定命名、preset)、
回复分片长度、慢回复提示、来源前缀、逐帧日志、
图片视觉(开关 / provider / model / 最多张数 / 下载超时)、联网搜索(代理 / 端点 / key)、
生图(接口地址 / key / 模型 / 重试与轮询)、人格(开关 / 名字 / 设定 / 自定义 preset id)。
设置页按功能分组排列:接入与范围 → 身份与权限 → 群管理 → 会话 → 回复 → 图片视觉 → 联网搜索。
- 改完点「保存」;连接方式那几项(模式/地址/token)保存后会自动重启适配器,其它项下一条消息生效。
- 类型映射:布尔是勾选框;白名单是逗号分隔的文本框;连接方式是下拉框。
- 配置落点(DSH ≥ 0.1.7):插件把自己的
Configschema 交给 DSH 当设置表单,界面上的改动由 DSH 写进 profile 的cordis.patch.yml(也就是下面「方式二」里那个文件);0.1.6 时代的独立settings.yaml文档已经不存在了。 - 设置命名空间就是条目 id(默认
dsh-qq-bot);条目被改名时,客户端会回退匹配「以 qq-bot 结尾」的那个命名空间。 - 连接状态不在设置页里显示,在对话里让 Agent 调用
qqbot工具(action=status)看。 access_token是只写字段:已设置时页面显示「已设置;留空则保持不变」,只有输入新值才会覆盖。 Host 侧把它声明成 schema 的 secret,值不会下发到浏览器,保存时也不会被空值清掉。
工作区(自动管理,无需配置)
Agent 会话的工作目录不需要任何配置:插件读取 DSH 官方默认 Workspace 的位置
(/deepseek-harness),在它的同级目录自动创建 QQ_msg 作为工作区根,
并注册为 DSH workspace——Web UI 会话侧栏的「QQ 消息」分组,新 QQ 会话自动归入该分组:
- Windows:
C:\Users\\Documents\deepseek-harness\QQ_msg - Linux / macOS:
~/Documents/deepseek-harness/QQ_msg(系统没有 Documents 目录时退到~/deepseek-harness/QQ_msg)
带图消息的原图缓存放在 QQ_msg/qq-images//,会话结算后自动删除该缓存,
会话与工作区本身保留。旧的 workspace 配置项已废弃移除。
方式二:改 composition 行配置(当 base 层用)
不要改包内的 cordis.patch.yml(升级会覆盖)。写进你自己的
$DSH_HOME/profiles/web/cordis.patch.yml —— 它在所有 bundle 之后应用,按 id 定向覆盖:
- id: dsh-qq-bot
config:
mode: forward # forward | reverse(工作区自动管理,见上文)
onebot:
wsUrl: 'ws://127.0.0.1:3001' # mode=forward 用
listen: '0.0.0.0:6199' # mode=reverse 用
httpUrl: 'http://127.0.0.1:3000' # 留空则改用 WS action 发消息
accessToken: ''
group:
enabled: true
requireAt: true # 群里是否必须 @
allow: [] # 群号白名单,空数组 = 所有群
private:
enabled: true
allow: [] # 用户号白名单,空数组 = 所有人
commandPrefix: '' # 例如 '/ai',空串 = 不限制
reply:
maxChars: 1200
ackAfterMs: 8000 # 慢回合先回「正在处理」;0 关闭
ackText: '正在处理,请稍候…'
contextHeader: true # 给消息加 "[QQ群 123 · 小明(22222)]" 前缀
firstTurnHint: '(提示:这是一次 QQ 聊天。请用简体中文、简洁自然的口语作答;不要输出 Markdown 表格,代码块保持简短。)'
auth: # 权限,见「五、权限与群管理」
owners: [] # 拥有者 QQ:全权限 + 永久授权 + 可走独立会话
superAdmins: [] # 后台设定的高级管理员(不可永久授权)
ownerPreset: '' # 填了才把拥有者/非拥有者分成两条会话
guestPreset: '' # 非拥有者用的 preset(仅人格分离,非安全边界)
guestDshTools: false # 是否允许访客用 DSH 内部工具(默认否)
guestToolAllow: ['qqbot', 'qqgroup', 'qq_websearch', 'qq_webfetch', 'qq_imgsearch', 'qq_history'] # 访客仍可用的工具白名单
grantTtlMinutes: 60 # 群管理员动态授权默认时长
grantMaxTtlMinutes: 1440 # 群管理员单次可授最长
admin:
enabled: true
manageGroups: [] # 可管理的群;空 = 回退用 group.allow
requireGroupAdmin: true # 是否承认「本群管理员」这一路权限
prefix: '/' # 指令前缀(必须在消息首位)
dryRun: false # true = 只回复将执行什么
allowConsole: false # 允许 Web UI 会话直接调用管理工具
session:
fixedName: true # 固定新建会话名为 群聊_ / 私聊_(不再 LLM 自动起标题)
perChatSession: true # true=每个 QQ 会话一个 DSH 会话
agentPreset: '' # ''=部署默认 preset;可填自定义 preset id 换人格/工具
adapterPath: '' # ''=用包内自带适配器
nodePath: '' # ''=自动解析 node
logAdapterFrames: false # 排查时开,会打印每一帧 JSON
vision:
enabled: true # 图片视觉:收到图片时用视觉模型识别并并入回复
provider: '' # 视觉 provider 路由(留空 = 会话模型;复用 DSH 自定义 providers)
model: '' # 视觉模型 id(留空 = 会话模型;官方如 deepseek-flash)
maxImages: 3 # 单条消息最多送入的图片数
downloadTimeoutMs: 10000 # 图片下载超时(毫秒)
webSearch: # qq_websearch 联网搜索
proxy: '' # http://host:port;留空 = 直连;搜索与 url 抓取都走它
baseUrl: '' # 留空 = https://api.deepseek.com/anthropic/v1(仅 multiEngine=false 用)
apiKeyEnv: '' # 留空 = DEEPSEEK_BALANCE_API_KEY(自动读 DSH 凭据服务)
maxUses: 5 # 每次搜索最多触发次数
multiEngine: true # 多引擎组合搜索(默认开):Tavily → DuckDuckGo → Yandex/Bing/Wikipedia
# 全部走上方 proxy;关闭则落回官方 DeepSeek web_search
moli: # dsh-moli 接管(qq_webfetch 用 Moli 渲染)
enabled: false # 默认关;开启后 qq_webfetch 改用 Moli 无头浏览器渲染抓取(可抓 SPA/JS 页;需已安装 moli 引擎,否则无效)
proxy: '' # http://host:port;留空自动透传 webSearch.proxy
exePath: '' # moli 可执行文件路径;留空自动探测 ~/.local/bin/moli 与 PATH
history: # qq_history 聊天记录(AI 自取)
scopeMessages: 5 # 识别窗口:触发消息往前最多多少条
imageLimit: 3 # 窗口内最多识别多少张图(最近的优先)
imageGen: # qq_draw 生图(自填接口,插件不带 key)
enabled: false # 打开后 AI 才能画图
baseUrl: '' # OpenAI 兼容,例 https://api.example.com/v1(请求 地址/images/generations)
apiKey: '' # 只写字段;留空则读下面那个环境变量
apiKeyEnv: IMAGE_API_KEY
model: seedream-5.0-lite
size: 1024x1024
timeoutMs: 180000 # 单次请求超时
maxAttempts: 4 # 失败重试次数(含首次;429/5xx/网络/空结果)
retryIntervalMs: 5000
poll: true # 供应商返回 task_id 时继续轮询
pollIntervalMs: 5000
maxPolls: 30
persona: # 自定义人格:每个新会话第一轮附一次
enabled: false
name: ''
text: ''
agentPresetCustom: '' # 自定义 preset id(如 qq);非空时优先于 agentPreset
imgSearch: # qq_imgsearch 图片反搜
enabled: true # 默认开;关闭后工具返回「已关闭」
maxResults: 5 # 每次反搜最多返回结果数
saucenaoKey: '' # SauceNAO API key(saucenao.com 注册可得);留空不启用
weightedShuffle: false # 有代理时按体量权重随机重排引擎顺序(避免固定引擎一直打头)
# 请求全走上方 webSearch.proxy:无代理 = 直连引擎按可用性优先
# (TraceMoe → IQDB);有代理 = 放开全部可用引擎按用户体量优先
# (Yandex → Tineye → Copyseeker → SauceNAO → IQDB → TraceMoe)
agentPreset 是换人格的正规做法:给 QQ 机器人做一个专属 preset,把 id 填进来。
图片视觉(vision)
收到图片时用视觉模型识别并并入 Agent 输入(原图缓存到 /qq-images//,会话结算后删除)。
provider / model 留空 = 会话模型;也可以给视觉单独指定 provider / model。
官方视觉模型(DeepSeek API):deepseek-flash 接受图片与文本(OpenAI 兼容,base64 data URL 或图片 URL 均可,支持 JPEG / PNG / GIF / WebP)。
旧的 deepseek-v4-flash-vision-exp 名字已退役,但请求仍被接受(由最新 Flash 服务)。见 DeepSeek Vision 文档。
其它 OpenAI 兼容通道:选一个在 DSH providers 里声明了 input: [text, image] 的模型即可;
注意 deepseek-v4-flash-0731 这类默认模型实测不真正支持图片(图不进上下文、会编造解释),换视觉模型时务必确认声明了图片输入。
工作区(自动管理,无需配置)
Agent 会话的工作目录不需要任何配置:插件读取 DSH 官方默认 Workspace 的位置
(/deepseek-harness),在它的同级目录自动创建 QQ_msg 作为工作区根,
并注册为 DSH workspace——Web UI 会话侧栏的「QQ 消息」分组,新 QQ 会话自动归入该分组:
- Windows:
C:\Users\\Documents\deepseek-harness\QQ_msg - Linux / macOS:
~/Documents/deepseek-harness/QQ_msg(系统没有 Documents 目录时退到~/deepseek-harness/QQ_msg)
带图消息的原图缓存放在 QQ_msg/qq-images//,会话结算后自动删除该缓存,
会话与工作区本身保留。旧的 workspace 配置项已废弃移除。
方式二:改 composition 行配置(当 base 层用)
不要改包内的 cordis.patch.yml(升级会覆盖)。写进你自己的
$DSH_HOME/profiles/web/cordis.patch.yml —— 它在所有 bundle 之后应用,按 id 定向覆盖:
- id: dsh-qq-bot
config:
mode: forward # forward | reverse(工作区自动管理,见上文)
onebot:
wsUrl: 'ws://127.0.0.1:3001' # mode=forward 用
listen: '0.0.0.0:6199' # mode=reverse 用
httpUrl: 'http://127.0.0.1:3000' # 留空则改用 WS action 发消息
accessToken: ''
group:
enabled: true
requireAt: true # 群里是否必须 @
allow: [] # 群号白名单,空数组 = 所有群
private:
enabled: true
allow: [] # 用户号白名单,空数组 = 所有人
commandPrefix: '' # 例如 '/ai',空串 = 不限制
reply:
maxChars: 1200
ackAfterMs: 8000 # 慢回合先回「正在处理」;0 关闭
ackText: '正在处理,请稍候…'
contextHeader: true # 给消息加 "[QQ群 123 · 小明(22222)]" 前缀
firstTurnHint: '(提示:这是一次 QQ 聊天。请用简体中文、简洁自然的口语作答;不要输出 Markdown 表格,代码块保持简短。)'
auth: # 权限,见「五、权限与群管理」
owners: [] # 拥有者 QQ:全权限 + 永久授权 + 可走独立会话
superAdmins: [] # 后台设定的高级管理员(不可永久授权)
ownerPreset: '' # 填了才把拥有者/非拥有者分成两条会话
guestPreset: '' # 非拥有者用的 preset(仅人格分离,非安全边界)
guestDshTools: false # 是否允许访客用 DSH 内部工具(默认否)
guestToolAllow: ['qqbot', 'qqgroup', 'qq_websearch', 'qq_webfetch', 'qq_imgsearch', 'qq_history'] # 访客仍可用的工具白名单
grantTtlMinutes: 60 # 群管理员动态授权默认时长
grantMaxTtlMinutes: 1440 # 群管理员单次可授最长
admin:
enabled: true
manageGroups: [] # 可管理的群;空 = 回退用 group.allow
requireGroupAdmin: true # 是否承认「本群管理员」这一路权限
prefix: '/' # 指令前缀(必须在消息首位)
dryRun: false # true = 只回复将执行什么
allowConsole: false # 允许 Web UI 会话直接调用管理工具
session:
fixedName: true # 固定新建会话名为 群聊_ / 私聊_(不再 LLM 自动起标题)
perChatSession: true # true=每个 QQ 会话一个 DSH 会话
agentPreset: '' # ''=部署默认 preset;可填自定义 preset id 换人格/工具
adapterPath: '' # ''=用包内自带适配器
nodePath: '' # ''=自动解析 node
logAdapterFrames: false # 排查时开,会打印每一帧 JSON
vision:
enabled: true # 图片视觉:收到图片时用视觉模型识别并并入回复
provider: '' # 视觉 provider 路由(留空 = 会话模型;复用 DSH 自定义 providers)
model: '' # 视觉模型 id(留空 = 会话模型;官方如 deepseek-flash)
maxImages: 3 # 单条消息最多送入的图片数
downloadTimeoutMs: 10000 # 图片下载超时(毫秒)
webSearch: # qq_websearch 联网搜索
proxy: '' # http://host:port;留空 = 直连;搜索与 url 抓取都走它
baseUrl: '' # 留空 = https://api.deepseek.com/anthropic/v1(仅 multiEngine=false 用)
apiKeyEnv: '' # 留空 = DEEPSEEK_BALANCE_API_KEY(自动读 DSH 凭据服务)
maxUses: 5 # 每次搜索最多触发次数
multiEngine: true # 多引擎组合搜索(默认开):Tavily → DuckDuckGo → Yandex/Bing/Wikipedia
# 全部走上方 proxy;关闭则落回官方 DeepSeek web_search
moli: # dsh-moli 接管(qq_webfetch 用 Moli 渲染)
enabled: false # 默认关;开启后 qq_webfetch 改用 Moli 无头浏览器渲染抓取(可抓 SPA/JS 页;需已安装 moli 引擎,否则无效)
proxy: '' # http://host:port;留空自动透传 webSearch.proxy
exePath: '' # moli 可执行文件路径;留空自动探测 ~/.local/bin/moli 与 PATH
history: # qq_history 聊天记录(AI 自取)
scopeMessages: 5 # 识别窗口:触发消息往前最多多少条
imageLimit: 3 # 窗口内最多识别多少张图(最近的优先)
imageGen: # qq_draw 生图(自填接口,插件不带 key)
enabled: false # 打开后 AI 才能画图
baseUrl: '' # OpenAI 兼容,例 https://api.example.com/v1(请求 地址/images/generations)
apiKey: '' # 只写字段;留空则读下面那个环境变量
apiKeyEnv: IMAGE_API_KEY
model: seedream-5.0-lite
size: 1024x1024
timeoutMs: 180000 # 单次请求超时
maxAttempts: 4 # 失败重试次数(含首次;429/5xx/网络/空结果)
retryIntervalMs: 5000
poll: true # 供应商返回 task_id 时继续轮询
pollIntervalMs: 5000
maxPolls: 30
persona: # 自定义人格:每个新会话第一轮附一次
enabled: false
name: ''
text: ''
agentPresetCustom: '' # 自定义 preset id(如 qq);非空时优先于 agentPreset
imgSearch: # qq_imgsearch 图片反搜
enabled: true # 默认开;关闭后工具返回「已关闭」
maxResults: 5 # 每次反搜最多返回结果数
saucenaoKey: '' # SauceNAO API key(saucenao.com 注册可得);留空不启用
weightedShuffle: false # 有代理时按体量权重随机重排引擎顺序(避免固定引擎一直打头)
# 请求全走上方 webSearch.proxy:无代理 = 直连引擎按可用性优先
# (TraceMoe → IQDB);有代理 = 放开全部可用引擎按用户体量优先
# (Yandex → Tineye → Copyseeker → SauceNAO → IQDB → TraceMoe)
agentPreset 是换人格的正规做法:给 QQ 机器人做一个专属 preset,把 id 填进来。
非拥有者不使用 DSH 内部功能:执行级工具闸
用一个 tools.guard 硬闸,只在建会话时注册一次,判据是闭包里的可变标志位:
- 注册在
agent.ctx上 → 只作用于该会话的 agent - 每次工具执行时才求值 → 拥有者/访客交替说话不需要增删限制
- 用白名单(
auth.guestToolAllow,默认qqbot,qqgroup,qq_websearch,qq_webfetch,qq_imgsearch,qq_history)→run_code(PTC 保留传输)这类"不在全局层"的特殊工具也会被自动挡住;访客联网统一走qq_websearch(搜索)与qq_webfetch(抓网页),DSH 自带的web_fetch已从默认白名单移除 - 被拦时返回拒绝原因,模型会把它转达给用户(最多多一次"被拒绝的调用"往返)
因此它同时满足:0 额外 token(不注入提示词、不改工具表、不记 request/header)、单会话单历史、不改 DSH 本身。
想放开就设 auth.guestDshTools: true(不建议)。
为什么不用"每条消息增删
tools.restrict":restrict是静态过滤器,想按说话人变就得反复增删,工具面抖动会让 agent loop 多记request/header,反而更费 token、代码也更多。guard 是每次执行才求值的函数,配一个可变标志位就够。