1. 项目介绍
现在大模型的 API 很便宜,搭一个能上 QQ 的机器人成本不高,就想着自己做一个试试。
整体是三层:
QQ 客户端(手机)
↓ 扫码登录
协议端(NapCat / SnowLuma) ← 登录 QQ,把协议翻译成 OneBot 标准接口
↓ WebSocket(OneBot v11)
AstrBot ← 接消息、调模型、管人格
↓ HTTP
大模型 API
说人话:
- 协议端:让一个程序能以 QQ 号的身份登录,收发消息。没有它,AstrBot 拿不到 QQ 的消息。
- AstrBot:拿到消息之后干什么——调模型、发回复。
- QQ 客户端:用来跟机器人聊天、扫码登录。
协议端有两个选择,NapCat 和 SnowLuma,功能一样,选一个就行。
2. AstrBot
AstrBot 是一个支持插件、多平台的大模型机器人框架。
- GitHub:https://github.com/AstrBotDevs/AstrBot
- 文档:https://astrbot.app/
2.1 基础安装
Docker 一条命令:
docker run -d --name astrbot \
--network host \
-v /www/wwwroot/astrbot/data:/AstrBot/data \
soulter/astrbot:latest
--network host 是为了省掉端口映射的麻烦,后面协议端连它的时候直接用 127.0.0.1。
起来之后浏览器打开 http://服务器IP:6185。
ssh -N -L 16185:127.0.0.1:6185 用户名@服务器IP,然后开 127.0.0.1:16185。理由在《把大肥鱼搬上腾讯云》第三节。默认账号密码在容器日志里:
docker logs astrbot 2>&1 | grep -i password
登进去第一件事:改密码。
2.2 配置模型
AstrBot 里叫「服务提供商」。进「配置 → 服务提供商 → 新增」:
类型 OpenAI 兼容
API Base https://api.deepseek.com/v1
API Key 你的 key
模型 deepseek-chat
保存之后在「默认模型」里选中它。
2.3 配置 QQ 平台
「配置 → 平台 → 新增」:
类型 aiocqhttp
名称 napcat
反向 WS 主机 0.0.0.0
反向 WS 端口 6199
Token (留空,或者跟协议端填一致)
这里有个坑:6199 是 AstrBot 的 OneBot 反向 WS 端口,不是 6185。
6185 是给人看的 WebUI,6199 才是给协议端连的。
保存之后重启:
docker restart astrbot
3. NapCatQQ
NapCat 是基于 TypeScript 的 QQ 协议端,把 QQ 协议转成 OneBot 标准接口。
- GitHub:https://github.com/NapNeko/NapCatQQ
- 文档:https://napneko.github.io/
3.1 安装
docker run -d --name napcat \
--network host \
-e NAPCAT_GID=0 \
-v /www/wwwroot/napcat/config:/app/napcat/config \
-v /www/wwwroot/napcat/qq:/app/.config/QQ \
mlikiowa/napcat-docker:latest
-e NAPCAT_GID=0 那句的意思是让容器里跑出来的文件属主跟着 root 走。但宿主机那两个挂载目录的属主也得对得上,否则 NapCat 读不到登录态,表现是「每次重启都让你重新扫码」——十个里有九个会当成风控,其实是权限。
两个挂载点很关键:
config 协议配置(OneBot 连接信息)
qq 登录态(不挂的话容器重启就要重新扫码)
3.2 扫码登录
docker logs napcat 2>&1 | tail -40
日志里会打印一个字符画的二维码,或者告诉你二维码图片存在哪:
二维码已保存到 /app/napcat/cache/qrcode.png
拷出来:
docker cp napcat:/app/napcat/cache/qrcode.png ./qrcode.png
用手机 QQ 扫,然后手机上点「确认登录」。
第一次登录可能触发设备验证(短信或者好友辅助),这个是正常的。
3.3 连接 AstrBot
NapCat 的 WebUI 在 http://服务器IP:6099。
网络配置 → 新建 → WebSocket 客户端:
名称 astrbot
URL ws://127.0.0.1:6199/ws
重连间隔 30000
启用心跳 是
保存之后看 AstrBot 的日志:
docker logs -f astrbot
连上会打印类似这样:
[aiocqhttp] 1234567890/你的昵称: 你好
前面是 QQ 号,后面是昵称。能刷出这行就是通了。
4. SnowLuma
SnowLuma 是另一个 QQ 协议端。功能和 NapCat 一样——把 QQ 变成 OneBot v11,AstrBot 那头一行都不用改。
但后来我把它换成了默认。换的理由不是功能,是它走的路子不一样;而那个路子,恰好更抗踢。
4.1 它和 NapCat 差在哪
先把一个常见的误传说掉:两个都在跑真的 QQ。不是「一个用协议、一个用客户端」,两个都是 NTQQ 的客户端,一个不少。
NapCat 跑的是一个拆掉了显示层的 QQ。Shell 模式自己把 NTQQ 引擎拉起来(loadQQWrapper,启动时带 --no-sandbox),只是不渲染窗口。它拿消息走两条路:调 QQ 内部的 Node 模块,以及 Hook 原生包——登录时抓数据库密码用的就是后面这条。官网那句「天生无头、不依赖 Electron、内存 50~100MB」是真的,省掉的正是渲染和显示那一层。
SnowLuma 跑的是一个原样的 QQ。镜像里装了 Linux QQ,容器起来先把 QQ 拉起来,配 Xvfb 开出虚拟显示,再往 QQ 进程里注入 hook,读它已经解析好的内部数据包,翻成 OneBot v11。它不省任何一层。
区别就在这一句:NapCat 拆掉显示层去跑 QQ,SnowLuma 原样跑 QQ。
那为什么拆掉显示层会更容易掉线?我的理解是:风控判断的不是「你发了什么」,是「这个设备像不像一个正常人在用」。SnowLuma 那边,登录是在真 QQ 的界面里扫的,会话由原装客户端自己维持,渲染进程、显示层、心跳全是 QQ 自己的——它和一个坐在电脑前的人没有区别。NapCat 把这些拆了,只留引擎和消息通道。
我不敢说这就是它更稳的全部原因。账号本身的质量、常用地、发消息的节奏,影响可能都比协议端大得多。但在我这台机器上,换过去之后确实没再出现过那种「日志显示在线、消息发不出去」的状态。
吃内存这件事也能对上。SnowLuma 是真的在跑一个完整的 QQ:渲染进程、Xvfb、VNC 一个不少,单账号 300MB 起,多开能到 1.2G。NapCat 那 50~100MB 省掉的,省的就是这些。
还有一个顺带的差别,是扫码。NapCat 把二维码打进日志,你得 docker logs 里捞、或者 docker cp 出来再扫。SnowLuma 是 noVNC 网页远程桌面:你看到的是真的 QQ 登录窗口,鼠标点上去扫。
4.2 安装
docker run -d --name snowluma \
--network host \
--restart unless-stopped \
-e TZ=Asia/Shanghai \
-v /www/wwwroot/snowluma/data:/app/data \
<镜像名>
镜像名以它 Releases 页面上写的为准。它的发行包分 full 和 lite:full 自带 Node 运行时,lite 要你机器上本来就有 Node 22 以上。Docker 镜像走的是 full 那一套,这点不用你操心。
别用 latest——协议端发版快,某次拉到坏版本,表现就是「某天开始老掉线」,而且你根本不会想到是镜像的问题。
它有四个端口,一个都别混:
5099—— WebUI。配 OneBot 连接、看日志、看账号状态6081—— noVNC。网页版远程桌面,扫码登录在这里,不在 WebUI 里3000/3001—— OneBot 的 HTTP / WS,默认正向
4.3 连接
这里和 NapCat 不是一回事,得说清楚:NapCat 的 WebUI 在 6099、反向连 AstrBot 的 6199;SnowLuma 的 WebUI 在 5099、正向 WS 在 3001。数字没一个对得上。
最省事的走法还是反向 WS:WebUI → 网络配置 → 新建 WebSocket 客户端,填 ws://127.0.0.1:6199/ws。AstrBot 那头不动。
配完去 http://服务器IP:6081 扫码。看到的是 QQ 的登录窗口,手机扫完还要在手机上点「确认登录」。登录态存在容器里,之后重启不用再扫——前提是 data 那个目录挂出来了。
反过来让 AstrBot 主动连 ws://127.0.0.1:3001/ 也能通,但要改 AstrBot 的平台配置。没必要。
注意:换协议端的时候先把旧的停掉(docker stop napcat),不然两个都连同一个 6199,AstrBot 会同时收到两份事件,表现为机器人回两遍。
4.4 换之前要知道的三件事
- 它不认 NapCat 的登录态。
napcat/qq拷过去没用,必须重新扫码。换的那天,手机得在手边。 - 它更吃内存。单账号常驻 300MB 到 1.2G。2G 的机器上换过去会很紧,4G 才舒服。
- 它不是完全开源。源码可见,但核心 native 组件闭源,许可写着非商业。自己玩没问题,别拿去做生意。
5. 常见问题
5.1 AstrBot 一直显示重连中
先看协议端到底登录没:
docker logs napcat 2>&1 | grep -iE "登录|online|account"
协议端没登录成功的话,AstrBot 那头怎么配都连不上。先解决登录,再解决连接。
5.2 二维码扫了没反应
- 二维码有效期大概两分钟,过期要重新取
- 手机上要点「确认登录」,光扫不够
- 日志里有「二维码已过期」就重新跑一遍流程
5.3 容器重启后要重新扫码
qq 那个目录没挂载,或者挂载路径写错了。
NapCat 的登录态在容器内的 /app/.config/QQ,务必挂出来。
5.4 消息发不出去,但日志显示在线
这个基本就是被风控了。
表现:
- 登录成功,能收到消息
- 发消息失败,或者发出去对方收不到
- 过一段时间被踢下线
没有技术解法,只能换号,或者减少使用频率。
5.5 换协议端要重配吗
不用。 两个都走 OneBot v11,AstrBot 那头不用改。
只要:
- 停掉旧的(
docker stop napcat) - 起新的,连同一个
ws://127.0.0.1:6199/ws - 重新扫码
5.6 改了配置,重启之后又变回去了
这个坑不报错,只会让你以为是自己记错了。
# 你以为改了配置
vim /www/wwwroot/astrbot/data/cmd_config.json
# 重启让它生效
docker restart astrbot
# 结果:被改回去了
AstrBot 启动时会把内存里那份配置写回 cmd_config.json。restart 是「停掉 → 启动」,启动那一刻它拿内存里的旧值覆盖你刚改的文件,而且不报错。
正确顺序是先停、再改、再起:
docker stop astrbot
vim /www/wwwroot/astrbot/data/cmd_config.json
docker start astrbot
stop 和 restart 的差别就在这。踩的时候是改 possibility_reply,写成 0.05,读出来是 0.02——查了半天以为是配置项名字写错了。
6. 几个实践建议
① 容器都用 --network host
省掉端口映射的麻烦。代价是要自己注意端口冲突:
6185 AstrBot WebUI
6199 AstrBot 的 OneBot WS(协议端连这个)
6099 NapCat WebUI
5099 SnowLuma WebUI
6081 SnowLuma 的 noVNC(扫码在这里)
3000 SnowLuma 的 OneBot HTTP API
3001 SnowLuma 的 OneBot WS
② 登录态一定要挂载
不挂的话每次重启都要扫码,而频繁扫码本身就会让风控更容易注意到你。
③ 别频繁重启协议端
重启 = 重新登录 = 一次新的设备握手。
稳定跑着比反复折腾安全。
④ 出问题先看协议端日志
大部分「连不上」的答案都在协议端那头,不在 AstrBot。
7. 参考
- AstrBot 文档:https://astrbot.app/
- NapCat 文档:https://napneko.github.io/
- OneBot v11 标准:https://github.com/botuniverse/onebot-11
写于 2026 年 10 月
发表回复