diff --git a/docs/plugins/PLUS_COMMAND_GUIDE.md b/docs/plugins/PLUS_COMMAND_GUIDE.md index 2725b703d..a146cdda1 100644 --- a/docs/plugins/PLUS_COMMAND_GUIDE.md +++ b/docs/plugins/PLUS_COMMAND_GUIDE.md @@ -1,5 +1,12 @@ # 增强命令系统使用指南 +> ⚠️ **重要:插件命令必须使用 PlusCommand!** +> +> - ✅ **推荐**:`PlusCommand` - 插件开发的标准基类 +> - ❌ **禁止**:`BaseCommand` - 仅供框架内部使用 +> +> 如果你直接使用 `BaseCommand`,将需要手动处理参数解析、正则匹配等复杂逻辑,并且 `execute()` 方法签名也不同。 + ## 概述 增强命令系统是MoFox-Bot插件系统的一个扩展,让命令的定义和使用变得更加简单直观。你不再需要编写复杂的正则表达式,只需要定义命令名、别名和参数处理逻辑即可。 @@ -224,24 +231,95 @@ class ConfigurableCommand(PlusCommand): ## 返回值说明 -`execute`方法需要返回一个三元组: +`execute`方法必须返回一个三元组: ```python -return (执行成功标志, 可选消息, 是否拦截后续处理) +async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]: + # ... 你的逻辑 ... + return (执行成功标志, 日志描述, 是否拦截消息) ``` -- **执行成功标志** (bool): True表示命令执行成功,False表示失败 -- **可选消息** (Optional[str]): 用于日志记录的消息 -- **是否拦截后续处理** (bool): True表示拦截消息,不进行后续处理 +### 返回值详解 + +| 位置 | 类型 | 名称 | 说明 | +|------|------|------|------| +| 1 | `bool` | 执行成功标志 | `True` = 命令执行成功
`False` = 命令执行失败 | +| 2 | `Optional[str]` | 日志描述 | 用于内部日志记录的描述性文本
⚠️ **不是发给用户的消息!** | +| 3 | `bool` | 是否拦截消息 | `True` = 拦截,阻止后续处理(推荐)
`False` = 不拦截,继续后续处理 | + +### 重要:消息发送 vs 日志描述 + +⚠️ **常见错误:在返回值中返回用户消息** + +```python +# ❌ 错误做法 - 不要这样做! +async def execute(self, args: CommandArgs): + message = "你好,这是给用户的消息" + return True, message, True # 这个消息不会发给用户! + +# ✅ 正确做法 - 使用 self.send_text() +async def execute(self, args: CommandArgs): + await self.send_text("你好,这是给用户的消息") # 发送给用户 + return True, "执行了问候命令", True # 日志描述 +``` + +### 完整示例 + +```python +async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]: + """execute 方法的完整示例""" + + # 1. 参数验证 + if args.is_empty(): + await self.send_text("⚠️ 请提供参数") + return True, "缺少参数", True + + # 2. 执行逻辑 + user_input = args.get_raw() + result = process_input(user_input) + + # 3. 发送消息给用户 + await self.send_text(f"✅ 处理结果:{result}") + + # 4. 返回:成功、日志描述、拦截消息 + return True, f"处理了用户输入: {user_input[:20]}", True +``` + +### 拦截标志使用指导 + +- **返回 `True`**(推荐):命令已完成处理,不需要后续处理(如 LLM 回复) +- **返回 `False`**:允许系统继续处理(例如让 LLM 也回复) ## 最佳实践 -1. **命令命名**:使用简短、直观的命令名 -2. **别名设置**:为常用命令提供简短别名 -3. **参数验证**:总是检查参数的有效性 -4. **错误处理**:提供清晰的错误提示和使用说明 -5. **配置支持**:重要设置应该可配置 -6. **聊天类型**:根据命令功能选择合适的聊天类型限制 +### 1. 命令设计 +- ✅ **命令命名**:使用简短、直观的命令名(如 `time`、`help`、`status`) +- ✅ **别名设置**:为常用命令提供简短别名(如 `echo` -> `e`、`say`) +- ✅ **聊天类型**:根据命令功能选择 `ChatType.ALL`/`GROUP`/`PRIVATE` + +### 2. 参数处理 +- ✅ **总是验证**:使用 `args.is_empty()`、`args.count()` 检查参数 +- ✅ **友好提示**:参数错误时提供清晰的用法说明 +- ✅ **默认值**:为可选参数提供合理的默认值 + +### 3. 消息发送 +- ✅ **使用 `self.send_text()`**:发送消息给用户 +- ❌ **不要在返回值中返回用户消息**:返回值是日志描述 +- ✅ **拦截消息**:大多数情况返回 `True` 作为第三个参数 + +### 4. 错误处理 +- ✅ **Try-Catch**:捕获并处理可能的异常 +- ✅ **清晰反馈**:告诉用户发生了什么问题 +- ✅ **记录日志**:在返回值中提供有用的调试信息 + +### 5. 配置管理 +- ✅ **可配置化**:重要设置应该通过 `self.get_config()` 读取 +- ✅ **提供默认值**:即使配置缺失也能正常工作 + +### 6. 代码质量 +- ✅ **类型注解**:使用完整的类型提示 +- ✅ **文档字符串**:为 `execute()` 方法添加文档说明 +- ✅ **代码注释**:为复杂逻辑添加必要的注释 ## 完整示例 diff --git a/docs/plugins/README.md b/docs/plugins/README.md new file mode 100644 index 000000000..e9869a8e9 --- /dev/null +++ b/docs/plugins/README.md @@ -0,0 +1,265 @@ +# 📚 MoFox-Bot 插件开发文档导航 + +欢迎来到 MoFox-Bot 插件系统开发文档!本文档帮助你快速找到所需的学习资源。 + +--- + +## 🎯 我应该从哪里开始? + +### 第一次接触插件开发? +👉 **从这里开始**:[快速开始指南](quick-start.md) + +这是一个循序渐进的教程,带你从零开始创建第一个插件,包含完整的代码示例。 + +### 遇到问题了? +👉 **先看这里**:[故障排除指南](troubleshooting-guide.md) ⭐ + +包含10个最常见问题的解决方案,可能5分钟就能解决你的问题。 + +### 想深入了解特定功能? +👉 **查看下方分类导航**,找到你需要的文档。 + +--- + +## 📖 学习路径建议 + +### 🌟 新手路径(按顺序阅读) + +1. **[快速开始指南](quick-start.md)** ⭐ 必读 + - 创建插件目录和配置 + - 实现第一个 Action 组件 + - 实现第一个 Command 组件 + - 添加配置文件 + - 预计阅读时间:30-45分钟 + +2. **[增强命令指南](PLUS_COMMAND_GUIDE.md)** ⭐ 必读 + - 理解 PlusCommand 与 BaseCommand 的区别 + - 学习命令参数处理 + - 掌握返回值规范 + - 预计阅读时间:20-30分钟 + +3. **[Action 组件详解](action-components.md)** ⭐ 必读 + - 理解 Action 的激活机制 + - 学习自定义激活逻辑 + - 掌握 Action 的使用场景 + - 预计阅读时间:25-35分钟 + +4. **[故障排除指南](troubleshooting-guide.md)** ⭐ 建议收藏 + - 常见错误及解决方案 + - 最佳实践速查 + - 调试技巧 + - 随时查阅 + +--- + +### 🚀 进阶路径(根据需求选择) + +#### 需要配置系统? +- **[配置文件系统指南](configuration-guide.md)** + - 自动生成配置文件 + - 配置 Schema 定义 + - 配置读取和验证 + +#### 需要响应事件? +- **[事件系统指南](event-system-guide.md)** + - 订阅系统事件 + - 创建自定义事件 + - 事件处理器实现 + +#### 需要集成外部功能? +- **[Tool 组件指南](tool_guide.md)** + - 为 LLM 提供工具调用能力 + - 函数调用集成 + - Tool 参数定义 + +#### 需要依赖其他插件? +- **[依赖管理指南](dependency-management.md)** + - 声明插件依赖 + - Python 包依赖 + - 依赖版本管理 + +#### 需要高级激活控制? +- **[Action 激活机制重构指南](action-activation-guide.md)** + - 自定义激活逻辑 + - 关键词匹配激活 + - LLM 智能判断激活 + - 随机激活策略 + +--- + +## 📂 文档结构说明 + +### 核心文档(必读) + +``` +📄 quick-start.md 快速开始指南 ⭐ 新手必读 +📄 PLUS_COMMAND_GUIDE.md 增强命令系统指南 ⭐ 必读 +📄 action-components.md Action 组件详解 ⭐ 必读 +📄 troubleshooting-guide.md 故障排除指南 ⭐ 遇到问题先看这个 +``` + +### 进阶文档(按需阅读) + +``` +📄 configuration-guide.md 配置系统详解 +📄 event-system-guide.md 事件系统详解 +📄 tool_guide.md Tool 组件详解 +📄 action-activation-guide.md Action 激活机制详解 +📄 dependency-management.md 依赖管理详解 +📄 manifest-guide.md Manifest 文件规范 +``` + +### API 参考文档 + +``` +📁 api/ API 参考文档目录 + ├── 消息相关 + │ ├── send-api.md 消息发送 API + │ ├── message-api.md 消息处理 API + │ └── chat-api.md 聊天流 API + │ + ├── AI 相关 + │ ├── llm-api.md LLM 交互 API + │ └── generator-api.md 回复生成 API + │ + ├── 数据相关 + │ ├── database-api.md 数据库操作 API + │ ├── config-api.md 配置读取 API + │ └── person-api.md 人物关系 API + │ + ├── 组件相关 + │ ├── plugin-manage-api.md 插件管理 API + │ └── component-manage-api.md 组件管理 API + │ + └── 其他 + ├── emoji-api.md 表情包 API + ├── tool-api.md 工具 API + └── logging-api.md 日志 API +``` + +### 其他文件 + +``` +📄 index.md 文档索引(旧版,建议查看本 README) +``` + +--- + +## 🎓 按功能查找文档 + +### 我想创建... + +| 目标 | 推荐文档 | 难度 | +|------|----------|------| +| **一个简单的命令** | [快速开始](quick-start.md) → [增强命令指南](PLUS_COMMAND_GUIDE.md) | ⭐ 入门 | +| **一个智能 Action** | [快速开始](quick-start.md) → [Action 组件](action-components.md) | ⭐⭐ 中级 | +| **带复杂参数的命令** | [增强命令指南](PLUS_COMMAND_GUIDE.md) | ⭐⭐ 中级 | +| **需要配置的插件** | [配置系统指南](configuration-guide.md) | ⭐⭐ 中级 | +| **响应系统事件的插件** | [事件系统指南](event-system-guide.md) | ⭐⭐⭐ 高级 | +| **为 LLM 提供工具** | [Tool 组件指南](tool_guide.md) | ⭐⭐⭐ 高级 | +| **依赖其他插件的插件** | [依赖管理指南](dependency-management.md) | ⭐⭐ 中级 | + +### 我想学习... + +| 主题 | 相关文档 | +|------|----------| +| **如何发送消息** | [发送 API](api/send-api.md) / [增强命令指南](PLUS_COMMAND_GUIDE.md) | +| **如何处理参数** | [增强命令指南](PLUS_COMMAND_GUIDE.md) | +| **如何使用 LLM** | [LLM API](api/llm-api.md) | +| **如何操作数据库** | [数据库 API](api/database-api.md) | +| **如何读取配置** | [配置 API](api/config-api.md) / [配置系统指南](configuration-guide.md) | +| **如何获取消息历史** | [消息 API](api/message-api.md) / [聊天流 API](api/chat-api.md) | +| **如何发送表情包** | [表情包 API](api/emoji-api.md) | +| **如何记录日志** | [日志 API](api/logging-api.md) | + +--- + +## 🆘 遇到问题? + +### 第一步:查看故障排除指南 +👉 [故障排除指南](troubleshooting-guide.md) 包含10个最常见问题的解决方案 + +### 第二步:查看相关文档 +- **插件无法加载?** → [快速开始指南](quick-start.md) +- **命令无响应?** → [增强命令指南](PLUS_COMMAND_GUIDE.md) +- **Action 不触发?** → [Action 组件详解](action-components.md) +- **配置不生效?** → [配置系统指南](configuration-guide.md) + +### 第三步:检查日志 +查看 `logs/app_*.jsonl` 获取详细错误信息 + +### 第四步:寻求帮助 +- 在线文档:https://mofox-studio.github.io/MoFox-Bot-Docs/ +- GitHub Issues:提交详细的问题报告 +- 社区讨论:加入开发者社区 + +--- + +## 📌 重要提示 + +### ⚠️ 常见陷阱 + +1. **不要使用 `BaseCommand`** + - ✅ 使用:`PlusCommand` + - ❌ 避免:`BaseCommand`(仅供框架内部使用) + +2. **不要在返回值中返回用户消息** + - ✅ 使用:`await self.send_text("消息")` + - ❌ 避免:`return True, "消息", True` + +3. **手动创建 ComponentInfo 时必须指定 component_type** + - ✅ 推荐:使用 `get_action_info()` 自动生成 + - ⚠️ 手动创建时:必须指定 `component_type=ComponentType.ACTION` + +### 💡 最佳实践 + +- ✅ 总是使用类型注解 +- ✅ 为 `execute()` 方法添加文档字符串 +- ✅ 使用 `self.get_config()` 读取配置 +- ✅ 使用异步操作 `async/await` +- ✅ 在发送消息前验证参数 +- ✅ 提供清晰的错误提示 + +--- + +## 🔄 文档更新记录 + +### v1.1.0 (2024-12-17) +- ✨ 新增 [故障排除指南](troubleshooting-guide.md) +- ✅ 修复 [快速开始指南](quick-start.md) 中的 BaseCommand 示例 +- ✅ 增强 [增强命令指南](PLUS_COMMAND_GUIDE.md) 的返回值说明 +- ✅ 完善 [Action 组件](action-components.md) 的 component_type 说明 +- 📝 创建本导航文档 + +### v1.0.0 (2024-11) +- 📚 初始文档发布 + +--- + +## 📞 反馈与贡献 + +如果你发现文档中的错误或有改进建议: + +1. **提交 Issue**:在 GitHub 仓库提交文档问题 +2. **提交 PR**:直接修改文档并提交 Pull Request +3. **社区反馈**:在社区讨论中提出建议 + +你的反馈对我们改进文档至关重要!🙏 + +--- + +## 🎉 开始你的插件开发之旅 + +准备好了吗?从这里开始: + +1. 📖 阅读 [快速开始指南](quick-start.md) +2. 💻 创建你的第一个插件 +3. 🔧 遇到问题查看 [故障排除指南](troubleshooting-guide.md) +4. 🚀 探索更多高级功能 + +**祝你开发愉快!** 🎊 + +--- + +**最后更新**:2024-12-17 +**文档版本**:v1.1.0 diff --git a/docs/plugins/action-components.md b/docs/plugins/action-components.md index d93f6bfb0..23ac599a6 100644 --- a/docs/plugins/action-components.md +++ b/docs/plugins/action-components.md @@ -38,11 +38,44 @@ class ExampleAction(BaseAction): 执行Action的主要逻辑 Returns: - Tuple[bool, str]: (是否成功, 执行结果描述) + Tuple[bool, str]: 两个元素的元组 + - bool: 是否执行成功 (True=成功, False=失败) + - str: 执行结果的简短描述(用于日志记录) + + 注意: + - 使用 self.send_text() 等方法发送消息给用户 + - 返回值中的描述仅用于内部日志,不会发送给用户 """ - # ---- 执行动作的逻辑 ---- + # 发送消息给用户 + await self.send_text("这是发给用户的消息") + + # 返回执行结果(用于日志) return True, "执行成功" ``` + +#### execute() 返回值 vs Command 返回值 + +⚠️ **重要:Action 和 Command 的返回值不同!** + +| 组件类型 | 返回值 | 说明 | +|----------|----------|------| +| **Action** | `Tuple[bool, str]` | 2个元素:成功标志、日志描述 | +| **Command** | `Tuple[bool, Optional[str], bool]` | 3个元素:成功标志、日志描述、拦截标志 | + +```python +# Action 返回值 +async def execute(self) -> Tuple[bool, str]: + await self.send_text("给用户的消息") + return True, "日志:执行了XX动作" # 2个元素 + +# Command 返回值 +async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]: + await self.send_text("给用户的消息") + return True, "日志:执行了XX命令", True # 3个元素 +``` + +--- + #### associated_types: 该Action会发送的消息类型,例如文本、表情等。 这部分由Adapter传递给处理器。 @@ -68,6 +101,65 @@ class ExampleAction(BaseAction): --- +## 组件信息注册说明 + +### 自动生成 ComponentInfo(推荐) + +大多数情况下,你不需要手动创建 `ActionInfo` 对象。系统提供了 `get_action_info()` 方法来自动生成: + +```python +# 推荐的方式 - 自动生成 +class HelloAction(BaseAction): + action_name = "hello" + action_description = "问候动作" + # ... 其他配置 ... + +# 在插件中注册 +def get_plugin_components(self): + return [ + (HelloAction.get_action_info(), HelloAction), # 自动生成 ActionInfo + ] +``` + +### 手动创建 ActionInfo(高级用法) + +⚠️ **重要:如果手动创建 ActionInfo,必须指定 `component_type` 参数!** + +当你需要自定义 `ActionInfo` 时(例如动态生成组件),必须手动指定 `component_type`: + +```python +from src.plugin_system import ActionInfo, ComponentType + +# ❌ 错误 - 缺少 component_type +action_info = ActionInfo( + name="hello", + description="问候动作" + # 错误:会报错 "missing required argument: 'component_type'" +) + +# ✅ 正确 - 必须指定 component_type +action_info = ActionInfo( + name="hello", + description="问候动作", + component_type=ComponentType.ACTION # 必须指定! +) +``` + +**为什么需要手动指定?** + +- `get_action_info()` 方法会自动设置 `component_type` +- 但手动创建时,系统无法自动推断类型,必须明确指定 + +**什么时候需要手动创建?** + +- 动态生成组件 +- 自定义 `get_handler_info()` 方法 +- 需要特殊的 ComponentInfo 配置 + +大多数情况下,直接使用 `get_action_info()` 即可,无需手动创建。 + +--- + ## 🎯 Action 调用的决策机制 Action采用**两层决策机制**来优化性能和决策质量: diff --git a/docs/plugins/index.md b/docs/plugins/index.md index c39efe72e..ddd76a5f1 100644 --- a/docs/plugins/index.md +++ b/docs/plugins/index.md @@ -5,6 +5,7 @@ ## 新手入门 - [📖 快速开始指南](quick-start.md) - 快速创建你的第一个插件 +- [🔧 故障排除指南](troubleshooting-guide.md) - 快速解决常见问题 ⭐ **新增** ## 组件功能详解 diff --git a/docs/plugins/quick-start.md b/docs/plugins/quick-start.md index ff32a43eb..4c1c973fe 100644 --- a/docs/plugins/quick-start.md +++ b/docs/plugins/quick-start.md @@ -195,29 +195,35 @@ Command是最简单,最直接的响应,不由LLM判断选择使用 ```python # 在现有代码基础上,添加Command组件 import datetime -from src.plugin_system import BaseCommand -#导入Command基类 +from src.plugin_system import PlusCommand, CommandArgs +# 导入增强命令基类 - 推荐使用! -class TimeCommand(BaseCommand): +class TimeCommand(PlusCommand): """时间查询Command - 响应/time命令""" command_name = "time" command_description = "查询当前时间" - # === 命令设置(必须填写)=== - command_pattern = r"^/time$" # 精确匹配 "/time" 命令 + # 注意:使用 PlusCommand 不需要 command_pattern,会自动生成! - async def execute(self) -> Tuple[bool, Optional[str], bool]: - """执行时间查询""" + async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]: + """执行时间查询 + + Args: + args: 命令参数(本例中不使用) + + Returns: + (成功标志, 日志描述, 是否拦截消息) + """ # 获取当前时间 time_format: str = "%Y-%m-%d %H:%M:%S" now = datetime.datetime.now() time_str = now.strftime(time_format) - # 发送时间信息 - message = f"⏰ 当前时间:{time_str}" - await self.send_text(message) + # 发送时间信息给用户 + await self.send_text(f"⏰ 当前时间:{time_str}") + # 返回:成功、日志描述、拦截消息 return True, f"显示了当前时间: {time_str}", True @register_plugin @@ -239,14 +245,29 @@ class HelloWorldPlugin(BasePlugin): ] ``` -同样的,我们通过 `get_plugin_components()` 方法,通过调用`get_action_info()`这个内置方法将 `TimeCommand` 注册为插件的一个组件。 +同样的,我们通过 `get_plugin_components()` 方法,通过调用`get_command_info()`这个内置方法将 `TimeCommand` 注册为插件的一个组件。 **Command组件解释:** -- `command_pattern` 使用正则表达式匹配用户输入 -- `^/time$` 表示精确匹配 "/time" +> ⚠️ **重要:请使用 PlusCommand 而不是 BaseCommand!** +> +> - ✅ **PlusCommand**:推荐使用,自动处理参数解析,无需编写正则表达式 +> - ❌ **BaseCommand**:仅供框架内部使用,插件开发者不应直接使用 -有关 Command 组件的更多信息,请参考 [Command组件指南](./command-components.md)。 +**PlusCommand 的优势:** +- ✅ 无需编写 `command_pattern` 正则表达式 +- ✅ 自动解析命令参数(通过 `CommandArgs`) +- ✅ 支持命令别名(`command_aliases`) +- ✅ 更简单的 API,更容易上手 + +**execute() 方法说明:** +- 参数:`args: CommandArgs` - 包含解析后的命令参数 +- 返回值:`(bool, str, bool)` 三元组 + - `bool`:命令是否执行成功 + - `str`:日志描述(**不是发给用户的消息**) + - `bool`:是否拦截消息,阻止后续处理 + +有关增强命令的详细信息,请参考 [增强命令指南](./PLUS_COMMAND_GUIDE.md)。 ### 8. 测试时间查询Command @@ -377,28 +398,31 @@ class HelloAction(BaseAction): return True, "发送了问候消息" -class TimeCommand(BaseCommand): +class TimeCommand(PlusCommand): """时间查询Command - 响应/time命令""" command_name = "time" command_description = "查询当前时间" - # === 命令设置(必须填写)=== - command_pattern = r"^/time$" # 精确匹配 "/time" 命令 + # 注意:PlusCommand 不需要 command_pattern! - async def execute(self) -> Tuple[bool, str, bool]: - """执行时间查询""" + async def execute(self, args: CommandArgs) -> Tuple[bool, str, bool]: + """执行时间查询 + + Args: + args: 命令参数对象 + """ import datetime - # 获取当前时间 + # 从配置获取时间格式 time_format: str = self.get_config("time.format", "%Y-%m-%d %H:%M:%S") # type: ignore now = datetime.datetime.now() time_str = now.strftime(time_format) - # 发送时间信息 - message = f"⏰ 当前时间:{time_str}" - await self.send_text(message) + # 发送时间信息给用户 + await self.send_text(f"⏰ 当前时间:{time_str}") + # 返回:成功、日志描述、拦截消息 return True, f"显示了当前时间: {time_str}", True ``` diff --git a/docs/plugins/troubleshooting-guide.md b/docs/plugins/troubleshooting-guide.md new file mode 100644 index 000000000..c65bcb576 --- /dev/null +++ b/docs/plugins/troubleshooting-guide.md @@ -0,0 +1,395 @@ +# 🔧 插件开发故障排除指南 + +本指南帮助你快速解决 MoFox-Bot 插件开发中的常见问题。 + +--- + +## 📋 快速诊断清单 + +遇到问题时,首先按照以下步骤检查: + +1. ✅ 检查日志文件 `logs/app_*.jsonl` +2. ✅ 确认插件已在 `_manifest.json` 中正确配置 +3. ✅ 验证你使用的是 `PlusCommand` 而不是 `BaseCommand` +4. ✅ 检查 `execute()` 方法签名是否正确 +5. ✅ 确认返回值格式正确 + +--- + +## 🔴 严重问题:插件无法加载 + +### 错误 #1: "未检测到插件" + +**症状**: +- 插件目录存在,但日志中没有加载信息 +- `get_plugin_components()` 返回空列表 + +**可能原因与解决方案**: + +#### ❌ 缺少 `@register_plugin` 装饰器 + +```python +# 错误 - 缺少装饰器 +class MyPlugin(BasePlugin): # 不会被检测到 + pass + +# 正确 - 添加装饰器 +@register_plugin # 必须添加! +class MyPlugin(BasePlugin): + pass +``` + +#### ❌ `plugin.py` 文件不存在或位置错误 + +``` +plugins/ + └── my_plugin/ + ├── _manifest.json ✅ + └── plugin.py ✅ 必须在这里 +``` + +#### ❌ `_manifest.json` 格式错误 + +```json +{ + "manifest_version": 1, + "name": "My Plugin", + "version": "1.0.0", + "description": "插件描述", + "author": { + "name": "Your Name" + } +} +``` + +--- + +### 错误 #2: "ActionInfo.__init__() missing required argument: 'component_type'" + +**症状**: +``` +TypeError: ActionInfo.__init__() missing 1 required positional argument: 'component_type' +``` + +**原因**:手动创建 `ActionInfo` 时未指定 `component_type` 参数 + +**解决方案**: + +```python +from src.plugin_system import ActionInfo, ComponentType + +# ❌ 错误 - 缺少 component_type +action_info = ActionInfo( + name="my_action", + description="我的动作" +) + +# ✅ 正确方法 1 - 使用自动生成(推荐) +class MyAction(BaseAction): + action_name = "my_action" + action_description = "我的动作" + +def get_plugin_components(self): + return [ + (MyAction.get_action_info(), MyAction) # 自动生成,推荐! + ] + +# ✅ 正确方法 2 - 手动指定 component_type +action_info = ActionInfo( + name="my_action", + description="我的动作", + component_type=ComponentType.ACTION # 必须指定! +) +``` + +--- + +## 🟡 命令问题:命令无响应 + +### 错误 #3: 命令被识别但不执行 + +**症状**: +- 输入 `/mycommand` 后没有任何反应 +- 日志显示命令已匹配但未执行 + +**可能原因与解决方案**: + +#### ❌ 使用了 `BaseCommand` 而不是 `PlusCommand` + +```python +# ❌ 错误 - 使用 BaseCommand +from src.plugin_system import BaseCommand + +class MyCommand(BaseCommand): # 不推荐! + command_name = "mycommand" + command_pattern = r"^/mycommand$" # 需要手动写正则 + + async def execute(self): # 签名错误! + pass + +# ✅ 正确 - 使用 PlusCommand +from src.plugin_system import PlusCommand, CommandArgs + +class MyCommand(PlusCommand): # 推荐! + command_name = "mycommand" + # 不需要 command_pattern,会自动生成! + + async def execute(self, args: CommandArgs): # 正确签名 + await self.send_text("命令执行成功") + return True, "执行了mycommand", True +``` + +#### ❌ `execute()` 方法签名错误 + +```python +# ❌ 错误的签名(缺少 args 参数) +async def execute(self) -> Tuple[bool, Optional[str], bool]: + pass + +# ❌ 错误的签名(参数类型错误) +async def execute(self, args: list[str]) -> Tuple[bool, Optional[str], bool]: + pass + +# ✅ 正确的签名 +async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]: + await self.send_text("响应用户") + return True, "日志描述", True +``` + +--- + +### 错误 #4: 命令发送了消息但用户没收到 + +**症状**: +- 日志显示命令执行成功 +- 但用户没有收到任何消息 + +**原因**:在返回值中返回消息,而不是使用 `self.send_text()` + +**解决方案**: + +```python +# ❌ 错误 - 在返回值中返回消息 +async def execute(self, args: CommandArgs): + message = "这是给用户的消息" + return True, message, True # 这不会发送给用户! + +# ✅ 正确 - 使用 self.send_text() +async def execute(self, args: CommandArgs): + # 发送消息给用户 + await self.send_text("这是给用户的消息") + + # 返回日志描述(不是用户消息) + return True, "执行了某个操作", True +``` + +--- + +### 错误 #5: "notice处理失败" 或重复消息 + +**症状**: +- 日志中出现 "notice处理失败" +- 用户收到重复的消息 + +**原因**:同时使用了 `send_api.send_text()` 和返回消息 + +**解决方案**: + +```python +# ❌ 错误 - 混用不同的发送方式 +from src.plugin_system.apis.chat_api import send_api + +async def execute(self, args: CommandArgs): + await send_api.send_text(self.stream_id, "消息1") # 不要这样做 + return True, "消息2", True # 也不要返回消息 + +# ✅ 正确 - 只使用 self.send_text() +async def execute(self, args: CommandArgs): + await self.send_text("这是唯一的消息") # 推荐方式 + return True, "日志:执行成功", True # 仅用于日志 +``` + +--- + +## 🟢 配置问题 + +### 错误 #6: 配置警告 "配置中不存在字空间或键" + +**症状**: +``` +获取全局配置 plugins.my_plugin 失败: "配置中不存在字空间或键 'plugins'" +``` + +**这是正常的吗?** + +✅ **是的,这是正常行为!** 不需要修复。 + +**说明**: +- 系统首先尝试从全局配置加载:`config/plugins/my_plugin/config.toml` +- 如果不存在,会自动回退到插件本地配置:`plugins/my_plugin/config.toml` +- 这个警告可以安全忽略 + +**如果你想消除警告**: +1. 在 `config/plugins/` 目录创建你的插件配置目录 +2. 或者直接忽略 - 使用本地配置完全正常 + +--- + +## 🔧 返回值问题 + +### 错误 #7: 返回值格式错误 + +**Action 返回值** (2个元素): +```python +async def execute(self) -> Tuple[bool, str]: + await self.send_text("消息") + return True, "日志描述" # 2个元素 +``` + +**Command 返回值** (3个元素): +```python +async def execute(self, args: CommandArgs) -> Tuple[bool, Optional[str], bool]: + await self.send_text("消息") + return True, "日志描述", True # 3个元素(增加了拦截标志) +``` + +**对比表格**: + +| 组件类型 | 返回值 | 元素说明 | +|----------|--------|----------| +| **Action** | `(bool, str)` | (成功标志, 日志描述) | +| **Command** | `(bool, str, bool)` | (成功标志, 日志描述, 拦截标志) | + +--- + +## 🎯 参数解析问题 + +### 错误 #8: 无法获取命令参数 + +**症状**: +- `args` 为空或不包含预期的参数 + +**解决方案**: + +```python +async def execute(self, args: CommandArgs): + # 检查是否有参数 + if args.is_empty(): + await self.send_text("❌ 缺少参数\n用法: /command <参数>") + return True, "缺少参数", True + + # 获取原始参数字符串 + raw_input = args.get_raw() + + # 获取解析后的参数列表 + arg_list = args.get_args() + + # 获取第一个参数 + first_arg = args.get_first("默认值") + + # 获取指定索引的参数 + second_arg = args.get_arg(1, "默认值") + + # 检查标志 + if args.has_flag("--verbose"): + # 处理 --verbose 模式 + pass + + # 获取标志的值 + output = args.get_flag_value("--output", "default.txt") +``` + +--- + +## 📝 类型注解问题 + +### 错误 #9: IDE 报类型错误 + +**解决方案**:确保使用正确的类型导入 + +```python +from typing import Tuple, Optional, List, Type +from src.plugin_system import ( + BasePlugin, + PlusCommand, + BaseAction, + CommandArgs, + ComponentInfo, + CommandInfo, + ActionInfo, + ComponentType +) + +# 正确的类型注解 +def get_plugin_components(self) -> List[Tuple[ComponentInfo, Type]]: + return [ + (MyCommand.get_command_info(), MyCommand), + (MyAction.get_action_info(), MyAction) + ] +``` + +--- + +## 🚀 性能问题 + +### 错误 #10: 插件响应缓慢 + +**可能原因**: + +1. **阻塞操作**:在 `execute()` 中使用了同步 I/O +2. **大量数据处理**:在主线程处理大文件或复杂计算 +3. **频繁的数据库查询**:每次都查询数据库 + +**解决方案**: + +```python +import asyncio + +async def execute(self, args: CommandArgs): + # ✅ 使用异步操作 + result = await some_async_function() + + # ✅ 对于同步操作,使用 asyncio.to_thread + result = await asyncio.to_thread(blocking_function) + + # ✅ 批量数据库操作 + from src.common.database.optimization.batch_scheduler import get_batch_scheduler + scheduler = get_batch_scheduler() + await scheduler.schedule_batch_insert(Model, data_list) + + return True, "执行成功", True +``` + +--- + +## 📞 获取帮助 + +如果以上方案都无法解决你的问题: + +1. **查看日志**:检查 `logs/app_*.jsonl` 获取详细错误信息 +2. **查阅文档**: + - [快速开始指南](./quick-start.md) + - [增强命令指南](./PLUS_COMMAND_GUIDE.md) + - [Action组件指南](./action-components.md) +3. **在线文档**:https://mofox-studio.github.io/MoFox-Bot-Docs/ +4. **提交 Issue**:在 GitHub 仓库提交详细的问题报告 + +--- + +## 🎓 最佳实践速查 + +| 场景 | 推荐做法 | 避免 | +|------|----------|------| +| 创建命令 | 使用 `PlusCommand` | ❌ 使用 `BaseCommand` | +| 发送消息 | `await self.send_text()` | ❌ 在返回值中返回消息 | +| 注册组件 | 使用 `get_action_info()` | ❌ 手动创建不带 `component_type` 的 Info | +| 参数处理 | 使用 `CommandArgs` 方法 | ❌ 手动解析字符串 | +| 异步操作 | 使用 `async/await` | ❌ 使用同步阻塞操作 | +| 配置读取 | `self.get_config()` | ❌ 硬编码配置值 | + +--- + +**最后更新**:2024-12-17 +**版本**:v1.0.0 + +有问题欢迎反馈,帮助我们改进这份指南!