基于语义路由的智能服务分发——让 AI 决定调用哪个微服务 基于语义路由的智能服务分发——让 AI 决定调用哪个微服务一、传统路由的困境规则维护成本爆炸在典型的微服务架构中API 网关的路由规则通常是静态路径匹配或正则表达式匹配。例如/user/** - user-service /order/** - order-service /product/** - product-service当服务数量增长到几十甚至上百个时规则维护变得越来越困难。更棘手的是当用户意图与 API 路径之间存在语义鸿沟时静态路由就彻底失效了。例如用户问我的积分能兑换什么应该路由到points-service还是product-service帮我查一下上个月在华东区的销售额——应该调用哪个报表服务这正是语义路由要解决的问题通过大模型理解用户输入的语义意图动态决策调用哪个微服务。flowchart TD A[用户请求br/自然语言/HTTP] -- B[语义路由引擎] B -- C[文本向量化br/Embedding] C -- D[服务能力向量库br/Milvus/Chroma] D -- E[相似度匹配br/Top-K 候选服务] E -- F{置信度 阈值} F --|是| G[直接路由到目标服务] F --|否| H[LLM 语义推理br/二次确认] H -- I[确定目标服务] G -- J[执行服务调用] I -- J J -- K[返回结果] subgraph 服务能力注册中心 L1[user-service: 用户信息管理] L2[order-service: 订单查询与创建] L3[points-service: 积分查询与兑换] L4[report-service: 销售报表与数据分析] end D -- L1 D -- L2 D -- L3 D -- L4二、核心方案基于 Embedding 的语义路由2.1 服务能力描述首先我们需要为每个微服务创建一份能力描述Service Manifest包含服务名称、功能描述和示例查询import java.util.List; /** * 服务能力描述。 * 每个微服务注册时提交自己的语义描述用于路由匹配。 */ public class ServiceManifest { /** 服务名称 */ private String serviceName; /** 服务路由地址如 user-service */ private String routeTarget; /** 功能描述自然语言用于语义匹配 */ private String description; /** 示例用户查询帮助 Embedding 建立语义映射 */ private ListString exampleQueries; /** 优先级当多个服务匹配时按优先级排序 */ private int priority; public ServiceManifest() {} public ServiceManifest(String serviceName, String routeTarget, String description, ListString exampleQueries, int priority) { this.serviceName serviceName; this.routeTarget routeTarget; this.description description; this.exampleQueries exampleQueries; this.priority priority; } /** * 生成用于 Embedding 的文本表示。 * 将服务名称、描述和示例查询拼接为一段文本进行向量化。 */ public String toEmbeddingText() { StringBuilder sb new StringBuilder(); sb.append(服务: ).append(serviceName).append(\n); sb.append(功能: ).append(description).append(\n); if (exampleQueries ! null !exampleQueries.isEmpty()) { sb.append(示例: ); sb.append(String.join(; , exampleQueries)); } return sb.toString(); } // Getter 和 Setter public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName serviceName; } public String getRouteTarget() { return routeTarget; } public void setRouteTarget(String routeTarget) { this.routeTarget routeTarget; } public String getDescription() { return description; } public void setDescription(String description) { this.description description; } public ListString getExampleQueries() { return exampleQueries; } public void setExampleQueries(ListString exampleQueries) { this.exampleQueries exampleQueries; } public int getPriority() { return priority; } public void setPriority(int priority) { this.priority priority; } }2.2 语义路由引擎import java.util.*; import java.util.stream.Collectors; /** * 语义路由引擎。 * 通过 Embedding 相似度匹配 LLM 推理将用户请求路由到最合适的微服务。 */ public class SemanticRouter { /** 相似度阈值高于此值直接路由低于则交由 LLM 二次推理 */ private static final double CONFIDENCE_THRESHOLD 0.75; /** Top-K 候选服务数量 */ private static final int TOP_K 3; /** Embedding 服务用于文本向量化和相似度搜索 */ private final EmbeddingService embeddingService; /** LLM 客户端用于低置信度场景的语义推理 */ private final LlmClient llmClient; /** 已注册的服务清单 */ private final ListServiceManifest manifests; /** * param embeddingService 嵌入向量服务 * param llmClient LLM 客户端 * throws IllegalArgumentException 如果必需参数为 null */ public SemanticRouter(EmbeddingService embeddingService, LlmClient llmClient) { if (embeddingService null) { throw new IllegalArgumentException(EmbeddingService 不能为 null); } if (llmClient null) { throw new IllegalArgumentException(LlmClient 不能为 null); } this.embeddingService embeddingService; this.llmClient llmClient; this.manifests new ArrayList(); } /** * 注册服务清单。 * * param manifest 服务能力描述 */ public void registerService(ServiceManifest manifest) { if (manifest null) { throw new IllegalArgumentException(ServiceManifest 不能为 null); } manifests.add(manifest); // 将服务描述的 Embedding 存储到向量数据库中 embeddingService.upsert(manifest.getServiceName(), manifest.toEmbeddingText()); System.out.println(已注册服务: manifest.getServiceName()); } /** * 根据用户输入进行语义路由。 * * param userInput 用户输入可以是自然语言查询或 HTTP 请求 * return 路由决策结果 * throws RoutingException 如果路由失败 */ public RoutingResult route(String userInput) throws RoutingException { if (userInput null || userInput.isBlank()) { throw new IllegalArgumentException(用户输入不能为空); } if (manifests.isEmpty()) { throw new RoutingException(没有注册任何服务请先调用 registerService); } try { // 第一步Embedding 相似度搜索获取 Top-K 候选 ListScoredService candidates embeddingService.search(userInput, TOP_K); // 第二步判断置信度 if (!candidates.isEmpty()) { ScoredService top candidates.get(0); if (top.getScore() CONFIDENCE_THRESHOLD) { // 高置信度直接路由 return new RoutingResult(top.getServiceName(), top.getScore(), candidates, RoutingDecisionType.DIRECT_MATCH); } } // 第三步低置信度交由 LLM 进行语义推理 String llmDecision queryLlmForRouting(userInput, candidates); return parseLlmDecision(llmDecision, candidates); } catch (RoutingException e) { throw e; } catch (Exception e) { throw new RoutingException(语义路由失败: e.getMessage(), e); } } /** * 调用 LLM 进行语义推理路由。 * 当 Embedding 相似度不足以区分时使用。 */ private String queryLlmForRouting(String userInput, ListScoredService candidates) { StringBuilder prompt new StringBuilder(); prompt.append(你是一个 API 路由专家。根据用户输入判断应该调用以下哪个微服务。\n\n); prompt.append(## 用户输入\n); prompt.append(userInput).append(\n\n); prompt.append(## 候选服务\n); for (int i 0; i candidates.size(); i) { ScoredService candidate candidates.get(i); prompt.append(i 1).append(. ).append(candidate.getServiceName()) .append( (相似度: ).append(String.format(%.2f, candidate.getScore())) .append()\n); } prompt.append(\n请只返回最合适的服务名称如 user-service不要多余文字。); prompt.append(\n如果没有合适的服务返回 NONE。); try { return llmClient.chat(prompt.toString()).trim(); } catch (Exception e) { System.err.println(LLM 路由推理异常回退到 Embedding 结果: e.getMessage()); return candidates.isEmpty() ? NONE : candidates.get(0).getServiceName(); } } /** * 解析 LLM 返回的路由决策。 */ private RoutingResult parseLlmDecision(String llmResponse, ListScoredService candidates) { // 移除可能的多余字符如引号、标点 String serviceName llmResponse.replaceAll(\||。||\\s, ).trim(); if (NONE.equalsIgnoreCase(serviceName) || serviceName.isEmpty()) { return new RoutingResult(null, 0.0, candidates, RoutingDecisionType.NO_MATCH); } // 在候选列表中找到对应的 ServiceManifest for (ScoredService candidate : candidates) { if (candidate.getServiceName().equalsIgnoreCase(serviceName)) { return new RoutingResult(serviceName, candidate.getScore(), candidates, RoutingDecisionType.LLM_ASSISTED); } } // LLM 返回的服务不在候选列表中信任 LLM 直接返回 return new RoutingResult(serviceName, 0.5, candidates, RoutingDecisionType.LLM_DIRECT); } } /** 带评分的服务候选 */ class ScoredService { private String serviceName; private double score; public ScoredService(String serviceName, double score) { this.serviceName serviceName; this.score score; } public String getServiceName() { return serviceName; } public double getScore() { return score; } } /** 路由决策类型 */ enum RoutingDecisionType { /** 直接匹配高置信度 Embedding */ DIRECT_MATCH, /** LLM 辅助推理 */ LLM_ASSISTED, /** LLM 直接决策候选列表外 */ LLM_DIRECT, /** 无匹配 */ NO_MATCH } /** 路由结果 */ class RoutingResult { private String targetService; private double confidence; private ListScoredService candidates; private RoutingDecisionType decisionType; public RoutingResult(String targetService, double confidence, ListScoredService candidates, RoutingDecisionType decisionType) { this.targetService targetService; this.confidence confidence; this.candidates candidates; this.decisionType decisionType; } public String getTargetService() { return targetService; } public double getConfidence() { return confidence; } public ListScoredService getCandidates() { return candidates; } public RoutingDecisionType getDecisionType() { return decisionType; } public boolean isRouted() { return targetService ! null decisionType ! RoutingDecisionType.NO_MATCH; } }三、与 Spring Cloud Gateway 集成语义路由可以与 Spring Cloud Gateway 集成作为自定义过滤器在工作import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; /** * 语义路由过滤器——Spring Cloud Gateway 集成。 * 在网关层根据请求内容进行语义路由决策。 */ Component public class SemanticRoutingFilter extends AbstractGatewayFilterFactorySemanticRoutingFilter.Config { private final SemanticRouter semanticRouter; public SemanticRoutingFilter(SemanticRouter semanticRouter) { super(Config.class); this.semanticRouter semanticRouter; } Override public GatewayFilter apply(Config config) { return (ServerWebExchange exchange, GatewayFilterChain chain) - { // 提取请求中的查询文本可以从 query param 或 body 获取 String query exchange.getRequest().getQueryParams().getFirst(q); if (query null || query.isBlank()) { // 没有查询文本时直接透传走原有路由规则 return chain.filter(exchange); } try { // 调用语义路由引擎获取目标服务 RoutingResult result semanticRouter.route(query); if (result.isRouted()) { // 将路由目标写入请求属性供后续路由逻辑使用 exchange.getAttributes().put(semantic-route-target, result.getTargetService()); exchange.getAttributes().put(semantic-route-confidence, result.getConfidence()); exchange.getAttributes().put(semantic-route-decision-type, result.getDecisionType().name()); // 修改请求 URI替换为目标服务的路径 // 实际项目中会将 targetService 通过服务发现解析为具体地址 exchange.getRequest().mutate() .header(X-Semantic-Route, result.getTargetService()); } else { // 没有匹配到服务返回 404 exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND); return exchange.getResponse().setComplete(); } } catch (RoutingException e) { exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR); return exchange.getResponse().setComplete(); } return chain.filter(exchange); }; } public static class Config { // 可配置的过滤参数如路由优先级、降级规则等 } }四、关键挑战与应对策略4.1 冷启动问题新服务刚注册时Embedding 还未来得及索引查询可能无法匹配。解决方案预热机制服务注册时立即调用 Embedding API 进行索引。降级策略语义路由失败时回退到传统的 URL 路径匹配。4.2 延迟开销语义路由引入的额外延迟主要由 Embedding 查询和 LLM 推理产生Embedding 查询通常在 20-50ms使用 Milvus 等高性能向量数据库。LLM 推理200-2000ms取决于模型和网络延迟。优化策略设置缓存对历史查询的 Embedding 结果进行缓存TTL 可设为 1 小时。分层策略仅当 Embedding 置信度 阈值时才调用 LLM。异步预热预计算热门查询的 Embedding存入本地缓存。4.3 误路由的监测在生产环境中必须持续监测语义路由的准确性/** * 路由准确性监控。 */ public class RoutingMonitor { private final MapString, AtomicLong routeCount new ConcurrentHashMap(); private final MapString, AtomicLong misrouteCount new ConcurrentHashMap(); /** * 记录一次路由决策。 */ public void recordRoute(String targetService, String userInput, boolean correct) { routeCount.computeIfAbsent(targetService, k - new AtomicLong()).incrementAndGet(); if (!correct) { misrouteCount.computeIfAbsent(targetService, k - new AtomicLong()).incrementAndGet(); } } /** * 获取各服务的路由准确率。 */ public MapString, Double getAccuracy() { MapString, Double accuracy new LinkedHashMap(); for (String service : routeCount.keySet()) { long total routeCount.get(service).get(); long misrouted misrouteCount.getOrDefault(service, new AtomicLong()).get(); accuracy.put(service, 1.0 - (double) misrouted / total); } return accuracy; } }五、总结语义路由是 AI 与微服务治理结合的典型案例。它为传统基于规则的网关路由提供了智能层的补充而不是替代。在实际应用中建议采用分层路由策略L1 层静态路由处理明确的 RESTful API 路径如/user/{id}保持高性能。L2 层Embedding 语义路由处理自然语言查询或模糊意图提供毫秒级延迟。L3 层LLM 深度推理处理 L2 无法确定的复杂语义场景提供最高准确率但延迟较高。通过这三层结构的组合可以在性能、准确率和灵活性之间取得平衡。服务能力描述的演化与维护语义路由的准确率高度依赖 ServiceManifest 的质量。随着服务数量增长和功能演进Manifest 的描述漂移是必须解决的问题——描述信息与实际 API 能力不一致时路由准确率持续下降。我们建立了两个维护机制(1) Manifest 与服务代码放在同一 Git 仓库CI/CD 中增加 Manifest 一致性检查通过 LLM 对比 Manifest 描述与实际 API 接口定义偏差超过阈值时阻断构建(2) 基于路由准确率监控RoutingMonitor的自动告警——当某个服务的链路准确率连续 1 小时低于 85% 时自动向服务负责人发送 Manifest 更新提醒。与 RAG 的协同模式语义路由与 RAGRetrieval-Augmented Generation在架构上有天然的互补关系。RAG 解决信息从哪里拿语义路由解决请求发给哪个服务。两者结合可形成完整的智能请求分发链路用户请求 → 语义路由确定目标服务→ RAG 在该服务内部检索相关文档 → LLM 基于检索上下文生成回答。这个架构在我们的企业 AI 助手中已支撑日均百万级请求路由准确率 92.7%。生产落地的注意事项语义路由在生产环境的部署需关注三点(1) Embedding 向量数据库的高可用——Milvus 的集群模式比单节点模式更适合生产但其资源消耗单节点 8GB 内存需要在预算中考虑(2) LLM 调用的失败降级——当 LLM 推理超时2s时直接回退到 Embedding Top-1 结果牺牲少量准确率换取延迟稳定性(3) 路由决策的审计日志——每次语义路由决策包括决策类型、候选列表、置信度都应落盘用于问题回溯和模型迭代。作者程序员鸭梨李然Java 架构师专注 AI 后端架构与微服务治理实践。欢迎留言交流。