把连续多条 QQ 消息合并处理,同一轮只回一条并持续修订
30.7/100
综合分
1
GitHub 星标
—
npm 周下载
—/5
用户评分
数据截至 2026/10/30 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add bufan528/dsh-qqbot-merge该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)
状态速览
判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态信任档位:仅索引
是什么
dsh 原生插件 · platform
装得上吗
本站尚未做安装检查
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 5 天前
档位由哪些信号合成
本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。
社区实装还没有人打卡 · 装过的第一个就是你
安全扫描自动 · 静态分析源码v2.7.0🔵 扫描:含敏感能力尚无人工评估展开查看扫描说明与证据 ↓
安全扫描自动 · 静态分析源码
v2.7.0静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。
可执行外部命令读写/删除本地文件读取剪贴板或密钥环境变量发起外部网络请求
查看扫描证据(共 82 处,展示前 73 条)
- 发起网络请求src/client/bind-page.ts:196
const res = await fetch(BIND_STATE_PATH, { cache: 'no-store' }); - 发起网络请求src/client/bind-page.ts:253
const res = await fetch(BIND_MANUAL_PATH, { - 发起网络请求src/client/bind-page.ts:352
void fetch(BIND_UNBIND_PATH, { method: 'POST' }) - 发起网络请求src/client/bind-page.ts:414
const refresh = useCallback(() => { void fetch(BIND_REFRESH_PATH, { method: 'POST' }).catch(() => {}); }, []); - 发起网络请求src/client/config-panel.ts:265
void fetch(BIND_CONFIG_PATH, { cache: 'no-store' }) - 发起网络请求src/client/config-panel.ts:306
void fetch(BIND_CONFIG_PATH, { - 发起网络请求src/client/index.ts:236
const res = await fetch(BIND_STATE_PATH, { cache: 'no-store' }); - 发起网络请求src/client/merge-panel.ts:443
void fetch(MERGE_CONFIG_PATH, { cache: 'no-store' }) - 发起网络请求src/client/merge-panel.ts:484
void fetch(MERGE_CONFIG_PATH, { - 读取密钥环境变量src/gateway/bootstrap.ts:70
tokenBaseUrl: process.env.QQBOT_TOKEN_BASE_URL?.replace(/ +$/, '') || 'https://api.bot.qq.com', - 读取本地文件src/media/media-cleaner.ts:28
if (!existsSync(MEDIA_ROOT)) return 0; - 读取本地文件src/media/media-cleaner.ts:33
const entries = await readdir(MEDIA_ROOT, { withFileTypes: true }); - 读取本地文件src/media/media-cleaner.ts:39
const info = await stat(filePath); - 删除本地文件src/media/media-cleaner.ts:41
await rm(filePath, { force: true }); - 读取本地文件src/media/send-file-tool.ts:128
if (!existsSync(absPath)) { - 读取本地文件src/media/send-media.ts:63
const info = await stat(filePath); - 发起网络请求src/media/vision-tool.ts:87
const resp = await fetch(image, { signal, redirect: 'error' }); - 读取本地文件src/media/vision-tool.ts:93
const info = await stat(image).catch(() => null); - 读取本地文件src/media/vision-tool.ts:96
data = new Uint8Array(await readFile(image)); - 读取本地文件src/media/vision-tool.ts:187
if (!existsSync(settingsPath)) return; - 读取本地文件src/media/vision-tool.ts:190
const raw = readFileSync(settingsPath, 'utf8'); - 写入本地文件src/media/vision-tool.ts:216
writeFileSync(settingsPath, yaml.dump(doc), 'utf8'); - 读取本地文件src/model/prefs-store.ts:100
if (!existsSync(this.prefsPath)) return; - 读取本地文件src/model/prefs-store.ts:101
const content = readFileSync(this.prefsPath, 'utf8'); - 写入本地文件src/model/prefs-store.ts:131
mkdirSync(dirname(this.prefsPath), { recursive: true }); - 写入本地文件src/model/prefs-store.ts:137
writeFileSync(this.prefsPath, JSON.stringify(data, null, 2), 'utf8'); - 读取本地文件src/model/settings-reader.ts:84
if (!existsSync(settingsPath)) { - 读取本地文件src/model/settings-reader.ts:88
const content = readFileSync(settingsPath, 'utf8'); - 写入本地文件src/setup/bind-routes.test.ts:259
writeFileSync(patchPath, [ - 读取密钥环境变量src/setup/bind-routes.test.ts:270
const savedSecret = process.env.QQBOT_SECRET; - 读取密钥环境变量src/setup/bind-routes.test.ts:272
process.env.QQBOT_SECRET = 's3cr3t'; - 读取密钥环境变量src/setup/bind-routes.test.ts:283
expect(process.env.QQBOT_SECRET).toBeUndefined(); - 读取本地文件src/setup/bind-routes.test.ts:285
const content = readFileSync(patchPath, 'utf8'); - 读取密钥环境变量src/setup/bind-routes.test.ts:293
if (savedSecret === undefined) delete process.env.QQBOT_SECRET; - 读取密钥环境变量src/setup/bind-routes.test.ts:294
else process.env.QQBOT_SECRET = savedSecret; - 删除本地文件递归src/setup/bind-routes.test.ts:295
rmSync(dir, { recursive: true, force: true }); - 写入本地文件src/setup/bind-routes.test.ts:303
writeFileSync(patchPath, [ - 读取本地文件src/setup/bind-routes.test.ts:310
const before = readFileSync(patchPath, 'utf8'); - 读取本地文件src/setup/bind-routes.test.ts:319
expect(readFileSync(patchPath, 'utf8')).toEqual(before); - 删除本地文件递归src/setup/bind-routes.test.ts:322
rmSync(dir, { recursive: true, force: true }); - 删除本地文件递归src/setup/bind-routes.test.ts:328
rmSync(profileDirState.dir, { recursive: true, force: true }); - 读取密钥环境变量src/setup/bind-routes.ts:331
delete process.env.QQBOT_SECRET; - 读取本地文件src/setup/persist.ts:59
if (existsSync(patchPath)) { - 读取本地文件src/setup/persist.ts:60
entries = parsePatchEntries(readFileSync(patchPath, 'utf8')); - 写入本地文件src/setup/persist.ts:87
mkdirSync(dir, { recursive: true }); - 写入本地文件src/setup/persist.ts:88
writeFileSync(patchPath, output, 'utf8'); - 读取本地文件src/setup/persist.ts:118
if (!existsSync(patchPath)) return true; - 读取本地文件src/setup/persist.ts:120
const entries = parsePatchEntries(readFileSync(patchPath, 'utf8')); - 写入本地文件src/setup/persist.ts:130
mkdirSync(profileDir, { recursive: true }); - 写入本地文件src/setup/persist.ts:131
writeFileSync(patchPath, yaml.dump(entries), 'utf8'); - 读取本地文件src/setup/persist.ts:165
if (existsSync(patchPath)) { - 读取本地文件src/setup/persist.ts:166
entries = parsePatchEntries(readFileSync(patchPath, 'utf8')); - 写入本地文件src/setup/persist.ts:184
mkdirSync(profileDir, { recursive: true }); - 写入本地文件src/setup/persist.ts:185
writeFileSync(patchPath, yaml.dump(entries), 'utf8'); - 执行外部命令来自配置/变量src/setup/presenters.ts:91
const child = spawn(command, args, { detached: true, stdio: 'ignore', windowsHide: true }); - 删除本地文件递归src/shared/utils.test.ts:26
if (dir) rmSync(dir, { recursive: true, force: true }); - 写入本地文件src/shared/utils.test.ts:38
mkdirSync(plugin, { recursive: true }); - 写入本地文件src/shared/utils.test.ts:39
writeFileSync(join(profile, 'cordis.patch.yml'), '# patch\n'); - 写入本地文件src/shared/utils.test.ts:40
writeFileSync(join(profile, 'dependencies', 'pnpm-workspace.yaml'), 'packages:\n - .\n'); - 写入本地文件src/shared/utils.test.ts:50
mkdirSync(plugin, { recursive: true }); - 写入本地文件src/shared/utils.test.ts:51
writeFileSync(join(profile, 'cordis.patch.yml'), '# patch\n'); - 写入本地文件src/shared/utils.test.ts:60
mkdirSync(plugin, { recursive: true }); - 读取本地文件src/shared/utils.ts:21
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as { version?: string }; - 读取本地文件src/shared/utils.ts:57
existsSync(resolve(dir, 'cordis.patch.yml')) ? dir : null); - 读取本地文件src/shared/utils.ts:61
existsSync(resolve(dir, 'pnpm-workspace.yaml')) ? dir : null); - 发起网络请求src/transport/attachment.ts:135
const resp = await fetch(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS) }); - 写入本地文件src/transport/attachment.ts:142
writeFileSync(destPath, buf); - 写入本地文件src/transport/attachment.ts:164
mkdirSync(MEDIA_ROOT, { recursive: true }); - 写入本地文件src/transport/deliverables.test.ts:44
await writeFile(png, 'x'); - 写入本地文件src/transport/deliverables.test.ts:45
await writeFile(md, 'y'); - 写入本地文件src/transport/deliverables.test.ts:61
await writeFile(ok, 'x'); - 写入本地文件src/transport/deliverables.test.ts:79
await writeFile(outside, 'x'); - 写入本地文件src/transport/deliverables.test.ts:96
await writeFile(p, n);
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 GitHub 仓库
静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。
依赖的 DSH / Cordis 模块
@deepseek-ai/cordis@deepseek-ai/dsh-agent@deepseek-ai/dsh-llm@deepseek-ai/dsh-session@deepseek-ai/dsh-system-prompt@deepseek-ai/dsh-user-approval@deepseek-ai/schemastery给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个
展开完整介绍(132 字)↓
维护 @tencent-connect/dsh-qqbot 0.5.3(MIT)的分支:连续消息合并,配合不打断的 agent.steer() 和单条消息回复修订。替代官方 QQ 插件,为 dsh-longterm-memory 提供 qqbotMerge 服务。
README
由 DeepSeek 最新模型翻译生成⚠️ 这是维护分支(modified fork),不是腾讯官方发布包
本包 dsh-qqbot-merge 是 @tencent-connect/dsh-qqbot 0.5.3(MIT) 的维护分支,在原版基础上新增 「连续消息智能合并」(非打断式 steering) 与 「同一轮只发一条回复并持续修订」,并对外提供 qqbotMerge cordis 服务,供 dsh-longterm-memory 的「QQ 接入」页读取/写入配置。
- 原项目版权归 Tencent Connect 所有,见 LICENSE;本分支的修改部分同样以 MIT 发布。
- 不能与官方 @tencent-connect/dsh-qqbot 同时启用:两者都会用同一个 QQ Bot appId 建立网关连接、注册绑定页路由。启用本包 = 替换官方 QQ 插件(详见下方「安装与迁移」)。
- 安全提示:官方原版会把 appId / appSecret 以明文写入 profile 的 cordis.patch.yml;本分支沿用同一机制,不额外保存、不上传、不回显 Secret。
展开完整文档(README 剩余 1 节)
⚠️ 这是维护分支(modified fork),不是腾讯官方发布包
本包 dsh-qqbot-merge 是 @tencent-connect/dsh-qqbot 0.5.3(MIT) 的维护分支,在原版基础上新增 「连续消息智能合并」(非打断式 steering) 与 「同一轮只发一条回复并持续修订」,并对外提供 qqbotMerge cordis 服务,供 dsh-longterm-memory 的「QQ 接入」页读取/写入配置。
- 原项目版权归 Tencent Connect 所有,见 LICENSE;本分支的修改部分同样以 MIT 发布。
- 不能与官方 @tencent-connect/dsh-qqbot 同时启用:两者都会用同一个 QQ Bot appId 建立网关连接、注册绑定页路由。启用本包 = 替换官方 QQ 插件(详见下方「安装与迁移」)。
- 安全提示:官方原版会把 appId / appSecret 以明文写入 profile 的 cordis.patch.yml;本分支沿用同一机制,不额外保存、不上传、不回显 Secret。
安装与迁移(替换官方 QQ 插件)
1) 安装本分支
npx @deepseek-ai/dsh plugin --profile desktop add dsh-qqbot-merge
2) 从 profile 移除官方插件,并把 bundles 里的 "@tencent-connect/dsh-qqbot" 换成 "dsh-qqbot-merge"
npx @deepseek-ai/dsh plugin --profile desktop remove @tencent-connect/dsh-qqbot
3) 迁移凭据:把原 im-qqbot 行的 appId/appSecret 复制到新行(行 id 为 qqbot-merge),或重启后重新扫码绑定;不要在两行里同时保留凭据
4) 重启 DSH 桌面端
启用合并(可选,默认关闭、行为与官方 0.5.3 一致):打开 DSH 设置页 →「连续消息智能合并」栏目,打开开关并保存(同一页可直接看到运行期计数与最近一批);或直接改 profile cordis.patch.yml 里 qqbot-merge 行的 qqbotMerge.enabled: true。
本地构建(从源码)
npm install
npm run build # tsc -> dist/
npm run build:client # tsdown -> lib/client.js(绑定页 + 设置面板客户端半边)
npx vitest run # 18 files / 243 tests
npm pack # 291 files, ~1.2 MB
然后 dsh plugin --profile <name> add .\dsh-qqbot-merge-0.1.1.tgz。
与官方 0.5.3 的差异
| 方面 | 官方 0.5.3 | 本分支 |
| 任务执行中收到新消息 | concurrencyGuard(strategy:'merge') 缓冲到本轮结束后再合并 → 下一轮处理 | 合并窗口(默认 800ms)后通过公开 agent.steer() 并入当前轮下一步骤,不取消当前模型调用/工具 |
| 一轮多 step 的输出 | 每个 assistant/message 各发一条 QQ 消息 | 同一轮只在同一条 QQ 消息上全量重写,turn/end 只 complete 一次(群聊/非流式降级为轮末单条) |
| 对外接口 | 无 cordis 服务 | ctx.provide('qqbotMerge', …),契约 qqbot-merge@v1 |
| 其余功能 | —— | 审批、待答问题、文件交付、群聊历史、模型/预设切换、绑定页均保留 |
真实 QQ 端到端(连续消息 → 一条回复)尚未在发布者机器上验证(未替用户替换运行中的实例;同一 appId 无法并行建两条连接)。使用前请先测试,并保留回滚:把 profile 的 dependencies/bundles 换回 @tencent-connect/dsh-qqbot@0.5.3 即可。
基于 deepseek-harness (dsh) 的 QQ Bot 插件,把 DeepSeek AI 助手接入 QQ 私聊与群聊。
扫码加入 QQ 群 / 频道
QQ 开发者交流群群号: 1032635674 QQ 开发者社区频道频道号: 20dnumts4z
这是维护分支 dsh-qqbot-merge@0.1.1,fork 自官方 @tencent-connect/dsh-qqbot 0.5.3(MIT,源码取自 codeload.github.com/tencent-connect/dsh-qqbot/zip/refs/tags/0.5.3)。在上游功能之上新增:
- 连续消息智能合并:智能体执行任务期间,用户连续发来的消息按 QQ 会话隔离,以静默窗口(默认 800ms)合并;忙碌时用 agent.steer(...) 非打断式并入当前轮的下一个 step,空闲时用 agent.followup(...),不再排队到下一轮;agent/turn-stopping 时把仍在窗口内的批次并入当前轮;steer 竞态自动降级 followup;超上限的消息通知用户,并作为下一批 followup (不静默丢弃)。
- 同一轮单条回复:replyMode: 'single-message' 时,一轮里多个 step 的文本持续修订同一条 QQ 消息(turn/end 才 complete),流式不可用时降级为轮末单条静态发送。
- 对外 cordis 服务 qqbotMerge(契约 qqbot-merge@v1):供 DSH Memory Vault 等插件读取真实配置/计数/最近批次并写入配置;配置只写本插件自己的 cordis row config。
- 端内合并设置:DSH 设置页的「连续消息智能合并」栏目可直接配置开关 / 静默窗口 / 单批上限 / 私聊与群聊开关 / 运行中策略 / 回复模式,并查看运行期计数与最近一批,不再依赖其他插件。
合并默认 关闭(qqbotMerge.enabled: false),关闭时行为与官方 0.5.3 完全一致。
本包与官方插件不能同时启用(同一个 QQ appId、同名绑定页路由)。
真实 QQ 端到端未验证;详见仓库的 docs/evidence/qqbot-merge-implementation.md。
架构
QQ 用户 → QQ WebSocket → dsh-qqbot → ctx.agents → dsh agent loop → LLM ↑ │ └── session/event ──────────┘ (assistant reply → QQ sendMarkdown)
安装
方式一:桌面版安装(推荐)
无需命令行,全程在应用内完成:
1. 安装:左侧栏点击「插件」→「添加插件」→ 输入 @tencent-connect/dsh-qqbot@latest → 安装并立即启动
2. 连接:左下角头像 → 设置 →「QQ 机器人」→ 手机 QQ 扫码连接
扫码成功后凭据自动保存,后续启动无需再次扫码。
方式二:手动执行
安装到 profile
npx @deepseek-ai/dsh plugin --profile qqbot add @tencent-connect/dsh-qqbot
启动
npx @deepseek-ai/dsh --profile qqbot
首次启动时,插件检测到凭据未配置会自动进入扫码引导:终端输出二维码 → 手机 QQ 扫码绑定 → 凭据自动保存到 profile,后续启动无需再次扫码。
无端内绑定页的环境(如纯命令行)可设置 QQBOT_BIND_OPEN_BROWSER=1,额外拉起系统浏览器扫码页作为降级通道。
提示:建议升级至 0.4.0 以上版本扫码,支持点击链接在浏览器打开,避免部分终端二维码渲染错位的问题。
方式三:本地路径安装
构建
cd /path/to/dsh-qqbot
pnpm install && pnpm build
安装到 profile(本地路径)
npx @deepseek-ai/dsh plugin --profile qqbot add /path/to/dsh-qqbot
启动
export QQBOT_APPID="你的AppID" QQBOT_SECRET="你的AppSecret"
npx @deepseek-ai/dsh --profile qqbot
方式四:--patch 开发模式
export QQBOT_APPID="你的AppID" QQBOT_SECRET="你的AppSecret"
npx @deepseek-ai/dsh web --patch /path/to/dsh-qqbot/cordis.dev.yml
配置项
| 配置 | 类型 | 默认值 | 说明 |
| appId | string | 必填 | QQ Bot AppID(或通过 QQBOT_APPID 环境变量) |
| appSecret | string | 必填 | QQ Bot AppSecret(或通过 QQBOT_SECRET 环境变量) |
| provider | string | deepseek-official | LLM 服务商名称 |
| model | string | deepseek-chat | 模型名称 |
| preset | string | - | 智能体 preset id |
| cwd | string | process.cwd() | 智能体工作目录 |
| requireMention | boolean | true | 群聊是否需要 @bot 才触发 |
| groupPrompt | string | - | 群聊额外 system prompt |
| directPrompt | string | - | 私聊额外 system prompt |
| textChunkLimit | number | 4500 | 单条消息最大字符数 |
| streaming | boolean | true | 是否启用流式输出(群聊始终不启用) |
| sessionIdleTimeout | number | 1800000 | 会话闲置超时(ms),默认 30 分钟 |
| processingTimeoutMs | number | 1800000 | 处理超时(ms),超时中断当前 LLM 调用 |
| maxQueue | number | 20 | 并发队列最大长度 |
| historyLimit | number | 10 | 群历史缓冲条数 |
| askTimeoutMs | number | 300000 | 待答问题超时(ms),默认 5 分钟(ask_user_question) |
| showToolResults | boolean | false | 是否展示工具调用成功结果(错误始终展示) |
| debug | boolean | false | 调试模式 |
访问控制(access)
| 配置 | 类型 | 默认值 | 说明 |
| access.c2cMode | open/allowlist/disabled | open | 私聊访问模式 |
| access.c2cAllow | string[] | [] | 私聊白名单(user openid) |
| access.groupMode | open/allowlist/disabled | open | 群聊访问模式 |
| access.groupAllow | string[] | [] | 群聊白名单(group openid) |
富媒体理解(media)
| 配置 | 类型 | 默认值 | 说明 |
| media.enabled | boolean | true | 是否启用富媒体理解(图片/视频下载 + 工具分析) |
| media.maxMB | number | 200 | 富媒体下载大小上限(MB) |
| media.ttlHours | number | 24 | 富媒体存活时长(小时),0=永不过期 |
视觉理解(vision)
| 配置 | 类型 | 默认值 | 说明 |
| vision.enabled | boolean | false | 是否启用视觉理解(qqbot_describe_image 工具) |
| vision.provider | string | - | 视觉模型服务商(如 pi-ai) |
| vision.model | string | - | 视觉模型 id(如 qwen-vl-max) |
| vision.maxBytes | number | 10MB | 图片字节上限 |
| vision.maxTokens | number | 1024 | 输出 token 上限 |
| vision.timeoutMs | number | 120000 | 视觉调用超时(ms) |
附件发送(sendFile)
| 配置 | 类型 | 默认值 | 说明 |
| sendFile.restrictPaths | boolean | true | 是否启用路径白名单(仅 media + cwd + extraRoots) |
| sendFile.extraRoots | string[] | [] | 额外允许访问的根目录 |
连续消息智能合并(qqbotMerge)
合并配置由本插件自己的设置界面管理,不再依赖任何其他插件:打开 DSH 设置页 → 「连续消息智能合并」(settings.section id qqbot-merge),即可编辑下列字段并查看运行期状态。数据来自宿主路由 /qqbot/im-qqbot/merge(GET 读状态、POST 写配置),未绑定 / 路由未就绪时面板显示「尚未就绪」而不是报错。也可以直接改配置档里 qqbot-merge 行的 qqbotMerge 字段。
| 配置 | 类型 | 默认值 | 说明 |
| qqbotMerge.enabled | boolean | false | 是否启用连续消息智能合并(关闭时行为与官方 0.5.3 一致) |
| qqbotMerge.windowMs | number | 800 | 静默合并窗口(ms),100–10000 |
| qqbotMerge.maxBatchMessages | number | 8 | 单批最多合并消息条数,1–50 |
| qqbotMerge.maxBatchChars | number | 4000 | 单批最多合并字符数,200–20000 |
| qqbotMerge.scope.c2c | boolean | true | 私聊是否启用合并 |
| qqbotMerge.scope.group | boolean | false | 群聊是否启用合并(按发送者分组,同一发送者才合并) |
| qqbotMerge.busyPolicy | steer/queue | steer | 运行中策略:并入当前轮(非打断式)/ 排队到下一轮(旧行为) |
| qqbotMerge.replyMode | single-message/per-step | single-message | 回复模式:同一条消息持续修订 / 按步骤逐条发送 |
同一面板还展示运行期诊断:steer 是否可用、steer 机制、集成方式、说明、8 项运行计数(合并批次、并入当前轮、空闲提交、超限溢出、steer 降级、单条修订、单条完成、批次中止)与最近一批的条数/字符数/方式/结果。保存后立即生效,并写入本插件自己的 cordis row config (宿主会因此重载插件,重载后 revision 归零属正常)。
内置命令
| 命令 | 说明 |
| /new(别名 /reset /clear) | 开始新会话(清空上下文) |
| /compact | 压缩会话历史(摘要替换旧记录,保留上下文) |
| /model | 查看或切换模型 |
| /preset | 查看或切换 agent preset(新会话生效) |
| /stop | 中止当前生成 |
| /bot-ping | 网络延迟检测(返回传输与处理耗时) |
| /bot-version | 查看版本信息 |
| /bot-status | 查看当前会话状态 |
| /bot-help | 查看所有指令 |
核心模块
src/
├── index.ts # Cordis 插件入口(async apply) ├── config.ts # 配置 Schema ├── types.ts # 全局类型定义 ├── setup/ # 凭据绑定(扫码) │ ├── index.ts # 绑定编排(ensureCredentials) │ ├── binding-service.ts # 扫码状态机(宿主内唯一事实源) │ ├── bind-routes.ts # 端内绑定页路由(/qqbot/im-qqbot/bind) │ ├── presenters.ts # 呈现策略(终端/浏览器降级) │ ├── qr-svg.ts # 二维码 SVG 渲染(宿主侧) │ └── persist.ts # 凭据写入 cordis.patch.yml ├── client/ # 浏览器半边(dsh.client Web Bundle) │ ├── index.ts # 侧栏 Tab + 设置页栏目注册 │ ├── bind-page.ts # 端内绑定页(二维码 + 状态轮询) │ ├── config-panel.ts # 设置页基础配置面板 │ └── merge-panel.ts # 设置页「连续消息智能合并」面板 ├── merge/ # 连续消息智能合并 │ ├── contract.ts # 跨插件契约(qqbot-merge@v1) │ ├── coordinator.ts # 静默窗口合并 + steer/queue 调度 │ ├── service.ts # qqbotMerge 服务 + 单条回复 hooks │ └── routes.ts # 设置路由(/qqbot/im-qqbot/merge) ├── gateway/ # 网关装配 │ ├── bootstrap.ts # 启动装配(监听 session/event 等) │ └── middleware-setup.ts # 中间件链配置 ├── transport/ # 传输层 │ ├── inbound.ts # QQ 入站消息 → agent.followup() │ ├── outbound.ts # session/event → QQ sendMarkdown │ ├── outbound-buffer.ts # 流式缓冲 │ ├── streaming-writer.ts # 流式写入 │ ├── reply-target.ts # 回复目标解析 │ ├── msgid-cache.ts # 被动回复 msgid 缓存 │ ├── reply-limiter.ts # 被动回复限流 │ ├── tool-presenter.ts # 工具调用展示 │ └── chunker.ts # Markdown 文本切分 ├── session/ # 会话管理层 │ ├── session-manager.ts # QQ peer → Agent 映射 │ └── idle-evictor.ts # 闲置回收 ├── model/ # 模型路由层 │ ├── model-resolver.ts # 路由解析 │ ├── prefs-store.ts # per-peer 偏好持久化 │ └── settings-reader.ts # settings.yaml 只读 ├── features/ # 交互特性 │ ├── question-channel.ts # ask_user_question 问答通道 │ ├── approval-channel.ts # approval 审批确认通道 │ └── answer-parser.ts # 答案解析 ├── media/ # 多媒体 │ ├── vision-tool.ts # 图片视觉理解 │ ├── send-file-tool.ts # 发送本地文件 │ └── media-cleaner.ts # 媒体 TTL 清理 ├── middleware/ # 中间件 │ ├── question-answer.ts # 问答答案处理 │ └── attachment.ts # 附件处理 ├── shared/ # 共享工具 │ ├── utils.ts # 通用函数 │ ├── scope.ts # scope/peer 提取 │ ├── bind-protocol.ts # 绑定页协议(路由/常量) │ ├── merge-protocol.ts # 合并设置协议(路由/常量) │ └── send-helper.ts # 分块发送 └── commands/ # 斜杠命令
会话路由
sessionKey: qqbot:${appId}:${scope}:${peerId},由 SHA-256 确定性派生 SessionId,重启后可恢复。
解析策略:进程内复用 → 持久化恢复 → 全新创建。
设计原则
- 纯 Cordis 插件 — 遵循 dsh "Plugins, not loop changes" 原则
- 声明式依赖 — inject = ['agents'],不直接耦合其他插件
- 会话隔离 — 每个 QQ 私聊用户/群聊各一个独立智能体
- Preset 支持 — 可通过 agent-presets 服务挂载预设(工具集、提示词等)
- 闲置回收 — 超时自动 dispose 智能体,防止内存泄漏
- Markdown 输出 — 回复以 Markdown 格式发送,支持代码块/表格感知切分
- 图片理解 — 支持 qqbot_describe_image 视觉理解,可分析用户发送的图片
- 附件发送 — 支持 qqbot_send_file 将本地文件发送给用户(默认启用路径白名单)
- 问答互动 — 支持 ask_user_question,单选生成内联按钮(点一个其余变灰)、多选回复编号,逐题推进 + 问题级超时
- 操作确认 — 支持 approval/request,关键操作通过「允许/拒绝」按钮请求用户确认
本地开发
安装依赖
pnpm install
构建
pnpm build
开发模式(watch)
pnpm dev
用 --patch 方式调试
export QQBOT_APPID="xxx" QQBOT_SECRET="xxx"
npx @deepseek-ai/dsh web --patch /path/to/dsh-qqbot/cordis.dev.yml
License
MIT预览分享卡片长什么样 ↓
分享 · DeepSeek Harness Hub
bufan528/dsh-qqbot-merge
把连续多条 QQ 消息合并处理,同一轮只回一条并持续修订
相关插件 · 平台集成
智能体工程课walkinglabs/learn-harness-engineering
一门项目制课程,教你搭建环境、管理状态、做验证、加控制机制,让 AI 编程智能体稳定干活。
★ 21,155已汉化DSH
71.7
视频翻译配音liuzhao1225/YouDub-webui
开源视频本地化工作台:导入视频,识别、翻译并逐句克隆原声,生成字幕和配音成片。
★ 5,792已汉化Cordis
71.4
失败经验库Ikalus1988/MisakaNet
📚 零依赖、由 git 支撑的微课程库,让 AI 智能体异步分享并检索经过验证的调试经验。| https://misakanet.org
★ 550已汉化DSH
71.2
桌面智能体工作区freestylefly/wesight
WeSight 是开源桌面控制台,面向本地 AI 智能体。它帮你安装或复用 Claude…
★ 933已汉化MCP
71
IM 机器人接入xmanrui/dsh-im
让 DeepSeek Harness 触手可及 Connecting DeepSeek Harness
★ 3,110已汉化DeepSeek 客户端
70.9
中国平台研究引擎Jesseovo/last30days-skill-cn
给 AI Agent 的中文互联网研究 Skill:检索 8 大平台最近 30…
★ 2,031已汉化DSH
70.7
这是你的仓库?认领条目