AI助手融合架构方案:融合“路由-执行”模式与Nacos 3.0的智能工具调用系统 (含核心实现代码)
企业级AI助手统一架构方案:融合“路由-执行”模式与Nacos 3.0的智能工具调用系统 (含核心实现代码)
一、 背景与挑战:为何简单的AI集成还不够?
随着大语言模型(LLM)能力的日益增强,将其与企业现有业务系统(如微服务)结合,打造智能AI助手已成为趋势。然而,一个初步的集成方案往往会迅速遇到两个核心瓶颈:
- 工具维护的噩梦:当企业有成百上千个API接口时,如何在AI侧手动维护和注册这些“工具”?这不仅工作量巨大,而且极易出错,难以跟上业务的快速迭代。
- 上下文与成本的诅咒:为了让AI知道它能做什么,最直接的方法是把所有工具的说明书(API规范)都在每次对话时发给它。这会迅速耗尽AI模型的上下文窗口,导致API调用成本飙升,并因为信息过载而降低决策的准确性。
我们必须寻找一个更智能、更具扩展性的架构,来应对拥有海量业务能力的企业级应用场景。
二、 核心思路:“先路由,再执行”的两阶段智能调度
本方案的核心思想,源自于模拟人类专家的工作方式:当面对一个复杂问题时,专家不会立刻深入细节,而是先快速判断问题属于哪个领域,再调动该领域的知识和工具来解决问题。
我们引入一个名为 “AI路由器(AI Router)” 的概念,将AI的思考过程拆分为两个独立的阶段:
-
路由阶段(轻量级、低成本):
- 目标:快速、低成本地从企业所有可用的工具中,筛选出与当前用户问题最相关的几个。
- 技术:利用向量搜索(Vector Search)。我们将所有工具的“功能描述”预先转换成数学向量并存入数据库。当用户提问时,我们同样将问题转换成向量,通过计算相似度,高效地“大海捞针”,找出最匹配的工具。
-
执行阶段(重量级、高精度):
- 目标:只将路由阶段筛选出的极少数相关工具提供给最强大的AI模型,让它在极小的干扰下,做出最精准的决策并执行。
- 技术:利用Spring AI的函数调用(Function Calling)。
这个架构将“体力活”(从海量工具中筛选)与“脑力活”(在少量相关工具中决策)彻底分开,从而实现效率、成本和准确性的完美平衡。
三、 架构组件与实现详解
我们的系统由三个核心服务和一套自动化流程构成,其中Nacos 3.0扮演了至关重要的“企业能力目录”角色。
1. 业务能力层:现有微服务集群
(基于SpringCloud Alibaba)
- 角色:企业业务能力的基石。
- 职责:保持现状,完全零改造。每个微服务只需像往常一样,利用
springdoc-openapi
等工具暴露自己的API文档,并向Nacos注册即可。
2. 企业能力目录:Nacos 3.0
- 角色:权威的、动态的、结构化的“企业API黄页”。它是连接业务层与AI智能层的关键桥梁。
- 配合细节:
- 自动聚合API规范:当任何一个微服务(如
order-service
)启动并向Nacos 3.0注册时,它不仅会注册自己的网络地址,还会将自己的OpenAPI 3规范(由springdoc
自动生成)作为**元数据(Metadata)**一同上报给Nacos。 - 提供统一查询接口:Nacos 3.0提供了新的API,允许外部系统查询注册上来服务的所有元数据。这使得我们的AI适配层有了一个单一、可靠的入口来获取整个企业所有微服务的API定义。
- 自动聚合API规范:当任何一个微服务(如
3. AI适配层:ai-adapter-gateway
(适配器网关)
- 角色:企业所有微服务能力的“统一AI发言人”和“执行者”。
- 与Nacos 3.0的配合细节:
- 动态生成工具清单:此网关不再需要手动配置。它在启动时,会调用Nacos 3.0的API,获取所有已注册服务的列表及其OpenAPI规范元数据。然后,它在内存中动态解析这些规范,自动生成符合AI协议的
/ai/tools
接口的输出。当有新微服务上线或旧服务下线时,网关无需重启,只需刷新配置即可感知到变化。 - 动态服务发现与调用:当它接收到
/ai/use-tool
的调用命令时,它会利用Nacos传统的服务发现能力,找到目标微服务的一个健康实例,并将请求动态路由过去。
- 动态生成工具清单:此网关不再需要手动配置。它在启动时,会调用Nacos 3.0的API,获取所有已注册服务的列表及其OpenAPI规范元数据。然后,它在内存中动态解析这些规范,自动生成符合AI协议的
4. 智能调度层:intelligent-assistant
(智能助手核心)
这是本方案的核心,内部包含了“路由-执行”的完整逻辑。
4.1 核心组件一:离线工具索引器 (ToolIndexingRunner.java
)
职责:在应用启动时,自动从ai-adapter-gateway
拉取所有工具,将其描述向量化后存入向量数据库,为“路由阶段”做准备。
package com.example.assistant.config;import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;import lombok.extern.slf4j.Slf4j;@Slf4j
@Component
public class ToolIndexingRunner implements CommandLineRunner {private final VectorStore vectorStore;@Value("${gateway.base-url:http://localhost:8081}")private String gatewayBaseUrl;public ToolIndexingRunner(VectorStore vectorStore) {this.vectorStore = vectorStore;}@Overridepublic void run(String... args) {log.info("--- [启动任务] 开始进行工具的离线索引 ---");try {RestTemplate restTemplate = new RestTemplate();String toolsUrl = gatewayBaseUrl + "/ai/tools";// 1. 从网关获取所有工具的规范ResponseEntity<List<Map<String, Object>>> response = restTemplate.exchange(toolsUrl,HttpMethod.GET,null,new ParameterizedTypeReference<>() {});List<Map<String, Object>> tools = response.getBody();if (tools == null || tools.isEmpty()) {log.warn("警告:未从网关获取到任何工具用于索引。请检查 ai-adapter-gateway 是否正常运行并已发现工具。");return;}// 2. 将每个工具的描述转换为一个 "Document" 对象List<Document> documents = tools.stream().map(toolInfo -> {String toolName = (String) toolInfo.get("name");String description = (String) toolInfo.get("description");if (toolName != null && description != null) {// 文档内容是描述,元数据里存放工具名,这非常重要!return new Document(description, Collections.singletonMap("toolName", toolName));}return null;}).filter(Objects::nonNull).collect(Collectors.toList());// 3. 将这些文档添加到向量数据库中(内部会自动调用EmbeddingClient进行向量化)if (!documents.isEmpty()) {// 在生产环境中,可以考虑先清空旧的索引再添加,以处理工具的删除和更新vectorStore.add(documents);log.info("成功索引了 {} 个工具到向量数据库中。", documents.size());} else {log.warn("没有有效的工具被索引。");}} catch (Exception e) {log.error("错误:工具索引失败!请确保 ai-adapter-gateway 正在运行。错误信息: {}", e.getMessage());}log.info("--- [启动任务] 工具索引完成 ---");}
}
4.2 核心组件二:动态函数工厂 (GatewayToolFactory.java
)
职责:在应用启动时,同样从ai-adapter-gateway
获取工具列表,然后为每一个工具动态地在Spring容器中创建一个可执行的Function
Bean,为“执行阶段”做准备。
package com.example.assistant.config;import java.util.List;
import java.util.Map;
import java.util.function.Function;import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;import lombok.extern.slf4j.Slf4j;@Slf4j
@Component
public class GatewayToolFactory implements BeanFactoryPostProcessor {@Value("${gateway.base-url:http://localhost:8081}")private String gatewayBaseUrl;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {log.info("--- [启动任务] 动态函数工厂开始工作 ---");RestTemplate restTemplate = new RestTemplate();String toolsUrl = gatewayBaseUrl + "/ai/tools";try {// 1. 调用网关获取工具列表ResponseEntity<List<Map<String, Object>>> response = restTemplate.exchange(toolsUrl, HttpMethod.GET, null, new ParameterizedTypeReference<>() {});List<Map<String, Object>> tools = response.getBody();if (tools == null || tools.isEmpty()) {log.warn("未从网关获取到任何工具,无法创建动态函数。");return;}DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;// 2. 遍历工具列表,为每个工具动态创建并注册Beanfor (Map<String, Object> toolInfo : tools) {String toolName = (String) toolInfo.get("name");String description = (String) toolInfo.get("description");String beanName = toolName + "Function";// 3. 创建一个通用的Function,它知道如何调用网关// Spring AI 需要一个有明确泛型类型的Function。输入是Map<String, Object>,代表AI提供的参数Function<Object, Object> toolFunction = (input) -> {log.info("AI决定调用动态函数: {}, 参数: {}", toolName, input);return callGateway(toolName, (Map<String, Object>) input);};// 4. 创建Bean定义 (BeanDefinition)GenericBeanDefinition beanDefinition = new GenericBeanDefinition();beanDefinition.setBeanClass(Function.class); // 使用Function接口作为Bean类beanDefinition.setInstanceSupplier(() -> toolFunction); // 使用Supplier提供实例beanDefinition.setDescription(description); // 关键!将描述附加到Bean定义上// 5. 注册Beanfactory.registerBeanDefinition(beanName, beanDefinition);log.info("成功动态注册Bean: [名称: {}, 描述: {}]", beanName, description);}} catch (Exception e) {log.error("动态函数工厂工作失败!错误: {}", e.getMessage());}log.info("--- [启动任务] 动态函数工厂完成 ---");}// 这是一个通用的调用网关的私有方法private Object callGateway(String toolName, Map<String, Object> input) {RestTemplate restTemplate = new RestTemplate();String gatewayUrl = gatewayBaseUrl + "/ai/use-tool";HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);Map<String, Object> requestBody = Map.of("tool_name", toolName, "input", input);HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);try {ResponseEntity<Map> response = restTemplate.postForEntity(gatewayUrl, entity, Map.class);return response.getBody() != null ? response.getBody().get("output") : null;} catch (Exception e) {log.error("调用网关工具 '{}' 失败: {}", toolName, e.getMessage());// 返回一个错误信息给AI,让它知道调用失败了return Map.of("error", "调用工具失败", "details", e.getMessage());}}
}
4.3 核心组件三:AI路由器 (ToolRouter.java
)
职责:在运行时,根据用户问题,从向量数据库中快速、低成本地筛选出最相关的工具。
package com.example.assistant.service;import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;import java.util.List;
import java.util.stream.Collectors;import lombok.extern.slf4j.Slf4j;@Slf4j
@Service
public class ToolRouter {private final VectorStore vectorStore;public ToolRouter(VectorStore vectorStore) {this.vectorStore = vectorStore;}/*** 路由的核心方法:根据用户问题,筛选出最相关的Top K个工具的Bean名称。* @param userQuery 用户的问题* @param topK 需要筛选出的工具数量* @return 相关工具的Bean名称列表 (e.g., ["getOrderByIdFunction", "cancelOrderFunction"])*/public List<String> route(String userQuery, int topK) {log.info("--- [路由阶段] 开始,用户问题: '{}'", userQuery);// 使用向量搜索,查找最相似的 topK 个工具SearchRequest request = SearchRequest.query(userQuery).withTopK(topK);List<Document> similarDocuments = vectorStore.similaritySearch(request);if (similarDocuments.isEmpty()) {log.warn("路由决策:未找到任何相关工具。");return List.of();}List<String> relevantToolNames = similarDocuments.stream().map(doc -> {String toolName = (String) doc.getMetadata().get("toolName");// 转换为我们在工厂中定义的Bean的名称return toolName + "Function";}).collect(Collectors.toList());log.info("路由决策:筛选出的 Top {} 相关工具为: {}", topK, relevantToolNames);return relevantToolNames;}
}
五、 系统流程图
图1:系统启动与自动化索引流程(离线)
(使用高兼容性语法重制)
图2:用户请求处理流程(运行时)
六、 总结与价值
本“路由-执行”架构方案,在Nacos 3.0的强力支持下,为构建能处理复杂业务的企业级AI助手提供了坚实的基础,其核心价值在于:
- 极致的自动化,解放生产力:借助Nacos 3.0的能力,彻底解决了手动维护和注册海量工具的难题。当业务微服务新增API或版本更新时,其能力会自动同步到Nacos,再流转到适配网关,最后被智能助手索引和使用,整个过程无需任何人工干预。
- 高度的系统解耦与可靠性:Nacos作为权威的“企业能力目录”,使得AI系统与业务系统之间实现了完美的解耦。AI系统不关心业务微服务的网络拓扑和部署细节,只面向Nacos提供的服务契约。
- 极致的性能与成本效益:通过两阶段调度,避免了向昂贵的大模型发送大量无关信息,极大地降低了API调用成本和响应延迟。
- 无与伦比的扩展性:无论后台的业务系统有多么庞大和复杂,该架构都能从容应对,保证AI决策的高效和精准。
七、 未来发展方向
基于当前架构,我们可以向更高级的AI Agent形态演进:
- 多步工具链(Multi-step Chains):允许AI规划并连续调用多个工具来完成一个复杂任务(例如:先查询用户信息,再根据用户等级查询其可用的产品,最后为用户下单)。
- 自适应路由(Adaptive Routing):路由阶段本身可以变得更智能。例如,对于简单问题直接回答,对于中等问题使用向量搜索路由,对于极其复杂的问题,甚至可以调用一个专门的“规划AI”来设计执行步骤。
- 主动学习与工具优化:记录AI调用工具的成功率和用户反馈,反向优化工具的描述(Description),使其对AI更友好,甚至可以自动发现和建议新的组合工具。
- 与RAG(检索增强生成)的融合:将工具调用(Action)与知识库检索(Knowledge)相结合,打造既能“查询”又能“操作”的全能型AI助理,完美覆盖企业所有场景。