☰
XAgent ToolServer 深度解析:Manager/Node 双容器架构、完整 API 说明与部署配置实战
2026/9/25 3:23:27 网站建设 项目流程
  • AI Agent
  • 大模型
  • 后端
  • 任务调度

【免费下载链接】XAgent

An Autonomous LLM Agent for Complex Task Solving

项目地址:https://gitcode.com/gh_mirrors/xa/XAgent
点击查看免费下载

ToolServer 是 XAgent 的工具执行后端:它以 Docker 容器为隔离单元,为 XAgent 的 Agent 提供文件编辑、Python Notebook、网页浏览、Shell 和 Rapid API 五类内置工具,并通过一套 Manager—Monitor—Node 的三级架构管理这些容器实例的生命周期。本文以 ToolServer/README.md 为核心骨架,结合 ToolServer/ToolServerManager/main.py、ToolServer/ToolServerNode/main.py 及 docker-compose.yml 等仓库源码,完整梳理其架构原理、配置文件参数、部署流程与全部 API 端点,读完后可独立部署 ToolServer,并能对照源码理解每一个接口的真实行为。

一、ToolServer 在 XAgent 体系中的定位

对 LLM Agent 而言,"会想"不等于"会做"——执行 Python 代码、访问网页、编辑文件、调用第三方 API 这些动作必须落在一个可控的执行环境中。XAgent 的解决方案就是把所有工具的执行下沉到一个独立的服务端 ToolServer 中,Agent 侧只通过 HTTP 接口与其交互。这样带来两个关键收益:

  1. 安全隔离:工具在 Docker 容器内运行,Agent 生成的 shell 命令、Python 代码不会直接触碰宿主机;
  2. 会话化资源管理:每个任务会话独占一个容器实例,用完即关,避免状态互相污染。

从 XAgent 侧的调用链可以印证这一点。XAgentServer 通过环境变量TOOLSERVER_URL指向 ToolServerManager(见 docker-compose.yml 中XAgentServer.environment的- TOOLSERVER_URL=http://ToolServerManager:8080),而 Agent 框架内的 XAgent/toolserver_interface.py 中的ToolServerInterface类负责封装全部交互:lazy_init在初始化时调用/get_cookie领取容器会话,close方法在任务结束时调用/close_session归还资源。可以说 ToolServer 是 XAgent "手和脚"的集中承载者。

二、三大组件:Manager、Monitor 与 Node

ToolServer/README.md 将 ToolServer 划分为三个部分,各自职责与源码对应关系如下:

组件职责源码位置
ToolServerManager创建和管理 ToolServerNode 实例,对外提供统一 APIToolServer/ToolServerManager/main.py
ToolServerMonitor监控 Node 状态,自动剔除异常实例ToolServer/ToolServerManager/node_checker.py
ToolServerNode真正提供工具的执行容器ToolServer/ToolServerNode/main.py

2.1 Manager:会话创建与请求路由

Manager 是一个 FastAPI 应用(ToolServer/ToolServerManager/main.py#L16),它本身不执行任何工具,而是承担两件核心工作:

(1)按需创建 Node 容器并下发 cookie。当客户端 POST/get_cookie时,Manager 会:

  • 依据manager.yml中node.creation_kwargs直接调用 Docker SDK 启动一个xagentteam/toolserver-node:latest容器(main.py#L127-L131);
  • 把容器 ID 写入响应 cookienode_id,同时把 IP、端口、状态、健康度、最近请求时间等元数据存入 MongoDB(ToolServerNode文档模型见 ToolServer/ToolServerManager/models.py);
  • 调用wait_for_node_startup每秒探测一次容器健康状态,直到creation_wait_seconds(默认 30 秒)超时(main.py#L75-L106)。

(2)按 cookie 路由请求到具体 Node。启动时 Manager 会遍历redirect_to_node_path配置,把一批路径动态注册为路由(main.py#L48-L53)。当前 assets/config/manager.yml 中注册的重定向路径包括/、/execute_tool、/get_available_tools、/get_json_schema_for_tools、/get_json_schema_for_envs、/retrieving_tools、/register_new_tool、/upload_file、/download_file、/download_workspace、/get_workspace_structure。每个被重定向的请求都会经过route_to_node(main.py#L228-L269):校验 cookie 中的node_id有效且容器 running → 更新该节点的last_req_time(这个时间戳正是空闲回收的依据)→ 以http://<node_ip>:<port>为目的地转发原始请求。

2.2 Monitor:内嵌于 Manager 的健康巡检循环

README 中描述的独立 "ToolServerMonitor" 组件,在当前实现中是以协程形式内嵌在 Manager 进程里的:main.py的 startup 钩子在builtin_monitor: true时启动check_nodes_status_loop异步任务(main.py#L31-L45)。巡检循环每health_check_interval(默认 1 秒)执行一次check_nodes_status(node_checker.py#L11-L54),做三件事:

  1. 对账:数据库中每个节点都去 Docker 侧核实,容器已不存在的节点直接从数据库删除;
  2. 同步状态:把容器的State.Status与健康检查状态回写到数据库;
  3. 空闲回收:若节点 running 且last_req_time距现在超过idling_close_minutes(默认 30 分钟),执行container.stop()(node_checker.py#L51-L54)——这就是 README 提到的"idle 后自动关闭 Node 实例"的具体实现。

此外,NodeChecker文档模型会记录当前 Manager 的 pid,Manager 重启时可据此清理残留巡检任务,保证同一套数据库下巡检循环不会重复运行。

2.3 Node:工具注册与执行环境

Node 容器启动时(ToolServer/ToolServerNode/main.py#L22-L33)会:

  • 尝试启动容器内的 docker 服务(service docker start,配合privileged: true,使 Node 内部也能再跑容器);
  • 实例化ToolRegister(位于 ToolServer/ToolServerNode/core/register/register.py),加载全部已注册工具与环境;
  • 调用build_tool_embeddings基于 ToolServer/ToolServerNode/assets/doc_embeding.npy 预构建工具文档的向量索引,供/retrieving_tools做相似度检索。

内置工具按"环境(env)"组织,对应 ToolServer/ToolServerNode/core/envs/ 下的filesystem.py(文件编辑)、pycoding.py(Python 代码执行)、web.py(网页浏览),扩展工具如 ToolServer/ToolServerNode/extensions/envs/rapidapi.py、ToolServer/ToolServerNode/extensions/envs/shell.py 等。README 列出的五类工具与配置项的对应关系:

工具能力关键配置(node.yml)
文档编辑器读写、修改工作目录中的文件filesystem.work_directory: /app/workspace/、filesystem.ignored_list(过滤.git、node_modules、site-packages等目录)
Python Notebook执行 Python 代码、验证想法、绘图notebook.timeout: 300、notebook.save_name: python_notebook.ipynb
网页浏览器搜索并访问网页(headless Chrome)web.browser、web.headless、bing.api_key(留空则退回备用搜索 DuckDuckGo)
Shell执行任意 shell 命令、安装程序、托管服务shell.timeout: 300
Rapid API检索并调用 Rapid API 工具集中的 APIrapidapi.api_key、rapidapi.endpoint,依赖 ToolServer/ToolServerNode/assets/rapidapi_high_quality_apis.json 等资产文件

如需开发新工具,仓库提供了完整指南 ToolServer/ToolServerNode/assets/HOW_TO_BUILD_NEW_TOOLS_CN.md,且 Node 端还暴露了/register_new_tool接口支持运行时动态注册(ToolServer/ToolServerNode/main.py#L226-L249)。

三、配置详解:assets/config/ 下的三个关键文件

ToolServer/README.md 指出配置统一存放在assets/config/,修改后需重启 ToolServer 生效。docker-compose.yml将宿主机./assets/config以 bind 方式挂载到命名卷toolserverconfig,并映射进 Manager 与 Node 两个容器的/app/assets/config,因此改宿主机配置即可同时生效于所有新建容器,无需重新构建镜像。

3.1 manager.yml:Manager 与 Node 的创建策略

assets/config/manager.yml 的关键项:

builtin_monitor: True # 是否在 Manager 进程内运行节点巡检循环 node: creation_wait_seconds: 30 # /get_cookie 时等待节点就绪的最长秒数(探测间隔 1s) idling_close_minutes: 30 # 空闲多少分钟后 Monitor 自动 stop 该容器 health_check: true # 是否启用 docker healthcheck 判定节点可用性 health_check_interval: 1 # 巡检循环轮询间隔(秒) port: 31942 # Node 内部 FastAPI 服务端口 creation_kwargs: # 传给 docker.containers.run() 的完整参数 image: "xagentteam/toolserver-node:latest" network: "tool-server-network" privileged: true # 置 false 可禁止 Node 内使用 docker detach: true volumes: - "toolserverconfig:/app/assets/config" healthcheck: test: ["CMD", "bash", "-c", "curl -f -sS 'http://localhost:31942/' > /dev/null || exit 1"] interval: 1000000000 # 纳秒,1s timeout: 3000000000 retries: 3 redirect_to_node_path: # 哪些路径由 Manager 透传给 Node post: [/execute_tool, /get_available_tools, ...] get: [/]

README 特别强调:若不希望 XAgent 在 ToolServerNode 内再使用 docker(例如限制其安装程序、托管服务的能力),将node.privileged改为false。因为 Node 启动时会执行service docker start,而 dockerd 在非特权容器中无法运行,工具注册阶段会相应降级。

3.2 node.yml:Node 侧工具行为参数

assets/config/node.yml 控制 Node 内各工具的运行时行为,除上表外还有几处值得注意:

  • retriver段定义了工具检索的 embedding 端点(text-embedding-ada-002、维度 1536)以及预置向量文件embedding_file、id2tool_file——/retrieving_tools的相似度计算正是基于这份离线向量 ToolServer/ToolServerNode/assets/doc2tool.json;
  • toolregister.env_max_tools_display: 10对应 README 中"available_envs的 tools 列表最多返回 50 条/每环境展示上限"类截断逻辑(ToolServer/ToolServerNode/core/register/register.py 中的展示数量控制);
  • enabled_extensions段通过模块路径动态启用扩展工具,例如取消注释extensions.envs.rapidapi即加载 Rapid API 环境。

README 提醒:要在node.yml中填入bing.api_key启用必应搜索(不填则走备用搜索 DuckDuckGo),填入rapidapi.api_key与rapidapi.endpoint启用 Rapid API。

3.3 docker-compose.yml:超时与网络

README 提到遇到 ToolServer 超时应调整docker-compose.yml中services.ToolServerManager.command里-t后的值。在当前 docker-compose.yml 中该值为:

command: ["--workers","2","-t","600"]

即 Manager 以 gunicorn 2 个 worker 启动,请求超时设为 600 秒。由于 Shell、Notebook 等工具本身允许 300 秒的执行超时,且 Agent 任务链可能串联多次工具调用,遇到长任务超时时可调大该值。另外注意 Manager 容器挂载了/var/run/docker.sock——这是它能以 Python SDK 直接创建、停止、删除 Node 容器的前提。

四、构建与启动

前置条件是宿主机安装docker与docker-compose。两种启动方式(摘自 ToolServer/README.md):

# 方式一:直接拉取官方镜像启动 docker compose up # 方式二:先自行构建镜像再启动 docker compose build docker compose up

docker compose build会使用仓库内 dockerfiles/ToolServerManager/Dockerfile 与 dockerfiles/ToolServerNode/Dockerfile 构建xagentteam/toolserver-manager:latest与xagentteam/toolserver-node:latest。完整的 docker-compose.yml 还会同时拉起db(MongoDB,供 Manager 存节点元数据)、XAgentServer、xagent-mysql、xagent-redis等服务,构成完整的 XAgent 服务栈;若只关心 ToolServer,可单独运行其中的ToolServerManager与db服务。所有容器通过名为tool-server-network的 bridge 网络互通(manager.yml中creation_kwargs.network与 compose 的networks.default.name均指向它),Manager 正是靠这个网络拿到 Node 的 IP 进行转发。

五、API 完整说明

以下端点说明基于 ToolServer/README.md 的 API 章节,并校正/补全了源码中的真实参数名与行为细节。所有请求都发往 Manager 的 8080 端口,Manager 再把工具类请求透传给 cookie 绑定的 Node。

提示:README 中写作/get_cookies,实际源码注册的端点为/get_cookie(ToolServer/ToolServerManager/main.py#L108,XAgent 客户端同样调用/get_cookie,见 XAgent/toolserver_interface.py#L94-L95)。

5.1 /get_cookie:建立会话

POST 请求,无参数。Manager 立即创建一个新的 Node 容器,返回消息与版本号,并把node_idcookie 写入响应。此后所有工具请求都必须携带该 cookie,Manager 据此定位目标 Node;cookie 无效会返回 403,Node 非 running 返回 503(main.py#L243-L248)。若creation_wait_seconds内容器未就绪,返回 503 "Node creation timeout!"。

5.2 /get_available_tools:获取全部工具

无需参数,返回三段信息(实现见 ToolServer/ToolServerNode/main.py#L127-L140):

{ "available_envs": [ { "name": "env1", "description": "description1", "tools": ["tool1", "tool2"] } ], "available_tools": ["tool1", "tool2"], "tools_json": [ { "name": "tool1", "description": "description1", "parameters": { "type": "object", "properties": { "param1": { "type": "string", "description": "description1" }, "param2": { "type": "integer", "description": "description2" } }, "required": ["param1", "param2"] } } ] }

注意两点截断策略:available_envs中每个环境列出的工具数量受node.yml的toolregister.env_max_tools_display限制(README 标注上限 50);available_tools不包含被标记为隐藏的内部工具(如ShellEnv_read_stdout这类用于 450 重试的内部工具)。

5.3 /retrieving_tools:按问题检索工具

给定问题,返回语义最相关的 top_k 个工具(实现见 ToolServer/ToolServerNode/main.py#L142-L173)。请求体:

{ "question": "question", "top_k": 10 }

top_k在源码中的默认值为 5。返回:

{ "retrieved_tools": ["tool1", "tool2"], "tools_json": [ { "name": "tool1", "description": "...", "parameters": { } } ] }

从源码结构看,检索走ada_retriever(ToolServer/ToolServerNode/utils/retriever.py):用预构建的doc_embeding.npy向量与doc2tool.json的 id2tool 映射做相似度排序;若 Rapid API 扩展已启用,其 API 同样参与检索结果。

5.4 /get_json_schema_for_tools 与 /get_json_schema_for_envs:按名取 schema

两个端点用于"指名道姓"地获取工具/环境的 JSON schema(实现分别见 main.py#L176-L199 与 main.py#L201-L224)。源码中的参数名是tool_names与env_names:

{ "tool_names": ["tool1", "tool2"] }
{ "env_names": ["env1", "env2"] }

返回结构一致地分为"命中的部分 + 缺失列表",便于 Agent 感知自己拼错或引用了不存在的名字:

{ "tools_json": [ { "name": "tool1", "description": "...", "parameters": {} } ], "missing_tools": ["tool3"] }
{ "envs_json": [ { "name": "env1", "description": "description1", "tools": ["tool1", "tool2"] } ], "missing_envs": ["env3"] }

5.5 /execute_tool:执行工具与 450 异步重试协议

执行指定工具,源码参数名为tool_name与arguments(另可选env_name指定在哪个环境中执行同名工具)(main.py#L251-L289):

{ "tool_name": "tool1", "arguments": { "param1": "value1", "param2": "value2" } }

成功时返回体由wrap_tool_response统一包装为type: simple / composite / binary三种形态之一(XAgent 侧的unwrap_tool_response会拆包,binary 数据落地到local_workspace,见 XAgent/toolserver_interface.py#L29-L66)。

450 状态码是 ToolServer 最有特色的协议:它表示"工具尚未执行完毕,需要后续调用才能拿到完整结果"(典型场景是 Shell 里启动了长时命令,需要先读 stdout)。触发链路是:工具抛出OutputNotReady异常 → Node 将其转为 450 响应(main.py#L280-L281)。响应体示例(README 原样保留):

{ "detail": { "type": "retry", "next_calling": "ShellEnv_read_stdout", "arguments": {} } }

next_calling指明下一次应调用的工具名,Agent 按其指引再次 POST/execute_tool直至拿到最终输出。这套"可中断-可续跑"的协议让 ToolServer 能够承载超出一对请求/响应模型的长耗时操作。

5.6 /close_session 与 /release_session:归还与销毁会话

任务结束时 XAgent 客户端会调用/close_session(XAgent/toolserver_interface.py#L97-L101):

  • /close_session(main.py#L181-L200):Manager 获取容器并stop(),容器停止但保留,理论上可通过/reconnect_session重启续用(main.py#L152-L179);
  • /release_session(main.py#L202-L226):kill()容器后remove()彻底删除,并释放其占用的数据库记录。

这两个端点体现了 ToolServer 的会话资源语义:close 是"暂停",release 是"销毁"。此外即使客户端不调用,Monitor 的空闲回收(idling_close_minutes)也会兜底关闭长期无请求的节点。

六、小结:把 XAgent 接入 ToolServer 的最小路径

综合以上各节,一条可落地的接入路线是:

  1. docker compose up启动全栈,Manager 监听宿主机 8080 端口(docker-compose.yml 的ports: 8080:8080);
  2. 按需在 assets/config/node.yml 填入bing.api_key、rapidapi.api_key,按需在 assets/config/manager.yml 调整node.privileged与idling_close_minutes,重启生效;
  3. XAgent 侧配置TOOLSERVER_URL(或本地运行时use_selfhost_toolserver对应的 URL),ToolServerInterface会自动完成/get_cookie→ 工具调用 →/close_session的完整会话;
  4. 遇到长任务超时,优先调大docker-compose.yml中-t值与notebook.timeout/shell.timeout,并注意 450 重试协议需要 Agent 侧按next_calling指引继续调用。

这样,ToolServer 便成为 XAgent 可水平扩展、可安全销毁、可动态检索增强的工具执行底座;而 ToolServer/ToolServerNode/assets/HOW_TO_BUILD_NEW_TOOLS_CN.md 中的新工具开发指南,则是继续扩充这份工具清单的入口。

  • AI Agent
  • 大模型
  • 后端
  • 任务调度

【免费下载链接】XAgent

An Autonomous LLM Agent for Complex Task Solving

项目地址:https://gitcode.com/gh_mirrors/xa/XAgent
点击查看免费下载
上一篇:OpenSpeedy调试工具插件开发:入门教程
下一篇:uuid性能基准测试指南:如何构建你自己的UUID生成速度benchmark

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询