Spring AI Alibaba Nacos 集成实践

🎯 基础理论

什么是 Spring AI Alibaba Nacos 集成?

Spring AI Alibaba 与 Nacos 的集成提供了以下核心功能:

  1. Prompt 模板管理:通过 Nacos 配置中心动态管理 AI 提示词模板
  2. MCP 服务发现:基于 Nacos 的 MCP (Model Context Protocol) 服务注册与发现
  3. 动态配置:实时更新 AI 相关配置,无需重启应用
  4. 负载均衡:支持 MCP 服务的负载均衡和故障转移

核心组件架构

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   AI 应用       │    │   Nacos 服务    │    │   MCP 服务      │
│                 │    │                 │    │                 │
│ ┌─────────────┐ │    │ ┌─────────────┐ │    │ ┌─────────────┐ │
│ │Prompt模板   │◄┼────┼►│配置中心     │ │    │ │工具服务     │ │
│ └─────────────┘ │    │ └─────────────┘ │    │ └─────────────┘ │
│ ┌─────────────┐ │    │ ┌─────────────┐ │    │ ┌─────────────┐ │
│ │MCP客户端    │◄┼────┼►│服务注册中心 │◄┼────┼►│服务注册     │ │
│ └─────────────┘ │    │ └─────────────┘ │    │ └─────────────┘ │
└─────────────────┘    └─────────────────┘    └─────────────────┘

🛠️ 环境搭建

1. 前置条件

  • Java 17+
  • Maven 3.6+
  • Nacos Server 2.x 或 3.x
  • Spring Boot 3.x

2. 启动 Nacos 服务器

使用 Docker 启动 Nacos
# 启动 Nacos 容器
docker run -d \
  --name nacos-server \
  -p 8848:8848 \
  -p 9848:9848 \
  -e MODE=standalone \
  nacos/nacos-server:v2.3.0
验证 Nacos 启动

访问 Nacos 控制台:http://localhost:8848/nacos

  • 用户名:nacos
  • 密码:nacos

3. 项目依赖配置

创建基础的 Spring Boot 项目,添加以下依赖:

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring AI Alibaba Core -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        <version>1.0.0-M8.1-SNAPSHOT</version>
    </dependency>
    
    <!-- Nacos Prompt 模板支持 -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-nacos-prompt</artifactId>
        <version>1.0.0-M8.1-SNAPSHOT</version>
    </dependency>
    
    <!-- Nacos MCP 客户端支持 -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-nacos-mcp-client</artifactId>
        <version>1.0.0-M8.1-SNAPSHOT</version>
    </dependency>
</dependencies>

📝 Nacos Prompt 模板管理

1. 基础配置

application.yml 中配置 Nacos 连接信息:

spring:
  ai:
    nacos:
      prompt:
        template:
          enabled: true  # 启用 Nacos Prompt 模板
    alibaba:
      nacos:
        server-addr: localhost:8848
        username: nacos
        password: nacos
        namespace: public

2. 在 Nacos 中配置 Prompt 模板

步骤 1:登录 Nacos 控制台

访问 http://localhost:8848/nacos,使用 nacos/nacos 登录。

步骤 2:创建配置

在「配置管理」→「配置列表」中,点击「+」创建新配置:

  • Data ID: ai-prompt-templates
  • Group: DEFAULT_GROUP
  • 配置格式: YAML
  • 配置内容:
templates:
  greeting:
    content: "你好,{name}!欢迎使用 Spring AI Alibaba。"
    description: "问候语模板"
    variables:
      - name: "name"
        type: "string"
        required: true
        description: "用户姓名"
  
  code-review:
    content: |
      请对以下代码进行审查:
      
      ```{language}
      {code}
      ```
      
      请从以下方面进行分析:
      1. 代码质量
      2. 性能优化建议
      3. 安全性考虑
      4. 最佳实践建议
    description: "代码审查模板"
    variables:
      - name: "language"
        type: "string"
        required: true
        description: "编程语言"
      - name: "code"
        type: "string"
        required: true
        description: "待审查的代码"

3. Java 代码实现

创建 Prompt 服务类
package com.example.demo.service;

import com.alibaba.cloud.ai.prompt.ConfigurablePromptTemplateFactory;
import org.springframework.ai.prompt.PromptTemplate;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class PromptService {
    
    private final ConfigurablePromptTemplateFactory promptTemplateFactory;
    
    public PromptService(ConfigurablePromptTemplateFactory promptTemplateFactory) {
        this.promptTemplateFactory = promptTemplateFactory;
    }
    
    /**
     * 获取问候语
     */
    public String getGreeting(String name) {
        PromptTemplate template = promptTemplateFactory.create("greeting");
        return template.render(Map.of("name", name));
    }
    
    /**
     * 获取代码审查提示词
     */
    public String getCodeReviewPrompt(String language, String code) {
        PromptTemplate template = promptTemplateFactory.create("code-review");
        return template.render(Map.of(
            "language", language,
            "code", code
        ));
    }
}
创建控制器
package com.example.demo.controller;

import com.example.demo.service.PromptService;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/prompt")
public class PromptController {
    
    private final PromptService promptService;
    
    public PromptController(PromptService promptService) {
        this.promptService = promptService;
    }
    
    @GetMapping("/greeting")
    public String greeting(@RequestParam String name) {
        return promptService.getGreeting(name);
    }
    
    @PostMapping("/code-review")
    public String codeReview(@RequestBody CodeReviewRequest request) {
        return promptService.getCodeReviewPrompt(request.language(), request.code());
    }
    
    public record CodeReviewRequest(String language, String code) {}
}

4. 测试 Prompt 模板

启动应用
mvn spring-boot:run
测试接口
# 测试问候语
curl "http://localhost:8080/api/prompt/greeting?name=张三"

# 测试代码审查
curl -X POST "http://localhost:8080/api/prompt/code-review" \
  -H "Content-Type: application/json" \
  -d '{
    "language": "java",
    "code": "public class Hello { public static void main(String[] args) { System.out.println(\"Hello World\"); } }"
  }'

🔌 Nacos MCP 客户端

1. MCP 客户端配置

application.yml 中添加 MCP 客户端配置:

spring:
  ai:
    alibaba:
      mcp:
        nacos:
          server-addr: localhost:8848
          username: nacos
          password: nacos
          namespace: public
          client:
            sse:
              connections:
                weather-service:
                  service-name: weather-mcp-server
                  version: 1.0.0
                calculator-service:
                  service-name: calculator-mcp-server
                  version: 1.0.0

2. 创建 MCP 客户端服务

package com.example.demo.service;

import com.alibaba.cloud.ai.mcp.nacos.client.transport.LoadbalancedMcpSyncClient;
import org.springframework.ai.mcp.client.McpSyncClient;
import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;

@Service
public class McpClientService {
    
    private final LoadbalancedMcpSyncClient mcpClient;
    
    public McpClientService(LoadbalancedMcpSyncClient mcpClient) {
        this.mcpClient = mcpClient;
    }
    
    /**
     * 获取可用的工具列表
     */
    public List<McpSchema.Tool> getAvailableTools() {
        return mcpClient.listTools().tools();
    }
    
    /**
     * 调用天气查询工具
     */
    public String getWeather(String city) {
        var request = McpSchema.CallToolRequest.builder()
            .name("get_weather")
            .arguments(Map.of("city", city))
            .build();
        
        var response = mcpClient.callTool(request);
        return response.content().get(0).text();
    }
    
    /**
     * 调用计算器工具
     */
    public String calculate(String expression) {
        var request = McpSchema.CallToolRequest.builder()
            .name("calculate")
            .arguments(Map.of("expression", expression))
            .build();
        
        var response = mcpClient.callTool(request);
        return response.content().get(0).text();
    }
}

3. 创建 MCP 控制器

package com.example.demo.controller;

import com.example.demo.service.McpClientService;
import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/mcp")
public class McpController {
    
    private final McpClientService mcpClientService;
    
    public McpController(McpClientService mcpClientService) {
        this.mcpClientService = mcpClientService;
    }
    
    @GetMapping("/tools")
    public List<McpSchema.Tool> getTools() {
        return mcpClientService.getAvailableTools();
    }
    
    @GetMapping("/weather")
    public String getWeather(@RequestParam String city) {
        return mcpClientService.getWeather(city);
    }
    
    @GetMapping("/calculate")
    public String calculate(@RequestParam String expression) {
        return mcpClientService.calculate(expression);
    }
}

🖥️ Nacos MCP 服务端

1. 创建 MCP 服务端项目

添加依赖
<dependencies>
    <!-- MCP 服务端支持 -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-nacos-mcp-server</artifactId>
        <version>1.0.0-M8.1-SNAPSHOT</version>
    </dependency>
    
    <!-- WebFlux 支持 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
        <version>1.0.0-M8</version>
    </dependency>
</dependencies>
配置文件
spring:
  ai:
    alibaba:
      mcp:
        nacos:
          server-addr: localhost:8848
          username: nacos
          password: nacos
          namespace: public
          registry:
            service-register: true
            service-name: weather-mcp-server
            service-version: 1.0.0
            service-group: DEFAULT_GROUP

server:
  port: 8081

2. 实现 MCP 工具

天气查询工具
package com.example.weather.tools;

import org.springframework.ai.mcp.server.McpAsyncFunction;
import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

@Component
public class WeatherTool implements McpAsyncFunction {
    
    @Override
    public String getName() {
        return "get_weather";
    }
    
    @Override
    public String getDescription() {
        return "获取指定城市的天气信息";
    }
    
    @Override
    public McpSchema.ToolInputSchema getInputSchema() {
        return McpSchema.ToolInputSchema.builder()
            .type("object")
            .properties(Map.of(
                "city", Map.of(
                    "type", "string",
                    "description", "城市名称"
                )
            ))
            .required(List.of("city"))
            .build();
    }
    
    @Override
    public CompletableFuture<List<McpSchema.TextContent>> apply(Map<String, Object> arguments) {
        String city = (String) arguments.get("city");
        
        // 模拟天气查询
        String weatherInfo = getWeatherInfo(city);
        
        var content = McpSchema.TextContent.builder()
            .type("text")
            .text(weatherInfo)
            .build();
        
        return CompletableFuture.completedFuture(List.of(content));
    }
    
    private String getWeatherInfo(String city) {
        // 这里可以调用真实的天气 API
        return String.format("{
" +
            "  \"city\": \"%s\",
" +
            "  \"temperature\": \"22°C\",
" +
            "  \"weather\": \"晴天\",
" +
            "  \"humidity\": \"65%%\",
" +
            "  \"wind\": \"东南风 3级\"
" +
            "}", city);
    }
}
计算器工具
package com.example.weather.tools;

import org.springframework.ai.mcp.server.McpAsyncFunction;
import org.springframework.ai.mcp.spec.McpSchema;
import org.springframework.stereotype.Component;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

@Component
public class CalculatorTool implements McpAsyncFunction {
    
    private final ScriptEngine scriptEngine;
    
    public CalculatorTool() {
        this.scriptEngine = new ScriptEngineManager().getEngineByName("JavaScript");
    }
    
    @Override
    public String getName() {
        return "calculate";
    }
    
    @Override
    public String getDescription() {
        return "执行数学计算";
    }
    
    @Override
    public McpSchema.ToolInputSchema getInputSchema() {
        return McpSchema.ToolInputSchema.builder()
            .type("object")
            .properties(Map.of(
                "expression", Map.of(
                    "type", "string",
                    "description", "数学表达式,如:2+3*4"
                )
            ))
            .required(List.of("expression"))
            .build();
    }
    
    @Override
    public CompletableFuture<List<McpSchema.TextContent>> apply(Map<String, Object> arguments) {
        String expression = (String) arguments.get("expression");
        
        try {
            Object result = scriptEngine.eval(expression);
            var content = McpSchema.TextContent.builder()
                .type("text")
                .text("计算结果:" + expression + " = " + result)
                .build();
            
            return CompletableFuture.completedFuture(List.of(content));
        } catch (Exception e) {
            var content = McpSchema.TextContent.builder()
                .type("text")
                .text("计算错误:" + e.getMessage())
                .build();
            
            return CompletableFuture.completedFuture(List.of(content));
        }
    }
}

3. 启动类

package com.example.weather;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class WeatherMcpServerApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(WeatherMcpServerApplication.class, args);
    }
}

🌐 Nacos MCP 网关

MCP 网关提供了动态代理功能,可以将 Nacos 中注册的服务转换为 MCP 协议服务。

1. 网关配置

spring:
  ai:
    alibaba:
      mcp:
        nacos:
          server-addr: localhost:8848
          username: nacos
          password: nacos
          namespace: public
          dynamic:
            service-namespace: public
            service-group: DEFAULT_GROUP
            service-names:
              - weather-service
              - calculator-service

server:
  port: 8082

2. 在 Nacos 中配置服务模板

在 Nacos 控制台中创建配置:

  • Data ID: weather-service-mcp-template
  • Group: DEFAULT_GROUP
  • 配置内容:
{
  "tools": [
    {
      "name": "get_weather",
      "description": "获取天气信息",
      "inputSchema": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string",
            "description": "城市名称"
          }
        },
        "required": ["city"]
      },
      "requestTemplate": {
        "url": "/api/weather?city={{.args.city}}",
        "method": "GET"
      },
      "responseTemplate": {
        "body": "天气信息:{{.response.body}}"
      }
    }
  ]
}

🚀 实战项目

项目:智能客服系统

我们将创建一个完整的智能客服系统,集成所有 Nacos 功能。

1. 项目结构
intelligent-customer-service/
├── customer-service-app/          # 主应用
├── weather-mcp-server/           # 天气服务
├── knowledge-mcp-server/         # 知识库服务
├── mcp-gateway/                  # MCP 网关
└── docker-compose.yml            # 容器编排
2. 主应用实现
package com.example.customerservice.service;

import com.alibaba.cloud.ai.prompt.ConfigurablePromptTemplateFactory;
import com.example.customerservice.mcp.McpClientService;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
public class CustomerServiceBot {
    
    private final ChatClient chatClient;
    private final ConfigurablePromptTemplateFactory promptFactory;
    private final McpClientService mcpClientService;
    
    public CustomerServiceBot(ChatClient chatClient, 
                             ConfigurablePromptTemplateFactory promptFactory,
                             McpClientService mcpClientService) {
        this.chatClient = chatClient;
        this.promptFactory = promptFactory;
        this.mcpClientService = mcpClientService;
    }
    
    public String handleCustomerQuery(String query, String customerName) {
        // 1. 使用 Nacos 中的 prompt 模板
        var promptTemplate = promptFactory.create("customer-service");
        
        // 2. 根据查询类型调用相应的 MCP 服务
        String contextInfo = "";
        if (query.contains("天气")) {
            contextInfo = mcpClientService.getWeather(extractCity(query));
        } else if (query.contains("计算")) {
            contextInfo = mcpClientService.calculate(extractExpression(query));
        }
        
        // 3. 构建完整的提示词
        String fullPrompt = promptTemplate.render(Map.of(
            "customerName", customerName,
            "query", query,
            "contextInfo", contextInfo
        ));
        
        // 4. 调用 AI 模型生成回复
        return chatClient.call(new Prompt(fullPrompt)).getResult().getOutput().getContent();
    }
    
    private String extractCity(String query) {
        // 简单的城市提取逻辑
        return "北京"; // 实际应用中需要更复杂的 NLP 处理
    }
    
    private String extractExpression(String query) {
        // 简单的表达式提取逻辑
        return "2+3"; // 实际应用中需要更复杂的解析
    }
}
3. 在 Nacos 中配置客服模板
templates:
  customer-service:
    content: |
      你是一个专业的客服助手,正在为客户 {customerName} 提供服务。
      
      客户问题:{query}
      
      相关信息:{contextInfo}
      
      请根据以上信息,用友好、专业的语气回复客户。如果需要更多信息,请礼貌地询问。
      
      回复要求:
      1. 语气友好、专业
      2. 回答准确、有用
      3. 如果无法解答,请引导客户联系人工客服
    description: "客服回复模板"
    variables:
      - name: "customerName"
        type: "string"
        required: true
      - name: "query"
        type: "string"
        required: true
      - name: "contextInfo"
        type: "string"
        required: false

📋 最佳实践

1. 配置管理最佳实践

环境隔离
# 开发环境
spring:
  profiles:
    active: dev
  ai:
    nacos:
      prompt:
        template:
          enabled: true
    alibaba:
      nacos:
        server-addr: dev-nacos:8848
        namespace: dev
        
---
# 生产环境
spring:
  profiles: prod
  ai:
    alibaba:
      nacos:
        server-addr: prod-nacos:8848
        namespace: prod
配置版本管理
@Component
public class PromptVersionManager {
    
    private final ConfigurablePromptTemplateFactory promptFactory;
    
    public PromptVersionManager(ConfigurablePromptTemplateFactory promptFactory) {
        this.promptFactory = promptFactory;
    }
    
    public String getPromptWithFallback(String templateName, String version) {
        try {
            // 尝试获取指定版本的模板
            return promptFactory.create(templateName + "_v" + version).getTemplate();
        } catch (Exception e) {
            // 回退到默认版本
            return promptFactory.create(templateName).getTemplate();
        }
    }
}

2. 性能优化

缓存策略
@Service
public class CachedPromptService {
    
    private final ConfigurablePromptTemplateFactory promptFactory;
    private final Cache<String, PromptTemplate> templateCache;
    
    public CachedPromptService(ConfigurablePromptTemplateFactory promptFactory) {
        this.promptFactory = promptFactory;
        this.templateCache = Caffeine.newBuilder()
            .maximumSize(100)
            .expireAfterWrite(Duration.ofMinutes(10))
            .build();
    }
    
    public PromptTemplate getTemplate(String name) {
        return templateCache.get(name, key -> promptFactory.create(key));
    }
}
连接池配置
spring:
  ai:
    alibaba:
      nacos:
        # 连接池配置
        pool:
          max-active: 20
          max-idle: 10
          min-idle: 5
          max-wait: 5000

3. 监控和日志

自定义监控指标
@Component
public class NacosMetrics {
    
    private final MeterRegistry meterRegistry;
    private final Counter promptLoadCounter;
    private final Timer mcpCallTimer;
    
    public NacosMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.promptLoadCounter = Counter.builder("nacos.prompt.load")
            .description("Prompt template load count")
            .register(meterRegistry);
        this.mcpCallTimer = Timer.builder("nacos.mcp.call")
            .description("MCP service call duration")
            .register(meterRegistry);
    }
    
    public void recordPromptLoad(String templateName) {
        promptLoadCounter.increment(Tags.of("template", templateName));
    }
    
    public void recordMcpCall(String serviceName, Duration duration) {
        mcpCallTimer.record(duration, Tags.of("service", serviceName));
    }
}

🔧 故障排除

常见问题及解决方案

1. Nacos 连接失败

问题:应用启动时无法连接到 Nacos 服务器

解决方案

# 检查 Nacos 服务状态
curl http://localhost:8848/nacos/v1/ns/operator/metrics

# 检查网络连通性
telnet localhost 8848

# 查看应用日志
tail -f logs/spring.log | grep nacos
2. Prompt 模板加载失败

问题:提示词模板无法从 Nacos 加载

解决方案

@Component
public class PromptHealthChecker {
    
    @EventListener
    public void onApplicationReady(ApplicationReadyEvent event) {
        try {
            // 验证关键模板是否可用
            promptFactory.create("customer-service");
            log.info("Prompt templates loaded successfully");
        } catch (Exception e) {
            log.error("Failed to load prompt templates", e);
            // 可以选择使用本地备份模板
        }
    }
}
3. MCP 服务发现问题

问题:MCP 客户端无法发现服务

解决方案

@Component
public class McpServiceHealthChecker {
    
    @Scheduled(fixedRate = 30000) // 每30秒检查一次
    public void checkMcpServices() {
        try {
            List<McpSchema.Tool> tools = mcpClientService.getAvailableTools();
            log.info("Available MCP tools: {}", tools.size());
        } catch (Exception e) {
            log.warn("MCP service check failed", e);
        }
    }
}

调试技巧

1. 启用详细日志
logging:
  level:
    com.alibaba.cloud.ai: DEBUG
    com.alibaba.nacos: DEBUG
    org.springframework.ai.mcp: DEBUG
2. 使用 Actuator 监控
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,nacos
  endpoint:
    health:
      show-details: always

📚 参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2018wl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值