编程 Spring AI 1.1 深度解析:从 RAG 到 MCP 协议——Java 开发者构建企业级 AI 应用的工程化实战

2026-05-10 04:41:17 +0800 CST views 4

Spring AI 1.1 深度解析:从 RAG 到 MCP 协议——Java 开发者构建企业级 AI 应用的工程化实战

从 2025 年 Spring AI 1.0 GA 正式发布,到 2026 年全面拥抱 Agent 工程,Spring AI 已成为 Java 开发者构建企业级 AI 应用的首选框架。但大多数人对 Spring AI 的认知还停留在「调 API」层面——本文将深入 Spring AI 的工程化内核,揭示 Tool Calling 的底层机制、MCP 协议的企业级接入、Agent 框架的自主决策能力,以及 Memory 在生产环境中的实战价值。文中的所有代码均可直接落地,覆盖 Spring AI 1.1.2 稳定版与 Spring Boot 3.4.x。

一、为什么 Java 开发者必须关注 Spring AI

1.1 企业 AI 落地的「最后一公里」问题

2026 年,企业 AI 应用已从概念验证走向规模化生产。但 Java 生态面临一个尴尬现实:Python 有 LangChain、LlamaIndex、AutoGen 等成熟框架,而 Java 开发者要么自己封装 API 调用,要么依赖不成熟的第三方库。

Spring AI 的出现彻底改变了这一局面。作为 Spring 官方的 AI 框架,它带来了三个核心价值:

传统 AI 集成(分散混乱)          Spring AI 集成(统一标准)
┌─────────────────────┐          ┌──────────────────────────────────┐
│  OpenAI SDK         │          │  Spring AI + Spring Boot         │
│  Anthropic SDK       │          │                                  │
│  通义千问 SDK        │    →     │  统一的 ChatClient API          │
│  Ollama SDK         │          │  统一 VectorStore 接口           │
│  各模型 API 格式不同 │          │  统一 Tool/Agent/Memory 接口    │
│  无标准化工具调用    │          │  统一 MCP 协议支持               │
│  无企业级监控治理    │          │  统一安全/监控/配置管理          │
└─────────────────────┘          └──────────────────────────────────┘

1.2 Spring AI 版本演进与生态版图

Spring AI 的版本策略非常清晰:

版本发布时间Spring Boot 要求JDK 要求核心里程碑
1.0.0 GA2025 Q13.2+17+正式版发布,核心 API 稳定
1.1.x2025 Q2-Q43.2+/3.3+17+/21MCP 协议支持、Alibaba 扩展
1.1.2当前最新3.2+/3.3+17+/21生产稳定版
1.2.x2026 Q13.3+21增强 Agent 框架、结构化输出
2.0.x2026 Q23.4+21+虚拟线程原生、下一代内存管理

版本对应关系(必须遵守):

  • Spring AI 1.1.x → Spring Boot 3.2.x | JDK 17+
  • Spring AI 1.2.x → Spring Boot 3.3.x | JDK 21+
  • Spring AI 2.0.x → Spring Boot 3.4+ | JDK 21+

1.3 核心能力地图

Spring AI 1.1.x 核心能力
├── 模型接入层
│   ├── OpenAI (GPT-4o, o1, o3)
│   ├── 通义千问 (Qwen, Qwen-Max)
│   ├── Ollama (本地部署)
│   ├── Azure OpenAI
│   ├── Anthropic (Claude)
│   └── Google Vertex AI (Gemini)
├── AI 应用开发层
│   ├── ChatClient API(统一对话接口)
│   ├── Prompt Template(动态模板)
│   ├── Structured Output(结构化输出)
│   └── ChatMemory(对话记忆)
├── 工具系统层
│   ├── Tool Calling / Function Calling
│   ├── MCP Client(模型上下文协议客户端)
│   ├── MCP Server(模型上下文协议服务端)
│   └── 函数注册与调用链
├── Agent 层
│   ├── React Agent(推理-行动循环)
│   ├── Agent 规划与执行
│   └── 多 Agent 协作
├── RAG 层
│   ├── Document Reader(全格式文档加载)
│   ├── Text Splitter(语义分块)
│   ├── Embedding(向量嵌入)
│   ├── VectorStore(向量存储:pgvector/Chroma/Milvus)
│   └── 检索重排序
└── 企业特性
    ├── 全局异常处理
    ├── 超时/重试/熔断
    └── 可观测性(Prometheus 指标)

二、环境搭建:生产级项目初始化

2.1 Maven 依赖配置(必须用 BOM)

Spring AI 的依赖管理非常严格——必须引入 Spring AI BOM 统一版本号,否则依赖冲突会让你痛不欲生。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.5</version>
    </parent>

    <groupId>com.enterprise</groupId>
    <artifactId>spring-ai-enterprise</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>21</java.version>
        <spring-ai.version>1.1.2</spring-ai.version>
    </properties>

    <!-- 1. Spring AI BOM 统一版本管理(必加!) -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- 通义千问扩展(可选) -->
            <dependency>
                <groupId>com.alibaba.cloud.ai</groupId>
                <artifactId>spring-ai-alibaba-bom</artifactId>
                <version>1.1.2.0</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <!-- Web + 异步 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring AI 核心 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-openai</artifactId>
        </dependency>
        <!-- 通义千问专用 Starter(可选) -->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>

        <!-- 向量存储:pgvector(推荐生产使用) -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
        </dependency>

        <!-- 文档解析 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-tika-document-reader</artifactId>
        </dependency>

        <!-- MCP 协议支持 -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-mcp-server</artifactId>
        </dependency>

        <!-- 企业级:安全 + 监控 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>

        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>
</project>

2.2 application.yml 企业级配置

server:
  port: 8080

spring:
  # API Key 必须使用环境变量,禁止硬编码!
  ai:
    # OpenAI 配置
    openai:
      api-key: ${OPENAI_API_KEY}
      base-url: https://api.openai.com
      chat:
        options:
          model: gpt-4o          # 或 gpt-4o-mini, o1-preview, o3
          temperature: 0.1       # 生产用低温度保证稳定性
          max-tokens: 4096
          # 流式响应
          stream: true

    # 通义千问配置(可选)
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
      chat:
        options:
          model: qwen-plus
          temperature: 0.1

    # 向量存储配置(pgvector)
    vectorstore:
      pgvector:
        host: ${PG_HOST:localhost}
        port: ${PG_PORT:5432}
        database: ${PG_DATABASE:vector_db}
        username: ${PG_USER:postgres}
        password: ${PG_PASSWORD:postgres}
        initialize-schema: true  # 首次启动自动建表

  # PostgreSQL 数据源
  datasource:
    url: jdbc:postgresql://${PG_HOST:localhost}:${PG_PORT:5432}/${PG_DATABASE:vector_db}
    username: ${PG_USER:postgres}
    password: ${PG_PASSWORD:postgres}
    driver-class-name: org.postgresql.Driver
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5

# MCP 客户端配置
mcp:
  client:
    enabled: true
    toolcallback:
      enabled: true
    streamable-http:
      connections:
        # 连接 Jina MCP 服务器(网页内容提取)
        jina:
          url: "https://mcp.jina.ai"
          endpoint: "/sse"
          request-timeout: 60000
        # 连接文件系统 MCP 服务器
        filesystem:
          url: "http://localhost:8787"
          endpoint: "/sse"

# 监控端点
management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus,metrics
  metrics:
    tags:
      application: spring-ai-enterprise

2.3 全局异常处理(生产必备)

@Configuration
public class AiExceptionHandler {

    // 统一 AI 异常处理
    @RestControllerAdvice
    public static class GlobalAiExceptionHandler {

        @ExceptionHandler(GenericAiException.class)
        public ResponseEntity<ErrorResponse> handleGenericAiException(GenericAiException e) {
            log.error("AI 调用失败: {}", e.getMessage(), e);
            return ResponseEntity
                .status(HttpStatus.BAD_GATEWAY)
                .body(new ErrorResponse("AI_SERVICE_ERROR", e.getMessage()));
        }

        @ExceptionHandler(AiResponseException.class)
        public ResponseEntity<ErrorResponse> handleAiResponseException(AiResponseException e) {
            log.error("AI 响应异常: {}", e.getMessage());
            return ResponseEntity
                .status(HttpStatus.UNPROCESSABLE_ENTITY)
                .body(new ErrorResponse("AI_RESPONSE_ERROR", e.getMessage()));
        }

        @ExceptionHandler(Exception.class)
        public ResponseEntity<ErrorResponse> handleGenericException(Exception e) {
            log.error("未知异常: {}", e.getMessage(), e);
            return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(new ErrorResponse("INTERNAL_ERROR", "系统内部错误"));
        }
    }

    public record ErrorResponse(String code, String message, long timestamp) {
        public ErrorResponse(String code, String message) {
            this(code, message, System.currentTimeMillis());
        }
    }
}

三、ChatClient API:统一对话接口的艺术

3.1 Hello World:最小可用对话

Spring AI 的 ChatClient 是整个框架的核心入口,取代了旧版的 ChatModel 直接调用,提供更简洁的链式 API。

@Service
@RequiredArgsConstructor
public class SimpleChatService {

    private final ChatClient chatClient;

    // 最简对话
    public String hello(String userInput) {
        return chatClient.prompt()
            .user(userInput)
            .call()
            .content();
    }

    // 指定模型
    public String chatWithModel(String input, String model) {
        return chatClient.prompt()
            .options(ChatOptionsBuilder.builder()
                .model(model)
                .temperature(0.7)
                .build())
            .user(input)
            .call()
            .content();
    }
}

3.2 Prompt 模板:结构化提示词工程

@Service
public class PromptTemplateService {

    private final ChatClient chatClient;

    // 方式1:StringTemplate(简单场景)
    public String simpleTemplate(String name, String task) {
        PromptTemplate template = new PromptTemplate(
            "你是一个专业的{role},请帮助用户完成{task}。"
        );

        Prompt prompt = template.render(Map.of(
            "role", name,
            "task", task
        ));

        return chatClient.prompt(prompt).call().content();
    }

    // 方式2:Spring 模板引擎(复杂场景)
    public String springTemplate(String userQuery, String context) {
        String template = """
            你是一个企业知识库问答助手。请严格基于以下知识库内容回答用户问题。
            
            【知识库内容】
            {knowledge}
            
            【用户问题】
            {question}
            
            【回答要求】
            1. 必须使用知识库中的内容,不允许编造
            2. 如果知识库中没有相关内容,明确告知用户
            3. 在回答末尾标注参考来源
            """;

        Prompt prompt = Prompt.builder()
            .template(template)
            .variable("knowledge", context)
            .variable("question", userQuery)
            .build();

        return chatClient.prompt(prompt).call().content();
    }

    // 方式3:系统消息 + 用户消息(角色扮演场景)
    public String rolePlay(String userMessage, String systemPrompt) {
        return chatClient.prompt()
            .system(systemPrompt)
            .user(userMessage)
            .options(ChatOptionsBuilder.builder()
                .temperature(0.3)  // 低温度保证角色一致性
                .build())
            .call()
            .content();
    }
}

3.3 流式响应:SSE 实现实时输出

@Service
@RequiredArgsConstructor
public class StreamingChatService {

    private final ChatClient chatClient;

    // 流式响应:适合长文本生成场景
    public Flux<String> streamingChat(String userMessage) {
        return chatClient.prompt()
            .user(userMessage)
            .stream()
            .content();
    }

    // WebFlux 控制器:SSE 推送到前端
    @RestController
    @RequestMapping("/api/ai")
    public static class StreamingController {

        private final StreamingChatService streamingService;

        @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
        public Flux<String> streamChat(@RequestParam String message) {
            return streamingService.streamingChat(message)
                .map(chunk -> "data: " + chunk + "\n\n")
                .concatWith(Flux.just("data: [DONE]\n\n"));
        }
    }
}

四、Tool Calling:让 AI 真正「动手干活」

4.1 为什么 Tool Calling 是 AI Agent 的地基

Function Calling(函数调用)是 Spring AI 最核心的能力,也是从「会说话」到「会干活」的分水岭。它的本质是让大模型在生成响应时,输出一个结构化的「工具调用请求」,应用程序执行后把结果传回,大模型再结合结果生成最终答案。

传统 AI 对话(只能说话)           Tool Calling(能动手干活)
┌──────────────────────┐          ┌─────────────────────────────────────┐
│ 用户 → AI → 回答      │          │ 用户 → AI → "需要查天气"             │
│                      │          │         ↓                              │
│ 局限:                │          │    工具调用请求                      │
│ - 只能基于训练数据    │          │    {tool: "weather",                │
│ - 不知道实时信息      │          │     args: {city: "上海"}}           │
│ - 无法执行操作        │          │         ↓                              │
│ - 幻觉无法避免        │          │    应用执行工具                      │
│                      │          │    {temp: 22, humidity: 65}         │
│                      │          │         ↓                              │
│                      │          │    AI 结合结果 → 最终回答              │
└──────────────────────┘          └─────────────────────────────────────┘

4.2 注解式 Tool 定义(最简洁的方式)

Spring AI 1.1 支持通过注解声明式定义工具,无需手动实现接口:

@Service
public class EnterpriseTools {

    private final OrderService orderService;
    private final ProductService productService;
    private final UserService userService;

    public EnterpriseTools(OrderService orderService,
                          ProductService productService,
                          UserService userService) {
        this.orderService = orderService;
        this.productService = productService;
        this.userService = userService;
    }

    /**
     * 工具1:查询订单状态
     * AI 会自动识别用户想要查询订单的场景
     */
    @Tool(name = "query_order", description = "查询订单的详细信息,包括状态、金额、物流信息")
    public String queryOrder(@ToolParam(name = "order_no", description = "订单编号") String orderNo) {
        try {
            Order order = orderService.getOrderByNo(orderNo);
            return String.format("订单号: %s, 状态: %s, 金额: %.2f元, 下单时间: %s, 物流: %s",
                order.getOrderNo(),
                order.getStatus().getDesc(),
                order.getAmount(),
                order.getCreateTime(),
                order.getLogisticsNo() != null ? order.getLogisticsNo() : "暂无"
            );
        } catch (OrderNotFoundException e) {
            return "未找到订单: " + orderNo;
        }
    }

    /**
     * 工具2:查询产品库存
     */
    @Tool(name = "query_product_stock", description = "查询产品的库存数量和价格")
    public String queryProductStock(
            @ToolParam(name = "product_id", description = "产品ID") Long productId,
            @ToolParam(name = "region", description = "地区代码") @ToolParam(defaultValue = "CN_EAST") String region) {
        ProductStock stock = productService.getStock(productId, region);
        return String.format("产品ID: %d, 库存: %d件, 区域: %s, 单价: %.2f元",
            productId, stock.getQuantity(), region, stock.getPrice());
    }

    /**
     * 工具3:获取用户信息
     */
    @Tool(name = "get_user_info", description = "获取用户的基本信息、会员等级和积分")
    public String getUserInfo(@ToolParam(name = "user_id", description = "用户ID") Long userId) {
        User user = userService.getUser(userId);
        return String.format("用户: %s, 会员: %s, 积分: %d, 注册时间: %s",
            user.getName(),
            user.getVipLevel().getDesc(),
            user.getPoints(),
            user.getRegisterTime()
        );
    }
}

4.3 在 ChatClient 中注册工具

@Service
@RequiredArgsConstructor
public class ToolChatService {

    private final ChatClient chatClient;
    private final EnterpriseTools enterpriseTools;

    /**
     * 带工具调用的对话
     */
    public String chatWithTools(String userMessage) {
        return chatClient.prompt()
            .user(userMessage)
            .tools(enterpriseTools)  // 注册工具
            .options(ChatOptionsBuilder.builder()
                .model("gpt-4o")
                .temperature(0.1)
                .build())
            .call()
            .content();
    }

    /**
     * 多轮对话示例:用户问"我的订单到哪了?"
     * AI 自动判断需要调用 query_order 工具
     */
    public void demoToolConversation() {
        String response = chatWithTools("我想查一下订单号 ORD20260315001 的状态");
        System.out.println(response);
        // 输出可能:
        // "根据查询结果,您的订单 ORD20260315001 当前状态为【配送中】,
        //  金额为 299.00 元,下单时间为 2026-03-15 14:32:18,
        //  物流单号:SF1234567890,预计明天送达。"
    }

    /**
     * 批量工具:同时注册多个工具类
     */
    public String chatWithMultipleToolSets(String message,
            Object... toolObjects) {
        return chatClient.prompt()
            .user(message)
            .tools(toolObjects)
            .call()
            .content();
    }
}

4.4 手动实现 ToolCallback(高级场景)

对于更复杂的场景,可以手动实现 ToolCallback 接口:

@Component
public class WeatherToolCallback implements ToolCallback {

    private static final String DESCRIPTION = """
        查询指定城市的实时天气信息,包括温度、湿度、风力、空气质量等。
        当用户询问天气、气温、要不要带伞、穿什么衣服时使用此工具。
        """;

    private final WeatherService weatherService;

    public WeatherToolCallback(WeatherService weatherService) {
        this.weatherService = weatherService;
    }

    @Override
    public ToolDefinition getToolDefinition() {
        return ToolDefinition.builder()
            .name("weather_query")
            .description(DESCRIPTION)
            .inputSchema("""
                {
                  "type": "object",
                  "properties": {
                    "city": {
                      "type": "string",
                      "description": "城市名称,例如:上海、北京、深圳"
                    },
                    "date": {
                      "type": "string",
                      "description": "查询日期,格式:YYYY-MM-DD,不填则查今天"
                    }
                  },
                  "required": ["city"]
                }
                """)
            .build();
    }

    @Override
    public ToolCallResult call(ToolContext toolContext) {
        // 解析 AI 传入的参数
        Map<String, Object> args = toolContext.getToolCall().getArguments();
        String city = (String) args.get("city");
        String date = (String) args.getOrDefault("date",
            LocalDate.now().toString());

        // 调用天气服务
        WeatherInfo weather = weatherService.getWeather(city, date);

        return ToolCallResult.success(String.format(
            "【%s %s 天气】\n温度: %d~%d°C\n湿度: %d%%\n风力: %s\n空气质量: %s\n建议: %s",
            city, date,
            weather.getMinTemp(), weather.getMaxTemp(),
            weather.getHumidity(),
            weather.getWindLevel(),
            weather.getAqi(),
            weather.getSuggestion()
        ));
    }

    @Override
    public String getToolCallId() {
        return "weather_query";
    }
}

4.5 工具调用的生产级最佳实践

@Service
public class ToolSecurityService {

    private final ChatClient chatClient;
    private final PermissionService permissionService;

    /**
     * 工具权限控制:只有授权用户才能调用特定工具
     */
    public String chatWithPermissionCheck(Long userId, String message) {
        // 获取用户权限
        Set<String> userPermissions = permissionService.getUserPermissions(userId);

        // 动态构建可用工具
        List<Object> allowedTools = new ArrayList<>();

        if (userPermissions.contains("ORDER_QUERY")) {
            allowedTools.add(new EnterpriseTools(orderService, productService, userService));
        }
        if (userPermissions.contains("INVENTORY_QUERY")) {
            allowedTools.add(inventoryTools);
        }
        if (userPermissions.contains("HR_QUERY")) {
            allowedTools.add(hrTools);  // 仅 HR 部门可用
        }

        if (allowedTools.isEmpty()) {
            return "您没有可用的 AI 工具权限,请联系管理员授权。";
        }

        return chatClient.prompt()
            .user(message)
            .tools(allowedTools.toArray())
            .call()
            .content();
    }

    /**
     * 工具调用超时控制
     */
    public String chatWithTimeout(String message) {
        try {
            return ChatClient.create(chatModel)
                .prompt()
                .user(message)
                .tools(enterpriseTools)
                .options(ChatOptionsBuilder.builder()
                    .toolCallTimeout(Duration.ofSeconds(10))  // 单个工具调用超时
                    .build())
                .call()
                .content();
        } catch (AiException e) {
            log.error("工具调用超时或失败: {}", e.getMessage());
            return "工具调用遇到问题,请稍后重试。错误详情:" + e.getMessage();
        }
    }

    /**
     * 工具调用限流
     */
    @RateLimiter(name = "toolCallRateLimiter", fallbackMethod = "rateLimitFallback")
    public String chatWithRateLimit(String message) {
        return chatWithTools(message);
    }

    public String rateLimitFallback(String message, Exception e) {
        return "请求过于频繁,请稍后再试。当前系统限制每分钟最多 60 次 AI 调用。";
    }
}

五、MCP 协议:AI 与外部系统的标准化总线

5.1 MCP 为什么是 2026 年最重要的 AI 协议

MCP(Model Context Protocol,模型上下文协议)由 Anthropic 在 2024 年底推出,2026 年已演化为 AI 工具调用的事实标准。它的核心价值是解决工具调用的碎片化问题——以前每个模型、每个平台都有自己的工具调用协议,AI 应用换一个模型就要重写所有工具。MCP 通过统一协议让任何 MCP 兼容的 AI 应用可以调用任何 MCP 兼容的工具,就像 USB 协议让任何设备可以插任何电脑。

MCP 协议架构
┌─────────────┐       MCP        ┌─────────────┐
│   AI 模型   │◄──────────────►│  MCP Host   │
│  (Claude,   │   标准化协议     │  (Spring AI)│
│   GPT-4o)   │                │             │
└─────────────┘                └──────┬──────┘
                                      │
                    ┌─────────────────┼─────────────────┐
                    │                 │                 │
                    ▼                 ▼                 ▼
            ┌───────────┐     ┌───────────┐     ┌───────────┐
            │ MCP Server│     │ MCP Server│     │ MCP Server│
            │ (Jina AI) │     │(文件系统) │     │ (数据库)  │
            └───────────┘     └───────────┘     └───────────┘

5.2 Spring AI MCP 客户端:调用外部 MCP 工具

@Configuration
public class McpClientConfig {

    /**
     * 配置 MCP 客户端连接到外部 MCP 服务器
     */
    @Bean
    public McpClientFactory mcpClientFactory(
            ObjectProvider<McpSyncHttpClientRequestCustomizer> customizers) {
        return new McpClientFactory(customizers);
    }

    /**
     * MCP SSE 传输配置
     */
    @Bean
    public SyncMcpToolCallbackProvider syncMcpToolCallbackProvider(
            McpClientFactory factory,
            @Value("${mcp.client.jina.url}") String jinaUrl,
            @Value("${mcp.client.jina.endpoint}") String jinaEndpoint,
            @Value("${jina.mcp.apikey:}") String jinaApiKey) {

        var transport = new HttpClientSseWebClientTransport(
            HttpClient.newHttpClient(),
            jinaUrl,
            jinaEndpoint
        );

        var client = factory.create(transport, new SimpleCapabilitySampler());
        var provider = new SyncMcpToolCallbackProvider(client);

        return provider;
    }
}
@Service
public class McpClientService {

    private final ChatClient chatClient;
    private final SyncMcpToolCallbackProvider mcpToolProvider;

    /**
     * 使用 MCP 工具:Jina AI 网页内容提取
     */
    public String chatWithMcpTools(String userMessage) {
        List<ToolCallback> mcpTools = Arrays.asList(
            mcpToolProvider.getToolCallbacks()
        );

        log.info("MCP 工具数量: {}", mcpTools.size());
        mcpTools.forEach(tool ->
            log.info("  - MCP 工具: {}", tool.getToolDefinition().name())
        );

        return chatClient.prompt()
            .user(userMessage)
            .tools(mcpTools.toArray())
            .call()
            .content();
    }

    /**
     * 示例:让 AI 帮你搜索并总结网页内容
     */
    public String summarizeWebContent(String url) {
        String prompt = String.format(
            "请访问以下网址,提取主要内容并用中文总结:\n\n%s", url
        );
        return chatWithMcpTools(prompt);
    }
}

5.3 Spring AI MCP 服务端:将业务接口暴露为 MCP 工具

@Configuration
public class McpServerConfig {

    /**
     * 启动 MCP Server:让外部 AI 应用调用本系统接口
     */
    @Bean
    public McpServerProperties mcpServerProperties() {
        McpServerProperties props = new McpServerProperties();
        props.setName("enterprise-mcp-server");
        props.setVersion("1.0.0");
        props.setDescription("企业级业务工具 MCP 服务");
        return props;
    }

    /**
     * STDIO 传输模式(适合 CLI 工具调用)
     */
    @Bean
    public McpServer mcpStdioServer(McpServerProperties props,
            ObjectProvider<ToolCallback> toolCallbacks) {
        return McpServer.builder(props)
            .setTools(toolCallbacks)
            .setTransport(new StdioServerTransport())
            .build();
    }

    /**
     * SSE 传输模式(适合 Web 应用)
     */
    @Bean
    public McpServer mcpSseServer(McpServerProperties props,
            ObjectProvider<ToolCallback> toolCallbacks) {
        return McpServer.builder(props)
            .setTools(toolCallbacks)
            .setTransport(new SseServerTransport())
            .build();
    }
}
/**
 * 将业务方法暴露为 MCP 工具
 */
@McpTool(name = "enterprise_tools", description = "企业核心业务工具集")
@Component
public class EnterpriseMcpTools {

    private final OrderService orderService;
    private final WarehouseService warehouseService;

    public EnterpriseMcpTools(OrderService orderService,
                             WarehouseService warehouseService) {
        this.orderService = orderService;
        this.warehouseService = warehouseService;
    }

    @McpFunction(name = "create_order",
                 description = "创建一个新订单,需提供用户ID、商品ID列表和收货地址")
    public String createOrder(
            @McpParam(name = "user_id", description = "用户ID") Long userId,
            @McpParam(name = "items", description = "商品项列表,JSON数组格式") String itemsJson,
            @McpParam(name = "address", description = "收货地址") String address) {

        try {
            List<OrderItem> items = parseItems(itemsJson);
            Order order = orderService.createOrder(userId, items, address);
            return "订单创建成功!订单号:" + order.getOrderNo() +
                   ",总金额:" + order.getAmount() + "元";
        } catch (BusinessException e) {
            return "创建订单失败:" + e.getMessage();
        }
    }

    @McpFunction(name = "query_warehouse_stock",
                 description = "查询仓库库存,返回商品的库存数量和可用数量")
    public String queryWarehouseStock(
            @McpParam(name = "product_id", description = "商品ID") Long productId,
            @McpParam(name = "warehouse_code", description = "仓库代码,不填则查所有仓库") String warehouseCode) {

        List<WarehouseStock> stocks = warehouseService.getStocks(
            productId,
            warehouseCode != null ? warehouseCode : null
        );

        return stocks.stream()
            .map(s -> String.format("仓库[%s]: 库存=%d, 可用=%d",
                s.getWarehouseCode(), s.getTotalStock(), s.getAvailableStock()))
            .collect(Collectors.joining("\n"));
    }
}

5.4 MCP 工具发现与注册

/**
 * MCP 工具注册中心:动态管理所有 MCP 工具
 */
@Service
@Slf4j
public class McpToolRegistry {

    private final Map<String, ToolCallback> tools = new ConcurrentHashMap<>();

    public McpToolRegistry(List<ToolCallback> toolCallbacks) {
        toolCallbacks.forEach(callback -> {
            String name = callback.getToolCallId();
            tools.put(name, callback);
            log.info("MCP 工具注册: {}", name);
        });
    }

    public ToolCallback getTool(String name) {
        return tools.get(name);
    }

    public List<ToolCallback> getAllTools() {
        return new ArrayList<>(tools.values());
    }

    public List<String> getToolNames() {
        return new ArrayList<>(tools.keySet());
    }

    public boolean hasTool(String name) {
        return tools.containsKey(name);
    }

    /**
     * 按类别筛选工具
     */
    public List<ToolCallback> getToolsByCategory(String category) {
        return tools.values().stream()
            .filter(t -> t.getToolDefinition().description().contains(category))
            .toList();
    }
}

六、Agent 框架:从「单轮对话」到「自主决策」

6.1 什么是 AI Agent

AI Agent 的本质是让 AI 具备「思考 → 规划 → 执行 → 反思」的闭环能力。传统的 AI 调用是「问一句答一句」,而 Agent 可以自主分解任务、调用多个工具、收集结果、做出决策。

传统对话模式                    Agent 自主决策模式
用户: 查我上个月的订单总额         用户: 帮我分析一下我上半年的消费情况
     ↓                                 ↓
AI: [直接回答或说不知道]          Agent: 需要分解任务
                                        ↓
                                   1. 查询所有订单
                                   2. 按月分类统计
                                   3. 查询商品分类
                                   4. 生成分析报告
                                        ↓
                                   [自主执行多个步骤]
                                        ↓
                                   返回综合分析结果

6.2 Spring AI Agent 实战

@Service
@RequiredArgsConstructor
public class EnterpriseAgentService {

    private final ChatClient chatClient;
    private final McpToolRegistry toolRegistry;

    /**
     * 构建企业级 Agent
     */
    public Agent buildEnterpriseAgent() {
        return Agent.builder()
            .name("enterprise_assistant")
            .description("企业级智能助手,可查询订单、库存、用户信息等")
            .chatClient(chatClient)
            .tools(toolRegistry.getAllTools())  // 所有已注册工具
            .maxIterations(10)                   // 最多迭代 10 步,防止死循环
            .toolCallPolicy(ToolCallPolicy.EACH_STEP)  // 每步都允许工具调用
            .build();
    }

    /**
     * Agent 对话入口
     */
    public String agentChat(String userMessage) {
        Agent agent = buildEnterpriseAgent();

        // Agent 执行循环
        AgentResult result = agent.run(userMessage);

        if (result.isSuccess()) {
            return result.getOutput().getContent();
        } else {
            log.warn("Agent 执行异常: {}, 已执行步数: {}",
                result.getError(), result.getSteps());
            return "处理遇到问题:" + result.getError() +
                   "(已尝试 " + result.getSteps() + " 步)";
        }
    }

    /**
     * 带内存的 Agent(多轮对话)
     */
    public String agentChatWithMemory(String sessionId, String userMessage) {
        // 获取或创建会话记忆
        ChatMemory memory = getOrCreateMemory(sessionId);

        return chatClient.prompt()
            .user(userMessage)
            .tools(toolRegistry.getAllTools())
            .advisors(new ChatMemoryAdvisor(memory))  // 注入对话记忆
            .call()
            .content();
    }

    private final Map<String, ChatMemory> sessionMemories = new ConcurrentHashMap<>();

    private ChatMemory getOrCreateMemory(String sessionId) {
        return sessionMemories.computeIfAbsent(sessionId,
            id -> new InMemoryChatMemory());
    }
}

6.3 React Agent:推理-行动循环

/**
 * React Agent:Think → Act → Observe 循环
 */
@Service
public class ReactAgentService {

    private final ChatClient chatClient;
    private final McpToolRegistry toolRegistry;

    /**
     * 手写 React Agent 实现(深入理解原理)
     */
    public String reactAgentChat(String userGoal) {
        String observation = "";
        int maxSteps = 10;

        for (int step = 0; step < maxSteps; step++) {
            // Step 1: Think - 让 AI 分析当前状态,决定下一步行动
            String thought = think(observation, userGoal);

            // 如果 AI 说"任务完成",结束循环
            if (thought.contains("TASK_COMPLETE")) {
                return extractFinalAnswer(thought);
            }

            // 如果 AI 说"无法完成",返回失败
            if (thought.contains("CANNOT_COMPLETE")) {
                return "无法完成任务:" + extractReason(thought);
            }

            // Step 2: Act - 解析工具调用并执行
            String action = extractAction(thought);
            if (action != null) {
                ToolCallResult result = executeTool(action);
                observation = "工具执行结果: " + result.content();
                log.info("Step {}: 执行工具 {} -> {}", step + 1,
                    extractToolName(action), result.content().substring(0, 100));
            } else {
                // 无工具调用,直接生成答案
                return thought;
            }
        }

        return "任务超时,已执行最大步数 " + maxSteps;
    }

    private String think(String observation, String goal) {
        String prompt = String.format("""
            你是企业智能助手,正在帮助用户完成任务。

            用户目标:%s

            当前状态:
            %s

            请分析当前状态,决定下一步行动:
            1. 如果需要调用工具,使用以下格式:
               ACTION: tool_name
               ARGS: {"param1": "value1", "param2": "value2"}

            2. 如果任务已完成,回复:
               TASK_COMPLETE
               答案:<最终答案>

            3. 如果确定无法完成,回复:
               CANNOT_COMPLETE
               原因:<无法完成的原因>

            4. 如果需要用户提供更多信息,回复:
               NEED_INFO
               缺少:<缺少什么信息>
            """, goal, observation.isEmpty() ? "(初始状态)" : observation);

        return chatClient.prompt()
            .user(prompt)
            .options(ChatOptionsBuilder.builder().temperature(0.1).build())
            .call()
            .content();
    }

    private ToolCallResult executeTool(String action) {
        // 解析并执行工具(与 Tool Calling 部分相同)
        // ...
        return ToolCallResult.success("执行成功");
    }
}

七、ChatMemory:让 AI 记住上下文

7.1 为什么 Memory 决定 Agent 的智商

没有 Memory 的 AI 是「金鱼脑」——每轮对话都是独立的,上下文不连贯。Memory 让 AI 记住之前的对话历史,是多轮交互和复杂任务执行的基础。

@Service
public class MemoryChatService {

    private final ChatClient chatClient;

    // 方式1:InMemoryChatMemory(单机,开发测试用)
    public String chatWithInMemoryMemory(String sessionId, String message) {
        // 每个 session 有独立的内存
        var memory = new InMemoryChatMemory();

        // 添加用户消息到记忆
        memory.add(sessionId, new UserMessage(message));

        // 获取相关记忆(自动向量检索)
        List<ChatMemory.Message> history = memory.get(sessionId,
            MessageWindowChatMemoryMessageWindow.of(10));  // 最近10条

        // 构建带记忆的 Prompt
        StringContext prompt = buildPromptWithMemory(message, history);

        String response = chatClient.prompt()
            .user(prompt)
            .call()
            .content();

        // 将 AI 回答也加入记忆
        memory.add(sessionId, new AssistantMessage(response));

        return response;
    }

    // 方式2:TokenBufferChatMemory(按 Token 数限制,防止溢出)
    public String chatWithTokenLimit(String sessionId, String message,
            int maxTokens) {
        var memory = TokenBufferChatMemory.builder()
            .maxTokens(maxTokens)  // 最多保留 N 个 Token
            .build();

        return chatClient.prompt()
            .user(message)
            .advisors(new ChatMemoryAdvisor(memory))
            .call()
            .content();
    }

    // 方式3:持久化 Memory(生产环境)
    @Service
    public static class PersistentMemoryService {

        private final JdbcChatMemory jdbcChatMemory;
        private final ChatClient chatClient;

        public PersistentMemoryService(JdbcChatMemory jdbcChatMemory) {
            this.jdbcChatMemory = jdbcChatMemory;
        }

        public String chat(String sessionId, String message) {
            return chatClient.prompt()
                .user(message)
                .advisors(new ChatMemoryAdvisor(jdbcChatMemory))
                .call()
                .content();
        }

        /**
         * 清理会话记忆(GDPR 合规)
         */
        public void clearSessionMemory(String sessionId) {
            jdbcChatMemory.clear(sessionId);
        }

        /**
         * 搜索历史记忆(语义检索)
         */
        public List<String> searchMemory(String sessionId, String query) {
            return jdbcChatMemory.get(sessionId, Query.chat(query))
                .stream()
                .map(Object::toString)
                .toList();
        }
    }
}

7.2 Memory + RAG + Tool Calling 三件套实战

这是生产级 AI 应用的标准组合——Memory 记住对话历史、RAG 提供知识库检索、Tool Calling 执行实际操作:

@Service
@Slf4j
public class ProductionAgentService {

    private final ChatClient chatClient;
    private final McpToolRegistry toolRegistry;
    private final ChatMemory chatMemory;
    private final VectorStore vectorStore;

    /**
     * 生产级 Agent:三件套组合
     * Memory(对话记忆)+ RAG(知识检索)+ Tool Calling(工具执行)
     */
    public String productionAgentChat(String sessionId, String userMessage) {

        // 1. 检索相关知识(增强上下文)
        List<Document> knowledge = vectorStore.similaritySearch(
            userMessage, 3
        );
        String knowledgeContext = knowledge.stream()
            .map(d -> "【知识】" + d.getContent())
            .collect(Collectors.joining("\n\n"));

        // 2. 获取对话历史
        List<ChatMemory.Message> history = chatMemory.get(
            sessionId,
            MessageWindowChatMemoryMessageWindow.of(20)
        );
        String historyContext = buildHistoryContext(history);

        // 3. 构建增强 Prompt
        String prompt = String.format("""
            ## 对话历史
            %s

            ## 企业知识库检索结果
            %s

            ## 当前用户消息
            %s

            请基于以上信息回答用户问题。
            如果需要调用工具,使用以下格式:
            TOOL_CALL: 工具名称
            TOOL_ARGS: {"参数名": "参数值"}

            如果问题已解决,回答:
            完成:<最终答案>
            """,
            historyContext.isEmpty() ? "(无历史记录)" : historyContext,
            knowledgeContext.isEmpty() ? "(无相关知识)" : knowledgeContext,
            userMessage
        );

        // 4. 执行带工具的对话
        String response = chatClient.prompt()
            .user(prompt)
            .tools(toolRegistry.getAllTools())
            .call()
            .content();

        // 5. 更新对话记忆
        chatMemory.add(sessionId, new UserMessage(userMessage));
        chatMemory.add(sessionId, new AssistantMessage(response));

        return response;
    }

    private String buildHistoryContext(List<ChatMemory.Message> history) {
        if (history.isEmpty()) return "";
        return history.stream()
            .map(m -> {
                if (m instanceof UserMessage u) return "用户: " + u.getContent();
                if (m instanceof AssistantMessage a) return "助手: " + a.getContent();
                return m.toString();
            })
            .collect(Collectors.joining("\n"));
    }
}

八、RAG:企业知识库的工程化实现

8.1 企业 RAG 流水线全流程

@Service
@RequiredArgsConstructor
public class EnterpriseRagService {

    private final DocumentReader documentReader;
    private final TextSplitter textSplitter;
    private final EmbeddingModel embeddingModel;
    private final VectorStore vectorStore;
    private final ChatClient chatClient;

    /**
     * RAG 全流程:文档入库
     */
    @Transactional
    public int ingestDocument(String filePath) {
        // 1. 加载文档(支持 PDF/Word/Excel/Markdown/TXT)
        Resource resource = new FileSystemResource(filePath);
        List<Document> documents = documentReader.read(resource);

        // 2. 语义分块(关键步骤:分块质量直接影响检索效果)
        List<Document> chunks = textSplitter.split(documents);

        // 3. 向量化并入库
        vectorStore.add(chunks);

        log.info("文档入库完成:{} -> {} 个知识块", filePath, chunks.size());
        return chunks.size();
    }

    /**
     * RAG 全流程:问答检索
     */
    public String ragQuery(String question) {
        // 1. 向量化查询词
        Embedding queryEmbedding = embeddingModel.embed(
            new TextEmbeddingOptions(question)
        );

        // 2. 向量相似度检索
        List<Document> relevantDocs = vectorStore.similaritySearch(
            SearchRequest.builder()
                .query(question)
                .topK(5)
                .similarityThreshold(0.7)  // 相似度阈值过滤
                .build()
        );

        if (relevantDocs.isEmpty()) {
            return "知识库中未找到相关内容,请换个问题或联系管理员补充知识库。";
        }

        // 3. 构建上下文
        String context = relevantDocs.stream()
            .map(doc -> {
                String source = doc.getMetadata().getOrDefault("source", "未知");
                return "【来源:" + source + "】\n" + doc.getContent();
            })
            .collect(Collectors.joining("\n\n---\n\n"));

        // 4. 生成回答
        String prompt = String.format("""
            你是一个企业知识库问答助手。请严格基于以下检索到的内容回答问题。
            如果检索内容中没有答案,明确告知用户,不要编造。

            【检索到的内容】
            %s

            【用户问题】
            %s

            【回答要求】
            1. 优先使用检索内容中的原文
            2. 在回答末尾标注信息来源
            3. 如果内容不足,说明"以下信息可能不完整"
            """, context, question);

        String answer = chatClient.prompt()
            .user(prompt)
            .options(ChatOptionsBuilder.builder().temperature(0.1).build())
            .call()
            .content();

        // 5. 添加来源信息
        String sources = relevantDocs.stream()
            .map(doc -> "- " + doc.getMetadata().getOrDefault("source", "未知"))
            .distinct()
            .collect(Collectors.joining("\n"));

        return answer + "\n\n**参考来源:**\n" + sources;
    }

    /**
     * 批量文档入库(虚拟线程加速)
     */
    @Async
    public CompletableFuture<Integer> batchIngest(List<String> filePaths) {
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            List<CompletableFuture<Integer>> futures = filePaths.stream()
                .map(path -> CompletableFuture.supplyAsync(
                    () -> ingestDocument(path), executor))
                .toList();

            int total = futures.stream()
                .mapToInt(f -> f.join())
                .sum();

            return CompletableFuture.completedFuture(total);
        }
    }
}

8.2 pgvector 生产级配置

@Configuration
public class VectorStoreConfig {

    @Bean
    public VectorStore vectorStore(JdbcClient jdbcClient,
            EmbeddingModel embeddingModel) {
        return PgVectorStore.builder(jdbcClient, embeddingModel)
            .schemaName("public")
            .distanceType(VectorStoreOptions.DistanceType.COSINE_DISTANCE)
            .indexType(PgVectorStoreOptions.IndexType.HNSW)
            .build();
    }
}

九、Spring AI Alibaba:通义千问集成

/**
 * 通义千问专用 ChatClient
 */
@Service
public class QwenChatService {

    private final ChatClient qwenChatClient;

    public QwenChatService(ChatModel dashscopeChatModel) {
        this.qwenChatClient = ChatClient.create(dashscopeChatModel);
    }

    /**
     * 通义千问 Plus
     */
    public String chatWithQwenPlus(String message) {
        return qwenChatClient.prompt()
            .user(message)
            .options(ChatOptionsBuilder.builder()
                .model("qwen-plus")
                .temperature(0.7)
                .maxTokens(4096)
                .build())
            .call()
            .content();
    }

    /**
     * 通义千问 VL(视觉语言模型):图片理解
     */
    public String chatWithImage(String message, byte[] imageData) {
        return qwenChatClient.prompt()
            .user(u -> u
                .text(message)
                .media(MimeTypeUtils.IMAGE_PNG, imageData))
            .options(ChatOptionsBuilder.builder()
                .model("qwen-vl-plus")
                .build())
            .call()
            .content();
    }

    /**
     * 通义万相(图像生成)
     */
    public byte[] generateImage(String prompt) {
        // Spring AI Alibaba 提供了图像生成 API
        ImagePrompt imagePrompt = new ImagePrompt(prompt);
        ImageResponse response = dashscopeImageModel.call(imagePrompt);
        return response.getResult().getImage().getData();
    }
}

十、生产级监控与治理

10.1 全链路可观测性

@Configuration
public class AiObservabilityConfig {

    /**
     * AI 调用埋点:记录每次 AI 调用的关键指标
     */
    @Bean
    public ChatClientMetrics chatClientMetrics() {
        return new ChatClientMetrics() {
            @Override
            public void recordTokenUsage(String model, int inputTokens,
                    int outputTokens, long durationMs) {
                MeterRegistry registry = this.getMeterRegistry();
                registry.counter("ai.tokens.usage",
                    "model", model, "type", "input").increment(inputTokens);
                registry.counter("ai.tokens.usage",
                    "model", model, "type", "output").increment(outputTokens);
                registry.timer("ai.call.duration",
                    "model", model).record(durationMs, TimeUnit.MILLISECONDS);
            }

            private MeterRegistry getMeterRegistry() {
                return new SimpleMeterRegistry();
            }
        };
    }

    /**
     * 自定义健康检查:AI 服务可用性探测
     */
    @Component
    public static class AiHealthIndicator
            implements ReactiveHealthIndicator {

        private final ChatClient chatClient;

        public AiHealthIndicator(ChatClient chatClient) {
            this.chatClient = chatClient;
        }

        @Override
        public Mono<Health> health() {
            return Mono.fromCallable(() -> {
                String response = chatClient.prompt()
                    .user("ping")
                    .options(ChatOptionsBuilder.builder()
                        .temperature(0.0)
                        .maxTokens(5)
                        .build())
                    .call()
                    .content();

                if (response != null && !response.isEmpty()) {
                    return Health.up()
                        .withDetail("model", "gpt-4o")
                        .withDetail("latency", "OK")
                        .build();
                }
                return Health.down().withDetail("error", "Empty response").build();
            }).onErrorResume(e ->
                Mono.just(Health.down()
                    .withDetail("error", e.getMessage()).build())
            );
        }
    }
}

10.2 敏感信息过滤

/**
 * Prompt 注入防御:过滤用户输入中的恶意 Prompt
 */
@Service
@Slf4j
public class PromptSecurityService {

    private static final List<Pattern> INJECTION_PATTERNS = List.of(
        Pattern.compile("ignore previous instructions",
            Pattern.CASE_INSENSITIVE),
        Pattern.compile("disregard (your|all) (instructions|rules)",
            Pattern.CASE_INSENSITIVE),
        Pattern.compile("you (are|should act as) a different",
            Pattern.CASE_INSENSITIVE),
        Pattern.compile("system prompt.*leak",
            Pattern.CASE_INSENSITIVE)
    );

    public String sanitizeUserInput(String input) {
        if (input == null) return "";

        String sanitized = input;
        for (Pattern pattern : INJECTION_PATTERNS) {
            if (pattern.matcher(input).find()) {
                log.warn("检测到 Prompt 注入尝试: {}", input.substring(0, 100));
                sanitized = sanitized.replaceAll(
                    pattern.pattern(), "[内容已过滤]");
            }
        }

        // 长度限制
        if (sanitized.length() > 10000) {
            sanitized = sanitized.substring(0, 10000);
            log.warn("用户输入超长,已截断至 10000 字符");
        }

        return sanitized;
    }
}

十一、总结:Java AI 开发的工程化路线图

11.1 从入门到生产的四阶段路径

阶段1:Hello World(1天)
  ChatClient 基础对话 → Prompt 模板 → 流式响应
  → 能跑起来,感受到 AI 的能力

阶段2:Tool Calling(1周)
  定义工具 → 注册到 ChatClient → 工具调用链
  → AI 能真正「干活」,查订单、查库存

阶段3:RAG + Memory(2周)
  文档加载 → 向量入库 → 检索增强 → 对话记忆
  → 企业知识库问答,多轮对话

阶段4:MCP + Agent(1个月)
  MCP 客户端/服务端 → Agent 框架 → 自主决策
  → 完整的企业级 AI 应用,可上线生产

11.2 核心 API 速查表

能力关键 API重要参数
对话ChatClient.prompt().user().call().content()model, temperature
工具调用@Tool + .tools()toolCallTimeout
MCP 客户端SyncMcpToolCallbackProviderSSE/STDIO 传输
MCP 服务端@McpFunction + McpServer.builder()STDIO/SSE
AgentAgent.builder().tools().build()maxIterations
MemoryChatMemoryAdvisormaxTokens
RAGVectorStore.similaritySearch()topK, threshold
Structured OutputChatClient.prompt().advisor()outputType
流式响应.stream().content()-

11.3 2026 年 Spring AI 生态展望

Spring AI 正在成为 Java 生态的 AI 开发标准。2.0 版本将带来:

  • 虚拟线程原生支持:AI 服务全异步化,并发能力再上一个台阶
  • 下一代 Memory 管理:持久化、可搜索、跨会话共享的智能记忆
  • 增强的 MCP 支持:MCP 将成为 AI 应用间互操作的事实标准
  • 多模态原生集成:图像、音频、视频处理统一 API

对于 Java 开发者来说,现在学习 Spring AI 就是抢占企业 AI 市场的制高点。无论你是做企业内部 AI 平台、还是 SaaS AI 产品,Spring AI 都是绕不开的技术选型。

参考资料

推荐文章

支付宝批量转账
2024-11-18 20:26:17 +0800 CST
Golang 中应该知道的 defer 知识
2024-11-18 13:18:56 +0800 CST
平面设计常用尺寸
2024-11-19 02:20:22 +0800 CST
向满屏的 Import 语句说再见!
2024-11-18 12:20:51 +0800 CST
Go语言中实现RSA加密与解密
2024-11-18 01:49:30 +0800 CST
智慧加水系统
2024-11-19 06:33:36 +0800 CST
git使用笔记
2024-11-18 18:17:44 +0800 CST
Vue3 vue-office 插件实现 Word 预览
2024-11-19 02:19:34 +0800 CST
程序员茄子在线接单