1 Link 模块在整个系统中的作用是什么?为什么要单独抽出来?
考察点:模块设计、职责分离
参考答案:
Link 模块是工作流引擎和外部工具之间的"桥梁",专门负责对接第三方 API 来增强工作流的 AI 能力,一方面可以作为 Tool 来使用,一方面可以作为 Agent 节点的技能来使用。
它的核心职责包括工具管理,比如说存储工具的 OpenAPI Schema,知道每个工具该怎么调用;统一调用协议,不管是讯飞的 TTS、还是阿里千问的 TTS,都通过统一的接口调用;鉴权处理,自动处理 API Key、Token 等认证方式;参数转换,可以把工作流传过来的参数转成目标 API 需要的格式。
单独抽出来的好处是,工作流引擎不需要知道具体怎么调用一个工具,新增工具只需要在 Link 里注册就行,不用改工作流引擎,工具的调用逻辑可以独立升级。
参考答案版本 2:
Link 是整个系统的"工具调度中心",负责对接各种外部能力。
工作流引擎在执行到插件节点时,不会自己去调用语音合成、图片生成这些能力,而是把请求转发给 Link,由 Link 去调用实际的工具服务,拿到结果再返回给引擎。Link 就像一个中间代理,屏蔽了底层工具的差异性。
为什么要单独抽出来?主要是三个考虑。
第一是协议统一。外部工具五花八门,有的是 REST API,有的是 WebSocket,有的是自定义协议。每个工具的认证方式、参数格式、返回结构都不一样。如果让工作流引擎直接对接这些工具,引擎代码会变得非常臃肿,每加一个工具就要改引擎。Link 做了一层抽象,对内暴露统一的调用接口,对外适配各种协议。引擎只管按照标准格式调 Link,具体怎么调用底层工具是 Link 的事。
第二是工具生命周期管理。工具不是静态的,要支持动态注册、更新、下线。工具的元信息(参数定义、返回结构、调用地址)存在数据库里,Link 负责读取这些配置,按照配置去调用工具。新增工具只需要往数据库里写一条记录,不用改代码、不用重启服务。这种配置化的方式让系统的扩展性很好。
第三是和 MCP 协议的集成。MCP 是现在 AI 领域比较流行的工具协议,很多第三方能力都在往这个标准靠。Link 实现了 MCP 客户端,可以对接符合 MCP 标准的工具服务器。这样外部的工具生态可以直接接入,不用为每个工具单独开发适配器。
还有一个隐含的好处是故障隔离。工具调用是整个系统里最不可控的部分,外部服务可能超时、可能返回错误格式、可能直接挂掉。把这些不确定性封装在 Link 里,引擎只需要处理 Link 返回的标准结果。Link 内部可以做重试、降级、熔断这些容错逻辑,不让外部工具的问题影响到核心的工作流执行。
简单说,Link 的价值在于解耦。工作流引擎专注于流程调度,Link 专注于工具调用,各自演进互不干扰。想加新工具不用动引擎,想优化调度逻辑不用关心工具怎么接入。
2 你是怎么解析 OpenAPI Schema 的?怎么知道一个工具该怎么调用?
考察点:OpenAPI 规范、Schema 解析
参考答案:
OpenAPI 是描述 REST API 的标准格式。我们用它来定义工具的调用方式。一个工具的 OpenAPI Schema 主要包含了插件的基本信息,API 的请求参数等。
{
"openapi": "3.0.0",
"info": {"title": "TTS 服务"},
"servers": [{"url": "http://core-aitools:18668"}],
"paths": {
"/api/tts/synthesize": {
"post": {
"operationId": "synthesize_speech",
"requestBody": {
"content": {
"application/json": {
"schema": {
"properties": {
"text": {"type": "string", "description": "要合成的文本"},
"voice": {"type": "string", "description": "音色"}
}
}
}
}
}
}
}
}
}
解析流程分 4 步,先从数据库中查到工具的 Schema,然后获取服务的请求地址,请求参数等,然后封装成 HTTP 请求发起调用。
// 1. 从数据库查工具的 Schema
ToolEntity tool = toolCrudService.getById(toolId);
Map openApiSchema = JSON.parseObject(tool.getOpenApiSchema());
// 2. 获取服务地址
String serverUrl = openApiSchema.getServers().get(0).getUrl();
// 3. 根据 operationId 找到对应的 path 和 method
for (Entry path : openApiSchema.getPaths()) {
for (Entry method : path.getValue()) {
if (operationId.equals(method.get("operationId"))) {
// 找到了!记录 m...真诚点赞 诚不我欺
回复