# Phase 6：工具系统重构（Tool System Refactoring）

日期：2026-04-04
状态：待开始
Demo参考：claw-code-main — tools/, StreamingToolExecutor, toolOrchestration

---

## 1. 阶段目标

建立**分层工具执行架构**，从现有的扁平工具调用进化为具有注册表、编排层、Hook 拦截能力的工具系统。

**与现有系统的区别**：

| 现有 | 目标 |
|------|------|
| Sub-Commander 直接调用 Tools | ToolRegistry → ToolOrchestration → HookExecutor → ToolExecutor → Tools |
| 工具集固定 | 工具注册表支持动态注册 |
| 无流式工具执行 | StreamingToolExecutor 支持流式 |
| 无工具拦截 | PreTool/PostTool Hook 机制 |

---

## 2. 架构设计

### 2.1 新的工具执行架构

```
┌─────────────────────────────────────────────────────────────────┐
│                        Agent / Sub-Commander                     │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                      ToolOrchestration Layer                     │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────────┐ │
│  │ ToolRegistry│  │HookExecutor │  │ StreamingToolExecutor   │ │
│  │ (注册表)    │  │ (拦截层)    │  │ (流式执行器)            │ │
│  └─────────────┘  └─────────────┘  └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│                        ToolExecutor                              │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐       │
│  │ Built-in │  │ External │  │  MCP     │  │ Plugin   │       │
│  │  Tools   │  │  Tools   │  │  Tools   │  │  Tools   │       │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘       │
└─────────────────────────────────────────────────────────────────┘
```

### 2.2 核心组件

#### ToolRegistry（工具注册表）

```python
# backend/app/agents/tools/registry.py

@dataclass
class ToolManifest:
    """工具元数据"""
    name: str
    description: str
    category: ToolCategory
    parameters: dict  # JSON Schema
    return_schema: dict
    permission_class: PermissionClass  # READ/WRITE/EXTERNAL
    side_effect_scope: SideEffectScope  # NONE/LOCAL_STATE/DB_WRITE/NETWORK
    requires_confirmation: bool
    is_streaming: bool = False
    tags: list[str] = field(default_factory=list)

class ToolRegistry:
    """工具注册表"""
    
    def __init__(self):
        self._tools: dict[str, ToolManifest] = {}
        self._executors: dict[str, Callable] = {}
        self._hooks: dict[str, list[HookConfig]] = defaultdict(list)
    
    def register(
        self,
        manifest: ToolManifest,
        executor: Callable,
        hooks: list[HookConfig] | None = None
    ):
        """注册工具"""
        self._tools[manifest.name] = manifest
        self._executors[manifest.name] = executor
        if hooks:
            for hook in hooks:
                self._hooks[manifest.name].append(hook)
    
    def get(self, name: str) -> ToolManifest | None:
        """获取工具元数据"""
        return self._tools.get(name)
    
    def get_executor(self, name: str) -> Callable | None:
        """获取工具执行器"""
        return self._executors.get(name)
    
    def list_by_category(self, category: ToolCategory) -> list[ToolManifest]:
        """按类别列出工具"""
        return [t for t in self._tools.values() if t.category == category]
    
    def list_all(self) -> list[ToolManifest]:
        """列出所有工具"""
        return list(self._tools.values())
```

#### HookExecutor（拦截执行器）

```python
# backend/app/agents/tools/hook_executor.py

@dataclass
class HookConfig:
    """Hook 配置"""
    name: str
    hook_type: HookType  # PRE_TOOL_USE / POST_TOOL_USE / TOOL_ERROR / TOOL_SKIP
    handler: Callable
    filter_names: list[str] | None = None  # 只对特定工具生效，None 表示全部

class HookExecutor:
    """工具 Hook 执行器"""
    
    async def pre_execute(self, tool_name: str, arguments: dict) -> HookResult:
        """PreToolUse - 工具执行前拦截"""
        hooks = self._get_hooks(tool_name, HookType.PRE_TOOL_USE)
        
        ctx = HookContext(
            tool_name=tool_name,
            arguments=arguments,
            phase=HookPhase.PRE
        )
        
        for hook in hooks:
            result = await hook.handler(ctx)
            if result.action == HookAction.DENY:
                return HookResult(
                    action=HookAction.DENY,
                    message=result.message,
                    modified_args=None
                )
            elif result.action == HookAction.MODIFY:
                ctx.arguments = result.modified_args
        
        return HookResult(action=HookAction.ALLOW, modified_args=ctx.arguments)
    
    async def post_execute(
        self,
        tool_name: str,
        arguments: dict,
        result: Any
    ) -> HookResult:
        """PostToolUse - 工具执行后拦截"""
        hooks = self._get_hooks(tool_name, HookType.POST_TOOL_USE)
        
        ctx = HookContext(
            tool_name=tool_name,
            arguments=arguments,
            result=result,
            phase=HookPhase.POST
        )
        
        for hook in hooks:
            result = await hook.handler(ctx)
            if result.action == HookAction.MODIFY_RESULT:
                ctx.result = result.modified_result
        
        return HookResult(action=HookAction.CONTINUE, modified_result=ctx.result)
```

#### StreamingToolExecutor（流式工具执行器）

```python
# backend/app/agents/tools/streaming_executor.py

class StreamingToolExecutor:
    """流式工具执行器"""
    
    async def execute_streaming(
        self,
        tool_name: str,
        arguments: dict,
        callback: Callable[[dict], Awaitable[None]]
    ) -> Any:
        """执行流式工具"""
        manifest = self.registry.get(tool_name)
        if not manifest.is_streaming:
            raise ValueError(f"Tool {tool_name} does not support streaming")
        
        executor = self.registry.get_executor(tool_name)
        
        # 使用 aiterator 进行流式执行
        async for chunk in executor(**arguments):
            event = {"type": "chunk", "data": chunk}
            await callback(event)
            yield chunk
        
        final_event = {"type": "done", "tool": tool_name}
        await callback(final_event)
```

---

## 3. 新增工具集

### 3.1 文件操作工具

| 工具 | 描述 | 权限级别 |
|------|------|---------|
| GlobTool | 按模式匹配文件 | READ |
| GrepTool | 在文件中搜索内容 | READ |
| LintTool | 代码检查 | READ |
| EditTool | 编辑文件 | WRITE |

### 3.2 开发工具

| 工具 | 描述 | 权限级别 |
|------|------|---------|
| LSPTool | Language Server Protocol | READ |
| BashTool | 执行 Bash 命令 | EXTERNAL |
| PowerShellTool | 执行 PowerShell | EXTERNAL |

### 3.3 系统工具

| 工具 | 描述 | 权限级别 |
|------|------|---------|
| ScheduleCronTool | Cron 定时任务 | WRITE |
| ProcessTool | 进程管理 | EXTERNAL |

### 3.4 协作工具

| 工具 | 描述 | 权限级别 |
|------|------|---------|
| TeamAgentTool | 团队 Agent 协作 | EXTERNAL |
| TaskBroadcastTool | 任务广播 | WRITE |

### 3.5 高级工具

| 工具 | 描述 | 权限级别 |
|------|------|---------|
| RemoteTriggerTool | 远程触发 | EXTERNAL |
| MCPClientTool | MCP 客户端 | EXTERNAL |
| AskUserQuestionTool | 向用户提问 | READ |

---

## 4. 文件结构变化

```
backend/app/agents/tools/
├── __init__.py           # 现有 - 需重构
├── registry.py           # 新增 - 工具注册表
├── manifest.py           # 新增 - 工具元数据
├── hook_executor.py      # 新增 - Hook 执行器
├── hook_config.py        # 新增 - Hook 配置
├── streaming_executor.py  # 新增 - 流式执行器
├── base.py               # 新增 - 基础工具类
│
├── builtins/             # 新增 - 内置工具
│   ├── __init__.py
│   ├── file_tools.py     # Glob, Grep, Edit, Lint
│   ├── system_tools.py   # Bash, PowerShell, Cron
│   ├── dev_tools.py      # LSP, Git
│   └── collaboration_tools.py  # Team, Broadcast
│
├── task.py               # 现有 - 保留
├── schedule.py           # 现有 - 保留
├── search.py             # 现有 - 保留
├── forum.py              # 现有 - 保留
└── time_reasoning.py     # 现有 - 保留
```

---

## 5. 迁移计划

### 阶段 6.1：基础设施（1-2周）
- 创建 ToolRegistry 类
- 创建 ToolManifest 数据类
- 迁移现有工具到注册表

### 阶段 6.2：Hook 系统（2-3周）
- 创建 HookExecutor
- 实现 PreTool/PostTool 机制
- 添加内置 Hook（确认危险操作、日志记录）

### 阶段 6.3：流式执行（1-2周）
- 实现 StreamingToolExecutor
- 为支持流式的工具添加 streaming 标志
- 更新 AgentService 集成

### 阶段 6.4：新工具集（2-3周）
- 实现新增的工具类
- 添加完整的工具文档
- 单元测试覆盖

---

## 6. 验收标准

| 检查点 | 标准 |
|--------|------|
| 工具注册 | 所有现有工具已在 Registry 中注册 |
| Hook 执行 | PreTool/PostTool Hook 能正确拦截 |
| 流式执行 | 标记为 streaming 的工具可流式返回 |
| 新工具 | 新增工具集中每个工具可正常执行 |
| 向后兼容 | 现有 Sub-Commander 工具调用不受影响 |

---

## 7. Demo 借鉴

| claw-code 实现 | Jarvis 对应 |
|----------------|------------|
| `src/tools/` | `backend/app/agents/tools/` |
| `StreamingToolExecutor.ts` | `streaming_executor.py` |
| `toolExecution.ts` | `hook_executor.py` |
| `toolHooks.ts` | `hook_config.py` |
| `ToolManifest` | `manifest.py` |
