ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

从零搭建QQ AI机器人:协议端、NoneBot2框架与模型接入最小实践

从零搭建QQ AI机器人:协议端、NoneBot2框架与模型接入最小实践 做QQ机器人这件事很多人卡在最前面三道坎不知道怎么选框架、不知道怎么让机器人号“真正跑起来”、不知道AI模型怎么接进去。网上教程要么过时、要么只讲了其中一段很多人折腾一个周末最后卡在环境配置上就放弃了。其实现在的技术栈已经非常成熟。我的判断是QQ机器人的开发门槛已经从“怎么连上QQ”这一类的协议问题转移到了“怎么把对话体验和工程化做好”这一层的业务问题上。今天这篇教程就给你一条从零到一的最小链路协议端负责连接QQ框架端负责处理消息模型端负责生成回答三块拼起来就是一个能私聊能群聊的AI机器人。全文会分成三部分先讲清楚每个组件的分工和消息链路再给出完整可复制的环境配置与代码实现最后聊一聊最常见的坑和生产环境的工程建议。所有步骤都按“最小可行方案”来设计不需要你理解复杂的协议细节照着复制、替换、运行就能把机器人跑起来。1. 这篇文章真正要解决的问题很多人一上来就问“用哪个库做QQ机器人”这个提问方式本身就带着误区。QQ机器人并不是单个库能做完的事情它至少横跨协议层、框架层、模型层每层都有独立的生态和技术选型。如果只看零散教程你会在三层之间反复横跳今天看到有人用A框架明天看到有人用B协议端后天又看到C模型的封装。每个人的技术栈都不一样拼起来却不一定兼容这才是新手真正痛苦的地方。这篇文章要解决的就是一件事给你一条三层全通、可直接照抄的完整链路而不是给你一堆零散方案让你自己拼。读完你会得到一个能正常收发消息的QQ机器人支持私聊和群聊一套接入AI模型的代码发问题就能给回复一套排错思路遇到问题知道先看哪里一组生产环境的工程建议避免走弯路。适用读者也明确一下如果你是想快速验证想法、给群友做个聊天助理、或者刚接触机器人开发的新手这篇文章正合适。如果你要做的是企业级、要上架审核的官方机器人那应该走QQ开放平台的官方API本文的协议端方案更多用于个人学习和内测场景这一点在后面的安全章节还会展开。2. 核心概念三层架构与消息生命周期在动手之前必须先把三个概念分清楚协议端、框架端、模型端。这三个词经常被搞混但它们的分工完全不同。2.1 协议端负责“连接QQ”协议端解决的是底层通信问题它让程序能够收到QQ消息、发送QQ消息。个人机器人领域常见的做法是使用社区开源的协议实现比如NapCat这一类的方案。它们通过复用官方QQ客户端的通信能力对外暴露统一的接口从而让开发者不用自己逆向协议。协议端对外提供的是OneBot协议。OneBot是一个标准化的机器人通信协议定义了“收到消息”和“发送消息”的数据格式类似于HTTP对Web的意义。有了OneBot你写的上层业务代码不会被具体的协议实现绑定今天用A协议端明天换B协议端框架层代码基本不用改。2.2 框架端负责“处理消息”框架端是机器人业务逻辑的载体。本文选用NoneBot2作为示例它是Python生态里非常活跃的QQ机器人框架基于事件驱动设计支持插件化开发。你写一个插件框架就会把对应的消息事件分发进来由插件决定如何回应。框架端并不关心QQ底层协议是怎么实现的它只通过WebSocket等连接方式与协议端通信。协议端连接QQ框架端连接协议端两层通过OneBot协议对齐。开发者重点接触的是框架端因为这是写代码最多的地方。2.3 模型端负责“生成回答”模型端就是AI能力的来源。接入方式通常是调用大模型平台提供的API常见格式是OpenAI兼容接口也就是发送一个包含消息列表的POST请求模型返回文本结果。现在很多模型平台都提供OpenAI兼容的接入地址所以同一段调用代码可以在不同模型之间切换这是降低成本、做多模型兜底的基础。如果用一句话概括这三个组件的分工协议端让你“听得到看得见”框架端让你“会思考会回应”模型端让你“有内容可说”。2.4 一条消息的完整生命周期从用户在QQ里发消息到机器人回复完整链路是这样的用户在QQ里给机器人发消息协议端收到消息封装成OneBot格式通过WebSocket推给框架端框架端根据消息内容匹配到注册的插件处理器插件解析用户输入调用AI模型接口模型返回文本插件把结果封装成回复消息交给框架端框架端调用协议端接口把消息发回QQ或群聊。OneBot协议里一条群消息事件大致长这样{ post_type: message, message_type: group, user_id: 123456, group_id: 654321, raw_message: 你好, message: [ { type: text, data: { text: 你好 } } ] }这个结构里post_type是事件类型message_type区分私聊和群聊message是消息段数组。你不用手写这段JSON框架会帮你解析成Python对象但看懂它有助于理解后面的代码逻辑。3. 环境准备与前置条件开始动手前把下面这些东西准备好可以省不少来回折腾的时间。项目说明注意事项QQ账号机器人使用的账号强烈建议使用小号不要用主号Python环境3.10及以上版本以NoneBot2官方文档要求为准机器人框架NoneBot2通过pip安装协议端NapCat等OneBot协议端按照其官方文档部署AI模型API Key任一支持OpenAI兼容接口的模型平台确保接口路径和模型名正确网络连通性能访问模型API地址使用国内模型平台则没有额外问题这里先把“为什么要用小号”说清楚。个人项目中第三方协议端本质上是通过模拟官方客户端与QQ服务器通信这类方案存在账号风控的风险。用主号测试万一触发安全验证或者异常限制代价是比较大的。准备一个小号专门做实验是成本最低、也最稳妥的做法。版本问题也要提前说明NoneBot2和协议端的迭代速度都很快本文不写死具体版本号安装时直接用pip安装最新稳定版即可。如果你看到安装命令因为版本报错优先检查Python版本是否满足最低要求其次检查依赖之间有没有冲突。还有人会问是不是必须用Windows。实际上NoneBot2是跨平台的Windows、macOS、Linux都支持。协议端的部署方式在不同平台有差异比如Windows有图形化启动方式Linux可以用容器方式部署这一步以你所用协议端的官方文档为准。本文后面的示例代码与平台无关。如果你还没有模型平台的API Key也不想在本地起大模型有一个临时替代方案在本地安装Ollama这类模型工具它同样提供OpenAI兼容接口。把文章里的AI_API_BASE改成http://127.0.0.1:11434/v1模型名改成你本地拉取的模型名即可。这样整个链路完全本地闭环不产生API费用适合先跑通流程再换正式模型。4. 环境搭建与基础配置4.1 安装Python与依赖如果还没有Python环境先从官网安装Python 3.10以上版本安装时记得勾选“Add Python to PATH”。安装完成后打开命令行验证python --version pip --version然后安装NoneBot2框架、OneBot V11适配器和HTTPX请求库pip install nonebot2 nonebot-adapter-onebot httpx这里有一个容易忽略的点httpx是用来调用AI模型API的它属于异步HTTP客户端和NoneBot2的异步模型正好匹配。如果漏装后面AI请求会直接报模块不存在。安装依赖后建议顺手升级一下pip避免部分旧版本pip在解析依赖时出现异常。4.2 启动协议端并记录端口按照你选择的协议端官方文档完成部署不同版本界面差别会比较大但通用思路是一致的启动协议端进程登录机器人的QQ号找到WebSocket服务器配置开启一个端口比如3001记住消息格式要选择符合OneBot的格式端口号后面框架端要使用。这里最容易出错的不是配置本身而是你只登录了QQ、没有开启WebSocket服务框架端自然连不上。所以启动完协议端后建议先确认日志中有“WebSocket服务器启动”或类似提示再继续后面的步骤。下面给出一份NapCat中常见OneBot WebSocket Server配置的JSON片段供参考{ network: { websocketServers: [ { name: OneBot V11, enable: true, host: 127.0.0.1, port: 3001, messagePostFormat: array, reportSelfMessage: false, token: } ] } }需要说明的是不同版本的字段名可能不同上面是常见配置形式。只要能让框架通过WebSocket连到3001端口并收到消息就行。如果协议端不支持JSON配置也可以在图形界面里逐项填写。token字段建议留空除非你会在框架端同步配置AccessToken否则反而会连不上。4.3 创建NoneBot2项目文件这一步推荐手动创建项目文件比使用交互式脚手架更快也更清楚每个文件是干什么的。项目结构如下qq_bot/ ├── .env ├── bot.py ├── pyproject.toml └── plugins/ ├── __init__.py ├── hello.py └── ai_chat.py先创建.env用来放环境配置DRIVER~httpx~websockets HOST127.0.0.1 PORT8080 SUPERUSERS[你的QQ号]这几个配置项各有用途DRIVERNoneBot2使用的驱动这里配置为HTTPX加WebSocket既要连接协议端的WebSocket又要用HTTPX调用模型APIHOST和PORT框架自身服务的监听地址和端口默认使用8080SUPERUSERS机器人管理员QQ号需要以JSON数组格式填写用于部分管理操作。接着创建pyproject.toml声明框架使用的适配器和插件[project] name qq-bot version 0.1.0 description QQ AI 机器人示例 requires-python 3.10 [tool.nonebot] adapters [ { name OneBot V11, module_name nonebot.adapters.onebot.v11 } ] plugins [plugins.hello, plugins.ai_chat]最后一个文件是bot.py它是整个机器人进程的入口import nonebot from nonebot.adapters.onebot.v11 import Adapter nonebot.init() driver nonebot.get_driver() driver.register_adapter(Adapter) nonebot.load_from_toml(pyproject.toml) if __name__ __main__: nonebot.run()这段代码的逻辑很直白初始化NoneBot2注册OneBot V11适配器然后从pyproject.toml加载插件最后启动。如果你习惯使用nb-cli也可以跳过bot.py直接执行nb run它同样会读取pyproject.toml启动项目。5. 完整示例代码实现项目骨架搭好后接下来写插件。本节给三个示例从简单到完整递进第一个确认链路通不通第二个接入AI模型第三个覆盖群聊机器人的场景。5.1 示例一最小可运行插件创建plugins/hello.pyfrom nonebot import on_command from nonebot.adapters.onebot.v11 import MessageEvent hello on_command(hello, priority10, blockTrue) hello.handle() async def handle_hello(event: MessageEvent): await hello.finish(你好我是AI机器人。输入 /chat 加问题即可开始对话。)这个插件注册了一个名为hello的命令。用户在私聊或群里发送/hello框架就会进入这个处理器并回复一条消息。这里的关键点是on_command帮我们把“发命令”和“执行逻辑”绑定在一起你不需要自己判断消息类型也不需要关心底层协议。创建完成后运行python bot.py如果协议端与框架连通正常给机器人私聊发送/hello应该能收到回复文本。这说明整条链路已经通了后面的AI接入才有意义。5.2 示例二接入AI模型的命令插件创建plugins/ai_chat.py这个插件同时包含AI模型调用函数和命令处理逻辑。import os import httpx from nonebot import on_command from nonebot.adapters.onebot.v11 import MessageEvent, Message from nonebot.params import CommandArg # 读取模型配置 AI_API_KEY os.getenv(AI_API_KEY, ) AI_API_BASE os.getenv(AI_API_BASE, https://api.openai.com/v1) AI_MODEL os.getenv(AI_MODEL, gpt-4o-mini) chat on_command(chat, aliases{AI, ai}, priority10, blockTrue) async def call_ai_api(prompt: str) - str: 调用AI模型接口返回模型生成的文本。 if not AI_API_KEY: return 尚未配置AI_API_KEY请在.env文件中填写模型平台的密钥。 headers { Authorization: fBearer {AI_API_KEY}, Content-Type: application/json, } payload { model: AI_MODEL, messages: [{role: user, content: prompt}], temperature: 0.7, } async with httpx.AsyncClient(timeout60) as client: resp await client.post( f{AI_API_BASE}/chat/completions, headersheaders, jsonpayload, ) resp.raise_for_status() data resp.json() return data[choices][0][message][content] chat.handle() async def handle_chat(event: MessageEvent, arg: Message CommandArg()): prompt arg.extract_plain_text().strip() if not prompt: await chat.finish(请带上你的问题例如/chat 用一句话介绍量子计算) await chat.send(AI思考中请稍候…) try: reply await call_ai_api(prompt) except Exception as e: reply fAI调用失败{e} await chat.finish(reply)然后在.env中追加模型配置AI_API_KEYsk-你的密钥 AI_API_BASEhttps://你的模型服务地址/v1 AI_MODEL你的模型名这里必须说清楚三件事。第一AI_API_BASE和AI_MODEL要替换成你实际使用的模型平台的配置。现在很多平台提供OpenAI兼容接口通常都是https://域名/v1这种形式具体模型名以平台控制台为准。如果用的是本地Ollama则填http://127.0.0.1:11434/v1和对应的本地模型名。第二密钥属于敏感信息不要提交到Git仓库。示例里通过环境变量读取这是最低要求。更严格的做法是在生产环境使用系统环境变量或密钥管理服务。第三请求超时设置成60秒是考虑到部分模型生成内容较慢。如果你的模型响应快可以调小这个值避免用户等待过久。5.3 示例三群聊机器人自动回复QQ群机器人的常见交互是在群里机器人然后输入问题机器人自动回答。这个场景需要用事件监听来实现。在plugins目录下新建group_ai.py写入from nonebot import on_message from nonebot.adapters.onebot.v11 import MessageEvent from .ai_chat import call_ai_api group_ai on_message(priority5, blockFalse) group_ai.handle() async def handle_group_message(event: MessageEvent): # 只处理群聊中被的消息 if event.message_type ! group or not event.to_me: return prompt event.get_plaintext().strip() if not prompt: return await group_ai.send(AI思考中请稍候…) try: reply await call_ai_api(prompt) except Exception as e: reply fAI调用失败{e} await group_ai.finish(reply)核心逻辑是三点用on_message监听所有消息而不是只监听命令判断消息是否来自群聊以及是否被event.to_me为True表示消息了机器人提取纯文本作为prompt复用上一步的call_ai_api函数然后回复。这里有一个值得注意的坑priority5让它先于命令处理器执行但blockFalse不会阻断消息继续传递。所以如果在群里发/hello可能先被这里的处理器读到再被hello命令处理器处理导致重复回复。实际项目中建议根据你的交互方式二选一保留命令模式或者只保留机器人的模式不要两种入口同时开。6. 运行结果与效果验证所有文件都写完接下来启动并验证。启动命令python bot.py首次启动会看到NoneBot2的日志输出包括加载了哪些插件、适配器是否注册成功、WebSocket是否连接上协议端等信息。正常情况下日志里会有OneBot V11适配器的初始化提示以及成功连接协议端WebSocket的记录。然后做两层验证。第一层是功能验证。给机器人私聊发送/hello预期返回“你好我是AI机器人……”发送/chat 你好预期返回模型生成的内容在群里机器人再输入一句话预期回一条AI回答。这三个动作分别覆盖命令插件、AI调用和群聊交互。第二层是异常验证。故意把AI_API_KEY改成错误的值再发一条/chat预期回复“AI调用失败”而不是卡住不响应。这一步是为了确认异常处理逻辑生效避免真实环境下模型接口出问题时机器人“装死”。如果私聊正常、群聊没有反应优先检查机器人是否在群里、群设置是否允许机器人发言、机器人是否有群成员权限。这属于QQ侧的权限问题代码本身没有报错时优先从群设置排查。7. 常见问题与排查思路这里整理几个高频问题按“现象—原因—排查—解决”的顺序梳理。问题现象可能原因排查方式解决方案框架启动后提示连接协议端失败协议端未启动或端口不一致查看日志中WebSocket地址和端口核对协议端配置页面统一端口重启协议端机器人登录后提示风控或设备锁账号异常或有安全风险查看协议端登录日志按提示完成验证建议换小号发消息后机器人完全不回复命令没写对或插件未加载看框架启动日志是否加载了对应插件检查pyproject.toml中的plugins配置收到“AI调用失败”API Key错误、接口地址不对或网络不通打印异常详情确认请求地址核对API Key和API_BASE确认网络能访问请求模型接口很慢或无响应模型生成时间长或超时设置过短查看框架日志里的请求耗时调大timeout或改用更快的模型群聊机器人不回复群设置或权限问题先私聊验证同一段逻辑检查群设置确认机器人有发言权限排错的核心思路是沿着消息链路逐层排查先看协议端日志确认消息进来了没有再看框架端日志确认插件有没有被调用最后看模型API日志确认请求有没有发出去。把“消息在哪一层断了”定位出来问题就解决了一半。如果日志不够明确可以临时在插件里加print输出关键变量跑一遍再删掉。8. 最佳实践与工程建议8.1 安全与合规放在第一位这里必须把账号风险和平台规则说清楚。个人使用、学习验证时使用第三方协议端属于社区实践方式存在账号风控风险所以务必用小号测试不要把重要账号用于实验。同时要遵守QQ平台规则不要用机器人做营销、刷屏、诱导分享、欺诈等行为否则账号和所在群都可能被处理。如果你的目标是正式上线的官方机器人应该走QQ开放平台的官方渠道申请机器人能力走审核上架流程这才是长期合规的路线。8.2 配置与密钥管理密钥不要写死在代码里。示例中已经通过.env读取这是最低要求。更进一步可以做这几件事把.env加入.gitignore避免误提交到Git仓库生产环境使用系统环境变量或配置中心给API Key设置调用限额防止异常流量导致成本失控。模型参数也要通过配置管理。示例中把模型名、接口地址抽成环境变量这样切换模型时只需要改配置不需要改代码这对后续模型对比和降级很有帮助。8.3 稳定性与成本控制AI接口调用属于耗时的外部IO也是成本中心。工程上建议至少做以下几件事频率限制同一个用户或同一个群短时间内不要连续调用模型避免刷屏和费用暴涨结果缓存常见问题和答案可以缓存一段时间减少重复调用异常降级模型不可用时降级到简单的本地规则回复或提示用户稍后再试日志记录记录每次调用的用户、群、时间、模型、耗时、token消耗方便排查和成本归因多模型切换为同一个OpenAI兼容接口设计配置切换能力某个模型限流时自动切换到备用模型。这些是把“能跑的demo”变成“能用的服务”的关键。很多人卡在demo能跑、一遇到并发和异常就崩原因就是只写了正常路径没有处理边界条件。8.4 多模型与上下文管理的进阶方向如果机器人的定位是“AI聊天助手”那么单次问题、单次回答的模式很快会显得不够聪明。更进阶的方案是维护每个会话的上下文历史把最近的几轮对话一起发给模型这样模型能理解前文。实现方式一般是以用户或群为单位在内存或Redis中保存一个消息队列超过一定轮数就丢弃最旧的消息。这部分代码量不大但能显著提升对话体验。它的成本也会增加因为输入token变多了所以建议限制上下文轮数并对不同群设置不同的策略。9. 总结与后续学习方向本文的核心内容就是三件事用协议端解决QQ消息收发用NoneBot2解决消息处理和插件化用OpenAI兼容接口解决AI回答。把这三层接起来一个支持私聊和群聊的AI机器人就完成了。整个方案不依赖复杂的协议知识代码也都是最小实现适合作为个人机器人项目的起点。接下来可以继续深入的方向很多NoneBot2官方文档里的插件机制、会话控制、定时任务OneBot协议的事件类型和扩展字段模型端的提示词工程、上下文管理、工具调用Function Calling以及把项目部署到服务器、用Docker打包、接入更多管理命令等等。最后提醒一句任何机器人先想清楚使用边界。能做什么、不能做什么、谁来负责、账号风险怎么控制这些问题比技术本身更值得在意。技术链路已经很成熟了真正的复杂度在业务理解和工程化那一侧。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进