从零搭建一个 QQ AI 陪伴机器人:AstrBot + NapCat 全流程与踩坑实录

这篇不是"跑通就行"的入门文,而是一份跑了半个月、掉了二十多次线之后攒下来的实战笔记。
如果你正打算用 AstrBot + NapCat 接一个 QQ 机器人,或者已经在用、但被"莫名其妙掉线 / 半夜弹黑窗 / 消息不回"折磨,这篇应该能帮你少走很多弯路。


一、先说结论(省流版)

如果你只想要能跑不折腾的方案,直接抄这四条:

  1. NapCat 用官方 NapCat.Shell.Windows.Node 独立包,别用 OneKey 注入版。
  2. 判在线看日志和探活接口,别看 online 字段——它会骗你。
  3. 守护脚本必须加失败熔断,否则死循环重登会把账号玩进风控。
  4. Windows 上一切黑窗,九成来自计划任务和 .cmd 本体,不是你以为的那个程序。

下面逐条展开。


二、整体架构

QQ 客户端(协议层由 NapCat 承担)
  ↕
NapCat(6099 = WebUI 管理台;3011 = HTTP 探活口,发消息也走它)
  ↕ 反向 WebSocket(NapCat → AstrBot ws://127.0.0.1:6199/ws)
AstrBot(6185 = WebUI;6199 = 反向 WS 服务端)
  ├ 对话模型:云端 LLM(OpenAI 兼容接口)
  ├ 视觉模型:本地 Ollama 或免费云模型
  ├ 向量嵌入:bge-m3(本地或云)
  └ 语音:TTS 克隆 / 官方语音合成

分工要说清楚,不然排查方向一定跑偏:

  • "被踢下线"发生在 NapCat ↔ QQ 服务器之间,跟你的 Python 业务代码基本无关。
  • "不回消息"可能是三层里的任意一层:协议端没收到、AstrBot 没触发、模型超时。
  • AstrBot 插件管不到 NapCat 掉线——这是两层的事,所以市面上没有"防掉线插件"这种东西。

三、安装推荐(组件怎么选)

3.1 AstrBot:用 venv + pip,别用 Docker

python -m venv <BOT_ROOT>\astrbot\.venv
<BOT_ROOT>\astrbot\.venv\Scripts\pip.exe install astrbot

为什么不用 WebUI 一键更新:CLI/venv 安装的 AstrBot,点 WebUI 更新会报:

You are running AstrBot via CLI, please use pip or uv tool upgrade

正确升级姿势:

# 1) 先停(杀 6185 监听进程)
# 2) 升级
<BOT_ROOT>\astrbot\.venv\Scripts\pip.exe install -U astrbot==<版本>
# 3) 再起

3.2 NapCat:三种形态,选错会一直掉线

形态包名依赖桌面 QQ稳定性建议
官方 Shell 独立包NapCat.Shell.Windows.Node.zip❌ 不需要⭐⭐⭐⭐⭐首选
OneKey 注入版NapCat.Shell.Windows.OneKey.zip✅ 注入桌面 QQ⭐⭐不推荐
Framework 版NapCat.Framework.zip需自行嵌入⭐⭐⭐进阶

为什么强烈推荐 Shell 独立包:

  1. 自带 node.exe + 配套 wrapper.node,ABI 对得上;
  2. 不依赖桌面 QQ 进程,QQ 更新不会把它带崩;
  3. 不会和桌面版抢同一账号 Token。

⚠️ 这里有个巨坑:不能拿系统 node 去跑旧包的 napcat.mjs。
wrapper.node 是 Electron 原生模块,系统 node 的 ABI 不匹配,会直接报
Error: The specified module could not be found。
必须用官方包里那一套运行时。

Shell 包解压后的标准结构:

Shell\
  node.exe            ← 自带运行时,别替换
  wrapper.node        ← 与 node.exe 配套
  index.js            ← 入口,内部已设置好 NAPCAT_WRAPPER_PATH 等环境变量
  config.json
  napcat\napcat.mjs   ← NapCat 主体
  napcat\config\      ← 配置目录

启动命令就一行:

<NAPCAT_ROOT>\Shell\node.exe index.js -q <BOT_QQ>

-q 是快速登录(复用上次登录态);不加则走扫码。

3.3 版本匹配

  • NapCat 版本要与 QQ 版本对得上(在 config.json 里能看到 curVersion)。
  • 不要盲目追最新。社区里有整片反馈:最新版几小时被踢一次,降级到稳定版反而能连续在线几十小时。
  • 判断标准只有一个:你的环境下连续在线时长。

3.4 模型与嵌入

角色推荐说明
对话任意 OpenAI 兼容云端模型响应快、便宜优先
视觉本地 qwen2.5vl 或免费云模型省钱
嵌入bge-m3(dim 1024)中英混合知识库首选
TTS官方 TTS / 克隆 TTS克隆注意版权,仅私用

知识库参数经验值:chunk 512 / overlap 50 / top_k 50 / top_m 5。

3.5 Windows 上"无窗口启动"

这是 Windows 上最容易被忽略的一节。目标是:开机自启但绝不闪黑窗。

按可靠程度排序:

方式效果备注
pythonw.exe + CREATE_NO_WINDOW⭐⭐⭐⭐⭐Python 项目首选
wscript.exe + Run(cmd, 0, False)⭐⭐⭐⭐⭐通用,VBS 包一层
-WindowStyle Hidden⭐⭐仍会先建 conhost,会闪
.bat 直接跑⭐必闪

Python 启动器示例:

import os, subprocess

CREATE_NO_WINDOW = 0x08000000
DETACHED_PROCESS = 0x00000008

log = open(r"<BOT_ROOT>\logs\napcat-shell.log", "a", encoding="utf-8")
subprocess.Popen(
    [r"<NAPCAT_ROOT>\Shell\node.exe", "index.js", "-q", "<BOT_QQ>"],
    cwd=r"<NAPCAT_ROOT>\Shell",
    stdout=log, stderr=subprocess.STDOUT,
    stdin=subprocess.DEVNULL,
    creationflags=CREATE_NO_WINDOW | DETACHED_PROCESS,
    close_fds=True,
)

再用 wscript.exe 包一层 VBS,彻底避免任何控制台:

On Error Resume Next
Set sh = CreateObject("WScript.Shell")
sh.Run """<PYTHONW_PATH>"" ""<BOT_ROOT>\tools\start_napcat_py.py""", 0, False

注意:node.exe 是控制台程序,CREATE_NO_WINDOW 能压住;但某些 Rust 程序(比如 bsk.exe)会自己建 console,CREATE_NO_WINDOW 无效——这类只能改启动方式,或者启动后 ShowWindow(SW_HIDE)。


四、掉线治理(本文核心)

这一节是我掉了二十多次线换来的,建议收藏。

4.1 第一课:别信 online 字段

很多教程教你查 get_status 返回的 online,看到 true 就放心了。

它会骗你。

online: true 只能说明 Node 进程还活着,不代表 QQ 协议层还连着。我遇到过好几次"端口通、online=true、但发消息全失败"的情况——行话叫脑死亡。

正确判据顺序:

日志  >  端口  >  API 字段

具体做法:给 NapCat 加一个 HTTP 探活口,用它查登录身份,而不是查状态:

curl -X POST http://127.0.0.1:3011/get_login_info \
  -H "Authorization: Bearer <your-token>"

返回里带你的机器人 QQ 号,才算真在线;探活口能连上但返回未登录 → 假在线。

4.2 掉线分型表(对症下药)

现象真实类型处理
6099 有,3011 没有卡在登录看日志,多半登录态失效
端口齐 + online=false注入丢失重启注入即可,不用扫码
端口齐 + 重启后仍 false真·登录态失效认命,扫码
端口全无 + 没进程进程崩了重新拉起
端口齐 + online=true 但发消息全败脑死亡用 L7 探活触发重启

第二行是很多人踩最久的坑:"注入丢失"不等于要扫码,登录态数据还在,重新拉起就能复用。

4.3 注入版和 Shell 版抢 Token(最隐蔽的坑)

如果你同时装过 OneKey 注入版和 Shell 独立包,会看到这种诡异现象:

  • Shell 模式反复掉线,免扫码也失效;
  • 杀了注入版进程,过几分钟它又自己起来了;
  • 新实例启动后 WebUI 端口变成 6100(因为 6099 被老实例占着)。

根因链是这样的:

开机自启 / 守护脚本
  → launcher.bat(注入版)
  → NapCatWinBootMain.exe 注入 QQ.exe
  → 抢同一账号 Token
  → Shell 版被踢

处理办法(缺一不可):

  1. 停掉所有注入版进程(连进程树一起);
  2. 把注入器物理隔离(改名 .disabled 而不是删除,方便回滚):
    • NapCatWinBootMain.exe
    • NapCatWinBootHook.dll
    • 注意根目录和 napcat\ 子目录各有一份,两处都要处理;
  3. 把启动脚本改成 Shell 版;
  4. 重启守护进程(旧进程还在跑旧逻辑)。

4.4 绝不能同时跑两组实例

健康的实例数量是固定的:

1 个主进程:node.exe index.js -q <QQ>
1 个 worker:node.exe ...\napcat\mjs -q <QQ>

如果是 2 主 + 2 worker,说明起了两组,必然互相抢 Token,表现就是"能上能下、反复掉线"。

排查命令(Windows):

Get-CimInstance Win32_Process -Filter "Name='node.exe'" |
  Where-Object { $_.CommandLine -match 'napcat' } |
  Select-Object ProcessId, ParentProcessId, CommandLine

顺手提一句:查这类信息别把 PowerShell 塞进 bash 里,bash 会先吃掉 $_。
用 -EncodedCommand(Base64 + UTF-16LE)传脚本,彻底免疫转义。

4.5 失败熔断:这条能救你的账号

-q 快速登录失败时,NapCat 会直接退出(exit code > 0)。

如果你的守护脚本傻乎乎地"退出就重启、重启还失败、再重启"……

就会形成死循环 → 频繁无效登录 → 触发腾讯风控 → 账号被临时冻结。

必须加熔断:

if not napcat_online and fail_count >= 3:
    if restarts >= 3:
        # 连续 3 次重启仍未上线 → 停手,写标记文件,等人扫码
        write_flag("NAPCAT_NEED_MANUAL.txt")
        log("FUSE: 已停止自动重启,需人工扫码")
    else:
        launch_napcat()
        restarts += 1

恢复上线后,计数和标记一起清零。

4.6 顺手解决:登录后默认显示"离线/隐身"

NapCat 无头登录后,手机端看你的机器人是离线——不是它没上线,是它从没设置过在线状态。

调一次就行:

curl -X POST http://127.0.0.1:3011/set_online_status \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -d '{"status":10,"ext_status":0,"battery_status":0}'

status: 10 = 在线。三个字段缺一个都会报 schema 错误。

注意这个状态不持久,NapCat 重启就回到默认。做法:在守护循环里"检测到在线且未设置"时设一次,掉线时把标记清掉。


五、看门狗设计(四道防线)

一个合格的看门狗,要能区分四种"看起来都像挂了"的情况:

防线判据动作
进程6185 / 6099 端口不通,连续 3 次拉起进程
连接3011 探活失败 ≥120 秒,但进程在重启 AstrBot 逼重连(15 分钟节流)
假死最后一条日志是"收到用户消息",之后 ≥15 分钟无新日志连续 2 票且距上次 >20 分钟 → 重启
重启后AstrBot 刚起来自动重载依赖插件

假死判定是重点,用"日志静默 30 分钟"会疯狂误判(深夜没人说话也会被判死)。改成语义版:

只有当"最后一条日志恰好是收到用户消息、但之后一直没处理"时才怀疑假死。

空闲时完全不判,零误报。

另外两个工程细节:

  • 掉线告警别弹窗:晚上会被骂。写日志 + 状态文件就够。
  • 每日定时重启一次:作为兜底,防累积性卡死(凌晨执行最安全)。

六、Windows 黑窗治理(排查顺序很重要)

"半夜弹一个黑框一闪而过"这种事,按这个顺序查,基本一轮就能定位:

  1. 启动文件夹
    %APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup
  2. 注册表 Run
    HKCU\Software\Microsoft\Windows\CurrentVersion\Run + HKLM\...\Run
  3. 计划任务(最容易漏)
    Get-ScheduledTask | Where-Object { $_.State -ne 'Disabled' } |
      ForEach-Object { "{0,-30} {1}" -f $_.TaskName, ($_.Actions.Execute + ' ' + $_.Actions.Arguments) }
    
  4. 被引用的 .cmd / .bat 本体(超级容易漏!)
    你改了 .vbs,但计划任务指向的是 .cmd,.cmd 里还写着 :loop ... goto loop——黑窗就来自这里。
  5. 进程树和工作目录
    Get-CimInstance Win32_Process -Filter "Name='cmd.exe'" |
      Select-Object ProcessId, ParentProcessId, CommandLine
    
    父进程已退出、cmd.exe 还挂着的,就是孤儿进程。

常见元凶清单:

  • 自愈脚本的 :loop 死循环(一个进程没退干净,还抢锁)
  • 软件更新计划任务(每小时跑一次,必须管理员权限,会闪 UAC)
  • 显卡/游戏辅助类程序每 2~3 秒轮询一次 PowerShell
  • 用了 python.exe 而不是 pythonw.exe 的定时备份任务

七、坑位速查表

#坑解法
1CLI 装的 AstrBot 点 WebUI 更新报错用 pip install -U astrbot==<版本>
2taskkill /F 杀不掉僵死进程改用 .NET [Process]::GetProcessById($pid).Kill()
3系统 node 跑 napcat.mjs 报模块找不到用官方包自带 node.exe + wrapper.node
4机器人反复掉线先查是否两组实例 / 注入版抢 Token
5手机端显示机器人离线调 set_online_status(status=10)
6重启后卡在"二维码已扫描"重启 NapCat(未登录状态重启零损失)
7自动扫码方案失败QQ 禁止相册/长按授权,只能用摄像头
8插件配置改完就丢重载插件会用内存值覆盖文件,改完别立刻 reload
9批量改配置被拒PUT /api/v1/system-config 必须传完整配置树
10PowerShell 传参报"缺少表达式"bash 吃掉了 $,用 -EncodedCommand
11防火墙/杀软导致卡顿把项目目录加进白名单(限制 I/O 的杀软尤其明显)
12掉登录期间的消息QQ 协议端直接丢弃,无法补回复

八、常用 API 速查

# 登录拿 token
POST /api/v1/auth/login   {"username":"<user>","password":"<pwd>"}

# 插件管理
POST /api/plugin/reload   {"name":"<plugin>"}
POST /api/plugin/on|off   {"name":"<plugin>"}
GET/POST /api/plug/<plugin>/get_config|save_config

# 知识库
GET  /api/v1/knowledge-bases
POST /api/v1/knowledge-bases            {"name":"...","embedding_provider_id":"..."}
POST /api/v1/knowledge-bases/{id}/documents   (multipart: file)

# 代发消息(走 NapCat 探活口)
POST http://127.0.0.1:3011/send_group_msg    {"group_id":<gid>,"message":"..."}
POST http://127.0.0.1:3011/send_private_msg  {"user_id":<uid>,"message":"..."}

⚠️ v4.x 的 API 前缀是 /api/v1,不是 /api;而且用 curl.exe 调,PowerShell 的 Invoke-RestMethod 在登录接口会 422。


九、结语

做完这一整套,我的感受是:

机器人项目的稳定性,八成不在"AI 那部分",而在启动链路和守护逻辑。

模型再强,一掉线就是零;守护脚本写错一个循环,账号都能搭进去。

如果只让我留三条建议:

  1. 别用注入版,Shell 独立包能省掉一半莫名其妙的问题。
  2. 判在线用探活接口 + 日志,别信状态字段。
  3. 守护必须熔断,宁可停下等人,也别让它无限重试。

本文基于真实部署记录整理,所有账号、密钥、路径均已脱敏。欢迎在评论区交流你踩过的坑。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐