from typing import List, Any, Optional, Type from src.common.logger import get_logger from rich.traceback import install from src.plugin_system.base.component_types import ComponentType, ToolInfo install(extra_lines=3) logger = get_logger("base_tool") class BaseTool: """所有工具的基类""" # 工具名称,子类必须重写 name = None # 工具描述,子类必须重写 description = None # 工具参数定义,子类必须重写 parameters = None # 是否可供LLM使用,默认为False available_for_llm = False @classmethod def get_tool_definition(cls) -> dict[str, Any]: """获取工具定义,用于LLM工具调用 Returns: dict: 工具定义字典 """ if not cls.name or not cls.description or not cls.parameters: raise NotImplementedError(f"工具类 {cls.__name__} 必须定义 name, description 和 parameters 属性") return { "type": "function", "function": {"name": cls.name, "description": cls.description, "parameters": cls.parameters}, } @classmethod def get_tool_info(cls) -> ToolInfo: """获取工具信息""" if not cls.name or not cls.description: raise NotImplementedError(f"工具类 {cls.__name__} 必须定义 name 和 description 属性") return ToolInfo( name=cls.name, tool_description=cls.description, available_for_llm=cls.available_for_llm, tool_parameters=cls.parameters, component_type=ComponentType.TOOL, ) # 工具参数定义,子类必须重写 async def execute(self, function_args: dict[str, Any]) -> dict[str, Any]: """执行工具函数(供llm调用) 通过该方法,maicore会通过llm的tool call来调用工具 传入的是json格式的参数,符合parameters定义的格式 Args: function_args: 工具调用参数 Returns: dict: 工具执行结果 """ raise NotImplementedError("子类必须实现execute方法") async def direct_execute(self, **function_args: dict[str, Any]) -> dict[str, Any]: """直接执行工具函数(供插件调用) 通过该方法,插件可以直接调用工具,而不需要传入字典格式的参数 插件可以直接调用此方法,用更加明了的方式传入参数 示例: result = await tool.direct_execute(arg1="参数",arg2="参数2") 工具开发者可以重写此方法以实现与llm调用差异化的执行逻辑 Args: **function_args: 工具调用参数 Returns: dict: 工具执行结果 """ if self.parameters and (missing := [p for p in self.parameters.get("required", []) if p not in function_args]): raise ValueError(f"工具类 {self.__class__.__name__} 缺少必要参数: {', '.join(missing)}") return await self.execute(function_args)