tool.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. """Tool 抽象基类 + ToolRegistry —— Agent 的工具系统
  2. 每个 Tool 是 Agent 可调用的原子能力单元。
  3. ToolRegistry 管理工具注册与发现(实例级,非全局)。
  4. """
  5. from __future__ import annotations
  6. from abc import ABC, abstractmethod
  7. from dataclasses import dataclass, field
  8. from typing import Any, Dict, List, Optional
  9. # ==================== ToolResult ====================
  10. @dataclass
  11. class ToolResult:
  12. """Tool 执行结果"""
  13. success: bool = True
  14. data: Any = None
  15. error: Optional[str] = None
  16. # 调用耗时(毫秒)
  17. duration_ms: int = 0
  18. # 额外元数据
  19. metadata: Dict[str, Any] = field(default_factory=dict)
  20. @classmethod
  21. def ok(cls, data: Any, **kwargs) -> "ToolResult":
  22. return cls(success=True, data=data, **kwargs)
  23. @classmethod
  24. def fail(cls, error: str, **kwargs) -> "ToolResult":
  25. return cls(success=False, error=error, **kwargs)
  26. # ==================== Tool 抽象基类 ====================
  27. class Tool(ABC):
  28. """Tool 抽象基类
  29. 每个 Tool 是一个原子能力单元,例如:
  30. - WebSearchTool: 网络搜索
  31. - DatabaseQueryTool: 数据库查询
  32. - HttpRequestTool: HTTP 调用
  33. - LLMCallTool: LLM 推理
  34. Tool 的 parameters_schema 描述输入参数的 JSON Schema,
  35. 供 Agent 框架生成 function calling 的 tool definition。
  36. 使用方式:
  37. class WebSearchTool(Tool):
  38. name = "web_search"
  39. description = "搜索互联网获取最新信息"
  40. @property
  41. def parameters_schema(self) -> dict:
  42. return {
  43. "type": "object",
  44. "properties": {
  45. "query": {"type": "string", "description": "搜索关键词"},
  46. "max_results": {"type": "integer", "default": 5},
  47. },
  48. "required": ["query"],
  49. }
  50. async def execute(self, **kwargs) -> ToolResult:
  51. results = await search_api(kwargs["query"])
  52. return ToolResult.ok(results)
  53. """
  54. @property
  55. @abstractmethod
  56. def name(self) -> str:
  57. """Tool 唯一标识,用于 Agent 引用"""
  58. ...
  59. @property
  60. @abstractmethod
  61. def description(self) -> str:
  62. """Tool 功能描述,写入 LLM prompt"""
  63. ...
  64. @property
  65. @abstractmethod
  66. def parameters_schema(self) -> dict:
  67. """输入参数 JSON Schema
  68. Returns:
  69. {
  70. "type": "object",
  71. "properties": {...},
  72. "required": [...],
  73. }
  74. """
  75. ...
  76. @abstractmethod
  77. async def execute(self, **kwargs) -> ToolResult:
  78. """执行 Tool,返回结果"""
  79. ...
  80. def to_openai_tool(self) -> dict:
  81. """导出为 OpenAI function calling 格式"""
  82. return {
  83. "type": "function",
  84. "function": {
  85. "name": self.name,
  86. "description": self.description,
  87. "parameters": self.parameters_schema,
  88. },
  89. }
  90. def __repr__(self) -> str:
  91. return f"<Tool name={self.name}>"
  92. # ==================== ToolRegistry ====================
  93. class ToolRegistry:
  94. """Tool 注册表 —— 实例级,可注入
  95. 替代全局 dict,支持测试隔离和多 Agent 场景。
  96. 使用方式:
  97. registry = ToolRegistry()
  98. registry.register(WebSearchTool())
  99. registry.register(FactCheckTool())
  100. tool = registry.get("web_search")
  101. result = await tool.execute(query="AI 安全")
  102. """
  103. def __init__(self):
  104. self._tools: Dict[str, Tool] = {}
  105. def register(self, tool: Tool) -> "ToolRegistry":
  106. """注册 Tool,链式调用"""
  107. if tool.name in self._tools:
  108. raise ValueError(f"Tool '{tool.name}' 已注册")
  109. self._tools[tool.name] = tool
  110. return self
  111. def get(self, name: str) -> Tool:
  112. """按名获取 Tool"""
  113. tool = self._tools.get(name)
  114. if tool is None:
  115. raise KeyError(f"Tool '{name}' 未注册,可用: {self.list_names()}")
  116. return tool
  117. def list_names(self) -> List[str]:
  118. return list(self._tools.keys())
  119. def list_all(self) -> List[Tool]:
  120. return list(self._tools.values())
  121. def to_openai_tools(self) -> List[dict]:
  122. """导出全部 Tool 为 OpenAI function calling 格式"""
  123. return [t.to_openai_tool() for t in self._tools.values()]
  124. def __len__(self) -> int:
  125. return len(self._tools)
  126. def __contains__(self, name: str) -> bool:
  127. return name in self._tools