3.1 工具系统的架构设计 读者读完这节,能掌握工具系统的核心架构原理,学会设计可扩展的工具系统 学习目标 理解工具系统在Agent智能体中的核心作用 掌握工具系统的分层架构设计方法 学会工具注册、发现和调用的机制 了解工具系统的扩展性和可维护性设计 能够根据具体需求设计合适的工具系统 核心概念 工具系统是Agent智能体的核心组件,负责管理与外部世界的交互: 工具抽象:统一的工具接口和规范 动态注册:运行时的工具注册和发现机制 策略调度:智能的工具选择和调用策略 结果处理:工具执行结果的解析和处理 错误恢复:优雅的工具调用失败处理 工具系统架构图:从注册到调用的完整流程 环境准备 / 前置知识 必需依赖 推荐开发环境 开发工具:VS Code, PyCharm 设计工具:Draw.
读者读完这节,能掌握工具系统的核心架构原理,学会设计可扩展的工具系统
工具系统是Agent智能体的核心组件,负责管理与外部世界的交互:
# 工具系统相关依赖 typing-extensions>=4.5.0 dataclasses>=0.8 pydantic>=1.10.0 asyncio>=3.4.0
from abc import ABC, abstractmethod from typing import Dict, List, Any, Optional from dataclasses import dataclass, field from enum import Enum import time import uuid from datetime import datetime import json class ToolType(Enum): """工具类型枚举""" SEARCH = "search" CALCULATION = "calculation" DATA_PROCESSING = "data_processing" COMMUNICATION = "communication" SYSTEM = "system" CUSTOM = "custom" class ToolStatus(Enum): """工具状态枚举""" ACTIVE = "active" INACTIVE = "inactive" ERROR = "error" DEPRECATED = "deprecated" @dataclass class ToolMetadata: """工具元数据""" name: str description: str version: str = "1.0.0" author: str = "Unknown" tags: List[str] = field(default_factory=list) category: ToolType = ToolType.CUSTOM dependencies: List[str] = field(default_factory=list) parameters_schema: Dict[str, Any] = field(default_factory=dict) return_schema: Dict[str, Any] = field(default_factory=dict) max_retries: int = 3 timeout: int = 30 memory_limit: int = 1024 # MB def to_dict(self) -> Dict[str, Any]: """转换为字典""" return { 'name': self.name, 'description': self.description, 'version': self.version, 'author': self.author, 'tags': self.tags, 'category': self.category.value, 'dependencies': self.dependencies, 'parameters_schema': self.parameters_schema, 'return_schema': self.return_schema, 'max_retries': self.max_retries, 'timeout': self.timeout, 'memory_limit': self.memory_limit } @dataclass class ToolResult: """工具执行结果""" tool_id: str tool_name: str success: bool data: Any = None error: Optional[str] = None execution_time: float = 0.0 timestamp: datetime = field(default_factory=datetime.now) metadata: Dict[str, Any] = field(default_factory=dict) def to_dict(self) -> Dict[str, Any]: """转换为字典""" return { 'tool_id': self.tool_id, 'tool_name': self.tool_name, 'success': self.success, 'data': self.data, 'error': self.error, 'execution_time': self.execution_time, 'timestamp': self.timestamp.isoformat(), 'metadata': self.metadata } class ToolInterface(ABC): """工具抽象接口""" @property @abstractmethod def metadata(self) -> ToolMetadata: """获取工具元数据""" pass @abstractmethod def execute(self, **kwargs) -> ToolResult: """执行工具""" pass @abstractmethod def validate_parameters(self, **kwargs) -> bool: """验证参数""" pass def get_description(self) -> str: """获取工具描述""" return self.metadata.description def get_parameters_info(self) -> Dict[str, Any]: """获取参数信息""" return self.metadata.parameters_schema def can_handle(self, task: str) -> bool: """判断是否可以处理任务""" task_lower = task.lower() desc_lower = self.metadata.description.lower() return any(keyword in desc_lower for keyword in task_lower.split()) class BaseTool(ToolInterface): """基础工具实现""" def __init__(self, metadata: ToolMetadata): self._metadata = metadata self._status = ToolStatus.ACTIVE self._execution_history: List[ToolResult] = [] self._last_execution_time: Optional[datetime] = None @property def metadata(self) -> ToolMetadata: return self._metadata @property def status(self) -> ToolStatus: return self._status @status.setter def status(self, value: ToolStatus): self._status = value @property def execution_history(self) -> List[ToolResult]: return self._execution_history.copy() def execute(self, **kwargs) -> ToolResult: """执行工具(基础实现)""" start_time = time.time() tool_id = str(uuid.uuid4()) try: # 验证参数 if not self.validate_parameters(**kwargs): return ToolResult( tool_id=tool_id, tool_name=self._metadata.name, success=False, error="参数验证失败", execution_time=time.time() - start_time ) # 执行工具 result_data = self._execute_impl(**kwargs) # 记录执行历史 result = ToolResult( tool_id=tool_id, tool_name=self._metadata.name, success=True, data=result_data, execution_time=time.time() - start_time ) self._execution_history.append(result) self._last_execution_time = datetime.now() return result except Exception as e: # 记录失败历史 result = ToolResult( tool_id=tool_id, tool_name=self._metadata.name, success=False, error=str(e), execution_time=time.time() - start_time ) self._execution_history.append(result) self._last_execution_time = datetime.now() return result @abstractmethod def _execute_impl(self, **kwargs) -> Any: """具体实现工具逻辑""" pass def validate_parameters(self, **kwargs) -> bool: """验证参数(基础实现)""" # 检查必需参数 required_params = self._metadata.parameters_schema.get('required', []) for param in required_params: if param not in kwargs: return False # 检查参数类型 properties = self._metadata.parameters_schema.get('properties', {}) for param_name, param_value in kwargs.items(): if param_name in properties: expected_type = properties[param_name].get('type') if expected_type and not self._check_type(param_value, expected_type): return False return True def _check_type(self, value: Any, expected_type: str) -> bool: """检查参数类型""" type_mapping = { 'string': str, 'number': (int, float), 'integer': int, 'boolean': bool, 'array': list, 'object': dict } expected_python_type = type_mapping.get(expected_type) return expected_python_type is None or isinstance(value, expected_python_type) def get_statistics(self) -> Dict[str, Any]: """获取执行统计""" if not self._execution_history: return {'total_executions': 0} total_executions = len(self._execution_history) successful_executions = sum(1 for r in self._execution_history if r.success) failed_executions = total_executions - successful_executions success_rate = successful_executions / total_executions if total_executions > 0 else 0 avg_execution_time = sum(r.execution_time for r in self._execution_history) / total_executions max_execution_time = max(r.execution_time for r in self._execution_history) min_execution_time = min(r.execution_time for r in self._execution_history) return { 'total_executions': total_executions, 'successful_executions': successful_executions, 'failed_executions': failed_executions, 'success_rate': success_rate, 'avg_execution_time': avg_execution_time, 'max_execution_time': max_execution_time, 'min_execution_time': min_execution_time, 'last_execution_time': self._last_execution_time.isoformat() if self._last_execution_time else None } # 具体工具示例 class SearchTool(BaseTool): """搜索工具示例""" def __init__(self): metadata = ToolMetadata( name="web_search", description="网络搜索工具,用于搜索网页信息", version="1.0.0", author="Agent Team", tags=["search", "web", "information"], category=ToolType.SEARCH, dependencies=["requests"], parameters_schema={ "type": "object", "properties": { "query": { "type": "string", "description": "搜索查询字符串" }, "max_results": { "type": "integer", "default": 10, "description": "最大搜索结果数" } }, "required": ["query"] } ) super().__init__(metadata) def _execute_impl(self, **kwargs) -> Any: """实现搜索逻辑""" query = kwargs['query'] max_results = kwargs.get('max_results', 10) # 模拟搜索结果 results = [] for i in range(min(max_results, 5)): results.append({ 'title': f"搜索结果 {i+1}: {query}", 'url': f"https://example.com/result{i+1}", 'snippet': f"这是关于'{query}'的搜索结果片段...", 'relevance': 0.9 - i * 0.1 }) return { 'query': query, 'results': results, 'total_results': len(results), 'search_time': time.time() } class CalculatorTool(BaseTool): """计算器工具示例""" def __init__(self): metadata = ToolMetadata( name="calculator", description="数学计算工具,执行各种数学运算", version="1.0.0", author="Agent Team", tags=["math", "calculation", "computation"], category=ToolType.CALCULATION, parameters_schema={ "type": "object", "properties": { "expression": { "type": "string", "description": "数学表达式" }, "precision": { "type": "integer", "default": 2, "description": "计算精度(小数位数)" } }, "required": ["expression"] } ) super().__init__(metadata) def _execute_impl(self, **kwargs) -> Any: """实现计算逻辑""" expression = kwargs['expression'] precision = kwargs.get('precision', 2) try: # 安全计算(仅允许数学运算) allowed_chars = set('0123456789+-*/.() ') if not all(c in allowed_chars for c in expression): raise ValueError("表达式包含非法字符") # 计算结果 result = eval(expression) # 应用精度 if precision > 0: result = round(result, precision) return { 'expression': expression, 'result': result, 'precision': precision, 'calculation_time': time.time() } except Exception as e: raise ValueError(f"计算错误: {str(e)}")
from typing import Dict, List, Optional import threading import logging class ToolRegistry: """工具注册表""" def __init__(self): self._tools: Dict[str, ToolInterface] = {} self._tool_types: Dict[ToolType, List[str]] = {} self._categories: Dict[str, List[str]] = {} self._lock = threading.RLock() self._logger = logging.getLogger(__name__) self._aliases: Dict[str, str] = {} # 初始化分类 for tool_type in ToolType: self._tool_types[tool_type] = [] def register_tool(self, tool: ToolInterface, alias: Optional[str] = None) -> bool: """注册工具""" with self._lock: tool_name = tool.metadata.name # 检查是否已存在 if tool_name in self._tools: self._logger.warning(f"工具 '{tool_name}' 已存在,将被替换") # 注册工具 self._tools[tool_name] = tool # 更新类型分类 tool_type = tool.metadata.category if tool_name not in self._tool_types[tool_type]: self._tool_types[tool_type].append(tool_name) # 更新标签分类 for tag in tool.metadata.tags: if tag not in self._categories: self._categories[tag] = [] if tool_name not in self._categories[tag]: self._categories[tag].append(tool_name) # 注册别名 if alias: self._aliases[alias] = tool_name self._logger.info(f"工具 '{tool_name}' 注册成功") return True def get_tool(self, tool_name: str) -> Optional[ToolInterface]: """获取工具""" with self._lock: # 支持别名 actual_name = self._aliases.get(tool_name, tool_name) return self._tools.get(actual_name) def list_tools(self, tool_type: Optional[ToolType] = None, tag: Optional[str] = None) -> List[str]: """列出工具""" with self._lock: if tool_type: return self._tool_types.get(tool_type, []).copy() elif tag: return self._categories.get(tag, []).copy() else: return list(self._tools.keys()) def search_tools(self, keyword: str, tool_type: Optional[ToolType] = None) -> List[str]: """搜索工具""" with self._lock: keyword = keyword.lower() matching_tools = [] for tool_name, tool in self._tools.items(): # 检查类型过滤 if tool_type and tool.metadata.category != tool_type: continue # 检查关键词匹配 if (keyword in tool_name.lower() or keyword in tool.metadata.description.lower() or any(keyword in tag.lower() for tag in tool.metadata.tags)): matching_tools.append(tool_name) return matching_tools def get_statistics(self) -> Dict[str, Any]: """获取注册表统计""" with self._lock: total_tools = len(self._tools) active_tools = len([t for t in self._tools.values() if t.status.value == "active"]) # 按类型统计 type_stats = {tool_type.value: len(tools) for tool_type, tools in self._tool_types.items()} return { 'total_tools': total_tools, 'active_tools': active_tools, 'type_distribution': type_stats, 'total_aliases': len(self._aliases) } class ToolDiscovery: """工具发现机制""" def __init__(self, registry: ToolRegistry): self.registry = registry self.discovery_strategies = [ self._discover_by_type, self._discover_by_tag, self._discover_by_keyword ] def discover_tools(self, context: Dict[str, Any]) -> List[ToolInterface]: """根据上下文发现工具""" discovered_tools = [] for strategy in self.discovery_strategies: try: tools = strategy(context) discovered_tools.extend(tools) except Exception as e: logging.error(f"发现策略执行失败: {e}") # 去重 unique_tools = {} for tool in discovered_tools: unique_tools[tool.metadata.name] = tool return list(unique_tools.values()) def _discover_by_type(self, context: Dict[str, Any]) -> List[ToolInterface]: """根据类型发现工具""" preferred_type = context.get('preferred_type') if preferred_type: try: tool_type = ToolType(preferred_type) tool_names = self.registry.list_tools(tool_type=tool_type) return [self.registry.get_tool(name) for name in tool_names if self.registry.get_tool(name)] except ValueError: pass return [] def _discover_by_tag(self, context: Dict[str, Any]) -> List[ToolInterface]: """根据标签发现工具""" required_tags = context.get('required_tags', []) if not required_tags: return [] matching_tools = [] for tag in required_tags: tool_names = self.registry.list_tools(tag=tag) for name in tool_names: tool = self.registry.get_tool(name) if tool and all(tag in tool.metadata.tags for tag in required_tags): matching_tools.append(tool) return matching_tools def _discover_by_keyword(self, context: Dict[str, Any]) -> List[ToolInterface]: """根据关键词发现工具""" keywords = context.get('keywords', []) if not keywords: return [] matching_tools = [] for keyword in keywords: tool_names = self.registry.search_tools(keyword) for name in tool_names: tool = self.registry.get_tool(name) if tool: matching_tools.append(tool) return matching_tools
A:设计可扩展的工具系统需要注意:
A:处理并发执行的方法包括:
A:保证系统稳定性的方法:
本节详细介绍了工具系统的架构设计,包括:
通过掌握这些架构设计原则,你可以构建一个可扩展、可维护、高性能的工具系统,为Agent智能体提供强大的外部交互能力。
关键词:Agent智能体开发实战,工具系统架构,工具注册,工具发现,设计模式
难度:进阶
预计阅读:35分钟