面向 Linux 服务器的 QQ 群聊 Agent:直接连接外部 OneBot v11 服务,每次触发使用独立的 OpenAI Chat Completions 会话,不依赖 DSH、MCP、Electron 或 Windows 运行环境。
下面这些改动都来自真实群聊里踩过的坑,每条的失败模式与效果写在改动清单里:
- 对话行为:一轮里分条发言、看图先定性再回话、表情包清单常驻系统提示、收尾自检;
- 发送链路:网络级发送重试、QQ 系统表情、消息 id 归一化、内联工具调用兜底解析;
- 贴纸系统:自动收藏与 QQ 收藏优先、查找兜底、同步护栏;
- 主动发言:多个活跃时段、间隔护栏、跳过原因日志、follow-up 提醒、重启补齐漏消息;
- 模型接入:按用途控制思考模式、服务商审核拦截重试、兜底模型切换。
配置项示例见 配置示例;运维命令统一收在 src/ops.js, 用法见 运维工具;本地回归测试在 test/local/。 衍生关系与版权说明见 NOTICE。
运维命令都是程序自带(src/ops.js),只读命令不会碰业务数据:
$ node src/ops.js audit
════════ 1. systemd 用户服务 ════════
[正常] qq-agent-linux.service active
[正常] qq-agent-backup.timer enabled(下次 周日 04:10)
[正常] process-guard.timer enabled(每 10 分钟)
════════ 4. 代码体检 ════════
js 语法: 62/62 通过 未定义调用: 0 处
════════ 9. 运行态 ════════
控制台 API: 200 账号: 在线
===== 自检结论 =====
全部通过(0 项异常)
$ node src/ops.js watch-send --minutes=5
基线:outbox 最新 rowid=17,待处理消息 0 条
SEND_OK 第 1 条新发送:rowid=18(send_message)
群聊里的一轮(示意;真实聊天记录不会公开)——接住对方的话之后, 如果还有自己的半句就分条接着说,合适的场合直接用表情:
群友: 今晚还打不打
机器人:打啊
机器人:我吃完饭了 缓十分钟就来
机器人:[表情包:别墨迹]
OneBot WebSocket
-> 按会话串行入库
-> SQLite/WAL 消息状态机
-> 默认随机 8~12 秒、最长 20 秒有界聚合
-> legacy / threaded / lifecycle 路由
-> 可选持久化线程与检查点
-> 一次性 Agent 会话
-> 会话绑定工具
-> OneBot HTTP
消息只在处理成功后确认。模型或进程失败时,未发送批次自动重试;
发送结果无法确认时进入 held,必须人工核对,避免重复发言。
全新 Linux 机器推荐运行交互式安装器。它会询问部署目录和端口,自动安装 Docker(需要 sudo 确认)、下载 SnowLuma、配置 OneBot、安装 QQ Agent,并生成和 同步全部服务凭据。SnowLuma 已经包含 OneBot,不需要再安装 NapCat 或 Lagrange。
git clone https://github.com/sakurawwwxh/qq-agent-plus.git
cd qq-agent
bash deploy-all.sh默认目录为 /mnt/data/qq-agent,默认对外端口为:
3210:QQ Agent 控制台;5099:SnowLuma WebUI;6081:QQ 登录使用的 noVNC。
OneBot HTTP 3000 和 WebSocket 3001 默认只绑定
127.0.0.1,不会暴露到局域网。安装器不会要求填写本机 IP,而是在完成后自动
检测并打印访问地址。所有生成的凭据保存在
/mnt/data/qq-agent/deployment-access.txt,权限为 0600。
脚本不会擅自修改 UFW、firewalld 或云安全组;需要跨主机访问时,应只向可信
局域网或 VPN 放行上述三个用户入口,不要把 noVNC 或 OneBot 暴露到公网。
首次安装会同时询问模型 Base URL、API Key、模型名和 QQ 白名单。基础设施启动后, 打开脚本给出的 noVNC 地址并扫码登录 QQ,再回到终端按 Enter;脚本会验证 OneBot 登录并询问是否激活。确认旧机器人已停止或排除相同群聊后,也可手动执行:
/mnt/data/qq-agent/app/manage.sh activate --confirm-exclusive无人值守安装可使用:
bash deploy-all.sh --yes --root-dir /mnt/data/qq-agent \
--agent-port 3210 --snowluma-port 5099 --novnc-port 6081 \
--model-base-url https://api.deepseek.com \
--model-api-key "$DEEPSEEK_API_KEY" --model deepseek-chat \
--allow-groups 123456789无人值守模式必须提供模型配置,或者显式增加 --skip-model-config,部署后再从控制台
填写。白名单可以留空,但机器人在配置允许的会话前不会响应。
仅本脚本管理且配置一致的安装允许重跑。开始写入前会核对 Agent 配置与部署记录、
systemd 服务目录、SnowLuma 容器的 Compose 归属/数据卷,以及端口占用。发现已有
非受管安装、残留不完整状态或后台修改过的凭据时,会报错退出,不覆盖配置或重启服务;
--yes 和 --rotate-credentials 都不能绕过这一保护。
可先进行只读检查(不创建目录、下载依赖或修改服务):
bash deploy-all.sh --check-only --root-dir /mnt/data/qq-agent旧 Bridge/SnowLuma 生产环境应使用 deploy.sh 更新 Agent,保留实际数据目录、
监听地址和 OneBot 配置;不要删除已有数据或伪造 .env 来绕过检查。全栈检查还需要
realpath、ss(iproute2);已有 Docker 但无法读取容器时会安全退出。
受管安装重跑会保留 SnowLuma 数据、QQ 登录态和现有凭据。如需同步轮换 Agent、
OneBot、SnowLuma WebUI 与 noVNC 凭据,增加 --rotate-credentials;启用
SnowLuma 2FA 后还需提供 --snowluma-totp。完整参数见:
bash deploy-all.sh --help要求:
- Linux + systemd user service
curl、tar、sha256sum、rsync(缺少 Node.js 时自动安装已校验的 Node 22)- 已运行的 OneBot v11 HTTP 和正向 WebSocket 服务
- OpenAI Chat Completions 兼容模型
git clone https://github.com/sakurawwwxh/qq-agent-plus.git
cd qq-agent
bash deploy.sh \
--install-dir /mnt/data/qq-agent/app \
--data-dir /mnt/data/qq-agent/data \
--host 192.168.31.109 \
--port 3210deploy.sh 不安装 SnowLuma,适合已有 OneBot 服务或只更新 Agent。该脚本会:
- 校验参数、源码和 Node.js
node:sqlite能力 - 安装生产依赖
- 自动检测或安装 Node.js 22 运行时
- 初始化独立数据目录和控制台 Token
- 注册并启用
qq-agent-linux.service - 配置进程异常自动重启
- 检查端口冲突和 systemd unit
- 首次安装以
observe模式启动,更新时保留已有运行模式 - 更新前自动创建代码快照,失败时恢复旧代码、配置和服务
- 排除
.git、.dbg、运行数据、凭据和本地调试记录
查看全部参数:
bash deploy.sh --help如果已有外部备份流程,可以显式跳过代码快照:
bash deploy.sh --install-dir /mnt/data/qq-agent/app \
--data-dir /mnt/data/qq-agent/data --host 192.168.31.109 --port 3210 \
--no-backup首次从旧 Bridge 迁移连接配置时可附加:
--import-bridge /path/to/old/config.json \
--credential-file /path/to/credentials.env该操作只复制配置,不修改旧目录或数据。
更新已有安装:
git pull --ff-only
bash deploy.sh \
--install-dir /mnt/data/qq-agent/app \
--data-dir /mnt/data/qq-agent/data \
--host 192.168.31.109 \
--port 3210更新不会重置现有配置或运行模式。默认在
DATA_DIR/deploy-backups/ 创建部署前代码快照;任何安装、配置、systemd
校验或健康检查失败都会自动恢复旧代码、配置和服务。
部署脚本还会安装独立的 GitHub 更新 service/timer。自动更新默认关闭,可在
“控制 -> 更新部署”配置管理员后恢复。提示与部署都以已发布的 Release 为准
(草稿、预发布和 main 上的日常提交都不算),目标就是该 tag 的提交:先执行单元测试,
再复用 deploy.sh 部署;当前部署已包含最新 Release 时不会回退,比较不出方向时不动手;
失败会回滚、停止自动更新并私聊管理员。
详见自动更新部署。
bash manage.sh status
bash manage.sh logs
bash manage.sh health
bash manage.sh token
bash manage.sh restart
bash manage.sh observe
bash manage.sh activate --confirm-exclusive
bash manage.sh update-status
bash manage.sh update-now --confirm
bash manage.sh backup /path/to/new-backup-dirmanage.sh 之外的运维工具(只读体检、数据备份、发送/登录监控、进程看门狗、
表情名导出、非交互部署、SSH 隧道)统一由 src/ops.js 提供,只用 Node 内置模块:
node src/ops.js help # 全部子命令
node src/ops.js audit # 服务 + 代码 + 数据体检(只读)
node src/ops.js audit-host # 主机体检(只读)
node src/ops.js scan # 未定义调用扫描
node src/ops.js backup --confirm # 停/起服务 + 打包数据目录,只留最近 4 份
node src/ops.js install-timers --print # 查看两个 systemd user 定时器
node src/ops.js console --open # 建 SSH 隧道并打开控制台每个子命令都支持 --help;环境变量、常用示例与远程执行说明见
运维工具文档。
本机看控制台不用碰公网端口:控制台只监听服务器的
127.0.0.1:3210。 Windows 用户直接双击仓库里的console-tunnel.bat——首次输入一次user@host并记住,它会自动从服务器读取控制台令牌、建好 SSH 隧道并免登录打开浏览器 (同时转发 5099 SnowLuma WebUI 与 6081 QQ 扫码登录)。macOS / Linux 或有 Node 的机器 用上面的node src/ops.js console --open,效果相同。
控制台默认端口为 3210。Token 可在
设置 -> 系统 -> 控制台安全 中轮换。
顶层“控制”页是统一运维入口,集中提供 QQ Agent、DSH、Bridge、SnowLuma 和
QQ 远程桌面的入口与在线状态,并可跳转到模型、搜索、OneBot 和控制台 Token
设置。该页还可手动更新、暂停或恢复自动更新。SnowLuma 登录密钥可在该页直接修改,
密钥仅随单次请求发送,不写入 QQ
Agent 配置或前端存储。旧的 3110 门户不再映射。
启用前必须确保旧机器人未处理相同会话,否则会产生双回复。
数据默认位于部署参数指定的 data 目录:
config.json:配置和凭据,权限0600messages.sqlite:消息、租约和出站状态sessions/:每次 Agent 运行记录memory/:群友长期印象和跨 Session 会话交接状态identity-pilot.sqlite:实验性统一 QQ 身份索引(仅启用实验开关后创建)slang-pilot.sqlite:黑话发现、研究任务和两级审批审计(仅启用后创建)daily-moments.json:每日群聊总结、说说决策与发布结果qzone-interactions.json:好友动态未读队列、评论回复和外部写入状态console-access.txt:控制台地址和 Token,权限0600
“设置 -> 实验功能”只管理实验能力的运行开关与固化状态,不承载业务数据或高级
参数。固化后对应功能会取得独立顶层入口;关闭运行开关不会撤销入口或删除历史数据。
“旧印象”页按 QQ 号聚合白名单会话里的身份、别名、消息统计、好友状态及已有会话
印象,并开放受限的 person_memory_lookup 查询工具。“好友管理”页维护主动候选、
收到的好友请求、审批参数及状态。管理员批准主动候选后,系统通过 SnowLuma
send_packet 调用已验证的 QQ 好友协议;仅业务响应明确成功才标记已提交,超时、
断线或响应无法解析会进入“发送结果未知”且禁止自动重试。收到 friend_add 事件
后才闭环为“已成为好友”。收到的好友请求同样需要管理员审批,同意后调用标准
set_friend_add_request 并自动加入私聊白名单。
实现边界见统一身份试点。
“观测”页展示并管理表情包、黑话与黑话研究数据。人物和旧印象由固化后的独立页面 管理,不再混放在通用观测入口。手动上传的表情保存在数据目录;QQ 收藏表情的删除 只会从 AI 资产库隐藏,不会改动 QQ 客户端收藏。表情图片通过控制台鉴权的同源代理 加载,临时 QQ 图片 URL 不会返回给浏览器。 详细口径见资产观测。
“设置 -> 实验功能”还可启用黑话语料库试点。白名单群消息先经过本地零 Token 检测,达到频次和人数门槛后进入“观测 -> 黑话研究”。管理员先批准是否消耗模型 和搜索额度进行研究,再批准是否写入候选库;只有之后人工确认的词条才会进入 Agent 上下文,并遵守群内私有或全局安全作用域。实现细节见 黑话语料库试点。
每次 Agent 运行仍有独立的审计记录。lifecycle 模式会按 threadId
持久化 provider transcript(包括工具轨迹和供应商返回的
reasoning_content),并在下一批消息中按原顺序续接;结构化 handoff
作为生命周期滚动后的压缩状态继续保留。控制台可检查注入历史、最新完整模型
输入以及逐轮 Token/缓存命中。
生命周期默认在上次请求输入达到 32000 Token 时换代;单次 Agent 运行累计预算
为 160000 Token,追加工具轮会在请求前预估预算并安全收尾。
“设置 -> 聊天设置”可分别配置未思考等待的最短值与最长值。每次自动唤醒会在
范围内重新随机,默认 8000–12000ms;连续消息仍由 maxBatchWaitMs=20000
限制从首条待处理消息起的最长聚合时间。生命周期的等待批次会立即归入当前
threadId,控制台不会先显示独立窗口再合并;新 Session 也不会抢占正在查看的
详情。生命周期批次栏支持横向滚动,切换批次时保留详情和批次栏位置。
顶部状态与用量页均按每次模型调用返回的 usage、实际模型和调用时刻计价。
“今日”以及按天统计固定使用 Asia/Shanghai 自然日,不受服务器系统时区影响。
“设置 -> 每日动态”可启用每日群聊总结。任务按上海时间运行,读取当天活跃群的
消息、长期记忆和会话交接;模型可以联网研究、查看近期群图或收藏图,最终自行
决定发布或跳过。发布通过 SnowLuma send_qzone_msg 完成,并按日期记录幂等状态,
服务重启不会自动重复发布结果不明的说说。
每日动态使用专门的说说提示词,完整读取设置中的角色卡和管理员附加规则,
不再附加普通群聊的工具流程。提示词与设计说明见
动态提示词。生成新草稿 不发布;检查正文后可点击
发布这份草稿,直接发送同一份内容,不再次消耗模型 Token。
中断的生成可重新执行;非法模型参数会触发纠错,纠错失败显示“生成失败”。
“发布结果待核对”只能核对空间记录,不能盲目重发。
“设置 -> 动态互动”可按可配置间隔阅览好友动态、决定点赞或评论,并检查自己 动态及已评论动态中的新回复。未阅览内容按最新优先统一提交给模型,超出模型 上下文窗口的旧条目继续保持未读。首次启用默认只建立基线,不突然互动历史内容。 设计、状态与幂等规则见动态互动。
存档页的“主动唤醒”是管理员显式运行:有未读消息时绕过普通响应档位并立即
处理当前批次;没有未读消息时读取该模式配置的最近存档,让模型自行决定是否
发言。该操作仍受运行模式、暂停、白名单、时间控制、并发上限和 held 状态保护。
“设置 -> 时间控制”默认关闭。关闭时忽略全部时间规则,不改变现有唤醒、提示词、
模型请求或消息处理策略。开启后统一使用上海时间,全局规则默认为 DS 低峰:
工作日 00:00–09:00、12:00–14:00、18:00–24:00,周末全天。
每个群聊及私聊均可覆盖为继承全局、DS 低峰、自定义星期/时段或全天活跃。
自定义允许跨午夜,例如周五 22:00–02:00 延续至周六凌晨;
00:00–24:00 为全天,自定义空时间表表示始终非活跃。
非活跃期消息与拍一拍仅归档,不触发 AI,不积压自动补回复;进入活跃期后新消息 按原模式处理,已归档消息仍可作为历史上下文。模型请求、工具轮、重试、记忆整理 及发送均受时间门控。每日动态与控制台模型测试遵循全局时间表,每日汇总还会排除 当前非活跃的会话;被时间限制挡住的定时动态推迟至活跃窗口。
规则修改即时生效。在途请求跨入非活跃期会被中止,后续请求与发信被拦截;
供应商对已经收到的请求仍可能计费,无法承诺撤销这部分 Token。
发送结果不明确的 held 记录不会因时间切换而被丢弃或自动重发。
聊天、密钥、Token 和运行数据均被 Git 忽略。
npm ci --omit=dev --ignore-scripts
npm run test:unit # 单元测试
npm run test:local # 本地回归(自动用临时数据目录,不碰生产数据)
node src/ops.js scan --strict
npm audit --omit=dev
bash -n deploy.sh manage.shCI(GitHub Actions)在每次推送和 PR 上跑:语法检查、未定义调用扫描(严格模式)、 单元测试与本地回归。当前基线上有 5 个遗留失败用例被显式跳过,清单与实测表现见 已知问题。
详细说明见 Linux 运维手册。 试验性三模式对话引擎见 Conversation Modes;早期参与者续接方案见 Threaded Conversation Pilot。
本项目使用 MIT 许可(见 LICENSE);衍生关系与第三方版权见 NOTICE。 OneBot 协议端是独立软件,遵循其自身许可。
