本文档详细说明如何使用 Tool Agent 系统中的工具,从工具注册到实际调用的完整流程。
┌─────────────────────────────────────────────────────────────┐
│ 用户/客户端 │
└────────────────────┬────────────────────────────────────────┘
│
│ 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. GET /tools
▼
Router
│
│ 2. 读取 registry.json
▼
返回工具列表
│
│ 包含:
│ • 工具ID、名称、描述
│ • 输入/输出Schema
│ • 运行状态 (active/stopped)
│ • 后端类型 (local/docker/remote)
▼
用户选择工具
┌─────────┐
│ 用户 │
└────┬────┘
│
│ 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 │
│ } │
│ } │
└──────────────────────────────────────────┘
用户
│
│ 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. 格式化结果返回用户
▼
用户收到结果
功能: 健康检查
请求: 无参数
响应:
{
"status": "ok"
}
功能: 获取完整工具列表
请求: 无参数
响应:
{
"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: 工具运行端口 (如果正在运行)功能: 调用指定工具
请求:
{
"tool_id": "image_stitcher",
"params": {
"images": ["base64_image_1", "base64_image_2"],
"direction": "horizontal",
"spacing": 10
}
}
响应 (成功):
{
"status": "success",
"result": {
"image": "base64_result_image",
"width": 1024,
"height": 512
},
"error": null
}
响应 (失败):
{
"status": "error",
"result": null,
"error": "Tool 'xxx' is not running or has no active endpoint"
}
功能: 与 ServiceAgent 对话
请求:
{
"message": "帮我生成一张猫的图片",
"chat_id": "user_123_session_1"
}
响应:
{
"response": "好的,我使用 LibLib ControlNet 工具为您生成图片...",
"chat_id": "user_123_session_1"
}
说明:
chat_id: 每个对话窗口的唯一标识,用于保持上下文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']}")
# 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
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 时:
stopped 或 inactiveuv run main.py --port {port} (后台进程)docker start {container_id}GET /health 直到返回 {"status": "ok"}running8001 (默认)8002, 8003, 8004... (自动递增)用户 → POST /run_tool → Router → Dispatcher → Tool Instance → 返回结果
优点:
缺点:
用户 → POST /chat → ServiceAgent → 搜索工具 → 调用工具 → 格式化结果 → 返回用户
优点:
缺点:
A: 工具可能未启动。解决方法:
GET /tools手动启动工具 (如果自动启动失败):
cd tools/local/{tool_id}
uv run main.py --port 8002
A: 调用 GET /tools,查看返回的 input_schema 字段:
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"])
A: 可以。每个工具运行在独立端口,支持并发调用:
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", {...})
)
A:
tools/local/{tool_id}/tests/last_run.log