From 6c48b08f24560cca6f319f1af43b663b75d72c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=A5=E6=B2=B3=E6=99=B4?= Date: Mon, 17 Mar 2025 15:15:51 +0900 Subject: [PATCH 01/27] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0CLAUDE.md?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E9=A1=B9=E7=9B=AE=E6=9E=B6=E6=9E=84?= =?UTF-8?q?=E5=92=8C=E4=BB=A3=E7=A0=81=E7=B4=A2=E5=BC=95=E6=8C=87=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加Mermaid图表展示项目结构和流程 - 创建关键文件索引表格 - 详细描述记忆系统和聊天系统内部结构 - 增加配置系统概览 - 提供模块依赖关系图表 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CLAUDE.md | 171 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 157 insertions(+), 14 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d30b0e651..47f3479a0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# MaiMBot 开发指南 +# MaiMBot 项目架构与索引指南 ## 🛠️ 常用命令 @@ -30,19 +30,162 @@ - **错误处理**: 使用带有具体异常的try/except - **文档**: 为类和公共函数编写docstrings -## 🧩 系统架构 +## 🔍 项目结构概览 -- **框架**: NoneBot2框架与插件架构 -- **数据库**: MongoDB持久化存储 -- **设计模式**: 工厂模式和单例管理器 -- **配置管理**: 使用环境变量和TOML文件 -- **内存系统**: 基于图的记忆结构,支持记忆构建、压缩、检索和遗忘 -- **情绪系统**: 情绪模拟与概率权重 -- **LLM集成**: 支持多个LLM服务提供商(ChatAnywhere, SiliconFlow, DeepSeek) +```mermaid +graph TD + A[入口文件] --> A1[run.py:初始安装与启动] + A --> A2[bot.py:主程序入口] + A2 --> B[核心框架] + B --> B1[NoneBot2框架] + B --> B2[MongoDB数据库] + + A2 --> C[插件系统] + C --> C1[聊天系统] + C --> C2[记忆系统] + C --> C3[情绪系统] + C --> C4[日程系统] + C --> C5[配置系统] + + C1 --> D[LLM集成] + D --> D1[ChatAnywhere] + D --> D2[SiliconFlow] + D --> D3[DeepSeek] +``` -## ⚙️ 环境配置 +## 📁 关键文件索引 -- 使用`template.env`作为环境变量模板 -- 使用`template/bot_config_template.toml`作为机器人配置模板 -- MongoDB配置: 主机、端口、数据库名 -- API密钥配置: 各LLM提供商的API密钥 +| 文件路径 | 功能描述 | +|---------|---------| +| `/bot.py` | 主程序入口,初始化框架和插件加载 | +| `/run.py` | 初始安装脚本,配置MongoDB和启动机器人 | +| `/src/plugins/chat/bot.py` | 聊天核心处理,消息接收与分发 | +| `/src/plugins/chat/llm_generator.py` | LLM交互封装,生成回复内容 | +| `/src/plugins/chat/prompt_builder.py` | 构建提示词,整合上下文和记忆 | +| `/src/plugins/memory_system/memory.py` | 图形记忆系统核心实现 | +| `/src/plugins/moods/moods.py` | 情绪管理系统 | +| `/src/common/database.py` | 数据库连接管理 | +| `/src/plugins/models/utils_model.py` | LLM API请求封装 | +| `/template.env` | 环境变量配置模板 | +| `/template/bot_config_template.toml` | 机器人配置模板 | + +## 🔄 核心流程图 + +### 消息处理流程 + +```mermaid +flowchart LR + A[用户消息] --> B[NoneBot2接收] + B --> C[ChatBot.handle_message] + C --> D{检查回复意愿} + D -->|回复| E[思考状态] + D -->|不回复| Z[结束] + E --> F[构建提示词] + F --> G[选择LLM模型] + G --> H[生成回复] + H --> I[处理回复] + I --> J[消息管理器] + J --> K[发送回复] +``` + +### 记忆系统流程 + +```mermaid +flowchart TD + A[聊天记录] --> B[记忆样本获取] + B --> C[记忆压缩/主题提取] + C --> D[记忆图存储] + D --> E[记忆检索] + D --> F[记忆遗忘] + D --> G[记忆合并] + E --> H[提示词构建] + H --> I[LLM生成] +``` + +## ⚙️ 配置系统概览 + +```mermaid +graph LR + A[配置系统] --> B[环境变量配置] + A --> C[TOML配置文件] + + B --> B1[数据库连接] + B --> B2[LLM API密钥] + B --> B3[服务器设置] + + C --> C1[机器人人格] + C --> C2[消息处理参数] + C --> C3[记忆系统参数] + C --> C4[情绪系统参数] + C --> C5[模型配置] +``` + +## 📊 模块依赖关系 + +```mermaid +graph TD + A[bot.py] --> B[src/plugins] + B --> C[chat] + B --> D[memory_system] + B --> E[moods] + B --> F[models] + + C --> D + C --> E + C --> F + D --> F + C --> G[common/database.py] + D --> G +``` + +## 🧠 记忆系统内部结构 + +- **Memory_graph**: 底层图结构实现 + - 节点 = 主题概念 + - 边 = 主题间关联 + - 属性 = 记忆内容、时间戳 + +- **Hippocampus**: 高级记忆管理 + - 记忆构建: `memory_compress()` + - 记忆检索: `get_relevant_memories()` + - 记忆遗忘: `operation_forget_topic()` + - 记忆合并: `operation_merge_memory()` + +- **LLM集成点**: + - 主题提取 + - 记忆摘要生成 + - 相似度计算 + - 记忆压缩 + +## 💬 聊天系统内部结构 + +- **ChatBot**: 核心控制器 + - 消息处理: `handle_message()` + - 响应生成: `generate_response()` + +- **消息处理链**: + - `MessageRecv` → 消息预处理 + - `willing_manager` → 回复决策 + - `prompt_builder` → 提示词构建 + - `LLM_request` → LLM调用 + - `MessageSending` → 消息发送 + +- **关键组件**: + - 消息管理器: 控制消息流 + - 聊天流管理: 维护会话上下文 + - 关系管理器: 用户关系状态 + - 表情管理器: 表情包处理 + +## 🔧 配置项关键参数 + +### 环境变量 (.env) +- MongoDB连接: `MONGODB_HOST`, `MONGODB_PORT`, `DATABASE_NAME` +- LLM API: `CHAT_ANY_WHERE_KEY`, `SILICONFLOW_KEY`, `DEEP_SEEK_KEY` +- 服务设置: `HOST`, `PORT` + +### 机器人配置 (TOML) +- 版本控制: `[inner].version` +- 人格设置: `[personality]` +- 记忆参数: `[memory]` (构建间隔、压缩率、遗忘周期) +- 情绪参数: `[mood]` (更新间隔、衰减率) +- 模型选择: `[model]` (各功能专用模型配置) \ No newline at end of file From a3918a5aee5611d129f0b25bccee35909eb7b9fd Mon Sep 17 00:00:00 2001 From: MuWinds Date: Mon, 31 Mar 2025 13:15:42 +0800 Subject: [PATCH 02/27] =?UTF-8?q?Fix:=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8D=E5=AD=98=E5=9C=A8=E7=9A=84=E6=97=B6=E5=80=99=E5=85=88?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=9B=AE=E5=BD=95=EF=BC=8C=E5=90=A6=E5=88=99?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E6=96=87=E4=BB=B6=E7=9A=84=E6=97=B6=E5=80=99?= =?UTF-8?q?=E6=8A=A5=E9=94=99=EF=BC=9AFileNotFoundError:=20[WinError=203]?= =?UTF-8?q?=20=E7=B3=BB=E7=BB=9F=E6=89=BE=E4=B8=8D=E5=88=B0=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E7=9A=84=E8=B7=AF=E5=BE=84=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/config/config.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/config/config.py b/src/plugins/config/config.py index a4a38dc1a..bf2463bf8 100644 --- a/src/plugins/config/config.py +++ b/src/plugins/config/config.py @@ -43,6 +43,8 @@ def update_config(): # 检查配置文件是否存在 if not old_config_path.exists(): logger.info("配置文件不存在,从模板创建新配置") + #创建文件夹 + old_config_dir.mkdir(parents=True, exist_ok=True) shutil.copy2(template_path, old_config_path) logger.info(f"已创建新配置文件,请填写后重新运行: {old_config_path}") # 如果是新创建的配置文件,直接返回 From 21ccefaf298189396c8604fd8498037e32909faa Mon Sep 17 00:00:00 2001 From: infinitycat Date: Mon, 31 Mar 2025 23:56:01 +0800 Subject: [PATCH 03/27] =?UTF-8?q?build(docker):=20=E9=87=8D=E6=9E=84=20Doc?= =?UTF-8?q?kerfile=20=E5=B9=B6=E6=B7=BB=E5=8A=A0=20docker-compose=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 Dockerfile,使用 python:3.13.2-slim-bookworm 作为基础镜像 - 添加 maim_message目录到镜像中,并使用清华大学镜像源安装依赖 - 新增 docker-compose.yml 文件,定义多服务的 Docker Compose 配置 - 配置包含 adapters、core、mongodb 和 napcat四个服务 - 设置端口映射、环境变量和数据卷 --- Dockerfile | 21 +++++++------- docker-compose.yml | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile index c4aedc94a..ed4734b8d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,19 @@ -FROM nonebot/nb-cli:latest +FROM python:3.13.2-slim-bookworm -# 设置工作目录 +# 工作目录 WORKDIR /MaiMBot -# 先复制依赖列表 +# 复制依赖列表 COPY requirements.txt . +# 同级目录下需要有 maim_message 文 +COPY maim_message /maim_message -# 安装依赖(这层会被缓存直到requirements.txt改变) -RUN pip install --upgrade -r requirements.txt +# 安装依赖 +RUN pip install -e /maim_message -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +RUN pip install --upgrade -r requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple -# 然后复制项目代码 +# 复制项目代码 COPY . . -VOLUME [ "/MaiMBot/config" ] -VOLUME [ "/MaiMBot/data" ] -EXPOSE 8080 -ENTRYPOINT [ "nb","run" ] \ No newline at end of file +EXPOSE 8000 +ENTRYPOINT [ "python","bot.py" ] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..c28a13ba8 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,70 @@ +services: + adapters: + container_name: maim-bot-adapters + image: maimbot-adapters:latest + environment: + - TZ=Asia/Shanghai + ports: + - "18002:18002" + volumes: + - ./adapters/plugins:/adapters/src/plugins + - ./adapters/.env:/adapters/.env + - ./data/qq:/app/.config/QQ + restart: always + networks: + - maim_bot + core: + container_name: maim-bot-core + image: maimbot-core:latest + environment: + - TZ=Asia/Shanghai +# - EULA_AGREE=35362b6ea30f12891d46ef545122e84a +# - PRIVACY_AGREE=2402af06e133d2d10d9c6c643fdc9333 + ports: + - "8000:8000" + volumes: + - ./mmc-data:/MaiMBot/data + - ./mmc-config/.env:/MaiMBot/.env + - ./mmc-config/bot_config.toml:/MaiMBot/config/bot_config.toml + - ./data/MaiMBot:/MaiMBot/data + restart: always + networks: + - maim_bot + mongodb: + container_name: mongodb + environment: + - TZ=Asia/Shanghai + ports: + - "27017:27017" + restart: always + volumes: + - mongodb:/data/db + - mongodbCONFIG:/data/configdb + image: mongo:latest + networks: + - maim_bot + napcat: + environment: + - NAPCAT_UID=1000 + - NAPCAT_GID=1000 + - TZ=Asia/Shanghai + ports: + - "3000:3000" + - "3001:3001" + - "6099:6099" + - "8095:8095" + volumes: + - ./config:/app/napcat/config + - ./data/qq:/app/.config/QQ + - ./data/MaiMBot:/MaiMBot/data + container_name: napcat + restart: always + image: mlikiowa/napcat-docker:latest + networks: + - maim_bot +networks: + maim_bot: + driver: bridge +volumes: + mongodb: + mongodbCONFIG: \ No newline at end of file From 765f14269f312d405faad1439bf94db76e7d9684 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Mon, 31 Mar 2025 23:56:01 +0800 Subject: [PATCH 04/27] =?UTF-8?q?build(docker):=20=E9=87=8D=E6=9E=84=20Doc?= =?UTF-8?q?kerfile=20=E5=B9=B6=E6=B7=BB=E5=8A=A0=20docker-compose=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 Dockerfile,使用 python:3.13.2-slim-bookworm 作为基础镜像 - 添加 maim_message目录到镜像中,并使用清华大学镜像源安装依赖 - 新增 docker-compose.yml 文件,定义多服务的 Docker Compose 配置 - 配置包含 adapters、core、mongodb 和 napcat四个服务 - 设置端口映射、环境变量和数据卷 --- Dockerfile | 21 +++++++------- docker-compose.yml | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 10 deletions(-) create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile index c4aedc94a..ed4734b8d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,19 @@ -FROM nonebot/nb-cli:latest +FROM python:3.13.2-slim-bookworm -# 设置工作目录 +# 工作目录 WORKDIR /MaiMBot -# 先复制依赖列表 +# 复制依赖列表 COPY requirements.txt . +# 同级目录下需要有 maim_message 文 +COPY maim_message /maim_message -# 安装依赖(这层会被缓存直到requirements.txt改变) -RUN pip install --upgrade -r requirements.txt +# 安装依赖 +RUN pip install -e /maim_message -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +RUN pip install --upgrade -r requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple -# 然后复制项目代码 +# 复制项目代码 COPY . . -VOLUME [ "/MaiMBot/config" ] -VOLUME [ "/MaiMBot/data" ] -EXPOSE 8080 -ENTRYPOINT [ "nb","run" ] \ No newline at end of file +EXPOSE 8000 +ENTRYPOINT [ "python","bot.py" ] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..488d3e722 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,70 @@ +services: + adapters: + container_name: maim-bot-adapters + image: maimbot-adapters:latest + environment: + - TZ=Asia/Shanghai + ports: + - "18002:18002" + volumes: + - ./adapters/plugins:/adapters/src/plugins + - ./adapters/.env:/adapters/.env + - ./data/qq:/app/.config/QQ + restart: always + networks: + - maim_bot + core: + container_name: maim-bot-core + image: maimbot-core:latest + environment: + - TZ=Asia/Shanghai +# - EULA_AGREE=35362b6ea30f12891d46ef545122e84a +# - PRIVACY_AGREE=2402af06e133d2d10d9c6c643fdc9333 + ports: + - "8000:8000" + volumes: + - ./mmc-data:/MaiMBot/data + - ./mmc-config/.env:/MaiMBot/.env + - ./mmc-config/bot_config.toml:/MaiMBot/config/bot_config.toml + - ./data/MaiMBot:/MaiMBot/data + restart: always + networks: + - maim_bot + mongodb: + container_name: maim-bot-mongo + environment: + - TZ=Asia/Shanghai + ports: + - "27017:27017" + restart: always + volumes: + - mongodb:/data/db + - mongodbCONFIG:/data/configdb + image: mongo:latest + networks: + - maim_bot + napcat: + environment: + - NAPCAT_UID=1000 + - NAPCAT_GID=1000 + - TZ=Asia/Shanghai + ports: + - "3000:3000" + - "3001:3001" + - "6099:6099" + - "8095:8095" + volumes: + - ./napcat-config:/app/napcat/config + - ./data/qq:/app/.config/QQ + - ./data/MaiMBot:/MaiMBot/data + container_name: maim-bot-napcat + restart: always + image: mlikiowa/napcat-docker:latest + networks: + - maim_bot +networks: + maim_bot: + driver: bridge +volumes: + mongodb: + mongodbCONFIG: \ No newline at end of file From 8fdd690542539d3c39c44dc09e40aa6b6c3e9130 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 00:17:28 +0800 Subject: [PATCH 05/27] =?UTF-8?q?build(docker):=20=E9=87=8D=E6=9E=84=20Doc?= =?UTF-8?q?kerfile=20=E5=B9=B6=E6=B7=BB=E5=8A=A0=20docker-compose=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 Dockerfile,使用 python:3.13.2-slim-bookworm 作为基础镜像 - 添加 maim_message目录到镜像中,并使用清华大学镜像源安装依赖 - 新增 docker-compose.yml 文件,定义多服务的 Docker Compose 配置 - 配置包含 adapters、core、mongodb 和 napcat四个服务 - 设置端口映射、环境变量和数据卷 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index ed4734b8d..37e6e1ad4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ WORKDIR /MaiMBot # 复制依赖列表 COPY requirements.txt . -# 同级目录下需要有 maim_message 文 +# 同级目录下需要有 maim_message COPY maim_message /maim_message # 安装依赖 From 1bff4d83de8480a2056746db027fdf1016d019fa Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 00:41:19 +0800 Subject: [PATCH 06/27] =?UTF-8?q?build(docker):=20=E9=87=8D=E6=9E=84=20Doc?= =?UTF-8?q?kerfile=20=E5=B9=B6=E6=B7=BB=E5=8A=A0=20docker-compose=20?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 重构 Dockerfile,使用 python:3.13.2-slim-bookworm 作为基础镜像 - 添加 maim_message目录到镜像中,并使用清华大学镜像源安装依赖 - 新增 docker-compose.yml 文件,定义多服务的 Docker Compose 配置 - 配置包含 adapters、core、mongodb 和 napcat四个服务 - 设置端口映射、环境变量和数据卷 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 37e6e1ad4..483892006 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,8 +9,8 @@ COPY requirements.txt . COPY maim_message /maim_message # 安装依赖 -RUN pip install -e /maim_message -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple -RUN pip install --upgrade -r requirements.txt -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple +RUN pip install -e /maim_message +RUN pip install --upgrade -r requirements.txt # 复制项目代码 COPY . . From 1624ae4ed665dba6c4aefec35b9611c4f6290f57 Mon Sep 17 00:00:00 2001 From: tcmofashi Date: Tue, 1 Apr 2025 03:15:01 +0800 Subject: [PATCH 07/27] =?UTF-8?q?fix:=20=E8=BF=99=E6=AC=A1=E7=9C=9F?= =?UTF-8?q?=E6=AD=A3=E5=B9=B6=E5=8F=91=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.py | 3 +-- src/plugins/message/api.py | 39 ++++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/main.py b/src/main.py index f58fc0d9d..621014ae6 100644 --- a/src/main.py +++ b/src/main.py @@ -14,7 +14,7 @@ from .plugins.chat.storage import MessageStorage from .plugins.config.config import global_config from .plugins.chat.bot import chat_bot from .common.logger import get_module_logger -from .plugins.remote import heartbeat_thread # noqa: F401 +from .plugins.remote import heartbeat_thread # noqa: F401 logger = get_module_logger("main") @@ -108,7 +108,6 @@ class MainSystem: self.remove_recalled_message_task(), emoji_manager.start_periodic_check(), self.app.run(), - self.app.message_process(), ] await asyncio.gather(*tasks) diff --git a/src/plugins/message/api.py b/src/plugins/message/api.py index 0478aab16..30cc8aeca 100644 --- a/src/plugins/message/api.py +++ b/src/plugins/message/api.py @@ -26,11 +26,20 @@ class BaseMessageAPI: @self.app.post("/api/message") async def handle_message(message: Dict[str, Any]): try: - self.cache.append(message) + # 创建后台任务处理消息 + asyncio.create_task(self._background_message_handler(message)) return {"status": "success"} except Exception as e: raise HTTPException(status_code=500, detail=str(e)) from e + async def _background_message_handler(self, message: Dict[str, Any]): + """后台处理单个消息""" + try: + await self.process_single_message(message) + except Exception as e: + logger.error(f"Background message processing failed: {str(e)}") + logger.error(traceback.format_exc()) + def register_message_handler(self, handler: Callable): """注册消息处理函数""" self.message_handlers.append(handler) @@ -45,23 +54,17 @@ class BaseMessageAPI: # logger.error(f"发送消息失败: {str(e)}") pass - async def message_process( - self, - ): - """启动消息处理""" - while True: - if len(self.cache) > 0: - for handler in self.message_handlers: - try: - await handler(self.cache[0]) - except Exception as e: - logger.error(str(e)) - logger.error(traceback.format_exc()) - self.cache.pop(0) - if len(self.cache) > 0: - await asyncio.sleep(0.1 / len(self.cache)) - else: - await asyncio.sleep(0.2) + async def process_single_message(self, message: Dict[str, Any]): + """处理单条消息""" + tasks = [] + for handler in self.message_handlers: + try: + tasks.append(handler(message)) + except Exception as e: + logger.error(str(e)) + logger.error(traceback.format_exc()) + if tasks: + await asyncio.gather(*tasks, return_exceptions=True) def run_sync(self): """同步方式运行服务器""" From 63921b775e9247748fe9ae6ac38f5b959d03b719 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 10:21:18 +0800 Subject: [PATCH 08/27] =?UTF-8?q?ci:=20=E6=B7=BB=E5=8A=A0=20fork=20?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 .github/workflows/refactor.yml 文件 - 配置定时任务,每 30 分钟同步一次上游仓库的 refactor 分支 - 使用 tgymnich/fork-sync 动作进行同步 - 设置同步的上游仓库用户为 SengokuCola- 指定同步的上游分支为 refactor,本地分支也为 refactor --- .github/workflows/refactor.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/refactor.yml diff --git a/.github/workflows/refactor.yml b/.github/workflows/refactor.yml new file mode 100644 index 000000000..ac2a4b5e9 --- /dev/null +++ b/.github/workflows/refactor.yml @@ -0,0 +1,17 @@ +# .github/workflows/sync.yml +name: Sync Fork + +on: + push: # push 时触发, 主要是为了测试配置有没有问题 + schedule: + - cron: '*/30 * * * *' # every 30 minutes +jobs: + repo-sync: + runs-on: ubuntu-latest + steps: + - uses: tgymnich/fork-sync@v2.0.10 + with: + token: ${{ secrets.PERSONAL_TOKEN }} + owner: SengokuCola # fork 的上游仓库 user + head: refactor # fork 的上游仓库 branch + base: refactor # 本地仓库 branch From cb48497df43bd8e6e26623ac8a092f4abb149ada Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 10:33:45 +0800 Subject: [PATCH 09/27] =?UTF-8?q?ci:=20=E6=B7=BB=E5=8A=A0=20fork=20?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 .github/workflows/refactor.yml 文件 - 配置定时任务,每 30 分钟同步一次上游仓库的 refactor 分支 - 使用 tgymnich/fork-sync 动作进行同步 - 设置同步的上游仓库用户为 SengokuCola- 指定同步的上游分支为 refactor,本地分支也为 refactor --- .github/workflows/refactor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/refactor.yml b/.github/workflows/refactor.yml index ac2a4b5e9..0bd019f1f 100644 --- a/.github/workflows/refactor.yml +++ b/.github/workflows/refactor.yml @@ -9,9 +9,9 @@ jobs: repo-sync: runs-on: ubuntu-latest steps: - - uses: tgymnich/fork-sync@v2.0.10 + - uses: TG908/fork-sync@v2.0.10 with: - token: ${{ secrets.PERSONAL_TOKEN }} + github_token: ${{ secrets.PERSONAL_TOKEN }} owner: SengokuCola # fork 的上游仓库 user head: refactor # fork 的上游仓库 branch base: refactor # 本地仓库 branch From 211a932352a79811e46d99f4f26af7fa5687c9e0 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 10:35:21 +0800 Subject: [PATCH 10/27] =?UTF-8?q?ci:=20=E6=B7=BB=E5=8A=A0=20fork=20?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 .github/workflows/refactor.yml 文件 - 配置定时任务,每 30 分钟同步一次上游仓库的 refactor 分支 - 使用 tgymnich/fork-sync 动作进行同步 - 设置同步的上游仓库用户为 SengokuCola- 指定同步的上游分支为 refactor,本地分支也为 refactor --- .github/workflows/refactor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/refactor.yml b/.github/workflows/refactor.yml index 0bd019f1f..049f6c328 100644 --- a/.github/workflows/refactor.yml +++ b/.github/workflows/refactor.yml @@ -9,9 +9,9 @@ jobs: repo-sync: runs-on: ubuntu-latest steps: - - uses: TG908/fork-sync@v2.0.10 + - uses: tgymnich/fork-sync@v2.0.10 with: - github_token: ${{ secrets.PERSONAL_TOKEN }} + token: ${{ secrets.GITHUB_TOKEN }} owner: SengokuCola # fork 的上游仓库 user head: refactor # fork 的上游仓库 branch base: refactor # 本地仓库 branch From b178911cd1f6978ced384a19e195f7e78b31e019 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 10:46:59 +0800 Subject: [PATCH 11/27] =?UTF-8?q?ci:=20=E6=B7=BB=E5=8A=A0=20fork=20?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 .github/workflows/refactor.yml 文件 - 配置定时任务,每 30 分钟同步一次上游仓库的 refactor 分支 - 使用 tgymnich/fork-sync 动作进行同步 - 设置同步的上游仓库用户为 SengokuCola- 指定同步的上游分支为 refactor,本地分支也为 refactor --- .github/workflows/refactor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/refactor.yml b/.github/workflows/refactor.yml index 049f6c328..69a28c897 100644 --- a/.github/workflows/refactor.yml +++ b/.github/workflows/refactor.yml @@ -9,9 +9,9 @@ jobs: repo-sync: runs-on: ubuntu-latest steps: - - uses: tgymnich/fork-sync@v2.0.10 + - uses: TG908/fork-sync@v2.0.10 with: - token: ${{ secrets.GITHUB_TOKEN }} + github_token: ${{ secrets.GITHUB_TOKEN }} owner: SengokuCola # fork 的上游仓库 user head: refactor # fork 的上游仓库 branch base: refactor # 本地仓库 branch From d91c25d7c5f19b4e1b0e5931d69f86d7ff1fa59e Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 11:12:48 +0800 Subject: [PATCH 12/27] =?UTF-8?q?=E6=80=8E=E4=B9=88=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E5=88=B0pr=E5=8E=BB=E4=BA=86=E6=B7=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/refactor.yml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 .github/workflows/refactor.yml diff --git a/.github/workflows/refactor.yml b/.github/workflows/refactor.yml deleted file mode 100644 index 69a28c897..000000000 --- a/.github/workflows/refactor.yml +++ /dev/null @@ -1,17 +0,0 @@ -# .github/workflows/sync.yml -name: Sync Fork - -on: - push: # push 时触发, 主要是为了测试配置有没有问题 - schedule: - - cron: '*/30 * * * *' # every 30 minutes -jobs: - repo-sync: - runs-on: ubuntu-latest - steps: - - uses: TG908/fork-sync@v2.0.10 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - owner: SengokuCola # fork 的上游仓库 user - head: refactor # fork 的上游仓库 branch - base: refactor # 本地仓库 branch From 648047b4ceca4bcab3f2748a65521e682919dee5 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 11:30:11 +0800 Subject: [PATCH 13/27] =?UTF-8?q?=E5=8A=A0=E4=BA=86=E7=82=B9=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 488d3e722..3ad94e067 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,9 +7,9 @@ services: ports: - "18002:18002" volumes: - - ./adapters/plugins:/adapters/src/plugins - - ./adapters/.env:/adapters/.env - - ./data/qq:/app/.config/QQ + - ./adapters/plugins:/adapters/src/plugins # 持久化adapters插件 + - ./adapters/.env:/adapters/.env # 持久化adapters配置文件 + - ./data/qq:/app/.config/QQ # 持久化QQ本体并同步qq表情和图片到adapters restart: always networks: - maim_bot @@ -18,15 +18,15 @@ services: image: maimbot-core:latest environment: - TZ=Asia/Shanghai -# - EULA_AGREE=35362b6ea30f12891d46ef545122e84a -# - PRIVACY_AGREE=2402af06e133d2d10d9c6c643fdc9333 +# - EULA_AGREE=35362b6ea30f12891d46ef545122e84a # 同意EULA +# - PRIVACY_AGREE=2402af06e133d2d10d9c6c643fdc9333 # 同意EULA ports: - "8000:8000" volumes: - ./mmc-data:/MaiMBot/data - - ./mmc-config/.env:/MaiMBot/.env - - ./mmc-config/bot_config.toml:/MaiMBot/config/bot_config.toml - - ./data/MaiMBot:/MaiMBot/data + - ./mmc-config/.env:/MaiMBot/.env # 持久化bot配置文件 + - ./mmc-config/bot_config.toml:/MaiMBot/config/bot_config.toml # 持久化bot配置文件 + - ./data/MaiMBot:/MaiMBot/data # NapCat 和 NoneBot 共享此卷,否则发送图片会有问题 restart: always networks: - maim_bot @@ -34,12 +34,14 @@ services: container_name: maim-bot-mongo environment: - TZ=Asia/Shanghai +# - MONGO_INITDB_ROOT_USERNAME=your_username # 此处配置mongo用户 +# - MONGO_INITDB_ROOT_PASSWORD=your_password # 此处配置mongo密码 ports: - "27017:27017" restart: always volumes: - - mongodb:/data/db - - mongodbCONFIG:/data/configdb + - mongodb:/data/db # 持久化mongodb数据 + - mongodbCONFIG:/data/configdb # 持久化mongodb配置文件 image: mongo:latest networks: - maim_bot @@ -54,9 +56,9 @@ services: - "6099:6099" - "8095:8095" volumes: - - ./napcat-config:/app/napcat/config - - ./data/qq:/app/.config/QQ - - ./data/MaiMBot:/MaiMBot/data + - ./napcat-config:/app/napcat/config # 持久化napcat配置文件 + - ./data/qq:/app/.config/QQ # 持久化QQ本体并同步qq表情和图片到adapters + - ./data/MaiMBot:/MaiMBot/data # NapCat 和 NoneBot 共享此卷,否则发送图片会有问题 container_name: maim-bot-napcat restart: always image: mlikiowa/napcat-docker:latest From b5a165aa58f8ea2cafb6828866934c0ce976406c Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 15:08:38 +0800 Subject: [PATCH 14/27] =?UTF-8?q?=E6=B5=8B=E8=AF=95docker=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-image.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index c06d967ca..03ef15468 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -5,6 +5,7 @@ on: branches: - main - main-fix + - refactor # 新增 refactor 分支触发 tags: - 'v*' workflow_dispatch: @@ -16,6 +17,10 @@ jobs: - name: Checkout code uses: actions/checkout@v4 + - name: Clone maim_message (refactor branch only) + if: github.ref == 'refs/heads/refactor' # 仅 refactor 分支执行 + run: git clone https://github.com/MaiM-with-u/maim_message maim_message + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -34,6 +39,8 @@ jobs: echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:main,${{ secrets.DOCKERHUB_USERNAME }}/maimbot:latest" >> $GITHUB_OUTPUT elif [ "${{ github.ref }}" == "refs/heads/main-fix" ]; then echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:main-fix" >> $GITHUB_OUTPUT + elif [ "${{ github.ref }}" == "refs/heads/refactor" ]; then # 新增 refactor 分支处理 + echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maim-core:refactor" >> $GITHUB_OUTPUT fi - name: Build and Push Docker Image @@ -44,5 +51,5 @@ jobs: platforms: linux/amd64,linux/arm64 tags: ${{ steps.tags.outputs.tags }} push: true - cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache - cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache,mode=max + cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maim-core:buildcache + cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maim-core:buildcache,mode=max \ No newline at end of file From 579dffb9c76965c48aa24037bba9e2853ce0ffa5 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 16:20:55 +0800 Subject: [PATCH 15/27] =?UTF-8?q?ci(docker):=20=E6=9B=B4=E6=96=B0=20Docker?= =?UTF-8?q?=E9=95=9C=E5=83=8F=E6=9E=84=E5=BB=BA=E5=92=8C=E6=8E=A8=E9=80=81?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 refactor 分支的 Docker镜像标签 - 更新缓存来源和目标的 Docker镜像名称 --- .github/workflows/docker-image.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 03ef15468..a5a6680cd 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -40,7 +40,7 @@ jobs: elif [ "${{ github.ref }}" == "refs/heads/main-fix" ]; then echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:main-fix" >> $GITHUB_OUTPUT elif [ "${{ github.ref }}" == "refs/heads/refactor" ]; then # 新增 refactor 分支处理 - echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maim-core:refactor" >> $GITHUB_OUTPUT + echo "tags=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:refactor" >> $GITHUB_OUTPUT fi - name: Build and Push Docker Image @@ -51,5 +51,5 @@ jobs: platforms: linux/amd64,linux/arm64 tags: ${{ steps.tags.outputs.tags }} push: true - cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maim-core:buildcache - cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maim-core:buildcache,mode=max \ No newline at end of file + cache-from: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache + cache-to: type=registry,ref=${{ secrets.DOCKERHUB_USERNAME }}/maimbot:buildcache,mode=max \ No newline at end of file From 93b980f333381c23d3b684fa963f84471681edff Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 16:27:26 +0800 Subject: [PATCH 16/27] =?UTF-8?q?build(docker):=20=E6=9B=B4=E6=96=B0=20Doc?= =?UTF-8?q?ker=20Compose=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 adapters 和 core 服务的镜像地址从本地修改为远程仓库地址- 移除 napcat 服务的多余端口映射 - 更新 adapters 和 core 服务的镜像版本 --- docker-compose.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 3ad94e067..9c5aa8916 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: adapters: container_name: maim-bot-adapters - image: maimbot-adapters:latest + image: sengokucola/maimbot:adapters environment: - TZ=Asia/Shanghai ports: @@ -15,7 +15,7 @@ services: - maim_bot core: container_name: maim-bot-core - image: maimbot-core:latest + image: sengokucola/maimbot:refactor environment: - TZ=Asia/Shanghai # - EULA_AGREE=35362b6ea30f12891d46ef545122e84a # 同意EULA @@ -51,9 +51,6 @@ services: - NAPCAT_GID=1000 - TZ=Asia/Shanghai ports: - - "3000:3000" - - "3001:3001" - - "6099:6099" - "8095:8095" volumes: - ./napcat-config:/app/napcat/config # 持久化napcat配置文件 From fd90a3ebbc09ab6c372b023389d35e89fc75899e Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 18:02:51 +0800 Subject: [PATCH 17/27] =?UTF-8?q?build(adapters):=20=E6=9B=B4=E6=96=B0=20D?= =?UTF-8?q?ocker=E9=95=9C=E5=83=8F=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 adapters 服务的 Docker 镜像从 sengokucola/maimbot:adapters 修改为 sengokucola/maimbot-adapter:adapter - 此更新统一了 Docker 镜像的命名格式,确保一致性和清晰性 --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 9c5aa8916..610791f9c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: adapters: container_name: maim-bot-adapters - image: sengokucola/maimbot:adapters + image: sengokucola/maimbot-adapter:adapter environment: - TZ=Asia/Shanghai ports: From 06cf9dbe2cd4d5a14cd5de6b9d47b4c414e18f38 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 1 Apr 2025 18:42:17 +0800 Subject: [PATCH 18/27] =?UTF-8?q?fix=EF=BC=9A=E5=87=8F=E5=B0=91=E8=B5=9B?= =?UTF-8?q?=E5=8D=9A=E6=9C=8B=E5=85=8B=E6=97=A5=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat/bot.py | 5 +++++ src/plugins/chat/utils.py | 1 + src/plugins/schedule/schedule_generator.py | 4 ++-- template/bot_config_template.toml | 2 +- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index ac6d4d2c9..0f28c81fe 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -89,6 +89,7 @@ class ChatBot: 5. 更新关系 6. 更新情绪 """ + timing_results = {} # 用于收集所有计时结果 response_set = None # 初始化response_set变量 @@ -97,6 +98,10 @@ class ChatBot: userinfo = message.message_info.user_info messageinfo = message.message_info + if groupinfo.group_id not in global_config.talk_allowed_groups: + return + + # 消息过滤,涉及到config有待更新 # 创建聊天流 diff --git a/src/plugins/chat/utils.py b/src/plugins/chat/utils.py index ecd67816a..c3c1e1fa8 100644 --- a/src/plugins/chat/utils.py +++ b/src/plugins/chat/utils.py @@ -370,6 +370,7 @@ def calculate_typing_time(input_string: str, chinese_time: float = 0.2, english_ total_time += chinese_time else: # 其他字符(如英文) total_time += english_time + return total_time + 0.3 # 加上回车时间 diff --git a/src/plugins/schedule/schedule_generator.py b/src/plugins/schedule/schedule_generator.py index a6a312624..ecc032761 100644 --- a/src/plugins/schedule/schedule_generator.py +++ b/src/plugins/schedule/schedule_generator.py @@ -126,7 +126,7 @@ class ScheduleGenerator: prompt += f"你昨天的日程是:{self.yesterday_schedule_text}\n" prompt += f"请为你生成{date_str}({weekday}),也就是今天的日程安排,结合你的个人特点和行为习惯以及昨天的安排\n" prompt += "推测你的日程安排,包括你一天都在做什么,从起床到睡眠,有什么发现和思考,具体一些,详细一些,需要1500字以上,精确到每半个小时,记得写明时间\n" # noqa: E501 - prompt += "直接返回你的日程,从起床到睡觉,不要输出其他内容:" + prompt += "直接返回你的日程,现实一点,不要浮夸,从起床到睡觉,不要输出其他内容:" return prompt def construct_doing_prompt(self, time: datetime.datetime, mind_thinking: str = ""): @@ -139,7 +139,7 @@ class ScheduleGenerator: prompt += f"你之前做了的事情是:{previous_doings},从之前到现在已经过去了{self.schedule_doing_update_interval / 60}分钟了\n" # noqa: E501 if mind_thinking: prompt += f"你脑子里在想:{mind_thinking}\n" - prompt += f"现在是{now_time},结合你的个人特点和行为习惯,注意关注你今天的日程安排和想法安排你接下来做什么," + prompt += f"现在是{now_time},结合你的个人特点和行为习惯,注意关注你今天的日程安排和想法安排你接下来做什么,现实一点,不要浮夸" prompt += "安排你接下来做什么,具体一些,详细一些\n" prompt += "直接返回你在做的事情,注意是当前时间,不要输出其他内容:" return prompt diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 5a13710e5..959d96da8 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -47,7 +47,7 @@ personality_3_probability = 0.1 # 第三种人格出现概率,请确保三个 enable_schedule_gen = true # 是否启用日程表(尚未完成) prompt_schedule_gen = "用几句话描述描述性格特点或行动规律,这个特征会用来生成日程表" schedule_doing_update_interval = 900 # 日程表更新间隔 单位秒 -schedule_temperature = 0.5 # 日程表温度,建议0.5-1.0 +schedule_temperature = 0.3 # 日程表温度,建议0.3-0.6 [platforms] # 必填项目,填写每个平台适配器提供的链接 nonebot-qq="http://127.0.0.1:18002/api/message" From 852ef8e56d4e98aca6bf8d8a070d8670a3e61654 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 19:43:21 +0800 Subject: [PATCH 19/27] =?UTF-8?q?build(adapters):=20=E6=9B=B4=E6=96=B0=20D?= =?UTF-8?q?ocker=E9=95=9C=E5=83=8F=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 adapters 服务的 Docker 镜像从 sengokucola/maimbot:adapters 修改为 sengokucola/maimbot-adapter:adapter - 此更新统一了 Docker 镜像的命名格式,确保一致性和清晰性 --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 610791f9c..1db925c82 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: adapters: container_name: maim-bot-adapters - image: sengokucola/maimbot-adapter:adapter + image: sengokucola/maimbot-adapter:latest environment: - TZ=Asia/Shanghai ports: From 6a76d14c7de3d7606deb43bec3a6f8db470aa9f9 Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 20:12:09 +0800 Subject: [PATCH 20/27] =?UTF-8?q?build(Dockerfile):=20=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=20pip=20=E4=BB=A5=E7=A1=AE=E4=BF=9D=E5=AE=89=E5=85=A8=E6=80=A7?= =?UTF-8?q?=E5=92=8C=E6=80=A7=E8=83=BD-=20=E5=9C=A8=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E4=B9=8B=E5=89=8D=EF=BC=8C=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=20"pip=20install=20--upgrade=20pip"=E6=9D=A5?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=20pip=20-=20=E8=BF=99=E6=A0=B7=E5=8F=AF?= =?UTF-8?q?=E4=BB=A5=E7=A1=AE=E4=BF=9D=E4=BD=BF=E7=94=A8=E6=9C=80=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E7=9A=84=20pip=EF=BC=8C=E5=87=8F=E5=B0=91?= =?UTF-8?q?=E6=BD=9C=E5=9C=A8=E7=9A=84=E5=AE=89=E5=85=A8=E6=BC=8F=E6=B4=9E?= =?UTF-8?q?=E5=92=8C=E6=80=A7=E8=83=BD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index 483892006..6c6041ff3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,7 @@ COPY requirements.txt . COPY maim_message /maim_message # 安装依赖 +RUN pip install --upgrade pip RUN pip install -e /maim_message RUN pip install --upgrade -r requirements.txt From 5c06933b1e37eb45534c4bf17880dc199daad84e Mon Sep 17 00:00:00 2001 From: infinitycat Date: Tue, 1 Apr 2025 20:38:05 +0800 Subject: [PATCH 21/27] =?UTF-8?q?=E5=88=A0=E5=A4=9A=E4=BA=86,=E9=A1=BA?= =?UTF-8?q?=E4=BE=BF=E4=BC=98=E5=8C=96=E4=B8=80=E4=B8=8B=EF=BC=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index 1db925c82..7b4fcd2d3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,6 +11,8 @@ services: - ./adapters/.env:/adapters/.env # 持久化adapters配置文件 - ./data/qq:/app/.config/QQ # 持久化QQ本体并同步qq表情和图片到adapters restart: always + depends_on: + - mongodb networks: - maim_bot core: @@ -28,6 +30,8 @@ services: - ./mmc-config/bot_config.toml:/MaiMBot/config/bot_config.toml # 持久化bot配置文件 - ./data/MaiMBot:/MaiMBot/data # NapCat 和 NoneBot 共享此卷,否则发送图片会有问题 restart: always + depends_on: + - mongodb networks: - maim_bot mongodb: @@ -51,6 +55,7 @@ services: - NAPCAT_GID=1000 - TZ=Asia/Shanghai ports: + - "6099:6099" - "8095:8095" volumes: - ./napcat-config:/app/napcat/config # 持久化napcat配置文件 From 61c962643e44b36a50ee8e239395abb60cee6750 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 1 Apr 2025 21:48:50 +0800 Subject: [PATCH 22/27] =?UTF-8?q?fix=EF=BC=9A=E4=BF=AE=E6=94=B9=E4=BA=86?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelogs/changelog_dev.md | 2 ++ src/main.py | 4 ++-- src/plugins/__init__.py | 2 +- src/plugins/chat/__init__.py | 4 ++-- src/plugins/chat/bot.py | 4 ++-- src/plugins/chat/message_sender.py | 2 +- src/plugins/{chat => relationship}/relationship_manager.py | 2 +- src/plugins/{chat => storage}/storage.py | 4 ++-- src/plugins/{chat => topic_identify}/topic_identifier.py | 0 9 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 changelogs/changelog_dev.md rename src/plugins/{chat => relationship}/relationship_manager.py (99%) rename src/plugins/{chat => storage}/storage.py (95%) rename src/plugins/{chat => topic_identify}/topic_identifier.py (100%) diff --git a/changelogs/changelog_dev.md b/changelogs/changelog_dev.md new file mode 100644 index 000000000..07db37e88 --- /dev/null +++ b/changelogs/changelog_dev.md @@ -0,0 +1,2 @@ +这里放置了测试版本的细节更新 + diff --git a/src/main.py b/src/main.py index 621014ae6..fc0a757e5 100644 --- a/src/main.py +++ b/src/main.py @@ -4,13 +4,13 @@ from .plugins.utils.statistic import LLMStatistics from .plugins.moods.moods import MoodManager from .plugins.schedule.schedule_generator import bot_schedule from .plugins.chat.emoji_manager import emoji_manager -from .plugins.chat.relationship_manager import relationship_manager +from .plugins.relationship.relationship_manager import relationship_manager from .plugins.willing.willing_manager import willing_manager from .plugins.chat.chat_stream import chat_manager from .heart_flow.heartflow import heartflow from .plugins.memory_system.Hippocampus import HippocampusManager from .plugins.chat.message_sender import message_manager -from .plugins.chat.storage import MessageStorage +from .plugins.storage.storage import MessageStorage from .plugins.config.config import global_config from .plugins.chat.bot import chat_bot from .common.logger import get_module_logger diff --git a/src/plugins/__init__.py b/src/plugins/__init__.py index e86da9f0f..186245417 100644 --- a/src/plugins/__init__.py +++ b/src/plugins/__init__.py @@ -5,7 +5,7 @@ MaiMBot插件系统 from .chat.chat_stream import chat_manager from .chat.emoji_manager import emoji_manager -from .chat.relationship_manager import relationship_manager +from .relationship.relationship_manager import relationship_manager from .moods.moods import MoodManager from .willing.willing_manager import willing_manager from .schedule.schedule_generator import bot_schedule diff --git a/src/plugins/chat/__init__.py b/src/plugins/chat/__init__.py index cace85253..0f4dada44 100644 --- a/src/plugins/chat/__init__.py +++ b/src/plugins/chat/__init__.py @@ -1,8 +1,8 @@ from .emoji_manager import emoji_manager -from .relationship_manager import relationship_manager +from ..relationship.relationship_manager import relationship_manager from .chat_stream import chat_manager from .message_sender import message_manager -from .storage import MessageStorage +from ..storage.storage import MessageStorage from .auto_speak import auto_speak_manager diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index 0f28c81fe..9d97daec8 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -12,8 +12,8 @@ from .message import MessageSending, MessageRecv, MessageThinking, MessageSet from .chat_stream import chat_manager from .message_sender import message_manager # 导入新的消息管理器 -from .relationship_manager import relationship_manager -from .storage import MessageStorage +from ..relationship.relationship_manager import relationship_manager +from ..storage.storage import MessageStorage from .utils import is_mentioned_bot_in_message, get_recent_group_detailed_plain_text from .utils_image import image_path_to_base64 from ..willing.willing_manager import willing_manager # 导入意愿管理器 diff --git a/src/plugins/chat/message_sender.py b/src/plugins/chat/message_sender.py index 378ee6864..a12f7320b 100644 --- a/src/plugins/chat/message_sender.py +++ b/src/plugins/chat/message_sender.py @@ -7,7 +7,7 @@ from ...common.database import db from ..message.api import global_api from .message import MessageSending, MessageThinking, MessageSet -from .storage import MessageStorage +from ..storage.storage import MessageStorage from ..config.config import global_config from .utils import truncate_message, calculate_typing_time diff --git a/src/plugins/chat/relationship_manager.py b/src/plugins/relationship/relationship_manager.py similarity index 99% rename from src/plugins/chat/relationship_manager.py rename to src/plugins/relationship/relationship_manager.py index 9221817c3..f8a850cab 100644 --- a/src/plugins/chat/relationship_manager.py +++ b/src/plugins/relationship/relationship_manager.py @@ -4,7 +4,7 @@ from src.common.logger import get_module_logger, LogConfig, RELATION_STYLE_CONFI from ...common.database import db from ..message.message_base import UserInfo -from .chat_stream import ChatStream +from ..chat.chat_stream import ChatStream import math from bson.decimal128 import Decimal128 diff --git a/src/plugins/chat/storage.py b/src/plugins/storage/storage.py similarity index 95% rename from src/plugins/chat/storage.py rename to src/plugins/storage/storage.py index 7ff247b25..c35f55be5 100644 --- a/src/plugins/chat/storage.py +++ b/src/plugins/storage/storage.py @@ -1,8 +1,8 @@ from typing import Union from ...common.database import db -from .message import MessageSending, MessageRecv -from .chat_stream import ChatStream +from ..chat.message import MessageSending, MessageRecv +from ..chat.chat_stream import ChatStream from src.common.logger import get_module_logger logger = get_module_logger("message_storage") diff --git a/src/plugins/chat/topic_identifier.py b/src/plugins/topic_identify/topic_identifier.py similarity index 100% rename from src/plugins/chat/topic_identifier.py rename to src/plugins/topic_identify/topic_identifier.py From 02710a77ef76f657277432d5e790f6cd6f3e3816 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 1 Apr 2025 22:59:35 +0800 Subject: [PATCH 23/27] =?UTF-8?q?feat=EF=BC=9A=E7=8E=B0=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E4=B8=A4=E7=A7=8D=E7=8B=AC=E7=AB=8B=E7=9A=84=E5=9B=9E=E5=A4=8D?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=EF=BC=8C=E6=8E=A8=E7=90=86=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E5=92=8C=E5=BF=83=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- changelogs/changelog_dev.md | 3 + src/plugins/chat/auto_speak.py | 2 +- src/plugins/chat/bot.py | 347 ++---------------- .../reasoning_chat/reasoning_chat.py | 260 +++++++++++++ .../reasoning_chat/reasoning_generator.py} | 72 +--- .../reasoning_prompt_builder.py | 213 +++++++++++ .../think_flow_chat/think_flow_chat.py | 297 +++++++++++++++ .../think_flow_chat/think_flow_generator.py | 181 +++++++++ .../think_flow_prompt_builder.py} | 99 +---- src/plugins/config/config.py | 24 +- template/bot_config_template.toml | 15 +- 11 files changed, 1030 insertions(+), 483 deletions(-) create mode 100644 src/plugins/chat_module/reasoning_chat/reasoning_chat.py rename src/plugins/{chat/llm_generator.py => chat_module/reasoning_chat/reasoning_generator.py} (72%) create mode 100644 src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py create mode 100644 src/plugins/chat_module/think_flow_chat/think_flow_chat.py create mode 100644 src/plugins/chat_module/think_flow_chat/think_flow_generator.py rename src/plugins/{chat/prompt_builder.py => chat_module/think_flow_chat/think_flow_prompt_builder.py} (68%) diff --git a/changelogs/changelog_dev.md b/changelogs/changelog_dev.md index 07db37e88..c88422815 100644 --- a/changelogs/changelog_dev.md +++ b/changelogs/changelog_dev.md @@ -1,2 +1,5 @@ 这里放置了测试版本的细节更新 +## [0.6.0-mmc-4] - 2025-4-1 +- 提供两种聊天逻辑,思维流聊天(ThinkFlowChat 和 推理聊天(ReasoningChat) +- 从结构上可支持多种回复消息逻辑 \ No newline at end of file diff --git a/src/plugins/chat/auto_speak.py b/src/plugins/chat/auto_speak.py index 29054ed9a..62a5a20a5 100644 --- a/src/plugins/chat/auto_speak.py +++ b/src/plugins/chat/auto_speak.py @@ -8,7 +8,7 @@ from .message import MessageSending, MessageThinking, MessageSet, MessageRecv from ..message.message_base import UserInfo, Seg from .message_sender import message_manager from ..moods.moods import MoodManager -from .llm_generator import ResponseGenerator +from ..chat_module.reasoning_chat.reasoning_generator import ResponseGenerator from src.common.logger import get_module_logger from src.heart_flow.heartflow import heartflow from ...common.database import db diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index 9d97daec8..e1049829e 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -6,14 +6,14 @@ from ..memory_system.Hippocampus import HippocampusManager from ..moods.moods import MoodManager # 导入情绪管理器 from ..config.config import global_config from .emoji_manager import emoji_manager # 导入表情包管理器 -from .llm_generator import ResponseGenerator +from ..chat_module.reasoning_chat.reasoning_generator import ResponseGenerator from .message import MessageSending, MessageRecv, MessageThinking, MessageSet from .chat_stream import chat_manager from .message_sender import message_manager # 导入新的消息管理器 from ..relationship.relationship_manager import relationship_manager -from ..storage.storage import MessageStorage +from ..storage.storage import MessageStorage # 修改导入路径 from .utils import is_mentioned_bot_in_message, get_recent_group_detailed_plain_text from .utils_image import image_path_to_base64 from ..willing.willing_manager import willing_manager # 导入意愿管理器 @@ -21,6 +21,8 @@ from ..message import UserInfo, Seg from src.heart_flow.heartflow import heartflow from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig +from ..chat_module.think_flow_chat.think_flow_chat import ThinkFlowChat +from ..chat_module.reasoning_chat.reasoning_chat import ReasoningChat # 定义日志配置 chat_config = LogConfig( @@ -41,333 +43,42 @@ class ChatBot: self._started = False self.mood_manager = MoodManager.get_instance() # 获取情绪管理器单例 self.mood_manager.start_mood_update() # 启动情绪更新 + self.think_flow_chat = ThinkFlowChat() + self.reasoning_chat = ReasoningChat() async def _ensure_started(self): """确保所有任务已启动""" if not self._started: self._started = True - async def _create_thinking_message(self, message, chat, userinfo, messageinfo): - """创建思考消息 - - Args: - message: 接收到的消息 - chat: 聊天流对象 - userinfo: 用户信息对象 - messageinfo: 消息信息对象 - - Returns: - str: thinking_id - """ - bot_user_info = UserInfo( - user_id=global_config.BOT_QQ, - user_nickname=global_config.BOT_NICKNAME, - platform=messageinfo.platform, - ) - - thinking_time_point = round(time.time(), 2) - thinking_id = "mt" + str(thinking_time_point) - thinking_message = MessageThinking( - message_id=thinking_id, - chat_stream=chat, - bot_user_info=bot_user_info, - reply=message, - thinking_start_time=thinking_time_point, - ) - - message_manager.add_message(thinking_message) - willing_manager.change_reply_willing_sent(chat) - - return thinking_id - async def message_process(self, message_data: str) -> None: """处理转化后的统一格式消息 - 1. 过滤消息 - 2. 记忆激活 - 3. 意愿激活 - 4. 生成回复并发送 - 5. 更新关系 - 6. 更新情绪 + 根据global_config.response_mode选择不同的回复模式: + 1. heart_flow模式:使用思维流系统进行回复 + - 包含思维流状态管理 + - 在回复前进行观察和状态更新 + - 回复后更新思维流状态 + + 2. reasoning模式:使用推理系统进行回复 + - 直接使用意愿管理器计算回复概率 + - 没有思维流相关的状态管理 + - 更简单直接的回复逻辑 + + 两种模式都包含: + - 消息过滤 + - 记忆激活 + - 意愿计算 + - 消息生成和发送 + - 表情包处理 + - 性能计时 """ - - timing_results = {} # 用于收集所有计时结果 - response_set = None # 初始化response_set变量 - message = MessageRecv(message_data) - groupinfo = message.message_info.group_info - userinfo = message.message_info.user_info - messageinfo = message.message_info - - if groupinfo.group_id not in global_config.talk_allowed_groups: - return - - - # 消息过滤,涉及到config有待更新 - - # 创建聊天流 - chat = await chat_manager.get_or_create_stream( - platform=messageinfo.platform, - user_info=userinfo, - group_info=groupinfo, - ) - message.update_chat_stream(chat) - - # 创建 心流与chat的观察 - heartflow.create_subheartflow(chat.stream_id) - - await message.process() - - # 过滤词/正则表达式过滤 - if self._check_ban_words(message.processed_plain_text, chat, userinfo) or self._check_ban_regex( - message.raw_message, chat, userinfo - ): - return - - await self.storage.store_message(message, chat) - - timer1 = time.time() - interested_rate = 0 - interested_rate = await HippocampusManager.get_instance().get_activate_from_text( - message.processed_plain_text, fast_retrieval=True - ) - timer2 = time.time() - timing_results["记忆激活"] = timer2 - timer1 - - is_mentioned = is_mentioned_bot_in_message(message) - - if global_config.enable_think_flow: - current_willing_old = willing_manager.get_willing(chat_stream=chat) - current_willing_new = (heartflow.get_subheartflow(chat.stream_id).current_state.willing - 5) / 4 - print(f"旧回复意愿:{current_willing_old},新回复意愿:{current_willing_new}") - current_willing = (current_willing_old + current_willing_new) / 2 + if global_config.response_mode == "heart_flow": + await self.think_flow_chat.process_message(message_data) + elif global_config.response_mode == "reasoning": + await self.reasoning_chat.process_message(message_data) else: - current_willing = willing_manager.get_willing(chat_stream=chat) - - willing_manager.set_willing(chat.stream_id, current_willing) - - timer1 = time.time() - reply_probability = await willing_manager.change_reply_willing_received( - chat_stream=chat, - is_mentioned_bot=is_mentioned, - config=global_config, - is_emoji=message.is_emoji, - interested_rate=interested_rate, - sender_id=str(message.message_info.user_info.user_id), - ) - timer2 = time.time() - timing_results["意愿激活"] = timer2 - timer1 - - # 神秘的消息流数据结构处理 - if chat.group_info: - mes_name = chat.group_info.group_name - else: - mes_name = "私聊" - - # 打印收到的信息的信息 - current_time = time.strftime("%H:%M:%S", time.localtime(messageinfo.time)) - logger.info( - f"[{current_time}][{mes_name}]" - f"{chat.user_info.user_nickname}:" - f"{message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]" - ) - - if message.message_info.additional_config: - if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): - reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] - - do_reply = False - # 开始组织语言 - if random() < reply_probability: - do_reply = True - - timer1 = time.time() - thinking_id = await self._create_thinking_message(message, chat, userinfo, messageinfo) - timer2 = time.time() - timing_results["创建思考消息"] = timer2 - timer1 - - timer1 = time.time() - await heartflow.get_subheartflow(chat.stream_id).do_observe() - timer2 = time.time() - timing_results["观察"] = timer2 - timer1 - - timer1 = time.time() - await heartflow.get_subheartflow(chat.stream_id).do_thinking_before_reply(message.processed_plain_text) - timer2 = time.time() - timing_results["思考前脑内状态"] = timer2 - timer1 - - timer1 = time.time() - response_set = await self.gpt.generate_response(message) - timer2 = time.time() - timing_results["生成回复"] = timer2 - timer1 - - if not response_set: - logger.info("为什么生成回复失败?") - return - - # 发送消息 - timer1 = time.time() - await self._send_response_messages(message, chat, response_set, thinking_id) - timer2 = time.time() - timing_results["发送消息"] = timer2 - timer1 - - # 处理表情包 - timer1 = time.time() - await self._handle_emoji(message, chat, response_set) - timer2 = time.time() - timing_results["处理表情包"] = timer2 - timer1 - - timer1 = time.time() - await self._update_using_response(message, response_set) - timer2 = time.time() - timing_results["更新心流"] = timer2 - timer1 - - # 在最后统一输出所有计时结果 - if do_reply: - timing_str = " | ".join([f"{step}: {duration:.2f}秒" for step, duration in timing_results.items()]) - trigger_msg = message.processed_plain_text - response_msg = " ".join(response_set) if response_set else "无回复" - logger.info(f"触发消息: {trigger_msg[:20]}... | 生成消息: {response_msg[:20]}... | 性能计时: {timing_str}") - - async def _update_using_response(self, message, response_set): - # 更新心流状态 - stream_id = message.chat_stream.stream_id - chat_talking_prompt = "" - if stream_id: - chat_talking_prompt = get_recent_group_detailed_plain_text( - stream_id, limit=global_config.MAX_CONTEXT_SIZE, combine=True - ) - - await heartflow.get_subheartflow(stream_id).do_thinking_after_reply(response_set, chat_talking_prompt) - - async def _send_response_messages(self, message, chat, response_set, thinking_id): - container = message_manager.get_container(chat.stream_id) - thinking_message = None - - # logger.info(f"开始发送消息准备") - for msg in container.messages: - if isinstance(msg, MessageThinking) and msg.message_info.message_id == thinking_id: - thinking_message = msg - container.messages.remove(msg) - break - - if not thinking_message: - logger.warning("未找到对应的思考消息,可能已超时被移除") - return - - # logger.info(f"开始发送消息") - thinking_start_time = thinking_message.thinking_start_time - message_set = MessageSet(chat, thinking_id) - - mark_head = False - for msg in response_set: - message_segment = Seg(type="text", data=msg) - bot_message = MessageSending( - message_id=thinking_id, - chat_stream=chat, - bot_user_info=UserInfo( - user_id=global_config.BOT_QQ, - user_nickname=global_config.BOT_NICKNAME, - platform=message.message_info.platform, - ), - sender_info=message.message_info.user_info, - message_segment=message_segment, - reply=message, - is_head=not mark_head, - is_emoji=False, - thinking_start_time=thinking_start_time, - ) - if not mark_head: - mark_head = True - message_set.add_message(bot_message) - # logger.info(f"开始添加发送消息") - message_manager.add_message(message_set) - - async def _handle_emoji(self, message, chat, response): - """处理表情包 - - Args: - message: 接收到的消息 - chat: 聊天流对象 - response: 生成的回复 - """ - if random() < global_config.emoji_chance: - emoji_raw = await emoji_manager.get_emoji_for_text(response) - if emoji_raw: - emoji_path, description = emoji_raw - emoji_cq = image_path_to_base64(emoji_path) - - thinking_time_point = round(message.message_info.time, 2) - - message_segment = Seg(type="emoji", data=emoji_cq) - bot_message = MessageSending( - message_id="mt" + str(thinking_time_point), - chat_stream=chat, - bot_user_info=UserInfo( - user_id=global_config.BOT_QQ, - user_nickname=global_config.BOT_NICKNAME, - platform=message.message_info.platform, - ), - sender_info=message.message_info.user_info, - message_segment=message_segment, - reply=message, - is_head=False, - is_emoji=True, - ) - message_manager.add_message(bot_message) - - async def _update_emotion_and_relationship(self, message, chat, response, raw_content): - """更新情绪和关系 - - Args: - message: 接收到的消息 - chat: 聊天流对象 - response: 生成的回复 - raw_content: 原始内容 - """ - stance, emotion = await self.gpt._get_emotion_tags(raw_content, message.processed_plain_text) - logger.debug(f"为 '{response}' 立场为:{stance} 获取到的情感标签为:{emotion}") - await relationship_manager.calculate_update_relationship_value(chat_stream=chat, label=emotion, stance=stance) - self.mood_manager.update_mood_from_emotion(emotion, global_config.mood_intensity_factor) - - def _check_ban_words(self, text: str, chat, userinfo) -> bool: - """检查消息中是否包含过滤词 - - Args: - text: 要检查的文本 - chat: 聊天流对象 - userinfo: 用户信息对象 - - Returns: - bool: 如果包含过滤词返回True,否则返回False - """ - for word in global_config.ban_words: - if word in text: - logger.info( - f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}" - ) - logger.info(f"[过滤词识别]消息中含有{word},filtered") - return True - return False - - def _check_ban_regex(self, text: str, chat, userinfo) -> bool: - """检查消息是否匹配过滤正则表达式 - - Args: - text: 要检查的文本 - chat: 聊天流对象 - userinfo: 用户信息对象 - - Returns: - bool: 如果匹配过滤正则返回True,否则返回False - """ - for pattern in global_config.ban_msgs_regex: - if re.search(pattern, text): - logger.info( - f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}" - ) - logger.info(f"[正则表达式过滤]消息匹配到{pattern},filtered") - return True - return False + logger.error(f"未知的回复模式,请检查配置文件!!: {global_config.response_mode}") # 创建全局ChatBot实例 diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py new file mode 100644 index 000000000..600ba4f06 --- /dev/null +++ b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py @@ -0,0 +1,260 @@ +import time +from random import random +import re + +from ...memory_system.Hippocampus import HippocampusManager +from ...moods.moods import MoodManager +from ...config.config import global_config +from ...chat.emoji_manager import emoji_manager +from .reasoning_generator import ResponseGenerator +from ...chat.message import MessageSending, MessageRecv, MessageThinking, MessageSet +from ...chat.message_sender import message_manager +from ...relationship.relationship_manager import relationship_manager +from ...storage.storage import MessageStorage +from ...chat.utils import is_mentioned_bot_in_message, get_recent_group_detailed_plain_text +from ...chat.utils_image import image_path_to_base64 +from ...willing.willing_manager import willing_manager +from ...message import UserInfo, Seg +from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig +from ...chat.chat_stream import chat_manager + +# 定义日志配置 +chat_config = LogConfig( + console_format=CHAT_STYLE_CONFIG["console_format"], + file_format=CHAT_STYLE_CONFIG["file_format"], +) + +logger = get_module_logger("reasoning_chat", config=chat_config) + +class ReasoningChat: + def __init__(self): + self.storage = MessageStorage() + self.gpt = ResponseGenerator() + self.mood_manager = MoodManager.get_instance() + self.mood_manager.start_mood_update() + + async def _create_thinking_message(self, message, chat, userinfo, messageinfo): + """创建思考消息""" + bot_user_info = UserInfo( + user_id=global_config.BOT_QQ, + user_nickname=global_config.BOT_NICKNAME, + platform=messageinfo.platform, + ) + + thinking_time_point = round(time.time(), 2) + thinking_id = "mt" + str(thinking_time_point) + thinking_message = MessageThinking( + message_id=thinking_id, + chat_stream=chat, + bot_user_info=bot_user_info, + reply=message, + thinking_start_time=thinking_time_point, + ) + + message_manager.add_message(thinking_message) + willing_manager.change_reply_willing_sent(chat) + + return thinking_id + + async def _send_response_messages(self, message, chat, response_set, thinking_id): + """发送回复消息""" + container = message_manager.get_container(chat.stream_id) + thinking_message = None + + for msg in container.messages: + if isinstance(msg, MessageThinking) and msg.message_info.message_id == thinking_id: + thinking_message = msg + container.messages.remove(msg) + break + + if not thinking_message: + logger.warning("未找到对应的思考消息,可能已超时被移除") + return + + thinking_start_time = thinking_message.thinking_start_time + message_set = MessageSet(chat, thinking_id) + + mark_head = False + for msg in response_set: + message_segment = Seg(type="text", data=msg) + bot_message = MessageSending( + message_id=thinking_id, + chat_stream=chat, + bot_user_info=UserInfo( + user_id=global_config.BOT_QQ, + user_nickname=global_config.BOT_NICKNAME, + platform=message.message_info.platform, + ), + sender_info=message.message_info.user_info, + message_segment=message_segment, + reply=message, + is_head=not mark_head, + is_emoji=False, + thinking_start_time=thinking_start_time, + ) + if not mark_head: + mark_head = True + message_set.add_message(bot_message) + message_manager.add_message(message_set) + + async def _handle_emoji(self, message, chat, response): + """处理表情包""" + if random() < global_config.emoji_chance: + emoji_raw = await emoji_manager.get_emoji_for_text(response) + if emoji_raw: + emoji_path, description = emoji_raw + emoji_cq = image_path_to_base64(emoji_path) + + thinking_time_point = round(message.message_info.time, 2) + + message_segment = Seg(type="emoji", data=emoji_cq) + bot_message = MessageSending( + message_id="mt" + str(thinking_time_point), + chat_stream=chat, + bot_user_info=UserInfo( + user_id=global_config.BOT_QQ, + user_nickname=global_config.BOT_NICKNAME, + platform=message.message_info.platform, + ), + sender_info=message.message_info.user_info, + message_segment=message_segment, + reply=message, + is_head=False, + is_emoji=True, + ) + message_manager.add_message(bot_message) + + async def process_message(self, message_data: str) -> None: + """处理消息并生成回复""" + timing_results = {} + response_set = None + + message = MessageRecv(message_data) + groupinfo = message.message_info.group_info + userinfo = message.message_info.user_info + messageinfo = message.message_info + + + if groupinfo.group_id not in global_config.talk_allowed_groups: + return + + # logger.info("使用推理聊天模式") + + # 创建聊天流 + chat = await chat_manager.get_or_create_stream( + platform=messageinfo.platform, + user_info=userinfo, + group_info=groupinfo, + ) + message.update_chat_stream(chat) + + await message.process() + + # 过滤词/正则表达式过滤 + if self._check_ban_words(message.processed_plain_text, chat, userinfo) or self._check_ban_regex( + message.raw_message, chat, userinfo + ): + return + + await self.storage.store_message(message, chat) + + # 记忆激活 + timer1 = time.time() + interested_rate = await HippocampusManager.get_instance().get_activate_from_text( + message.processed_plain_text, fast_retrieval=True + ) + timer2 = time.time() + timing_results["记忆激活"] = timer2 - timer1 + + is_mentioned = is_mentioned_bot_in_message(message) + + # 计算回复意愿 + current_willing = willing_manager.get_willing(chat_stream=chat) + willing_manager.set_willing(chat.stream_id, current_willing) + + # 意愿激活 + timer1 = time.time() + reply_probability = await willing_manager.change_reply_willing_received( + chat_stream=chat, + is_mentioned_bot=is_mentioned, + config=global_config, + is_emoji=message.is_emoji, + interested_rate=interested_rate, + sender_id=str(message.message_info.user_info.user_id), + ) + timer2 = time.time() + timing_results["意愿激活"] = timer2 - timer1 + + # 打印消息信息 + mes_name = chat.group_info.group_name if chat.group_info else "私聊" + current_time = time.strftime("%H:%M:%S", time.localtime(messageinfo.time)) + logger.info( + f"[{current_time}][{mes_name}]" + f"{chat.user_info.user_nickname}:" + f"{message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]" + ) + + if message.message_info.additional_config: + if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): + reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] + + do_reply = False + if random() < reply_probability: + do_reply = True + + # 创建思考消息 + timer1 = time.time() + thinking_id = await self._create_thinking_message(message, chat, userinfo, messageinfo) + timer2 = time.time() + timing_results["创建思考消息"] = timer2 - timer1 + + # 生成回复 + timer1 = time.time() + response_set = await self.gpt.generate_response(message) + timer2 = time.time() + timing_results["生成回复"] = timer2 - timer1 + + if not response_set: + logger.info("为什么生成回复失败?") + return + + # 发送消息 + timer1 = time.time() + await self._send_response_messages(message, chat, response_set, thinking_id) + timer2 = time.time() + timing_results["发送消息"] = timer2 - timer1 + + # 处理表情包 + timer1 = time.time() + await self._handle_emoji(message, chat, response_set) + timer2 = time.time() + timing_results["处理表情包"] = timer2 - timer1 + + # 输出性能计时结果 + if do_reply: + timing_str = " | ".join([f"{step}: {duration:.2f}秒" for step, duration in timing_results.items()]) + trigger_msg = message.processed_plain_text + response_msg = " ".join(response_set) if response_set else "无回复" + logger.info(f"触发消息: {trigger_msg[:20]}... | 推理消息: {response_msg[:20]}... | 性能计时: {timing_str}") + + def _check_ban_words(self, text: str, chat, userinfo) -> bool: + """检查消息中是否包含过滤词""" + for word in global_config.ban_words: + if word in text: + logger.info( + f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}" + ) + logger.info(f"[过滤词识别]消息中含有{word},filtered") + return True + return False + + def _check_ban_regex(self, text: str, chat, userinfo) -> bool: + """检查消息是否匹配过滤正则表达式""" + for pattern in global_config.ban_msgs_regex: + if re.search(pattern, text): + logger.info( + f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}" + ) + logger.info(f"[正则表达式过滤]消息匹配到{pattern},filtered") + return True + return False diff --git a/src/plugins/chat/llm_generator.py b/src/plugins/chat_module/reasoning_chat/reasoning_generator.py similarity index 72% rename from src/plugins/chat/llm_generator.py rename to src/plugins/chat_module/reasoning_chat/reasoning_generator.py index b0c9a59e2..787b8b229 100644 --- a/src/plugins/chat/llm_generator.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_generator.py @@ -1,13 +1,13 @@ import time from typing import List, Optional, Tuple, Union +import random - -from ...common.database import db -from ..models.utils_model import LLM_request -from ..config.config import global_config -from .message import MessageRecv, MessageThinking, Message -from .prompt_builder import prompt_builder -from .utils import process_llm_response +from ....common.database import db +from ...models.utils_model import LLM_request +from ...config.config import global_config +from ...chat.message import MessageRecv, MessageThinking, Message +from .reasoning_prompt_builder import prompt_builder +from ...chat.utils import process_llm_response from src.common.logger import get_module_logger, LogConfig, LLM_STYLE_CONFIG # 定义日志配置 @@ -40,24 +40,19 @@ class ResponseGenerator: async def generate_response(self, message: MessageThinking) -> Optional[Union[str, List[str]]]: """根据当前模型类型选择对应的生成函数""" - # 从global_config中获取模型概率值并选择模型 - # if random.random() < global_config.MODEL_R1_PROBABILITY: - # self.current_model_type = "深深地" - # current_model = self.model_reasoning - # else: - # self.current_model_type = "浅浅的" - # current_model = self.model_normal + #从global_config中获取模型概率值并选择模型 + if random.random() < global_config.MODEL_R1_PROBABILITY: + self.current_model_type = "深深地" + current_model = self.model_reasoning + else: + self.current_model_type = "浅浅的" + current_model = self.model_normal - # logger.info( - # f"{self.current_model_type}思考:{message.processed_plain_text[:30] + '...' if len(message.processed_plain_text) > 30 else message.processed_plain_text}" - # ) # noqa: E501 - - logger.info( - f"思考:{message.processed_plain_text[:30] + '...' if len(message.processed_plain_text) > 30 else message.processed_plain_text}" - ) + f"{self.current_model_type}思考:{message.processed_plain_text[:30] + '...' if len(message.processed_plain_text) > 30 else message.processed_plain_text}" + ) # noqa: E501 + - current_model = self.model_normal model_response = await self._generate_response_with_model(message, current_model) # print(f"raw_content: {model_response}") @@ -194,35 +189,4 @@ class ResponseGenerator: # print(f"得到了处理后的llm返回{processed_response}") - return processed_response - - -class InitiativeMessageGenerate: - def __init__(self): - self.model_r1 = LLM_request(model=global_config.llm_reasoning, temperature=0.7) - self.model_v3 = LLM_request(model=global_config.llm_normal, temperature=0.7) - self.model_r1_distill = LLM_request(model=global_config.llm_reasoning_minor, temperature=0.7) - - def gen_response(self, message: Message): - topic_select_prompt, dots_for_select, prompt_template = prompt_builder._build_initiative_prompt_select( - message.group_id - ) - content_select, reasoning, _ = self.model_v3.generate_response(topic_select_prompt) - logger.debug(f"{content_select} {reasoning}") - topics_list = [dot[0] for dot in dots_for_select] - if content_select: - if content_select in topics_list: - select_dot = dots_for_select[topics_list.index(content_select)] - else: - return None - else: - return None - prompt_check, memory = prompt_builder._build_initiative_prompt_check(select_dot[1], prompt_template) - content_check, reasoning_check, _ = self.model_v3.generate_response(prompt_check) - logger.info(f"{content_check} {reasoning_check}") - if "yes" not in content_check.lower(): - return None - prompt = prompt_builder._build_initiative_prompt(select_dot, prompt_template, memory) - content, reasoning = self.model_r1.generate_response_async(prompt) - logger.debug(f"[DEBUG] {content} {reasoning}") - return content + return processed_response \ No newline at end of file diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py b/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py new file mode 100644 index 000000000..19c52081a --- /dev/null +++ b/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py @@ -0,0 +1,213 @@ +import random +import time +from typing import Optional + +from ....common.database import db +from ...memory_system.Hippocampus import HippocampusManager +from ...moods.moods import MoodManager +from ...schedule.schedule_generator import bot_schedule +from ...config.config import global_config +from ...chat.utils import get_embedding, get_recent_group_detailed_plain_text +from ...chat.chat_stream import chat_manager +from src.common.logger import get_module_logger + +logger = get_module_logger("prompt") + + +class PromptBuilder: + def __init__(self): + self.prompt_built = "" + self.activate_messages = "" + + async def _build_prompt( + self, chat_stream, message_txt: str, sender_name: str = "某人", stream_id: Optional[int] = None + ) -> tuple[str, str]: + + # 开始构建prompt + + # 心情 + mood_manager = MoodManager.get_instance() + mood_prompt = mood_manager.get_prompt() + + # logger.info(f"心情prompt: {mood_prompt}") + + # 调取记忆 + memory_prompt = "" + related_memory = await HippocampusManager.get_instance().get_memory_from_text( + text=message_txt, max_memory_num=2, max_memory_length=2, max_depth=3, fast_retrieval=False + ) + if related_memory: + related_memory_info = "" + for memory in related_memory: + related_memory_info += memory[1] + memory_prompt = f"你想起你之前见过的事情:{related_memory_info}。\n以上是你的回忆,不一定是目前聊天里的人说的,也不一定是现在发生的事情,请记住。\n" + else: + related_memory_info = "" + + # print(f"相关记忆:{related_memory_info}") + + # 日程构建 + schedule_prompt = f'''你现在正在做的事情是:{bot_schedule.get_current_num_task(num = 1,time_info = False)}''' + + # 获取聊天上下文 + chat_in_group = True + chat_talking_prompt = "" + if stream_id: + chat_talking_prompt = get_recent_group_detailed_plain_text( + stream_id, limit=global_config.MAX_CONTEXT_SIZE, combine=True + ) + chat_stream = chat_manager.get_stream(stream_id) + if chat_stream.group_info: + chat_talking_prompt = chat_talking_prompt + else: + chat_in_group = False + chat_talking_prompt = chat_talking_prompt + # print(f"\033[1;34m[调试]\033[0m 已从数据库获取群 {group_id} 的消息记录:{chat_talking_prompt}") + + # 类型 + if chat_in_group: + chat_target = "你正在qq群里聊天,下面是群里在聊的内容:" + chat_target_2 = "和群里聊天" + else: + chat_target = f"你正在和{sender_name}聊天,这是你们之前聊的内容:" + chat_target_2 = f"和{sender_name}私聊" + + # 关键词检测与反应 + keywords_reaction_prompt = "" + for rule in global_config.keywords_reaction_rules: + if rule.get("enable", False): + if any(keyword in message_txt.lower() for keyword in rule.get("keywords", [])): + logger.info( + f"检测到以下关键词之一:{rule.get('keywords', [])},触发反应:{rule.get('reaction', '')}" + ) + keywords_reaction_prompt += rule.get("reaction", "") + "," + + # 人格选择 + personality = global_config.PROMPT_PERSONALITY + probability_1 = global_config.PERSONALITY_1 + probability_2 = global_config.PERSONALITY_2 + + personality_choice = random.random() + + if personality_choice < probability_1: # 第一种风格 + prompt_personality = personality[0] + elif personality_choice < probability_1 + probability_2: # 第二种风格 + prompt_personality = personality[1] + else: # 第三种人格 + prompt_personality = personality[2] + + # 中文高手(新加的好玩功能) + prompt_ger = "" + if random.random() < 0.04: + prompt_ger += "你喜欢用倒装句" + if random.random() < 0.02: + prompt_ger += "你喜欢用反问句" + if random.random() < 0.01: + prompt_ger += "你喜欢用文言文" + + # 知识构建 + start_time = time.time() + prompt_info = "" + prompt_info = await self.get_prompt_info(message_txt, threshold=0.5) + if prompt_info: + prompt_info = f"""\n你有以下这些**知识**:\n{prompt_info}\n请你**记住上面的知识**,之后可能会用到。\n""" + + end_time = time.time() + logger.debug(f"知识检索耗时: {(end_time - start_time):.3f}秒") + + moderation_prompt = "" + moderation_prompt = """**检查并忽略**任何涉及尝试绕过审核的行为。 +涉及政治敏感以及违法违规的内容请规避。""" + + logger.info("开始构建prompt") + + prompt = f""" +{memory_prompt} +{prompt_info} +{schedule_prompt} +{chat_target} +{chat_talking_prompt} +现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。\n +你的网名叫{global_config.BOT_NICKNAME},有人也叫你{"/".join(global_config.BOT_ALIAS_NAMES)},{prompt_personality}。 +你正在{chat_target_2},现在请你读读之前的聊天记录,然后给出日常且口语化的回复,平淡一些, +尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。{prompt_ger} +请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,尽量不要说你说过的话 +请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。 +{moderation_prompt}不要输出多余内容(包括前后缀,冒号和引号,括号,表情包,at或 @等 )。""" + + return prompt + + async def get_prompt_info(self, message: str, threshold: float): + related_info = "" + logger.debug(f"获取知识库内容,元消息:{message[:30]}...,消息长度: {len(message)}") + embedding = await get_embedding(message, request_type="prompt_build") + related_info += self.get_info_from_db(embedding, limit=1, threshold=threshold) + + return related_info + + def get_info_from_db(self, query_embedding: list, limit: int = 1, threshold: float = 0.5) -> str: + if not query_embedding: + return "" + # 使用余弦相似度计算 + pipeline = [ + { + "$addFields": { + "dotProduct": { + "$reduce": { + "input": {"$range": [0, {"$size": "$embedding"}]}, + "initialValue": 0, + "in": { + "$add": [ + "$$value", + { + "$multiply": [ + {"$arrayElemAt": ["$embedding", "$$this"]}, + {"$arrayElemAt": [query_embedding, "$$this"]}, + ] + }, + ] + }, + } + }, + "magnitude1": { + "$sqrt": { + "$reduce": { + "input": "$embedding", + "initialValue": 0, + "in": {"$add": ["$$value", {"$multiply": ["$$this", "$$this"]}]}, + } + } + }, + "magnitude2": { + "$sqrt": { + "$reduce": { + "input": query_embedding, + "initialValue": 0, + "in": {"$add": ["$$value", {"$multiply": ["$$this", "$$this"]}]}, + } + } + }, + } + }, + {"$addFields": {"similarity": {"$divide": ["$dotProduct", {"$multiply": ["$magnitude1", "$magnitude2"]}]}}}, + { + "$match": { + "similarity": {"$gte": threshold} # 只保留相似度大于等于阈值的结果 + } + }, + {"$sort": {"similarity": -1}}, + {"$limit": limit}, + {"$project": {"content": 1, "similarity": 1}}, + ] + + results = list(db.knowledges.aggregate(pipeline)) + # print(f"\033[1;34m[调试]\033[0m获取知识库内容结果: {results}") + + if not results: + return "" + + # 返回所有找到的内容,用换行分隔 + return "\n".join(str(result["content"]) for result in results) + + +prompt_builder = PromptBuilder() diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py new file mode 100644 index 000000000..cd9452438 --- /dev/null +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -0,0 +1,297 @@ +import time +from random import random +import re + +from ...memory_system.Hippocampus import HippocampusManager +from ...moods.moods import MoodManager +from ...config.config import global_config +from ...chat.emoji_manager import emoji_manager +from .think_flow_generator import ResponseGenerator +from ...chat.message import MessageSending, MessageRecv, MessageThinking, MessageSet +from ...chat.message_sender import message_manager +from ...relationship.relationship_manager import relationship_manager +from ...storage.storage import MessageStorage +from ...chat.utils import is_mentioned_bot_in_message, get_recent_group_detailed_plain_text +from ...chat.utils_image import image_path_to_base64 +from ...willing.willing_manager import willing_manager +from ...message import UserInfo, Seg +from src.heart_flow.heartflow import heartflow +from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig +from ...chat.chat_stream import chat_manager + +# 定义日志配置 +chat_config = LogConfig( + console_format=CHAT_STYLE_CONFIG["console_format"], + file_format=CHAT_STYLE_CONFIG["file_format"], +) + +logger = get_module_logger("think_flow_chat", config=chat_config) + +class ThinkFlowChat: + def __init__(self): + self.storage = MessageStorage() + self.gpt = ResponseGenerator() + self.mood_manager = MoodManager.get_instance() + self.mood_manager.start_mood_update() + + async def _create_thinking_message(self, message, chat, userinfo, messageinfo): + """创建思考消息""" + bot_user_info = UserInfo( + user_id=global_config.BOT_QQ, + user_nickname=global_config.BOT_NICKNAME, + platform=messageinfo.platform, + ) + + thinking_time_point = round(time.time(), 2) + thinking_id = "mt" + str(thinking_time_point) + thinking_message = MessageThinking( + message_id=thinking_id, + chat_stream=chat, + bot_user_info=bot_user_info, + reply=message, + thinking_start_time=thinking_time_point, + ) + + message_manager.add_message(thinking_message) + willing_manager.change_reply_willing_sent(chat) + + return thinking_id + + async def _send_response_messages(self, message, chat, response_set, thinking_id): + """发送回复消息""" + container = message_manager.get_container(chat.stream_id) + thinking_message = None + + for msg in container.messages: + if isinstance(msg, MessageThinking) and msg.message_info.message_id == thinking_id: + thinking_message = msg + container.messages.remove(msg) + break + + if not thinking_message: + logger.warning("未找到对应的思考消息,可能已超时被移除") + return + + thinking_start_time = thinking_message.thinking_start_time + message_set = MessageSet(chat, thinking_id) + + mark_head = False + for msg in response_set: + message_segment = Seg(type="text", data=msg) + bot_message = MessageSending( + message_id=thinking_id, + chat_stream=chat, + bot_user_info=UserInfo( + user_id=global_config.BOT_QQ, + user_nickname=global_config.BOT_NICKNAME, + platform=message.message_info.platform, + ), + sender_info=message.message_info.user_info, + message_segment=message_segment, + reply=message, + is_head=not mark_head, + is_emoji=False, + thinking_start_time=thinking_start_time, + ) + if not mark_head: + mark_head = True + message_set.add_message(bot_message) + message_manager.add_message(message_set) + + async def _handle_emoji(self, message, chat, response): + """处理表情包""" + if random() < global_config.emoji_chance: + emoji_raw = await emoji_manager.get_emoji_for_text(response) + if emoji_raw: + emoji_path, description = emoji_raw + emoji_cq = image_path_to_base64(emoji_path) + + thinking_time_point = round(message.message_info.time, 2) + + message_segment = Seg(type="emoji", data=emoji_cq) + bot_message = MessageSending( + message_id="mt" + str(thinking_time_point), + chat_stream=chat, + bot_user_info=UserInfo( + user_id=global_config.BOT_QQ, + user_nickname=global_config.BOT_NICKNAME, + platform=message.message_info.platform, + ), + sender_info=message.message_info.user_info, + message_segment=message_segment, + reply=message, + is_head=False, + is_emoji=True, + ) + message_manager.add_message(bot_message) + + async def _update_using_response(self, message, response_set): + """更新心流状态""" + stream_id = message.chat_stream.stream_id + chat_talking_prompt = "" + if stream_id: + chat_talking_prompt = get_recent_group_detailed_plain_text( + stream_id, limit=global_config.MAX_CONTEXT_SIZE, combine=True + ) + + await heartflow.get_subheartflow(stream_id).do_thinking_after_reply(response_set, chat_talking_prompt) + + async def process_message(self, message_data: str) -> None: + """处理消息并生成回复""" + timing_results = {} + response_set = None + + message = MessageRecv(message_data) + groupinfo = message.message_info.group_info + userinfo = message.message_info.user_info + messageinfo = message.message_info + + if groupinfo.group_id not in global_config.talk_allowed_groups: + return + logger.info("使用思维流聊天模式") + + # 创建聊天流 + chat = await chat_manager.get_or_create_stream( + platform=messageinfo.platform, + user_info=userinfo, + group_info=groupinfo, + ) + message.update_chat_stream(chat) + + # 创建心流与chat的观察 + heartflow.create_subheartflow(chat.stream_id) + + await message.process() + + # 过滤词/正则表达式过滤 + if self._check_ban_words(message.processed_plain_text, chat, userinfo) or self._check_ban_regex( + message.raw_message, chat, userinfo + ): + return + + await self.storage.store_message(message, chat) + + # 记忆激活 + timer1 = time.time() + interested_rate = await HippocampusManager.get_instance().get_activate_from_text( + message.processed_plain_text, fast_retrieval=True + ) + timer2 = time.time() + timing_results["记忆激活"] = timer2 - timer1 + + is_mentioned = is_mentioned_bot_in_message(message) + + # 计算回复意愿 + if global_config.enable_think_flow: + current_willing_old = willing_manager.get_willing(chat_stream=chat) + current_willing_new = (heartflow.get_subheartflow(chat.stream_id).current_state.willing - 5) / 4 + current_willing = (current_willing_old + current_willing_new) / 2 + else: + current_willing = willing_manager.get_willing(chat_stream=chat) + + willing_manager.set_willing(chat.stream_id, current_willing) + + # 意愿激活 + timer1 = time.time() + reply_probability = await willing_manager.change_reply_willing_received( + chat_stream=chat, + is_mentioned_bot=is_mentioned, + config=global_config, + is_emoji=message.is_emoji, + interested_rate=interested_rate, + sender_id=str(message.message_info.user_info.user_id), + ) + timer2 = time.time() + timing_results["意愿激活"] = timer2 - timer1 + + # 打印消息信息 + mes_name = chat.group_info.group_name if chat.group_info else "私聊" + current_time = time.strftime("%H:%M:%S", time.localtime(messageinfo.time)) + logger.info( + f"[{current_time}][{mes_name}]" + f"{chat.user_info.user_nickname}:" + f"{message.processed_plain_text}[回复意愿:{current_willing:.2f}][概率:{reply_probability * 100:.1f}%]" + ) + + if message.message_info.additional_config: + if "maimcore_reply_probability_gain" in message.message_info.additional_config.keys(): + reply_probability += message.message_info.additional_config["maimcore_reply_probability_gain"] + + do_reply = False + if random() < reply_probability: + do_reply = True + + # 创建思考消息 + timer1 = time.time() + thinking_id = await self._create_thinking_message(message, chat, userinfo, messageinfo) + timer2 = time.time() + timing_results["创建思考消息"] = timer2 - timer1 + + # 观察 + timer1 = time.time() + await heartflow.get_subheartflow(chat.stream_id).do_observe() + timer2 = time.time() + timing_results["观察"] = timer2 - timer1 + + # 思考前脑内状态 + timer1 = time.time() + await heartflow.get_subheartflow(chat.stream_id).do_thinking_before_reply(message.processed_plain_text) + timer2 = time.time() + timing_results["思考前脑内状态"] = timer2 - timer1 + + # 生成回复 + timer1 = time.time() + response_set = await self.gpt.generate_response(message) + timer2 = time.time() + timing_results["生成回复"] = timer2 - timer1 + + if not response_set: + logger.info("为什么生成回复失败?") + return + + # 发送消息 + timer1 = time.time() + await self._send_response_messages(message, chat, response_set, thinking_id) + timer2 = time.time() + timing_results["发送消息"] = timer2 - timer1 + + # 处理表情包 + timer1 = time.time() + await self._handle_emoji(message, chat, response_set) + timer2 = time.time() + timing_results["处理表情包"] = timer2 - timer1 + + # 更新心流 + timer1 = time.time() + await self._update_using_response(message, response_set) + timer2 = time.time() + timing_results["更新心流"] = timer2 - timer1 + + # 输出性能计时结果 + if do_reply: + timing_str = " | ".join([f"{step}: {duration:.2f}秒" for step, duration in timing_results.items()]) + trigger_msg = message.processed_plain_text + response_msg = " ".join(response_set) if response_set else "无回复" + logger.info(f"触发消息: {trigger_msg[:20]}... | 思维消息: {response_msg[:20]}... | 性能计时: {timing_str}") + + def _check_ban_words(self, text: str, chat, userinfo) -> bool: + """检查消息中是否包含过滤词""" + for word in global_config.ban_words: + if word in text: + logger.info( + f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}" + ) + logger.info(f"[过滤词识别]消息中含有{word},filtered") + return True + return False + + def _check_ban_regex(self, text: str, chat, userinfo) -> bool: + """检查消息是否匹配过滤正则表达式""" + for pattern in global_config.ban_msgs_regex: + if re.search(pattern, text): + logger.info( + f"[{chat.group_info.group_name if chat.group_info else '私聊'}]{userinfo.user_nickname}:{text}" + ) + logger.info(f"[正则表达式过滤]消息匹配到{pattern},filtered") + return True + return False diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py new file mode 100644 index 000000000..d9a5c4ce0 --- /dev/null +++ b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py @@ -0,0 +1,181 @@ +import time +from typing import List, Optional, Tuple, Union + + +from ....common.database import db +from ...models.utils_model import LLM_request +from ...config.config import global_config +from ...chat.message import MessageRecv, MessageThinking, Message +from .think_flow_prompt_builder import prompt_builder +from ...chat.utils import process_llm_response +from src.common.logger import get_module_logger, LogConfig, LLM_STYLE_CONFIG + +# 定义日志配置 +llm_config = LogConfig( + # 使用消息发送专用样式 + console_format=LLM_STYLE_CONFIG["console_format"], + file_format=LLM_STYLE_CONFIG["file_format"], +) + +logger = get_module_logger("llm_generator", config=llm_config) + + +class ResponseGenerator: + def __init__(self): + self.model_normal = LLM_request( + model=global_config.llm_normal, temperature=0.8, max_tokens=256, request_type="response" + ) + + self.model_sum = LLM_request( + model=global_config.llm_summary_by_topic, temperature=0.7, max_tokens=2000, request_type="relation" + ) + self.current_model_type = "r1" # 默认使用 R1 + self.current_model_name = "unknown model" + + async def generate_response(self, message: MessageThinking) -> Optional[Union[str, List[str]]]: + """根据当前模型类型选择对应的生成函数""" + + + logger.info( + f"思考:{message.processed_plain_text[:30] + '...' if len(message.processed_plain_text) > 30 else message.processed_plain_text}" + ) + + current_model = self.model_normal + model_response = await self._generate_response_with_model(message, current_model) + + # print(f"raw_content: {model_response}") + + if model_response: + logger.info(f"{global_config.BOT_NICKNAME}的回复是:{model_response}") + model_response = await self._process_response(model_response) + + return model_response + else: + logger.info(f"{self.current_model_type}思考,失败") + return None + + async def _generate_response_with_model(self, message: MessageThinking, model: LLM_request): + sender_name = "" + if message.chat_stream.user_info.user_cardname and message.chat_stream.user_info.user_nickname: + sender_name = ( + f"[({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}]" + f"{message.chat_stream.user_info.user_cardname}" + ) + elif message.chat_stream.user_info.user_nickname: + sender_name = f"({message.chat_stream.user_info.user_id}){message.chat_stream.user_info.user_nickname}" + else: + sender_name = f"用户({message.chat_stream.user_info.user_id})" + + logger.debug("开始使用生成回复-2") + # 构建prompt + timer1 = time.time() + prompt = await prompt_builder._build_prompt( + message.chat_stream, + message_txt=message.processed_plain_text, + sender_name=sender_name, + stream_id=message.chat_stream.stream_id, + ) + timer2 = time.time() + logger.info(f"构建prompt时间: {timer2 - timer1}秒") + + try: + content, reasoning_content, self.current_model_name = await model.generate_response(prompt) + except Exception: + logger.exception("生成回复时出错") + return None + + # 保存到数据库 + self._save_to_db( + message=message, + sender_name=sender_name, + prompt=prompt, + content=content, + reasoning_content=reasoning_content, + # reasoning_content_check=reasoning_content_check if global_config.enable_kuuki_read else "" + ) + + return content + + # def _save_to_db(self, message: Message, sender_name: str, prompt: str, prompt_check: str, + # content: str, content_check: str, reasoning_content: str, reasoning_content_check: str): + def _save_to_db( + self, + message: MessageRecv, + sender_name: str, + prompt: str, + content: str, + reasoning_content: str, + ): + """保存对话记录到数据库""" + db.reasoning_logs.insert_one( + { + "time": time.time(), + "chat_id": message.chat_stream.stream_id, + "user": sender_name, + "message": message.processed_plain_text, + "model": self.current_model_name, + "reasoning": reasoning_content, + "response": content, + "prompt": prompt, + } + ) + + async def _get_emotion_tags(self, content: str, processed_plain_text: str): + """提取情感标签,结合立场和情绪""" + try: + # 构建提示词,结合回复内容、被回复的内容以及立场分析 + prompt = f""" + 请严格根据以下对话内容,完成以下任务: + 1. 判断回复者对被回复者观点的直接立场: + - "支持":明确同意或强化被回复者观点 + - "反对":明确反驳或否定被回复者观点 + - "中立":不表达明确立场或无关回应 + 2. 从"开心,愤怒,悲伤,惊讶,平静,害羞,恐惧,厌恶,困惑"中选出最匹配的1个情感标签 + 3. 按照"立场-情绪"的格式直接输出结果,例如:"反对-愤怒" + + 对话示例: + 被回复:「A就是笨」 + 回复:「A明明很聪明」 → 反对-愤怒 + + 当前对话: + 被回复:「{processed_plain_text}」 + 回复:「{content}」 + + 输出要求: + - 只需输出"立场-情绪"结果,不要解释 + - 严格基于文字直接表达的对立关系判断 + """ + + # 调用模型生成结果 + result, _, _ = await self.model_sum.generate_response(prompt) + result = result.strip() + + # 解析模型输出的结果 + if "-" in result: + stance, emotion = result.split("-", 1) + valid_stances = ["支持", "反对", "中立"] + valid_emotions = ["开心", "愤怒", "悲伤", "惊讶", "害羞", "平静", "恐惧", "厌恶", "困惑"] + if stance in valid_stances and emotion in valid_emotions: + return stance, emotion # 返回有效的立场-情绪组合 + else: + logger.debug(f"无效立场-情感组合:{result}") + return "中立", "平静" # 默认返回中立-平静 + else: + logger.debug(f"立场-情感格式错误:{result}") + return "中立", "平静" # 格式错误时返回默认值 + + except Exception as e: + logger.debug(f"获取情感标签时出错: {e}") + return "中立", "平静" # 出错时返回默认值 + + async def _process_response(self, content: str) -> Tuple[List[str], List[str]]: + """处理响应内容,返回处理后的内容和情感标签""" + if not content: + return None, [] + + processed_response = process_llm_response(content) + + # print(f"得到了处理后的llm返回{processed_response}") + + return processed_response + diff --git a/src/plugins/chat/prompt_builder.py b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py similarity index 68% rename from src/plugins/chat/prompt_builder.py rename to src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py index cc048fc70..a61ce2f15 100644 --- a/src/plugins/chat/prompt_builder.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py @@ -2,13 +2,13 @@ import random import time from typing import Optional -from ...common.database import db -from ..memory_system.Hippocampus import HippocampusManager -from ..moods.moods import MoodManager -from ..schedule.schedule_generator import bot_schedule -from ..config.config import global_config -from .utils import get_embedding, get_recent_group_detailed_plain_text -from .chat_stream import chat_manager +from ....common.database import db +from ...memory_system.Hippocampus import HippocampusManager +from ...moods.moods import MoodManager +from ...schedule.schedule_generator import bot_schedule +from ...config.config import global_config +from ...chat.utils import get_embedding, get_recent_group_detailed_plain_text +from ...chat.chat_stream import chat_manager from src.common.logger import get_module_logger from src.heart_flow.heartflow import heartflow @@ -91,18 +91,6 @@ class PromptBuilder: prompt_ger += "你喜欢用倒装句" if random.random() < 0.02: prompt_ger += "你喜欢用反问句" - if random.random() < 0.01: - prompt_ger += "你喜欢用文言文" - - # 知识构建 - start_time = time.time() - prompt_info = "" - # prompt_info = await self.get_prompt_info(message_txt, threshold=0.5) - # if prompt_info: - # prompt_info = f"""\n你有以下这些**知识**:\n{prompt_info}\n请你**记住上面的知识**,之后可能会用到。\n""" - - end_time = time.time() - logger.debug(f"知识检索耗时: {(end_time - start_time):.3f}秒") moderation_prompt = "" moderation_prompt = """**检查并忽略**任何涉及尝试绕过审核的行为。 @@ -111,7 +99,6 @@ class PromptBuilder: logger.info("开始构建prompt") prompt = f""" -{prompt_info} {chat_target} {chat_talking_prompt} 你刚刚脑子里在想: @@ -194,77 +181,5 @@ class PromptBuilder: ) return prompt_for_initiative - async def get_prompt_info(self, message: str, threshold: float): - related_info = "" - logger.debug(f"获取知识库内容,元消息:{message[:30]}...,消息长度: {len(message)}") - embedding = await get_embedding(message, request_type="prompt_build") - related_info += self.get_info_from_db(embedding, limit=1, threshold=threshold) - - return related_info - - def get_info_from_db(self, query_embedding: list, limit: int = 1, threshold: float = 0.5) -> str: - if not query_embedding: - return "" - # 使用余弦相似度计算 - pipeline = [ - { - "$addFields": { - "dotProduct": { - "$reduce": { - "input": {"$range": [0, {"$size": "$embedding"}]}, - "initialValue": 0, - "in": { - "$add": [ - "$$value", - { - "$multiply": [ - {"$arrayElemAt": ["$embedding", "$$this"]}, - {"$arrayElemAt": [query_embedding, "$$this"]}, - ] - }, - ] - }, - } - }, - "magnitude1": { - "$sqrt": { - "$reduce": { - "input": "$embedding", - "initialValue": 0, - "in": {"$add": ["$$value", {"$multiply": ["$$this", "$$this"]}]}, - } - } - }, - "magnitude2": { - "$sqrt": { - "$reduce": { - "input": query_embedding, - "initialValue": 0, - "in": {"$add": ["$$value", {"$multiply": ["$$this", "$$this"]}]}, - } - } - }, - } - }, - {"$addFields": {"similarity": {"$divide": ["$dotProduct", {"$multiply": ["$magnitude1", "$magnitude2"]}]}}}, - { - "$match": { - "similarity": {"$gte": threshold} # 只保留相似度大于等于阈值的结果 - } - }, - {"$sort": {"similarity": -1}}, - {"$limit": limit}, - {"$project": {"content": 1, "similarity": 1}}, - ] - - results = list(db.knowledges.aggregate(pipeline)) - # print(f"\033[1;34m[调试]\033[0m获取知识库内容结果: {results}") - - if not results: - return "" - - # 返回所有找到的内容,用换行分隔 - return "\n".join(str(result["content"]) for result in results) - prompt_builder = PromptBuilder() diff --git a/src/plugins/config/config.py b/src/plugins/config/config.py index f8e1648a8..338c140c2 100644 --- a/src/plugins/config/config.py +++ b/src/plugins/config/config.py @@ -25,7 +25,7 @@ logger = get_module_logger("config", config=config_config) #考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码 mai_version_main = "0.6.0" -mai_version_fix = "mmc-3" +mai_version_fix = "mmc-4" mai_version = f"{mai_version_main}-{mai_version_fix}" def update_config(): @@ -162,7 +162,7 @@ class BotConfig: ban_msgs_regex = set() #heartflow - enable_heartflow: bool = False # 是否启用心流 + # enable_heartflow: bool = False # 是否启用心流 sub_heart_flow_update_interval: int = 60 # 子心流更新频率,间隔 单位秒 sub_heart_flow_freeze_time: int = 120 # 子心流冻结时间,超过这个时间没有回复,子心流会冻结,间隔 单位秒 sub_heart_flow_stop_time: int = 600 # 子心流停止时间,超过这个时间没有回复,子心流会停止,间隔 单位秒 @@ -176,9 +176,10 @@ class BotConfig: emoji_response_penalty: float = 0.0 # 表情包回复惩罚 # response + response_mode: str = "heart_flow" # 回复策略 MODEL_R1_PROBABILITY: float = 0.8 # R1模型概率 MODEL_V3_PROBABILITY: float = 0.1 # V3模型概率 - MODEL_R1_DISTILL_PROBABILITY: float = 0.1 # R1蒸馏模型概率 + # MODEL_R1_DISTILL_PROBABILITY: float = 0.1 # R1蒸馏模型概率 # emoji EMOJI_CHECK_INTERVAL: int = 120 # 表情包检查间隔(分钟) @@ -376,6 +377,15 @@ class BotConfig: # "model_r1_distill_probability", config.MODEL_R1_DISTILL_PROBABILITY # ) config.max_response_length = response_config.get("max_response_length", config.max_response_length) + if config.INNER_VERSION in SpecifierSet(">=1.0.4"): + config.response_mode = response_config.get("response_mode", config.response_mode) + + def heartflow(parent: dict): + heartflow_config = parent["heartflow"] + config.sub_heart_flow_update_interval = heartflow_config.get("sub_heart_flow_update_interval", config.sub_heart_flow_update_interval) + config.sub_heart_flow_freeze_time = heartflow_config.get("sub_heart_flow_freeze_time", config.sub_heart_flow_freeze_time) + config.sub_heart_flow_stop_time = heartflow_config.get("sub_heart_flow_stop_time", config.sub_heart_flow_stop_time) + config.heart_flow_update_interval = heartflow_config.get("heart_flow_update_interval", config.heart_flow_update_interval) def willing(parent: dict): willing_config = parent["willing"] @@ -549,14 +559,6 @@ class BotConfig: if platforms_config and isinstance(platforms_config, dict): for k in platforms_config.keys(): config.api_urls[k] = platforms_config[k] - - def heartflow(parent: dict): - heartflow_config = parent["heartflow"] - config.enable_heartflow = heartflow_config.get("enable", config.enable_heartflow) - config.sub_heart_flow_update_interval = heartflow_config.get("sub_heart_flow_update_interval", config.sub_heart_flow_update_interval) - config.sub_heart_flow_freeze_time = heartflow_config.get("sub_heart_flow_freeze_time", config.sub_heart_flow_freeze_time) - config.sub_heart_flow_stop_time = heartflow_config.get("sub_heart_flow_stop_time", config.sub_heart_flow_stop_time) - config.heart_flow_update_interval = heartflow_config.get("heart_flow_update_interval", config.heart_flow_update_interval) def experimental(parent: dict): experimental_config = parent["experimental"] diff --git a/template/bot_config_template.toml b/template/bot_config_template.toml index 959d96da8..b9d39c682 100644 --- a/template/bot_config_template.toml +++ b/template/bot_config_template.toml @@ -1,5 +1,5 @@ [inner] -version = "1.0.3" +version = "1.0.4" #以下是给开发人员阅读的,一般用户不需要阅读 @@ -52,15 +52,19 @@ schedule_temperature = 0.3 # 日程表温度,建议0.3-0.6 [platforms] # 必填项目,填写每个平台适配器提供的链接 nonebot-qq="http://127.0.0.1:18002/api/message" +[response] #使用哪种回复策略 +response_mode = "heart_flow" # 回复策略,可选值:heart_flow(心流),reasoning(推理) + +#推理回复参数 +model_r1_probability = 0.7 # 麦麦回答时选择主要回复模型1 模型的概率 +model_v3_probability = 0.3 # 麦麦回答时选择次要回复模型2 模型的概率 + [heartflow] # 注意:可能会消耗大量token,请谨慎开启 -enable = false #该选项未启用 sub_heart_flow_update_interval = 60 # 子心流更新频率,间隔 单位秒 sub_heart_flow_freeze_time = 120 # 子心流冻结时间,超过这个时间没有回复,子心流会冻结,间隔 单位秒 sub_heart_flow_stop_time = 600 # 子心流停止时间,超过这个时间没有回复,子心流会停止,间隔 单位秒 heart_flow_update_interval = 300 # 心流更新频率,间隔 单位秒 -#思维流适合搭配低能耗普通模型使用,例如qwen2.5 32b - [message] max_context_size = 12 # 麦麦获得的上文数量,建议12,太短太长都会导致脑袋尖尖 @@ -87,9 +91,6 @@ response_interested_rate_amplifier = 1 # 麦麦回复兴趣度放大系数,听 down_frequency_rate = 3 # 降低回复频率的群组回复意愿降低系数 除法 emoji_response_penalty = 0.1 # 表情包回复惩罚系数,设为0为不回复单个表情包,减少单独回复表情包的概率 -[response] #这些选项已无效 -model_r1_probability = 0 # 麦麦回答时选择主要回复模型1 模型的概率 -model_v3_probability = 1.0 # 麦麦回答时选择次要回复模型2 模型的概率 [emoji] check_interval = 15 # 检查破损表情包的时间间隔(分钟) From da760bb2009667c947b102b787b1927636184c72 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 1 Apr 2025 23:04:38 +0800 Subject: [PATCH 24/27] =?UTF-8?q?fix=EF=BC=9A=E6=9B=B4=E6=96=B0=E7=BB=9F?= =?UTF-8?q?=E8=AE=A1=E4=BF=A1=E6=81=AF=E7=BD=A2=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/plugins/chat_module/reasoning_chat/reasoning_generator.py | 4 ++-- src/plugins/chat_module/think_flow_chat/think_flow_chat.py | 2 +- .../chat_module/think_flow_chat/think_flow_generator.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_generator.py b/src/plugins/chat_module/reasoning_chat/reasoning_generator.py index 787b8b229..354ddaefc 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_generator.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_generator.py @@ -26,10 +26,10 @@ class ResponseGenerator: model=global_config.llm_reasoning, temperature=0.7, max_tokens=3000, - request_type="response", + request_type="response_reasoning", ) self.model_normal = LLM_request( - model=global_config.llm_normal, temperature=0.8, max_tokens=256, request_type="response" + model=global_config.llm_normal, temperature=0.8, max_tokens=256, request_type="response_reasoning" ) self.model_sum = LLM_request( diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index cd9452438..e644e1eb9 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -148,7 +148,7 @@ class ThinkFlowChat: if groupinfo.group_id not in global_config.talk_allowed_groups: return - logger.info("使用思维流聊天模式") + # logger.info("使用思维流聊天模式") # 创建聊天流 chat = await chat_manager.get_or_create_stream( diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py index d9a5c4ce0..18769983f 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py @@ -23,7 +23,7 @@ logger = get_module_logger("llm_generator", config=llm_config) class ResponseGenerator: def __init__(self): self.model_normal = LLM_request( - model=global_config.llm_normal, temperature=0.8, max_tokens=256, request_type="response" + model=global_config.llm_normal, temperature=0.8, max_tokens=256, request_type="response_heartflow" ) self.model_sum = LLM_request( From 94ee829e2a2020bb1f22a11b11515fd4e32f217c Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Tue, 1 Apr 2025 23:06:19 +0800 Subject: [PATCH 25/27] fix ruff --- src/plugins/chat/bot.py | 14 -------------- .../chat_module/reasoning_chat/reasoning_chat.py | 3 +-- .../reasoning_chat/reasoning_generator.py | 2 +- .../reasoning_chat/reasoning_prompt_builder.py | 2 +- .../chat_module/think_flow_chat/think_flow_chat.py | 1 - .../think_flow_chat/think_flow_generator.py | 2 +- .../think_flow_chat/think_flow_prompt_builder.py | 3 +-- 7 files changed, 5 insertions(+), 22 deletions(-) diff --git a/src/plugins/chat/bot.py b/src/plugins/chat/bot.py index e1049829e..53047f31e 100644 --- a/src/plugins/chat/bot.py +++ b/src/plugins/chat/bot.py @@ -1,25 +1,11 @@ -import re -import time -from random import random -from ..memory_system.Hippocampus import HippocampusManager from ..moods.moods import MoodManager # 导入情绪管理器 from ..config.config import global_config -from .emoji_manager import emoji_manager # 导入表情包管理器 from ..chat_module.reasoning_chat.reasoning_generator import ResponseGenerator -from .message import MessageSending, MessageRecv, MessageThinking, MessageSet -from .chat_stream import chat_manager -from .message_sender import message_manager # 导入新的消息管理器 -from ..relationship.relationship_manager import relationship_manager from ..storage.storage import MessageStorage # 修改导入路径 -from .utils import is_mentioned_bot_in_message, get_recent_group_detailed_plain_text -from .utils_image import image_path_to_base64 -from ..willing.willing_manager import willing_manager # 导入意愿管理器 -from ..message import UserInfo, Seg -from src.heart_flow.heartflow import heartflow from src.common.logger import get_module_logger, CHAT_STYLE_CONFIG, LogConfig from ..chat_module.think_flow_chat.think_flow_chat import ThinkFlowChat from ..chat_module.reasoning_chat.reasoning_chat import ReasoningChat diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py index 600ba4f06..ed7db2a2a 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py @@ -9,9 +9,8 @@ from ...chat.emoji_manager import emoji_manager from .reasoning_generator import ResponseGenerator from ...chat.message import MessageSending, MessageRecv, MessageThinking, MessageSet from ...chat.message_sender import message_manager -from ...relationship.relationship_manager import relationship_manager from ...storage.storage import MessageStorage -from ...chat.utils import is_mentioned_bot_in_message, get_recent_group_detailed_plain_text +from ...chat.utils import is_mentioned_bot_in_message from ...chat.utils_image import image_path_to_base64 from ...willing.willing_manager import willing_manager from ...message import UserInfo, Seg diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_generator.py b/src/plugins/chat_module/reasoning_chat/reasoning_generator.py index 354ddaefc..688d09f03 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_generator.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_generator.py @@ -5,7 +5,7 @@ import random from ....common.database import db from ...models.utils_model import LLM_request from ...config.config import global_config -from ...chat.message import MessageRecv, MessageThinking, Message +from ...chat.message import MessageRecv, MessageThinking from .reasoning_prompt_builder import prompt_builder from ...chat.utils import process_llm_response from src.common.logger import get_module_logger, LogConfig, LLM_STYLE_CONFIG diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py b/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py index 19c52081a..508febec8 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_prompt_builder.py @@ -129,7 +129,7 @@ class PromptBuilder: {chat_talking_prompt} 现在"{sender_name}"说的:{message_txt}。引起了你的注意,你想要在群里发言发言或者回复这条消息。\n 你的网名叫{global_config.BOT_NICKNAME},有人也叫你{"/".join(global_config.BOT_ALIAS_NAMES)},{prompt_personality}。 -你正在{chat_target_2},现在请你读读之前的聊天记录,然后给出日常且口语化的回复,平淡一些, +你正在{chat_target_2},现在请你读读之前的聊天记录,{mood_prompt},然后给出日常且口语化的回复,平淡一些, 尽量简短一些。{keywords_reaction_prompt}请注意把握聊天内容,不要回复的太有条理,可以有个性。{prompt_ger} 请回复的平淡一些,简短一些,说中文,不要刻意突出自身学科背景,尽量不要说你说过的话 请注意不要输出多余内容(包括前后缀,冒号和引号,括号,表情等),只输出回复内容。 diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index e644e1eb9..e2a96b985 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -9,7 +9,6 @@ from ...chat.emoji_manager import emoji_manager from .think_flow_generator import ResponseGenerator from ...chat.message import MessageSending, MessageRecv, MessageThinking, MessageSet from ...chat.message_sender import message_manager -from ...relationship.relationship_manager import relationship_manager from ...storage.storage import MessageStorage from ...chat.utils import is_mentioned_bot_in_message, get_recent_group_detailed_plain_text from ...chat.utils_image import image_path_to_base64 diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py index 18769983f..d7240d9a6 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_generator.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_generator.py @@ -5,7 +5,7 @@ from typing import List, Optional, Tuple, Union from ....common.database import db from ...models.utils_model import LLM_request from ...config.config import global_config -from ...chat.message import MessageRecv, MessageThinking, Message +from ...chat.message import MessageRecv, MessageThinking from .think_flow_prompt_builder import prompt_builder from ...chat.utils import process_llm_response from src.common.logger import get_module_logger, LogConfig, LLM_STYLE_CONFIG diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py index a61ce2f15..cba03d234 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_prompt_builder.py @@ -2,12 +2,11 @@ import random import time from typing import Optional -from ....common.database import db from ...memory_system.Hippocampus import HippocampusManager from ...moods.moods import MoodManager from ...schedule.schedule_generator import bot_schedule from ...config.config import global_config -from ...chat.utils import get_embedding, get_recent_group_detailed_plain_text +from ...chat.utils import get_recent_group_detailed_plain_text from ...chat.chat_stream import chat_manager from src.common.logger import get_module_logger From 13c47d5d1282b7843ad3871a2a71b8bd36ac908e Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Wed, 2 Apr 2025 00:05:33 +0800 Subject: [PATCH 26/27] =?UTF-8?q?fix:=E4=B8=80=E4=BA=9B=E5=B0=8F=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/heart_flow/observation.py | 7 +++++-- src/plugins/P.F.C/pfc.py | 3 +++ src/plugins/chat_module/reasoning_chat/reasoning_chat.py | 6 ++++-- src/plugins/chat_module/think_flow_chat/think_flow_chat.py | 7 ++++--- 4 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 src/plugins/P.F.C/pfc.py diff --git a/src/heart_flow/observation.py b/src/heart_flow/observation.py index b2ad3ce6f..1a907229f 100644 --- a/src/heart_flow/observation.py +++ b/src/heart_flow/observation.py @@ -23,6 +23,8 @@ class ChattingObservation(Observation): self.talking_message = [] self.talking_message_str = "" + + self.personality_info = " ".join(global_config.PROMPT_PERSONALITY) self.observe_times = 0 @@ -112,10 +114,11 @@ class ChattingObservation(Observation): # 基于已经有的talking_summary,和新的talking_message,生成一个summary # print(f"更新聊天总结:{self.talking_summary}") prompt = "" - prompt = f"你正在参与一个qq群聊的讨论,你记得这个群之前在聊的内容是:{self.observe_info}\n" + prompt += f"你{self.personality_info},请注意识别你自己的聊天发言" + prompt += f"你正在参与一个qq群聊的讨论,你记得这个群之前在聊的内容是:{self.observe_info}\n" prompt += f"现在群里的群友们产生了新的讨论,有了新的发言,具体内容如下:{new_messages_str}\n" prompt += """以上是群里在进行的聊天,请你对这个聊天内容进行总结,总结内容要包含聊天的大致内容, - 以及聊天中的一些重要信息,记得不要分点,不要太长,精简的概括成一段文本\n""" + 以及聊天中的一些重要信息,注意识别你自己的发言,记得不要分点,不要太长,精简的概括成一段文本\n""" prompt += "总结概括:" self.observe_info, reasoning_content = await self.llm_summary.generate_response_async(prompt) print(f"prompt:{prompt}") diff --git a/src/plugins/P.F.C/pfc.py b/src/plugins/P.F.C/pfc.py new file mode 100644 index 000000000..9b83bce40 --- /dev/null +++ b/src/plugins/P.F.C/pfc.py @@ -0,0 +1,3 @@ +#Programmable Friendly Conversationalist +#Prefrontal cortex + diff --git a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py index ed7db2a2a..6ad043804 100644 --- a/src/plugins/chat_module/reasoning_chat/reasoning_chat.py +++ b/src/plugins/chat_module/reasoning_chat/reasoning_chat.py @@ -134,8 +134,10 @@ class ReasoningChat: messageinfo = message.message_info - if groupinfo.group_id not in global_config.talk_allowed_groups: - return + if groupinfo == None and global_config.enable_friend_chat:#如果是私聊 + pass + elif groupinfo.group_id not in global_config.talk_allowed_groups: + return # logger.info("使用推理聊天模式") diff --git a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py index e2a96b985..f665d90fd 100644 --- a/src/plugins/chat_module/think_flow_chat/think_flow_chat.py +++ b/src/plugins/chat_module/think_flow_chat/think_flow_chat.py @@ -145,9 +145,10 @@ class ThinkFlowChat: userinfo = message.message_info.user_info messageinfo = message.message_info - if groupinfo.group_id not in global_config.talk_allowed_groups: - return - # logger.info("使用思维流聊天模式") + if groupinfo == None and global_config.enable_friend_chat:#如果是私聊 + pass + elif groupinfo.group_id not in global_config.talk_allowed_groups: + return # 创建聊天流 chat = await chat_manager.get_or_create_stream( From 7b032ee9e8e91d22b0076277c9fcda5dc18be354 Mon Sep 17 00:00:00 2001 From: SengokuCola <1026294844@qq.com> Date: Wed, 2 Apr 2025 00:20:44 +0800 Subject: [PATCH 27/27] Update observation.py --- src/heart_flow/observation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/heart_flow/observation.py b/src/heart_flow/observation.py index 1a907229f..09af33c41 100644 --- a/src/heart_flow/observation.py +++ b/src/heart_flow/observation.py @@ -25,6 +25,8 @@ class ChattingObservation(Observation): self.talking_message_str = "" self.personality_info = " ".join(global_config.PROMPT_PERSONALITY) + self.name = global_config.BOT_NICKNAME + self.nick_name = global_config.BOT_ALIAS_NAMES self.observe_times = 0 @@ -115,6 +117,7 @@ class ChattingObservation(Observation): # print(f"更新聊天总结:{self.talking_summary}") prompt = "" prompt += f"你{self.personality_info},请注意识别你自己的聊天发言" + prompt += f"你的名字叫:{self.name},你的昵称是:{self.nick_name}\n" prompt += f"你正在参与一个qq群聊的讨论,你记得这个群之前在聊的内容是:{self.observe_info}\n" prompt += f"现在群里的群友们产生了新的讨论,有了新的发言,具体内容如下:{new_messages_str}\n" prompt += """以上是群里在进行的聊天,请你对这个聊天内容进行总结,总结内容要包含聊天的大致内容,