
这次我们来看一个非常具体的实践把 DeepSeek 接进 QQ 机器人让它像真人一样在群里聊天并且支持“多段回复逻辑”——也就是说AI 不是一次性把一大段文字甩到群里而是像真人打字那样一句话一条消息分段发送。这个需求在 QQ 群机器人、私聊机器人、频道管家里都很常见。网上相关的教程很零散很多只讲“能跑通”不讲“怎么把对话做得像人”。这篇文章直接给出一套从零到一的完整链路QQ 机器人服务选型、消息中转服务编写、DeepSeek API 接入、多段回复拆分逻辑、拟人化 System Prompt 设计以及最后的排错清单。先说结论整个方案不需要高性能显卡核心依赖是 DeepSeek 的 API 或本地模型服务普通云服务器或家用电脑都能跑。文章里的代码基于通用 OneBot v11 协议和 OpenAI 兼容接口换机器人框架不需要重写核心逻辑。一次性把架构和代码讲透建议直接收藏。1. 核心能力速览能力项说明项目类型QQ 机器人 DeepSeek 大模型接入核心能力群聊/私聊消息接收、调用 DeepSeek 生成回复、多段回复拆分发送、拟人化人设控制硬件要求调用云端 API 时无显卡要求本地部署模型需要根据模型版本评估 GPU 显存支持平台Windows / Linux / macOS 均可机器人服务端推荐 Linux 云服务器启动方式机器人框架独立启动 Python 中转服务独立启动是否支持 API是DeepSeek 提供 OpenAI 兼容接口是否支持批量任务消息事件本身是异步并发可同时处理多个群/多个用户消息主要依赖Python 3.10、openai、websockets、OneBot v11 机器人框架适合场景QQ 群闲聊机器人、私聊问答机器人、频道自动回复、个人 AI 助理需要先说明一个材料边界本文不绑定某个特定机器人框架的版本也不绑定 DeepSeek 的具体模型编号。DeepSeek 的 API 模型名、价格、限流策略会变接入时以官方开放平台文档为准。2. 整体架构一条消息从 QQ 到 DeepSeek 再回来在写代码之前先把链路理清楚。不然很多人会卡在“消息到底是怎么被收到的”这个问题上。QQ客户端/QQ群 ↓ 消息事件 QQ机器人协议服务NapCat / Lagrange / OneBot实现 ↓ OneBot v11 标准事件通过 WebSocket 推送 Python 中转服务 ↓ 组装 Prompt携带上下文 DeepSeek API或本地模型服务 ↓ 返回文本 Python 中转服务 ↓ 按多段回复逻辑拆分 QQ机器人协议服务 ↓ 逐条发送 QQ群/私聊这里最关键的角色是中间那层“QQ 机器人协议服务”。现在的主流方案基本都支持 OneBot v11 协议它把 QQ 各种事件收到消息、收到好友请求、群成员变动等统一成标准 JSON 格式。我们的代码只需要对接 OneBot v11不需要去理解 QQ 内部协议。另一边DeepSeek 提供 OpenAI 兼容接口意味着我们直接用openaiPython SDK把base_url指向 DeepSeek 的地址就行。这种兼容性让我们后续如果要切换其他模型只需要改base_url和api_key代码主体不用动。多段回复逻辑的位置就在 Python 中转服务里。它决定了一段完整回复要拆成几条消息、每条什么时候发、间隔多久。这个逻辑的设计直接决定了机器人像不像真人。3. 前置准备账号、API Key、运行环境3.1 准备一个 QQ 机器人账号这里说的不是腾讯官方机器人开放平台的企业认证账号而是指用一个普通 QQ 号作为机器人本体通过 OneBot 框架去登录和收发消息。强烈建议单独注册一个 QQ 小号不要拿主号测试。原因后面“合规提醒”部分会展开简单说就是第三方 QQ 机器人协议有一定账号风险用小号可以避免影响日常社交身份。3.2 获取 DeepSeek API Key打开 DeepSeek 开放平台登录后进入“API Keys”页面创建一个新的 API Key。这个 Key 只在创建时完整展示一次要立即复制保存到本地。注意两点。第一API Key 是敏感凭证不要提交到 Git 仓库不要粘贴到公开帖子、截图里。建议放进.env文件或环境变量。第二DeepSeek 开放平台是充值后按 token 计费新用户一般会送一定额度的体验金具体价格和模型列表以官方页面为准本文示例中的model字段需要替换成你账号可用的模型标识。3.3 准备运行环境需要准备的东西一台能跑 Python 的机器。Windows 本地测试也行生产部署建议用 Linux 云服务器。Python 3.10 或更高版本。能访问 DeepSeek API 的网络环境。如果后续要本地部署 DeepSeek 模型则需要一块足够显存的 NVIDIA 显卡或者大内存机器这一部分在“资源占用”章节单独讲。检查 Python 版本python --version4. 部署 QQ 机器人服务OneBot v114.1 方案选择社区里常用的 OneBot v11 实现有几个方向NapCat基于 QQNT 的协议实现配置相对简单社区活跃适合 QQ 机器人开发。Lagrange.OneBot同样支持 OneBot v11偏向跨平台更新节奏稳定。go-cqhttp早期常用目前已停止维护新项目不推荐。我的建议是新项目优先选择 NapCat 或 Lagrange.OneBot。具体安装步骤以对应项目官方文档为准因为 QQ 协议变动频繁框架版本也在持续更新。4.2 安装与启动以 Linux 服务器为例通用步骤是# 以下只是通用示例实际命令请以项目官方 README 为准 # 下载并解压机器人框架 wget 官方发布的包地址 unzip 包文件.zip cd 解压目录 # 启动框架首次启动会生成配置目录 ./启动脚本启动后浏览器打开框架提供的 Web 管理面板一般是http://127.0.0.1:端口扫码登录机器人 QQ 账号。4.3 配置 WebSocket 连接在框架的配置文件里找到 OneBot v11 相关配置开启 WebSocket 服务端或客户端如果让 Python 中转服务作为客户端主动连接框架就开启“WebSocket 服务器”填写监听端口比如3001。如果让框架主动连接 Python 服务就开启“正向 WebSocket 客户端”填写 Python 服务的地址和端口。推荐第一种Python 脚本作为客户端去连框架结构更简单重连逻辑也好写。配置项大概长这样{ network: { wsServers: [ { name: ws-server-1, enable: true, host: 127.0.0.1, port: 3001 } ] } }具体字段名因框架而异以你选的框架配置页面为准。4.4 验证机器人服务框架启动后向机器人 QQ 发一条私聊消息然后在框架日志里应该能看到对应的消息事件推送记录。能在这里看到消息说明协议层已经通了接下来才轮到我们的 Python 代码。5. 编写消息中转服务Python 接消息、调 DeepSeek、发回复5.1 安装依赖pip install openai websockets python-dotenvopenai官方 SDK用来调用 DeepSeek 的 OpenAI 兼容接口。websockets连接 OneBot v11 的 WebSocket 服务。python-dotenv读取.env配置文件。5.2 最小启动脚本接收消息先写一个最小版本让 Python 能连接到 OneBot WebSocket打印收到的消息事件。import asyncio import json import websockets WS_URL ws://127.0.0.1:3001 async def receive_loop(): async with websockets.connect(WS_URL) as ws: print(已连接到 OneBot WebSocket:, WS_URL) async for raw in ws: event json.loads(raw) # OneBot v11 消息事件 if event.get(post_type) message: msg_type event.get(message_type) user_id event.get(user_id) group_id event.get(group_id) message event.get(raw_message) or event.get(message) print(f[{msg_type}] user{user_id} group{group_id}: {message}) if __name__ __main__: asyncio.run(receive_loop())跑起来之后再给机器人发一条消息控制台应该能打印出对应内容。如果连不上优先检查 WebSocket 地址、端口、框架日志。5.3 调用 DeepSeek API把 API Key 放到.env文件DEEPSEEK_API_KEYsk-xxxx DEEPSEEK_BASE_URLhttps://api.deepseek.com DEEPSEEK_MODELdeepseek-chat WS_URLws://127.0.0.1:3001然后写一个调用 DeepSeek 的函数import os from dotenv import load_dotenv from openai import OpenAI load_dotenv() client OpenAI( api_keyos.getenv(DEEPSEEK_API_KEY), base_urlos.getenv(DEEPSEEK_BASE_URL), ) MODEL os.getenv(DEEPSEEK_MODEL, deepseek-chat) def chat_once(user_message: str, history: list[dict] | None None) - str: messages [{role: system, content: SYSTEM_PROMPT}] if history: messages.extend(history) messages.append({role: user, content: user_message}) resp client.chat.completions.create( modelMODEL, messagesmessages, temperature0.8, max_tokens1024, ) return resp.choices[0].message.contenthistory是多轮对话上下文后面在“对话记忆管理”里再详细说。5.4 拼接回复并发送在收到消息事件后调用chat_once然后根据消息类型发送回复。async def handle_event(ws, event): if event.get(post_type) ! message: return msg_type event.get(message_type) user_id event.get(user_id) group_id event.get(group_id) raw_msg event.get(raw_message) or event.get(message) # 这里可以加消息清洗、前缀过滤逻辑 reply chat_once(raw_msg) if msg_type group and group_id: action { action: send_group_msg, params: {group_id: group_id, message: reply}, } else: action { action: send_private_msg, params: {user_id: user_id, message: reply}, } await ws.send(json.dumps(action))把handle_event接到receive_loop里async def receive_loop(): async with websockets.connect(WS_URL) as ws: print(已连接到 OneBot WebSocket:, WS_URL) async for raw in ws: event json.loads(raw) if event.get(post_type) message: asyncio.create_task(handle_event(ws, event))用asyncio.create_task的好处是如果 DeepSeek 接口响应慢不会阻塞其他消息的处理多个群同时来消息时能并发处理。到这一步一个能“有问必答”的 QQ 机器人已经可以跑了。但现在的回复还是“一次性甩一大段”的机器感风格。接下来才是重点多段回复逻辑。6. 多段回复逻辑三种常用实现多段回复的核心诉求是AI 生成的内容不能像文档一样直接粘贴到群里要拆开、分批、带间隔地发出来。6.1 方式一按自然段拆分发送最简单也最常用。把 DeepSeek 返回的完整文本按空行\n\n拆成多个自然段然后逐段发送段与段之间间隔 0.8 到 1.5 秒。import asyncio def split_paragraphs(text: str, max_len: int 2000) - list[str]: 按自然段拆分单段超长时按长度继续切。 paragraphs [p.strip() for p in text.split(\n\n) if p.strip()] chunks [] for para in paragraphs: while len(para) max_len: chunks.append(para[:max_len]) para para[max_len:] chunks.append(para) return chunks async def send_reply_in_chunks(ws, action, delay: float 0.9): 把回复拆成多条消息依次发送。 reply action[params][message] chunks split_paragraphs(reply) for i, chunk in enumerate(chunks): if i 0: await asyncio.sleep(delay) params dict(action[params]) params[message] chunk await ws.send(json.dumps({ action: action[action], params: params, }))这种方式适合 DeepSeek 已经生成了完整长文本的场景。Prompt 里可以让模型“写回复时用空行分段一段一个意思”这样后端拆分更干净。6.2 方式二流式输出 断句发送如果想让机器人更像真人打字可以使用流式输出。DeepSeek 的 OpenAI 兼容接口支持streamTrue模型每生成一小段就返回一次我们可以边收边判断是否发送。基本思路开启流式请求。累积当前句子的文本。遇到句号、问号、感叹号或换行时把当前句子作为一个消息发送。发送之间加短间隔。def chat_stream(user_message: str): messages [ {role: system, content: SYSTEM_PROMPT}, {role: user, content: user_message}, ] stream client.chat.completions.create( modelMODEL, messagesmessages, streamTrue, temperature0.8, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: yield chunk.choices[0].delta.content发送侧import re SENTENCE_END re.compile(r[。!?;\n]) async def stream_reply_to_qq(ws, action): message action[params][message] buffer for delta in chat_stream(message): buffer delta if SENTENCE_END.search(buffer): chunk buffer.strip() if chunk: params dict(action[params]) params[message] chunk await ws.send(json.dumps({ action: action[action], params: params, })) await asyncio.sleep(0.5) buffer if buffer.strip(): params dict(action[params]) params[message] buffer.strip() await ws.send(json.dumps({ action: action[action], params: params, }))这种方式体验更接近真人但实现复杂度高一些而且需要处理“流式输出卡在半句话没结束”的边界情况。建议第一次先跑通方式一再升级到方式二。6.3 方式三让模型输出结构化多条回复在 System Prompt 中要求模型返回 JSON 数组每个元素是一条待发送的消息当你需要分多条回复时只输出如下 JSON 格式不要输出其他内容 [第一条消息, 第二条消息, 第三条消息]后端解析import json def parse_multi_reply(reply: str) - list[str]: try: data json.loads(reply) if isinstance(data, list): return [str(item) for item in data if str(item).strip()] except json.JSONDecodeError: pass # 解析失败时当作单条回复 return [reply]然后逐条发送。这个方法适合需要严格控制回复结构的场景比如“先回复一句简短结论再补充一个使用步骤列表”。6.4 多段回复的边界条件不管用哪种方式都要注意几个边界单条消息长度限制。QQ 群消息建议单条不要超过 2000 字超长会被分段展示或发送异常。拆分后不能改变语义。句子不要在半中间切断段落拆分必须按照完整标点或空行。间隔时间要合理。太短没有真人感太长用户会以为机器人挂了0.5 到 1.5 秒之间比较合适。如果回复本身就是一句短话不要强行拆分。只有超过一定长度或者明确包含多个独立语义块时才拆。7. 拟人化聊天配置多段回复只是“形似”拟人化的关键在 System Prompt 和上下文管理。7.1 System Prompt 示例下面是一份可以直接使用的拟人化提示词。核心原则是设定性格、限制长度、要求口语化、禁止暴露 AI 身份如果场景允许。SYSTEM_PROMPT 你是一个生活在 QQ 群里的普通网友昵称「阿澈」性格随和说话带点幽默感。 以下是你必须遵守的聊天规则 1. 使用口语化短句像真人打字聊天不要使用书面腔和报告式语言。 2. 日常聊天回复控制在 1 到 3 句不要长篇大论。除非对方明确求助、提问技术问题才给详细说明。 3. 不要主动说自己是 AI不要说自己没有情感不要输出模型内部信息。 4. 可以适当使用语气词比如哈哈、确实、离谱、嗯嗯、草。 5. 要回复多要点时一条消息只表达一个意思分多条发送消息之间自然衔接。 6. 遇到不知道的问题直接说不知道不要编造。 7. 不讨论违法、敏感、暴力、色情内容遇到这类话题就自然回避。这里特别要说明拟人化不等于“欺骗”。在私人测试群、熟人娱乐场景里这种设定没有问题但如果机器人面向公众建议在群公告或自动回复中声明“本机器人为 AI”避免法律和伦理风险。7.2 对话记忆管理无状态调用 API 的话机器人每句话都是“失忆”的。要做多轮对话需要把聊天历史传给模型。通常只保留最近 10 到 20 条消息避免上下文过长导致费用升高和响应变慢。class SessionMemory: def __init__(self, max_messages: int 20): self.max_messages max_messages self.messages [] def add_user(self, content: str): self.messages.append({role: user, content: content}) self._trim() def add_assistant(self, content: str): self.messages.append({role: assistant, content: content}) self._trim() def _trim(self): if len(self.messages) self.max_messages: self.messages self.messages[-self.max_messages:] def to_list(self): return list(self.messages)每个 QQ 用户或每个群维护一个独立的SessionMemory实例用user_id或group_id作为 key。这样不同群的对话不会互相串味。memory_map {} def get_memory(session_key: str) - SessionMemory: if session_key not in memory_map: memory_map[session_key] SessionMemory() return memory_map[session_key]注意长期运行的机器人内存里会积累大量 Session需要定时清理无活跃的会话或者直接用 Redis 这类外部存储做 LRU 淘汰。7.3 人设不一致问题很多人设翻车的原因是每次请求都传一个短 Prompt模型容易“出戏”。解决方法是固定 System Prompt并且不要在用户消息里夹带“人设补充”把角色信息全部放到 System Prompt 中。另外如果希望长期角色扮演稳定可以考虑用更长、更详细的角色卡把说话风格、背景故事、典型回复都写进去。8. 功能测试与效果验证8.1 单条回复测试给机器人发一条普通消息比如“今天好累”。预期结果回复 1 到 3 句口语化没有长篇大论。判断标准回复内容不是“作为一个人工智能我无法体验累”这类 AI 味句子。8.2 多段回复测试故意触发一个需要多段回复的问题比如“帮我列一下周末去露营要带的物品分三批发”。预期结果机器人分多条消息发送消息之间有时间间隔语义完整不是一次性一大段。8.3 多轮对话测试连续追问同一个话题比如“你晚饭吃了什么”“那这道菜怎么做”“需要什么材料”。判断标准机器人记得刚才聊的是做饭话题不会突然跳到无关内容。8.4 异常输入测试分别测试空消息、只发图片消息、连续快速发多条消息、超长消息。预期结果空消息不触发回复或回复提示。图片消息在拿到图片描述前可以先回复“这张图我看不到”。如果需要识别图片需要额外接多模态接口不在本文范围。连续快速消息不会导致回复乱序可以加去重和频率限制。8.5 失败时排查什么优先看三个东西Python 服务日志有没有报错。DeepSeek API 返回的 HTTP 状态码。机器人框架日志里有没有发送失败信息。9. DeepSeek API 调用规范与批量任务9.1 请求参数说明标准的 chat completions 请求包含几个关键参数model模型标识以官方文档为准。messages消息列表。temperature控制随机性闲聊可以设为 0.8 到 1.0问答场景建议 0.3 以下。max_tokens控制单次回复最大长度。多段回复可以适当调大比如 1024 或 2048让模型先把完整内容生成出来再拆分。示例请求curl https://api.deepseek.com/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $DEEPSEEK_API_KEY \ -d { model: deepseek-chat, messages: [ {role: system, content: 你是一个QQ网友说话简短口语化。}, {role: user, content: 你好介绍下你自己} ], temperature: 0.8, max_tokens: 512 }9.2 错误处理与重试DeepSeek API 可能返回 HTTP 429限流、401Key 错误、400参数错误、500服务端错误。建议在代码里统一捕获异常做退避重试。import time import openai def chat_once_with_retry(user_message: str, max_retries: int 3): for attempt in range(max_retries): try: return chat_once(user_message) except openai.RateLimitError: wait 2 ** attempt time.sleep(wait) except openai.AuthenticationError: raise except openai.APIConnectionError: wait 2 ** attempt time.sleep(wait) return 我现在有点卡过一会儿再回你。9.3 批量/并发消息处理当机器人在多个群同时工作时多个消息事件会并发进来。asyncio.create_task已经能实现并发但要控制最大并发数避免把 API 限额打满。可以使用asyncio.Semaphoresemaphore asyncio.Semaphore(5) async def handle_event_with_limit(ws, event): async with semaphore: await handle_event(ws, event)如果要做更复杂的任务队列可以引入 Redis 做消息队列把收到的消息先写入队列再由 worker 消费。这一步在消息量很大的场景才有必要个人小群直接并发就够。10. 资源占用与性能观察10.1 API 模式资源占用使用 DeepSeek 云端 API 时瓶颈不在本地硬件而在网络和 API 限流。CPU 占用很低。Python 中转服务在个人服务器上常年跑只有 1%-5% CPU。内存占用取决于 Session 缓存量和 WebSocket 连接数。几十个会话时内存通常不超过 200MB。网络方面每个请求的耗时主要取决于模型推理速度和消息长度。普通闲聊 3-8 秒返回完整内容属于正常范围。观察方法# Linux 下查看 Python 进程资源占用 ps aux | grep python # 实时查看 CPU/内存 top如果响应越来越慢先看是不是 Session 上下文太长再看是不是多个群同时请求触发了 API 限流。10.2 本地部署模式资源占用可选如果你想完全本地部署 DeepSeek 模型比如用 Ollama 加载模型那么硬件门槛会显著上升。保守的建议是7B 级别模型在量化后需要 6G 以上显存才能跑得动14B 级别需要 12G 以上显存更大参数模型建议 24G 以上。具体占用以实际模型的量化版本和上下文长度为准这里不写死某一款型号的数字因为模型更新太快。本地部署的好处是数据不出服务器、无 API 费用适合隐私要求严格的场景。缺点是需要维护模型服务响应速度也受显卡性能限制。10.3 性能优化建议限制max_tokens不要让模型生成无意义的长文本。冗余的历史消息及时清理控制上下文长度。添加回复频率限制比如同一个用户 3 秒内只能触发一次机器人回复避免连续刷消息。WebSocket 断线时增加重连逻辑并保证重连后 Session 不丢失。11. 常见问题与排查方法问题现象可能原因排查方式解决方案Python 连不上 WebSocket地址端口填错或框架未启动 WS 服务检查框架配置和日志修正 WS_URL确认端口监听开启机器人不回复消息事件没收到或调用 API 报错查看 Python 控制台日志和框架日志先跑最小接收脚本确认事件能打印API 返回 401API Key 无效检查DEEPSEEK_API_KEY是否正确加载重新创建 Key检查.env路径API 返回 429触发限流或余额不足查看响应头和官方后台增加退避重试按需充值回复是英文模型没接收到中文指令确认 System Prompt 是否传入检查 messages 结构System Prompt 放在第一条回复一次性一大段没有启用多段拆分逻辑确认调用的是send_reply_in_chunks在发送前调用拆分函数多轮对话不记得前文Session 没有保存或 key 用错检查get_memory的 key用 user_id/group_id 区分会话机器人群里响应所有人消息没有过滤触发条件检查是否只处理 或指定前缀增加命令前缀或 过滤WebSocket 频繁断开网络波动或框架自动重连逻辑异常查看框架日志在 Python 侧增加自动重连发消息失败提示“消息过长”单条消息超过长度限制检查发送前的拆分长度调小max_len强制分块12. 最佳实践与合规提醒这一部分很重要尤其是 QQ 机器人这个场景技术能跑通只是第一步安全边界一定要想清楚。第一QQ 机器人账号风险。使用第三方 OneBot 方案登录 QQ 账号本质上绕过了官方客户端协议存在账号被限制的风险。建议只用小号测试不要用于重要业务账号。如果要做正式商用机器人优先考虑腾讯官方机器人开放平台或企业微信机器人等官方渠道。第二拟人化聊天的边界。让 AI 模拟真人聊天绝对不能用于诈骗、冒充他人、诱导付费、批量骚扰等非法用途。面向公众使用时应该让用户知道面对的是 AI 机器人。第三聊天数据隐私。QQ 群里的聊天内容可能包含群友的个人信息和隐私。中转服务落日志时不要记录完整消息内容或者及时脱敏。Session 缓存要设置过期时间避免长期留存在内存中。第四内容安全。虽然拟人化要求“说话自然”但不能让机器人在群里输出违法违规、恶意中伤、色情暴力内容。建议在回复发送前加一层敏感词过滤或者在 System Prompt 里明确禁止相关话题。第五接口凭证管理。DeepSeek API Key 涉及费用不要写在代码里提交到 GitHub。使用python-dotenv或服务器环境变量管理密钥并定期巡检是否有泄露。13. 总结与下一步这套方案的核心价值在于用最少的代码把 DeepSeek 变成一个真正能在 QQ 群里“聊起来”的机器人而不是一个“问答接口”。多段回复逻辑决定了体验的下限System Prompt 和上下文管理决定了体验的上限。第一次跑通后最先应该验证的三件事消息能否稳定收发、DeepSeek 返回是否正常、多段拆分是否符合预期。最容易踩的坑是 WebSocket 地址配错和 API Key 没加载成功这两个问题占了初期调试的七成。后续可以继续扩展的方向包括接入长短期记忆让机器人记住用户偏好、增加图片理解和生成能力、接入定时任务让机器人主动发言、把接入方式从 QQ 复制到企业微信或飞书、用 Redis 做多实例部署支撑更大并发。建议先按文中的最小脚本跑通再加上多段回复拆分最后加入自己的拟人化人设。每一步都可以独立验证出问题也能快速定位。收藏备用动手搭一个试试。