talents - Gateway 三件套:OpenApi / Tool / MCP
Gateway 网关是 Solon AI 生态中解决"工具过多导致上下文溢出"的核心机制。提供三个独立的网关 Talent,分别对接 REST API、本地工具、MCP 服务,均内置四阶段自适应发现模式,可根据工具数量自动切换策略,平衡模型推理精度与 Token 消耗。
相关依赖包: solon-ai-talent-gateway
1、架构概览
| 网关 Talent | 适用场景 | 工具来源 | 管理粒度 |
|---|---|---|---|
| OpenApiGatewayTalent | 对接海量 WebAPI,自动解析 OpenAPI / Swagger 文档 | 远程或本地的 OpenAPI / Swagger 定义文件 | 以 API 源 为单位增删 |
| ToolGatewayTalent | 治理代码侧 FunctionTool 数量膨胀(本地工具与 MCP Tool 都可纳管) | FunctionTool(本地工具,或已接入的 MCP Tool) | 运行时按 单个 Tool 命令式增删 |
| McpGatewayTalent | 按服务整体接入并治理 MCP 服务端工具 | MCP Client 连接 | 按 整条 MCP 连接 增删;连接内的工具用白/黑名单做开放控制 |
三者共享相同的四阶段自适应模式和 API 风格,可独立使用,也可组合挂载到同一个 Agent 上。
如何选? 看两点——接入方式和增删单位:
- 工具以 OpenAPI / Swagger 文档 形式存在 →
OpenApiGatewayTalent。 - 工具是 代码里注册的
FunctionTool,需要在运行时逐个动态增删(命令式,精确到单个 Tool)→ToolGatewayTalent。 - 工具来自 MCP 服务端,按服务整体接入更顺手 →
McpGatewayTalent。
这里有个容易被误解的点:「想精确控制 MCP 暴露哪些工具」并不等于「必须用 ToolGatewayTalent」。McpGatewayTalent 在接入每条连接时,可通过 McpServerParameters 的 allowedTools(白名单,空表示全部)/ disallowedTools(黑名单,空表示不禁)做工具级开放控制,精确到该服务暴露哪几个工具。
所以两者的真正分野不是「能不能管到单个工具」,而是控制方式:ToolGatewayTalent 是运行时、命令式、按单个 Tool 动态增删;McpGatewayTalent 是接入时声明式配置白/黑名单,工具集随连接生命周期管理,变更名单后需调用 refreshMcpServer() 生效。工具来源杂、需要运行时灵活拼装就用前者;按 MCP 服务整体接入、用名单声明式收敛工具面就用后者。
2、四阶段自适应发现机制
三个网关 Talent 使用完全一致的阈值切换逻辑:
| 模式名称 | 触发条件 | 对 LLM 的表现 | 暴露的工具 |
|---|---|---|---|
| FULL(全量) | 数量 <= dynamicThreshold(默认 8) | 直接看到所有工具完整定义 | 原始工具(OpenAPI 为 call_api) |
| SUMMARY(摘要) | 8 < 数量 <= listThreshold(默认 30/40) | 展示"工具名 + 功能描述 + Endpoint"清单 | get_*_detail + call_*(无搜索) |
| LIST(名字) | 30/40 < 数量 <= searchThreshold(默认 100) | 仅展示分组及工具名列表,需按需检索 | search_* + get_*_detail + call_* |
| SEARCH(搜索) | 数量 > 100 | 清单完全折叠,强制关键词检索 | search_* + get_*_detail + call_* |
核心设计思想:接口/工具数量少时,直接将完整 Schema 嵌入 Instruction,LLM 可一步到位调用;数量增多后,逐步折叠信息,通过中转工具引导 LLM 先搜索、再查看详情、最后执行。
3、OpenApiGatewayTalent — 对接海量 WebAPI
3.1 核心特性
- 双版本解析:内置自适应解析器,自动识别 Swagger 2.0 和 OpenAPI 3.0 规范(通过 JSON 中是否存在
openapi字段判断版本)。 - 多源 API 整合:支持从多个不同的 Swagger 节点(
http://或classpath:)并行加载接口,按 tag 自动分组。 - $ref 递归解析:深度展开
$ref引用(含嵌套对象、数组内部引用),自动处理循环引用(输出_Circular_Reference_标记)。 - 全自动代理:AI 仅需下达意图,由网关自动完成 Path 占位符替换(含 URL 编码)、Query/Body 序列化、Header 注入及 Multipart 提交。
- 上下文保护:内置响应结果截断(默认 8000 字符,可通过
maxContextLength配置),防止庞大的业务数据撑爆 AI 上下文。 - 细粒度权限控制:每个 API 源支持独立的
allowedTools(白名单)和disallowedTools(黑名单),运行时可在ApiSourceClient副本上动态修改,不影响原始配置。 - 失败重试:内置
RetryUtil重试机制(默认 3 次),可全局或按源配置超时时间。 - 禁用接口自动忽略:解析时自动跳过 Swagger 中标记为
@Deprecated的接口,保持 Agent 技能树整洁。
3.2 应用示例
// 1. 初始化 Talent
OpenApiGatewayTalent apiTalent = new OpenApiGatewayTalent()
.dynamicThreshold(5) // 超过 5 个接口开启摘要模式
.listThreshold(30) // 超过 30 个接口开启名字列表模式
.searchThreshold(100) // 超过 100 个接口开启强制搜索模式
.maxContextLength(10000) // 结果截断长度
.defaultTimeout(Duration.ofSeconds(30))
.defaultAuthenticator((http, tool) -> {
http.header("Authorization", "Bearer your-token"); // 全局认证
});
// 2. 挂载多个业务模块的 API(支持 http 或 classpath)
apiTalent.addApi("http://order-service:8081/v3/api-docs", "http://order-service:8081");
apiTalent.addApi("classpath:swagger/user-api.json", "http://user-service:8082");
// 2.1 也可使用 ApiSource 对象精细配置(支持独立认证、权限过滤、超时等)
ApiSource source = new ApiSource();
source.setDocUrl("http://pay-service:8083/v3/api-docs");
source.setApiBaseUrl("http://pay-service:8083");
source.setAllowedTools(Arrays.asList("createPayment", "queryPayment")); // 仅加载 2 个接口
source.setAuthenticator(ApiAuthenticator.bearer("pay-service-token"));
source.setTimeout(Duration.ofSeconds(60));
apiTalent.addApi(source);
// 3. 构建智能体
ReActAgent agent = ReActAgent.of(LlmUtil.getChatModel())
.role("高级业务助理")
.instruction("请优先使用提供的业务 API 解决问题,不要猜测接口参数。")
.defaultTalentAdd(apiTalent)
.build();
// 4. 运行时动态管理
apiTalent.removeApi("http://order-service:8081/v3/api-docs"); // 移除某个源
apiTalent.refreshApi("http://pay-service:8083/v3/api-docs"); // 刷新权限过滤
apiTalent.refreshApiAll(); // 刷新所有源
3.3 内置工具说明
根据当前阶段模式,AI 会自动调用以下工具(工具名以全量场景为例):
| 工具名 | 可用模式 | 功能说明 |
|---|---|---|
call_api | 全部模式 | 执行 REST API 调用。支持路径参数替换({name} 格式,自动 URL 编码)、Query/Body 序列化、Multipart 提交 |
get_api_detail | SUMMARY/LIST/SEARCH | 获取指定接口的完整参数 Schema(Header、Path、Query、Body)及返回值结构 |
search_apis | LIST/SEARCH | 通过多关键词(空格分隔,AND 逻辑匹配)在海量 API 库中模糊搜索接口,返回分组、描述及 Endpoint |
3.4 内置认证策略
ApiAuthenticator 是一个函数式接口,支持灵活的认证扩展:
// 方式 1:Bearer Token
apiTalent.defaultAuthenticator(ApiAuthenticator.bearer("your-token"));
// 方式 2:自定义 Header
apiTalent.defaultAuthenticator(ApiAuthenticator.apiKey("X-Api-Key", "your-key"));
// 方式 3:完全自定义(如动态签名)
apiTalent.defaultAuthenticator((http, tool) -> {
http.header("X-Timestamp", String.valueOf(System.currentTimeMillis()));
http.header("X-Signature", HmacUtils.sign(tool.getPath(), secretKey));
});
认证优先级:源级 Authenticator > 全局 defaultAuthenticator。
3.5 运行时权限管理
每个 API 源通过 ApiSourceClient 维护独立的权限副本:
ApiSourceClient client = apiTalent.getApiSource("http://pay-service:8083/v3/api-docs");
// 在 client 副本上动态修改(不影响 ApiSource 原始配置)
client.setAllowedTools(Arrays.asList("createPayment")); // 运行时缩小范围
client.getDisallowedTools().add("queryRefund"); // 运行时追加黑名单
// 刷新使权限生效
apiTalent.refreshApi("http://pay-service:8083/v3/api-docs");
4、ToolGatewayTalent — 通用工具网关(本地工具 + MCP Tool)
4.1 核心特性
- 通用工具网关:适用于任意
FunctionTool,既能纳管代码注册的本地工具、第三方 SDK 工具,也能纳管 MCP Tool。 - 以单个 Tool 为单位治理:动态添加/移除的颗粒度是单个
FunctionTool;这也是它与McpGatewayTalent的核心区别——后者以整条 MCP Client 连接为单位增删。两者在管理 MCP Tool 上能力重叠,按所需的治理颗粒度选择即可。 - 分类管理:支持按
category分组注册工具,在 Instruction 中按分组展示,帮助 LLM 定位。 - 代理执行:通过内置
call_tool代理工具透明执行底层业务工具,支持上下文传导。
4.2 应用示例
ToolGatewayTalent toolGateway = new ToolGatewayTalent()
.dynamicThreshold(8)
.listThreshold(40)
.searchThreshold(100);
// 添加工具(支持按分组注册)
toolGateway.addTool("财务工具", financeToolProvider);
toolGateway.addTool("HR工具", hrToolProvider);
toolGateway.addTool("常规工具", singleFunctionTool);
// 构建智能体
ReActAgent agent = ReActAgent.of(LlmUtil.getChatModel())
.role("企业业务助手")
.defaultTalentAdd(toolGateway)
.build();
4.3 内置工具说明
| 工具名 | 可用模式 | 功能说明 |
|---|---|---|
call_tool | SUMMARY/LIST/SEARCH | 代理执行指定的业务工具,传入 tool_name 和 tool_args |
get_tool_detail | SUMMARY/LIST/SEARCH | 获取指定工具的完整参数 Schema(含 JSON Schema 定义) |
search_tools | LIST/SEARCH | 通过多关键词(空格分隔,AND 逻辑)搜索匹配的工具名和描述 |
注意:FULL 模式下,原始业务工具直接暴露给 LLM,不经过代理。
5、McpGatewayTalent — 管理多 MCP 服务端
5.1 核心特性
- 多服务管理:按配置名(如
"weather"、"database")管理多个 MCP 服务端连接,支持动态添加与移除。 - 工具级开放控制:通过
McpServerParameters的allowedTools(白名单,空表示全部)/disallowedTools(黑名单,空表示不禁)声明式筛选每个 MCP 服务端实际暴露哪些工具——所以它不是只能整条连接「全开全关」,同样能精确到单个 MCP Tool 的开放控制。变更名单后调用refreshMcpServer()生效。 - 影子交换策略:刷新工具列表时采用"先加新、再删旧"的原子替换,避免调用中的线程因工具短暂丢失而失败。
- 生命周期管理:移除服务时自动关闭底层连接;禁用服务仅从工具索引移除,保留 Provider 不关闭连接。
- 按需依赖:MCP 功能依赖
solon-ai-mcp模块,如果项目不使用 MCP,可排除该依赖。
5.2 应用示例
McpGatewayTalent mcpGateway = new McpGatewayTalent()
.dynamicThreshold(8)
.listThreshold(40)
.searchThreshold(100);
// 方式 1:通过 McpServerParameters 添加
mcpGateway.addMcpServer("weather", new McpServerParameters().then(e->{
e.setTransport("stdio");
e.setCommand("npx");
e.addArgVar("-y");
e.addArgVar("@modelcontextprotocol/server-weather");
}));
// 方式 1+:接入时做工具级开放控制(只暴露该服务的部分工具)
mcpGateway.addMcpServer("database",new McpServerParameters().then(e-> {
e.setTransport("stdio");
e.setCommand("npx");
e.setArgs(Arrays.asList("-y", "@modelcontextprotocol/server-database"));
e.setAllowedTools(Arrays.asList("query", "list_tables"));
e.setDisallowedTools(Arrays.asList("drop_table", "execute_raw"));
}));
// 方式 2:通过 McpClientProvider 添加
McpClientProvider provider = McpClientProviders.fromMcpServer(params);
mcpGateway.addMcpServer("database", provider);
// 运行时管理
mcpGateway.removeMcpServer("weather"); // 移除并关闭连接
mcpGateway.refreshMcpServer("database"); // 刷新工具列表(权限变更后)
mcpGateway.refreshMcpServerAll(); // 刷新所有 MCP 服务
// 查询
Set<String> serverNames = mcpGateway.getMcpServerNames();
boolean exists = mcpGateway.hasMcpServer("database");
5.3 内置工具说明
与 ToolGatewayTalent 使用相同的工具名称和逻辑:
| 工具名 | 可用模式 | 功能说明 |
|---|---|---|
call_tool | SUMMARY/LIST/SEARCH | 代理执行指定的 MCP 工具,传入 tool_name 和 tool_args,支持上下文传导 |
get_tool_detail | SUMMARY/LIST/SEARCH | 获取指定 MCP 工具的完整参数 Schema |
search_tools | LIST/SEARCH | 通过关键词搜索匹配的 MCP 工具 |
6、OpenAPI 解析器详解
6.1 解析器架构
ApiResolver(接口)
└── OpenApiResolver(自适应路由,自动判断 v2/v3)
├── OpenApiV2Resolver(Swagger 2.0,基于 swagger-parser 1.x)
└── OpenApiV3Resolver(OpenAPI 3.0,基于 swagger-parser 2.x)
6.2 解析能力矩阵
| 能力 | Swagger 2.0 | OpenAPI 3.0 | 说明 |
|---|---|---|---|
| Path 参数解析 | ✅ | ✅ | 自动识别为必填,Schema 带完整类型定义 |
| Query 参数解析 | ✅ | ✅ | 支持枚举(enum)、required 标记 |
| Header 参数解析 | ✅ | ✅ | 独立的 headerSchema 输出 |
| Body 参数解析 | ✅ | ✅ | V2 通过 BodyParameter/definitions;V3 通过 RequestBody/content |
| FormData 解析 | ✅ | — | 自动识别为 Multipart |
| RequestBody 解析 | — | ✅ | 根据 Content-Type 自动识别 JSON/Multipart |
| $ref 引用展开 | ✅ | ✅ | 递归解析 components/schemas 和 definitions |
| 循环引用保护 | ✅ | ✅ | 输出 _Circular_Reference_ 标记,防止无限递归 |
| 数组类型展开 | ✅ | ✅ | Array 内部 items 引用深度平铺 |
| Response 解析 | ✅ | ✅ | 优先 200,回退 201/default |
| BaseUrl 提取 | ✅ | ✅ | V2 从 host+schemes+basePath;V3 从 servers 节点(支持相对路径补全) |
| Deprecated 过滤 | ✅ | ✅ | 标记为废弃的接口自动跳过,不注册到工具表 |
| 分组(Tags) | ✅ | ✅ | 按 OpenAPI 的 tags 字段自动分组,首个 tag 作为 category |
6.3 自定义解析器
可通过 resolver() 方法替换默认解析器(实现 ApiResolver 接口即可):
apiTalent.resolver(new MyCustomApiResolver());
7、通用配置参数
以下参数三个网关 Talent 共享(方法名一致,仅阈值默认值略有差异):
| 配置方法 | 默认值 | 说明 |
|---|---|---|
dynamicThreshold(n) | 8 | 全量平铺模式的上限 |
listThreshold(n) | OpenAPI: 30,其他: 40 | 摘要模式的上限 |
searchThreshold(n) | 100 | 名字列表模式的上限 |
retryConfig(maxRetries) | 3 | 失败重试次数 |
retryConfig(maxRetries, retryDelayMs) | 3, 1000 | 失败重试次数 + 初始延迟(仅 McpGatewayTalent 支持延迟参数) |
maxContextLength(n) | 8000 | 响应截断长度(仅 OpenApiGatewayTalent) |
8、注意事项
- 接口唯一性:不同数据源加载的接口/工具,其名称(OperationId)不能重复。网关内部统一转为小写存储,调用时大小写不敏感。
- 路径占位符:确保 Swagger 定义中的路径参数使用
{name}格式,网关会自动完成URLEncoder编码替换。如果调用时路径中仍存在{xxx},网关会返回明确的错误提示。 - 过时接口:标记为
@Deprecated的接口会被自动忽略,不进入工具表。 - 禁用源:
ApiSource.setEnabled(false)或McpClientProvider.setEnabled(false)的源仅注册到管理表,不加入工具索引。可通过setEnabled(true)+refreshApi()/refreshMcpServer()重新激活。 - 权限变更刷新:在
ApiSourceClient副本上修改allowedTools/disallowedTools后,需调用refreshApi()才能生效。 - Maven 依赖:MCP 功能依赖
solon-ai-mcp;OpenAPI 解析依赖swagger-parser(v1 和 v3 两个版本),如果不需要可排除。