当前位置: 首页 > news >正文

【微服务】【Nacos 3】 ② 深度解析:AI模块介绍

📖目录

  • 前言
  • 1. Nacos AI 模块概述
  • 2. 核心组件详解
    • 2.1 MCP (Model Control Plane)
      • 2.1.1 核心功能
      • 2.1.2 关键类分析
        • McpServerOperationService
        • 索引机制
      • 2.1.3 控制器层
    • 2.2 A2A (Agent to Agent)
      • 2.2.1 核心功能
      • 2.2.2 关键类分析
        • A2aServerOperationService
        • 请求处理器
  • 3. 关键源码剖析
    • 3.1 模型服务注册流程
    • 3.2 代理通信处理流程
  • 4. 架构设计亮点
    • 4.1 分层架构设计
    • 4.2 缓存机制优化
    • 4.3 插件化设计
  • 5. 使用场景
    • 5.1 微服务AI治理
    • 5.2 多模型版本管理
    • 5.3 跨域代理通信
  • 6. 未来发展趋势
    • 6.1 AI服务网格集成
    • 6.2 智能负载均衡
    • 6.3 自动扩缩容
  • 7. 结语与学习建议
      • 7.1. 动手实践(快速搭建本地环境)
      • 7.2. 调试技巧
      • 7.3. 延伸阅读
  • 📝 版权声明


前言

📌 文章说明:本文基于 Nacos 3.x 最新版本(截至 2025 年),深入介绍 Nacos AI 模块的设计理念、核心组件以及关键功能,帮助读者了解如何利用 Nacos 管理 AI 模型服务。适合对 AI 服务治理感兴趣的中高级 Java 后端开发者和架构师阅读。

🔖 关键词:#Nacos #AI服务治理 #MCP #A2A #模型管理 #Nacos3.x #源码解析

1. Nacos AI 模块概述

Nacos AI 模块是 Nacos 3.x 中新增的一个重要功能模块,旨在为 AI 模型服务提供统一的服务发现、配置管理和治理能力。该模块主要包括两个子系统:

  • MCP (Model Control Plane):模型控制平面,用于管理 AI 模型的注册、发现和配置
  • A2A (Agent to Agent):代理间通信系统,支持 AI 代理之间的通信和协调

首先需要纠正一个认知:Nacos 3.x 目前没有独立的 “AI 模块”,而是将 AI 相关能力(如服务健康度 AI 预测、配置异常 AI 诊断等)嵌入到核心模块中(如服务发现模块 nacos-naming、配置模块 nacos-config),通过依赖轻量级 AI 工具包或算法实现,而非单独拆分模块。
这也是为什么你编译后看不到 “AI 字眼” 的核心原因 - 能力已分散在现有模块中,而非集中在独立目录。

2. 核心组件详解

2.1 MCP (Model Control Plane)

MCP 是 AI 模块的核心组件,负责 AI 模型服务的全生命周期管理。

2.1.1 核心功能

  • 模型服务注册与发现
  • 模型版本管理
  • 工具规范定义
  • 端点配置管理
  • 元数据存储与查询

2.1.2 关键类分析

McpServerOperationService

这是 MCP 模块的核心服务类,负责模型服务的操作管理:

// McpServerOperationService 提供了模型服务的关键操作实现,包含模型服务的注册、查询、更新和删除等方法
public class McpServerOperationService {// 创建新的MCP服务器public String createMcpServer(String namespaceId, McpServerBasicInfo serverSpecification,McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException {// 解析存在的MCP服务器IDString existId = resolveMcpServerId(namespaceId, serverSpecification.getName(), StringUtils.EMPTY);// 如果存在ID不为空if (StringUtils.isNotEmpty(existId)) {// 抛出资源冲突异常throw new NacosApiException(NacosApiException.CONFLICT, ErrorCode.RESOURCE_CONFLICT,String.format("mcp server `%s` has existed, please update it rather than create.",serverSpecification.getName()));}// 获取版本详情ServerVersionDetail versionDetail = serverSpecification.getVersionDetail();// 如果版本详情为空且版本不为空if (null == versionDetail && StringUtils.isNotBlank(serverSpecification.getVersion())) {// 创建版本详情对象versionDetail = new ServerVersionDetail();// 设置版本versionDetail.setVersion(serverSpecification.getVersion());// 设置版本详情serverSpecification.setVersionDetail(versionDetail);}// 如果版本详情为空或版本为空if (Objects.isNull(versionDetail) || StringUtils.isEmpty(versionDetail.getVersion())) {// 抛出参数验证异常throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR,"Version must be specified in parameter [serverSpecification](file:///Users/zhiyixie/Downloads/work_space/码云/Nacos/ai/src/main/java/com/alibaba/nacos/ai/form/mcp/admin/McpDetailForm.java#L35-L35)");}// 声明IDString id;// 获取自定义MCP IDString customMcpId = serverSpecification.getId();// 如果自定义MCP ID为空if (StringUtils.isEmpty(customMcpId)) {// 生成随机UUID作为IDid = UUID.randomUUID().toString();} else {// 如果自定义MCP ID不是UUID字符串if (!StringUtils.isUuidString(customMcpId)) {// 抛出参数验证异常throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR,"parameter `serverSpecification.id` is not match uuid pattern,  must obey uuid pattern");}// 如果通过ID获取的MCP服务器不为空if (mcpServerIndex.getMcpServerById(serverSpecification.getId()) != null) {// 抛出参数验证异常throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR,"parameter `serverSpecification.id` conflict with exist mcp server id");}// 设置ID为自定义MCP IDid = customMcpId;}// 设置服务器规范的IDserverSpecification.setId(id);// 获取当前UTC时间ZonedDateTime currentTime = ZonedDateTime.now(ZoneOffset.UTC);// 创建日期时间格式化器DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Constants.RELEASE_DATE_FORMAT);// 格式化当前时间String formattedCurrentTime = currentTime.format(formatter);// 设置版本详情的发布日期versionDetail.setRelease_date(formattedCurrentTime);// 创建新的服务器规范对象McpServerStorageInfo newSpecification = new McpServerStorageInfo();// 复制属性BeanUtils.copyProperties(serverSpecification, newSpecification);// 注入工具和端点injectToolAndEndpoint(namespaceId, serverSpecification.getId(), newSpecification, toolSpecification,endpointSpecification, Boolean.FALSE);// 构建服务器版本信息McpServerVersionInfo versionInfo = buildServerVersionInfo(newSpecification, id, versionDetail);// 创建配置请求信息ConfigRequestInfo configRequestInfo = new ConfigRequestInfo();// 设置不更新已存在的配置configRequestInfo.setUpdateForExist(Boolean.FALSE);// 构建MCP服务器版本表单ConfigFormV3 mcpServerVersionForm = buildMcpServerVersionForm(namespaceId, versionInfo);// 发布配置configOperationService.publishConfig(mcpServerVersionForm, configRequestInfo, null);// 构建MCP配置表单ConfigForm configForm = buildMcpConfigForm(namespaceId, id, versionDetail.getVersion(), newSpecification);// 记录操作开始时间long startOperationTime = System.currentTimeMillis();// 发布配置configOperationService.publishConfig(configForm, configRequestInfo, null);// 同步效果服务syncEffectService.toSync(configForm, startOperationTime);// 在成功的数据库操作后删除相关缓存invalidateCacheAfterDbOperation(namespaceId, serverSpecification.getName(), id);// 返回IDreturn id;}// 更新已存在的MCP服务器public void updateMcpServer(String namespaceId, boolean isPublish, McpServerBasicInfo serverSpecification,McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification, boolean overrideExisting) throws NacosException {// 获取MCP服务器IDString mcpServerId = serverSpecification.getId();// 解析MCP服务器IDmcpServerId = resolveMcpServerId(namespaceId, serverSpecification.getName(), mcpServerId);// 如果服务器规范的ID为空if (StringUtils.isEmpty(serverSpecification.getId())) {// 设置服务器规范的IDserverSpecification.setId(mcpServerId);}// 获取版本详情ServerVersionDetail versionDetail = serverSpecification.getVersionDetail();// 如果版本详情为空且版本不为空if (null == versionDetail && StringUtils.isNotBlank(serverSpecification.getVersion())) {// 创建版本详情对象versionDetail = new ServerVersionDetail();// 设置版本versionDetail.setVersion(serverSpecification.getVersion());// 设置版本详情serverSpecification.setVersionDetail(versionDetail);}// 如果版本详情为空或版本为空if (Objects.isNull(versionDetail) || StringUtils.isEmpty(versionDetail.getVersion())) {// 抛出参数验证异常throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR,"Version must be specified in parameter [serverSpecification](file:///Users/zhiyixie/Downloads/work_space/码云/Nacos/ai/src/main/java/com/alibaba/nacos/ai/form/mcp/admin/McpDetailForm.java#L35-L35)");}// 获取MCP服务器版本信息final McpServerVersionInfo mcpServerVersionInfo = getMcpServerVersionInfo(namespaceId, mcpServerId);// 获取更新版本String updateVersion = versionDetail.getVersion();// 创建新的服务器规范对象McpServerStorageInfo newSpecification = new McpServerStorageInfo();// 复制属性BeanUtils.copyProperties(serverSpecification, newSpecification);// 注入工具和端点injectToolAndEndpoint(namespaceId, mcpServerId, newSpecification, toolSpecification, endpointSpecification, overrideExisting);// 构建MCP配置表单ConfigForm configForm = buildMcpConfigForm(namespaceId, mcpServerId, updateVersion, newSpecification);// 发布配置configOperationService.publishConfig(configForm, new ConfigRequestInfo(), null);// 获取版本详情列表List<ServerVersionDetail> versionDetails = mcpServerVersionInfo.getVersionDetails();// 创建版本集合Set<String> versionSet = versionDetails.stream().map(ServerVersionDetail::getVersion).collect(Collectors.toSet());// 如果版本集合不包含更新版本if (!versionSet.contains(updateVersion)) {// 创建版本对象ServerVersionDetail version = new ServerVersionDetail();// 设置版本version.setVersion(updateVersion);// 将版本添加到版本详情列表中versionDetails.add(version);// 设置版本列表mcpServerVersionInfo.setVersions(versionDetails);}// 如果是发布操作if (isPublish) {// 设置名称mcpServerVersionInfo.setName(newSpecification.getName());// 设置描述mcpServerVersionInfo.setDescription(newSpecification.getDescription());// 设置仓库mcpServerVersionInfo.setRepository(newSpecification.getRepository());// 设置协议mcpServerVersionInfo.setProtocol(newSpecification.getProtocol());// 设置前端协议mcpServerVersionInfo.setFrontProtocol(newSpecification.getFrontProtocol());// 设置能力mcpServerVersionInfo.setCapabilities(newSpecification.getCapabilities());// 设置最新发布版本mcpServerVersionInfo.setLatestPublishedVersion(updateVersion);// 遍历版本详情列表for (ServerVersionDetail detail : versionDetails) {// 如果版本详情的版本等于更新版本if (detail.getVersion().equals(updateVersion)) {// 获取当前UTC时间ZonedDateTime currentTime = ZonedDateTime.now(ZoneOffset.UTC);// 创建日期时间格式化器DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");// 格式化当前时间String formattedCurrentTime = currentTime.format(formatter);// 设置发布日期detail.setRelease_date(formattedCurrentTime);// 设置为最新版本detail.setIs_latest(true);// 跳出循环break;} else {// 设置为非最新版本detail.setIs_latest(false);}}// 设置版本列表mcpServerVersionInfo.setVersions(versionDetails);// 设置是否启用mcpServerVersionInfo.setEnabled(newSpecification.isEnabled());}// 构建MCP服务器版本表单ConfigFormV3 mcpServerVersionForm = buildMcpServerVersionForm(namespaceId, mcpServerVersionInfo);// 记录操作开始时间long startOperationTime = System.currentTimeMillis();// 发布配置configOperationService.publishConfig(mcpServerVersionForm, new ConfigRequestInfo(), null);// 同步效果服务syncEffectService.toSync(mcpServerVersionForm, startOperationTime);// 在成功的数据库操作后删除相关缓存invalidateCacheAfterDbUpdateOperation(namespaceId, mcpServerVersionInfo.getName(),serverSpecification.getName(), mcpServerId);}// 删除已存在的MCP服务器public void deleteMcpServer(String namespaceId, String mcpName, String mcpServerId, String version)throws NacosException {// 解析MCP服务器IDmcpServerId = resolveMcpServerId(namespaceId, mcpName, mcpServerId);// 获取MCP服务器版本信息McpServerVersionInfo mcpServerVersionInfo = getMcpServerVersionInfo(namespaceId, mcpServerId);// 创建需要删除的版本列表List<String> versionsNeedDelete = new ArrayList<>();// 如果版本不为空if (StringUtils.isNotEmpty(version)) {// 将版本添加到需要删除的版本列表中versionsNeedDelete.add(version);} else {// 将版本详情列表中的版本收集到需要删除的版本列表中versionsNeedDelete = mcpServerVersionInfo.getVersionDetails().stream().map(ServerVersionDetail::getVersion).collect(Collectors.toList());}// 遍历需要删除的版本列表for (String versionNeedDelete : versionsNeedDelete) {// 删除MCP工具toolOperationService.deleteMcpTool(namespaceId, mcpServerId, versionNeedDelete);// 删除MCP服务器端点服务endpointOperationService.deleteMcpServerEndpointService(namespaceId, mcpServerVersionInfo.getName() + "::" + versionNeedDelete);// 格式化服务器规范信息数据IDString serverSpecDataId = McpConfigUtils.formatServerSpecInfoDataId(mcpServerId, versionNeedDelete);// 删除配置configOperationService.deleteConfig(serverSpecDataId, Constants.MCP_SERVER_GROUP, namespaceId, null, null,"nacos", null);// 格式化服务器版本信息数据IDString serverVersionDataId = McpConfigUtils.formatServerVersionInfoDataId(mcpServerId);// 删除配置configOperationService.deleteConfig(serverVersionDataId, Constants.MCP_SERVER_VERSIONS_GROUP, namespaceId,null, null, "nacos", null);}// 在成功的数据库操作后删除相关缓存invalidateCacheAfterDbOperation(namespaceId, mcpName, mcpServerId);}// 获取指定MCP服务器详细信息public McpServerDetailInfo getMcpServerDetail(String namespaceId, String mcpServerId, String mcpServerName,String version) throws NacosException {// 解析MCP服务器IDmcpServerId = resolveMcpServerId(namespaceId, mcpServerName, mcpServerId);// 获取MCP服务器版本信息McpServerVersionInfo mcpServerVersionInfo = getMcpServerVersionInfo(namespaceId, mcpServerId);// 如果版本为空if (StringUtils.isEmpty(version)) {// 获取版本详情列表大小int size = mcpServerVersionInfo.getVersionDetails().size();// 获取最后一个版本详情ServerVersionDetail last = mcpServerVersionInfo.getVersionDetails().get(size - 1);// 设置版本为最后一个版本version = last.getVersion();}// 构建查询MCP服务器请求ConfigQueryChainRequest request = buildQueryMcpServerRequest(namespaceId, mcpServerId, version);// 处理请求获取响应ConfigQueryChainResponse response = configQueryChainService.handle(request);// 如果配置未找到if (McpConfigUtils.isConfigNotFound(response.getStatus())) {// 抛出未找到异常throw new NacosApiException(NacosApiException.NOT_FOUND, ErrorCode.MCP_SEVER_VERSION_NOT_FOUND,String.format("mcp server `%s` for version `%s` not found", mcpServerId, version));}// 将响应内容转换为MCP服务器存储信息McpServerStorageInfo serverSpecification = JacksonUtils.toObj(response.getContent(),McpServerStorageInfo.class);// 创建结果对象McpServerDetailInfo result = new McpServerDetailInfo();// 设置IDresult.setId(mcpServerId);// 设置命名空间IDresult.setNamespaceId(namespaceId);// 复制属性BeanUtils.copyProperties(serverSpecification, result);// 获取版本详情列表List<ServerVersionDetail> versionDetails = mcpServerVersionInfo.getVersionDetails();// 获取最新发布版本String latestVersion = mcpServerVersionInfo.getLatestPublishedVersion();// 遍历版本详情列表for (ServerVersionDetail versionDetail : versionDetails) {// 设置是否为最新版本versionDetail.setIs_latest(versionDetail.getVersion().equals(latestVersion));}// 设置所有版本result.setAllVersions(mcpServerVersionInfo.getVersionDetails());// 获取版本详情ServerVersionDetail versionDetail = result.getVersionDetail();// 设置是否为最新版本versionDetail.setIs_latest(versionDetail.getVersion().equals(latestVersion));// 设置版本result.setVersion(versionDetail.getVersion());// 如果工具描述引用不为空if (Objects.nonNull(serverSpecification.getToolsDescriptionRef())) {// 获取MCP工具McpToolSpecification toolSpec = toolOperationService.getMcpTool(namespaceId,serverSpecification.getToolsDescriptionRef());// 设置工具规范result.setToolSpec(toolSpec);}// 如果协议不是STDIOif (!AiConstants.Mcp.MCP_PROTOCOL_STDIO.equalsIgnoreCase(serverSpecification.getProtocol())) {// 注入端点injectEndpoint(result);}// 返回结果return result;}}
索引机制

为了提高查询性能,AI 模块实现了多种索引机制:

  • PlainMcpServerIndex: 基础索引实现
  • CachedMcpServerIndex: 带缓存的索引实现
  • MemoryMcpCacheIndex: 内存缓存索引实现

我们这里选择带缓冲的索引实现来查看:

// CachedMcpServerIndex 结合内存缓存和数据库查询,提升查询性能
public class CachedMcpServerIndex extends AbstractMcpServerIndex {private final McpCacheIndex cacheIndex;/*** 根据ID获取MCP服务器信息*/@Override// 重写父类方法,根据ID获取MCP服务器信息public McpServerIndexData getMcpServerById(String id) {// 检查缓存是否启用if (!cacheEnabled) {// 缓存未启用,直接从数据库查询LOGGER.debug("缓存已禁用,直接从数据库查询mcpId: {}", id);// 从数据库获取MCP服务器信息return getMcpServerByIdFromDatabase(id);}// 优先查询缓存// 从缓存中获取MCP服务器数据McpServerIndexData cachedData = cacheIndex.getMcpServerById(id);// 检查缓存中是否存在数据if (cachedData != null) {// 缓存命中,返回缓存数据LOGGER.debug("缓存命中 mcpId: {}", id);// 返回缓存中的数据return cachedData;}// 缓存未命中,查询数据库// 缓存未命中,从数据库查询LOGGER.debug("缓存未命中 mcpId: {}, 查询数据库", id);// 从数据库获取MCP服务器信息McpServerIndexData dbData = getMcpServerByIdFromDatabase(id);// 检查数据库中是否存在数据if (dbData != null) {// 数据库中存在数据,更新缓存cacheIndex.updateIndex(dbData.getNamespaceId(), dbData.getId(), dbData.getId());// 记录缓存更新日志LOGGER.debug("更新缓存 mcpId: {}", id);}// 返回数据库查询结果return dbData;}/*** 根据名称获取MCP服务器信息*/@Override// 重写父类方法,根据命名空间ID和名称获取MCP服务器信息public McpServerIndexData getMcpServerByName(String namespaceId, String name) {// 检查参数是否有效if (StringUtils.isEmpty(namespaceId) && StringUtils.isEmpty(name)) {// 参数无效,记录警告日志LOGGER.warn("getMcpServerByName的参数无效: namespaceId={}, name={}", namespaceId, name);// 返回nullreturn null;}// 检查命名空间ID是否为空if (StringUtils.isEmpty(namespaceId)) {// 命名空间ID为空,获取第一个匹配名称的MCP服务器return getFirstMcpServerByName(name);}// 检查缓存是否启用if (!cacheEnabled) {// 缓存未启用,直接从数据库查询LOGGER.debug("缓存已禁用,直接从数据库查询名称: {}:{}", namespaceId, name);// 从数据库获取MCP服务器信息return getMcpServerByNameFromDatabase(namespaceId, name);}// 优先查询缓存// 从缓存中获取MCP服务器数据McpServerIndexData cachedData = cacheIndex.getMcpServerByName(namespaceId, name);// 检查缓存中是否存在数据if (cachedData != null) {// 缓存命中,返回缓存数据LOGGER.debug("缓存命中名称: {}:{}", namespaceId, name);// 返回缓存中的数据return cachedData;}// 缓存未命中,查询数据库// 缓存未命中,从数据库查询LOGGER.debug("缓存未命中名称: {}:{}, 查询数据库", namespaceId, name);// 从数据库获取MCP服务器信息McpServerIndexData dbData = getMcpServerByNameFromDatabase(namespaceId, name);// 检查数据库中是否存在数据if (dbData != null) {// 数据库中存在数据,更新缓存cacheIndex.updateIndex(namespaceId, name, dbData.getId());// 记录缓存更新日志LOGGER.debug("更新缓存名称: {}:{}", namespaceId, name);}// 返回数据库查询结果return dbData;}@Override// 重写父类方法,在搜索完成后更新缓存protected void afterSearch(List<McpServerIndexData> indexDataList, String name) {// 更新缓存// 检查缓存是否启用if (cacheEnabled) {// 遍历搜索结果列表for (McpServerIndexData indexData : indexDataList) {// 更新缓存索引cacheIndex.updateIndex(indexData.getNamespaceId(), name, indexData.getId());}// 记录缓存更新日志LOGGER.debug("从搜索结果中更新了{}个缓存条目", indexDataList.size());}}// 私有方法,启动定时同步任务private void startSyncTask() {// 创建定时任务,固定延迟执行syncTask = scheduledExecutor.scheduleWithFixedDelay(() -> {try {// 记录开始缓存同步任务的日志LOGGER.debug("开始缓存同步任务");// 同步数据库中的缓存syncCacheFromDatabase();// 记录缓存同步任务完成的日志LOGGER.debug("缓存同步任务完成");} catch (Exception e) {// 记录缓存同步任务错误的日志LOGGER.error("缓存同步任务期间发生错误", e);}}, syncInterval, syncInterval, TimeUnit.SECONDS);// 记录缓存同步任务启动的日志LOGGER.info("缓存同步任务已启动,间隔: {}秒", syncInterval);}// 私有方法,从数据库同步缓存private void syncCacheFromDatabase() {// 记录开始从数据库同步缓存的日志LOGGER.debug("从数据库同步缓存");// 获取有序的命名空间列表List<String> namespaceList = fetchOrderedNamespaceList();// 遍历命名空间列表for (String namespaceId : namespaceList) {try {// 按页面搜索MCP服务器名称,使用模糊搜索searchMcpServerByNameWithPage(namespaceId, null,Constants.MCP_LIST_SEARCH_BLUR, 1, 1000);} catch (Exception e) {// 记录命名空间缓存同步错误的日志LOGGER.error("命名空间缓存同步错误: {}", namespaceId, e);}}}@Override// 重写接口方法,按页面搜索MCP服务器public Page<McpServerIndexData> searchMcpServerByNameWithPage(String namespaceId, String name, String search,int pageNo, int limit) {// 调用searchMcpServers方法搜索配置信息Page<ConfigInfo> serverInfos = searchMcpServers(namespaceId, name, search, pageNo, limit);// 将ConfigInfo列表映射为McpServerIndexData列表List<McpServerIndexData> indexDataList = serverInfos.getPageItems().stream().map(this::mapMcpServerVersionConfigToIndexData).toList();// 创建结果页面对象Page<McpServerIndexData> result = new Page<>();// 设置页面条目result.setPageItems(indexDataList);// 设置总条目数result.setTotalCount(serverInfos.getTotalCount());// 计算并设置可用页面数result.setPagesAvailable((int) Math.ceil((double) serverInfos.getTotalCount() / (double) limit));// 设置当前页码result.setPageNumber(pageNo);// 调用afterSearch回调方法,用于子类处理搜索后的逻辑afterSearch(indexDataList, name);// 返回结果return result;}}

2.1.3 控制器层

McpAdminController是 MCP 模块的管理控制器,提供 RESTful API 接口:

// 提供模型服务的增删改查接口
@RestController
@RequestMapping(Constants.MCP_ADMIN_PATH)
public class McpAdminController {private final McpServerOperationService mcpServerOperationService;/*** 列出MCP服务器列表** @param mcpListForm 列出MCP服务器的请求表单* @param pageForm 请求的分页信息* @return 包含MCP服务器列表的{@link Result}结果* @throws NacosApiException 如果请求参数无效或处理出错*/// GET请求映射到/list路径@GetMapping(value = "/list")// 安全注解,指定需要读权限,签名类型为AI,API类型为管理API@Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API)// 列出MCP服务器的方法public Result<Page<McpServerBasicInfo>> listMcpServers(McpListForm mcpListForm, PageForm pageForm)throws NacosException {// 验证MCP列表表单参数mcpListForm.validate();// 验证分页表单参数pageForm.validate();// 返回成功结果,包含分页的MCP服务器列表return Result.success(mcpServerOperationService.listMcpServerWithPage(mcpListForm.getNamespaceId(), mcpListForm.getMcpName(), mcpListForm.getSearch(),pageForm.getPageNo(), pageForm.getPageSize()));}/*** 获取指定MCP服务器的详细信息** @param mcpForm 获取MCP服务器的请求表单* @return 包含详细信息的{@link McpServerDetailInfo}* @throws NacosException 处理过程中任何异常*/// GET请求映射到根路径@GetMapping// 安全注解,指定需要读权限,签名类型为AI,API类型为管理API@Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API)// 获取MCP服务器的方法public Result<McpServerDetailInfo> getMcpServer(McpForm mcpForm) throws NacosException {// 验证MCP表单参数mcpForm.validate();// 返回成功结果,包含MCP服务器详细信息return Result.success(mcpServerOperationService.getMcpServerDetail(mcpForm.getNamespaceId(), mcpForm.getMcpId(), mcpForm.getMcpName(), mcpForm.getVersion()));}/*** 创建新的MCP服务器** @param mcpForm 创建MCP服务器的请求表单* @throws NacosException 处理过程中任何异常*/// POST请求映射到根路径@PostMapping// 安全注解,指定需要写权限,签名类型为AI,API类型为管理API@Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API)// 创建MCP服务器的方法public Result<String> createMcpServer(McpDetailForm mcpForm) throws NacosException {// 验证MCP详细表单参数mcpForm.validate();// 解析MCP服务器基本信息McpServerBasicInfo basicInfo = McpRequestUtil.parseMcpServerBasicInfo(mcpForm);// 解析MCP工具规范McpToolSpecification mcpTools = McpRequestUtil.parseMcpTools(mcpForm);// 解析MCP端点规范McpEndpointSpec endpointSpec = McpRequestUtil.parseMcpEndpointSpec(basicInfo, mcpForm);// 调用服务创建MCP服务器,获取MCP服务器IDString mcpId = mcpServerOperationService.createMcpServer(mcpForm.getNamespaceId(), basicInfo, mcpTools,endpointSpec);// 返回成功结果,包含MCP服务器IDreturn Result.success(mcpId);}/*** 更新已存在的MCP服务器** @param mcpForm 更新MCP服务器的请求表单* @throws NacosException 处理过程中任何异常*/// PUT请求映射到根路径@PutMapping// 安全注解,指定需要写权限,签名类型为AI,API类型为管理API@Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API)// 更新MCP服务器的方法public Result<String> updateMcpServer(McpUpdateForm mcpForm) throws NacosException {// 验证MCP更新表单参数mcpForm.validate();// 解析MCP服务器基本信息McpServerBasicInfo basicInfo = McpRequestUtil.parseMcpServerBasicInfo(mcpForm);// 解析MCP工具规范McpToolSpecification mcpTools = McpRequestUtil.parseMcpTools(mcpForm);// 解析MCP端点规范McpEndpointSpec endpointSpec = McpRequestUtil.parseMcpEndpointSpec(basicInfo, mcpForm);// 调用服务更新MCP服务器mcpServerOperationService.updateMcpServer(mcpForm.getNamespaceId(), mcpForm.getLatest(), basicInfo, mcpTools,endpointSpec, mcpForm.isOverrideExisting());// 返回成功结果return Result.success("ok");}/*** 删除已存在的MCP服务器** @param mcpForm 删除MCP服务器的请求表单* @throws NacosException 处理过程中任何异常*/// DELETE请求映射到根路径@DeleteMapping// 安全注解,指定需要写权限,签名类型为AI,API类型为管理API@Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API)// 删除MCP服务器的方法public Result<String> deleteMcpServer(McpForm mcpForm) throws NacosException {// 验证MCP表单参数mcpForm.validate();// 调用服务删除MCP服务器mcpServerOperationService.deleteMcpServer(mcpForm.getNamespaceId(), mcpForm.getMcpName(), mcpForm.getMcpId(), mcpForm.getVersion());// 返回成功结果return Result.success("ok");}
}

2.2 A2A (Agent to Agent)

A2A 模块实现了 AI 代理间的通信机制,支持代理的注册、发现和通信。

2.2.1 核心功能

  • 代理卡片管理
  • 代理身份编码
  • 端点请求处理
  • 代理间通信

2.2.2 关键类分析

A2aServerOperationService

这是 A2A 模块的核心服务类,负责代理服务的操作管理:

// 提供代理服务的注册、查询、更新和删除等方法
@Service
public class A2aServerOperationService {// 实现注册Agent的方法public void registerAgent(AgentCard agentCard, String namespaceId, String registrationType) throws NacosException {try {// 1. 注册Agent的基本信息// 构建Agent卡片版本信息AgentCardVersionInfo agentCardVersionInfo = AgentCardUtil.buildAgentCardVersionInfo(agentCard,registrationType, true);// 转换为配置表单ConfigForm configForm = transferVersionInfoToConfigForm(agentCardVersionInfo, namespaceId);// 创建配置请求信息,设置不允许更新已存在的配置ConfigRequestInfo versionConfigRequest = new ConfigRequestInfo();versionConfigRequest.setUpdateForExist(Boolean.FALSE);// 发布配置configOperationService.publishConfig(configForm, versionConfigRequest, null);// 2. 注册Agent的版本信息// 构建Agent卡片详细信息AgentCardDetailInfo agentCardDetailInfo = AgentCardUtil.buildAgentCardDetailInfo(agentCard,registrationType);// 转换为版本配置表单ConfigForm configFormVersion = transferAgentInfoToConfigForm(agentCardDetailInfo, namespaceId);// 创建Agent卡片配置请求信息,设置不允许更新已存在的配置ConfigRequestInfo agentCardConfigRequest = new ConfigRequestInfo();agentCardConfigRequest.setUpdateForExist(Boolean.FALSE);// 记录操作开始时间long startOperationTime = System.currentTimeMillis();// 发布版本配置configOperationService.publishConfig(configFormVersion, agentCardConfigRequest, null);// 同步效果服务syncEffectService.toSync(configFormVersion, startOperationTime);} catch (ConfigAlreadyExistsException e) {// 如果配置已存在,抛出资源冲突异常throw new NacosApiException(NacosException.CONFLICT, ErrorCode.RESOURCE_CONFLICT,String.format("AgentCard name %s already exist", agentCard.getName()));}}// 实现删除Agent的方法public void deleteAgent(String namespaceId, String agentName, String version) throws NacosException {// 对Agent名称进行编码String encodedName = agentIdCodecHolder.encode(agentName);// 构建配置查询请求ConfigQueryChainRequest request = ConfigQueryChainRequest.buildConfigQueryChainRequest(encodedName, AGENT_GROUP,namespaceId);// 处理查询请求ConfigQueryChainResponse response = configQueryChainService.handle(request);// 如果配置未找到,直接返回if (response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND) {return;}// 将响应内容转换为Agent卡片版本信息对象AgentCardVersionInfo agentCardVersionInfo = JacksonUtils.toObj(response.getContent(),AgentCardVersionInfo.class);// 获取所有版本列表List<String> allVersions = agentCardVersionInfo.getVersionDetails().stream().map(AgentVersionDetail::getVersion).toList();// 1. 如果指定了版本,只删除对应版本的Agentif (StringUtils.isNotEmpty(version)) {// 构造版本数据IDString versionDataId = encodedName + "-" + version;// 删除指定版本的配置configOperationService.deleteConfig(versionDataId, AGENT_VERSION_GROUP, namespaceId, null, null, "nacos",null);// 获取版本详情列表List<AgentVersionDetail> versionDetails = agentCardVersionInfo.getVersionDetails();// 判断是否为最新版本boolean isLatestVersion = version.equals(agentCardVersionInfo.getLatestPublishedVersion());// 如果只有一个版本且就是要删除的版本,则删除整个Agent信息if (versionDetails.size() == 1 && versionDetails.get(0).getVersion().equals(version)) {configOperationService.deleteConfig(encodedName, AGENT_GROUP, namespaceId, null, null, "nacos", null);} else {// 从版本详情列表中移除指定版本agentCardVersionInfo.getVersionDetails().removeIf(versionDetail -> versionDetail.getVersion().equals(version));// 如果删除的是最新版本,则清空最新版本信息if (isLatestVersion) {agentCardVersionInfo.setLatestPublishedVersion(null);agentCardVersionInfo.setVersion(null);}// 更新配置表单ConfigForm updateForm = transferVersionInfoToConfigForm(agentCardVersionInfo, namespaceId);// 创建配置请求信息,设置允许更新已存在的配置ConfigRequestInfo configRequestInfo = new ConfigRequestInfo();configRequestInfo.setUpdateForExist(Boolean.TRUE);// 发布更新后的配置configOperationService.publishConfig(updateForm, configRequestInfo, null);}} else {// 2. 如果未指定版本,删除所有版本和Agent信息// 遍历所有版本并删除for (String each : allVersions) {String versionDataId = encodedName + "-" + each;configOperationService.deleteConfig(versionDataId, AGENT_VERSION_GROUP, namespaceId, null, null,"nacos", null);}// 删除Agent基本信息configOperationService.deleteConfig(encodedName, AGENT_GROUP, namespaceId, null, null, "nacos", null);}}// 实现更新Agent卡片的方法public void updateAgentCard(AgentCard agentCard, String namespaceId, String registrationType, boolean setAsLatest)throws NacosException {// 查询现有的Agent卡片版本信息final AgentCardVersionInfo existingAgentInfo = queryAgentCardVersionInfo(namespaceId, agentCard.getName());// 检查版本是否存在,如果不存在,则添加新版本到版本信息中boolean versionExisted = existingAgentInfo.getVersionDetails().stream().anyMatch(agentVersionDetail -> StringUtils.equals(agentVersionDetail.getVersion(), agentCard.getVersion()));if (!versionExisted) {existingAgentInfo.getVersionDetails().add(AgentCardUtil.buildAgentVersionDetail(agentCard, setAsLatest));}// 如果输入的新注册类型为空,则使用现有的注册类型if (StringUtils.isEmpty(registrationType)) {registrationType = existingAgentInfo.getRegistrationType();}// 构建Agent卡片详细信息AgentCardDetailInfo agentCardDetailInfo = AgentCardUtil.buildAgentCardDetailInfo(agentCard, registrationType);// 复制属性,排除versionDetails和latestPublishedVersion字段BeanUtils.copyProperties(agentCardDetailInfo, existingAgentInfo, "versionDetails", "latestPublishedVersion");// 如果设置为最新版本if (setAsLatest) {// 设置最新发布的版本existingAgentInfo.setLatestPublishedVersion(agentCard.getVersion());// 更新版本详情列表List<AgentVersionDetail> updatedVersionDetails = existingAgentInfo.getVersionDetails().stream().peek(detail -> {if (StringUtils.equals(detail.getVersion(), agentCard.getVersion())) {// 只更新对应版本detail.setLatest(true);AgentCardUtil.updateUpdateTime(detail);} else {detail.setLatest(false);}}).toList();existingAgentInfo.setVersionDetails(updatedVersionDetails);}// 更新Agent版本信息ConfigForm configForm = transferVersionInfoToConfigForm(existingAgentInfo, namespaceId);ConfigRequestInfo configRequestInfo = new ConfigRequestInfo();configRequestInfo.setUpdateForExist(Boolean.TRUE);configOperationService.publishConfig(configForm, configRequestInfo, null);// 更新Agent信息ConfigForm versionConfigForm = transferAgentInfoToConfigForm(agentCardDetailInfo, namespaceId);ConfigRequestInfo versionConfigRequestInfo = new ConfigRequestInfo();versionConfigRequestInfo.setUpdateForExist(Boolean.TRUE);long startOperationTime = System.currentTimeMillis();configOperationService.publishConfig(versionConfigForm, versionConfigRequestInfo, null);syncEffectService.toSync(versionConfigForm, startOperationTime);}// 实现列出Agents的方法public Page<AgentCardVersionInfo> listAgents(String namespaceId, String agentName, String search, int pageNo,int pageSize) throws NacosException {String dataId;// 如果Agent名称为空或搜索类型为模糊搜索if (StringUtils.isEmpty(agentName) || Constants.A2A.SEARCH_BLUR.equalsIgnoreCase(search)) {search = Constants.A2A.SEARCH_BLUR;// 构造模糊搜索的数据IDdataId = Constants.ALL_PATTERN + agentIdCodecHolder.encodeForSearch(agentName) + Constants.ALL_PATTERN;} else {search = Constants.A2A.SEARCH_ACCURATE;// 构造精确搜索的数据IDdataId = agentIdCodecHolder.encode(agentName);}// 查找配置信息页面Page<ConfigInfo> configInfoPage = configDetailService.findConfigInfoPage(search, pageNo, pageSize, dataId,AGENT_GROUP, namespaceId, null);// 将配置信息转换为Agent卡片版本信息列表List<AgentCardVersionInfo> versionInfos = configInfoPage.getPageItems().stream().map(configInfo -> JacksonUtils.toObj(configInfo.getContent(), AgentCardVersionInfo.class)).toList();// 构造返回结果页面Page<AgentCardVersionInfo> result = new Page<>();result.setPageItems(versionInfos);result.setTotalCount(configInfoPage.getTotalCount());result.setPagesAvailable((int) Math.ceil((double) configInfoPage.getTotalCount() / (double) pageSize));result.setPageNumber(pageNo);return result;}// 实现列出Agent版本的方法public List<AgentVersionDetail> listAgentVersions(String namespaceId, String name) throws NacosApiException {// 查询Agent卡片版本信息AgentCardVersionInfo agentCardVersionInfo = queryAgentCardVersionInfo(namespaceId, name);// 返回版本详情列表return agentCardVersionInfo.getVersionDetails();}}
请求处理器

A2A 模块通过一系列请求处理器来处理各种远程调用:

  • AgentEndpointRequestHandler: 处理代理端点请求
  • QueryAgentCardRequestHandler: 处理代理卡片查询请求
  • ReleaseAgentCardRequestHandler: 处理代理卡片释放请求
// 处理代理端点请求的具体实现
@Component
public class AgentEndpointRequestHandler extends RequestHandler<AgentEndpointRequest, AgentEndpointResponse> {@Override// 命名空间验证注解,确保请求的命名空间有效@NamespaceValidation// 参数提取器,用于RPC请求参数的提取和验证@ExtractorManager.Extractor(rpcExtractor = AgentRequestParamExtractor.class)// 安全注解,指定该操作需要写权限,签名类型为AI@Secured(action = ActionTypes.WRITE, signType = SignType.AI)public AgentEndpointResponse handle(AgentEndpointRequest request, RequestMeta meta) throws NacosException {// 创建响应对象AgentEndpointResponse response = new AgentEndpointResponse();// 设置响应类型与请求类型一致response.setType(request.getType());// 填充命名空间IDAgentRequestUtil.fillNamespaceId(request);try {// 验证请求参数的有效性validateRequest(request);// 将请求转换为Instance实例Instance instance = transferInstance(request);// 构造服务名称,格式为"编码后的代理名::版本号"String serviceName =agentIdCodecHolder.encode(request.getAgentName()) + "::" + request.getEndpoint().getVersion();// 创建服务对象,指定命名空间、组名和服名Service service = Service.newService(request.getNamespaceId(), Constants.A2A.AGENT_ENDPOINT_GROUP,serviceName);// 根据请求类型执行相应的操作switch (request.getType()) {// 注册端点case AiRemoteConstants.REGISTER_ENDPOINT:doRegisterEndpoint(service, instance, meta);break;// 注销端点case AiRemoteConstants.DE_REGISTER_ENDPOINT:doDeregisterEndpoint(service, instance, meta);break;// 参数类型错误,抛出异常default:throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR,String.format("parameter [type](file:///Users/zhiyixie/Downloads/work_space/码云/Nacos/api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/Entity.java#L28-L28) should be %s or %s, but was %s",AiRemoteConstants.REGISTER_ENDPOINT, AiRemoteConstants.DE_REGISTER_ENDPOINT,request.getType()));}} catch (NacosApiException e) {// 设置错误信息response.setErrorInfo(e.getErrCode(), e.getErrMsg());// 记录错误日志LOGGER.error("[{}] Register agent endpoint to agent {} error: {}", meta.getConnectionId(),request.getAgentName(), e.getErrMsg());}// 返回响应结果return response;}// 将AgentEndpointRequest转换为Instance对象private Instance transferInstance(AgentEndpointRequest request) throws NacosApiException {// 创建Instance实例Instance instance = new Instance();// 获取端点信息AgentEndpoint endpoint = request.getEndpoint();// 设置IP地址instance.setIp(endpoint.getAddress());// 设置端口号instance.setPort(endpoint.getPort());// 处理路径信息,如果为空则设置为空字符串String path = StringUtils.isBlank(endpoint.getPath()) ? StringUtils.EMPTY : endpoint.getPath();// 构造元数据映射Map<String, String> metadata = Map.of(Constants.A2A.AGENT_ENDPOINT_PATH_KEY, path,Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, endpoint.getTransport(),Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS, String.valueOf(endpoint.isSupportTls()));// 设置元数据instance.setMetadata(metadata);// 验证实例的有效性instance.validate();// 返回构造好的实例return instance;}// 验证请求参数的有效性private void validateRequest(AgentEndpointRequest request) throws NacosApiException {// 检查代理名称是否为空if (StringUtils.isBlank(request.getAgentName())) {throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING,"Required parameter [agentName](file:///Users/zhiyixie/Downloads/work_space/码云/Nacos/common/src/main/java/com/alibaba/nacos/common/paramcheck/ParamInfo.java#L50-L50) can't be empty or null");}// 检查端点信息是否为空if (null == request.getEndpoint()) {throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING,"Required parameter [endpoint](file:///Users/zhiyixie/Downloads/work_space/码云/Nacos/api/src/main/java/com/alibaba/nacos/api/annotation/NacosProperties.java#L193-L193) can't be null");}// 检查端点版本是否为空if (StringUtils.isBlank(request.getEndpoint().getVersion())) {throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING,"Required parameter `endpoint.version` can't be empty or null");}}// 执行端点注册操作private void doRegisterEndpoint(Service service, Instance instance, RequestMeta meta) throws NacosException {// 调用客户端操作服务注册实例clientOperationService.registerInstance(service, instance, meta.getConnectionId());}// 执行端点注销操作private void doDeregisterEndpoint(Service service, Instance instance, RequestMeta meta) {// 调用客户端操作服务注销实例clientOperationService.deregisterInstance(service, instance, meta.getConnectionId());}}

3. 关键源码剖析

3.1 模型服务注册流程

模型服务注册的核心流程如下:

  1. 客户端通过 McpAdminController 发起注册请求
  2. 请求被转发到 McpServerOperationService 进行处理
  3. 服务信息被持久化到配置中心
  4. 更新索引信息以便后续查询
// McpServerOperationService 中的注册方法示例
public McpServerDetailInfo createMcpServer(McpForm mcpForm) throws NacosException {// 验证表单数据// 构造服务信息对象// 保存到配置中心// 更新索引// 返回详细信息
}

3.2 代理通信处理流程

代理间通信的处理流程如下:

  1. 代理发起通信请求到 AgentEndpointRequestHandler
  2. 处理器解析请求参数
  3. 查询目标代理信息
  4. 建立通信连接并返回响应
// AgentEndpointRequestHandler 中的处理方法示例
@Override
public AgentEndpointResponse handle(agentEndpointRequest request, RequestMeta meta) throws NacosException {// 解析请求参数// 查询代理信息// 构造响应数据// 返回响应
}

4. 架构设计亮点

4.1 分层架构设计

Nacos AI 模块采用清晰的分层架构:

Controller Layer (REST API)↓
Service Layer (业务逻辑)↓
Index Layer (索引管理)↓
Persistence Layer (数据持久化)

这种设计使得各层职责分明,便于维护和扩展。

4.2 缓存机制优化

通过多级缓存机制提升查询性能:

  • 内存缓存:快速响应高频查询
  • 数据库查询:保证数据一致性
  • 缓存更新策略:平衡性能与一致性

4.3 插件化设计

AI 模块继承了 Nacos 的插件化设计理念,支持:

  • 自定义索引实现
  • 扩展认证机制
  • 灵活的配置管理

5. 使用场景

5.1 微服务AI治理

Nacos AI 模块能够与 Nacos 微服务架构无缝集成,提供 AI 服务的注册发现、配置管理等功能。

5.2 多模型版本管理

支持同一 AI 模型的多个版本管理,方便进行灰度发布和 A/B 测试。

5.3 跨域代理通信

通过 A2A 模块,不同域的 AI 代理可以安全高效地进行通信协作。

6. 未来发展趋势

6.1 AI服务网格集成

未来 Nacos AI 模块将进一步与服务网格技术集成,提供更完善的 AI 服务治理能力。

6.2 智能负载均衡

基于 AI 模型的性能指标实现智能负载均衡策略。

6.3 自动扩缩容

根据模型服务的负载情况自动调整实例数量。

7. 结语与学习建议

Nacos AI 模块为 AI 服务提供了完整的生命周期管理解决方案,从模型注册、版本管理到服务发现和配置管理,极大地简化了 AI 服务的运维复杂度。

7.1. 动手实践(快速搭建本地环境)

# 1. 克隆 Nacos 源码
git clone https://github.com/alibaba/nacos.git
cd nacos# 2. 编译所有模块,包含AI
mvn -Prelease-nacos clean install -DskipTests# 3. 启动单机模式
cd distribution/target/nacos-server-3.x.x/nacos/bin
sh startup.sh -m standalone

7.2. 调试技巧

  • 关注 nacos-ai 模块中的 McpServerOperationServiceA2aServerOperationService 类,通过断点调试理解核心流程

  • 查看日志输出,重点关注索引更新和配置变更相关的日志信息

7.3. 延伸阅读

  • Nacos 3.x 官方文档(最权威的版本更新与功能说明)

  • 《深入理解 Nacos 源码》(机械工业出版社,2025)(从源码角度解析 Nacos 设计思想)

🌟 核心学习原则:理解 AI 模块的设计理念和架构思想,而不仅仅是使用其功能接口。例如:

  • 为何要用多级索引机制?(提升查询性能)

  • 为何要分离 MCP 和 A2A?(解耦模型管理和代理通信)

  • 缓存机制如何平衡性能与一致性?(多级缓存策略)

📝 版权声明

本文为原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文链接及本声明。

✅ 本文持续更新,欢迎收藏 + 关注专栏【后端高阶面经:实战篇】

http://www.dtcms.com/a/617831.html

相关文章:

  • 湖州网站seowordpress页面重定向
  • 10场景思考:OLAP系统在监控中的作用
  • 数据结构之二叉树-链式结构(下)
  • 云南省建设考试中心网站长春自助建站软件
  • ReALM(Retrieval-Augmented Language Model)介绍
  • 玩转Docker | Docker环境下部署JSON可视化管理工具JsonHero
  • 学院评估 网站建设整改wordpress 多条件搜索
  • 通信系统架构设计
  • C++_Bug:现代写法拷贝构造中 swap 写法之小坑
  • 通关upload-labs(14-21)加分析源码
  • 【目标检测】YOLOv10n-ADown弹孔检测与识别系统
  • 扬中网站推广导流网盘怎么做电影网站
  • 【C++】:priority_queue的理解,使用和模拟实现
  • 深圳南山网站建设公司做网络推广需要多少钱
  • Rust中的集合Collection
  • Git 配置实践
  • 学习笔记十:多分类学习
  • 【实战案例】基于dino-4scale_r50_8xb2-36e_coco的棉田叶片病害识别与分类项目详解
  • opencv学习笔记9:基于CNN的mnist分类任务
  • 分布式系统中MPSC队列的内存回收策略适配避坑
  • Git笔记---分支相关操作
  • 基于YOLOv8的汽车目标检测系统实现与优化_含多种车型识别与自动驾驶应用场景
  • 广东省建设工程协会网站如何查看一个网站是不是用h5做的
  • 开发STM32日记1:安装软件、配置软件(芯片为STM32F103C8T6 )
  • 【Git】处理报错原因
  • 基于Bboss框架的ElasticSearch并发更新版本冲突问题解决
  • Highcharts常见问题解析(5):如何将多个图表导出到同一张图片或 PDF?
  • 什么是中间件?必须要有中间件吗?有哪些国产中间件厂商?
  • 第七章深度解析:从零构建智能体框架——模块化设计与全流程落地指南
  • 机器视觉3D无序抓取如何确保抓取精度,需要从以下五个核心方面入手,形成一个闭环的控制系统