feat(agents): Phase 6 tool system refactoring

Phase 6.1: ToolRegistry infrastructure
- Add ToolManifest with ToolCategory, PermissionClass, SideEffectScope
- Add ToolRegistry singleton with register/get/unregister/list/search
- Add BaseTool abstract class with ReadTool/WriteTool/DBWriteTool/ExternalTool/NetworkTool subclasses
- Add migration layer for backward compatibility

Phase 6.2: Hook interception system
- Add HookType (PRE_TOOL_USE, POST_TOOL_USE, TOOL_ERROR, TOOL_SKIP)
- Add HookManager with singleton for hook registration
- Add HookExecutor for pre/post/error hook execution

Phase 6.3: Streaming execution
- Add StreamingToolExecutor with batch execution support

Phase 6.4: New builtin tools
- Add file_tools: GlobTool, GrepTool, ReadFileTool, WriteFileTool
- Add system_tools: BashTool, PowerShellTool
- Add dev_tools: LSPTools, GitTool
- Add collaboration_tools: TeamAgentTool, TaskBroadcastTool

Tests: 29 passed
This commit is contained in:
2026-04-04 22:47:48 +08:00
parent a7b6b5eb90
commit e5bd492d74
16 changed files with 2541 additions and 2 deletions

View File

@@ -0,0 +1,161 @@
"""工具基类 - 工具系统重构 Phase 6.1"""
from abc import ABC, abstractmethod
from typing import Any, Generic, TypeVar
from app.agents.tools.manifest import (
PermissionClass,
SideEffectScope,
ToolCategory,
ToolManifest,
)
T = TypeVar("T")
class BaseTool(ABC, Generic[T]):
"""工具基类
提供工具的标准接口和默认实现。
所有自定义工具应继承此类。
"""
def __init__(
self,
name: str,
description: str,
category: ToolCategory,
permission_class: PermissionClass,
side_effect_scope: SideEffectScope = SideEffectScope.NONE,
requires_confirmation: bool = False,
is_streaming: bool = False,
tags: list[str] | None = None,
):
self.name = name
self.description = description
self.category = category
self.permission_class = permission_class
self.side_effect_scope = side_effect_scope
self.requires_confirmation = requires_confirmation
self.is_streaming = is_streaming
self.tags = tags or []
def get_manifest(self) -> ToolManifest:
"""获取工具元数据
Returns:
工具元数据
"""
return ToolManifest(
name=self.name,
description=self.description,
category=self.category,
parameters=self.get_parameters(),
return_schema=self.get_return_schema(),
permission_class=self.permission_class,
side_effect_scope=self.side_effect_scope,
requires_confirmation=self.requires_confirmation,
is_streaming=self.is_streaming,
tags=self.tags,
)
@abstractmethod
def get_parameters(self) -> dict[str, Any]:
"""获取参数 SchemaJSON Schema 格式)
Returns:
参数 schema
"""
pass
@abstractmethod
def get_return_schema(self) -> dict[str, Any]:
"""获取返回值 Schema
Returns:
返回值 schema
"""
pass
@abstractmethod
async def execute(self, **kwargs) -> T:
"""执行工具
Args:
**kwargs: 工具参数
Returns:
执行结果
"""
pass
async def execute_safe(self, **kwargs) -> dict[str, Any]:
"""安全执行工具,捕获异常
Args:
**kwargs: 工具参数
Returns:
包含 success 和 result/error 的字典
"""
try:
result = await self.execute(**kwargs)
return {"success": True, "result": result}
except Exception as e:
return {"success": False, "error": str(e)}
def __repr__(self) -> str:
return f"<{self.__class__.__name__}(name={self.name!r})>"
class ReadTool(BaseTool):
"""只读工具基类"""
def __init__(self, **kwargs):
kwargs.setdefault("category", ToolCategory.READ)
kwargs.setdefault("permission_class", PermissionClass.READ)
kwargs.setdefault("side_effect_scope", SideEffectScope.NONE)
super().__init__(**kwargs)
class WriteTool(BaseTool):
"""写入工具基类"""
def __init__(self, **kwargs):
kwargs.setdefault("category", ToolCategory.WRITE)
kwargs.setdefault("permission_class", PermissionClass.WRITE)
kwargs.setdefault("side_effect_scope", SideEffectScope.LOCAL_STATE)
super().__init__(**kwargs)
class DBWriteTool(BaseTool):
"""数据库写入工具基类"""
def __init__(self, **kwargs):
kwargs.setdefault("category", ToolCategory.DB_WRITE)
kwargs.setdefault("permission_class", PermissionClass.WRITE)
kwargs.setdefault("side_effect_scope", SideEffectScope.DB_WRITE)
kwargs.setdefault("requires_confirmation", True)
super().__init__(**kwargs)
class ExternalTool(BaseTool):
"""外部工具基类(执行外部命令等)"""
def __init__(self, **kwargs):
kwargs.setdefault("category", ToolCategory.EXTERNAL)
kwargs.setdefault("permission_class", PermissionClass.EXTERNAL)
kwargs.setdefault("side_effect_scope", SideEffectScope.NETWORK)
kwargs.setdefault("requires_confirmation", True)
super().__init__(**kwargs)
class NetworkTool(BaseTool):
"""网络工具基类"""
def __init__(self, **kwargs):
kwargs.setdefault("category", ToolCategory.NETWORK)
kwargs.setdefault("permission_class", PermissionClass.EXTERNAL)
kwargs.setdefault("side_effect_scope", SideEffectScope.NETWORK)
super().__init__(**kwargs)