Files
Mofox-Core/docs/plugins/troubleshooting-guide.md
2025-12-17 09:44:51 +08:00

9.8 KiB
Raw Blame History

🔧 插件开发故障排除指南

本指南帮助你快速解决 MoFox-Bot 插件开发中的常见问题。


📋 快速诊断清单

遇到问题时,首先按照以下步骤检查:

  1. 检查日志文件 logs/app_*.jsonl
  2. 确认插件已在 _manifest.json 中正确配置
  3. 验证你使用的是 PlusCommand 而不是 BaseCommand
  4. 检查 execute() 方法签名是否正确
  5. 确认返回值格式正确

🔴 严重问题:插件无法加载

错误 #1: "未检测到插件"

症状

  • 插件目录存在,但日志中没有加载信息
  • get_plugin_components() 返回空列表

可能原因与解决方案

缺少 @register_plugin 装饰器

# 错误 - 缺少装饰器
class MyPlugin(BasePlugin):  # 不会被检测到
    pass

# 正确 - 添加装饰器
@register_plugin  # 必须添加!
class MyPlugin(BasePlugin):
    pass

plugin.py 文件不存在或位置错误

plugins/
  └── my_plugin/
      ├── _manifest.json     ✅
      └── plugin.py           ✅ 必须在这里

_manifest.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 参数

解决方案

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

# ❌ 错误 - 使用 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() 方法签名错误

# ❌ 错误的签名(缺少 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()

解决方案

# ❌ 错误 - 在返回值中返回消息
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() 和返回消息

解决方案

# ❌ 错误 - 混用不同的发送方式
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个元素)

async def execute(self) -> Tuple[bool, str]:
    await self.send_text("消息")
    return True, "日志描述"  # 2个元素

Command 返回值 (3个元素)

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 为空或不包含预期的参数

解决方案

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 报类型错误

解决方案:确保使用正确的类型导入

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. 频繁的数据库查询:每次都查询数据库

解决方案

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. 查阅文档
  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

有问题欢迎反馈,帮助我们改进这份指南!