Files
Mofox-Core/docs/plugins/tool-components.md
雅诺狐 2d4745cd58 初始化
2025-08-11 19:34:18 +08:00

7.6 KiB
Raw Blame History

🔧 工具组件详解

📖 什么是工具

工具是MaiBot的信息获取能力扩展组件。如果说Action组件功能五花八门可以拓展麦麦能做的事情那么Tool就是在某个过程中拓宽了麦麦能够获得的信息量。

🎯 工具的特点

  • 🔍 信息获取增强:扩展麦麦获取外部信息的能力
  • 📊 数据丰富:帮助麦麦获得更多背景信息和实时数据
  • 🔌 插件式架构:支持独立开发和注册新工具
  • 自动发现:工具会被系统自动识别和注册

🆚 Tool vs Action vs Command 区别

特征 Action Command Tool
主要用途 扩展麦麦行为能力 响应用户指令 扩展麦麦信息获取
触发方式 麦麦智能决策 用户主动触发 LLM根据需要调用
目标 让麦麦做更多事情 提供具体功能 让麦麦知道更多信息
使用场景 增强交互体验 功能服务 信息查询和分析

🏗️ Tool组件的基本结构

每个工具必须继承 BaseTool 基类并实现以下属性和方法:

from src.plugin_system import BaseTool, ToolParamType

class MyTool(BaseTool):
    # 工具名称,必须唯一
    name = "my_tool"
    
    # 工具描述告诉LLM这个工具的用途
    description = "这个工具用于获取特定类型的信息"
    
    # 参数定义,仅定义参数
    # 比如想要定义一个类似下面的openai格式的参数表则可以这么定义:
    # {
    #     "type": "object",
    #     "properties": {
    #         "query": {
    #             "type": "string",
    #             "description": "查询参数"
    #         },
    #         "limit": {
    #             "type": "integer", 
    #             "description": "结果数量限制"
    #             "enum": [10, 20, 50]  # 可选值
    #         }
    #     },
    #     "required": ["query"]
    # }
    parameters = [
        ("query", ToolParamType.STRING, "查询参数", True, None),  # 必填参数
        ("limit", ToolParamType.INTEGER, "结果数量限制", False, ["10", "20", "50"])  # 可选参数
    ]

    available_for_llm = True  # 是否对LLM可用
    
    async def execute(self, function_args: Dict[str, Any]):
        """执行工具逻辑"""
        # 实现工具功能
        result = f"查询结果: {function_args.get('query')}"
        
        return {
            "name": self.name,
            "content": result
        }

属性说明

属性 类型 说明
name str 工具的唯一标识名称
description str 工具功能描述帮助LLM理解用途
parameters list[tuple] 参数定义

其构造而成的工具定义为:

definition: Dict[str, Any] = {"name": cls.name, "description": cls.description, "parameters": cls.parameters}

方法说明

方法 参数 返回值 说明
execute function_args dict 执行工具核心逻辑

🎨 完整工具示例

完成一个天气查询工具

from src.plugin_system import BaseTool
import aiohttp
import json

class WeatherTool(BaseTool):
    """天气查询工具 - 获取指定城市的实时天气信息"""
    
    name = "weather_query"
    description = "查询指定城市的实时天气信息,包括温度、湿度、天气状况等"
    available_for_llm = True  # 允许LLM调用此工具
    parameters = [
        ("city", ToolParamType.STRING, "要查询天气的城市名称,如:北京、上海、纽约", True, None),
        ("country", ToolParamType.STRING, "国家代码CN、US可选参数", False, None)
    ]
    
    async def execute(self, function_args: dict):
        """执行天气查询"""
        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()

🚨 注意事项和限制

当前限制

  1. 适用范围:主要适用于信息获取场景
  2. 配置要求:必须开启工具处理器

开发建议

  1. 功能专一:每个工具专注单一功能
  2. 参数明确:清晰定义工具参数和用途
  3. 错误处理:完善的异常处理和错误反馈
  4. 性能考虑:避免长时间阻塞操作
  5. 信息准确:确保获取信息的准确性和时效性

🎯 最佳实践

1. 工具命名规范

好的命名

name = "weather_query"        # 清晰表达功能
name = "knowledge_search"     # 描述性强
name = "stock_price_check"    # 功能明确

避免的命名

name = "tool1"               # 无意义
name = "wq"                  # 过于简短
name = "weather_and_news"    # 功能过于复杂

2. 描述规范

良好的描述

description = "查询指定城市的实时天气信息,包括温度、湿度、天气状况"

避免的描述

description = "天气"         # 过于简单
description = "获取信息"      # 不够具体

3. 参数设计

合理的参数设计

parameters = [
    ("city", ToolParamType.STRING, "城市名称,如:北京、上海", True, None),
    ("unit", ToolParamType.STRING, "温度单位celsius 或 fahrenheit", False, ["celsius", "fahrenheit"])
]

避免的参数设计

parameters = [
    ("data", "string", "数据", True)  # 参数过于模糊
]

4. 结果格式化

良好的结果格式

def _format_result(self, data):
    return f"""
🔍 查询结果
━━━━━━━━━━━━
📊 数据: {data['value']}
📅 时间: {data['timestamp']}
📝 说明: {data['description']}
━━━━━━━━━━━━
    """.strip()

避免的结果格式

def _format_result(self, data):
    return str(data)  # 直接返回原始数据