# 工具使用完整流程指南 本文档详细说明如何使用 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规范