从零搭建 QQ 官方机器人:腾讯云 Ubuntu 接入 Qwen 完整实战文章封面

AI 实践 / ARTICLE

从零搭建 QQ 官方机器人:腾讯云 Ubuntu 接入 Qwen 完整实战

记录在腾讯云 Ubuntu 上使用 qq-botpy 接入 QQ 官方机器人,并通过 Qwen 实现私聊与群聊问答的完整部署、排错和运维过程。

这篇文章记录一条已经实际跑通的路线:QQ 用户发送消息后,腾讯的机器人网关通过 WebSocket 把事件推送给 Ubuntu 上的 Python 程序,程序调用阿里云百炼 Qwen,再把回答发回 QQ。最终效果同时覆盖群聊 @机器人 和私聊两种场景。

本文使用 qq-botpy,因为它是本次部署验证过的实现。腾讯目前还提供较新的 qqbot-agent-sdk;如果你准备新建复杂的 Agent 项目,可以再评估新 SDK,但不要在排错阶段混用两套 API。

本文测试日期为 2026-08-27。示例中的 qwen3.8-max 是本次实际跑通的模型,但模型权限、名称和计费策略可能调整,重新部署时请先在阿里云控制台确认。

整体架构与完成标准

一次完整请求会依次经过以下链路:

QQ 私聊 / 群聊 @机器人
        ↓
QQ 官方机器人 WebSocket 网关
        ↓
腾讯云 Ubuntu 上的 qq-botpy
        ↓
消息路由与指令处理
        ↓
Qwen OpenAI 兼容接口
        ↓
生成答案并通过 QQ 回复

QQ 消息经 WebSocket、Ubuntu 服务和 Qwen 返回回复的数据流

各层职责如下:

层级 组件 职责
接入层 QQ 开放平台 创建机器人、分配 AppID/AppSecret、管理测试用户
传输层 WebSocket 接收 QQ 事件,无需自己暴露 HTTP 回调端口
运行层 Ubuntu + Python 常驻运行机器人进程
路由层 router.py 识别帮助、状态和普通问答
模型层 ai.py 调用 Qwen 的 OpenAI 兼容接口
运维层 systemd 后台运行、自动重启、开机启动和日志查看

完成部署后,应满足以下标准:

  • 群聊中 @机器人 你好 能收到回复;
  • 测试用户私聊机器人能收到回复;
  • 帮助、状态 等本地命令不依赖模型也能工作;
  • 服务重启后能自动恢复,日志可通过 systemd 查询。

准备账号与凭证

开始前准备以下资源:

项目 要求
QQ 开放平台 已创建的官方机器人,以及可用的 AppID、AppSecret
腾讯云服务器 Ubuntu 22.04 或 24.04,建议至少 2 核 2 GB
网络 服务器可访问 QQ 网关与阿里云百炼接口
阿里云百炼 已开通模型服务、创建 Workspace、取得 API Key
测试账号 已加入机器人体验名单的 QQ 账号
本地客户端 任意 SSH 客户端,例如 Windows Terminal

本文会用到四个关键配置:

QQ_APPID
QQ_SECRET
QWEN_API_KEY
QWEN_BASE_URL

QQ_APPID 和 QQ_SECRET 来自 QQ 开放平台,QWEN_API_KEY 来自阿里云百炼。北京地域默认使用 Workspace 专属兼容端点:

https://<YOUR_WORKSPACE_ID>.cn-beijing.maas.aliyuncs.com/compatible-mode/v1

这里的 Workspace ID 和 API Key 不是同一个值。若使用其他地域,应以控制台和阿里云官方文档给出的 Base URL 为准。

配置 QQ 机器人开放平台

创建机器人并取得凭证

登录 QQ 开放平台,新建机器人应用,进入开发设置页面,记录 AppID,并生成 AppSecret。

QQ 开放平台机器人创建入口示意

截图只用于说明入口位置。不同时间的控制台布局可能略有差异,以实际页面为准。

配置服务器公网 IP

在机器人开发设置中,把腾讯云服务器的公网 IPv4 加入 IP 白名单。可以在服务器执行:

curl -4 ifconfig.me

若服务器绑定了弹性公网 IP,应填写该固定地址。使用 NAT、代理或多出口网络时,要确认 QQ 平台实际看到的出口 IP。

添加体验用户

机器人未正式发布前,只允许体验名单中的账号测试。把自己的 QQ 号添加为体验用户,并在手机或桌面 QQ 中完成授权。群聊测试时,还要按平台提示把机器人加入允许测试的群。

选择事件接收方式

本文使用 qq-botpy 主动建立 WebSocket 长连接,不需要额外部署公网 HTTP 回调服务。代码会监听:

  • on_group_at_message_create:群聊中用户 @机器人;
  • on_c2c_message_create:用户与机器人的单聊消息。

创建客户端时必须开启 botpy.Intents(public_messages=True),否则相关公开消息事件不会进入程序。

准备腾讯云 Ubuntu 环境

先通过 SSH 登录服务器:

ssh root@<YOUR_SERVER_IP>

确认系统和 Python 版本:

cat /etc/os-release
python3 --version

安装基础环境:

apt update
apt install -y python3 python3-venv python3-pip curl ca-certificates

实验阶段先在 /root/qqbot 中操作,便于快速定位问题:

mkdir -p /root/qqbot
cd /root/qqbot
python3 -m venv venv
source venv/bin/activate
python -m pip install --upgrade pip

看到终端前出现 (venv),说明虚拟环境已激活。之后安装依赖:

pip install qq-botpy openai python-dotenv

/root/qqbot 只适合实验。正式运行时会先把源码迁移到 /opt/qqbot,再交给普通用户管理。普通用户无法穿过 /root 目录,仅对 /root/qqbot 执行 chown 并不能解决访问问题。

项目目录与文件职责

项目最终结构如下:

qqbot/
├── .env
├── .env.example
├── .gitignore
├── requirements.txt
├── config.py
├── ai.py
├── router.py
├── bot.py
└── venv/
文件 作用
.env 保存运行时配置
.env.example 配置模板
config.py 加载并检查配置
ai.py 创建 Qwen 客户端并完成对话请求
router.py 处理本地指令与 AI 问答
bot.py 连接 QQ、接收事件、发送回复
requirements.txt 固化最小依赖

从零创建全部配置与代码

以下文件可直接复制。建议逐个创建,遇到问题时更容易定位是哪一层出错。

.env

QQ_APPID=<YOUR_QQ_APPID>
QQ_SECRET=<YOUR_QQ_APP_SECRET>

QWEN_API_KEY=<YOUR_QWEN_API_KEY>
QWEN_BASE_URL=https://<YOUR_WORKSPACE_ID>.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
QWEN_MODEL=qwen3.8-max

保存后立即限制权限:

chmod 600 .env

config.py

import os

from dotenv import load_dotenv


load_dotenv()

QQ_APPID = os.getenv("QQ_APPID", "").strip()
QQ_SECRET = os.getenv("QQ_SECRET", "").strip()
QWEN_API_KEY = os.getenv("QWEN_API_KEY", "").strip()
QWEN_BASE_URL = os.getenv("QWEN_BASE_URL", "").strip()
QWEN_MODEL = os.getenv("QWEN_MODEL", "qwen3.8-max").strip()


def require_config() -> None:
    required = {
        "QQ_APPID": QQ_APPID,
        "QQ_SECRET": QQ_SECRET,
        "QWEN_API_KEY": QWEN_API_KEY,
        "QWEN_BASE_URL": QWEN_BASE_URL,
        "QWEN_MODEL": QWEN_MODEL,
    }
    missing = [name for name, value in required.items() if not value]
    if missing:
        raise RuntimeError(f"缺少环境变量: {', '.join(missing)}")

程序只判断配置是否存在,不打印任何真实值。

ai.py

from openai import AsyncOpenAI

from config import QWEN_API_KEY, QWEN_BASE_URL, QWEN_MODEL


client = AsyncOpenAI(
    api_key=QWEN_API_KEY,
    base_url=QWEN_BASE_URL,
)

SYSTEM_PROMPT = """你是一个运行在 QQ 官方机器人中的中文助手。
回答要准确、简洁、友好;不确定时明确说明,不要编造事实。
"""


async def ask_qwen(user_text: str) -> str:
    response = await client.chat.completions.create(
        model=QWEN_MODEL,
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_text},
        ],
        temperature=0.7,
    )

    answer = response.choices[0].message.content
    if not answer:
        return "模型没有返回有效内容,请稍后重试。"
    return answer.strip()

这里使用 OpenAI 兼容客户端,但请求实际发往 QWEN_BASE_URL。qwen3.8-max 是本次跑通配置;若收到模型不存在或无权限错误,应先回到百炼控制台检查 Workspace、地域和模型授权,而不是盲目改代码。

router.py

from ai import ask_qwen
from config import QWEN_MODEL


HELP_TEXT = """可用指令:
- 帮助:查看使用说明
- 状态:查看机器人状态
- 其他内容:交给 Qwen 回答
"""


async def route_message(text: str) -> str:
    clean_text = text.strip()

    if not clean_text:
        return "请发送你想咨询的内容。"

    if clean_text in {"帮助", "/help", "help"}:
        return HELP_TEXT

    if clean_text in {"状态", "/status", "status"}:
        return f"机器人运行正常,当前模型:{QWEN_MODEL}"

    return await ask_qwen(clean_text)

本地指令不调用 Qwen。因此当 帮助 可以回复、普通问题不能回复时,问题大概率位于模型配置或网络层,而不是 QQ 接入层。

bot.py

import botpy
from botpy.message import C2CMessage, GroupMessage

from config import QQ_APPID, QQ_SECRET, require_config
from router import route_message


async def process_message(text: str) -> str:
    try:
        return await route_message(text)
    except Exception as exc:
        print(f"处理消息失败: {type(exc).__name__}: {exc}")
        return "服务暂时不可用,请稍后再试。"


class MyClient(botpy.Client):
    async def on_ready(self):
        print(f"机器人已登录: {self.robot.name}")

    async def on_group_at_message_create(self, message: GroupMessage):
        user_id = message.member_openid
        text = message.content.strip()
        print(f"收到群聊消息 user={user_id}, length={len(text)}")

        answer = await process_message(text)
        await message.reply(msg_type=0, content=answer[:1900])

    async def on_c2c_message_create(self, message: C2CMessage):
        user_id = message.author.user_openid
        text = message.content.strip()
        print(f"收到私聊消息 user={user_id}, length={len(text)}")

        answer = await process_message(text)
        await message.reply(msg_type=0, content=answer[:1900])


if __name__ == "__main__":
    require_config()
    intents = botpy.Intents(public_messages=True)
    client = MyClient(intents=intents)
    client.run(appid=QQ_APPID, secret=QQ_SECRET)

这里保留了 on_group_at_message_create、on_c2c_message_create 和 public_messages=True。日志记录用户 OpenID 与消息长度。回复暂时截断到 1900 个字符,避免超过消息长度限制;正式产品可扩展为分段回复。

requirements.txt

qq-botpy
openai
python-dotenv

确认功能稳定后,可以用当前环境生成带版本号的锁定文件:

pip freeze > requirements.lock.txt

.env.example

QQ_APPID=<YOUR_QQ_APPID>
QQ_SECRET=<YOUR_QQ_APP_SECRET>
QWEN_API_KEY=<YOUR_QWEN_API_KEY>
QWEN_BASE_URL=https://<YOUR_WORKSPACE_ID>.cn-beijing.maas.aliyuncs.com/compatible-mode/v1
QWEN_MODEL=qwen3.8-max

.gitignore

.env
venv/
__pycache__/
*.py[cod]
*.log
.idea/
.vscode/

启动与功能测试

不要一上来就启动完整机器人。按配置、网络、模型、代码、QQ 的顺序验证,能显著减少排错范围。

只检查配置是否加载

python - <<'PY'
from config import QQ_APPID, QQ_SECRET, QWEN_API_KEY, QWEN_BASE_URL, QWEN_MODEL

print("QQ_APPID:", bool(QQ_APPID))
print("QQ_SECRET:", bool(QQ_SECRET))
print("QWEN_API_KEY:", bool(QWEN_API_KEY))
print("QWEN_BASE_URL:", bool(QWEN_BASE_URL))
print("QWEN_MODEL:", QWEN_MODEL)
PY

输出包含配置是否已加载的布尔值和当前模型名。

检查基础网络

curl -I https://api.sgroup.qq.com
curl -I "https://<YOUR_WORKSPACE_ID>.cn-beijing.maas.aliyuncs.com"

curl -I 可能收到 404 或 405,这不一定代表网络失败;只要能完成 DNS、TLS 并收到 HTTP 响应,就说明基础链路通常可达。

单独调用 Qwen

python - <<'PY'
import asyncio
from ai import ask_qwen

print(asyncio.run(ask_qwen("只回复:Qwen 连接成功")))
PY

这一步成功,说明 API Key、Base URL、模型名与 Python 客户端组合正确。

检查语法与导入

python -m py_compile config.py ai.py router.py bot.py
python -c "import botpy, openai, dotenv; print('imports ok')"

启动机器人

python bot.py

看到“机器人已登录”后,在 QQ 中按下表测试:

场景 输入 预期结果
群聊 @机器人 帮助 返回本地帮助文字
群聊 @机器人 用一句话解释 WebSocket 返回 Qwen 答案
私聊 状态 返回当前模型与运行状态
私聊 任意正常问题 返回 Qwen 答案
空消息 只 @ 不提问 提示发送咨询内容

常见报错与分层排查

排错时不要同时改多个文件。先根据现象确定层级,再只处理该层。

现象或报错 常见原因 处理方式
python: command not found 未进入虚拟环境或系统只有 python3 执行 source venv/bin/activate,或使用 python3
No module named botpy 依赖装到了错误的 Python 对比 which python 与 python -m pip -V,再重新安装
No module named dotenv 缺少 python-dotenv 执行 python -m pip install python-dotenv
配置布尔值为 False .env 不在当前目录、变量名错误或格式错误 确认工作目录、文件名和等号两侧内容
QQ 登录返回 100007 AppID/AppSecret 错误、应用状态或环境不匹配 回到 QQ 平台重新确认凭证和机器人配置
一直连接不上 QQ 出口 IP、DNS、时间或防火墙异常 核对公网 IP 白名单,执行 timedatectl 与网络检查
curl 根路径返回 404 请求的是服务根路径而非具体 API 只把它作为网络可达性参考,不据此判定 API 不可用
Qwen 返回 401 API Key 无效、过期或属于其他 Workspace 重新创建 Key,并确认 Key 与 Workspace 对应
Qwen 返回 404 Base URL、地域、模型名错误或模型未授权 使用控制台显示的专属端点,核对 QWEN_MODEL
Qwen 返回 429 触发限流或余额/配额不足 降低并发、稍后重试,并检查模型额度
ImportError: set_user_model 把旧版示例和当前 SDK 混用 删除旧接口调用,按本文的 AsyncOpenAI 实现
IndentationError 粘贴时混入 Tab 或缩进损坏 用 python -m py_compile 定位行号并统一四空格
机器人在线但群聊无回复 未 @、未开启公开消息 Intent、体验群未配置 检查事件函数名与 public_messages=True
私聊无回复 测试账号不在体验名单或事件未触发 先检查平台权限,再看终端是否打印收到消息
帮助能回、普通问题不能回 QQ 链路正常,Qwen 调用失败 单独运行 ask_qwen 测试并查看错误码
回复发送失败或被截断 内容过长或包含平台不接受的格式 保持截断,后续实现分段和格式清理

可以用这个顺序快速定位:

  1. 终端完全没有收到事件:查 QQ 平台、体验用户、群和 Intents;
  2. 收到事件,但 帮助 也失败:查 botpy 回复接口和程序异常;
  3. 帮助 成功,普通问答失败:查百炼端点、Key、模型权限和网络;
  4. Qwen 测试成功,但 QQ 回复失败:查消息长度、内容格式与平台限制。

systemd 后台运行

先迁移到正式目录

实验完成后创建普通用户和正式目录:

adduser --system --group --home /opt/qqbot qqbot
mkdir -p /opt/qqbot
cp /root/qqbot/config.py /opt/qqbot/
cp /root/qqbot/ai.py /opt/qqbot/
cp /root/qqbot/router.py /opt/qqbot/
cp /root/qqbot/bot.py /opt/qqbot/
cp /root/qqbot/requirements.txt /opt/qqbot/
cp /root/qqbot/.env /opt/qqbot/
cp /root/qqbot/.env.example /opt/qqbot/
cp /root/qqbot/.gitignore /opt/qqbot/
chown -R qqbot:qqbot /opt/qqbot
chmod 600 /opt/qqbot/.env

不要复制旧虚拟环境,直接以运行用户重新创建:

sudo -u qqbot python3 -m venv /opt/qqbot/venv
sudo -u qqbot /opt/qqbot/venv/bin/python -m pip install --upgrade pip
sudo -u qqbot /opt/qqbot/venv/bin/python -m pip install -r /opt/qqbot/requirements.txt

先以前台方式验证正式目录:

cd /opt/qqbot
sudo -u qqbot /opt/qqbot/venv/bin/python bot.py

确认能登录后按 Ctrl+C 停止,再配置 systemd。

创建 systemd 服务

新建 /etc/systemd/system/qqbot.service:

[Unit]
Description=QQ Official Bot with Qwen
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=qqbot
Group=qqbot
WorkingDirectory=/opt/qqbot
ExecStart=/opt/qqbot/venv/bin/python /opt/qqbot/bot.py
Restart=on-failure
RestartSec=5
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target

加载并启动:

systemctl daemon-reload
systemctl enable --now qqbot
systemctl status qqbot --no-pager

持续查看日志:

journalctl -u qqbot -f

查看最近 100 行:

journalctl -u qqbot -n 100 --no-pager

更新代码后的标准操作是先检查语法,再重启服务:

cd /opt/qqbot
/opt/qqbot/venv/bin/python -m py_compile config.py ai.py router.py bot.py
systemctl restart qqbot
systemctl status qqbot --no-pager

后续扩展路线

基础版本跑通后,可以按优先级逐步增强:

优先级 扩展 实现建议
P0 超时与重试 为模型请求增加超时;只对短暂网络错误和 429 做指数退避
P0 并发保护 用信号量限制同时调用模型的数量,防止瞬间打满额度
P1 分段回复 按安全长度切分长答案,逐段发送并保留顺序
P1 多轮上下文 以用户 OpenID 为键保存最近若干轮,并设置过期时间
P1 限流与黑名单 按用户和群设置每分钟请求次数,阻止滥用
P2 工具调用 接入天气、知识库或业务 API,并明确权限边界
P2 可观测性 增加请求 ID、耗时、错误类型和模型用量,不记录敏感正文
P2 内容安全 根据业务场景增加输入输出审核与人工兜底
P3 SDK 迁移评估 对比 qqbot-agent-sdk 的 Agent、插件和部署能力后再迁移

多轮对话不要无限累积。建议保留最近 6~10 轮,并为每个会话设置 30 分钟左右的过期时间;否则上下文会越来越长,延迟和费用也会持续上升。

重建检查清单

如果需要在一台新服务器上重建,可以照下面的顺序执行:

  • 创建 QQ 官方机器人,保存 AppID/AppSecret;
  • 设置公网出口 IP、体验用户和体验群;
  • 开通阿里云百炼 Workspace、API Key 与目标模型权限;
  • 安装 Python、venv、pip、curl 和 CA 证书;
  • 在实验目录创建虚拟环境并安装依赖;
  • 创建 .env、config.py、ai.py、router.py、bot.py;
  • 依次完成配置、网络、Qwen、语法和 QQ 消息测试;
  • 把源码迁移到 /opt/qqbot,重新创建虚拟环境;
  • 使用普通用户运行服务;
  • 配置 systemd、自动重启与开机启动;
  • 设置费用告警和日志策略。

官方资料

到这里,一个可维护的 QQ 官方机器人就完成了。最关键的经验不是把所有代码一次粘贴完,而是始终按“平台事件 → 本地路由 → 模型接口 → QQ 回复”的层次验证。这样无论将来替换模型、增加知识库,还是迁移到新的 SDK,都能快速判断问题真正发生在哪一层。