Gemini 全能 QQ 机器人部署手册 (V1.0 Release)

Gemini 全能 QQ 机器人部署手册 (V1.0 Release)

核心架构 :OneBot V11 (NapCat) + NoneBot2 + Gemini Flash
适用系统:Ubuntu 22.04 LTS (阿里云/腾讯云)


🟢 第一阶段:基础设施准备

SSH 连接服务器后,复制以下命令执行。

  1. 安装必要软件 (Docker + Python)

    bash 复制代码
    # 更新软件源
    sudo apt update && sudo apt upgrade -y
    
    # 安装 Docker
    curl -fsSL https://get.docker.com | bash
    
    # 安装 Python3 及虚拟环境工具
    sudo apt install python3-pip python3-venv -y
    
    # 创建项目文件夹
    mkdir -p /root/gemini_bot
    cd /root/gemini_bot

🔌 第二阶段:部署 QQ 协议端 (NapCat)

【避坑重点】 :这里我们使用 --net=host 模式启动。这样容器和宿主机共用网络,配置 WebSocket 时直接填 127.0.0.1 即可,彻底解决连接不上、找不到 IP 的问题

  1. 启动 NapCat 容器
    (请将下方命令中的 你的QQ号 替换为实际数字)

    bash 复制代码
    docker run -d \
      --name napcat \
      --net=host \
      --restart always \
      -e ACCOUNT=你的QQ号 \
      -v /root/gemini_bot/napcat/config:/app/config \
      mlikiowa/napcat-docker:latest
  2. 放行防火墙端口

    • 请去阿里云/腾讯云控制台的"防火墙"或"安全组",放行 TCP 6099TCP 8080

🧠 第三阶段:部署核心代码

【避坑重点】 :这里我们锁定安装 0.3.2 版本的 Google 库,这是目前配合此代码最稳定的版本,没有任何属性报错。

  1. 配置 Python 环境

    bash 复制代码
    # 确保在项目目录
    cd /root/gemini_bot
    
    # 创建虚拟环境
    python3 -m venv venv
    
    # 激活环境
    source venv/bin/activate
    
    # 安装依赖 (锁定版本,拒绝报错)
    pip install nonebot2[fastapi] nonebot-adapter-onebot google-generativeai==0.3.2 pytz
  2. 创建 memory.py (纯净版)

    • 此版本修复了全角空格问题。
    • 此版本修复了回复带时间前缀的问题。
    • 此版本使用了同步接口+Async包装,兼容性最强。

    使用 nano memory.py 创建文件,填入你的 API Key

python 复制代码
# 恢复旧版导入(关键!)
import google.generativeai as genai
import asyncio
import datetime
import pytz
import pickle
import os
import random

# ================= 核心配置区 =================
API_KEY = "在这里粘贴你的Google_API_Key"  # 必须替换!
DATA_FILE = "bot_memory.pkl"

# --- 拟人化参数 (V1.0 最终版) ---
COOLDOWN_SECONDS = 300  # 主动说话冷却 (5分钟)
TRIGGER_PROBABILITY = 0.015  # 闲聊插嘴概率 (1.5%)
REPEATER_THRESHOLD = 2  # 复读检测阈值 (至少2人)
REPEATER_EXEC_PROB = 0.1  # 复读执行概率 (10%,防止刷屏)
MEMORY_LIMIT_CHARS = 10000  # 缓存字数上限
MEMORY_KEEP_COUNT = 100  # 压缩时保留最近 200 条
# ============================================

# 旧版兼容写法(0.3.2 支持)
genai.configure(api_key=API_KEY)
# 移除无效的 tools 配置(避免报错),联网功能由模型自动处理
chat_model = genai.GenerativeModel('gemini-2.5-flash')

class GroupMemory:
    def __init__(self, group_id):
        self.group_id = group_id
        self.summary = "暂无早期历史记录。" 
        self.buffer = [] 
        self.lock = asyncio.Lock()
        
        # 状态追踪
        self.last_active_time = 0 
        self.last_msg_content = "" 
        self.repeat_count = 0 

    def add_message(self, name, msg, time_str):
        entry = f"[{time_str}] 【{name}】: {msg}"
        self.buffer.append(entry)
        
        # 复读计数
        clean_msg = msg.strip()
        if clean_msg == self.last_msg_content and clean_msg:
            self.repeat_count += 1
        else:
            self.last_msg_content = clean_msg
            self.repeat_count = 1

    def estimate_length(self):
        return sum(len(m) for m in self.buffer)

    async def check_and_compress(self):
        """安全压缩逻辑:API成功才删旧数据"""
        async with self.lock:
            if self.estimate_length() > MEMORY_LIMIT_CHARS and len(self.buffer) > MEMORY_KEEP_COUNT:
                to_compress = self.buffer[:-MEMORY_KEEP_COUNT]
                text_block = "\n".join(to_compress)
                
                prompt = f"""
                你是记忆整理员,更新长期记忆摘要(800字内):
                【长期记忆】:{self.summary}
                【待归档对话】:{text_block}
                """
                
                try:
                    # 同步接口 + asyncio 包装(避免异步兼容问题)
                    loop = asyncio.get_running_loop()
                    resp = await loop.run_in_executor(None, lambda: chat_model.generate_content(prompt))
                    
                    if resp.candidates[0].content.parts[0].text:
                        self.summary = resp.candidates[0].content.parts[0].text
                        self.buffer = self.buffer[-MEMORY_KEEP_COUNT:]
                        save_data()
                except Exception as e:
                    print(f"压缩记忆失败:{e}")
                    pass

    async def generate_reply(self, current_question, sender_name, is_active_interrupt=False):
        tz = pytz.timezone('Asia/Shanghai')
        context_str = "\n".join(self.buffer)

        # 构建回复提示词
        if is_active_interrupt:
            task_prompt = "没人艾特你,自然加入讨论,简短、像群友,可复读/玩梗,直接说话不加称呼,绝对不要输出任何时间戳/日期/时间格式。"
        else:
            if not current_question.strip():
                task_prompt = f"用户【{sender_name}】艾特了你,自然接话,绝对不要输出任何时间戳/日期/时间格式。"
            else:
                task_prompt = f"用户【{sender_name}】提问:{current_question},请回答,绝对不要输出任何时间戳/日期/时间格式。"

        final_prompt = f"""
        你是QQ群里的AI伙伴Gemini。
        【长期记忆】:{self.summary}
        【近期对话】:{context_str}
        【任务】:{task_prompt}
        【规则】:问时间时只说当前北京时间(比如"现在是下午3点20分"),但不要输出任何括号/时间戳/数字格式的时间;问新闻/数据自动联网,风格像群友而非客服。
        """
        try:
            loop = asyncio.get_running_loop()
            resp = await loop.run_in_executor(None, lambda: chat_model.generate_content(final_prompt))
            return resp.candidates[0].content.parts[0].text if resp.candidates[0].content.parts[0].text else "(暂时无法回复)"
        except Exception as e:
            return f"(回复失败:{str(e)})"

    async def check_active_intervention(self, current_msg):
        """主动介入判断:冷却→复读→闲聊→AI决策"""
        import time
        now = time.time()
        
        # 1. 冷却判断
        if now - self.last_active_time < COOLDOWN_SECONDS:
            return False, None

        # 2. 智能复读(10%概率)
        if self.repeat_count >= REPEATER_THRESHOLD and random.random() < REPEATER_EXEC_PROB:
            try:
                loop = asyncio.get_running_loop()
                check_resp = await loop.run_in_executor(None, 
                    lambda: chat_model.generate_content(f"判断'{current_msg}'是否是正常梗(非广告/脏话):是回YES,否回NO")
                )
                if "YES" in check_resp.candidates[0].content.parts[0].text.strip().upper():
                    self.last_active_time = now
                    return True, current_msg
            except:
                pass
            return False, None

        # 3. 闲聊插嘴(1.5%概率)
        if "gemini" not in current_msg.lower() and random.random() > TRIGGER_PROBABILITY:
            return False, None

        # 4. AI决策是否说话
        try:
            loop = asyncio.get_running_loop()
            decision_resp = await loop.run_in_executor(None,
                lambda: chat_model.generate_content(f"""
                最近消息:{chr(10).join(self.buffer[-15:])}
                当前消息:{current_msg}
                没被艾特,是否有必要说话?满足(客观问题无人答/讨论你/有趣梗)回YES,否则NO
                """)
            )
            if "YES" in decision_resp.candidates[0].content.parts[0].text.strip().upper():
                reply = await self.generate_reply("", "群友", is_active_interrupt=True)
                self.last_active_time = now
                return True, reply
        except Exception as e:
            print(f"主动介入失败:{e}")
            pass
            
        return False, None

# --- 记忆持久化 ---
memories = {}
def save_data():
    try:
        with open(DATA_FILE, 'wb') as f:
            pickle.dump(memories, f)
    except Exception as e:
        print(f"保存记忆失败:{e}")

def load_data():
    global memories
    if os.path.exists(DATA_FILE):
        try:
            with open(DATA_FILE, 'rb') as f:
                memories = pickle.load(f)
        except:
            memories = {}

def get_memory(group_id):
    if group_id not in memories:
        memories[group_id] = GroupMemory(group_id)
    return memories[group_id]

load_data()
  1. 创建 bot.py
    • 使用 nano bot.py 创建:
python 复制代码
import nonebot
from nonebot import on_message
from nonebot.adapters.onebot.v11 import Adapter, Bot, GroupMessageEvent
from memory import get_memory, save_data
import atexit
import datetime
import pytz

nonebot.init()
driver = nonebot.get_driver()
driver.register_adapter(Adapter)
atexit.register(save_data)

def unix_to_beijing(timestamp):
    dt_utc = datetime.datetime.fromtimestamp(timestamp, pytz.utc)
    dt_bj = dt_utc.astimezone(pytz.timezone('Asia/Shanghai'))
    return dt_bj.strftime("%H:%M:%S")

def get_current_bj_time():
    return datetime.datetime.now(pytz.timezone('Asia/Shanghai')).strftime("%H:%M:%S")

def is_at_me(bot: Bot, event: GroupMessageEvent) -> bool:
    if event.is_tome(): return True
    for seg in event.message:
        if seg.type == "at" and str(seg.data.get("qq")) == str(bot.self_id):
            return True
    return False

# === 单核处理器 ===
handler = on_message(priority=1, block=True)

@handler.handle()
async def _(bot: Bot, event: GroupMessageEvent):
    group_id = event.group_id
    user_id = event.user_id
    msg = event.get_plaintext().strip()
    if not msg: msg = "[非文本消息]"

    try:
        info = await bot.get_group_member_info(group_id=group_id, user_id=user_id)
        name = info.get('card') or info.get('nickname') or str(user_id)
    except: name = "群友"
    
    # 1. 记录用户消息
    mem = get_memory(group_id)
    mem.add_message(name, msg, unix_to_beijing(event.time))
    save_data()
    
    # 2. 压缩检查
    await mem.check_and_compress()
    
    # 3. 决策逻辑
    final_reply = None
    
    if is_at_me(bot, event):
        # 情况A:被艾特 -> 必回
        # 这里的 msg 可能包含 @机器人,Gemini处理时会自动忽略或理解为名字,无需刻意剔除
        final_reply = await mem.generate_reply(msg, name, is_active_interrupt=False)
    else:
        # 情况B:没被艾特 -> 尝试插嘴
        should, content = await mem.check_active_intervention(msg)
        if should and content:
            final_reply = content

    # 4. 如果有回复,发送并记录
    if final_reply:
        # 记录机器人自己的回复
        mem.add_message("Gemini", final_reply, get_current_bj_time())
        save_data()
        # 发送
        await handler.finish(final_reply)

if __name__ == "__main__":
    nonebot.run(host="0.0.0.0", port=8080)

⚙️ 第四阶段:配置系统服务 (Systemd)

为了实现您要求的命令管理,我们需要创建一个系统服务文件。

  1. 创建服务文件

    bash 复制代码
    sudo nano /etc/systemd/system/gemini-bot.service
  2. 粘贴内容
    (注意:ExecStart 指向了虚拟环境中的 python,确保路径准确)

    ini 复制代码
    [Unit]
    Description=Gemini QQ Bot Service
    After=network.target
    
    [Service]
    # 指定用户,通常是 root
    User=root
    # 你的项目路径
    WorkingDirectory=/root/gemini_bot
    # 使用虚拟环境的 Python 启动 bot.py
    ExecStart=/root/gemini_bot/venv/bin/python bot.py
    # 自动重启配置
    Restart=always
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target
  3. 加载服务

    bash 复制代码
    sudo systemctl daemon-reload
    sudo systemctl enable gemini-bot

🖥️ 第五阶段:NapCat 连接 (WebUI)

【避坑重点】 :因为我们用了 host 模式,这里的 WS 地址必须填 127.0.0.1

  1. 启动机器人

    bash 复制代码
    systemctl start gemini-bot
  2. 浏览器访问 NapCat

    访问 http://服务器IP:6099/webui
    (如果没有Token,输入 napcat 或查看日志 docker logs napcat)

  3. 扫码登录

    使用手机 QQ 扫码。

  4. 配置 WebSocket (Reverse)

    • 点击左侧 网络配置 -> WebSocket (Reverse)
    • URL 填写ws://127.0.0.1:8080/onebot/v11/ws
    • 启用:打钩
    • 点击 保存
  5. 重启 NapCat 生效

    bash 复制代码
    docker restart napcat

🎮 最终:管理命令速查表

恭喜!部署完成。以后您只需要使用以下命令管理您的机器人:

功能 命令
启动机器人 systemctl start gemini-bot
停止机器人 systemctl stop gemini-bot
改代码后重启 systemctl restart gemini-bot
查看运行状态 systemctl status gemini-bot
实时查看日志 journalctl -u gemini-bot -f

(日志中若看到 Connect to 127.0.0.1:8080Received message 即为成功)

相关推荐
得物技术2 小时前
入选AAAI-PerFM|得物社区推荐之基于大语言模型的新颖性推荐算法
人工智能·语言模型·推荐算法
赋创小助手2 小时前
NVIDIA H100与GH200选型指南:AI与HPC负载的硬件适配方案
服务器·人工智能·深度学习·神经网络·语言模型·自然语言处理·tensorflow
yuezhilangniao3 小时前
ai开发 名词解释-概念理解-LLMs(大语言模型)Chat Models(聊天模型)Embeddings Models(嵌入模型).
人工智能·语言模型·自然语言处理
weixin_402939993 小时前
【机器人】机器人方向的顶会--自用
人工智能·机器学习·机器人
飞睿科技3 小时前
解析ESP-SparkBot开源大模型AI桌面机器人的ESP32-S3核心方案
人工智能·嵌入式硬件·物联网·机器人·esp32·乐鑫科技·ai交互
仰科网关3 小时前
使用协议转换网关实现机器人EthernetIP转成西门子Profinet的项目案例
机器人·profinet·ethernetip·vfbox·协议转换·规约转换器
敲代码的猴先生3 小时前
论文分享 | 基于红绿列表的大语言模型水印技术
论文阅读·人工智能·深度学习·语言模型
BBTSOH159015160443 小时前
VR每日热点简报2026.1.22
人工智能·机器人·虚拟现实·遥操作
Lian_Ge_Blog3 小时前
MOE/GShard/Switch_Transformers结构学习总结
语言模型