想给 QQ 群加一个能自动回答问题、写文案、查资料的 AI 机器人,在当下已经不算复杂。核心是打通两条链路:一条是 DeepSeek 开放平台提供的模型 API,一条是 QQ 机器人开放平台提供的事件消息通道。真正让新手卡住的,往往是中间那一层:申请凭证、订阅事件、异步调用模型、回复消息,任何一步没接上,机器人都会“假装存在”但完全没有反应。
这篇文章会用 Python 3 从零开始,带你完整实现 DeepSeek 接入 QQ 机器人的全过程。全程使用 DeepSeek 官方 API 和 QQ 官方机器人 SDK,不涉及非官方协议。内容包括:平台注册与凭证申请、本地环境搭建、完整可运行代码、启动验证、高频报错排查,以及工程化建议。即使你之前没有写过 QQ 机器人,只要照着操作,也能跑通。
1. 接入方案与整体原理
1.1 为什么优先选官方渠道
不少人在搜索 QQ 机器人接入时,会看到各种基于第三方协议的方案。这些方案确实功能花哨,但本质上大多通过逆向、模拟客户端等方式接入,存在账号风控、封号、隐私泄露等风险,而且很容易随着 QQ 客户端更新而失效。
本文采用官方渠道,原因很明确:
- 稳定:官方接口和官方 SDK 会持续维护,不会因为客户端升级而突然失效。
- 安全:AppID、AppSecret、API Key 都掌握在自己手里,不经过第三方转发。
- 合规:符合平台规则,适合学习、测试以及正式上线的业务场景。
- 省事:不需要额外搭建消息中转服务,官方 SDK 使用 WebSocket 长连接收消息,本地就能跑。
1.2 消息链路拆解
整个系统最核心的消息流转过程,可以拆成下面几个步骤:
用户 @机器人 或 私聊机器人 ↓ QQ 服务器推送事件 ↓ 官方 SDK(botpy)收到消息事件 ↓ 程序提取用户文本内容 ↓ 调用 DeepSeek API,携带参数请求模型 ↓ DeepSeek 返回 AI 回复文本 ↓ 程序调用 QQ 官方 API 把回复发回群/私聊从代码角度来说,我们要做的事其实只有三件:
- 收到 QQ 消息事件。
- 把消息文本提交给 DeepSeek。
- 把 DeepSeek 的回复再发出去。
1.3 技术选型:WebSocket 和 WebHook 怎么选
QQ 机器人官方提供了两种接收消息的方式。
WebSocket 模式:
- 由 SDK 主动和 QQ 服务器建立长连接。
- 不需要公网 IP,不需要域名,不需要配置 HTTPS 证书。
- 适合本地开发和个人项目。
- 适合新手入门。
WebHook 模式:
- 由 QQ 服务器向你的公网接口推送事件。
- 需要公网服务器、域名和 HTTPS 证书。
- 适合正式线上服务,也适合和现有后端服务集成。
- 配置相对复杂。
本文选择 WebSocket 模式,原因很简单:零公网成本,本地就能完成全部联调。
2. 前置准备:DeepSeek 开放平台配置
在写代码之前,需要先把几个“通行证”准备好。第一步就是 DeepSeek 开放平台的 API Key。
2.1 注册并创建 API Key
打开 DeepSeek 开放平台,使用手机号或邮箱注册登录。登录后进入控制台,找到 API Key 管理页面。
创建 API Key 时注意以下几点:
- API Key 通常以
sk-开头,创建完成后只会完整显示一次,之后无法再次查看,必须先复制保存到本地。 - 不要把 API Key 直接写在代码里提交到 GitHub,后面会专门讲密钥管理。
- 一个账号可以创建多个 Key,建议区分开发环境、测试环境和生产环境,方便出问题时单独吊销。
2.2 确认 API 地址与模型名
DeepSeek 官方接口兼容 OpenAI 的调用格式。也就是说,Python 代码里可以直接使用openai这个包,把base_url指向 DeepSeek 的接口地址。
常用的 model 名称有两个:
deepseek-chat:通用对话模型,适合绝大多数问答、写作、总结场景。deepseek-reasoner:深度推理模型,适合复杂逻辑推导,但响应速度通常比 chat 模型慢。
部分第三方中转服务可能提供其他自定义模型名,本文以 DeepSeek 官方模型名为准。
需要注意:平台功能和模型名称可能随着版本调整,具体以 DeepSeek 开放平台文档为准。
2.3 费用与配额提示
DeepSeek 的计费方式是按 token 计费。token 可以简单理解为“模型看到的字符片段”,中文、英文、标点都会消耗 token。
对个人开发来说,日常测试和群聊机器人调用量不大,成本一般可控。但建议:
- 在控制台设置“余额预警”,避免不知不觉消耗完。
- 代码中限制单次回复长度,控制
max_tokens。 - 不带上下文记忆时,每次请求只包含当前问题,避免历史消息反复计费。
3. 前置准备:QQ 机器人开放平台配置
DeepSeek 这边准备好之后,接下来是 QQ 官方机器人平台。
3.1 创建机器人应用
进入 QQ 开放平台,使用 QQ 扫码登录。登录后找到“机器人”相关入口,创建一个新的机器人应用。
创建时需要填写:
- 机器人名称。
- 头像。
- 功能介绍。
这些信息会和机器人形象直接相关,建议认真填写。名称和头像在审核阶段会被检查,不要包含明显违规或夸张表述。
3.2 获取 AppID 与 AppSecret
机器人创建完成后,进入开发设置页面,能看到两个关键字段:
- AppID:机器人的唯一身份标识。
- AppSecret:机器人的密钥,用于 SDK 鉴权连接。
AppSecret 的敏感程度和 DeepSeek API Key 一样,不要泄露、不要提交到公开仓库。后续代码中,这两个值会传给 botpy SDK 的client.run()方法。
3.3 配置事件订阅
要让机器人能处理群消息和私聊消息,必须在开放平台打开对应的事件订阅。
在开发设置里选择 WebSocket 模式,然后申请以下事件权限:
- 接收群聊消息,通常对应“群 @ 机器人”事件。
- 接收 C2C 消息,也就是用户私聊机器人。
不同版本的平台界面可能略有差异,但核心逻辑一致:申请权限、等待审核或加入沙箱白名单、然后才能收到对应消息事件。如果只申请了群消息权限,私聊事件就永远不会触发。
3.4 沙箱环境与上线前准备
QQ 开放平台提供了沙箱测试机制。沙箱模式下,机器人不是对所有用户开放,而是只对指定的测试人员或白名单用户生效。这对调试阶段非常友好,可以避免机器人还没写好就被陌生人反复调用。
在沙箱里测试时:
- 需要把测试 QQ 号添加到白名单。
- 需要把机器人拉到一个测试群,群里至少要有白名单用户。
- 群内测试时,必须是“@机器人 内容”的格式,群机器人通常不支持自动响应所有消息。
功能稳定、准备对外发布时,再提交上架审核。审核通过后机器人才能在更多群或用户范围内使用。
4. 本地环境搭建
平台配置完成,接下来进入代码环节。
4.1 安装 Python
本文示例使用 Python 3,建议使用 3.9 及以上版本。可以在终端输入以下命令确认版本:
python --version如果没有安装 Python,去官网下载对应系统版本安装即可。Windows 用户安装时记得勾选“Add Python to PATH”。
4.2 创建项目目录
在本地新建一个项目目录,例如qq-deepseek-bot:
mkdir qq-deepseek-bot cd qq-deepseek-bot4.3 安装依赖
在项目里创建虚拟环境,然后安装依赖。
Linux / macOS 执行:
python3 -m venv venv source venv/bin/activateWindows 执行:
python -m venv venv venv\Scripts\activate然后安装两个核心库:
pip install qq-botpy openai说明一下它们的作用:
qq-botpy:腾讯官方提供的 QQ 机器人 Python SDK,封装了 WebSocket 连接、事件接收、消息发送等能力。openai:官方 OpenAI Python SDK,DeepSeek 接口兼容 OpenAI 格式,因此可以直接用它来调用 DeepSeek 模型。
为了统一管理依赖,可以在项目目录下创建requirements.txt:
qq-botpy openai后续换环境时,直接执行:
pip install -r requirements.txt5. 完整代码实现
环境准备完毕后,开始编写核心代码。
5.1 目录结构与配置入口
建议把代码拆成三个文件,职责清晰,后面扩展也方便:
qq-deepseek-bot/ ├── config.py # 配置文件,负责读取环境变量 ├── deepseek_client.py # DeepSeek 调用封装 ├── bot.py # QQ 机器人主程序 └── requirements.txt # 依赖清单先创建config.py:
# 文件路径:config.py import os # DeepSeek 配置 DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "") DEEPSEEK_BASE_URL = os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com") DEEPSEEK_MODEL = os.getenv("DEEPSEEK_MODEL", "deepseek-chat") # QQ 机器人配置 QQ_APP_ID = os.getenv("QQ_APP_ID", "") QQ_APP_SECRET = os.getenv("QQ_APP_SECRET", "") # 回复消息最大长度,避免触发平台长度限制 MAX_REPLY_LEN = 500这里使用os.getenv读取环境变量,好处是敏感信息不会硬编码在代码文件里。如果某个环境变量没有设置,程序会使用空字符串,后续启动时会统一检查。
5.2 封装 DeepSeek 客户端
接下来创建deepseek_client.py,负责调用 DeepSeek API:
# 文件路径:deepseek_client.py import asyncio from openai import AsyncOpenAI from config import ( DEEPSEEK_API_KEY, DEEPSEEK_BASE_URL, DEEPSEEK_MODEL, ) _client = None def get_client(): global _client if _client is None: _client = AsyncOpenAI( api_key=DEEPSEEK_API_KEY, base_url=DEEPSEEK_BASE_URL, ) return _client async def ask_deepseek(user_content: str, system_content: str = ""): """ 调用 DeepSeek 对话接口,返回文本回复。 """ if not DEEPSEEK_API_KEY: return "DeepSeek API Key 未配置,请检查环境变量 DEEPSEEK_API_KEY。" if not user_content.strip(): return "你好像没有输入内容哦。" system_content = system_content or "你是一个友好的QQ群AI助手,回答简洁、准确、友好。" try: # 使用 wait_for 设置 30 秒超时,避免一直等待模型响应 resp = await asyncio.wait_for( get_client().chat.completions.create( model=DEEPSEEK_MODEL, messages=[ {"role": "system", "content": system_content}, {"role": "user", "content": user_content}, ], temperature=0.7, max_tokens=1024, ), timeout=30, ) return resp.choices[0].message.content.strip() except asyncio.TimeoutError: return "抱歉,AI 响应超时了,请稍后再试。" except Exception as e: return f"抱歉,调用 DeepSeek 时出现异常:{e}"这段代码有几个细节值得说明:
使用AsyncOpenAI是因为 QQ 机器人的 SDK 是异步框架,事件回调本身就是async函数。如果在这里使用同步请求,会阻塞整个事件循环,导致机器人同时只能处理一条消息,体验很差。
asyncio.wait_for用于设置超时时间,防止 DeepSeek 接口迟迟不返回时,机器人卡死。
msg_id在原生 OpenAI SDK 里并不是一个参数,不需要传;消息的msg_id是在 QQ 机器人层控制的。
5.3 编写 QQ 机器人主程序
创建bot.py,这是整个机器人的入口:
# 文件路径:bot.py import re import botpy from botpy.message import C2CMessage, GroupMessage from config import ( DEEPSEEK_API_KEY, MAX_REPLY_LEN, QQ_APP_ID, QQ_APP_SECRET, ) from deepseek_client import ask_deepseek SYSTEM_PROMPT = "你是一个跑在QQ群和私聊里的AI助手,回答要简洁、准确、友好。" def clean_message(text: str) -> str: """ 清洗消息内容,去掉 QQ 消息里的 @ 占位符。 不同版本 SDK 返回的格式可能不同,这里做通用处理。 """ if not text: return "" # 去掉 <@!123456> 或 <@123456> 这类占位符 text = re.sub(r"<@!?\d+>", "", text).strip() return text def cut_reply(text: str, max_len: int = MAX_REPLY_LEN) -> str: """超长回复截断,避免触发平台长度限制。""" if len(text) <= max_len: return text return text[:max_len] + "……(消息过长已截断)" class QQDeepSeekBot(botpy.Client): async def on_group_at_message_create(self, message: GroupMessage): """群聊中 @ 机器人 时触发。""" prompt = clean_message(message.content) if not prompt: await message._api.post_group_message( group_openid=message.group_openid, msg_type=0, msg_id=message.id, content="请直接在群里 @我 并输入你想问的问题~", ) return reply = await ask_deepseek(prompt, system_content=SYSTEM_PROMPT) reply = cut_reply(reply) await message._api.post_group_message( group_openid=message.group_openid, msg_type=0, msg_id=message.id, content=reply, ) async def on_c2c_message_create(self, message: C2CMessage): """用户私聊机器人时触发。""" prompt = clean_message(message.content) if not prompt: await message._api.post_c2c_message( openid=message.author.user_openid, msg_type=0, msg_id=message.id, content="请输入你想问的问题~", ) return reply = await ask_deepseek(prompt, system_content=SYSTEM_PROMPT) reply = cut_reply(reply) await message._api.post_c2c_message( openid=message.author.user_openid, msg_type=0, msg_id=message.id, content=reply, ) def main(): if not QQ_APP_ID or not QQ_APP_SECRET: raise RuntimeError("请先配置 QQ_APP_ID 和 QQ_APP_SECRET 环境变量") if not DEEPSEEK_API_KEY: raise RuntimeError("请先配置 DEEPSEEK_API_KEY 环境变量") intents = botpy.Intents( group_message=True, c2c_message=True, ) client = QQDeepSeekBot(intents=intents) client.run(appid=QQ_APP_ID, secret=QQ_APP_SECRET) if __name__ == "__main__": main()这段代码看起来不长,但它已经覆盖了完整闭环。
关键点说明:
on_group_at_message_create是群聊 @ 事件回调方法,SDK 收到事件后会自动调用。on_c2c_message_create是私聊事件回调方法。message._api是 SDK 内部封装的消息发送 API,可以直接调用发送接口。post_group_message和post_c2c_message都是被动回复消息,msg_id用于告诉平台“这是对某条消息的回复”。botpy.Intents用于声明要订阅哪些事件,这里订阅了群消息和私聊消息。
5.4 配置环境变量
在运行之前,需要把密钥配置到环境变量里。
Linux / macOS:
export DEEPSEEK_API_KEY="sk-你的key" export QQ_APP_ID="你的AppID" export QQ_APP_SECRET="你的AppSecret"Windows CMD:
set DEEPSEEK_API_KEY=sk-你的key set QQ_APP_ID=你的AppID set QQ_APP_SECRET=你的AppSecretWindows PowerShell:
$env:DEEPSEEK_API_KEY="sk-你的key" $env:QQ_APP_ID="你的AppID" $env:QQ_APP_SECRET="你的AppSecret"如果你不想每次打开终端都重新设置,也可以在项目目录下创建.env文件并交给加载工具处理。但要注意,.env文件同样不能提交到 git 仓库。
6. 运行与效果验证
6.1 启动机器人
在项目目录下执行:
python bot.py如果配置无误,SDK 会输出类似“连接成功”或开始接收事件的相关日志。此时程序会一直前台运行,不要关闭窗口。
6.2 在群里 @ 机器人
打开测试群,确保机器人已经被拉进群。发送:
@机器人 你好,请介绍一下你自己正常情况下,机器人会很快回复一段 AI 生成的自我介绍。
这里要特别注意:群机器人通常只能响应“@机器人”的消息,不会自动回复群内所有消息。如果希望机器人只被 @ 时响应,也就是说,群内所有消息都处理,需要额外申请“接收群聊消息”权限,并且在代码里做更多过滤逻辑。本文示例默认只处理 @ 事件,比较安全。
6.3 私聊机器人
在 QQ 上找到机器人,发送:
帮我写一段关于机器学习的 100 字介绍机器人会通过on_c2c_message_create事件收到这条消息,然后调用 DeepSeek 生成内容,再通过post_c2c_message回复。
6.4 观察日志
运行期间,终端会打印 SDK 日志和可能的异常信息。建议保持终端可见。如果 DeepSeek 调用失败,ask_deepseek会把错误信息拼在回复里返回到 QQ,方便排查。
一个比较实用的调试技巧:在启动机器人之前,直接单独测试 DeepSeek 链路。可以写一个临时脚本:
import asyncio from deepseek_client import ask_deepseek async def main(): result = await ask_deepseek("你好") print(result) asyncio.run(main())这样可以做到“先验证模型接口通不通,再验证 QQ 消息链路通不通”,问题定位会高效很多。
7. 常见问题与排查清单
7.1 高频问题对照表
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 机器人启动报缺少 AppID/AppSecret | 环境变量未设置或设置后未在当前终端生效 | 检查 export/set 命令;重新打开终端再试 |
| 机器人不掉线,但群里收不到消息 | 开放平台事件订阅未开启,或沙箱白名单未配置 | 检查 QQ 开放平台事件权限;确认机器人已拉入测试群 |
| 私聊机器人无反应 | 未申请 C2C 消息权限,或用户不在白名单 | 检查开放平台 C2C 权限和白名单 |
| 调用 DeepSeek 报 401 | API Key 错误、过期,或 Key 复制多了空格 | 重新生成 Key;检查环境变量前后是否有空格 |
| 机器人回复超时 | 网络问题或 DeepSeek 响应较慢 | 增大asyncio.wait_for超时时间;检查网络连通性 |
| 回复内容被平台拒绝 | 回复内容过长,或包含敏感词 | 在cut_reply中缩短 max_len;设置更严格的内容校验 |
运行报ModuleNotFoundError: No module named 'botpy' | 未安装qq-botpy | 执行pip install qq-botpy |
| WebSocket 连接频繁断开 | 网络不稳定,或长时间没有心跳 | 官方 SDK 通常会自动重连;检查本地网络 |
7.2 深挖两个典型错误
第一个典型错误:DeepSeek 返回401 Authentication Fails。
这个错误绝大多数情况下是 API Key 没配好:
- Key 是否复制完整?
sk-开头,末尾没有空格。 - 是否在启动
python bot.py之前设置环境变量?export只对当前终端会话生效。 - 是否使用了错误的 Key?可以在 DeepSeek 平台重新创建。
第二个典型错误:机器人能跑,但群里 @ 之后没有任何反应。
这个问题大概率不在代码,而在平台配置。按顺序检查:
- 机器人的“事件订阅”是否勾选了群聊消息。
- 机器人是否真的在测试群里。
- 测试号是否在白名单里。
- 是否发送了 @ 机器人的消息,而不是普通群消息。
如果这些都确认没问题,再看本地日志。日志中如果没有任何事件输出,说明事件根本没推到本地,问题依然在平台侧。
8. 工程化建议与安全边界
个人小玩具能跑通之后,如果要进一步做成稳定的服务,需要考虑下面几个方面。
8.1 密钥管理
现在很多项目的翻车现场,都是把 API Key 或 AppSecret 提交到了 GitHub。建议从一开始就养成习惯:
- 所有密钥通过环境变量注入。
- 项目根目录添加
.gitignore,把.env、venv、日志文件排除在外。 - 不同环境使用不同的 API Key,方便单独吊销。
- 定期检查代码仓库历史,一旦发现密钥泄露,立即去平台吊销并重新生成。
8.2 超时与重试
DeepSeek API 偶尔会因为网络波动或高峰期排队而变慢。asyncio.wait_for设置了超时时间,但超时后直接返回错误,用户会看到“响应超时”的提示。
更健壮的做法是加入重试机制:
- 第一次调用失败后,等待 1-2 秒重试一次。
- 重试次数建议不超过 3 次。
- 针对 401/403 等鉴权错误不要重试,因为重试也没用。
8.3 消息长度与频率控制
QQ 官方接口对单条消息长度有限制,群聊和私聊可能不完全一样。代码里的cut_reply函数只做了简单截断。更优雅的方案是:
- 超长回复拆成多条消息按顺序发送。
- 根据问题类型决定是否启用
deepseek-reasoner,因为推理模型生成内容更长。 - 对同一用户的请求做频率限制,防止有人恶意刷消息,导致 API 费用飙升。
8.4 内容安全问题
DeepSeek 本身有一定的内容安全机制,但接入 QQ 群后,机器人会被很多人使用。建议:
- 在 system prompt 中明确说明“拒绝回答违法、暴力、低俗内容”。
- 对用户输入和 AI 输出都做简单敏感词过滤。
- 保留运行日志,但日志中不要记录完整消息内容,尤其是包含手机号、身份证号等个人信息时,要做脱敏处理。
8.5 关于第三方协议的风险提示
市面上存在着大量基于非官方协议的 QQ 机器人框架,它们最吸引人的地方是“功能多、不需要审核、什么都能做”。但代价也很明显:
- 违反平台用户协议,账号可能被限制或封禁。
- 通过逆向和模拟客户端实现,一旦协议更新,随时失效。
- 中间可能经过第三方服务器,消息内容存在泄露风险。
- 很多“免费框架”会夹带广告、挖矿脚本等不可控代码。
如果你的目标是长期稳定运行,建议老老实实使用官方平台能力。官方权限不够用,就去申请更多官方权限。
9. 最后给你一个调试建议
把整个流程跑通之后,你会发现代码本身并不复杂,复杂的是环境配置和平台权限。
最后一个实用的调试技巧:开发阶段不要把验证步骤耦合在一起。先把ask_deepseek()单独测试,确认 DeepSeek 链路是通的;再启动机器人,确认 QQ 事件能收到;最后再合到一起测完整流程。很多“机器人没反应”的问题,其实都是因为 DeepSeek 调用抛了异常,而异常信息只打印在终端里没有注意。
如果你按本文操作成功跑通了机器人,下一步可以考虑加离线记忆、关键词指令、多轮上下文,甚至用 Docker 部署到服务器上常驻运行。如果这篇文章对你有帮助,可以收藏备用;运行中遇到其他问题,欢迎在评论区留言交流。