# 工具使用完整流程指南

本文档详细说明如何使用 Tool Agent 系统中的工具，从工具注册到实际调用的完整流程。

## 目录
- [系统架构概览](#系统架构概览)
- [工具使用流程](#工具使用流程)
- [API接口说明](#api接口说明)
- [使用示例](#使用示例)
- [工具状态管理](#工具状态管理)

---

## 系统架构概览

```
┌─────────────────────────────────────────────────────────────┐
│                         用户/客户端                           │
└────────────────────┬────────────────────────────────────────┘
                     │
                     │ HTTP Request
                     ▼
┌─────────────────────────────────────────────────────────────┐
│                    Router (FastAPI)                          │
│                   端口: 8001 (默认)                          │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  API 端点:                                            │  │
│  │  • GET  /health      - 健康检查                      │  │
│  │  • GET  /tools       - 查看工具列表                  │  │
│  │  • POST /run_tool    - 调用工具                      │  │
│  │  • POST /chat        - ServiceAgent对话              │  │
│  └──────────────────────────────────────────────────────┘  │
│                                                              │
│  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │
│  │  Registry    │  │   Status     │  │   Dispatcher    │  │
│  │  工具注册表   │  │   Manager    │  │   请求分发器     │  │
│  └──────────────┘  └──────────────┘  └─────────────────┘  │
└────────────────────┬────────────────────────────────────────┘
                     │
                     │ HTTP 调用
                     ▼
┌─────────────────────────────────────────────────────────────┐
│                      工具实例层                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐     │
│  │ Local Tool   │  │ Docker Tool  │  │ Remote API   │     │
│  │ (uv环境)     │  │ (容器)       │  │ (云端服务)    │     │
│  │ 端口: 8001+  │  │ 端口: 映射   │  │ 端口: 外部   │     │
│  └──────────────┘  └──────────────┘  └──────────────┘     │
└─────────────────────────────────────────────────────────────┘
```

---

## 工具使用流程

### 流程1: 查看可用工具

```
用户
  │
  │ 1. GET /tools
  ▼
Router
  │
  │ 2. 读取 registry.json
  ▼
返回工具列表
  │
  │ 包含:
  │ • 工具ID、名称、描述
  │ • 输入/输出Schema
  │ • 运行状态 (active/stopped)
  │ • 后端类型 (local/docker/remote)
  ▼
用户选择工具
```

### 流程2: 调用工具 (完整流程)

```
┌─────────┐
│  用户    │
└────┬────┘
     │
     │ 1. 准备调用参数
     │    {
     │      "tool_id": "image_stitcher",
     │      "params": {
     │        "images": ["base64...", "base64..."],
     │        "direction": "horizontal"
     │      }
     │    }
     ▼
┌──────────────────────────────────────────┐
│  POST /run_tool                          │
│  Router 接收请求                          │
└────┬─────────────────────────────────────┘
     │
     │ 2. 查找工具元数据
     ▼
┌──────────────────────────────────────────┐
│  ToolRegistry.get(tool_id)               │
│  • 验证工具是否存在                       │
│  • 获取 input_schema                     │
│  • 获取 backend_runtime                  │
└────┬─────────────────────────────────────┘
     │
     │ 3. 检查工具状态
     ▼
┌──────────────────────────────────────────┐
│  ToolStatusManager.get_active_endpoint() │
│  • 检查工具是否运行中                     │
│  • 获取端点URL和端口                      │
│  • 获取HTTP方法 (POST/GET)               │
└────┬─────────────────────────────────────┘
     │
     │ 4. 如果工具未运行,自动启动
     ▼
┌──────────────────────────────────────────┐
│  ToolStatusManager.start_tool()          │
│  • Local: uv run main.py --port {port}  │
│  • Docker: docker start {container_id}  │
│  • Remote: 无需启动                      │
└────┬─────────────────────────────────────┘
     │
     │ 5. 分发请求到工具实例
     ▼
┌──────────────────────────────────────────┐
│  Dispatcher.dispatch()                   │
│  • 构造HTTP请求                          │
│  • 添加认证头 (如需要)                   │
│  • 发送到工具端点                        │
│    http://localhost:{port}/{endpoint}    │
└────┬─────────────────────────────────────┘
     │
     │ 6. 工具处理请求
     ▼
┌──────────────────────────────────────────┐
│  Tool Instance (FastAPI)                 │
│  • 参数验证 (Pydantic)                   │
│  • 执行业务逻辑                          │
│  • 返回结果                              │
└────┬─────────────────────────────────────┘
     │
     │ 7. 返回结果给用户
     ▼
┌──────────────────────────────────────────┐
│  Response                                │
│  {                                       │
│    "status": "success",                  │
│    "result": {                           │
│      "image": "base64...",               │
│      "width": 1024,                      │
│      "height": 512                       │
│    }                                     │
│  }                                       │
└──────────────────────────────────────────┘
```

### 流程3: 通过 ServiceAgent 对话调用工具

```
用户
  │
  │ 1. POST /chat
  │    {
  │      "message": "帮我把这两张图片横向拼接",
  │      "chat_id": "user_123"
  │    }
  ▼
Router
  │
  │ 2. 转发到 SessionManager
  ▼
ServiceAgent
  │
  │ 3. 理解用户意图
  │    • 解析需求: 图片拼接
  │    • 搜索工具: search_tools("图片拼接")
  ▼
  │ 4. 找到匹配工具
  │    • tool_id: image_stitcher
  │    • 确认参数需求
  ▼
  │ 5. 调用工具
  │    submit_task() 或 直接调用 /run_tool
  ▼
Router
  │
  │ 6. 执行工具调用 (同流程2)
  ▼
ServiceAgent
  │
  │ 7. 格式化结果返回用户
  ▼
用户收到结果
```

---

## API接口说明

### 1. GET /health

**功能**: 健康检查

**请求**: 无参数

**响应**:
```json
{
  "status": "ok"
}
```

---

### 2. GET /tools

**功能**: 获取完整工具列表

**请求**: 无参数

**响应**:
```json
{
  "backend_runtimes": [
    {
      "backend_runtime": "local",
      "name": "本地 Python 运行时",
      "description": "使用 uv 管理的本地 Python 进程"
    },
    {
      "backend_runtime": "docker",
      "name": "Docker 容器运行时",
      "description": "运行在 Docker 容器中"
    },
    {
      "backend_runtime": "remote",
      "name": "远程 API / 云端服务",
      "description": "调用外部 API"
    }
  ],
  "groups": [
    {
      "group_id": "runcomfy_lifecycle",
      "name": "ComfyUI 生命周期管理",
      "description": "启动、执行、停止 ComfyUI 环境",
      "backend_runtime": "remote",
      "tool_ids": ["launch_comfy_env", "runcomfy_workflow_executor", "runcomfy_stop_env"],
      "usage_order": ["launch_comfy_env", "runcomfy_workflow_executor", "runcomfy_stop_env"]
    }
  ],
  "tools": [
    {
      "tool_id": "image_stitcher",
      "name": "图片拼接工具",
      "description": "将多张图片拼接成一张",
      "category": "cv",
      "backend_runtime": "local",
      "group_ids": [],
      "input_schema": { ... },
      "output_schema": { ... },
      "state": "running",
      "port": 8001
    }
  ],
  "total": 5
}
```

**字段说明**:
- `backend_runtimes`: 后端运行环境分类
- `groups`: 工具组 (相关工具的集合)
- `tools`: 所有已注册工具
  - `state`: 工具状态 (`running` / `stopped`)
  - `port`: 工具运行端口 (如果正在运行)

---

### 3. POST /run_tool

**功能**: 调用指定工具

**请求**:
```json
{
  "tool_id": "image_stitcher",
  "params": {
    "images": ["base64_image_1", "base64_image_2"],
    "direction": "horizontal",
    "spacing": 10
  }
}
```

**响应 (成功)**:
```json
{
  "status": "success",
  "result": {
    "image": "base64_result_image",
    "width": 1024,
    "height": 512
  },
  "error": null
}
```

**响应 (失败)**:
```json
{
  "status": "error",
  "result": null,
  "error": "Tool 'xxx' is not running or has no active endpoint"
}
```

---

### 4. POST /chat

**功能**: 与 ServiceAgent 对话

**请求**:
```json
{
  "message": "帮我生成一张猫的图片",
  "chat_id": "user_123_session_1"
}
```

**响应**:
```json
{
  "response": "好的,我使用 LibLib ControlNet 工具为您生成图片...",
  "chat_id": "user_123_session_1"
}
```

**说明**:
- `chat_id`: 每个对话窗口的唯一标识,用于保持上下文
- ServiceAgent 会自动搜索和调用合适的工具

---

## 使用示例

### 示例1: Python 客户端调用

```python
import httpx
import base64

# 1. 查看可用工具
async with httpx.AsyncClient() as client:
    response = await client.get("http://localhost:8001/tools")
    tools = response.json()
    print(f"可用工具: {len(tools['tools'])}个")

# 2. 准备图片数据
with open("image1.png", "rb") as f:
    img1_b64 = base64.b64encode(f.read()).decode()
with open("image2.png", "rb") as f:
    img2_b64 = base64.b64encode(f.read()).decode()

# 3. 调用图片拼接工具
async with httpx.AsyncClient(timeout=60.0) as client:
    response = await client.post(
        "http://localhost:8001/run_tool",
        json={
            "tool_id": "image_stitcher",
            "params": {
                "images": [img1_b64, img2_b64],
                "direction": "horizontal",
                "spacing": 20,
                "background_color": "#FFFFFF"
            }
        }
    )
    result = response.json()

    if result["status"] == "success":
        # 保存结果
        result_img = base64.b64decode(result["result"]["image"])
        with open("result.png", "wb") as f:
            f.write(result_img)
        print(f"拼接完成! 尺寸: {result['result']['width']}x{result['result']['height']}")
    else:
        print(f"错误: {result['error']}")
```

### 示例2: curl 命令行调用

```bash
# 1. 查看工具列表
curl http://localhost:8001/tools | jq '.tools[] | {tool_id, name, state}'

# 2. 调用工具
curl -X POST http://localhost:8001/run_tool \
  -H "Content-Type: application/json" \
  -d '{
    "tool_id": "image_stitcher",
    "params": {
      "images": ["'$(base64 -w0 image1.png)'", "'$(base64 -w0 image2.png)'"],
      "direction": "horizontal"
    }
  }' | jq '.result.image' -r | base64 -d > result.png
```

### 示例3: 通过 ServiceAgent 对话

```python
import httpx

async with httpx.AsyncClient() as client:
    # 创建对话
    response = await client.post(
        "http://localhost:8001/chat",
        json={
            "message": "帮我把两张图片横向拼接,间距10像素",
            "chat_id": "my_session_001"
        }
    )
    print(response.json()["response"])

    # 继续对话 (保持上下文)
    response = await client.post(
        "http://localhost:8001/chat",
        json={
            "message": "改成垂直拼接",
            "chat_id": "my_session_001"  # 同一个 chat_id
        }
    )
    print(response.json()["response"])
```

---

## 工具状态管理

### 工具状态生命周期

```
┌──────────┐
│ INACTIVE │  工具已注册但未启动
└────┬─────┘
     │
     │ start_tool()
     ▼
┌──────────┐
│ STARTING │  正在启动中
└────┬─────┘
     │
     │ 健康检查通过
     ▼
┌──────────┐
│  ACTIVE  │  工具运行中,可接受请求
└────┬─────┘
     │
     │ stop_tool() 或 超时无请求
     ▼
┌──────────┐
│ STOPPED  │  工具已停止
└──────────┘
```

### 自动启动机制

当调用 `/run_tool` 时:
1. **检查状态**: 如果工具状态为 `stopped` 或 `inactive`
2. **自动启动**:
   - Local: 执行 `uv run main.py --port {port}` (后台进程)
   - Docker: 执行 `docker start {container_id}`
   - Remote: 无需启动
3. **健康检查**: 轮询 `GET /health` 直到返回 `{"status": "ok"}`
4. **更新状态**: 标记为 `running`
5. **执行请求**: 转发用户请求到工具端点

### 端口分配规则

- Router: `8001` (默认)
- Local工具: `8002, 8003, 8004...` (自动递增)
- Docker工具: 容器内部端口映射到宿主机
- Remote工具: 使用外部API的端口

---

## 工具调用流程总结

### 直接调用 (适合程序化调用)

```
用户 → POST /run_tool → Router → Dispatcher → Tool Instance → 返回结果
```

**优点**:
- 快速、直接
- 完全控制参数
- 适合自动化脚本

**缺点**:
- 需要了解工具的 input_schema
- 需要手动处理错误

### 通过 ServiceAgent 调用 (适合自然语言交互)

```
用户 → POST /chat → ServiceAgent → 搜索工具 → 调用工具 → 格式化结果 → 返回用户
```

**优点**:
- 自然语言交互
- 自动选择合适工具
- 智能参数推断
- 友好的错误提示

**缺点**:
- 响应稍慢 (需要LLM推理)
- 可能需要多轮对话确认参数

---

## 常见问题

### Q1: 工具调用失败,提示 "Tool is not running"?

**A**: 工具可能未启动。解决方法:
1. 检查工具状态: `GET /tools`
2. 手动启动工具 (如果自动启动失败):
   ```bash
   cd tools/local/{tool_id}
   uv run main.py --port 8002
   ```
3. 检查端口是否被占用

### Q2: 如何查看工具的输入参数要求?

**A**: 调用 `GET /tools`,查看返回的 `input_schema` 字段:
```python
tools = requests.get("http://localhost:8001/tools").json()
tool = next(t for t in tools["tools"] if t["tool_id"] == "image_stitcher")
print(tool["input_schema"])
```

### Q3: 可以同时调用多个工具吗?

**A**: 可以。每个工具运行在独立端口,支持并发调用:
```python
import asyncio

async def call_tool(tool_id, params):
    async with httpx.AsyncClient() as client:
        return await client.post(
            "http://localhost:8001/run_tool",
            json={"tool_id": tool_id, "params": params}
        )

# 并发调用
results = await asyncio.gather(
    call_tool("image_stitcher", {...}),
    call_tool("liblibai_controlnet", {...})
)
```

### Q4: 工具调用超时怎么办?

**A**:
1. 检查工具日志: `tools/local/{tool_id}/tests/last_run.log`
2. 增加超时时间 (Dispatcher 默认 600秒)
3. 优化工具性能或使用异步处理

---

## 下一步

- 查看 [任务书编写指南](../task_specs/README.md) 了解如何创建新工具
- 查看 [工具开发文档](./TOOL_DEVELOPMENT.md) 了解工具实现细节
- 查看 [API参考文档](./API_REFERENCE.md) 了解完整API规范

