diff --git a/HEARFLOW_API_说明文档.md b/HEARFLOW_API_说明文档.md deleted file mode 100644 index 434cd3cfa..000000000 --- a/HEARFLOW_API_说明文档.md +++ /dev/null @@ -1,241 +0,0 @@ -# HearflowAPI 使用说明 - -## 概述 - -HearflowAPI 是一个新增的插件API模块,提供了与心流和子心流相关的操作接口。通过这个API,插件开发者可以方便地获取和操作sub_hearflow实例。 - -## 主要功能 - -### 1. 获取子心流实例 - -#### `get_sub_hearflow_by_chat_id(chat_id: str) -> Optional[SubHeartflow]` -根据chat_id获取指定的sub_hearflow实例(仅获取已存在的)。 - -**参数:** -- `chat_id`: 聊天ID,与sub_hearflow的subheartflow_id相同 - -**返回值:** -- `SubHeartflow`: sub_hearflow实例,如果不存在则返回None - -**示例:** -```python -# 获取当前聊天的子心流实例 -current_subflow = await self.get_sub_hearflow_by_chat_id(self.observation.chat_id) -if current_subflow: - print(f"找到子心流: {current_subflow.chat_id}") -else: - print("子心流不存在") -``` - -#### `get_or_create_sub_hearflow_by_chat_id(chat_id: str) -> Optional[SubHeartflow]` -根据chat_id获取或创建sub_hearflow实例。 - -**参数:** -- `chat_id`: 聊天ID - -**返回值:** -- `SubHeartflow`: sub_hearflow实例,创建失败时返回None - -**示例:** -```python -# 获取或创建子心流实例 -subflow = await self.get_or_create_sub_hearflow_by_chat_id("some_chat_id") -if subflow: - print("成功获取或创建子心流") -``` - -### 2. 获取子心流列表 - -#### `get_all_sub_hearflow_ids() -> List[str]` -获取所有活跃子心流的ID列表。 - -**返回值:** -- `List[str]`: 所有活跃子心流的ID列表 - -#### `get_all_sub_hearflows() -> List[SubHeartflow]` -获取所有活跃的子心流实例。 - -**返回值:** -- `List[SubHeartflow]`: 所有活跃的子心流实例列表 - -**示例:** -```python -# 获取所有活跃的子心流ID -all_chat_ids = self.get_all_sub_hearflow_ids() -print(f"共有 {len(all_chat_ids)} 个活跃的子心流") - -# 获取所有活跃的子心流实例 -all_subflows = self.get_all_sub_hearflows() -for subflow in all_subflows: - print(f"子心流 {subflow.chat_id} 状态: {subflow.chat_state.chat_status.value}") -``` - -### 3. 心流状态操作 - -#### `get_sub_hearflow_chat_state(chat_id: str) -> Optional[ChatState]` -获取指定子心流的聊天状态。 - -**参数:** -- `chat_id`: 聊天ID - -**返回值:** -- `ChatState`: 聊天状态,如果子心流不存在则返回None - -#### `set_sub_hearflow_chat_state(chat_id: str, target_state: ChatState) -> bool` -设置指定子心流的聊天状态。 - -**参数:** -- `chat_id`: 聊天ID -- `target_state`: 目标状态 - -**返回值:** -- `bool`: 是否设置成功 - -**示例:** -```python -from src.chat.heart_flow.sub_heartflow import ChatState - -# 获取当前状态 -current_state = await self.get_sub_hearflow_chat_state(self.observation.chat_id) -print(f"当前状态: {current_state.value}") - -# 设置状态 -success = await self.set_sub_hearflow_chat_state(self.observation.chat_id, ChatState.FOCUS) -if success: - print("状态设置成功") -``` - -### 4. Replyer和Expressor操作 - -#### `get_sub_hearflow_replyer_and_expressor(chat_id: str) -> Tuple[Optional[Any], Optional[Any]]` -根据chat_id获取指定子心流的replyer和expressor实例。 - -**参数:** -- `chat_id`: 聊天ID - -**返回值:** -- `Tuple[Optional[Any], Optional[Any]]`: (replyer实例, expressor实例),如果子心流不存在或未处于FOCUSED状态,返回(None, None) - -#### `get_sub_hearflow_replyer(chat_id: str) -> Optional[Any]` -根据chat_id获取指定子心流的replyer实例。 - -**参数:** -- `chat_id`: 聊天ID - -**返回值:** -- `Optional[Any]`: replyer实例,如果不存在则返回None - -#### `get_sub_hearflow_expressor(chat_id: str) -> Optional[Any]` -根据chat_id获取指定子心流的expressor实例。 - -**参数:** -- `chat_id`: 聊天ID - -**返回值:** -- `Optional[Any]`: expressor实例,如果不存在则返回None - -**示例:** -```python -# 获取replyer和expressor -replyer, expressor = await self.get_sub_hearflow_replyer_and_expressor(self.observation.chat_id) -if replyer and expressor: - print(f"获取到replyer: {type(replyer).__name__}") - print(f"获取到expressor: {type(expressor).__name__}") - - # 检查属性 - print(f"Replyer聊天ID: {replyer.chat_id}") - print(f"Expressor聊天ID: {expressor.chat_id}") - print(f"是否群聊: {replyer.is_group_chat}") - -# 单独获取replyer -replyer = await self.get_sub_hearflow_replyer(self.observation.chat_id) -if replyer: - print("获取到replyer实例") - -# 单独获取expressor -expressor = await self.get_sub_hearflow_expressor(self.observation.chat_id) -if expressor: - print("获取到expressor实例") -``` - -## 可用的聊天状态 - -```python -from src.chat.heart_flow.sub_heartflow import ChatState - -ChatState.FOCUS # 专注模式 -ChatState.NORMAL # 普通模式 -ChatState.ABSENT # 离开模式 -``` - -## 完整插件示例 - -```python -from typing import Tuple -from src.plugin_system.base.base_action import BaseAction as PluginAction, register_action -from src.chat.heart_flow.sub_heartflow import ChatState - -@register_action -class MyHearflowPlugin(PluginAction): - """我的心流插件""" - - activation_keywords = ["心流信息"] - - async def process(self) -> Tuple[bool, str]: - try: - # 获取当前聊天的chat_id - current_chat_id = self.observation.chat_id - - # 获取子心流实例 - subflow = await self.get_sub_hearflow_by_chat_id(current_chat_id) - if not subflow: - return False, "未找到子心流实例" - - # 获取状态信息 - current_state = await self.get_sub_hearflow_chat_state(current_chat_id) - - # 构建回复 - response = f"心流信息:\n" - response += f"聊天ID: {current_chat_id}\n" - response += f"当前状态: {current_state.value}\n" - response += f"是否群聊: {subflow.is_group_chat}\n" - - return True, response - - except Exception as e: - return False, f"处理出错: {str(e)}" -``` - -## 注意事项 - -1. **线程安全**: API内部已处理锁机制,确保线程安全。 - -2. **错误处理**: 所有API方法都包含异常处理,失败时会记录日志并返回安全的默认值。 - -3. **性能考虑**: `get_sub_hearflow_by_chat_id` 只获取已存在的实例,性能更好;`get_or_create_sub_hearflow_by_chat_id` 会在需要时创建新实例。 - -4. **状态管理**: 修改心流状态时请谨慎,确保不会影响系统的正常运行。 - -5. **日志记录**: 所有操作都会记录适当的日志,便于调试和监控。 - -6. **Replyer和Expressor可用性**: - - 这些实例仅在子心流处于**FOCUSED状态**时可用 - - 如果子心流处于NORMAL或ABSENT状态,将返回None - - 需要确保HeartFC实例存在且正常运行 - -7. **使用Replyer和Expressor时的注意事项**: - - 直接调用这些实例的方法需要谨慎,可能影响系统正常运行 - - 建议主要用于监控、信息获取和状态检查 - - 不建议在插件中直接调用回复生成方法,这可能与系统的正常流程冲突 - -## 相关类型和模块 - -- `SubHeartflow`: 子心流实例类 -- `ChatState`: 聊天状态枚举 -- `DefaultReplyer`: 默认回复器类 -- `DefaultExpressor`: 默认表达器类 -- `HeartFChatting`: 专注聊天主类 -- `src.chat.heart_flow.heartflow`: 主心流模块 -- `src.chat.heart_flow.subheartflow_manager`: 子心流管理器 -- `src.chat.focus_chat.replyer.default_replyer`: 回复器模块 -- `src.chat.focus_chat.expressors.default_expressor`: 表达器模块 \ No newline at end of file diff --git a/MaiBot插件开发文档.md b/MaiBot插件开发文档.md new file mode 100644 index 000000000..242edd8db --- /dev/null +++ b/MaiBot插件开发文档.md @@ -0,0 +1,717 @@ +# MaiBot 插件开发文档 + +## 📖 总体介绍 + +MaiBot 是一个基于大语言模型的智能聊天机器人,采用现代化的插件系统架构,支持灵活的功能扩展和定制。插件系统提供了统一的开发框架,让开发者可以轻松创建和管理各种功能组件。 + +### 🎯 插件系统特点 + +- **组件化架构**:支持Action(动作)和Command(命令)两种主要组件类型 +- **统一API接口**:提供丰富的API功能,包括消息发送、数据库操作、LLM调用等 +- **配置驱动**:支持TOML配置文件,实现灵活的参数配置 +- **热加载机制**:支持动态加载和卸载插件 +- **依赖管理**:内置依赖检查和解析机制 +- **拦截控制**:Command组件支持消息拦截控制 + +## 🧩 主要组件 + +### 1. 插件(Plugin) + +插件是功能的容器,每个插件可以包含多个组件。插件通过继承 `BasePlugin` 类实现: + +```python +from src.plugin_system import BasePlugin, register_plugin + +@register_plugin +class MyPlugin(BasePlugin): + plugin_name = "my_plugin" + plugin_description = "我的插件" + plugin_version = "1.0.0" + plugin_author = "开发者" + config_file_name = "config.toml" # 可选配置文件 +``` + +### 2. Action组件 + +Action是给麦麦在回复之外提供额外功能的组件,由麦麦的决策系统自主选择是否使用,具有随机性和拟人化的调用特点。Action不是直接响应用户命令,而是让麦麦根据聊天情境智能地选择合适的动作,使其行为更加自然和真实。 + +```python +from src.plugin_system import BaseAction, ActionActivationType, ChatMode + +class MyAction(BaseAction): + # 激活设置 - 麦麦会根据这些条件决定是否使用此Action + focus_activation_type = ActionActivationType.KEYWORD + normal_activation_type = ActionActivationType.RANDOM + activation_keywords = ["关键词1", "关键词2"] + mode_enable = ChatMode.ALL + + async def execute(self) -> Tuple[bool, str]: + # 麦麦决定使用此Action时执行的逻辑 + await self.send_text("这是麦麦主动执行的动作") + return True, "执行成功" +``` + +### 3. Command组件 + +Command是直接响应用户明确指令的组件,与Action不同,Command是被动触发的,当用户输入特定格式的命令时立即执行。Command支持正则表达式匹配和消息拦截: + +```python +from src.plugin_system import BaseCommand + +class MyCommand(BaseCommand): + command_pattern = r"^/hello\s+(?P\w+)$" + command_help = "打招呼命令" + command_examples = ["/hello 世界"] + intercept_message = True # 拦截后续处理 + + async def execute(self) -> Tuple[bool, Optional[str]]: + name = self.matched_groups.get("name", "世界") + await self.send_text(f"你好,{name}!") + return True, f"已向{name}问候" +``` + +> **Action vs Command 区别**: +> - **Action**:麦麦主动决策使用,具有随机性和智能性,让麦麦行为更拟人化 +> - **Command**:用户主动触发,确定性执行,用于提供具体功能和服务 + +## 🚀 快速开始 + +### 1. 创建插件目录 + +在项目的 `src/plugins/` 文件夹下创建你的插件目录: + +``` +src/plugins/ +└── my_plugin/ + ├── plugin.py # 插件主文件 + ├── config.toml # 配置文件(可选) + └── README.md # 说明文档(可选) +``` + +### 2. 编写插件主文件 + +创建 `plugin.py` 文件: + +```python +from typing import List, Tuple, Type +from src.plugin_system import ( + BasePlugin, register_plugin, BaseAction, BaseCommand, + ComponentInfo, ActionActivationType, ChatMode +) + +# 定义一个简单的Action +class GreetingAction(BaseAction): + focus_activation_type = ActionActivationType.KEYWORD + activation_keywords = ["你好", "hello"] + + async def execute(self) -> Tuple[bool, str]: + await self.send_text("你好!很高兴见到你!") + return True, "执行问候动作" + +# 定义一个简单的Command +class InfoCommand(BaseCommand): + command_pattern = r"^/info$" + command_help = "显示插件信息" + command_examples = ["/info"] + + async def execute(self) -> Tuple[bool, str]: + await self.send_text("这是我的第一个插件!") + return True, "显示插件信息" + +# 注册插件 +@register_plugin +class MyFirstPlugin(BasePlugin): + plugin_name = "first_plugin" + plugin_description = "我的第一个插件" + plugin_version = "1.0.0" + plugin_author = "我的名字" + + def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: + return [ + (GreetingAction.get_action_info( + name="greeting", + description="问候用户" + ), GreetingAction), + (InfoCommand.get_command_info( + name="info", + description="显示插件信息" + ), InfoCommand), + ] +``` + +### 3. 创建配置文件(可选) + +创建 `config.toml` 文件: + +```toml +[plugin] +name = "first_plugin" +version = "1.0.0" +enabled = true + +[greeting] +enable_emoji = true +custom_message = "欢迎使用我的插件!" + +[logging] +level = "INFO" +``` + +### 4. 启动机器人 + +将插件放入 `src/plugins/` 目录后,启动MaiBot,插件会自动加载。 + +## 📚 完整说明 + +### 插件生命周期 + +1. **发现阶段**:系统扫描 `src/plugins/` 目录,查找Python文件 +2. **加载阶段**:导入插件模块,注册插件类 +3. **实例化阶段**:创建插件实例,加载配置文件 +4. **注册阶段**:注册插件及其包含的组件 +5. **运行阶段**:组件根据条件被激活和执行 + +### Action组件详解 + +Action组件是麦麦智能决策系统的重要组成部分,它们不是被动响应用户输入,而是由麦麦根据聊天情境主动选择执行。这种设计使麦麦的行为更加拟人化和自然,就像真人聊天时会根据情况做出不同的反应一样。 + +#### 激活类型 + +Action的激活类型决定了麦麦在什么情况下会考虑使用该Action: + +- `NEVER`:从不激活,通常用于临时禁用 +- `ALWAYS`:麦麦总是会考虑使用此Action +- `LLM_JUDGE`:通过LLM智能判断当前情境是否适合使用 +- `RANDOM`:基于随机概率决定是否使用,增加行为的不可预测性 +- `KEYWORD`:当检测到特定关键词时会考虑使用 + +#### 聊天模式 + +- `FOCUS`:专注聊天模式 +- `NORMAL`:普通聊天模式 +- `ALL`:所有模式 + +#### Action示例 + +```python +class AdvancedAction(BaseAction): + # 激活设置 + focus_activation_type = ActionActivationType.LLM_JUDGE + normal_activation_type = ActionActivationType.KEYWORD + activation_keywords = ["帮助", "help"] + llm_judge_prompt = "当用户需要帮助时回答'是',否则回答'否'" + random_activation_probability = 0.3 + mode_enable = ChatMode.ALL + parallel_action = True + + # 动作参数(用于LLM规划) + action_parameters = { + "query": "用户的问题或需求" + } + + # 使用场景描述 + action_require = [ + "用户明确请求帮助", + "检测到用户遇到困难" + ] + + async def execute(self) -> Tuple[bool, str]: + query = self.action_data.get("query", "") + + # 麦麦主动决定帮助用户时执行的逻辑 + await self.send_text(f"我来帮助你解决:{query}") + await self.send_type("emoji", "😊") + + # 存储执行记录 + await self.api.store_action_info( + action_build_into_prompt=True, + action_prompt_display=f"麦麦主动帮助用户:{query}", + action_done=True, + thinking_id=self.thinking_id + ) + + return True, f"麦麦已主动帮助处理:{query}" +``` + +### Command组件详解 + +#### 正则表达式匹配 + +Command使用正则表达式匹配用户输入,支持命名组捕获: + +```python +class UserCommand(BaseCommand): + # 匹配 /user add 用户名 + command_pattern = r"^/user\s+(?Padd|del|info)\s+(?P\w+)$" + command_help = "用户管理命令" + command_examples = [ + "/user add 张三", + "/user del 李四", + "/user info 王五" + ] + intercept_message = True + + async def execute(self) -> Tuple[bool, str]: + action = self.matched_groups.get("action") + username = self.matched_groups.get("username") + + if action == "add": + return await self._add_user(username) + elif action == "del": + return await self._delete_user(username) + elif action == "info": + return await self._show_user_info(username) + + return False, "无效的操作" +``` + +#### 消息拦截控制 + +- `intercept_message = True`:拦截消息,不进行后续处理 +- `intercept_message = False`:不拦截,继续处理其他组件 + +### 配置系统 + +插件支持TOML配置文件,配置会自动加载到插件实例: + +```python +class ConfigurablePlugin(BasePlugin): + config_file_name = "config.toml" + + def some_method(self): + # 获取配置值,支持嵌套键访问 + max_items = self.get_config("limits.max_items", 10) + custom_message = self.get_config("messages.greeting", "默认消息") +``` + +配置文件格式: + +```toml +[limits] +max_items = 20 +timeout = 30 + +[messages] +greeting = "欢迎使用配置化插件!" +error = "操作失败" + +[features] +enable_debug = true +``` + +### 错误处理 + +插件应该包含适当的错误处理: + +```python +async def execute(self) -> Tuple[bool, str]: + try: + # 执行逻辑 + result = await self._do_something() + return True, "操作成功" + except ValueError as e: + logger.error(f"{self.log_prefix} 参数错误: {e}") + await self.send_text("参数错误,请检查输入") + return False, f"参数错误: {e}" + except Exception as e: + logger.error(f"{self.log_prefix} 执行失败: {e}") + await self.send_text("操作失败,请稍后重试") + return False, f"执行失败: {e}" +``` + +## 🔌 API说明 + +### 消息API + +插件可以通过 `self.api` 访问各种API功能: + +#### 基础消息发送 + +```python +# 发送文本消息 +await self.send_text("这是文本消息") + +# 发送特定类型消息 +await self.send_type("emoji", "😊") +await self.send_type("image", image_url) + +# 发送命令消息 +await self.send_command("命令名", {"参数": "值"}) +``` + +#### 高级消息发送 + +```python +# 向指定群聊发送消息 +await self.api.send_text_to_group("消息内容", "群ID", "qq") + +# 向指定用户发送私聊消息 +await self.api.send_text_to_user("消息内容", "用户ID", "qq") + +# 向指定目标发送任意类型消息 +await self.api.send_message_to_target( + message_type="text", + content="消息内容", + platform="qq", + target_id="目标ID", + is_group=True, + display_message="显示消息" +) +``` + +#### 消息查询 + +```python +# 获取聊天类型 +chat_type = self.api.get_chat_type() # "group" 或 "private" + +# 获取最近消息 +recent_messages = self.api.get_recent_messages(count=5) +``` + +### 数据库API + +插件可以使用数据库API进行数据持久化: + +#### 通用查询 + +```python +# 查询数据 +results = await self.api.db_query( + model_class=SomeModel, + query_type="get", + filters={"field": "value"}, + limit=10, + order_by=["-time"] +) + +# 创建记录 +new_record = await self.api.db_query( + model_class=SomeModel, + query_type="create", + data={"field1": "value1", "field2": "value2"} +) + +# 更新记录 +updated_count = await self.api.db_query( + model_class=SomeModel, + query_type="update", + filters={"id": 123}, + data={"field": "new_value"} +) + +# 删除记录 +deleted_count = await self.api.db_query( + model_class=SomeModel, + query_type="delete", + filters={"id": 123} +) + +# 计数 +count = await self.api.db_query( + model_class=SomeModel, + query_type="count", + filters={"active": True} +) +``` + +#### 原始SQL查询 + +```python +# 执行原始SQL +results = await self.api.db_raw_query( + sql="SELECT * FROM table WHERE condition = ?", + params=["value"], + fetch_results=True +) +``` + +#### Action记录存储 + +```python +# 存储Action执行记录 +await self.api.store_action_info( + action_build_into_prompt=True, + action_prompt_display="显示的动作描述", + action_done=True, + thinking_id="思考ID", + action_data={"key": "value"} +) +``` + +### LLM API + +插件可以调用大语言模型: + +```python +# 获取可用模型 +models = self.api.get_available_models() + +# 使用模型生成内容 +success, response, reasoning, model_name = await self.api.generate_with_model( + prompt="你的提示词", + model_config=models["某个模型"], + request_type="plugin.generate", + temperature=0.7, + max_tokens=1000 +) + +if success: + await self.send_text(f"AI回复:{response}") +else: + await self.send_text("AI生成失败") +``` + +### 配置API + +```python +# 获取全局配置 +global_config = self.api.get_global_config() + +# 获取插件配置 +plugin_config = self.api.get_config("section.key", "默认值") +``` + +### 工具API + +```python +# 获取当前时间戳 +timestamp = self.api.get_current_timestamp() + +# 格式化时间 +formatted_time = self.api.format_timestamp(timestamp, "%Y-%m-%d %H:%M:%S") + +# JSON处理 +json_str = self.api.dict_to_json({"key": "value"}) +data = self.api.json_to_dict(json_str) + +# 生成UUID +uuid = self.api.generate_uuid() + +# 哈希计算 +hash_value = self.api.calculate_hash("text", "md5") +``` + +### 流API + +```python +# 获取当前聊天流信息 +chat_stream = self.api.get_service("chat_stream") +if chat_stream: + stream_id = chat_stream.stream_id + platform = chat_stream.platform + + # 群聊信息 + if chat_stream.group_info: + group_id = chat_stream.group_info.group_id + group_name = chat_stream.group_info.group_name + + # 用户信息 + user_id = chat_stream.user_info.user_id + user_name = chat_stream.user_info.user_nickname +``` + +### 心流API + +```python +# 等待新消息 +has_new_message = await self.api.wait_for_new_message(timeout=30) + +# 获取观察信息 +observations = self.api.get_service("observations") +``` + +## 🔧 高级功能 + +### 插件依赖管理 + +```python +@register_plugin +class DependentPlugin(BasePlugin): + plugin_name = "dependent_plugin" + plugin_description = "依赖其他插件的插件" + dependencies = ["core_actions", "example_plugin"] # 依赖列表 + + def get_plugin_components(self): + # 只有依赖满足时才会加载 + return [...] +``` + +### 并行Action + +```python +class ParallelAction(BaseAction): + parallel_action = True # 允许与其他Action并行执行 + + async def execute(self) -> Tuple[bool, str]: + # 这个Action可以与其他并行Action同时执行 + return True, "并行执行完成" +``` + +### 动态配置更新 + +```python +class DynamicPlugin(BasePlugin): + def get_plugin_components(self): + # 根据配置动态决定加载哪些组件 + components = [] + + if self.get_config("features.enable_greeting", True): + components.append((GreetingAction.get_action_info(), GreetingAction)) + + if self.get_config("features.enable_commands", True): + components.append((SomeCommand.get_command_info(), SomeCommand)) + + return components +``` + +### 自定义元数据 + +```python +class MetadataAction(BaseAction): + @classmethod + def get_action_info(cls, name=None, description=None): + info = super().get_action_info(name, description) + # 添加自定义元数据 + info.metadata = { + "category": "utility", + "priority": "high", + "custom_field": "custom_value" + } + return info +``` + +## 📋 开发规范 + +### 1. 命名规范 + +- 插件名使用小写字母和下划线:`my_plugin` +- 类名使用大驼峰:`MyPlugin`、`GreetingAction` +- 方法名使用小写字母和下划线:`execute`、`send_message` + +### 2. 文档规范 + +- 所有插件类都应该有完整的文档字符串 +- Action和Command的描述要清晰明确 +- 提供使用示例和配置说明 + +### 3. 错误处理 + +- 所有异步操作都要包含异常处理 +- 使用日志记录错误信息 +- 向用户返回友好的错误消息 + +### 4. 配置管理 + +- 敏感配置不要硬编码在代码中 +- 提供合理的默认值 +- 支持配置热更新 + +### 5. 性能考虑 + +- 避免在初始化时执行耗时操作 +- 合理使用缓存减少重复计算 +- 及时释放不需要的资源 + +## 🎯 最佳实践 + +### 1. 插件结构 + +``` +src/plugins/my_plugin/ +├── __init__.py # 空文件或简单导入 +├── plugin.py # 主插件文件 +├── actions/ # Action组件目录 +│ ├── __init__.py +│ ├── greeting.py +│ └── helper.py +├── commands/ # Command组件目录 +│ ├── __init__.py +│ ├── admin.py +│ └── user.py +├── utils/ # 工具函数 +│ ├── __init__.py +│ └── helpers.py +├── config.toml # 配置文件 +└── README.md # 说明文档 +``` + +### 2. 模块化设计 + +```python +# actions/greeting.py +from src.plugin_system import BaseAction + +class GreetingAction(BaseAction): + # ... 实现细节 + +# commands/admin.py +from src.plugin_system import BaseCommand + +class AdminCommand(BaseCommand): + # ... 实现细节 + +# plugin.py +from .actions.greeting import GreetingAction +from .commands.admin import AdminCommand + +@register_plugin +class MyPlugin(BasePlugin): + def get_plugin_components(self): + return [ + (GreetingAction.get_action_info(), GreetingAction), + (AdminCommand.get_command_info(), AdminCommand), + ] +``` + +### 3. 配置分层 + +```toml +# config.toml +[plugin] +name = "my_plugin" +version = "1.0.0" +enabled = true + +[components] +enable_greeting = true +enable_admin = false + +[greeting] +message_template = "你好,{username}!" +enable_emoji = true + +[admin] +allowed_users = ["admin", "moderator"] +``` + +### 4. 日志实践 + +```python +from src.common.logger import get_logger + +logger = get_logger("my_plugin") + +class MyAction(BaseAction): + async def execute(self): + logger.info(f"{self.log_prefix} 开始执行动作") + + try: + # 执行逻辑 + result = await self._do_something() + logger.debug(f"{self.log_prefix} 执行结果: {result}") + return True, "成功" + except Exception as e: + logger.error(f"{self.log_prefix} 执行失败: {e}", exc_info=True) + return False, str(e) +``` + +--- + +## 🎉 总结 + +MaiBot的插件系统提供了强大而灵活的扩展能力,通过Action和Command两种组件类型,开发者可以轻松实现各种功能。系统提供了丰富的API接口、完善的配置管理和错误处理机制,让插件开发变得简单高效。 + +遵循本文档的指导和最佳实践,你可以快速上手MaiBot插件开发,为机器人添加强大的自定义功能。 + +如有问题或建议,欢迎提交Issue或参与讨论! \ No newline at end of file diff --git a/bot.py b/bot.py index bdf140fbd..f262e9398 100644 --- a/bot.py +++ b/bot.py @@ -10,7 +10,7 @@ from dotenv import load_dotenv from rich.traceback import install # 最早期初始化日志系统,确保所有后续模块都使用正确的日志格式 -from src.common.logger import initialize_logging, get_logger +from src.common.logger import initialize_logging, get_logger, shutdown_logging from src.main import MainSystem from src.manager.async_task_manager import async_task_manager @@ -130,6 +130,9 @@ async def graceful_shutdown(): logger.error(f"等待任务取消时发生异常: {e}") logger.info("麦麦优雅关闭完成") + + # 关闭日志系统,释放文件句柄 + shutdown_logging() except Exception as e: logger.error(f"麦麦关闭失败: {e}", exc_info=True) @@ -271,6 +274,13 @@ if __name__ == "__main__": if "loop" in locals() and loop and not loop.is_closed(): loop.close() logger.info("事件循环已关闭") + + # 关闭日志系统,释放文件句柄 + try: + shutdown_logging() + except Exception as e: + print(f"关闭日志系统时出错: {e}") + # 在程序退出前暂停,让你有机会看到输出 # input("按 Enter 键退出...") # <--- 添加这行 sys.exit(exit_code) # <--- 使用记录的退出码 diff --git a/docs/plugin_detailed_guide.md b/docs/plugin_detailed_guide.md deleted file mode 100644 index 1af9fee3b..000000000 --- a/docs/plugin_detailed_guide.md +++ /dev/null @@ -1,752 +0,0 @@ -# MaiBot 插件详细解析指南 - -## 📋 目录 - -1. [插件基类详解](#插件基类详解) -2. [Action组件深入](#action组件深入) -3. [Command组件深入](#command组件深入) -4. [API系统详解](#api系统详解) -5. [配置系统](#配置系统) -6. [注册中心机制](#注册中心机制) -7. [高级功能](#高级功能) -8. [最佳实践](#最佳实践) - ---- - -## 插件基类详解 - -### BasePlugin 核心功能 - -`BasePlugin` 是所有插件的基类,提供插件的生命周期管理和基础功能。 - -```python -@register_plugin -class MyPlugin(BasePlugin): - # 必需的基本信息 - plugin_name = "my_plugin" # 插件唯一标识 - plugin_description = "插件功能描述" # 简短描述 - plugin_version = "1.0.0" # 版本号 - plugin_author = "作者名称" # 作者信息 - enable_plugin = True # 是否启用 - - # 可选配置 - dependencies = ["other_plugin"] # 依赖的其他插件 - config_file_name = "config.toml" # 配置文件名 - - def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: - """返回插件包含的组件列表(必须实现)""" - return [ - (MyAction.get_action_info(), MyAction), - (MyCommand.get_command_info(), MyCommand) - ] -``` - -### 插件生命周期 - -1. **加载阶段** - 插件管理器扫描插件目录 -2. **实例化阶段** - 创建插件实例,传入 `plugin_dir` -3. **配置加载** - 自动加载配置文件(如果指定) -4. **依赖检查** - 验证依赖的插件是否存在 -5. **组件注册** - 注册所有组件到注册中心 -6. **运行阶段** - 组件响应用户交互 - -### 配置访问 - -```python -class MyPlugin(BasePlugin): - config_file_name = "config.toml" - - def some_method(self): - # 获取配置值 - max_retry = self.get_config("network.max_retry", 3) - api_key = self.get_config("api.key", "") - - # 配置支持嵌套结构 - db_config = self.get_config("database", {}) -``` - ---- - -## Action组件深入 - -### Action激活机制 - -Action组件支持多种激活方式,可以组合使用: - -#### 1. 关键词激活 - -```python -class KeywordAction(BaseAction): - focus_activation_type = ActionActivationType.KEYWORD - normal_activation_type = ActionActivationType.KEYWORD - activation_keywords = ["天气", "weather", "温度"] - keyword_case_sensitive = False # 是否区分大小写 - - async def execute(self) -> Tuple[bool, str]: - # 获取触发的关键词 - triggered_keyword = self.action_data.get("triggered_keyword") - return True, f"检测到关键词: {triggered_keyword}" -``` - -#### 2. LLM智能判断 - -```python -class SmartAction(BaseAction): - focus_activation_type = ActionActivationType.LLM_JUDGE - llm_judge_prompt = """ - 判断用户消息是否表达了情感支持的需求。 - 如果用户显得沮丧、焦虑或需要安慰,返回True,否则返回False。 - """ - - async def execute(self) -> Tuple[bool, str]: - # LLM判断为需要情感支持 - user_emotion = self.action_data.get("emotion", "neutral") - return True, "我理解你现在的感受,有什么可以帮助你的吗? 🤗" -``` - -#### 3. 随机激活 - -```python -class RandomAction(BaseAction): - focus_activation_type = ActionActivationType.RANDOM - random_activation_probability = 0.1 # 10%概率触发 - - async def execute(self) -> Tuple[bool, str]: - import random - responses = ["今天天气不错呢!", "你知道吗,刚才想到一个有趣的事...", "随便聊聊吧!"] - return True, random.choice(responses) -``` - -#### 4. 始终激活 - -```python -class AlwaysAction(BaseAction): - focus_activation_type = ActionActivationType.ALWAYS - parallel_action = True # 允许与其他Action并行 - - async def execute(self) -> Tuple[bool, str]: - # 记录所有消息到数据库 - await self.api.store_user_data("last_message", self.action_data.get("message")) - return True, "" # 静默执行,不发送回复 -``` - -### Action数据访问 - -```python -class DataAction(BaseAction): - async def execute(self) -> Tuple[bool, str]: - # 访问消息数据 - message = self.action_data.get("message", "") - username = self.action_data.get("username", "用户") - user_id = self.action_data.get("user_id", "") - platform = self.action_data.get("platform", "") - - # 访问系统数据 - thinking_id = self.thinking_id - reasoning = self.reasoning # 执行该动作的理由 - - # 访问计时器信息 - timers = self.cycle_timers - - return True, f"处理来自 {platform} 的用户 {username} 的消息" -``` - -### 聊天模式支持 - -```python -class ModeAwareAction(BaseAction): - mode_enable = ChatMode.PRIVATE # 只在私聊中启用 - # mode_enable = ChatMode.GROUP # 只在群聊中启用 - # mode_enable = ChatMode.ALL # 在所有模式中启用 - - async def execute(self) -> Tuple[bool, str]: - current_mode = self.action_data.get("chat_mode", ChatMode.PRIVATE) - return True, f"当前聊天模式: {current_mode.name}" -``` - ---- - -## Command组件深入 - -### 高级正则表达式模式 - -Command使用正则表达式进行精确匹配,支持复杂的参数提取: - -#### 1. 基础命令 - -```python -class BasicCommand(BaseCommand): - command_pattern = r"^/hello$" - command_help = "简单的问候命令" - - async def execute(self) -> Tuple[bool, Optional[str]]: - await self.send_reply("Hello!") - return True, "Hello!" -``` - -#### 2. 带参数命令 - -```python -class ParameterCommand(BaseCommand): - command_pattern = r"^/user\s+(?Padd|remove|list)\s+(?P\w+)?$" - command_help = "用户管理命令,用法:/user [用户名]" - command_examples = ["/user add alice", "/user remove bob", "/user list"] - - async def execute(self) -> Tuple[bool, Optional[str]]: - action = self.matched_groups.get("action") - name = self.matched_groups.get("name") - - if action == "add" and name: - # 添加用户逻辑 - await self.api.store_user_data(f"user_{name}", {"name": name, "created": self.api.get_current_time()}) - response = f"用户 {name} 已添加" - elif action == "remove" and name: - # 删除用户逻辑 - await self.api.delete_user_data(f"user_{name}") - response = f"用户 {name} 已删除" - elif action == "list": - # 列出用户逻辑 - users = await self.api.get_user_data_pattern("user_*") - response = f"用户列表: {', '.join(users.keys())}" - else: - response = "参数错误,请查看帮助信息" - - await self.send_reply(response) - return True, response -``` - -#### 3. 复杂参数解析 - -```python -class AdvancedCommand(BaseCommand): - command_pattern = r"^/remind\s+(?P