tool_agent 是一个本地常驻的智能工具管理系统,内部由两个 Agent 协作驱动:
┌─────────────────────────────────────────────┐
│ Router Agent │
│ 统领全局,维护路由层 │
│ - 对外接口管理(FastAPI/MCP/WS) │
│ - 请求分发与调度 │
│ - 冷热调度、健康监控 │
│ - 中间件管理(鉴权/缓存/计量) │
├─────────────────────────────────────────────┤
│ Code Agent │
│ 维护工具库 │
│ - 编写/获取/部署新工具 │
│ - staging 验证与 promote │
│ - 逆向 API 自修复 │
│ - 财务管理(账号注册/API 充值) │
└─────────────────────────────────────────────┘
| 角色 | 职责 | 关注点 |
|---|---|---|
| Router Agent | 维护路由层,管理对外接口和内部分发 | "怎么调" — 调度、监控、流量 |
| Code Agent | 维护工具库,获取和生产工具 | "有什么可调" — 工具供给、质量、演进 |
两者通过内部消息通信:
外部 Agent 请求获取当前可用工具列表,按类别展示,供其挑选。
外部 Agent 选定工具后发起调用请求,tool_agent 启动对应工具服务,执行并返回结果。
外部 Agent 提出工具库中不存在的工具需求时,tool_agent 自主决策获取方式:
| 策略 | 说明 |
|---|---|
| A. 注册购买 API | 直接对接第三方付费/免费 API |
| B. 本地部署 | 拉取开源项目,本地或 Docker 部署 |
| C. 逆向 API | 注册账号后逆向分析目标服务接口 |
| D. Browser-Use | 通过浏览器自动化操作网页工具 |
┌─────────────────────────────────────────────┐
│ 外部 Agent 请求 │
└──────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 注册层 (Registry) │
│ - 工具分类注册 │
│ - 统一接口封装(输入/输出规范) │
│ - 工具元信息描述(用法、参数、返回值) │
└──────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 路由层 (Router) — 兼任对外网关 │
│ - 对外:FastAPI (HTTP+SSE) / MCP 接口 │
│ - 请求解析与工具匹配 │
│ - 冷热调度(按需唤醒/空闲休眠) │
│ - 环境感知(工具在哪、怎么启动、怎么对接) │
│ - 参数转换与传递 │
│ - 中间件:鉴权 / 结果缓存 / 调用计量 │
└──────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 环境层 (Runtime) │
│ - 工具物理存储与管理 │
│ - 环境隔离(本地进程 / Docker 容器) │
│ - 生命周期管理(启动/停止/健康检查) │
│ - 资源配额(CPU/内存/显存限制) │
└─────────────────────────────────────────────┘
路由层同时承担网关职责,对外仅暴露两个固定端口:
| 协议 | 端口 | 用途 |
|---|---|---|
| FastAPI (HTTP) | 8001 | RESTful 接口(含 SSE 流式推送) |
| MCP (Model Context Protocol) | 8001 | 标准 MCP Server,供支持 MCP 的 Agent 直接对接 |
不单独开 WebSocket 端口。流式返回用 SSE,异步任务用回调/轮询,理由:
GET /tools # 获取工具目录(按类别)
GET /tools/{tool_id}/schema # 获取单个工具的输入输出规范
POST /tools/{tool_id}/invoke # 调用工具(Accept: text/event-stream 时返回 SSE)
POST /tools/request # 提交新工具需求,返回 task_id
GET /tasks/{task_id}/status # 异步任务状态轮询
GET /health # 服务健康检查
GET /openapi.json # OpenAPI 定义自动导出
// POST /tools/request
{
"description": "需要一个图片压缩工具",
"callback_url": "http://caller-agent/callback" // 可选
}
// 响应
{
"task_id": "uuid",
"status": "pending"
}
// 任务完成后:
// 方式 A:tool_agent POST callback_url 推送结果
// 方式 B:外部 Agent 轮询 GET /tasks/{task_id}/status
以标准 MCP Tool 协议暴露,每个注册工具自动映射为一个 MCP Tool,外部 Agent 可通过 tools/list 和 tools/call 直接使用。
uv 本地工具各自独立 venv,通过子进程 + stdio JSON 通信(零网络开销,保持环境隔离);Docker 工具通过端口映射 HTTP 通信。
外部 Agent
│
┌─────────┴─────────┐
▼ ▼
:8001 FastAPI :8001 MCP
(HTTP + SSE)
└─────────┬─────────┘
│ 路由层按 tool_id 分发
┌─────────┴─────────┐
▼ ▼
┌── uv 本地工具 ──┐ ┌── Docker 工具 ──┐
│ 子进程 + stdio │ │ HTTP 端口映射 │
│ 各自独立 venv │ │ :9001 :9002 │
│ tool_a tool_c │ │ tool_b tool_d │
└─────────────────┘ └─────────────────┘
每个 uv 工具必须实现统一的 Python 入口,通过 stdin 接收请求、stdout 输出结果:
# tools/local/tool_a/main.py
import sys, json
class Tool:
name: str = "tool_a"
description: str = "工具描述"
def schema(self) -> dict:
return {"input": {...}, "output": {...}}
def run(self, params: dict, stream: bool = False) -> dict:
return {"result": ...}
def health(self) -> bool:
return True
if __name__ == "__main__":
tool = Tool()
request = json.loads(sys.stdin.read())
action = request.get("action", "run")
if action == "schema":
output = tool.schema()
elif action == "health":
output = {"healthy": tool.health()}
else:
output = tool.run(request.get("params", {}), request.get("stream", False))
json.dump(output, sys.stdout)
路由层调用方式:
import subprocess, json
def call_local_tool(tool_id: str, request: dict) -> dict:
result = subprocess.run(
["uv", "run", "--directory", f"tools/local/{tool_id}", "python", "main.py"],
input=json.dumps(request),
capture_output=True, text=True
)
return json.loads(result.stdout)
Docker 工具因环境隔离,必须在容器内启动 HTTP 服务:
POST /run # 执行工具主逻辑(支持流式 SSE)
GET /health # 健康检查
GET /schema # 返回自身输入输出 schema
请求/响应格式:
// POST /run 请求
{
"request_id": "uuid",
"params": { },
"stream": false
}
// POST /run 响应(非流式)
{
"request_id": "uuid",
"status": "success | error",
"result": { },
"error": "错误信息(可选)"
}
// POST /run 响应(流式,Content-Type: text/event-stream)
data: {"request_id": "uuid", "chunk": "部分结果...", "done": false}
data: {"request_id": "uuid", "chunk": "最终结果", "done": true}
| 中间件 | 作用 |
|---|---|
| 结果缓存 | 相同 tool_id + params 的重复调用直接返回缓存,可配置 TTL |
| 调用计量 | 记录每个工具的调用频次、耗时、错误率,供调度决策使用 |
| 场景 | 通信方式 | 说明 |
|---|---|---|
| uv 工具 ↔ 路由层 | 子进程 stdio JSON | uv run 启动独立 venv,stdin/stdout 传参,天然隔离 |
| Docker(conda) ↔ 路由层 | localhost:{映射端口} HTTP | 容器端口映射到宿主机 |
| Docker ↔ Docker | docker network | 同一 bridge 内可直接通信 |
| uv ↔ Docker(conda) | 经路由层中转 | 不直接通信,统一走路由层 |
对外(固定):
8001 - FastAPI(HTTP + SSE)
8001 - MCP Server
对内:
uv 本地工具 - 无需端口,子进程 stdio 通信
Docker 工具 - 9001+ 端口映射,按注册顺序递增
9000 - 路由层管理端口
对于同机通信场景,可选用 UDS 替代 TCP 端口:
/tmp/tool_agent/sockets/{tool_id}.sockregistry.json 中每个工具条目增加调度相关字段:
{
"tool_id": "tool_a",
"port": 9001,
"socket": "/tmp/tool_agent/sockets/tool_a.sock",
"last_used_time": "2026-03-19T10:00:00Z",
"call_count": 42,
"avg_latency_ms": 120,
"state": "running | sleeping | stopped"
}
uv run python -m tool_agentpyproject.tomluv run 启动子进程适用于:重依赖工具、GPU 工具、需要特殊系统库的工具
tools/
├── local/ # uv 本地工具(生产)
│ ├── tool_a/
│ │ ├── pyproject.toml
│ │ ├── main.py # 实现 /run /health /schema
│ │ └── ...
│ └── tool_b/
├── docker/ # Docker 工具(生产)
│ ├── tool_c/
│ │ ├── Dockerfile
│ │ ├── environment.yml # conda 环境定义
│ │ ├── main.py
│ │ └── ...
│ └── tool_d/
├── staging/ # 预发环境(Agent 新写的工具先放这里)
│ └── tool_new/
│ ├── main.py
│ └── test_tool.py # Agent 自动生成的测试脚本
└── registry.json # 工具注册表
路由层根据工具类型和使用频率,自动管理工具进程的生命周期:
| 类型 | 策略 | 说明 |
|---|---|---|
| 冷工具(uv 本地) | 按需唤醒 | 收到请求时 uv run 启动,空闲超时后自动杀掉进程释放内存 |
| 热工具(Docker) | 常驻 + 置换 | 高频工具保持容器运行;显存/内存满时按 LRU 策略置换低频容器 |
| API 代理 | 无状态 | 无需管理进程,直接转发 |
调度参数(可配置):
{
"cold_tool_idle_timeout_s": 300,
"hot_tool_max_containers": 5,
"eviction_policy": "lru"
}
Agent 自主编写的新工具不直接进入生产目录,需经过预发验证:
Agent 编写代码 → staging/ 目录
↓
路由层分配临时端口启动
↓
Agent 编写并执行测试脚本
↓
┌─ 通过 → Promote 到 local/ 或 docker/,注册到 registry
└─ 失败 → Agent 修复后重试,或标记为 failed 等待人工介入
安全审计:Agent 生成的代码在 promote 前需通过基础安全检查(禁止危险系统调用如 rm -rf、os.system 等)。
当逆向接入的工具出现故障时,触发自动修复闭环:
工具返回 error(403/签名失效/接口变更)
↓
路由层标记工具状态为 degraded
↓
通知 Tool Agent 介入
↓
┌─ 尝试 Browser-Use 重新抓包更新接口参数
├─ 尝试切换到备用 API 策略
└─ 均失败 → 标记 inactive,通知调用方降级
对外部调用者完全透明:调用方只看到短暂延迟或降级通知,无需感知内部策略切换。
常驻进程,统领全局,是系统的"大脑"。
| 能力 | 说明 |
|---|---|
| 对外接口管理 | 维护 FastAPI / MCP / WebSocket 服务,处理外部请求 |
| 请求路由 | 解析请求,匹配工具,分发到 uv 子进程或 Docker 容器 |
| 冷热调度 | 按需唤醒冷工具、LRU 置换热工具、管理进程/容器生命周期 |
| 健康监控 | 定期检查工具状态,发现异常时通知 Tool Agent 修复 |
| 中间件管理 | 鉴权、结果缓存、调用计量、流量控制 |
| 注册表维护 | 接收 Tool Agent 的 promote 通知,更新路由规则 |
按需唤醒或常驻,负责工具的"生产"和"维修"。
| 能力 | 说明 |
|---|---|
| 工程编码 | 拥有独立工作区(staging/),自主编写工具代码、测试脚本、接口适配器 |
| 代码审计 | 对生成代码进行安全检查(禁止危险系统调用、SQL 注入等) |
| 财务能力 | 自动注册第三方账号、管理 API Key、自动充值/订阅付费 API |
| 决策推理 | 分析工具需求,选择最优获取策略(API/部署/逆向/浏览器) |
| Browser-Use | 浏览器自动化,用于网页操作、信息采集、账号注册、逆向抓包 |
| 子 Agent 调度 | 将子任务分发给专用子 Agent 并行处理 |
| 自修复 | 接收 Router Agent 的异常通知,自动修复失效工具 |
| 知识维护 | 实时总结工具库结构变更,维护各层元信息 |
外部 Agent 请求新工具
│
▼
Router Agent 收到请求,查注册表无此工具
│
▼ 通知
Tool Agent 介入 → 决策获取策略 → 编码/部署/购买
│
▼
staging 验证通过 → promote
│
▼ 通知
Router Agent 更新注册表 + 路由规则
│
▼
Router Agent 响应外部 Agent:工具已就绪
Router Agent 健康检查发现工具异常
│
▼ 通知
Tool Agent 介入 → 自修复(重新抓包/切换策略)
│
▼ 修复完成通知
Router Agent 更新工具状态为 active
Agent 在获取新工具时可能涉及付费操作,需要一套财务管控机制:
{
"budget": {
"monthly_limit_usd": 100,
"single_tx_limit_usd": 20,
"require_approval_above_usd": 10,
"spent_this_month_usd": 0
},
"accounts": [
{
"provider": "openai",
"api_key": "sk-***",
"balance_usd": 50,
"auto_recharge": false
}
]
}
billing_log.json,可追溯Agent 编写代码的完整闭环:
staging/
├── {task_id}/ # 每个编码任务一个隔离目录
│ ├── main.py # Agent 编写的工具代码
│ ├── test_main.py # Agent 编写的测试脚本
│ ├── pyproject.toml # 依赖声明
│ └── run_log.txt # 测试运行日志
工作流程:
1. 创建任务目录 staging/{task_id}/
2. Agent 编写代码 + 测试脚本
3. 在隔离环境中 uv run pytest 执行测试
4. 测试失败 → Agent 读取日志 → 修复 → 重新测试(最多 N 轮)
5. 测试通过 → 安全审计 → Promote 到生产目录
{
"tool_id": "string",
"name": "工具名称",
"category": "分类标签",
"description": "功能描述",
"input_schema": {},
"output_schema": {},
"stream_support": false,
"runtime": {
"type": "local | docker | api | browser",
"entry": "启动入口",
"env": {},
"resource_limits": {
"cpu": "1.0",
"memory_mb": 512,
"gpu": false
}
},
"scheduling": {
"mode": "cold | hot | stateless",
"idle_timeout_s": 300,
"state": "running | sleeping | stopped | degraded"
},
"stats": {
"last_used_time": "ISO8601",
"call_count": 0,
"avg_latency_ms": 0,
"error_rate": 0.0
},
"fallback_strategy": ["api", "browser"],
"status": "active | inactive | staging | building"
}
// 同步调用 POST /tools/{tool_id}/invoke
// 请求
{
"params": { },
"stream": false
}
// 响应(非流式)
{
"status": "success | error",
"result": { }
}
// 响应(流式,Accept: text/event-stream)
data: {"chunk": "部分结果...", "done": false}
data: {"chunk": "最终结果", "done": true}
Router Agent 与 Tool Agent 通过 asyncio.Queue 通信,零网络开销:
# 消息格式
{
"type": "tool_request | tool_ready | tool_error | health_alert",
"payload": { }
}
tool_agent/
├── pyproject.toml # 主项目依赖(fastapi, uvicorn, docker, mcp-sdk 等)
├── README.md
│
├── src/
│ └── tool_agent/
│ ├── __init__.py
│ ├── __main__.py # 入口:uv run python -m tool_agent
│ ├── config.py # 全局配置(端口、调度参数、预算等)
│ │
│ ├── router/ # Router Agent — 路由层
│ │ ├── __init__.py
│ │ ├── agent.py # Router Agent 主逻辑(调度决策、健康监控)
│ │ ├── server.py # FastAPI 应用定义 + 路由注册 + SSE 流式
│ │ ├── mcp_server.py # MCP Server 适配层
│ │ ├── dispatcher.py # 请求分发:按 tool_id 路由到 uv 子进程 / Docker
│ │ ├── scheduler.py # 冷热调度(唤醒/休眠/LRU 置换)
│ │ ├── middleware/
│ │ │ ├── __init__.py
│ │ │ ├── auth.py # 鉴权
│ │ │ ├── cache.py # 结果缓存
│ │ │ └── metrics.py # 调用计量
│ │ └── health.py # 工具健康检查
│ │
│ ├── tool/ # Tool Agent — 工具库维护
│ │ ├── __init__.py
│ │ ├── agent.py # Tool Agent 主逻辑(决策、编排)
│ │ ├── builder.py # 工具编码:生成代码 + 测试脚本
│ │ ├── deployer.py # 工具部署:uv init / docker build
│ │ ├── promoter.py # staging → 生产目录 promote 流程
│ │ ├── auditor.py # 代码安全审计
│ │ ├── repairer.py # 逆向 API 自修复
│ │ ├── browser.py # Browser-Use 能力封装
│ │ └── finance.py # 财务管理(账号注册/充值/预算)
│ │
│ ├── registry/ # 注册层
│ │ ├── __init__.py
│ │ ├── registry.py # 注册表 CRUD(读写 registry.json)
│ │ ├── catalog.py # 工具目录(按类别组织、搜索)
│ │ └── schema.py # 工具元信息 schema 定义(Pydantic models)
│ │
│ ├── runtime/ # 环境层
│ │ ├── __init__.py
│ │ ├── local_runner.py # uv 子进程调用(subprocess + stdio JSON)
│ │ ├── docker_runner.py # Docker 容器管理(Docker SDK)
│ │ ├── api_proxy.py # 外部 API 代理转发
│ │ └── resource.py # 资源配额管理(CPU/内存/显存)
│ │
│ ├── messaging.py # 双 Agent 内部消息队列(asyncio.Queue)
│ └── models.py # 公共数据模型(请求/响应/工具元信息)
│
├── tools/ # 工具库(与主项目代码分离)
│ ├── local/ # uv 本地工具(生产)
│ │ └── example_tool/
│ │ ├── pyproject.toml
│ │ └── main.py
│ ├── docker/ # Docker 工具(生产)
│ │ └── example_gpu_tool/
│ │ ├── Dockerfile
│ │ ├── environment.yml
│ │ └── main.py
│ └── staging/ # 预发环境
│ └── .gitkeep
│
├── data/ # 运行时数据
│ ├── registry.json # 工具注册表
│ ├── billing_log.json # 财务支出记录
│ └── config.json # 运行时配置覆盖
│
└── tests/
├── test_dispatcher.py
├── test_registry.py
├── test_scheduler.py
└── test_local_runner.py
| 目录 | 归属 | 职责 |
|---|---|---|
src/tool_agent/router/ |
Router Agent | 对外接口、请求分发、调度、中间件、健康监控 |
src/tool_agent/tool/ |
Tool Agent | 工具编码、部署、审计、修复、财务、browser-use |
src/tool_agent/registry/ |
共享 | 注册表读写,双 Agent 都会访问 |
src/tool_agent/runtime/ |
Router Agent 调用 | 实际执行工具的运行时(子进程/Docker/API代理) |
tools/ |
Tool Agent 维护 | 工具代码本身,与主项目解耦 |
data/ |
共享 | 运行时状态数据 |
# src/tool_agent/__main__.py
import asyncio
from tool_agent.router.agent import RouterAgent
from tool_agent.tool.agent import ToolAgent
from tool_agent.messaging import MessageBus
async def main():
bus = MessageBus()
router_agent = RouterAgent(bus)
tool_agent = ToolAgent(bus)
await asyncio.gather(
router_agent.start(), # 启动 FastAPI/MCP/WS + 调度循环
tool_agent.start(), # 监听消息队列,按需处理任务
)
if __name__ == "__main__":
asyncio.run(main())
| 阶段 | 内容 | 说明 |
|---|---|---|
| Phase 1 | 基础框架 | 路由层(FastAPI + MCP + SSE)+ 注册层 + 工具目录查询 |
| Phase 2 | 路由与执行 | 路由层实现 + 冷热调度 + 本地工具调用链路打通 |
| Phase 3 | 环境层 | Docker 隔离 + 资源配额 + 工具生命周期管理 |
| Phase 4 | Staging 闭环 | 预发环境 + 代码审计 + 自动测试 + Promote 流程 |
| Phase 5 | Agent 智能 | 新工具自动获取(API/部署/逆向/browser-use)+ 自修复 |
| Phase 6 | 自维护 | 知识总结、健康检查、工具自动更新、调用缓存 |
| 风险 | 影响 | 应对措施 |
|---|---|---|
| 逆向 API 稳定性差 | 工具不可用 | 自修复闭环:Browser-Use 重新抓包 → 备用策略切换 → 降级通知 |
| Docker 环境资源占用 | 本地性能 | 冷热调度 + 资源配额(CPU/内存/显存上限) |
| 工具间依赖冲突 | 环境污染 | 严格隔离,每工具独立环境 |
| 外部 Agent 恶意调用 | 安全风险 | 鉴权机制 + 调用频率限制 |
| Agent 生成危险代码 | 系统安全 | staging 预发验证 + 代码安全审计 |
| 端口碎片化 | 管理复杂 | UDS 备选方案 + 端口回收 + LRU 淘汰 |