arrow1031-dev/dsh-qq-bot ↗★ 0

dsh-qq-bot

通过OneBot协议将QQ接入Agent会话 适合需要通过QQ群聊或私聊与DSH Agent交互的社区用户。

套件
dsh-qq-bot
相容性
待驗證
Harness 依賴範圍
*
版本
0.9.2
授權
MIT
最近更新
2026年10月22日

同名套件的其他儲存庫

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:arrow1031-dev/dsh-qq-bot

四、配置

方式一:在 DSH 设置里改(推荐)

重启 dsh web 后,打开 设置 → QQ 机器人,所有配置都在这一个选项卡里: 连接方式、WS/HTTP 地址、access_token、群聊开关与 @ 要求、白名单、命令前缀、 身份权限(QQ 号、preset 分流、工具闸)、群管理、会话(独立会话、固定命名、preset)、 回复分片长度、慢回复提示、来源前缀、逐帧日志、 图片视觉(开关 / provider / model / 最多张数 / 下载超时)、联网搜索(代理 / 端点 / key)、 生图(接口地址 / key / 模型 / 重试与轮询)、人格(开关 / 名字 / 设定 / 自定义 preset id)。 设置页按功能分组排列:接入与范围 → 身份与权限 → 群管理 → 会话 → 回复 → 图片视觉 → 联网搜索。

  • 改完点「保存」;连接方式那几项(模式/地址/token)保存后会自动重启适配器,其它项下一条消息生效。
  • 类型映射:布尔是勾选框;白名单是逗号分隔的文本框;连接方式是下拉框。
  • 配置落点(DSH ≥ 0.1.7):插件把自己的 Config schema 交给 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 是每次执行才求值的函数,配一个可变标志位就够。