Java + AI 实战:Spring AI 从入门到企业级落地

Java + AI 实战:Spring AI 从入门到企业级落地

本文档面向 Java 开发者,手把手教你用 Spring AI 框架集成大语言模型,构建生产级 AI 应用。

全程可实操,代码可复制,拿来即用。


目录

  1. 背景与趋势
  2. [技术选型:为什么选 Spring AI?](#技术选型:为什么选 Spring AI?)
  3. 环境准备
  4. [快速入门:第一个 AI 聊天接口](#快速入门:第一个 AI 聊天接口)
  5. 核心功能实操
    • 5.1 Prompt Template(提示词模板)
    • 5.2 Output Parser(结构化输出)
    • 5.3 多模型切换(OpenAI / DeepSeek / 本地 Ollama)
    • 5.4 Function Calling(函数调用)
    • 5.5 RAG 检索增强生成
  6. [完整项目:AI 智能客服助手](#完整项目:AI 智能客服助手)
  7. 生产环境部署建议
  8. 常见问题与排错
  9. 总结与资源

1. 背景与趋势

2024-2025 年,大语言模型(LLM)已经从"玩具"走向"生产力工具"。企业级 AI 集成面临三大痛点:

痛点 说明
多模型切换 OpenAI、DeepSeek、通义千问、本地模型,业务需要灵活切换
结构化输出 AI 返回的是文本,业务需要 JSON、表格等结构化数据
检索增强(RAG) 让 AI 基于自己的知识库回答,而非胡编乱造

Spring AI 正是为了解决这些问题而生------它是 Spring 官方推出的 AI 框架,类比于"JDBC 之于数据库,Spring AI 之于大模型"。


2. 技术选型:为什么选 Spring AI?

维度 Spring AI LangChain4j Semantic Kernel (微软)
生态整合 ⭐⭐⭐⭐⭐ Spring Boot 原生 ⭐⭐⭐⭐ Spring 友好 ⭐⭐⭐ 偏向 .NET
模型支持 OpenAI、DeepSeek、Ollama、通义、Azure 等 20+ OpenAI、Azure、Ollama OpenAI、Azure
RAG 能力 内置向量数据库支持 内置 需要额外配置
学习成本 ⭐⭐⭐⭐⭐ 熟悉 Spring 即可 ⭐⭐⭐⭐ ⭐⭐⭐
生产成熟度 Spring 官方,稳定迭代 社区活跃 微软背书

结论: 如果你团队以 Spring Boot 为主,选 Spring AI 是最平滑的路径。


3. 环境准备

3.1 基础环境

工具 版本要求 说明
JDK 17+ Spring Boot 3.x 要求
Maven 3.8+ 或 Gradle 8.x
IDE IntelliJ IDEA / VS Code 推荐 IDEA Ultimate
Spring Boot 3.2+ 配合 Spring AI 1.0+

3.2 获取 API Key

3.3 创建 Spring Boot 项目

访问 https://start.spring.io/,填入:

  • Project: Maven
  • Language: Java
  • Spring Boot: 3.2.x 或 3.3.x
  • Dependencies: Spring Web + Spring AI OpenAI

或者直接复制以下 pom.xml

xml 复制代码
<?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
         https://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.2.5</version>
        <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>spring-ai-demo</artifactId>
    <version>1.0.0</version>
    <name>spring-ai-demo</name>
    <description>Java + AI 实战项目</description>

    <properties>
        <java.version>17</java.version>
        <spring-ai.version>1.0.0-M5</spring-ai.version>
    </properties>

    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring AI OpenAI -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        </dependency>

        <!-- Spring AI Ollama(本地模型) -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- Lombok(可选,减少样板代码) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

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

    <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>
        </dependencies>
    </dependencyManagement>

    <repositories>
        <!-- Spring AI 里程碑仓库 -->
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

3.4 配置文件

application.yml

yaml 复制代码
spring:
  application:
    name: spring-ai-demo

  ai:
    openai:
      api-key: ${OPENAI_API_KEY:sk-your-key-here}
      chat:
        options:
          model: gpt-4o-mini
          temperature: 0.7
    # 可选:Ollama 本地模型
    ollama:
      base-url: http://localhost:11434
      chat:
        options:
          model: qwen2.5:7b

server:
  port: 8080

4. 快速入门:第一个 AI 聊天接口

4.1 主启动类

java 复制代码
package com.example.ai;

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

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

4.2 Controller --- 最简单的 AI 对话

java 复制代码
package com.example.ai.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/chat")
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    /**
     * 简单对话接口
     * GET /api/chat?message=你好
     */
    @GetMapping
    public String chat(@RequestParam(defaultValue = "你好,请介绍一下你自己") String message) {
        return chatClient.prompt()
                .user(message)
                .call()
                .content();
    }
}

4.3 测试

启动项目后访问:

bash 复制代码
curl http://localhost:8080/api/chat?message=用Java写一个快速排序算法

返回示例:

复制代码
以下是用Java实现的快速排序算法:

public class QuickSort {
    public static void quickSort(int[] arr, int low, int high) {
        if (low < high) {
            int pi = partition(arr, low, high);
            quickSort(arr, low, pi - 1);
            quickSort(arr, pi + 1, high);
        }
    }
    // ...
}

第一个 AI 接口完成!前后端分离架构,前端随便调。


5. 核心功能实操

5.1 Prompt Template(提示词模板)

硬编码提示词不够灵活,用模板来管理。

java 复制代码
package com.example.ai.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/api/template")
public class PromptTemplateController {

    private final ChatClient chatClient;

    public PromptTemplateController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    /**
     * 模板化提示词
     * GET /api/template/translate?text=Hello World&language=中文
     */
    @GetMapping("/translate")
    public String translate(
            @RequestParam String text,
            @RequestParam(defaultValue = "中文") String language) {

        String template = """
                你是专业的翻译助手。
                请将以下文本翻译成{language},只返回翻译结果,不要额外说明。
                
                文本:{text}
                """;

        PromptTemplate promptTemplate = new PromptTemplate(template);
        Map<String, Object> params = Map.of(
                "text", text,
                "language", language
        );

        return chatClient.prompt(promptTemplate.create(params))
                .call()
                .content();
    }
}

测试:

复制代码
GET /api/template/translate?text=Good morning&language=法语
→ "Bonjour"

5.2 Output Parser(结构化输出)

AI 返回的文本如果要做下游处理,需要转成结构化数据。

方式一:自定义解析
java 复制代码
package com.example.ai.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.*;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

@RestController
@RequestMapping("/api/parse")
public class OutputParserController {

    private final ChatClient chatClient;

    public OutputParserController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    /**
     * 让 AI 返回 JSON 格式
     * GET /api/parse/book?topic=Java并发编程
     */
    @GetMapping("/book")
    public String recommendBook(@RequestParam String topic) {
        String response = chatClient.prompt()
                .user("""
                        推荐一本关于'%s'的书籍,按以下JSON格式返回:
                        {
                            "title": "书名",
                            "author": "作者",
                            "rating": 评分,
                            "reason": "推荐理由"
                        }
                        只返回JSON,不要其他内容。
                        """.formatted(topic))
                .call()
                .content();

        // 提取 JSON 部分(防止 AI 有多余文字)
        Pattern pattern = Pattern.compile("\\{.*\\}", Pattern.DOTALL);
        Matcher matcher = pattern.matcher(response);
        if (matcher.find()) {
            return matcher.group();
        }
        return response;
    }
}
方式二:Spring AI 内置 BeanOutputParser(推荐)
java 复制代码
package com.example.ai.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/parse")
public class BeanOutputController {

    private final ChatClient chatClient;

    public BeanOutputController(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    // 定义输出结构
    public record BookRecommendation(
            String title,
            String author,
            double rating,
            String reason
    ) {}

    /**
     * 结构化输出 ------ 自动解析为 Java 对象
     * GET /api/parse/book2?topic=微服务架构
     */
    @GetMapping("/book2")
    public BookRecommendation recommendBook2(@RequestParam String topic) {
        var converter = new BeanOutputConverter<>(BookRecommendation.class);

        String json = chatClient.prompt()
                .user("推荐一本关于'" + topic + "'的书籍")
                .user(converter.getFormatInstruction()) // 自动注入格式指令
                .call()
                .content();

        return converter.convert(json); // 自动解析为 Java 对象
    }
}

访问结果(自动返回 JSON):

json 复制代码
{
    "title": "微服务架构设计模式",
    "author": "Chris Richardson",
    "rating": 4.7,
    "reason": "全面覆盖微服务设计、部署、治理的经典之作"
}

5.3 多模型切换(OpenAI / DeepSeek / Ollama)

生产环境中你不可能只绑死一个模型。Spring AI 通过 @Qualifier 实现多模型共存。

java 复制代码
package com.example.ai.config;

import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AiModelConfig {

    @Value("${spring.ai.openai.api-key}")
    private String openaiApiKey;

    /**
     * OpenAI 模型 Bean
     */
    @Bean("openAiChatModel")
    public ChatModel openAiChatModel() {
        var api = OpenAiApi.builder()
                .apiKey(openaiApiKey)
                .build();
        return OpenAiChatModel.builder()
                .openAiApi(api)
                .defaultOptions(OpenAiChatOptions.builder()
                        .model("gpt-4o-mini")
                        .temperature(0.7)
                        .build())
                .build();
    }

    /**
     * DeepSeek 模型 Bean(兼容 OpenAI SDK)
     */
    @Bean("deepSeekChatModel")
    public ChatModel deepSeekChatModel() {
        var api = OpenAiApi.builder()
                .apiKey(System.getenv("DEEPSEEK_API_KEY"))
                .baseUrl("https://api.deepseek.com")
                .build();
        return OpenAiChatModel.builder()
                .openAiApi(api)
                .defaultOptions(OpenAiChatOptions.builder()
                        .model("deepseek-chat")
                        .temperature(0.7)
                        .build())
                .build();
    }

    /**
     * Ollama 本地模型 Bean(免费,无需 Key)
     */
    @Bean("ollamaChatModel")
    public ChatModel ollamaChatModel() {
        var api = OllamaApi.builder()
                .baseUrl("http://localhost:11434")
                .build();
        return OllamaChatModel.builder()
                .ollamaApi(api)
                .defaultOptions(OllamaOptions.builder()
                        .model("qwen2.5:7b")
                        .temperature(0.7)
                        .build())
                .build();
    }
}

使用多模型:

java 复制代码
@RestController
@RequestMapping("/api/chat")
public class MultiModelController {

    private final ChatModel openAiModel;
    private final ChatModel deepSeekModel;
    private final ChatModel ollamaModel;

    public MultiModelController(
            @Qualifier("openAiChatModel") ChatModel openAiModel,
            @Qualifier("deepSeekChatModel") ChatModel deepSeekModel,
            @Qualifier("ollamaChatModel") ChatModel ollamaModel) {
        this.openAiModel = openAiModel;
        this.deepSeekModel = deepSeekModel;
        this.ollamaModel = ollamaModel;
    }

    /**
     * 切换模型
     * GET /api/chat/switch?model=openai&message=你好
     * model 可选: openai / deepseek / ollama
     */
    @GetMapping("/switch")
    public String switchChat(
            @RequestParam(defaultValue = "openai") String model,
            @RequestParam String message) {

        ChatModel chatModel = switch (model) {
            case "deepseek" -> deepSeekModel;
            case "ollama" -> ollamaModel;
            default -> openAiModel;
        };

        return chatModel.call(message);
    }
}

5.4 Function Calling(让 AI 调用你的 Java 方法)

这是最实用的功能------让 AI 能查询数据库、调用 API、执行业务逻辑。

定义工具函数
java 复制代码
package com.example.ai.tool;

import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.function.Function;

@Component
public class WeatherService implements Function<WeatherService.Request, WeatherService.Response> {

    public record Request(String city) {}
    public record Response(String city, String weather, double temperature, String advice) {}

    @Override
    public Response apply(Request request) {
        // 实际项目中这里调用真实天气 API
        return new Response(
                request.city(),
                "晴",
                24.5,
                "天气不错,适合外出活动 🌤️"
        );
    }
}
注册 Function Calling
java 复制代码
package com.example.ai.controller;

import com.example.ai.tool.WeatherService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/fc")
public class FunctionCallingController {

    private final ChatClient chatClient;

    public FunctionCallingController(ChatClient.Builder builder, WeatherService weatherService) {
        this.chatClient = builder
                .defaultTools(weatherService) // 注册工具
                .build();
    }

    /**
     * AI 会自动判断何时调用天气工具
     * GET /api/fc/weather?question=北京今天天气怎么样?
     */
    @GetMapping("/weather")
    public String askWeather(@RequestParam String question) {
        return chatClient.prompt()
                .user(question)
                .call()
                .content();
    }
}

效果: 用户问"北京今天天气怎么样?",AI 自动调用 WeatherService 获取真实天气数据,再组织成自然语言返回。

5.5 RAG 检索增强生成

RAG(Retrieval-Augmented Generation)让 AI 基于你自己的文档回答问题。

5.5.1 添加依赖
xml 复制代码
<!-- 向量数据库(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-openai-spring-boot-starter</artifactId>
</dependency>
5.5.2 文档入库
java 复制代码
package com.example.ai.service;

import org.springframework.ai.document.Document;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
import java.util.List;

@Service
public class DocumentIngestionService {

    private final VectorStore vectorStore;
    private final ResourceLoader resourceLoader;

    public DocumentIngestionService(VectorStore vectorStore, ResourceLoader resourceLoader) {
        this.vectorStore = vectorStore;
        this.resourceLoader = resourceLoader;
    }

    /**
     * 启动时加载知识库文档
     */
    @PostConstruct
    public void loadDocuments() {
        // 读取 classpath 下的文档
        Resource resource = resourceLoader.getResource("classpath:docs/knowledge-base.txt");
        TextReader reader = new TextReader(resource);
        List<Document> documents = reader.get();

        // 文本分块(Chunk)
        TokenTextSplitter splitter = new TokenTextSplitter();
        List<Document> chunks = splitter.apply(documents);

        // 向量化并存入向量数据库
        vectorStore.add(chunks);

        System.out.println("✅ 知识库加载完成,共 " + chunks.size() + " 个文档块");
    }
}
5.5.3 RAG 问答
java 复制代码
package com.example.ai.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.web.bind.annotation.*;

import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/rag")
public class RagController {

    private final ChatClient chatClient;
    private final VectorStore vectorStore;

    public RagController(ChatClient.Builder builder, VectorStore vectorStore) {
        this.chatClient = builder.build();
        this.vectorStore = vectorStore;
    }

    /**
     * 基于知识库的问答
     * GET /api/rag/ask?question=我们的产品支持哪些支付方式?
     */
    @GetMapping("/ask")
    public String ask(@RequestParam String question) {
        // 1. 在知识库中检索相关内容
        var results = vectorStore.similaritySearch(
                SearchRequest.query(question).withTopK(3)
        );

        // 2. 构建上下文
        String context = results.stream()
                .map(doc -> doc.getText())
                .collect(Collectors.joining("\n---\n"));

        // 3. 让 AI 基于上下文回答
        return chatClient.prompt()
                .user("""
                        请基于以下知识库内容回答问题。
                        如果知识库中没有相关信息,请如实说"知识库中没有相关记载"。
                        
                        知识库:
                        %s
                        
                        问题:%s
                        """.formatted(context, question))
                .call()
                .content();
    }
}

6. 完整项目:AI 智能客服助手

将以上技术整合为一个完整的 AI 智能客服系统。

6.1 项目结构

复制代码
spring-ai-demo/
├── src/main/java/com/example/ai/
│   ├── SpringAiDemoApplication.java
│   ├── config/
│   │   └── AiModelConfig.java              # 多模型配置
│   ├── controller/
│   │   ├── ChatController.java             # 基础聊天
│   │   ├── PromptTemplateController.java   # 提示词模板
│   │   ├── OutputParserController.java     # 结构化输出
│   │   ├── MultiModelController.java       # 多模型切换
│   │   ├── FunctionCallingController.java  # 函数调用
│   │   └── RagController.java              # RAG 问答
│   ├── service/
│   │   ├── CustomerService.java            # 客服业务逻辑
│   │   └── DocumentIngestionService.java   # 知识库加载
│   └── tool/
│       ├── WeatherService.java             # 天气查询工具
│       └── OrderService.java              # 订单查询工具
├── src/main/resources/
│   ├── application.yml
│   ├── docs/
│   │   └── knowledge-base.txt             # 知识库文档
│   └── prompts/
│       └── customer-service.st            # 客服提示词模板
└── pom.xml

6.2 智能客服核心逻辑

java 复制代码
package com.example.ai.service;

import com.example.ai.tool.OrderService;
import com.example.ai.tool.WeatherService;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.stereotype.Service;

import java.util.stream.Collectors;

@Service
public class CustomerService {

    private final ChatClient chatClient;
    private final VectorStore vectorStore;

    public CustomerService(
            ChatClient.Builder builder,
            VectorStore vectorStore,
            WeatherService weatherService,
            OrderService orderService) {
        this.chatClient = builder
                .defaultTools(weatherService, orderService) // 注册工具
                .build();
        this.vectorStore = vectorStore;
    }

    /**
     * 智能客服统一入口
     */
    public String answer(String question, String userId) {
        // 1. 检索知识库
        var docs = vectorStore.similaritySearch(
                SearchRequest.query(question).withTopK(3)
        );
        String context = docs.stream()
                .map(d -> d.getText())
                .collect(Collectors.joining("\n---\n"));

        // 2. 调用 AI(提示词中注入上下文 + 用户信息)
        return chatClient.prompt()
                .user("""
                        你是某电商平台的智能客服助手。
                        请基于以下知识库和工具回答用户问题。
                        
                        知识库:
                        %s
                        
                        用户ID:%s
                        用户问题:%s
                        
                        要求:
                        - 回答友好、专业
                        - 如果知识库中有答案,优先使用知识库
                        - 如果需要查订单,调用订单查询工具
                        - 如果不知道,不要编造
                        """.formatted(context, userId, question))
                .call()
                .content();
    }
}

6.3 订单查询工具

java 复制代码
package com.example.ai.tool;

import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

@Component
public class OrderService implements Function<OrderService.Request, OrderService.Response> {

    // 模拟订单数据
    private static final Map<String, OrderInfo> ORDERS = new ConcurrentHashMap<>();

    static {
        ORDERS.put("ORD001", new OrderInfo("ORD001", "已发货", "顺丰快递", "SF1234567890"));
        ORDERS.put("ORD002", new OrderInfo("ORD002", "配送中", "中通快递", "ZT9876543210"));
        ORDERS.put("ORD003", new OrderInfo("ORD003", "已完成", "圆通快递", "YT5555555555"));
    }

    public record Request(String orderId) {}
    public record Response(String orderId, String status, String courier, String trackingNo) {}

    @Override
    public Response apply(Request request) {
        OrderInfo info = ORDERS.get(request.orderId());
        if (info == null) {
            return new Response(request.orderId(), "未找到", "", "");
        }
        return new Response(info.orderId(), info.status(), info.courier(), info.trackingNo());
    }

    private record OrderInfo(String orderId, String status, String courier, String trackingNo) {}
}

6.4 前端对接示例(HTML)

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>AI 智能客服</title>
    <style>
        body { font-family: sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
        #chat-box { height: 400px; border: 1px solid #ccc; padding: 10px; overflow-y: auto; margin-bottom: 10px; background: #f9f9f9; }
        .msg { margin: 8px 0; padding: 8px 12px; border-radius: 8px; }
        .user { background: #007aff; color: white; text-align: right; margin-left: 30%; }
        .ai { background: white; border: 1px solid #ddd; text-align: left; margin-right: 30%; }
        #input-area { display: flex; gap: 8px; }
        #input-area input { flex: 1; padding: 8px; border: 1px solid #ccc; border-radius: 4px; }
        #input-area button { padding: 8px 20px; background: #007aff; color: white; border: none; border-radius: 4px; cursor: pointer; }
    </style>
</head>
<body>
    <h2>🤖 AI 智能客服</h2>
    <div id="chat-box">
        <div class="msg ai">您好!我是智能客服助手,请问有什么可以帮助您的?</div>
    </div>
    <div id="input-area">
        <input type="text" id="message" placeholder="请输入您的问题..." onkeypress="if(event.key==='Enter') send()">
        <button onclick="send()">发送</button>
    </div>

    <script>
        async function send() {
            const input = document.getElementById('message');
            const msg = input.value.trim();
            if (!msg) return;

            // 显示用户消息
            appendMsg('user', msg);
            input.value = '';

            // 调用后端 API
            try {
                const res = await fetch(`/api/chat?message=${encodeURIComponent(msg)}`);
                const data = await res.text();
                appendMsg('ai', data);
            } catch (err) {
                appendMsg('ai', '😅 网络异常,请稍后重试');
            }
        }

        function appendMsg(role, text) {
            const box = document.getElementById('chat-box');
            const div = document.createElement('div');
            div.className = 'msg ' + role;
            div.textContent = text;
            box.appendChild(div);
            box.scrollTop = box.scrollHeight;
        }
    </script>
</body>
</html>

7. 生产环境部署建议

7.1 配置文件分离

yaml 复制代码
# application-prod.yml
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}  # 通过环境变量注入,不要写死
    ollama:
      base-url: http://ollama-service:11434  # Docker 内部服务名

启动:

bash 复制代码
java -jar spring-ai-demo.jar --spring.profiles.active=prod

7.2 Docker 部署

dockerfile 复制代码
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/spring-ai-demo.jar app.jar
EXPOSE 8080
ENV SPRING_PROFILES_ACTIVE=prod
ENTRYPOINT ["java", "-jar", "app.jar"]
yaml 复制代码
# docker-compose.yml
version: '3.8'
services:
  ai-app:
    build: .
    ports:
      - "8080:8080"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - SPRING_PROFILES_ACTIVE=prod
    depends_on:
      - ollama
      - postgres

  ollama:
    image: ollama/ollama
    volumes:
      - ollama-data:/root/.ollama
    ports:
      - "11434:11434"

  postgres:
    image: pgvector/pgvector:pg16
    environment:
      POSTGRES_DB: vectordb
      POSTGRES_USER: ai
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  ollama-data:
  pgdata:

7.3 关键生产建议

建议项 说明
API Key 管理 使用 Vault / K8s Secret / 环境变量,绝不硬编码
限流 接入 Sentinel / Resilience4j,防止 API 调用超预算
缓存 高频问题的回答可以 Redis 缓存,减少 API 调用
监控 记录每次 AI 调用的耗时、Token 消耗、成功率
回退策略 模型不可用时降级到本地小模型或固定回复
内容安全 对 AI 输出做敏感词过滤、内容审核

8. 常见问题与排错

Q1:启动报错 "No bean named 'chatModel' available"

原因: 没有自动配置 ChatModel。

解决: 检查 pom.xml 是否有 Spring AI Starter 依赖,以及 application.ymlspring.ai.openai.api-key 是否正确配置。

Q2:中文乱码

解决: 确保 application.yml 中无 BOM,JVM 参数加 -Dfile.encoding=UTF-8

Q3:调用 OpenAI 超时

yaml 复制代码
spring:
  ai:
    openai:
      client:
        connect-timeout: 30s
        read-timeout: 60s

Q4:Ollama 连接被拒绝

解决: 确保 Ollama 已启动并监听正确端口:

bash 复制代码
ollama serve
curl http://localhost:11434/api/tags

Q5:Token 消耗太快

优化建议:

  • 使用更小的模型(gpt-4o-mini 代替 gpt-4o
  • 设置 maxTokens 限制输出长度
  • 使用缓存减少重复请求
  • 精简 System Prompt

9. 总结与资源

学完本文,你已掌握:

  • ✅ Spring AI 项目搭建与配置
  • ✅ 基础聊天接口(ChatClient)
  • ✅ 提示词模板(PromptTemplate)
  • ✅ 结构化输出(OutputParser / BeanOutputConverter)
  • ✅ 多模型切换(OpenAI / DeepSeek / Ollama)
  • ✅ 函数调用(Function Calling)
  • ✅ RAG 检索增强生成
  • ✅ 完整项目:AI 智能客服
  • ✅ 生产环境部署

推荐资源

资源 链接
Spring AI 官方文档 https://docs.spring.io/spring-ai/reference/
Spring AI GitHub https://github.com/spring-projects/spring-ai
Ollama 模型库 https://ollama.com/library
DeepSeek 平台 https://platform.deepseek.com/
PGVector 项目 https://github.com/pgvector/pgvector

下一步学习

  1. 接入 多模态(图片理解、语音交互)
  2. 集成 MCP 协议,让 AI 能操作更多外部工具
  3. 构建 AI Agent,支持多轮自主决策
  4. 实现 流式输出(SSE),提升用户体验

写在最后:

AI 不是银弹,但结合业务场景的 AI 应用一定是未来的核心竞争力。

本文所有代码均可直接运行,建议打开 IDE 边看边敲。

如果在实操中遇到问题,欢迎留言交流!


*本文由 AI 辅助生成

相关推荐
Raink老师1 小时前
【AI面试临阵磨枪-69】如何设计一个支持百万级工具的 Agent 系统?如何快速路由与选择工具?
人工智能·面试·职场和发展
oort1232 小时前
My Name:开发者部署平台OORT.sh—— AI时代的开发者部署平台,是Vibe Coding闺蜜
人工智能
Raink老师2 小时前
【AI面试临阵磨枪-77】音视频 + AI:实时字幕、翻译、降噪、虚拟人、多模态对话
人工智能·面试·音视频
Agent手记2 小时前
跨境电商如何用AI Agent自动运营多平台店铺?企业级「龙虾」矩阵智能体全流程落地指南
大数据·人工智能·ai·矩阵
DisonTangor2 小时前
【字节拥抱开源】Lance: 多任务协同的统一多模态建模
人工智能·ai作画·开源·aigc
冬奇Lab2 小时前
Agent系列(七):知识库集成——Agent 调用 RAG 的正确姿势
人工智能·agent
ITyunwei09872 小时前
主流 SaaS 工单系统对比
运维·服务器·人工智能
ZhengEnCi2 小时前
01-如何监听接口调用情况?
java·spring boot·后端