diff --git a/MaiBot插件开发文档.md b/MaiBot插件开发文档.md index 3c98a9bf2..85a30f573 100644 --- a/MaiBot插件开发文档.md +++ b/MaiBot插件开发文档.md @@ -12,896 +12,83 @@ MaiBot 是一个基于大语言模型的智能聊天机器人,采用现代化 - **热加载机制**:支持动态加载和卸载插件 - **依赖管理**:内置依赖检查和解析机制 - **拦截控制**:Command组件支持消息拦截控制 +- **双目录支持**:区分用户插件和系统内置插件 -## 🧩 主要组件 +### 📂 插件目录说明 -### 1. 插件(Plugin) +> ⚠️ **重要**:请将你的自定义插件放在项目根目录的 `plugins/` 文件夹下! -插件是功能的容器,每个插件可以包含多个组件。插件通过继承 `BasePlugin` 类实现: +MaiBot支持两个插件目录: -```python -from src.plugin_system import BasePlugin, register_plugin +- **`plugins/`** (项目根目录):**用户自定义插件目录**,这是你应该放置插件的位置 +- **`src/plugins/builtin/`**:**系统内置插件目录**,包含核心功能插件,请勿修改 -@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的核心概念 +### 🚀 快速入门 +- [🚀 快速开始指南](docs/plugins/quick-start.md) - 5分钟创建你的第一个插件 +- [📋 开发规范](docs/plugins/development-standards.md) - 代码规范和最佳实践 -Action是给麦麦在回复之外提供额外功能的智能组件,**由麦麦的决策系统自主选择是否使用**,具有随机性和拟人化的调用特点。Action不是直接响应用户命令,而是让麦麦根据聊天情境智能地选择合适的动作,使其行为更加自然和真实。 +### 📖 核心概念 +- [🧩 插件系统概述](docs/plugins/plugin-system-overview.md) - 插件架构和组件类型 +- [⚡ Action组件详解](docs/plugins/action-components.md) - 智能动作组件开发指南 +- [💻 Command组件详解](docs/plugins/command-components.md) - 命令组件开发指南 +- [🔧 工具系统详解](docs/plugins/tool-system.md) - 扩展麦麦信息获取能力的工具组件 -**Action的特点**: -- 🧠 **智能激活**:麦麦根据多种条件智能判断是否使用 -- 🎲 **随机性**:增加行为的不可预测性,更接近真人交流 -- 🤖 **拟人化**:让麦麦的回应更自然、更有个性 -- 🔄 **情境感知**:基于聊天上下文做出合适的反应 +### 🔌 API参考 +- [📡 消息API](docs/plugins/api/message-api.md) - 消息发送和处理接口 +- [💾 数据库API](docs/plugins/api/database-api.md) - 数据持久化接口 +- [🤖 LLM API](docs/plugins/api/llm-api.md) - 大语言模型调用接口 +- [⚙️ 配置API](docs/plugins/api/config-api.md) - 配置管理接口 +- [🛠️ 工具API](docs/plugins/api/utility-api.md) - 实用工具接口 -#### Action的两层决策机制 +### 🔧 高级功能 +- [🔗 插件依赖管理](docs/plugins/advanced/dependency-management.md) - 插件间依赖处理 +- [⏱️ 插件生命周期](docs/plugins/advanced/plugin-lifecycle.md) - 加载、执行、卸载流程 +- [🎛️ 动态配置](docs/plugins/advanced/dynamic-configuration.md) - 运行时配置更新 -Action采用**两层决策机制**来优化性能和决策质量: +### 📋 开发指南 +- [🏗️ 项目结构](docs/plugins/guides/project-structure.md) - 推荐的插件项目结构 +- [🐛 调试和测试](docs/plugins/guides/debugging-testing.md) - 插件调试技巧 +- [📊 性能优化](docs/plugins/guides/performance-optimization.md) - 插件性能最佳实践 -##### 第一层:激活控制(Activation Control) -**激活决定麦麦是否"知道"这个Action的存在**,即这个Action是否进入决策候选池。**不被激活的Action麦麦永远不会选择**。 +### 💡 示例和模板 +- [📚 完整示例](docs/plugins/examples/complete-examples.md) - 各种类型的插件示例 +- [🏗️ 插件模板](docs/plugins/templates/) - 快速开始的插件模板 -> 🎯 **设计目的**:在加载许多插件的时候降低LLM决策压力,避免让麦麦在过多的选项中纠结。 +## 🎯 推荐学习路径 -**激活类型说明**: -- `NEVER`:从不激活,Action对麦麦不可见 -- `ALWAYS`:永远激活,Action总是在麦麦的候选池中 -- `LLM_JUDGE`:通过LLM智能判断当前情境是否需要激活此Action -- `RANDOM`:基于随机概率决定是否激活 -- `KEYWORD`:当检测到特定关键词时激活 +### 🌟 新手入门 +1. 阅读 [🚀 快速开始指南](docs/plugins/quick-start.md) +2. 了解 [🧩 插件系统概述](docs/plugins/plugin-system-overview.md) +3. 学习 [⚡ Action组件详解](docs/plugins/action-components.md) +4. 了解 [🔧 工具系统详解](docs/plugins/tool-system.md) +5. 实践 [📚 完整示例](docs/plugins/examples/complete-examples.md) -**聊天模式控制**: -- `FOCUS`:仅在专注聊天模式下可激活 -- `NORMAL`:仅在普通聊天模式下可激活 -- `ALL`:所有模式下都可激活 +### 💪 进阶开发 +1. 掌握 [💻 Command组件详解](docs/plugins/command-components.md) +2. 学习 [🔌 API参考](docs/plugins/api/) 各模块 +3. 了解 [🔧 高级功能](docs/plugins/advanced/) +4. 遵循 [📋 开发规范](docs/plugins/development-standards.md) -##### 第二层:使用决策(Usage Decision) -**在Action被激活后,使用条件决定麦麦什么时候会"选择"使用这个Action**。 +### 🚀 专家级别 +1. 深入 [🔗 插件依赖管理](docs/plugins/advanced/dependency-management.md) +2. 掌握 [📊 性能优化](docs/plugins/guides/performance-optimization.md) +3. 贡献 [🏗️ 插件模板](docs/plugins/templates/) -这一层由以下因素综合决定: -- `action_require`:使用场景描述,帮助LLM判断何时选择 -- `action_parameters`:所需参数,影响Action的可执行性 -- 当前聊天上下文和麦麦的决策逻辑 +## 🎉 快速开始 -##### 两层决策机制示例 +想立即开始开发?跳转到 [🚀 快速开始指南](docs/plugins/quick-start.md),5分钟内创建你的第一个MaiBot插件! -假设有一个"发送表情"Action: +## 💬 社区和支持 -```python -class EmojiAction(BaseAction): - # 第一层:激活控制 - focus_activation_type = ActionActivationType.RANDOM # 专注模式下随机激活 - normal_activation_type = ActionActivationType.KEYWORD # 普通模式下关键词激活 - activation_keywords = ["表情", "emoji", "😊"] - - # 第二层:使用决策 - action_require = [ - "表达情绪时可以选择使用", - "增加聊天趣味性", - "不要连续发送多个表情" - ] -``` - -**决策流程**: -1. **第一层激活判断**: - - 普通模式:只有当用户消息包含"表情"、"emoji"或"😊"时,麦麦才"知道"可以使用这个Action - - 专注模式:随机激活,有概率让麦麦"看到"这个Action - -2. **第二层使用决策**: - - 即使Action被激活,麦麦还会根据`action_require`中的条件判断是否真正选择使用 - - 例如:如果刚刚已经发过表情,根据"不要连续发送多个表情"的要求,麦麦可能不会选择这个Action - -> 💡 **性能优化**:这种设计确保了当插件数量很多时,LLM只需要在少数被激活的Action中做选择,而不是在所有Action中纠结。 - -#### Action必须项清单 - -每个Action类都**必须**包含以下属性,缺少任何一项都可能导致Action无法正常工作: - -##### 1. 激活控制必须项(第一层决策) -```python -# 专注模式下的激活类型 - 控制何时让麦麦"看到"这个Action -focus_activation_type = ActionActivationType.LLM_JUDGE - -# 普通模式下的激活类型 - 控制何时让麦麦"看到"这个Action -normal_activation_type = ActionActivationType.KEYWORD - -# 启用的聊天模式 - 限制Action在哪些模式下可激活 -mode_enable = ChatMode.ALL - -# 是否允许与其他Action并行执行 -parallel_action = False -``` - -##### 2. 基本信息必须项 -```python -# Action的唯一标识名称 -action_name = "my_action" - -# Action的功能描述 -action_description = "描述这个Action的具体功能和用途" -``` - -##### 3. 使用决策必须项(第二层决策) -```python -# Action参数定义 - 告诉LLM执行时需要什么参数 -action_parameters = { - "param1": "参数1的说明", - "param2": "参数2的说明" -} - -# Action使用场景描述 - 帮助LLM判断何时"选择"使用 -action_require = [ - "使用场景描述1", - "使用场景描述2" -] - -# 关联的消息类型 - 说明Action能处理什么类型的内容 -associated_types = ["text", "emoji", "image"] -``` - -#### 完整的Action示例 - -```python -from src.plugin_system import BaseAction, ActionActivationType, ChatMode - -class GreetingAction(BaseAction): - # ===== 激活控制必须项 ===== - focus_activation_type = ActionActivationType.LLM_JUDGE - normal_activation_type = ActionActivationType.KEYWORD - mode_enable = ChatMode.ALL - parallel_action = False - - # ===== 基本信息必须项 ===== - action_name = "greeting" - action_description = "向用户发送问候消息,增加互动友好性" - - # 关键词配置(用于KEYWORD激活类型) - activation_keywords = ["你好", "hello", "hi"] - keyword_case_sensitive = False - - # LLM判断提示词(用于LLM_JUDGE激活类型) - llm_judge_prompt = """ - 判定是否需要使用问候动作的条件: - 1. 用户刚加入群聊或开始对话 - 2. 用户表达了友好的问候意图 - 3. 适合增加互动友好性的场合 - - 请回答"是"或"否"。 - """ - - # ===== 功能定义必须项 ===== - action_parameters = { - "greeting_type": "问候类型,如formal(正式)或casual(随意)", - "target_user": "问候的目标用户,可选" - } - - action_require = [ - "用户刚进入聊天", - "检测到问候关键词", - "适合增加友好互动的场合" - ] - - associated_types = ["text", "emoji"] - - async def execute(self) -> Tuple[bool, str]: - # 麦麦决定使用此Action时执行的逻辑 - greeting_type = self.action_data.get("greeting_type", "casual") - target_user = self.action_data.get("target_user", "") - - if greeting_type == "formal": - message = f"您好{target_user}!很高兴见到您!" - else: - message = f"嗨{target_user}~很开心见到你!😊" - - await self.send_text(message) - 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**: -> - 采用**两层决策机制**(激活控制 + 使用决策) -> - 麦麦主动决策使用,具有随机性和智能性 -> - 需要通过激活控制来管理LLM的决策负担 -> - **必须在类中定义所有必须项** -> - **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 - normal_activation_type = ActionActivationType.KEYWORD - mode_enable = ChatMode.ALL - parallel_action = False - - # 基本信息必须项 - action_name = "greeting" - action_description = "向用户发送问候消息" - - # 关键词配置 - activation_keywords = ["你好", "hello"] - keyword_case_sensitive = False - - # 功能定义必须项 - action_parameters = {} - action_require = ["用户发送问候语时使用"] - associated_types = ["text"] - - 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 [ - # Action组件 - 所有信息从类属性读取 - (GreetingAction.get_action_info(), GreetingAction), - # Command组件 - 仍需要手动指定name和description - (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 - mode_enable = ChatMode.ALL - parallel_action = True - - # ===== 基本信息必须项 ===== - action_name = "advanced_help" - action_description = "智能帮助系统,主动为用户提供帮助和指导" - - # 关键词配置 - activation_keywords = ["帮助", "help"] - keyword_case_sensitive = False - - # LLM判断提示词 - llm_judge_prompt = "当用户需要帮助时回答'是',否则回答'否'" - - # 随机激活概率 - random_activation_probability = 0.3 - - # ===== 功能定义必须项 ===== - action_parameters = { - "query": "用户的问题或需求" - } - - action_require = [ - "用户明确请求帮助", - "检测到用户遇到困难" - ] - - associated_types = ["text", "emoji"] - - 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) -``` +- 📖 **文档问题**:如果发现文档错误或需要改进,请提交Issue +- 🐛 **Bug报告**:在GitHub上报告插件系统相关的问题 +- 💡 **功能建议**:欢迎提出新功能建议和改进意见 +- 🤝 **贡献代码**:欢迎提交PR改进插件系统 --- -## 🎉 总结 - -MaiBot的插件系统提供了强大而灵活的扩展能力,通过Action和Command两种组件类型,开发者可以轻松实现各种功能。系统提供了丰富的API接口、完善的配置管理和错误处理机制,让插件开发变得简单高效。 - -遵循本文档的指导和最佳实践,你可以快速上手MaiBot插件开发,为机器人添加强大的自定义功能。 - -如有问题或建议,欢迎提交Issue或参与讨论! \ No newline at end of file +**开始你的MaiBot插件开发之旅吧!** 🚀 \ No newline at end of file diff --git a/docs/0.6Bing.md b/docs/Bing.md similarity index 65% rename from docs/0.6Bing.md rename to docs/Bing.md index 80a29a84d..5836b157a 100644 --- a/docs/0.6Bing.md +++ b/docs/Bing.md @@ -1,12 +1,3 @@ -- **智能化 MaiState 状态转换**: - - 当前 `MaiState` (整体状态,如 `OFFLINE`, `NORMAL_CHAT` 等) 的转换逻辑 (`MaiStateManager`) 较为简单,主要依赖时间和随机性。 - - 未来的计划是让主心流 (`Heartflow`) 负责决策自身的 `MaiState`。 - - 该决策将综合考虑以下信息: - - 各个子心流 (`SubHeartflow`) 的活动状态和信息摘要。 - - 主心流自身的状态和历史信息。 - - (可能) 结合预设的日程安排 (Schedule) 信息。 - - 目标是让 Mai 的整体状态变化更符合逻辑和上下文。 (计划在 064 实现) - - **参数化与动态调整聊天行为**: - 将 `NormalChatInstance` 和 `HeartFlowChatInstance` 中的关键行为参数(例如:回复概率、思考频率、兴趣度阈值、状态转换条件等)提取出来,使其更易于配置。 - 允许每个 `SubHeartflow` (即每个聊天场景) 拥有其独立的参数配置,实现"千群千面"。 @@ -33,12 +24,6 @@ - 管理日程或执行更复杂的分析任务。 - 目标:提升 HFC 的自主决策和行动能力,即使会增加一定的延迟。 -- **基于历史学习的行为模式应用**: - - **学习**: 分析过往聊天记录,提取和学习具体的行为模式(如特定梗的用法、情境化回应风格等)。可能需要专门的分析模块。 - - **存储与匹配**: 需要有效的方法存储学习到的行为模式,并开发强大的 **匹配** 机制,在运行时根据当前情境检索最合适的模式。**(匹配的准确性是关键)** - - **应用与评估**: 将匹配到的行为模式融入 HFC 的决策和回复生成(例如,将其整合进 Prompt)。之后需评估该行为模式应用的实际效果。 - - **人格塑造**: 通过学习到的实际行为来动态塑造人格,作为静态人设描述的补充或替代,使其更生动自然。 - - **标准化人设生成 (Standardized Persona Generation)**: - **目标**: 解决手动配置 `人设` 文件缺乏标准、难以全面描述个性的问题,并生成更丰富、可操作的人格资源。 - **方法**: 利用大型语言模型 (LLM) 辅助生成标准化的、结构化的人格**资源包**。 @@ -57,23 +42,10 @@ - 考虑引入基于事件关联、相对时间线索和绝对时间锚点的检索方式。 - 可能涉及设计新的事件表示或记忆结构。 - -- **实现 SubHeartflow 级记忆缓存池:** - - 在 `SubHeartflow` 层级或更高层级设计并实现一个缓存池,存储已检索的记忆/信息。 - - 避免在 HFC 等循环中重复进行相同的记忆检索调用。 - - 确保存储的信息能有效服务于当前交互上下文。 - - **基于人格生成预设知识:** - 开发利用 LLM 和人格配置生成背景知识的功能。 - 这些知识应符合角色的行为风格和可能的经历。 - 作为一种"冷启动"或丰富角色深度的方式。 -## 开发计划TODO:LIST - -- 人格功能:WIP -- 对特定对象的侧写功能 -- 图片发送,转发功能:WIP -- 幽默和meme功能:WIP -- 小程序转发链接解析 -- 自动生成的回复逻辑,例如自生成的回复方向,回复风格 \ No newline at end of file +1.更nb的工作记忆,直接开一个play_ground,通过llm进行内容检索,这个play_ground可以容纳巨量信息,并且十分通用化,十分好。 \ No newline at end of file diff --git a/docs/plugins/action-components.md b/docs/plugins/action-components.md new file mode 100644 index 000000000..3bdbdf0b6 --- /dev/null +++ b/docs/plugins/action-components.md @@ -0,0 +1,634 @@ +# ⚡ Action组件详解 + +## 📖 什么是Action + +Action是给麦麦在回复之外提供额外功能的智能组件,**由麦麦的决策系统自主选择是否使用**,具有随机性和拟人化的调用特点。Action不是直接响应用户命令,而是让麦麦根据聊天情境智能地选择合适的动作,使其行为更加自然和真实。 + +### 🎯 Action的特点 + +- 🧠 **智能激活**:麦麦根据多种条件智能判断是否使用 +- 🎲 **随机性**:增加行为的不可预测性,更接近真人交流 +- 🤖 **拟人化**:让麦麦的回应更自然、更有个性 +- 🔄 **情境感知**:基于聊天上下文做出合适的反应 + +## 🎯 两层决策机制 + +Action采用**两层决策机制**来优化性能和决策质量: + +### 第一层:激活控制(Activation Control) + +**激活决定麦麦是否"知道"这个Action的存在**,即这个Action是否进入决策候选池。**不被激活的Action麦麦永远不会选择**。 + +> 🎯 **设计目的**:在加载许多插件的时候降低LLM决策压力,避免让麦麦在过多的选项中纠结。 + +#### 激活类型说明 + +| 激活类型 | 说明 | 使用场景 | +|---------|-----|---------| +| `NEVER` | 从不激活,Action对麦麦不可见 | 临时禁用某个Action | +| `ALWAYS` | 永远激活,Action总是在麦麦的候选池中 | 核心功能,如回复、表情 | +| `LLM_JUDGE` | 通过LLM智能判断当前情境是否需要激活此Action | 需要智能判断的复杂场景 | +| `RANDOM` | 基于随机概率决定是否激活 | 增加行为随机性的功能 | +| `KEYWORD` | 当检测到特定关键词时激活 | 明确触发条件的功能 | + +#### 聊天模式控制 + +| 模式 | 说明 | +|-----|-----| +| `ChatMode.FOCUS` | 仅在专注聊天模式下可激活 | +| `ChatMode.NORMAL` | 仅在普通聊天模式下可激活 | +| `ChatMode.ALL` | 所有模式下都可激活 | + +### 第二层:使用决策(Usage Decision) + +**在Action被激活后,使用条件决定麦麦什么时候会"选择"使用这个Action**。 + +这一层由以下因素综合决定: +- `action_require`:使用场景描述,帮助LLM判断何时选择 +- `action_parameters`:所需参数,影响Action的可执行性 +- 当前聊天上下文和麦麦的决策逻辑 + +### 🎬 决策流程示例 + +假设有一个"发送表情"Action: + +```python +class EmojiAction(BaseAction): + # 第一层:激活控制 + focus_activation_type = ActionActivationType.RANDOM # 专注模式下随机激活 + normal_activation_type = ActionActivationType.KEYWORD # 普通模式下关键词激活 + activation_keywords = ["表情", "emoji", "😊"] + + # 第二层:使用决策 + action_require = [ + "表达情绪时可以选择使用", + "增加聊天趣味性", + "不要连续发送多个表情" + ] +``` + +**决策流程**: +1. **第一层激活判断**: + - 普通模式:只有当用户消息包含"表情"、"emoji"或"😊"时,麦麦才"知道"可以使用这个Action + - 专注模式:随机激活,有概率让麦麦"看到"这个Action + +2. **第二层使用决策**: + - 即使Action被激活,麦麦还会根据`action_require`中的条件判断是否真正选择使用 + - 例如:如果刚刚已经发过表情,根据"不要连续发送多个表情"的要求,麦麦可能不会选择这个Action + +## 📋 Action必须项清单 + +每个Action类都**必须**包含以下属性: + +### 1. 激活控制必须项 + +```python +# 专注模式下的激活类型 +focus_activation_type = ActionActivationType.LLM_JUDGE + +# 普通模式下的激活类型 +normal_activation_type = ActionActivationType.KEYWORD + +# 启用的聊天模式 +mode_enable = ChatMode.ALL + +# 是否允许与其他Action并行执行 +parallel_action = False +``` + +### 2. 基本信息必须项 + +```python +# Action的唯一标识名称 +action_name = "my_action" + +# Action的功能描述 +action_description = "描述这个Action的具体功能和用途" +``` + +### 3. 功能定义必须项 + +```python +# Action参数定义 - 告诉LLM执行时需要什么参数 +action_parameters = { + "param1": "参数1的说明", + "param2": "参数2的说明" +} + +# Action使用场景描述 - 帮助LLM判断何时"选择"使用 +action_require = [ + "使用场景描述1", + "使用场景描述2" +] + +# 关联的消息类型 - 说明Action能处理什么类型的内容 +associated_types = ["text", "emoji", "image"] +``` + +## 🔧 激活类型详解 + +### KEYWORD激活 + +当检测到特定关键词时激活Action: + +```python +class GreetingAction(BaseAction): + focus_activation_type = ActionActivationType.KEYWORD + normal_activation_type = ActionActivationType.KEYWORD + + # 关键词配置 + activation_keywords = ["你好", "hello", "hi", "嗨"] + keyword_case_sensitive = False # 不区分大小写 +``` + +### LLM_JUDGE激活 + +通过LLM智能判断是否激活: + +```python +class HelpAction(BaseAction): + focus_activation_type = ActionActivationType.LLM_JUDGE + normal_activation_type = ActionActivationType.LLM_JUDGE + + # LLM判断提示词 + llm_judge_prompt = """ + 判定是否需要使用帮助动作的条件: + 1. 用户表达了困惑或需要帮助 + 2. 用户提出了问题但没有得到满意答案 + 3. 对话中出现了技术术语或复杂概念 + + 请回答"是"或"否"。 + """ +``` + +### RANDOM激活 + +基于随机概率激活: + +```python +class SurpriseAction(BaseAction): + focus_activation_type = ActionActivationType.RANDOM + normal_activation_type = ActionActivationType.RANDOM + + # 随机激活概率 + random_activation_probability = 0.1 # 10%概率激活 +``` + +### ALWAYS/NEVER激活 + +```python +class CoreAction(BaseAction): + focus_activation_type = ActionActivationType.ALWAYS # 总是激活 + normal_activation_type = ActionActivationType.NEVER # 在普通模式下禁用 +``` + +## 🎨 完整Action示例 + +### 智能问候Action + +```python +from src.plugin_system import BaseAction, ActionActivationType, ChatMode + +class SmartGreetingAction(BaseAction): + """智能问候Action - 展示关键词激活的完整示例""" + + # ===== 激活控制必须项 ===== + focus_activation_type = ActionActivationType.KEYWORD + normal_activation_type = ActionActivationType.KEYWORD + mode_enable = ChatMode.ALL + parallel_action = False + + # ===== 基本信息必须项 ===== + action_name = "smart_greeting" + action_description = "智能问候系统,基于关键词触发,支持个性化问候消息" + + # 关键词配置 + activation_keywords = ["你好", "hello", "hi", "嗨", "问候", "早上好", "晚上好"] + keyword_case_sensitive = False + + # ===== 功能定义必须项 ===== + action_parameters = { + "username": "要问候的用户名(可选)", + "greeting_style": "问候风格:casual(随意)、formal(正式)、friendly(友好)" + } + + action_require = [ + "用户发送包含问候词汇的消息时使用", + "检测到新用户加入时使用", + "响应友好交流需求时使用", + "避免在短时间内重复问候同一用户" + ] + + associated_types = ["text", "emoji"] + + async def execute(self) -> Tuple[bool, str]: + """执行智能问候""" + # 获取参数 + username = self.action_data.get("username", "") + greeting_style = self.action_data.get("greeting_style", "casual") + + # 根据风格生成问候消息 + if greeting_style == "formal": + message = f"您好{username}!很荣幸为您服务!" + emoji = "🙏" + elif greeting_style == "friendly": + message = f"你好{username}!欢迎来到这里,希望我们能成为好朋友!" + emoji = "😊" + else: # casual + message = f"嗨{username}!很开心见到你~" + emoji = "👋" + + # 发送消息 + await self.send_text(message) + await self.send_type("emoji", emoji) + + return True, f"向{username or '用户'}发送了{greeting_style}风格的问候" +``` + +### 智能禁言Action + +以下是一个真实的群管理禁言Action示例,展示了LLM判断、参数验证、配置管理等高级功能: + +```python +from typing import Optional +import random +from src.plugin_system.base.base_action import BaseAction +from src.plugin_system.base.component_types import ActionActivationType, ChatMode + +class MuteAction(BaseAction): + """智能禁言Action - 基于LLM智能判断是否需要禁言""" + + # ===== 激活控制必须项 ===== + focus_activation_type = ActionActivationType.LLM_JUDGE # Focus模式使用LLM判定 + normal_activation_type = ActionActivationType.KEYWORD # Normal模式使用关键词 + mode_enable = ChatMode.ALL + parallel_action = False + + # ===== 基本信息必须项 ===== + action_name = "mute" + action_description = "智能禁言系统,基于LLM判断是否需要禁言" + + # ===== 激活配置 ===== + # 关键词设置(用于Normal模式) + activation_keywords = ["禁言", "mute", "ban", "silence"] + keyword_case_sensitive = False + + # LLM判定提示词(用于Focus模式) + llm_judge_prompt = """ +判定是否需要使用禁言动作的严格条件: + +使用禁言的情况: +1. 用户发送明显违规内容(色情、暴力、政治敏感等) +2. 恶意刷屏或垃圾信息轰炸 +3. 用户主动明确要求被禁言("禁言我"等) +4. 严重违反群规的行为 +5. 恶意攻击他人或群组管理 + +绝对不要使用的情况: +1. 正常聊天和交流 +2. 情绪化表达但无恶意 +3. 开玩笑或调侃,除非过分 +4. 单纯的意见分歧或争论 +""" + + # ===== 功能定义必须项 ===== + action_parameters = { + "target": "禁言对象,必填,输入你要禁言的对象的名字,请仔细思考不要弄错禁言对象", + "duration": "禁言时长,必填,输入你要禁言的时长(秒),单位为秒,必须为数字", + "reason": "禁言理由,可选", + } + + action_require = [ + "当有人违反了公序良俗的内容", + "当有人刷屏时使用", + "当有人发了擦边,或者色情内容时使用", + "当有人要求禁言自己时使用", + "如果某人已经被禁言了,就不要再次禁言了,除非你想追加时间!!", + ] + + associated_types = ["text", "command"] + + async def execute(self) -> Tuple[bool, Optional[str]]: + """执行智能禁言判定""" + # 获取参数 + target = self.action_data.get("target") + duration = self.action_data.get("duration") + reason = self.action_data.get("reason", "违反群规") + + # 参数验证 + if not target: + await self.send_text("没有指定禁言对象呢~") + return False, "禁言目标不能为空" + + if not duration: + await self.send_text("没有指定禁言时长呢~") + return False, "禁言时长不能为空" + + # 获取时长限制配置 + min_duration = self.api.get_config("mute.min_duration", 60) + max_duration = self.api.get_config("mute.max_duration", 2592000) + + # 验证时长格式并转换 + try: + duration_int = int(duration) + if duration_int <= 0: + await self.send_text("禁言时长必须是正数哦~") + return False, "禁言时长必须大于0" + + # 限制禁言时长范围 + if duration_int < min_duration: + duration_int = min_duration + elif duration_int > max_duration: + duration_int = max_duration + + except (ValueError, TypeError): + await self.send_text("禁言时长必须是数字哦~") + return False, f"禁言时长格式无效: {duration}" + + # 获取用户ID + try: + platform, user_id = await self.api.get_user_id_by_person_name(target) + except Exception as e: + await self.send_text("查找用户信息时出现问题~") + return False, f"查找用户ID时出错: {e}" + + if not user_id: + await self.send_text(f"找不到 {target} 这个人呢~") + return False, f"未找到用户 {target} 的ID" + + # 格式化时长显示 + time_str = self._format_duration(duration_int) + + # 获取模板化消息 + message = self._get_template_message(target, time_str, reason) + await self.send_message_by_expressor(message) + + # 发送群聊禁言命令 + success = await self.send_command( + command_name="GROUP_BAN", + args={"qq_id": str(user_id), "duration": str(duration_int)}, + display_message=f"禁言了 {target} {time_str}", + ) + + if success: + return True, f"成功禁言 {target},时长 {time_str}" + else: + await self.send_text("执行禁言动作失败") + return False, "发送禁言命令失败" + + def _get_template_message(self, target: str, duration_str: str, reason: str) -> str: + """获取模板化的禁言消息""" + templates = self.api.get_config( + "mute.templates", + [ + "好的,禁言 {target} {duration},理由:{reason}", + "收到,对 {target} 执行禁言 {duration},因为{reason}", + "明白了,禁言 {target} {duration},原因是{reason}", + "哇哈哈哈哈哈,已禁言 {target} {duration},理由:{reason}", + ], + ) + template = random.choice(templates) + return template.format(target=target, duration=duration_str, reason=reason) + + def _format_duration(self, seconds: int) -> str: + """将秒数格式化为可读的时间字符串""" + if seconds < 60: + return f"{seconds}秒" + elif seconds < 3600: + minutes = seconds // 60 + remaining_seconds = seconds % 60 + if remaining_seconds > 0: + return f"{minutes}分{remaining_seconds}秒" + else: + return f"{minutes}分钟" + else: + hours = seconds // 3600 + remaining_minutes = (seconds % 3600) // 60 + if remaining_minutes > 0: + return f"{hours}小时{remaining_minutes}分钟" + else: + return f"{hours}小时" +``` + +**关键特性说明**: + +1. **🎯 双模式激活**:Focus模式使用LLM_JUDGE更谨慎,Normal模式使用KEYWORD快速响应 +2. **🧠 严格的LLM判定**:详细提示词指导LLM何时应该/不应该使用禁言,避免误判 +3. **✅ 完善的参数验证**:验证必需参数、数值转换、用户ID查找等多重验证 +4. **⚙️ 配置驱动**:时长限制、消息模板等都可通过配置文件自定义 +5. **😊 友好的用户反馈**:错误提示清晰、随机化消息模板、时长格式化显示 +6. **🛡️ 安全措施**:严格权限控制、防误操作验证、完整错误处理 + +### 智能助手Action + +```python +class IntelligentHelpAction(BaseAction): + """智能助手Action - 展示LLM判断激活的完整示例""" + + # ===== 激活控制必须项 ===== + focus_activation_type = ActionActivationType.LLM_JUDGE + normal_activation_type = ActionActivationType.RANDOM + mode_enable = ChatMode.ALL + parallel_action = True + + # ===== 基本信息必须项 ===== + action_name = "intelligent_help" + action_description = "智能助手,主动提供帮助和建议" + + # LLM判断提示词 + llm_judge_prompt = """ + 判定是否需要提供智能帮助的条件: + 1. 用户表达了困惑或需要帮助 + 2. 对话中出现了技术问题 + 3. 用户寻求解决方案或建议 + 4. 适合提供额外信息的场合 + + 不要使用的情况: + 1. 用户明确表示不需要帮助 + 2. 对话进行得很顺利 + 3. 刚刚已经提供过帮助 + + 请回答"是"或"否"。 + """ + + # 随机激活概率 + random_activation_probability = 0.15 + + # ===== 功能定义必须项 ===== + action_parameters = { + "help_type": "帮助类型:explanation(解释)、suggestion(建议)、guidance(指导)", + "topic": "帮助主题或用户关心的问题", + "urgency": "紧急程度:low(低)、medium(中)、high(高)" + } + + action_require = [ + "用户表达困惑或寻求帮助时使用", + "检测到用户遇到技术问题时使用", + "对话中出现知识盲点时主动提供帮助", + "避免过度频繁地提供帮助,要恰到好处" + ] + + associated_types = ["text", "emoji"] + + async def execute(self) -> Tuple[bool, str]: + """执行智能帮助""" + # 获取参数 + help_type = self.action_data.get("help_type", "suggestion") + topic = self.action_data.get("topic", "") + urgency = self.action_data.get("urgency", "medium") + + # 根据帮助类型和紧急程度生成消息 + if help_type == "explanation": + message = f"关于{topic},让我来为你解释一下..." + elif help_type == "guidance": + message = f"在{topic}方面,我可以为你提供一些指导..." + else: # suggestion + message = f"针对{topic},我建议你可以尝试以下方法..." + + # 根据紧急程度调整表情 + if urgency == "high": + emoji = "🚨" + elif urgency == "low": + emoji = "💡" + else: + emoji = "🤔" + + # 发送帮助消息 + await self.send_text(message) + await self.send_type("emoji", emoji) + + return True, f"提供了{help_type}类型的帮助,主题:{topic}" +``` + +## 📊 性能优化建议 + +### 1. 合理使用激活类型 + +- **ALWAYS**: 仅用于核心功能 +- **LLM_JUDGE**: 适度使用,避免过多LLM调用 +- **KEYWORD**: 优选,性能最好 +- **RANDOM**: 控制概率,避免过于频繁 + +### 2. 优化execute方法 + +```python +async def execute(self) -> Tuple[bool, str]: + try: + # 快速参数验证 + if not self._validate_parameters(): + return False, "参数验证失败" + + # 核心逻辑 + result = await self._core_logic() + + # 成功返回 + return True, "执行成功" + + except Exception as e: + logger.error(f"{self.log_prefix} 执行失败: {e}") + return False, f"执行失败: {str(e)}" +``` + +### 3. 合理设置并行执行 + +```python +# 轻量级Action可以并行 +parallel_action = True # 如:发送表情、记录日志 + +# 重要Action应该独占 +parallel_action = False # 如:回复消息、状态切换 +``` + +## 🐛 调试技巧 + +### 1. 日志记录 + +```python +from src.common.logger import get_logger + +logger = get_logger("my_action") + +async def execute(self) -> Tuple[bool, str]: + logger.info(f"{self.log_prefix} 开始执行: {self.reasoning}") + logger.debug(f"{self.log_prefix} 参数: {self.action_data}") + + # 执行逻辑... + + logger.info(f"{self.log_prefix} 执行完成") +``` + +### 2. 激活状态检查 + +```python +# 在execute方法中检查激活原因 +def _debug_activation(self): + logger.debug(f"激活类型: Focus={self.focus_activation_type}, Normal={self.normal_activation_type}") + logger.debug(f"当前模式: {self.api.get_chat_mode()}") + logger.debug(f"激活原因: {self.reasoning}") +``` + +### 3. 参数验证 + +```python +def _validate_parameters(self) -> bool: + required_params = ["param1", "param2"] + for param in required_params: + if param not in self.action_data: + logger.warning(f"{self.log_prefix} 缺少必需参数: {param}") + return False + return True +``` + +## 🎯 最佳实践 + +### 1. 清晰的Action命名 + +- 使用描述性的类名:`SmartGreetingAction` 而不是 `Action1` +- action_name要简洁明确:`"smart_greeting"` 而不是 `"action_1"` + +### 2. 完整的文档字符串 + +```python +class MyAction(BaseAction): + """ + 我的Action - 一句话描述功能 + + 详细描述Action的用途、激活条件、执行逻辑等。 + + 激活条件: + - Focus模式:关键词激活 + - Normal模式:LLM判断激活 + + 执行逻辑: + 1. 验证参数 + 2. 生成响应 + 3. 发送消息 + """ +``` + +### 3. 错误处理 + +```python +async def execute(self) -> Tuple[bool, str]: + try: + # 主要逻辑 + pass + except ValueError as e: + await self.send_text("参数错误,请检查输入") + return False, f"参数错误: {e}" + except Exception as e: + await self.send_text("操作失败,请稍后重试") + return False, f"执行失败: {e}" +``` + +### 4. 配置驱动 + +```python +# 从配置文件读取设置 +enable_feature = self.api.get_config("my_action.enable_feature", True) +max_retries = self.api.get_config("my_action.max_retries", 3) +``` + +--- + +🎉 **现在你已经掌握了Action组件开发的完整知识!继续学习 [Command组件详解](command-components.md) 来了解命令开发。** \ No newline at end of file diff --git a/docs/plugins/api/message-api.md b/docs/plugins/api/message-api.md new file mode 100644 index 000000000..0d84708df --- /dev/null +++ b/docs/plugins/api/message-api.md @@ -0,0 +1,411 @@ +# 📡 消息API + +## 📖 概述 + +消息API提供了发送各种类型消息的接口,支持文本、表情、图片等多种消息类型,以及向不同目标发送消息的功能。 + +## 🔄 基础消息发送 + +### 发送文本消息 + +```python +# 发送普通文本消息 +await self.send_text("这是一条文本消息") + +# 发送多行文本 +message = """ +这是第一行 +这是第二行 +这是第三行 +""" +await self.send_text(message.strip()) +``` + +### 发送特定类型消息 + +```python +# 发送表情 +await self.send_type("emoji", "😊") + +# 发送图片 +await self.send_type("image", "https://example.com/image.jpg") + +# 发送音频 +await self.send_type("audio", "audio_file_path") +``` + +### 发送命令消息 + +```python +# 发送命令类型的消息 +await self.send_command("system_command", {"param": "value"}) +``` + +## 🎯 目标消息发送 + +### 向指定群聊发送消息 + +```python +# 向指定群聊发送文本消息 +success = await self.api.send_text_to_group( + text="这是发送到群聊的消息", + group_id="123456789", + platform="qq" +) + +if success: + print("消息发送成功") +else: + print("消息发送失败") +``` + +### 向指定用户发送私聊消息 + +```python +# 向指定用户发送私聊消息 +success = await self.api.send_text_to_user( + text="这是私聊消息", + user_id="987654321", + platform="qq" +) +``` + +### 通用目标消息发送 + +```python +# 向任意目标发送任意类型消息 +success = await self.api.send_message_to_target( + message_type="text", # 消息类型 + content="消息内容", # 消息内容 + platform="qq", # 平台 + target_id="123456789", # 目标ID + is_group=True, # 是否为群聊 + display_message="显示消息" # 可选:显示消息 +) +``` + +## 📨 消息类型支持 + +### 支持的消息类型 + +| 类型 | 说明 | 示例 | +|-----|------|------| +| `text` | 普通文本消息 | "Hello World" | +| `emoji` | 表情消息 | "😊" | +| `image` | 图片消息 | 图片URL或路径 | +| `audio` | 音频消息 | 音频文件路径 | +| `video` | 视频消息 | 视频文件路径 | +| `file` | 文件消息 | 文件路径 | + +### 消息类型示例 + +```python +# 文本消息 +await self.send_type("text", "普通文本") + +# 表情消息 +await self.send_type("emoji", "🎉") + +# 图片消息 +await self.send_type("image", "/path/to/image.jpg") + +# 音频消息 +await self.send_type("audio", "/path/to/audio.mp3") + +# 文件消息 +await self.send_type("file", "/path/to/document.pdf") +``` + +## 🔍 消息查询 + +### 获取聊天类型 + +```python +# 获取当前聊天类型 +chat_type = self.api.get_chat_type() + +if chat_type == "group": + print("当前是群聊") +elif chat_type == "private": + print("当前是私聊") +``` + +### 获取最近消息 + +```python +# 获取最近的5条消息 +recent_messages = self.api.get_recent_messages(count=5) + +for message in recent_messages: + print(f"用户: {message.user_nickname}") + print(f"内容: {message.processed_plain_text}") + print(f"时间: {message.timestamp}") +``` + +### 获取当前消息信息 + +```python +# 在Action或Command中获取当前处理的消息 +current_message = self.message + +# 消息基本信息 +user_id = current_message.message_info.user_info.user_id +user_nickname = current_message.message_info.user_info.user_nickname +message_content = current_message.processed_plain_text +timestamp = current_message.timestamp + +# 群聊信息(如果是群聊) +if current_message.message_info.group_info: + group_id = current_message.message_info.group_info.group_id + group_name = current_message.message_info.group_info.group_name +``` + +## 🌐 平台支持 + +### 支持的平台 + +| 平台 | 标识 | 说明 | +|-----|------|------| +| QQ | `qq` | QQ聊天平台 | +| 微信 | `wechat` | 微信聊天平台 | +| Discord | `discord` | Discord聊天平台 | + +### 平台特定功能 + +```python +# 获取当前平台 +current_platform = self.api.get_current_platform() + +# 根据平台调整消息格式 +if current_platform == "qq": + # QQ平台特定处理 + await self.send_text("[QQ] 消息内容") +elif current_platform == "wechat": + # 微信平台特定处理 + await self.send_text("【微信】消息内容") +``` + +## 🎨 消息格式化 + +### Markdown支持 + +```python +# 发送Markdown格式的消息(如果平台支持) +markdown_message = """ +**粗体文本** +*斜体文本* +`代码块` +[链接](https://example.com) +""" + +await self.send_text(markdown_message) +``` + +### 消息模板 + +```python +# 使用模板生成消息 +def format_user_info(username: str, level: int, points: int) -> str: + return f""" +👤 用户信息 +━━━━━━━━━━━━━━━━━━ +📛 用户名: {username} +⭐ 等级: Lv.{level} +💰 积分: {points:,} +━━━━━━━━━━━━━━━━━━ + """.strip() + +# 使用模板 +user_info = format_user_info("张三", 15, 12580) +await self.send_text(user_info) +``` + +### 表情和Unicode + +```python +# 发送Unicode表情 +await self.send_text("消息发送成功 ✅") + +# 发送表情包 +await self.send_type("emoji", "🎉") + +# 组合文本和表情 +await self.send_text("恭喜你完成任务!🎊🎉") +``` + +## 🔄 流式消息 + +### 获取聊天流信息 + +```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 + print(f"当前群聊: {group_name} ({group_id})") + + # 用户信息 + user_id = chat_stream.user_info.user_id + user_name = chat_stream.user_info.user_nickname + print(f"当前用户: {user_name} ({user_id})") +``` + +## 🚨 错误处理 + +### 消息发送错误处理 + +```python +async def safe_send_message(self, content: str) -> bool: + """安全发送消息,包含错误处理""" + try: + await self.send_text(content) + return True + except Exception as e: + logger.error(f"消息发送失败: {e}") + # 发送错误提示 + try: + await self.send_text("❌ 消息发送失败,请稍后重试") + except: + pass # 避免循环错误 + return False +``` + +### 目标消息发送错误处理 + +```python +async def send_to_group_safely(self, text: str, group_id: str) -> bool: + """安全向群聊发送消息""" + try: + success = await self.api.send_text_to_group( + text=text, + group_id=group_id, + platform="qq" + ) + + if not success: + logger.warning(f"向群聊 {group_id} 发送消息失败") + + return success + + except Exception as e: + logger.error(f"向群聊发送消息异常: {e}") + return False +``` + +## 📊 最佳实践 + +### 1. 消息长度控制 + +```python +async def send_long_message(self, content: str, max_length: int = 500): + """发送长消息,自动分段""" + if len(content) <= max_length: + await self.send_text(content) + else: + # 分段发送 + parts = [content[i:i+max_length] for i in range(0, len(content), max_length)] + for i, part in enumerate(parts): + prefix = f"[{i+1}/{len(parts)}] " if len(parts) > 1 else "" + await self.send_text(f"{prefix}{part}") + + # 避免发送过快 + if i < len(parts) - 1: + await asyncio.sleep(0.5) +``` + +### 2. 消息格式规范 + +```python +class MessageFormatter: + """消息格式化工具类""" + + @staticmethod + def success(message: str) -> str: + return f"✅ {message}" + + @staticmethod + def error(message: str) -> str: + return f"❌ {message}" + + @staticmethod + def warning(message: str) -> str: + return f"⚠️ {message}" + + @staticmethod + def info(message: str) -> str: + return f"ℹ️ {message}" + +# 使用示例 +await self.send_text(MessageFormatter.success("操作成功完成")) +await self.send_text(MessageFormatter.error("操作失败,请重试")) +``` + +### 3. 异步消息处理 + +```python +async def batch_send_messages(self, messages: List[str]): + """批量发送消息""" + tasks = [] + + for message in messages: + task = self.send_text(message) + tasks.append(task) + + # 并发发送,但控制并发数 + semaphore = asyncio.Semaphore(3) # 最多3个并发 + + async def send_with_limit(message): + async with semaphore: + await self.send_text(message) + + await asyncio.gather(*[send_with_limit(msg) for msg in messages]) +``` + +### 4. 消息缓存 + +```python +class MessageCache: + """消息缓存管理""" + + def __init__(self): + self._cache = {} + self._max_size = 100 + + def get_cached_message(self, key: str) -> Optional[str]: + return self._cache.get(key) + + def cache_message(self, key: str, message: str): + if len(self._cache) >= self._max_size: + # 删除最旧的缓存 + oldest_key = next(iter(self._cache)) + del self._cache[oldest_key] + + self._cache[key] = message + +# 使用缓存避免重复生成消息 +cache = MessageCache() + +async def send_user_info(self, user_id: str): + cache_key = f"user_info_{user_id}" + cached_message = cache.get_cached_message(cache_key) + + if cached_message: + await self.send_text(cached_message) + else: + # 生成新消息 + message = await self._generate_user_info(user_id) + cache.cache_message(cache_key, message) + await self.send_text(message) +``` + +--- + +🎉 **现在你已经掌握了消息API的完整用法!继续学习其他API接口。** \ No newline at end of file diff --git a/docs/plugins/command-components.md b/docs/plugins/command-components.md new file mode 100644 index 000000000..659156f45 --- /dev/null +++ b/docs/plugins/command-components.md @@ -0,0 +1,560 @@ +# 💻 Command组件详解 + +## 📖 什么是Command + +Command是直接响应用户明确指令的组件,与Action不同,Command是**被动触发**的,当用户输入特定格式的命令时立即执行。Command通过正则表达式匹配用户输入,提供确定性的功能服务。 + +### 🎯 Command的特点 + +- 🎯 **确定性执行**:匹配到命令立即执行,无随机性 +- ⚡ **即时响应**:用户主动触发,快速响应 +- 🔍 **正则匹配**:通过正则表达式精确匹配用户输入 +- 🛑 **拦截控制**:可以控制是否阻止消息继续处理 +- 📝 **参数解析**:支持从用户输入中提取参数 + +## 🆚 Action vs Command 核心区别 + +| 特征 | Action | Command | +|-----|-------|---------| +| **触发方式** | 麦麦主动决策使用 | 用户主动触发 | +| **决策机制** | 两层决策(激活+使用) | 直接匹配执行 | +| **随机性** | 有随机性和智能性 | 确定性执行 | +| **用途** | 增强麦麦行为拟人化 | 提供具体功能服务 | +| **性能影响** | 需要LLM决策 | 正则匹配,性能好 | + +## 🏗️ Command基本结构 + +### 必须属性 + +```python +from src.plugin_system import BaseCommand + +class MyCommand(BaseCommand): + # 正则表达式匹配模式 + command_pattern = r"^/help\s+(?P\w+)$" + + # 命令帮助说明 + command_help = "显示指定主题的帮助信息" + + # 使用示例 + command_examples = ["/help action", "/help command"] + + # 是否拦截后续处理 + intercept_message = True + + async def execute(self) -> Tuple[bool, Optional[str]]: + """执行命令逻辑""" + # 命令执行逻辑 + return True, "执行成功" +``` + +### 属性说明 + +| 属性 | 类型 | 说明 | +|-----|------|------| +| `command_pattern` | str | 正则表达式匹配模式 | +| `command_help` | str | 命令帮助说明 | +| `command_examples` | List[str] | 使用示例列表 | +| `intercept_message` | bool | 是否拦截消息继续处理 | + +## 🔍 正则表达式匹配 + +### 基础匹配 + +```python +class SimpleCommand(BaseCommand): + # 匹配 /ping + command_pattern = r"^/ping$" + + async def execute(self) -> Tuple[bool, Optional[str]]: + await self.send_text("Pong!") + return True, "发送了Pong回复" +``` + +### 参数捕获 + +使用命名组 `(?Ppattern)` 捕获参数: + +```python +class UserCommand(BaseCommand): + # 匹配 /user add 张三 或 /user del 李四 + command_pattern = r"^/user\s+(?Padd|del|info)\s+(?P\w+)$" + + async def execute(self) -> Tuple[bool, Optional[str]]: + # 通过 self.matched_groups 获取捕获的参数 + action = self.matched_groups.get("action") + username = self.matched_groups.get("username") + + if action == "add": + await self.send_text(f"添加用户:{username}") + elif action == "del": + await self.send_text(f"删除用户:{username}") + elif action == "info": + await self.send_text(f"用户信息:{username}") + + return True, f"执行了{action}操作" +``` + +### 可选参数 + +```python +class HelpCommand(BaseCommand): + # 匹配 /help 或 /help topic + command_pattern = r"^/help(?:\s+(?P\w+))?$" + + async def execute(self) -> Tuple[bool, Optional[str]]: + topic = self.matched_groups.get("topic") + + if topic: + await self.send_text(f"显示{topic}的帮助") + else: + await self.send_text("显示总体帮助") + + return True, "显示了帮助信息" +``` + +## 🛑 拦截控制详解 + +### 拦截消息 (intercept_message = True) + +```python +class AdminCommand(BaseCommand): + command_pattern = r"^/admin\s+.+" + command_help = "管理员命令" + intercept_message = True # 拦截,不继续处理 + + async def execute(self) -> Tuple[bool, Optional[str]]: + # 执行管理操作 + await self.send_text("执行管理命令") + # 消息不会继续传递给其他组件 + return True, "管理命令执行完成" +``` + +### 不拦截消息 (intercept_message = False) + +```python +class LogCommand(BaseCommand): + command_pattern = r"^/log\s+.+" + command_help = "记录日志" + intercept_message = False # 不拦截,继续处理 + + async def execute(self) -> Tuple[bool, Optional[str]]: + # 记录日志但不阻止后续处理 + await self.send_text("已记录到日志") + # 消息会继续传递,可能触发Action等其他组件 + return True, "日志记录完成" +``` + +### 拦截控制的用途 + +| 场景 | intercept_message | 说明 | +|-----|------------------|------| +| 系统命令 | True | 防止命令被当作普通消息处理 | +| 查询命令 | True | 直接返回结果,无需后续处理 | +| 日志命令 | False | 记录但允许消息继续流转 | +| 监控命令 | False | 监控但不影响正常聊天 | + +## 🎨 完整Command示例 + +### 用户管理Command + +```python +from src.plugin_system import BaseCommand +from typing import Tuple, Optional + +class UserManagementCommand(BaseCommand): + """用户管理Command - 展示复杂参数处理""" + + command_pattern = r"^/user\s+(?Padd|del|list|info)\s*(?P\w+)?(?:\s+--(?P.+))?$" + command_help = "用户管理命令,支持添加、删除、列表、信息查询" + command_examples = [ + "/user add 张三", + "/user del 李四", + "/user list", + "/user info 王五", + "/user add 赵六 --role=admin" + ] + intercept_message = True + + async def execute(self) -> Tuple[bool, Optional[str]]: + """执行用户管理命令""" + try: + action = self.matched_groups.get("action") + username = self.matched_groups.get("username") + options = self.matched_groups.get("options") + + # 解析选项 + parsed_options = self._parse_options(options) if options else {} + + if action == "add": + return await self._add_user(username, parsed_options) + elif action == "del": + return await self._delete_user(username) + elif action == "list": + return await self._list_users() + elif action == "info": + return await self._show_user_info(username) + else: + await self.send_text("❌ 不支持的操作") + return False, f"不支持的操作: {action}" + + except Exception as e: + await self.send_text(f"❌ 命令执行失败: {str(e)}") + return False, f"执行失败: {e}" + + def _parse_options(self, options_str: str) -> dict: + """解析命令选项""" + options = {} + if options_str: + for opt in options_str.split(): + if "=" in opt: + key, value = opt.split("=", 1) + options[key] = value + return options + + async def _add_user(self, username: str, options: dict) -> Tuple[bool, str]: + """添加用户""" + if not username: + await self.send_text("❌ 请指定用户名") + return False, "缺少用户名参数" + + # 检查用户是否已存在 + existing_users = await self._get_user_list() + if username in existing_users: + await self.send_text(f"❌ 用户 {username} 已存在") + return False, f"用户已存在: {username}" + + # 添加用户逻辑 + role = options.get("role", "user") + await self.send_text(f"✅ 成功添加用户 {username},角色: {role}") + return True, f"添加用户成功: {username}" + + async def _delete_user(self, username: str) -> Tuple[bool, str]: + """删除用户""" + if not username: + await self.send_text("❌ 请指定用户名") + return False, "缺少用户名参数" + + await self.send_text(f"✅ 用户 {username} 已删除") + return True, f"删除用户成功: {username}" + + async def _list_users(self) -> Tuple[bool, str]: + """列出所有用户""" + users = await self._get_user_list() + if users: + user_list = "\n".join([f"• {user}" for user in users]) + await self.send_text(f"📋 用户列表:\n{user_list}") + else: + await self.send_text("📋 暂无用户") + return True, "显示用户列表" + + async def _show_user_info(self, username: str) -> Tuple[bool, str]: + """显示用户信息""" + if not username: + await self.send_text("❌ 请指定用户名") + return False, "缺少用户名参数" + + # 模拟用户信息 + user_info = f""" +👤 用户信息: {username} +📧 邮箱: {username}@example.com +🕒 注册时间: 2024-01-01 +🎯 角色: 普通用户 + """.strip() + + await self.send_text(user_info) + return True, f"显示用户信息: {username}" + + async def _get_user_list(self) -> list: + """获取用户列表(示例)""" + return ["张三", "李四", "王五"] +``` + +### 系统信息Command + +```python +class SystemInfoCommand(BaseCommand): + """系统信息Command - 展示系统查询功能""" + + command_pattern = r"^/(?:status|info)(?:\s+(?Psystem|memory|plugins|all))?$" + command_help = "查询系统状态信息" + command_examples = [ + "/status", + "/info system", + "/status memory", + "/info plugins" + ] + intercept_message = True + + async def execute(self) -> Tuple[bool, Optional[str]]: + """执行系统信息查询""" + info_type = self.matched_groups.get("type", "all") + + try: + if info_type in ["system", "all"]: + await self._show_system_info() + + if info_type in ["memory", "all"]: + await self._show_memory_info() + + if info_type in ["plugins", "all"]: + await self._show_plugin_info() + + return True, f"显示了{info_type}类型的系统信息" + + except Exception as e: + await self.send_text(f"❌ 获取系统信息失败: {str(e)}") + return False, f"查询失败: {e}" + + async def _show_system_info(self): + """显示系统信息""" + import platform + import datetime + + system_info = f""" +🖥️ **系统信息** +📱 平台: {platform.system()} {platform.release()} +🐍 Python: {platform.python_version()} +⏰ 运行时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} + """.strip() + + await self.send_text(system_info) + + async def _show_memory_info(self): + """显示内存信息""" + import psutil + + memory = psutil.virtual_memory() + memory_info = f""" +💾 **内存信息** +📊 总内存: {memory.total // (1024**3)} GB +🟢 可用内存: {memory.available // (1024**3)} GB +📈 使用率: {memory.percent}% + """.strip() + + await self.send_text(memory_info) + + async def _show_plugin_info(self): + """显示插件信息""" + # 通过API获取插件信息 + plugins = await self._get_loaded_plugins() + + plugin_info = f""" +🔌 **插件信息** +📦 已加载插件: {len(plugins)} +🔧 活跃插件: {len([p for p in plugins if p.get('active', False)])} + """.strip() + + await self.send_text(plugin_info) + + async def _get_loaded_plugins(self) -> list: + """获取已加载的插件列表""" + # 这里可以通过self.api获取实际的插件信息 + return [ + {"name": "core_actions", "active": True}, + {"name": "example_plugin", "active": True}, + ] +``` + +### 自定义前缀Command + +```python +class CustomPrefixCommand(BaseCommand): + """自定义前缀Command - 展示非/前缀的命令""" + + # 使用!前缀而不是/前缀 + command_pattern = r"^[!!](?Proll|dice)\s*(?P\d+)?$" + command_help = "骰子命令,使用!前缀" + command_examples = ["!roll", "!dice 6", "!roll 20"] + intercept_message = True + + async def execute(self) -> Tuple[bool, Optional[str]]: + """执行骰子命令""" + import random + + command = self.matched_groups.get("command") + count = int(self.matched_groups.get("count", "6")) + + # 限制骰子面数 + if count > 100: + await self.send_text("❌ 骰子面数不能超过100") + return False, "骰子面数超限" + + result = random.randint(1, count) + await self.send_text(f"🎲 投掷{count}面骰子,结果: {result}") + + return True, f"投掷了{count}面骰子,结果{result}" +``` + +## 📊 性能优化建议 + +### 1. 正则表达式优化 + +```python +# ✅ 好的做法 - 简单直接 +command_pattern = r"^/ping$" + +# ❌ 避免 - 过于复杂 +command_pattern = r"^/(?:ping|pong|test|check|status|info|help|...)" + +# ✅ 好的做法 - 分离复杂逻辑 +class PingCommand(BaseCommand): + command_pattern = r"^/ping$" + +class StatusCommand(BaseCommand): + command_pattern = r"^/status$" +``` + +### 2. 参数验证 + +```python +async def execute(self) -> Tuple[bool, Optional[str]]: + # 快速参数验证 + username = self.matched_groups.get("username") + if not username or len(username) < 2: + await self.send_text("❌ 用户名不合法") + return False, "参数验证失败" + + # 主要逻辑 + ... +``` + +### 3. 异常处理 + +```python +async def execute(self) -> Tuple[bool, Optional[str]]: + try: + # 命令逻辑 + result = await self._do_command() + return True, "执行成功" + except ValueError as e: + await self.send_text(f"❌ 参数错误: {e}") + return False, f"参数错误: {e}" + except Exception as e: + logger.error(f"{self.log_prefix} 命令执行失败: {e}") + await self.send_text("❌ 命令执行失败") + return False, f"执行失败: {e}" +``` + +## 🐛 调试技巧 + +### 1. 正则测试 + +```python +import re + +pattern = r"^/user\s+(?Padd|del)\s+(?P\w+)$" +test_inputs = [ + "/user add 张三", + "/user del 李四", + "/user info 王五", # 不匹配 +] + +for input_text in test_inputs: + match = re.match(pattern, input_text) + print(f"'{input_text}' -> {match.groupdict() if match else 'No match'}") +``` + +### 2. 参数调试 + +```python +async def execute(self) -> Tuple[bool, Optional[str]]: + # 调试输出 + logger.debug(f"匹配组: {self.matched_groups}") + logger.debug(f"原始消息: {self.message.processed_plain_text}") + + # 命令逻辑... +``` + +### 3. 拦截测试 + +```python +# 测试不同的拦截设置 +intercept_message = True # 测试拦截 +intercept_message = False # 测试不拦截 + +# 观察后续Action是否被触发 +``` + +## 🎯 最佳实践 + +### 1. 命令设计原则 + +```python +# ✅ 好的命令设计 +"/user add 张三" # 动作 + 对象 + 参数 +"/config set key=value" # 动作 + 子动作 + 参数 +"/help command" # 动作 + 可选参数 + +# ❌ 避免的设计 +"/add_user_with_name_张三" # 过于冗长 +"/u a 张三" # 过于简写 +``` + +### 2. 帮助信息 + +```python +class WellDocumentedCommand(BaseCommand): + command_pattern = r"^/example\s+(?P\w+)$" + command_help = "示例命令:处理指定参数并返回结果" + command_examples = [ + "/example test", + "/example debug", + "/example production" + ] +``` + +### 3. 错误处理 + +```python +async def execute(self) -> Tuple[bool, Optional[str]]: + param = self.matched_groups.get("param") + + # 参数验证 + if param not in ["test", "debug", "production"]: + await self.send_text("❌ 无效的参数,支持: test, debug, production") + return False, "无效参数" + + # 执行逻辑 + try: + result = await self._process_param(param) + await self.send_text(f"✅ 处理完成: {result}") + return True, f"处理{param}成功" + except Exception as e: + await self.send_text("❌ 处理失败,请稍后重试") + return False, f"处理失败: {e}" +``` + +### 4. 配置集成 + +```python +async def execute(self) -> Tuple[bool, Optional[str]]: + # 从配置读取设置 + max_items = self.api.get_config("command.max_items", 10) + timeout = self.api.get_config("command.timeout", 30) + + # 使用配置进行处理 + ... +``` + +## 📝 Command vs Action 选择指南 + +### 使用Command的场景 + +- ✅ 用户需要明确调用特定功能 +- ✅ 需要精确的参数控制 +- ✅ 管理和配置操作 +- ✅ 查询和信息显示 +- ✅ 系统维护命令 + +### 使用Action的场景 + +- ✅ 增强麦麦的智能行为 +- ✅ 根据上下文自动触发 +- ✅ 情绪和表情表达 +- ✅ 智能建议和帮助 +- ✅ 随机化的互动 + +--- + +🎉 **现在你已经掌握了Command组件开发的完整知识!继续学习 [API参考](api/) 来了解所有可用的接口。** \ No newline at end of file diff --git a/docs/plugins/development-standards.md b/docs/plugins/development-standards.md new file mode 100644 index 000000000..0ef18fcf7 --- /dev/null +++ b/docs/plugins/development-standards.md @@ -0,0 +1,412 @@ +# 📋 开发标准规范 + +## 🎯 概述 + +本文档定义了MaiBot插件开发的标准规范,包括Action组件、Command组件和Tool组件的开发规范,确保代码质量、可维护性和性能。 + +## 🧩 组件开发规范 + +### Tool组件开发 + +**工具基本要求**: +- 继承 `BaseTool` 基类 +- 定义唯一的工具名称 +- 提供清晰的功能描述 +- 使用JSONSchema定义参数 +- 实现 `execute` 异步方法 +- 使用 `register_tool()` 注册 + +**工具开发模板**: +```python +from src.tools.tool_can_use.base_tool import BaseTool, register_tool + +class MyTool(BaseTool): + """工具类文档字符串""" + + name = "my_tool" + description = "详细的工具功能描述,告诉LLM这个工具的用途" + + parameters = { + "type": "object", + "properties": { + "param": { + "type": "string", + "description": "参数详细描述" + } + }, + "required": ["param"] + } + + async def execute(self, function_args, message_txt=""): + """执行工具逻辑 + + Args: + function_args: 工具调用参数 + message_txt: 原始消息文本 + + Returns: + dict: 包含name和content字段的结果 + """ + # 实现工具功能逻辑 + result = "处理结果" + + return { + "name": self.name, + "content": result + } + +# 注册工具 +register_tool(MyTool) +``` + +**工具命名规范**: +- 使用描述性的英文名称 +- 采用下划线命名法(snake_case) +- 体现工具的核心功能 +- 避免过于简短或复杂的名称 + +**示例**: +```python +# ✅ 好的命名 +name = "weather_query" # 天气查询 +name = "knowledge_search" # 知识搜索 +name = "stock_price_check" # 股价检查 + +# ❌ 避免的命名 +name = "tool1" # 无意义 +name = "wq" # 过于简短 +name = "weather_and_news" # 功能复杂 +``` + +### Action组件开发 + +**Action必需字段检查表**: + +**激活控制字段**: +- ✅ `activation_type`:激活类型(KEYWORD/LLM_JUDGE/RANDOM/ALWAYS/NEVER) +- ✅ `activation_config`:激活配置参数 + +**基本信息字段**: +- ✅ `name`:Action唯一标识名称 +- ✅ `description`:功能描述 +- ✅ `usage_tip`:使用提示 + +**功能定义字段**: +- ✅ `func`:执行函数 +- ✅ `llm_function_tips`:LLM调用提示 + +**Action开发模板**: +```python +from src.plugin_system.base_actions import BaseAction + +class MyAction(BaseAction): + """Action类文档字符串""" + + # 激活控制 + activation_type = "KEYWORD" # 或 LLM_JUDGE/RANDOM/ALWAYS/NEVER + activation_config = { + "keywords": ["关键词1", "关键词2"], + "priority": 1 + } + + # 基本信息 + name = "my_action" + description = "Action功能描述" + usage_tip = "使用场景和方法提示" + + # 功能定义 + func = "执行函数名" + llm_function_tips = "告诉LLM何时以及如何使用这个Action" + + async def 执行函数名(self, message_txt, sender_name, chat_stream): + """Action执行逻辑""" + # 实现Action功能 + await chat_stream.send_message("执行结果") +``` + +**激活类型使用规范**: +- `KEYWORD`:适用于有明确关键词的功能,性能最优 +- `LLM_JUDGE`:适用于需要智能判断的复杂场景 +- `RANDOM`:适用于随机触发的功能 +- `ALWAYS`:适用于总是可用的基础功能 +- `NEVER`:适用于临时禁用的功能 + +### Command组件开发 + +**Command开发模板**: +```python +from src.plugin_system.base_commands import BaseCommand + +class MyCommand(BaseCommand): + """Command类文档字符串""" + + # 命令基本信息 + command_name = "my_command" + description = "命令功能描述" + usage = "/my_command <参数> - 命令使用说明" + + # 匹配模式 + pattern = r"^/my_command\s+(.*)" + + async def execute(self, match, message_txt, sender_name, chat_stream): + """Command执行逻辑""" + params = match.group(1) if match.group(1) else "" + + # 实现命令功能 + await chat_stream.send_message(f"命令执行结果: {params}") +``` + +## 📝 代码结构标准 + +### 文件组织结构 + +``` +plugins/my_plugin/ +├── __init__.py # 插件入口 +├── plugin.py # 插件主文件 +├── config.toml # 插件配置 +├── actions/ # Action组件目录 +│ ├── __init__.py +│ └── my_action.py +├── commands/ # Command组件目录 +│ ├── __init__.py +│ └── my_command.py +├── utils/ # 工具函数目录 +│ ├── __init__.py +│ └── helpers.py +└── README.md # 插件说明文档 +``` + +### 插件主文件模板 + +```python +""" +插件名称:My Plugin +插件描述:插件功能描述 +作者:作者名称 +版本:1.0.0 +""" + +from src.plugin_system.plugin_interface import PluginInterface +from .actions.my_action import MyAction +from .commands.my_command import MyCommand + +class MyPlugin(PluginInterface): + """插件主类""" + + def get_action_info(self): + """获取Action信息""" + return [MyAction()] + + def get_command_info(self): + """获取Command信息""" + return [MyCommand()] + +# 插件实例 +plugin_instance = MyPlugin() +``` + +## 🔧 命名规范 + +### 类命名 +- **Action类**:使用 `Action` 后缀,如 `GreetingAction` +- **Command类**:使用 `Command` 后缀,如 `HelpCommand` +- **Tool类**:使用 `Tool` 后缀,如 `WeatherTool` +- **插件类**:使用 `Plugin` 后缀,如 `ExamplePlugin` + +### 变量命名 +- 使用小写字母和下划线(snake_case) +- 布尔变量使用 `is_`、`has_`、`can_` 前缀 +- 常量使用全大写字母 + +### 函数命名 +- 使用小写字母和下划线(snake_case) +- 异步函数不需要特殊前缀 +- 私有方法使用单下划线前缀 + +## 📊 性能优化规范 + +### Action激活类型选择 +1. **首选KEYWORD**:明确知道触发关键词时 +2. **谨慎使用LLM_JUDGE**:仅在必须智能判断时使用 +3. **合理设置优先级**:避免过多高优先级Action + +### 异步编程规范 +- 所有I/O操作必须使用异步 +- 避免在异步函数中使用阻塞操作 +- 合理使用 `asyncio.gather()` 并发执行 + +### 资源管理 +- 及时关闭文件、网络连接等资源 +- 使用上下文管理器(`async with`) +- 避免内存泄漏 + +## 🚨 错误处理规范 + +### 异常处理模板 + +```python +async def my_function(self, message_txt, sender_name, chat_stream): + """函数文档字符串""" + try: + # 核心逻辑 + result = await some_operation() + + # 成功处理 + await chat_stream.send_message(f"操作成功: {result}") + + except ValueError as e: + # 具体异常处理 + await chat_stream.send_message(f"参数错误: {str(e)}") + + except Exception as e: + # 通用异常处理 + await chat_stream.send_message(f"操作失败: {str(e)}") + # 记录错误日志 + logger.error(f"Function my_function failed: {str(e)}") +``` + +### 错误信息规范 +- 使用用户友好的错误提示 +- 避免暴露系统内部信息 +- 提供解决建议或替代方案 +- 记录详细的错误日志 + +## 🧪 测试标准 + +### 单元测试模板 + +```python +import unittest +import asyncio +from unittest.mock import Mock, AsyncMock +from plugins.my_plugin.actions.my_action import MyAction + +class TestMyAction(unittest.TestCase): + """MyAction测试类""" + + def setUp(self): + """测试前准备""" + self.action = MyAction() + self.mock_chat_stream = AsyncMock() + + def test_action_properties(self): + """测试Action属性""" + self.assertEqual(self.action.name, "my_action") + self.assertIsNotNone(self.action.description) + self.assertIsNotNone(self.action.activation_type) + + async def test_action_execution(self): + """测试Action执行""" + await self.action.执行函数名("测试消息", "测试用户", self.mock_chat_stream) + + # 验证消息发送 + self.mock_chat_stream.send_message.assert_called() + + def test_action_execution_sync(self): + """同步测试包装器""" + asyncio.run(self.test_action_execution()) + +if __name__ == '__main__': + unittest.main() +``` + +### 测试覆盖率要求 +- 核心功能必须有测试覆盖 +- 异常处理路径需要测试 +- 边界条件需要验证 + +## 📚 文档规范 + +### 代码文档 +- 所有类和函数必须有文档字符串 +- 使用Google风格的docstring +- 包含参数说明和返回值说明 + +### README文档模板 + +```markdown +# 插件名称 + +## 📖 插件描述 +简要描述插件的功能和用途 + +## ✨ 功能特性 +- 功能1:功能描述 +- 功能2:功能描述 + +## 🚀 快速开始 +### 安装配置 +1. 步骤1 +2. 步骤2 + +### 使用方法 +具体的使用说明和示例 + +## 📝 配置说明 +配置文件的详细说明 + +## 🔧 开发信息 +- 作者:作者名称 +- 版本:版本号 +- 许可证:许可证类型 +``` + +## 🔍 代码审查清单 + +### 基础检查 +- [ ] 代码符合命名规范 +- [ ] 类和函数有完整文档字符串 +- [ ] 异常处理覆盖完整 +- [ ] 没有硬编码的配置信息 + +### Action组件检查 +- [ ] 包含所有必需字段 +- [ ] 激活类型选择合理 +- [ ] LLM函数提示清晰 +- [ ] 执行函数实现正确 + +### Command组件检查 +- [ ] 正则表达式模式正确 +- [ ] 参数提取和验证完整 +- [ ] 使用说明准确 + +### Tool组件检查 +- [ ] 继承BaseTool基类 +- [ ] 参数定义遵循JSONSchema +- [ ] 返回值格式正确 +- [ ] 工具已正确注册 + +### 性能检查 +- [ ] 避免不必要的LLM_JUDGE激活 +- [ ] 异步操作使用正确 +- [ ] 资源管理合理 + +### 安全检查 +- [ ] 输入参数验证 +- [ ] SQL注入防护 +- [ ] 敏感信息保护 + +## 🎯 最佳实践总结 + +### 设计原则 +1. **单一职责**:每个组件专注单一功能 +2. **松耦合**:减少组件间依赖 +3. **高内聚**:相关功能聚合在一起 +4. **可扩展**:易于添加新功能 + +### 性能优化 +1. **合理选择激活类型**:优先使用KEYWORD +2. **避免阻塞操作**:使用异步编程 +3. **缓存重复计算**:提高响应速度 +4. **资源池化**:复用连接和对象 + +### 用户体验 +1. **友好的错误提示**:帮助用户理解问题 +2. **清晰的使用说明**:降低学习成本 +3. **一致的交互方式**:统一的命令格式 +4. **及时的反馈**:让用户知道操作状态 + +--- + +🎉 **遵循这些标准可以确保插件的质量、性能和用户体验!** \ No newline at end of file diff --git a/docs/plugins/examples/complete-examples.md b/docs/plugins/examples/complete-examples.md new file mode 100644 index 000000000..d54eb078b --- /dev/null +++ b/docs/plugins/examples/complete-examples.md @@ -0,0 +1,414 @@ +# 📚 完整示例 + +## 📖 概述 + +这里收集了各种类型的完整插件示例,展示了MaiBot插件系统的最佳实践和高级用法。每个示例都包含完整的代码、配置和说明。 + +## 🎯 示例列表 + +### 🌟 基础示例 +- [Hello World插件](#hello-world插件) - 快速入门示例 +- [简单计算器](#简单计算器) - Command基础用法 +- [智能问答](#智能问答) - Action基础用法 + +### 🔧 实用示例 +- [用户管理系统](#用户管理系统) - 数据库操作示例 +- [定时提醒插件](#定时提醒插件) - 定时任务示例 +- [天气查询插件](#天气查询插件) - 外部API调用示例 + +### 🛠️ 工具系统示例 +- [天气查询工具](#天气查询工具) - Focus模式信息获取工具 +- [知识搜索工具](#知识搜索工具) - 百科知识查询工具 + +### 🚀 高级示例 +- [多功能聊天助手](#多功能聊天助手) - 综合功能插件 +- [游戏管理插件](#游戏管理插件) - 复杂状态管理 +- [数据分析插件](#数据分析插件) - 数据处理和可视化 + +--- + +## Hello World插件 + +最基础的入门插件,展示Action和Command的基本用法。 + +### 功能说明 +- **HelloAction**: 响应问候语,展示关键词激活 +- **TimeCommand**: 查询当前时间,展示命令处理 + +### 完整代码 + +`plugins/hello_world_plugin/plugin.py`: + +```python +from typing import List, Tuple, Type +from src.plugin_system import ( + BasePlugin, register_plugin, BaseAction, BaseCommand, + ComponentInfo, ActionActivationType, ChatMode +) + +class HelloAction(BaseAction): + """问候Action""" + + # ===== 激活控制必须项 ===== + focus_activation_type = ActionActivationType.KEYWORD + normal_activation_type = ActionActivationType.KEYWORD + mode_enable = ChatMode.ALL + parallel_action = False + + # ===== 基本信息必须项 ===== + action_name = "hello_greeting" + action_description = "向用户发送友好的问候消息" + + # 关键词配置 + activation_keywords = ["你好", "hello", "hi"] + keyword_case_sensitive = False + + # ===== 功能定义必须项 ===== + action_parameters = { + "greeting_style": "问候风格:casual(随意) 或 formal(正式)" + } + + action_require = [ + "用户发送问候语时使用", + "营造友好的聊天氛围" + ] + + associated_types = ["text", "emoji"] + + async def execute(self) -> Tuple[bool, str]: + style = self.action_data.get("greeting_style", "casual") + + if style == "formal": + message = "您好!很高兴为您服务!" + emoji = "🙏" + else: + message = "嗨!很开心见到你!" + emoji = "😊" + + await self.send_text(message) + await self.send_type("emoji", emoji) + + return True, f"发送了{style}风格的问候" + +class TimeCommand(BaseCommand): + """时间查询Command""" + + command_pattern = r"^/time$" + command_help = "查询当前时间" + command_examples = ["/time"] + intercept_message = True + + async def execute(self) -> Tuple[bool, str]: + import datetime + + now = datetime.datetime.now() + time_str = now.strftime("%Y-%m-%d %H:%M:%S") + + await self.send_text(f"⏰ 当前时间:{time_str}") + + return True, f"显示了当前时间: {time_str}" + +@register_plugin +class HelloWorldPlugin(BasePlugin): + """Hello World插件""" + + plugin_name = "hello_world_plugin" + plugin_description = "Hello World演示插件" + plugin_version = "1.0.0" + plugin_author = "MaiBot Team" + enable_plugin = True + + def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: + return [ + (HelloAction.get_action_info(), HelloAction), + (TimeCommand.get_command_info( + name="time_query", + description="查询当前系统时间" + ), TimeCommand), + ] +``` + +### 配置文件 + +`plugins/hello_world_plugin/config.toml`: + +```toml +[plugin] +name = "hello_world_plugin" +version = "1.0.0" +enabled = true + +[greeting] +default_style = "casual" +enable_emoji = true + +[time] +timezone = "Asia/Shanghai" +format = "%Y-%m-%d %H:%M:%S" +``` + +--- + +## 天气查询工具 + +展示如何创建Focus模式下的信息获取工具,专门用于扩展麦麦的信息获取能力。 + +### 功能说明 +- **Focus模式专用**:仅在专注聊天模式下工作 +- **自动调用**:LLM根据用户查询自动判断是否使用 +- **信息增强**:为麦麦提供实时天气数据 +- **必须启用工具处理器** + +### 完整代码 + +`src/tools/tool_can_use/weather_tool.py`: + +```python +from src.tools.tool_can_use.base_tool import BaseTool, register_tool +import aiohttp +import json + +class WeatherTool(BaseTool): + """天气查询工具 - 获取指定城市的实时天气信息""" + + # 工具名称,必须唯一 + name = "weather_query" + + # 工具描述,告诉LLM这个工具的用途 + description = "查询指定城市的实时天气信息,包括温度、湿度、天气状况等" + + # 参数定义,遵循JSONSchema格式 + parameters = { + "type": "object", + "properties": { + "city": { + "type": "string", + "description": "要查询天气的城市名称,如:北京、上海、纽约" + }, + "country": { + "type": "string", + "description": "国家代码,如:CN、US,可选参数" + } + }, + "required": ["city"] + } + + async def execute(self, function_args, message_txt=""): + """执行天气查询""" + try: + city = function_args.get("city") + country = function_args.get("country", "") + + # 构建查询参数 + location = f"{city},{country}" if country else city + + # 调用天气API + weather_data = await self._fetch_weather(location) + + # 格式化结果 + result = self._format_weather_data(weather_data) + + return { + "name": self.name, + "content": result + } + + except Exception as e: + return { + "name": self.name, + "content": f"天气查询失败: {str(e)}" + } + + async def _fetch_weather(self, location: str) -> dict: + """获取天气数据""" + # 这里是示例,实际需要接入真实的天气API + # 例如:OpenWeatherMap、和风天气等 + api_url = f"http://api.weather.com/v1/current?q={location}" + + async with aiohttp.ClientSession() as session: + async with session.get(api_url) as response: + return await response.json() + + def _format_weather_data(self, data: dict) -> str: + """格式化天气数据""" + if not data: + return "暂无天气数据" + + # 提取关键信息 + city = data.get("location", {}).get("name", "未知城市") + temp = data.get("current", {}).get("temp_c", "未知") + condition = data.get("current", {}).get("condition", {}).get("text", "未知") + humidity = data.get("current", {}).get("humidity", "未知") + + # 格式化输出 + return f""" +🌤️ {city} 实时天气 +━━━━━━━━━━━━━━━━━━ +🌡️ 温度: {temp}°C +☁️ 天气: {condition} +💧 湿度: {humidity}% +━━━━━━━━━━━━━━━━━━ + """.strip() + +# 注册工具(重要!必须调用) +register_tool(WeatherTool) +``` + +### 使用说明 + +1. **部署位置**:将文件放在 `src/tools/tool_can_use/` 目录下 +2. **模式要求**:仅在Focus模式下可用 +3. **配置要求**:必须开启工具处理器 `enable_tool_processor = True` +4. **自动调用**:用户发送"今天北京天气怎么样?"时,麦麦会自动调用此工具 + +--- + +## 知识搜索工具 + +展示如何创建知识查询工具,为麦麦提供百科知识和专业信息。 + +### 功能说明 +- **知识增强**:扩展麦麦的知识获取能力 +- **分类搜索**:支持科学、历史、技术等分类 +- **多语言支持**:支持中英文结果 +- **智能调用**:LLM自动判断何时需要知识查询 + +### 完整代码 + +`src/tools/tool_can_use/knowledge_search_tool.py`: + +```python +from src.tools.tool_can_use.base_tool import BaseTool, register_tool +import aiohttp +import json + +class KnowledgeSearchTool(BaseTool): + """知识搜索工具 - 查询百科知识和专业信息""" + + name = "knowledge_search" + description = "搜索百科知识、专业术语解释、历史事件等信息" + + parameters = { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "要搜索的知识关键词或问题" + }, + "category": { + "type": "string", + "description": "知识分类:science(科学)、history(历史)、technology(技术)、general(通用)等", + "enum": ["science", "history", "technology", "general"] + }, + "language": { + "type": "string", + "description": "结果语言:zh(中文)、en(英文)", + "enum": ["zh", "en"] + } + }, + "required": ["query"] + } + + async def execute(self, function_args, message_txt=""): + """执行知识搜索""" + try: + query = function_args.get("query") + category = function_args.get("category", "general") + language = function_args.get("language", "zh") + + # 执行搜索逻辑 + search_results = await self._search_knowledge(query, category, language) + + # 格式化结果 + result = self._format_search_results(query, search_results) + + return { + "name": self.name, + "content": result + } + + except Exception as e: + return { + "name": self.name, + "content": f"知识搜索失败: {str(e)}" + } + + async def _search_knowledge(self, query: str, category: str, language: str) -> list: + """执行知识搜索""" + # 这里实现实际的搜索逻辑 + # 可以对接维基百科API、百度百科API等 + + # 示例API调用 + if language == "zh": + api_url = f"https://zh.wikipedia.org/api/rest_v1/page/summary/{query}" + else: + api_url = f"https://en.wikipedia.org/api/rest_v1/page/summary/{query}" + + async with aiohttp.ClientSession() as session: + async with session.get(api_url) as response: + if response.status == 200: + data = await response.json() + return [ + { + "title": data.get("title", "无标题"), + "summary": data.get("extract", "无摘要"), + "source": "Wikipedia" + } + ] + else: + return [] + + def _format_search_results(self, query: str, results: list) -> str: + """格式化搜索结果""" + if not results: + return f"未找到关于 '{query}' 的相关信息" + + formatted_text = f"📚 关于 '{query}' 的搜索结果:\n\n" + + for i, result in enumerate(results[:3], 1): # 限制显示前3条 + title = result.get("title", "无标题") + summary = result.get("summary", "无摘要") + source = result.get("source", "未知来源") + + formatted_text += f"{i}. **{title}**\n" + formatted_text += f" {summary}\n" + formatted_text += f" 📖 来源: {source}\n\n" + + return formatted_text.strip() + +# 注册工具 +register_tool(KnowledgeSearchTool) +``` + +### 配置示例 + +Focus模式配置文件示例: + +```python +# 在Focus模式配置中 +focus_config = { + "enable_tool_processor": True, # 必须启用工具处理器 + "tool_timeout": 30, # 工具执行超时时间(秒) + "max_tools_per_message": 3 # 单次消息最大工具调用数 +} +``` + +### 使用流程 + +1. **用户查询**:用户在Focus模式下发送"什么是量子计算?" +2. **LLM判断**:麦麦识别这是知识查询需求 +3. **工具调用**:自动调用 `knowledge_search` 工具 +4. **信息获取**:工具查询相关知识信息 +5. **整合回复**:麦麦将获取的信息整合到回复中 + +### 工具系统特点 + +- **🎯 专用性**:仅在Focus模式下工作,专注信息获取 +- **🔍 智能性**:LLM自动判断何时需要使用工具 +- **📊 丰富性**:为麦麦提供外部数据和实时信息 +- **⚡ 高效性**:系统自动发现和注册工具 +- **🔧 独立性**:目前需要单独编写,未来将更好融入插件系统 + +--- + +🎉 **这些示例展示了MaiBot插件系统的强大功能!根据你的需求选择合适的示例作为起点。** \ No newline at end of file diff --git a/docs/plugins/quick-start.md b/docs/plugins/quick-start.md new file mode 100644 index 000000000..5b34bfaa2 --- /dev/null +++ b/docs/plugins/quick-start.md @@ -0,0 +1,279 @@ +# 🚀 快速开始指南 + +## 📖 概述 + +这个指南将带你在5分钟内创建你的第一个MaiBot插件。我们将创建一个简单的问候插件,展示插件系统的基本概念。 + +## 🎯 学习目标 + +- 理解插件的基本结构 +- 创建你的第一个Action组件 +- 创建你的第一个Command组件 +- 学会配置插件 + +## 📂 准备工作 + +确保你已经: +1. 克隆了MaiBot项目 +2. 安装了Python依赖 +3. 了解基本的Python语法 + +## 🏗️ 创建插件 + +### 1. 创建插件目录 + +在项目根目录的 `plugins/` 文件夹下创建你的插件目录: + +```bash +mkdir plugins/hello_world_plugin +cd plugins/hello_world_plugin +``` + +### 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 HelloAction(BaseAction): + """问候Action - 展示智能动作的基本用法""" + + # ===== 激活控制必须项 ===== + focus_activation_type = ActionActivationType.KEYWORD + normal_activation_type = ActionActivationType.KEYWORD + mode_enable = ChatMode.ALL + parallel_action = False + + # ===== 基本信息必须项 ===== + action_name = "hello_greeting" + action_description = "向用户发送友好的问候消息" + + # 关键词配置 + activation_keywords = ["你好", "hello", "hi"] + keyword_case_sensitive = False + + # ===== 功能定义必须项 ===== + action_parameters = { + "greeting_style": "问候风格:casual(随意) 或 formal(正式)" + } + + action_require = [ + "用户发送问候语时使用", + "营造友好的聊天氛围" + ] + + associated_types = ["text", "emoji"] + + async def execute(self) -> Tuple[bool, str]: + """执行问候动作""" + # 获取参数 + style = self.action_data.get("greeting_style", "casual") + + # 根据风格生成问候语 + if style == "formal": + message = "您好!很高兴为您服务!" + emoji = "🙏" + else: + message = "嗨!很开心见到你!" + emoji = "😊" + + # 发送消息 + await self.send_text(message) + await self.send_type("emoji", emoji) + + return True, f"发送了{style}风格的问候" + +# ===== Command组件 ===== + +class TimeCommand(BaseCommand): + """时间查询Command - 展示命令的基本用法""" + + command_pattern = r"^/time$" + command_help = "查询当前时间" + command_examples = ["/time"] + intercept_message = True # 拦截消息处理 + + async def execute(self) -> Tuple[bool, str]: + """执行时间查询""" + import datetime + + now = datetime.datetime.now() + time_str = now.strftime("%Y-%m-%d %H:%M:%S") + + await self.send_text(f"⏰ 当前时间:{time_str}") + + return True, f"显示了当前时间: {time_str}" + +# ===== 插件注册 ===== + +@register_plugin +class HelloWorldPlugin(BasePlugin): + """Hello World插件 - 你的第一个MaiBot插件""" + + # 插件基本信息 + plugin_name = "hello_world_plugin" + plugin_description = "Hello World演示插件,展示基本的Action和Command用法" + plugin_version = "1.0.0" + plugin_author = "你的名字" + enable_plugin = True + config_file_name = "config.toml" + + def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: + """返回插件包含的组件列表""" + return [ + # Action组件 - 使用类中定义的所有属性 + (HelloAction.get_action_info(), HelloAction), + + # Command组件 - 需要指定name和description + (TimeCommand.get_command_info( + name="time_query", + description="查询当前系统时间" + ), TimeCommand), + ] +``` + +### 3. 创建配置文件 + +创建 `config.toml` 文件: + +```toml +[plugin] +name = "hello_world_plugin" +version = "1.0.0" +enabled = true +description = "Hello World演示插件" + +[greeting] +default_style = "casual" +enable_emoji = true + +[time] +timezone = "Asia/Shanghai" +format = "%Y-%m-%d %H:%M:%S" + +[logging] +level = "INFO" +``` + +### 4. 创建说明文档 + +创建 `README.md` 文件: + +```markdown +# Hello World 插件 + +## 概述 + +这是一个简单的Hello World插件,演示了MaiBot插件系统的基本用法。 + +## 功能 + +- **HelloAction**: 智能问候动作,响应用户的问候语 +- **TimeCommand**: 时间查询命令,显示当前时间 + +## 使用方法 + +### Action使用 +当用户发送包含"你好"、"hello"或"hi"的消息时,插件会自动触发问候动作。 + +### Command使用 +发送 `/time` 查询当前时间。 + +## 配置 + +可以通过 `config.toml` 调整插件行为。 +``` + +## 🎮 测试插件 + +### 1. 启动MaiBot + +将插件放入 `plugins/` 目录后,启动MaiBot: + +```bash +python main.py +``` + +### 2. 测试Action + +发送消息: +``` +你好 +``` + +期望输出: +``` +嗨!很开心见到你!😊 +``` + +### 3. 测试Command + +发送命令: +``` +/time +``` + +期望输出: +``` +⏰ 当前时间:2024-01-01 12:00:00 +``` + +## 🔍 解析代码 + +### Action组件重点 + +1. **激活控制**: 使用 `KEYWORD` 激活类型,当检测到指定关键词时触发 +2. **必须项完整**: 包含所有必须的类属性 +3. **智能决策**: 麦麦会根据情境决定是否使用这个Action + +### Command组件重点 + +1. **正则匹配**: 使用 `^/time$` 精确匹配 `/time` 命令 +2. **消息拦截**: 设置 `intercept_message = True` 防止命令继续处理 +3. **即时响应**: 匹配到命令立即执行 + +### 插件注册重点 + +1. **@register_plugin**: 装饰器自动注册插件 +2. **组件列表**: `get_plugin_components()` 返回所有组件 +3. **配置加载**: 自动加载 `config.toml` 文件 + +## 🎯 下一步 + +恭喜!你已经创建了第一个MaiBot插件。接下来可以: + +1. 学习 [Action组件详解](action-components.md) 掌握更复杂的Action开发 +2. 学习 [Command组件详解](command-components.md) 创建更强大的命令 +3. 查看 [API参考](api/) 了解所有可用的接口 +4. 参考 [完整示例](examples/complete-examples.md) 学习最佳实践 + +## 🐛 常见问题 + +### Q: 插件没有加载怎么办? +A: 检查: +1. 插件是否放在 `plugins/` 目录下 +2. `plugin.py` 文件语法是否正确 +3. 查看启动日志中的错误信息 + +### Q: Action没有触发怎么办? +A: 检查: +1. 关键词是否正确配置 +2. 消息是否包含激活关键词 +3. 聊天模式是否匹配 + +### Q: Command无响应怎么办? +A: 检查: +1. 正则表达式是否正确 +2. 命令格式是否精确匹配 +3. 是否有其他插件拦截了消息 + +--- + +🎉 **成功!你已经掌握了MaiBot插件开发的基础!** \ No newline at end of file diff --git a/docs/plugins/tool-system.md b/docs/plugins/tool-system.md new file mode 100644 index 000000000..d9093c89f --- /dev/null +++ b/docs/plugins/tool-system.md @@ -0,0 +1,495 @@ +# 🔧 工具系统详解 + +## 📖 什么是工具系统 + +工具系统是MaiBot的信息获取能力扩展组件,**专门用于在Focus模式下扩宽麦麦能够获得的信息量**。如果说Action组件功能五花八门,可以拓展麦麦能做的事情,那么Tool就是在某个过程中拓宽了麦麦能够获得的信息量。 + +### 🎯 工具系统的特点 + +- 🔍 **信息获取增强**:扩展麦麦获取外部信息的能力 +- 🎯 **Focus模式专用**:仅在专注聊天模式下工作,必须开启工具处理器 +- 📊 **数据丰富**:帮助麦麦获得更多背景信息和实时数据 +- 🔌 **插件式架构**:支持独立开发和注册新工具 +- ⚡ **自动发现**:工具会被系统自动识别和注册 + +### 🆚 Tool vs Action vs Command 区别 + +| 特征 | Action | Command | Tool | +|-----|-------|---------|------| +| **主要用途** | 扩展麦麦行为能力 | 响应用户指令 | 扩展麦麦信息获取 | +| **适用模式** | 所有模式 | 所有模式 | 仅Focus模式 | +| **触发方式** | 麦麦智能决策 | 用户主动触发 | LLM根据需要调用 | +| **目标** | 让麦麦做更多事情 | 提供具体功能 | 让麦麦知道更多信息 | +| **使用场景** | 增强交互体验 | 功能服务 | 信息查询和分析 | + +## 🏗️ 工具基本结构 + +### 必要组件 + +每个工具必须继承 `BaseTool` 基类并实现以下属性和方法: + +```python +from src.tools.tool_can_use.base_tool import BaseTool, register_tool + +class MyTool(BaseTool): + # 工具名称,必须唯一 + name = "my_tool" + + # 工具描述,告诉LLM这个工具的用途 + description = "这个工具用于获取特定类型的信息" + + # 参数定义,遵循JSONSchema格式 + parameters = { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "查询参数" + }, + "limit": { + "type": "integer", + "description": "结果数量限制" + } + }, + "required": ["query"] + } + + async def execute(self, function_args, message_txt=""): + """执行工具逻辑""" + # 实现工具功能 + result = f"查询结果: {function_args.get('query')}" + + return { + "name": self.name, + "content": result + } + +# 注册工具 +register_tool(MyTool) +``` + +### 属性说明 + +| 属性 | 类型 | 说明 | +|-----|------|------| +| `name` | str | 工具的唯一标识名称 | +| `description` | str | 工具功能描述,帮助LLM理解用途 | +| `parameters` | dict | JSONSchema格式的参数定义 | + +### 方法说明 + +| 方法 | 参数 | 返回值 | 说明 | +|-----|------|--------|------| +| `execute` | `function_args`, `message_txt` | `dict` | 执行工具核心逻辑 | + +## 🔄 自动注册机制 + +工具系统采用自动发现和注册机制: + +1. **文件扫描**:系统自动遍历 `tool_can_use` 目录中的所有Python文件 +2. **类识别**:寻找继承自 `BaseTool` 的工具类 +3. **自动注册**:调用 `register_tool()` 的工具会被注册到系统中 +4. **即用即加载**:工具在需要时被实例化和调用 + +### 注册流程 + +```python +# 1. 创建工具类 +class WeatherTool(BaseTool): + name = "weather_query" + description = "查询指定城市的天气信息" + # ... + +# 2. 注册工具(在文件末尾) +register_tool(WeatherTool) + +# 3. 系统自动发现(无需手动操作) +# discover_tools() 函数会自动完成注册 +``` + +## 🎨 完整工具示例 + +### 天气查询工具 + +```python +from src.tools.tool_can_use.base_tool import BaseTool, register_tool +import aiohttp +import json + +class WeatherTool(BaseTool): + """天气查询工具 - 获取指定城市的实时天气信息""" + + name = "weather_query" + description = "查询指定城市的实时天气信息,包括温度、湿度、天气状况等" + + parameters = { + "type": "object", + "properties": { + "city": { + "type": "string", + "description": "要查询天气的城市名称,如:北京、上海、纽约" + }, + "country": { + "type": "string", + "description": "国家代码,如:CN、US,可选参数" + } + }, + "required": ["city"] + } + + async def execute(self, function_args, message_txt=""): + """执行天气查询""" + try: + city = function_args.get("city") + country = function_args.get("country", "") + + # 构建查询参数 + location = f"{city},{country}" if country else city + + # 调用天气API(示例) + weather_data = await self._fetch_weather(location) + + # 格式化结果 + result = self._format_weather_data(weather_data) + + return { + "name": self.name, + "content": result + } + + except Exception as e: + return { + "name": self.name, + "content": f"天气查询失败: {str(e)}" + } + + async def _fetch_weather(self, location: str) -> dict: + """获取天气数据""" + # 这里是示例,实际需要接入真实的天气API + api_url = f"http://api.weather.com/v1/current?q={location}" + + async with aiohttp.ClientSession() as session: + async with session.get(api_url) as response: + return await response.json() + + def _format_weather_data(self, data: dict) -> str: + """格式化天气数据""" + if not data: + return "暂无天气数据" + + # 提取关键信息 + city = data.get("location", {}).get("name", "未知城市") + temp = data.get("current", {}).get("temp_c", "未知") + condition = data.get("current", {}).get("condition", {}).get("text", "未知") + humidity = data.get("current", {}).get("humidity", "未知") + + # 格式化输出 + return f""" +🌤️ {city} 实时天气 +━━━━━━━━━━━━━━━━━━ +🌡️ 温度: {temp}°C +☁️ 天气: {condition} +💧 湿度: {humidity}% +━━━━━━━━━━━━━━━━━━ + """.strip() + +# 注册工具 +register_tool(WeatherTool) +``` + +### 知识查询工具 + +```python +from src.tools.tool_can_use.base_tool import BaseTool, register_tool + +class KnowledgeSearchTool(BaseTool): + """知识搜索工具 - 查询百科知识和专业信息""" + + name = "knowledge_search" + description = "搜索百科知识、专业术语解释、历史事件等信息" + + parameters = { + "type": "object", + "properties": { + "query": { + "type": "string", + "description": "要搜索的知识关键词或问题" + }, + "category": { + "type": "string", + "description": "知识分类:science(科学)、history(历史)、technology(技术)、general(通用)等", + "enum": ["science", "history", "technology", "general"] + }, + "language": { + "type": "string", + "description": "结果语言:zh(中文)、en(英文)", + "enum": ["zh", "en"] + } + }, + "required": ["query"] + } + + async def execute(self, function_args, message_txt=""): + """执行知识搜索""" + try: + query = function_args.get("query") + category = function_args.get("category", "general") + language = function_args.get("language", "zh") + + # 执行搜索逻辑 + search_results = await self._search_knowledge(query, category, language) + + # 格式化结果 + result = self._format_search_results(query, search_results) + + return { + "name": self.name, + "content": result + } + + except Exception as e: + return { + "name": self.name, + "content": f"知识搜索失败: {str(e)}" + } + + async def _search_knowledge(self, query: str, category: str, language: str) -> list: + """执行知识搜索""" + # 这里实现实际的搜索逻辑 + # 可以对接维基百科API、百度百科API等 + + # 示例返回数据 + return [ + { + "title": f"{query}的定义", + "summary": f"关于{query}的详细解释...", + "source": "Wikipedia" + } + ] + + def _format_search_results(self, query: str, results: list) -> str: + """格式化搜索结果""" + if not results: + return f"未找到关于 '{query}' 的相关信息" + + formatted_text = f"📚 关于 '{query}' 的搜索结果:\n\n" + + for i, result in enumerate(results[:3], 1): # 限制显示前3条 + title = result.get("title", "无标题") + summary = result.get("summary", "无摘要") + source = result.get("source", "未知来源") + + formatted_text += f"{i}. **{title}**\n" + formatted_text += f" {summary}\n" + formatted_text += f" 📖 来源: {source}\n\n" + + return formatted_text.strip() + +# 注册工具 +register_tool(KnowledgeSearchTool) +``` + +## 📊 工具开发步骤 + +### 1. 创建工具文件 + +在 `src/tools/tool_can_use/` 目录下创建新的Python文件: + +```bash +# 例如创建 my_new_tool.py +touch src/tools/tool_can_use/my_new_tool.py +``` + +### 2. 实现工具类 + +```python +from src.tools.tool_can_use.base_tool import BaseTool, register_tool + +class MyNewTool(BaseTool): + name = "my_new_tool" + description = "新工具的功能描述" + + parameters = { + "type": "object", + "properties": { + # 定义参数 + }, + "required": [] + } + + async def execute(self, function_args, message_txt=""): + # 实现工具逻辑 + return { + "name": self.name, + "content": "执行结果" + } + +register_tool(MyNewTool) +``` + +### 3. 测试工具 + +创建测试文件验证工具功能: + +```python +import asyncio +from my_new_tool import MyNewTool + +async def test_tool(): + tool = MyNewTool() + result = await tool.execute({"param": "value"}) + print(result) + +asyncio.run(test_tool()) +``` + +### 4. 系统集成 + +工具创建完成后,系统会自动发现和注册,无需额外配置。 + +## ⚙️ 工具处理器配置 + +### 启用工具处理器 + +工具系统仅在Focus模式下工作,需要确保工具处理器已启用: + +```python +# 在Focus模式配置中 +focus_config = { + "enable_tool_processor": True, # 必须启用 + "tool_timeout": 30, # 工具执行超时时间(秒) + "max_tools_per_message": 3 # 单次消息最大工具调用数 +} +``` + +### 工具使用流程 + +1. **用户发送消息**:在Focus模式下发送需要信息查询的消息 +2. **LLM判断需求**:麦麦分析消息,判断是否需要使用工具获取信息 +3. **选择工具**:根据需求选择合适的工具 +4. **调用工具**:执行工具获取信息 +5. **整合回复**:将工具获取的信息整合到回复中 + +### 使用示例 + +```python +# 用户消息示例 +"今天北京的天气怎么样?" + +# 系统处理流程: +# 1. 麦麦识别这是天气查询需求 +# 2. 调用 weather_query 工具 +# 3. 获取北京天气信息 +# 4. 整合信息生成回复 + +# 最终回复: +"根据最新天气数据,北京今天晴天,温度22°C,湿度45%,适合外出活动。" +``` + +## 🚨 注意事项和限制 + +### 当前限制 + +1. **模式限制**:仅在Focus模式下可用 +2. **独立开发**:需要单独编写,暂未完全融入插件系统 +3. **适用范围**:主要适用于信息获取场景 +4. **配置要求**:必须开启工具处理器 + +### 未来改进 + +工具系统在之后可能会面临以下修改: + +1. **插件系统融合**:更好地集成到插件系统中 +2. **模式扩展**:可能扩展到其他聊天模式 +3. **配置简化**:简化配置和部署流程 +4. **性能优化**:提升工具调用效率 + +### 开发建议 + +1. **功能专一**:每个工具专注单一功能 +2. **参数明确**:清晰定义工具参数和用途 +3. **错误处理**:完善的异常处理和错误反馈 +4. **性能考虑**:避免长时间阻塞操作 +5. **信息准确**:确保获取信息的准确性和时效性 + +## 🎯 最佳实践 + +### 1. 工具命名规范 + +```python +# ✅ 好的命名 +name = "weather_query" # 清晰表达功能 +name = "knowledge_search" # 描述性强 +name = "stock_price_check" # 功能明确 + +# ❌ 避免的命名 +name = "tool1" # 无意义 +name = "wq" # 过于简短 +name = "weather_and_news" # 功能过于复杂 +``` + +### 2. 描述规范 + +```python +# ✅ 好的描述 +description = "查询指定城市的实时天气信息,包括温度、湿度、天气状况" + +# ❌ 避免的描述 +description = "天气" # 过于简单 +description = "获取信息" # 不够具体 +``` + +### 3. 参数设计 + +```python +# ✅ 合理的参数设计 +parameters = { + "type": "object", + "properties": { + "city": { + "type": "string", + "description": "城市名称,如:北京、上海" + }, + "unit": { + "type": "string", + "description": "温度单位:celsius(摄氏度) 或 fahrenheit(华氏度)", + "enum": ["celsius", "fahrenheit"] + } + }, + "required": ["city"] +} + +# ❌ 避免的参数设计 +parameters = { + "type": "object", + "properties": { + "data": { + "type": "string", + "description": "数据" # 描述不清晰 + } + } +} +``` + +### 4. 结果格式化 + +```python +# ✅ 良好的结果格式 +def _format_result(self, data): + return f""" +🔍 查询结果 +━━━━━━━━━━━━ +📊 数据: {data['value']} +📅 时间: {data['timestamp']} +📝 说明: {data['description']} +━━━━━━━━━━━━ + """.strip() + +# ❌ 避免的结果格式 +def _format_result(self, data): + return str(data) # 直接返回原始数据 +``` + +--- + +🎉 **工具系统为麦麦提供了强大的信息获取能力!合理使用工具可以让麦麦变得更加智能和博学。** \ No newline at end of file diff --git a/docs/use_tool.md b/docs/use_tool.md deleted file mode 100644 index ef6760b5b..000000000 --- a/docs/use_tool.md +++ /dev/null @@ -1,102 +0,0 @@ -# 工具系统使用指南 - -## 概述 - -`tool_can_use` 是一个插件式工具系统,允许轻松扩展和注册新工具。每个工具作为独立的文件存在于该目录下,系统会自动发现和注册这些工具。 - -## 工具结构 - -每个工具应该继承 `BaseTool` 基类并实现必要的属性和方法: - -```python -from src.tools.tool_can_use.base_tool import BaseTool, register_tool - -class MyNewTool(BaseTool): - # 工具名称,必须唯一 - name = "my_new_tool" - - # 工具描述,告诉LLM这个工具的用途 - description = "这是一个新工具,用于..." - - # 工具参数定义,遵循JSONSchema格式 - parameters = { - "type": "object", - "properties": { - "param1": { - "type": "string", - "description": "参数1的描述" - }, - "param2": { - "type": "integer", - "description": "参数2的描述" - } - }, - "required": ["param1"] # 必需的参数列表 - } - - async def execute(self, function_args, message_txt=""): - """执行工具逻辑 - - Args: - function_args: 工具调用参数 - message_txt: 原始消息文本 - - Returns: - dict: 包含执行结果的字典,必须包含name和content字段 - """ - # 实现工具逻辑 - result = f"工具执行结果: {function_args.get('param1')}" - - return { - "name": self.name, - "content": result - } - -# 注册工具 -register_tool(MyNewTool) -``` - -## 自动注册机制 - -工具系统通过以下步骤自动注册工具: - -1. 在`__init__.py`中,`discover_tools()`函数会自动遍历当前目录中的所有Python文件 -2. 对于每个文件,系统会寻找继承自`BaseTool`的类 -3. 这些类会被自动注册到工具注册表中 - -只要确保在每个工具文件的末尾调用`register_tool(YourToolClass)`,工具就会被自动注册。 - -## 添加新工具步骤 - -1. 在`tool_can_use`目录下创建新的Python文件(如`my_new_tool.py`) -2. 导入`BaseTool`和`register_tool` -3. 创建继承自`BaseTool`的工具类 -4. 实现必要的属性(`name`, `description`, `parameters`) -5. 实现`execute`方法 -6. 使用`register_tool`注册工具 - -## 与ToolUser整合 - -`ToolUser`类已经更新为使用这个新的工具系统,它会: - -1. 自动获取所有已注册工具的定义 -2. 基于工具名称找到对应的工具实例 -3. 调用工具的`execute`方法 - -## 使用示例 - -```python -from src.tools.tool_use import ToolUser - -# 创建工具用户 -tool_user = ToolUser() - -# 使用工具 -result = await tool_user.use_tool(message_txt="查询关于Python的知识", sender_name="用户", chat_stream=chat_stream) - -# 处理结果 -if result["used_tools"]: - print("工具使用结果:", result["collected_info"]) -else: - print("未使用工具") -``` \ No newline at end of file diff --git a/plugins/example_plugin/README.md b/plugins/example_plugin/README.md index ad5a52156..97be5f980 100644 --- a/plugins/example_plugin/README.md +++ b/plugins/example_plugin/README.md @@ -9,11 +9,29 @@ ### 🎯 Action组件 #### SmartGreetingAction - 智能问候 -- **触发方式**: 关键词触发 (你好、hello、hi、嗨等) +- **激活类型**: + - Focus模式: KEYWORD (关键词激活) + - Normal模式: KEYWORD (关键词激活) +- **触发关键词**: 你好、hello、hi、嗨、问候、早上好、晚上好 - **支持模式**: 所有聊天模式 -- **功能**: 智能问候,支持LLM个性化生成 +- **并行执行**: 否 +- **功能**: 智能问候,支持多种风格和LLM个性化生成 +- **参数**: username(用户名), greeting_style(问候风格) - **配置**: 可自定义问候模板、启用表情、LLM生成 +#### HelpfulAction - 智能助手 +- **激活类型**: + - Focus模式: LLM_JUDGE (LLM智能判断) + - Normal模式: RANDOM (随机激活,概率15%) +- **支持模式**: 所有聊天模式 +- **并行执行**: 是 +- **功能**: 主动提供帮助和建议,展示LLM判断激活机制 +- **参数**: help_type(帮助类型), topic(主题), complexity(复杂度) +- **特点**: + - 通过LLM智能判断是否需要提供帮助 + - 展示两层决策机制的实际应用 + - 支持多种帮助类型(解释、建议、指导、提示) + ### 📝 Command组件 #### 1. ComprehensiveHelpCommand - 综合帮助系统 @@ -94,36 +112,69 @@ ### 组件控制 ```toml [components] -enable_greeting = true # 启用智能问候 -enable_help = true # 启用帮助系统 -enable_send = true # 启用消息发送 -# ... 其他组件开关 +enable_greeting = true # 启用智能问候Action +enable_helpful = true # 启用智能助手Action +enable_help = true # 启用帮助系统Command +enable_send = true # 启用消息发送Command +enable_echo = true # 启用回声Command +enable_info = true # 启用消息信息Command +enable_dice = true # 启用骰子Command ``` -### 功能配置 +### Action配置 ```toml [greeting] template = "你好,{username}!" # 问候模板 enable_emoji = true # 启用表情 enable_llm = false # 启用LLM生成 +[helpful] +enable_llm = false # 启用LLM生成帮助 +enable_emoji = true # 启用鼓励表情 +random_activation_probability = 0.15 # 随机激活概率 +``` + +### Command配置 +```toml [send] max_message_length = 500 # 最大消息长度 [echo] max_length = 200 # 回声最大长度 enable_formatting = true # 启用格式化 + +[help] +enable_llm = false # 启用LLM生成帮助内容 +enable_emoji = true # 启用帮助表情 ``` ## 🚀 使用示例 -### 智能问候 +### Action组件示例 + +#### 智能问候Action (关键词激活) ``` 用户: 你好 -机器人: 你好,朋友!欢迎使用MaiBot综合插件系统!😊 +机器人: 嗨!很开心见到你~ 😊 + +用户: 早上好 +机器人: 早上好!今天也要元气满满哦! ✨ ``` -### 帮助查询 +#### 智能助手Action (LLM判断激活) +``` +用户: 我不太懂怎么使用这个功能 +机器人: 关于功能使用,我来为你解释一下:这是一个simple级别的概念... +这个概念其实很简单,让我用通俗的话来说明。 💡 + +用户: Python装饰器是什么? +机器人: 关于Python装饰器,我来为你解释一下:这是一个medium级别的概念... +装饰器是一种设计模式,用于在不修改原函数的情况下扩展功能。 🎯 +``` + +### Command组件示例 + +#### 帮助查询 ``` 用户: /help 机器人: [显示完整命令帮助列表] @@ -132,27 +183,53 @@ enable_formatting = true # 启用格式化 机器人: [显示send命令的详细帮助] ``` -### 消息发送 +#### 消息发送 ``` 用户: /send group 123456 大家好! 机器人: ✅ 消息已成功发送到 群聊 123456 ``` -### 日志监控(不拦截) +#### 骰子命令 ``` -用户: /log info 这是一条测试消息 -[日志记录但消息继续处理,可能触发智能问候等其他功能] +用户: !dice +机器人: 🎲 你投出了: 4 + +用户: !骰子 3 +机器人: 🎲 你投出了3个骰子: 2, 5, 1 (总计: 8) +``` + +### 两层决策机制展示 + +#### 第一层:激活控制 +``` +# SmartGreetingAction - 关键词激活 +用户消息包含"你好" → Action被激活 → 进入候选池 + +# HelpfulAction - LLM判断激活 +用户表达困惑 → LLM判断"是" → Action被激活 → 进入候选池 +用户正常聊天 → LLM判断"否" → Action不激活 → 不进入候选池 +``` + +#### 第二层:使用决策 +``` +# 即使Action被激活,LLM还会根据action_require判断是否真正使用 +# 比如HelpfulAction的条件:"避免过度频繁地提供帮助,要恰到好处" +# 如果刚刚已经提供了帮助,可能不会再次选择使用 ``` ## 📁 文件结构 ``` -src/plugins/built_in/example_comprehensive/ -├── plugin.py # 主插件文件 -├── config.toml # 配置文件 -└── README.md # 说明文档 +plugins/example_plugin/ # 用户插件目录 +├── plugin.py # 主插件文件 +├── config.toml # 配置文件 +└── README.md # 说明文档 ``` +> 💡 **目录说明**: +> - `plugins/` - 用户自定义插件目录(推荐放置位置) +> - `src/plugins/builtin/` - 系统内置插件目录 + ## 🔄 架构升级 此插件展示了从旧插件系统到新插件系统的完整升级: @@ -169,13 +246,48 @@ src/plugins/built_in/example_comprehensive/ 此插件可作为开发新插件的完整参考: -1. **Action开发**: 参考 `SmartGreetingAction` -2. **Command开发**: 参考各种Command实现 -3. **拦截控制**: 根据需要设置 `intercept_message` -4. **配置使用**: 通过 `self.api.get_config()` 读取配置 -5. **错误处理**: 完整的异常捕获和用户反馈 -6. **日志记录**: 结构化的日志输出 +### Action开发规范 +1. **必须项检查清单**: + - ✅ 激活控制必须项:`focus_activation_type`, `normal_activation_type`, `mode_enable`, `parallel_action` + - ✅ 基本信息必须项:`action_name`, `action_description` + - ✅ 功能定义必须项:`action_parameters`, `action_require`, `associated_types` + +2. **激活类型选择**: + - `KEYWORD`: 适合明确触发词的功能(如问候) + - `LLM_JUDGE`: 适合需要智能判断的功能(如帮助) + - `RANDOM`: 适合增加随机性的功能 + - `ALWAYS`: 适合总是考虑的功能 + - `NEVER`: 用于临时禁用 + +3. **两层决策设计**: + - 第一层(激活控制):控制Action是否进入候选池 + - 第二层(使用决策):LLM根据场景智能选择 + +### Command开发规范 +1. **拦截控制**: 根据需要设置 `intercept_message` +2. **正则表达式**: 使用命名组捕获参数 +3. **错误处理**: 完整的异常捕获和用户反馈 + +### 通用开发规范 +1. **配置使用**: 通过 `self.api.get_config()` 读取配置 +2. **日志记录**: 结构化的日志输出 +3. **API调用**: 使用新的统一API接口 +4. **注册简化**: Action使用 `get_action_info()` 无参数调用 ## 🎉 总结 -这个综合示例插件完美展示了新插件系统的强大功能,特别是**拦截控制机制**,让开发者可以精确控制消息处理流程,实现更灵活的插件交互模式。 \ No newline at end of file +这个综合示例插件完美展示了新插件系统的强大功能: + +### 🚀 核心特性 +- **两层决策机制**:优化LLM决策压力,提升性能 +- **完整的Action规范**:所有必须项都在类中统一定义 +- **灵活的激活控制**:支持多种激活类型和条件 +- **精确的拦截控制**:Command可以精确控制消息处理流程 + +### 📚 学习价值 +- **Action vs Command**: 清晰展示两种组件的不同设计理念 +- **激活机制**: 实际演示关键词、LLM判断、随机等激活方式 +- **配置驱动**: 展示如何通过配置文件控制插件行为 +- **错误处理**: 完整的异常处理和用户反馈机制 + +这个插件是理解和掌握MaiBot插件系统的最佳起点!🌟 \ No newline at end of file diff --git a/plugins/example_plugin/config.toml b/plugins/example_plugin/config.toml index b120fd181..c1e7a8ee6 100644 --- a/plugins/example_plugin/config.toml +++ b/plugins/example_plugin/config.toml @@ -8,11 +8,13 @@ description = "展示新插件系统完整功能的示例插件" # 组件启用控制 [components] -enable_greeting = false -enable_help = false -enable_send = false -enable_echo = false -enable_info = false +enable_greeting = true +enable_helpful = true +enable_help = true +enable_send = true +enable_echo = true +enable_info = true +enable_dice = true # 智能问候配置 [greeting] @@ -37,11 +39,19 @@ show_detailed_info = true include_stream_info = true max_content_preview = 100 +# 智能帮助配置 +[helpful] +enable_llm = false +enable_emoji = true +random_activation_probability = 0.15 + # 帮助系统配置 [help] show_extended_help = true include_action_info = true include_config_info = true +enable_llm = false +enable_emoji = true # 骰子命令配置 [dice] diff --git a/plugins/example_plugin/plugin.py b/plugins/example_plugin/plugin.py index 10719af84..219574e99 100644 --- a/plugins/example_plugin/plugin.py +++ b/plugins/example_plugin/plugin.py @@ -42,19 +42,270 @@ logger = get_logger("example_comprehensive") class SmartGreetingAction(BaseAction): """智能问候Action - 基于关键词触发的问候系统""" - # 激活设置 + # ===== 激活控制必须项 ===== focus_activation_type = ActionActivationType.KEYWORD normal_activation_type = ActionActivationType.KEYWORD - activation_keywords = ["你好", "hello", "hi", "嗨", "问候", "早上好", "晚上好"] - keyword_case_sensitive = False mode_enable = ChatMode.ALL parallel_action = False - # Action参数定义 - action_parameters = {"username": "要问候的用户名(可选)"} + # ===== 基本信息必须项 ===== + action_name = "smart_greeting" + action_description = "智能问候系统,基于关键词触发,支持个性化问候消息" - # Action使用场景 - action_require = ["用户发送包含问候词汇的消息", "检测到新用户加入时", "响应友好交流需求"] + # 关键词配置 + activation_keywords = ["你好", "hello", "hi", "嗨", "问候", "早上好", "晚上好"] + keyword_case_sensitive = False + + # ===== 功能定义必须项 ===== + action_parameters = { + "username": "要问候的用户名(可选)", + "greeting_style": "问候风格:casual(随意)、formal(正式)、friendly(友好),默认casual" + } + + action_require = [ + "用户发送包含问候词汇的消息时使用", + "检测到新用户加入时使用", + "响应友好交流需求时使用", + "避免在短时间内重复问候同一用户" + ] + + associated_types = ["text", "emoji"] + + async def execute(self) -> Tuple[bool, str]: + """执行智能问候""" + logger.info(f"{self.log_prefix} 执行智能问候动作: {self.reasoning}") + + try: + # 获取参数 + username = self.action_data.get("username", "") + greeting_style = self.action_data.get("greeting_style", "casual") + + # 获取配置 + template = self.api.get_config("greeting.template", "你好,{username}!欢迎使用MaiBot综合插件系统!") + enable_emoji = self.api.get_config("greeting.enable_emoji", True) + enable_llm = self.api.get_config("greeting.enable_llm", False) + + # 构建问候消息 + if enable_llm: + # 使用LLM生成个性化问候 + greeting_message = await self._generate_llm_greeting(username, greeting_style) + else: + # 使用模板生成问候 + greeting_message = await self._generate_template_greeting(template, username, greeting_style) + + # 发送问候消息 + await self.send_text(greeting_message) + + # 可选发送表情 + if enable_emoji: + emojis = ["😊", "👋", "🎉", "✨", "🌟"] + selected_emoji = random.choice(emojis) + await self.send_type("emoji", selected_emoji) + + logger.info(f"{self.log_prefix} 智能问候执行成功") + return True, f"向{username or '用户'}发送了{greeting_style}风格的问候" + + except Exception as e: + logger.error(f"{self.log_prefix} 智能问候执行失败: {e}") + return False, f"问候失败: {str(e)}" + + async def _generate_template_greeting(self, template: str, username: str, style: str) -> str: + """使用模板生成问候消息""" + # 根据风格调整问候语 + style_templates = { + "casual": "嗨{username}!很开心见到你~", + "formal": "您好{username},很荣幸为您服务!", + "friendly": "你好{username}!欢迎来到这里,希望我们能成为好朋友!😊" + } + + selected_template = style_templates.get(style, template) + username_display = f" {username}" if username else "" + + return selected_template.format(username=username_display) + + async def _generate_llm_greeting(self, username: str, style: str) -> str: + """使用LLM生成个性化问候""" + try: + # 获取可用模型 + models = self.api.get_available_models() + if not models: + logger.warning(f"{self.log_prefix} 无可用LLM模型,使用默认问候") + return await self._generate_template_greeting("你好{username}!", username, style) + + # 构建提示词 + prompt = f""" +请生成一个{style}风格的问候消息。 +用户名: {username or "用户"} +要求: +- 风格: {style} +- 简洁友好 +- 不超过50字 +- 符合中文表达习惯 +""" + + # 调用LLM + model_config = next(iter(models.values())) + success, response, reasoning, model_name = await self.api.generate_with_model( + prompt=prompt, + model_config=model_config, + request_type="plugin.greeting", + temperature=0.7, + max_tokens=100 + ) + + if success and response: + return response.strip() + else: + logger.warning(f"{self.log_prefix} LLM生成失败,使用默认问候") + return await self._generate_template_greeting("你好{username}!", username, style) + + except Exception as e: + logger.error(f"{self.log_prefix} LLM问候生成异常: {e}") + return await self._generate_template_greeting("你好{username}!", username, style) + + +class HelpfulAction(BaseAction): + """智能帮助Action - 展示LLM_JUDGE激活类型和随机激活的综合示例""" + + # ===== 激活控制必须项 ===== + focus_activation_type = ActionActivationType.LLM_JUDGE + normal_activation_type = ActionActivationType.RANDOM + mode_enable = ChatMode.ALL + parallel_action = True + + # ===== 基本信息必须项 ===== + action_name = "helpful_assistant" + action_description = "智能助手Action,主动提供帮助和建议,展示LLM判断激活" + + # LLM判断提示词 + llm_judge_prompt = """ + 判定是否需要使用智能帮助动作的条件: + 1. 用户表达了困惑或需要帮助 + 2. 用户提出了问题但没有得到满意答案 + 3. 对话中出现了技术术语或复杂概念 + 4. 用户似乎在寻找解决方案 + 5. 适合提供额外信息或建议的场合 + + 不要使用的情况: + 1. 用户明确表示不需要帮助 + 2. 对话进行得很顺利,无需干预 + 3. 用户只是在闲聊,没有实际需求 + + 请回答"是"或"否"。 + """ + + # 随机激活概率 + random_activation_probability = 0.15 + + # ===== 功能定义必须项 ===== + action_parameters = { + "help_type": "帮助类型:explanation(解释)、suggestion(建议)、guidance(指导)、tips(提示)", + "topic": "帮助主题或用户关心的问题", + "complexity": "复杂度:simple(简单)、medium(中等)、advanced(高级)" + } + + action_require = [ + "用户表达困惑或寻求帮助时使用", + "检测到用户遇到技术问题时使用", + "对话中出现知识盲点时主动提供帮助", + "避免过度频繁地提供帮助,要恰到好处" + ] + + associated_types = ["text", "emoji"] + + async def execute(self) -> Tuple[bool, str]: + """执行智能帮助""" + logger.info(f"{self.log_prefix} 执行智能帮助动作: {self.reasoning}") + + try: + # 获取参数 + help_type = self.action_data.get("help_type", "suggestion") + topic = self.action_data.get("topic", "") + complexity = self.action_data.get("complexity", "simple") + + # 根据帮助类型生成响应 + help_message = await self._generate_help_message(help_type, topic, complexity) + + # 发送帮助消息 + await self.send_text(help_message) + + # 可选发送鼓励表情 + if self.api.get_config("help.enable_emoji", True): + emojis = ["💡", "🤔", "💪", "🎯", "✨"] + selected_emoji = random.choice(emojis) + await self.send_type("emoji", selected_emoji) + + logger.info(f"{self.log_prefix} 智能帮助执行成功") + return True, f"提供了{help_type}类型的帮助,主题:{topic}" + + except Exception as e: + logger.error(f"{self.log_prefix} 智能帮助执行失败: {e}") + return False, f"帮助失败: {str(e)}" + + async def _generate_help_message(self, help_type: str, topic: str, complexity: str) -> str: + """生成帮助消息""" + # 获取配置 + enable_llm = self.api.get_config("help.enable_llm", False) + + if enable_llm: + return await self._generate_llm_help(help_type, topic, complexity) + else: + return await self._generate_template_help(help_type, topic, complexity) + + async def _generate_template_help(self, help_type: str, topic: str, complexity: str) -> str: + """使用模板生成帮助消息""" + help_templates = { + "explanation": f"关于{topic},我来为你解释一下:这是一个{complexity}级别的概念...", + "suggestion": f"针对{topic},我建议你可以尝试以下方法...", + "guidance": f"在{topic}方面,我可以为你提供一些指导...", + "tips": f"关于{topic},这里有一些实用的小贴士..." + } + + base_message = help_templates.get(help_type, f"关于{topic},我很乐意为你提供帮助!") + + # 根据复杂度调整消息 + if complexity == "advanced": + base_message += "\n\n这个话题比较深入,需要一些基础知识。" + elif complexity == "simple": + base_message += "\n\n这个概念其实很简单,让我用通俗的话来说明。" + + return base_message + + async def _generate_llm_help(self, help_type: str, topic: str, complexity: str) -> str: + """使用LLM生成个性化帮助""" + try: + models = self.api.get_available_models() + if not models: + return await self._generate_template_help(help_type, topic, complexity) + + prompt = f""" +请生成一个{help_type}类型的帮助消息。 +主题: {topic} +复杂度: {complexity} +要求: +- 风格友好、耐心 +- 内容准确、有用 +- 长度适中(100-200字) +- 根据复杂度调整语言难度 +""" + + model_config = next(iter(models.values())) + success, response, reasoning, model_name = await self.api.generate_with_model( + prompt=prompt, + model_config=model_config, + request_type="plugin.help", + temperature=0.7, + max_tokens=300 + ) + + if success and response: + return response.strip() + else: + return await self._generate_template_help(help_type, topic, complexity) + + except Exception as e: + logger.error(f"{self.log_prefix} LLM帮助生成异常: {e}") + return await self._generate_template_help(help_type, topic, complexity) # ===== Command组件 ===== @@ -405,6 +656,7 @@ class ExampleComprehensivePlugin(BasePlugin): # 从配置获取组件启用状态 enable_greeting = self.get_config("components.enable_greeting", True) + enable_helpful = self.get_config("components.enable_helpful", True) enable_help = self.get_config("components.enable_help", True) enable_send = self.get_config("components.enable_send", True) enable_echo = self.get_config("components.enable_echo", True) @@ -412,16 +664,12 @@ class ExampleComprehensivePlugin(BasePlugin): enable_dice = self.get_config("components.enable_dice", True) components = [] - # 添加Action组件 + # 添加Action组件 - 使用类中定义的所有属性 if enable_greeting: - components.append( - ( - SmartGreetingAction.get_action_info( - name="smart_greeting", description="智能问候系统,基于关键词触发" - ), - SmartGreetingAction, - ) - ) + components.append((SmartGreetingAction.get_action_info(), SmartGreetingAction)) + + if enable_helpful: + components.append((HelpfulAction.get_action_info(), HelpfulAction)) # 添加Command组件 if enable_help: diff --git a/src/plugin_system/core/plugin_manager.py b/src/plugin_system/core/plugin_manager.py index 2304a838f..17f532b30 100644 --- a/src/plugin_system/core/plugin_manager.py +++ b/src/plugin_system/core/plugin_manager.py @@ -32,7 +32,7 @@ class PluginManager: def _ensure_plugin_directories(self): """确保所有插件目录存在,如果不存在则创建""" - default_directories = ["src/plugins/built_in", "src/plugins/examples", "plugins"] + default_directories = ["src/plugins/built_in", "plugins"] for directory in default_directories: if not os.path.exists(directory):