Java SpringBoot 调用大模型 AI 构建智能应用实战指南

一、基础知识

(一)Java基础

Java是一种广泛使用的高级编程语言,具有简单、面向对象、分布式、解释型、健壮、安全、架构中立、可移植、高性能、多线程和动态等特点。在构建基于Spring Boot的AI应用之前,您需要具备扎实的Java基础。

  1. 基本语法
  • 数据类型:了解Java的基本数据类型,包括整数、浮点数、布尔值、字符等,以及它们的存储范围和特点。例如,int类型用于表示整数,double类型用于表示浮点数。

  • 变量与常量:掌握变量的声明和初始化,以及如何使用final关键字声明常量。例如:

    int age = 25; // 变量声明与初始化
    final double PI = 3.14159; // 常量声明
    
  • 运算符:熟悉各种运算符,如算术运算符(+-*/%)、赋值运算符(=+=-=等)、关系运算符(><==等)和逻辑运算符(&&||!)。

  • 流程控制语句:掌握ifelseswitch等条件语句,以及forwhiledo-while等循环语句。例如:

    if (age >= 18) {
        System.out.println("成年人");
    } else {
        System.out.println("未成年人");
    }
    
    for (int i = 0; i < 10; i++) {
        System.out.println(i);
    }
    
  1. 面向对象编程
  • 类与对象:理解类是对象的模板,对象是类的实例。掌握如何定义一个类,并创建对象。例如:

    public class Person {
        String name;
        int age;
    
        public void sayHello() {
            System.out.println("Hello, my name is " + name);
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Person person = new Person();
            person.name = "Alice";
            person.age = 30;
            person.sayHello();
        }
    }
    
  • 继承:了解继承的基本概念,子类可以继承父类的属性和方法。使用extends关键字实现继承。例如:

    public class Student extends Person {
        String school;
    
        public void study() {
            System.out.println("I am studying at " + school);
        }
    }
    
  • 多态:多态是指同一个接口可以被不同的实例实现。通过方法重载和方法覆盖实现多态。例如:

    public class Animal {
        public void makeSound() {
            System.out.println("Animal makes sound");
        }
    }
    
    public class Dog extends Animal {
        @Override
        public void makeSound() {
            System.out.println("Dog barks");
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Animal animal = new Dog();
            animal.makeSound(); // 输出:Dog barks
        }
    }
    
  • 封装:封装是将类的成员变量私有化,并通过公共方法(如gettersetter)访问和修改成员变量。例如:

    public class Person {
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
  1. 集合框架
  • 集合接口:熟悉CollectionListSetMap等接口及其常用实现类。例如:

    • List接口的实现类有ArrayListLinkedList等。ArrayList基于动态数组实现,适合随机访问;LinkedList基于双向链表实现,适合频繁插入和删除操作。
    • Set接口的实现类有HashSetTreeSet等。HashSet基于哈希表实现,不允许重复元素;TreeSet基于红黑树实现,可以对元素进行排序。
    • Map接口的实现类有HashMapTreeMap等。HashMap基于哈希表实现,键值对映射,键不允许重复;TreeMap基于红黑树实现,可以对键进行排序。
  • 集合操作:掌握集合的基本操作,如添加元素、删除元素、遍历集合等。例如:

    List<String> list = new ArrayList<>();
    list.add("Alice");
    list.add("Bob");
    for (String name : list) {
        System.out.println(name);
    }
    
    Set<String> set = new HashSet<>();
    set.add("Alice");
    set.add("Bob");
    for (String name : set) {
        System.out.println(name);
    }
    
    Map<String, Integer> map = new HashMap<>();
    map.put("Alice", 25);
    map.put("Bob", 30);
    for (Map.Entry<String, Integer> entry : map.entrySet()) {
        System.out.println(entry.getKey() + ": " + entry.getValue());
    }
    
  1. 异常处理
  • 异常分类:了解Java中的异常分为两类:Checked Exception(受检查异常)和Unchecked Exception(非受检查异常)。Checked Exception需要在方法签名中声明抛出异常,或者在方法体内捕获异常;Unchecked Exception通常是由程序逻辑错误引起的,如NullPointerExceptionArrayIndexOutOfBoundsException等。

  • 异常处理机制:掌握try-catch-finally语句的使用。try块中编写可能会抛出异常的代码,catch块捕获并处理异常,finally块无论是否捕获到异常都会执行,通常用于释放资源。例如:

    try {
        int result = 10 / 0;
    } catch (ArithmeticException e) {
        System.out.println("发生算术异常:" + e.getMessage());
    } finally {
        System.out.println("finally块执行");
    }
    

(二)Spring Boot基础

Spring Boot是一个基于Spring框架的开源Java基础框架,用于创建独立、生产级的基于Spring框架的应用程序。它简化了Spring应用的初始搭建以及开发过程,提供了许多自动配置的特性。

  1. 核心概念
  • 自动配置:Spring Boot会根据添加的依赖自动配置Spring和相关技术。例如,添加spring-boot-starter-web依赖后,Spring Boot会自动配置Tomcat和Spring MVC。

  • 依赖注入:通过@Autowired注解或构造器注入,Spring容器会自动将依赖注入到组件中。例如:

    @Service
    public class UserService {
        @Autowired
        private UserRepository userRepository;
    
        public User getUserById(Long id) {
            return userRepository.findById(id);
        }
    }
    
  • 注解:Spring Boot提供了许多注解,用于简化开发。例如,@SpringBootApplication是一个组合注解,包含了@Configuration@EnableAutoConfiguration@ComponentScan等注解,用于启动Spring Boot应用。

    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
  1. 项目创建与配置
  • 使用Spring Initializr创建项目:Spring Initializr是一个在线工具,可以通过它快速创建Spring Boot项目。访问Spring Initializr,选择项目元数据(如项目名称、依赖等),然后生成项目并下载。解压下载的项目后,导入到IDE中。

  • 添加依赖:在pom.xml文件中添加所需的依赖。例如,添加Spring Web依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
  • 配置文件:Spring Boot的配置文件是application.ymlapplication.properties。通过配置文件可以设置应用的各种参数,如端口号、数据库连接信息等。例如:

    server:
      port: 8080
    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/mydb
        username: root
        password: password
    
  1. 控制器与服务
  • 控制器(Controller):控制器是Spring MVC的核心组件之一,用于处理用户请求并返回响应。通过@RestController@Controller注解定义控制器。例如:

    @RestController
    @RequestMapping("/users")
    public class UserController {
        @Autowired
        private UserService userService;
    
        @GetMapping("/{id}")
        public User getUserById(@PathVariable Long id) {
            return userService.getUserById(id);
        }
    }
    
  • 服务(Service):服务层是业务逻辑的核心部分,通常包含数据的处理和操作。通过@Service注解定义服务。例如:

    @Service
    public class UserService {
        @Autowired
        private UserRepository userRepository;
    
        public User getUserById(Long id) {
            return userRepository.findById(id);
        }
    }
    
  1. 数据访问
  • JPA与Spring Data:Spring Data JPA是一个简化数据库访问的框架,通过定义接口继承JpaRepository,可以自动生成数据访问方法。例如:

    public interface UserRepository extends JpaRepository<User, Long> {
    }
    
  • 数据库连接:通过配置文件设置数据库连接信息。例如,连接MySQL数据库:

    spring:
      datasource:
        url: jdbc:mysql://localhost:3306/mydb
        username: root
        password: password
        driver-class-name: com.mysql.cj.jdbc.Driver
    

(三)AI大模型基础

AI大模型是近年来人工智能领域的重要发展成果,它们通过大量的数据训练,能够生成自然语言文本、图像等内容。在构建基于Spring Boot的AI应用时,您需要了解AI大模型的基本原理和应用场景。

  1. 常见AI大模型
  • GPT(Generative Pre-trained Transformer):由OpenAI开发的一系列生成式预训练模型,能够生成自然语言文本。例如,GPT-4是目前最先进的版本,支持多模态输入和输出。
  • LLaMA(Large Language Model Application):由Meta开发的开源语言模型,具有高效的训练和推理能力。
  • DeepSeek:由月之暗面科技有限公司开发的AI大模型,支持文本生成、多模态应用等功能。
  1. API调用
  • HTTP请求与响应:了解HTTP协议的基本概念,包括请求方法(如GETPOST)、请求头、请求体,以及响应状态码、响应头、响应体。例如,使用curl工具发送HTTP请求:

    curl -X POST https://api.openai.com/v1/chat/completions \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer YOUR_API_KEY" \
      -d '{
        "model": "gpt-4",
        "messages": [{"role": "user", "content": "Hello, how are you?"}]
      }'
    
  • API Key:API Key是用于身份验证的密钥,通常需要在请求头中携带。例如,OpenAI的API Key需要在Authorization头中以Bearer形式传递。

  1. 模型调用示例
  • 文本生成:通过API调用AI大模型生成文本。例如,使用Java代码调用OpenAI的GPT模型:

    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.nio.charset.StandardCharsets;
    
    public class OpenAIClient {
        private static final String API_URL = "https://api.openai.com/v1/chat/completions";
        private static final String API_KEY = "YOUR_API_KEY";
    
        public static void main(String[] args) throws Exception {
            URL url = new URL(API_URL);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/json");
            connection.setRequestProperty("Authorization", "Bearer " + API_KEY);
            connection.setDoOutput(true);
    
            String jsonInputString = "{"
                    + "\"model\": \"gpt-4\","
                    + "\"messages\": [{\"role\": \"user\", \"content\": \"Hello, how are you?\"}]"
                    + "}";
    
            try (var wr = new java.io.OutputStreamWriter(connection.getOutputStream(), StandardCharsets.UTF_8)) {
                wr.write(jsonInputString);
            }
    
            try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
                StringBuilder response = new StringBuilder();
                String responseLine;
                while ((responseLine = br.readLine()) != null) {
                    response.append(responseLine.trim());
                }
                System.out.println(response.toString());
            }
        }
    }
    

二、环境搭建

(一)开发环境

在开始构建基于Spring Boot的AI应用之前,您需要搭建一个合适的开发环境。

  1. 安装JDK
  • JDK(Java Development Kit)是Java开发工具包,包含了Java编译器、运行时环境等。推荐安装JDK 17或更高版本。可以从Oracle官网下载并安装JDK。

  • 安装完成后,配置环境变量。在Windows系统中,将JDK的bin目录添加到系统的PATH环境变量中。例如,如果JDK安装在C:\Program Files\Java\jdk-17\bin,则将其添加到PATH

  • 验证JDK是否安装成功,打开命令行工具,输入以下命令:

    java -version
    

    如果输出类似以下信息,则表示JDK安装成功:

    java version "17.0.1" 2021-10-19 LTS
    Java(TM) SE Runtime Environment (build 17.0.1+12-LTS-39)
    Java HotSpot(TM) 64-Bit Server VM (build 17.0.1+12-LTS-39, mixed mode)
    
  1. 配置项目构建工具
  • Maven:Maven是一个流行的Java项目构建工具,用于依赖管理和项目构建。可以从Maven官网下载并安装Maven。

  • Gradle:Gradle是另一种流行的Java项目构建工具,支持多种编程语言。可以从Gradle官网下载并安装Gradle。

  • 配置Maven或Gradle的环境变量。在Windows系统中,将Maven或Gradle的bin目录添加到系统的PATH环境变量中。

  • 验证Maven是否安装成功,打开命令行工具,输入以下命令:

    mvn -v
    

    如果输出类似以下信息,则表示Maven安装成功:

    Apache Maven 3.8.4 (9b656c72d54257dc5c184c05821c43f7e18a0b9a)
    Maven home: C:\Program Files\apache-maven-3.8.4
    Java version: 17.0.1, vendor: Oracle Corporation, runtime: C:\Program Files\Java\jdk-17
    Default locale: zh_CN, platform encoding: GBK
    OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
    
  1. 选择IDE
  • IntelliJ IDEA:IntelliJ IDEA是一款功能强大的Java集成开发环境,提供了代码编辑、调试、项目管理等功能。可以从JetBrains官网下载并安装IntelliJ IDEA。
  • Eclipse:Eclipse是另一款流行的Java集成开发环境,具有丰富的插件生态系统。可以从Eclipse官网下载并安装Eclipse。
  • 安装完成后,启动IDE并导入Spring Boot项目。例如,在IntelliJ IDEA中,可以通过File -> Open菜单导入项目。

(二)Spring Boot项目创建

使用Spring Initializr快速创建Spring Boot项目,并添加所需的依赖。

  1. 访问Spring Initializr
  1. 配置项目元数据
  • Project Metadata:填写项目元数据,包括项目名称、项目描述、包名等。
  • Dependencies:添加所需的依赖。例如,添加Spring WebSpring AI OpenAI Starter等依赖。Spring AI OpenAI Starter是一个用于集成OpenAI大模型的Spring Boot Starter,它提供了便捷的API调用功能。
  1. 生成项目
  • 点击“Generate”按钮,下载生成的项目压缩包。解压下载的项目后,导入到IDE中。
  1. 配置文件
  • application.yml文件中添加AI大模型的API Key和相关配置。例如:

    spring:
      ai:
        openai:
          api-key: YOUR_API_KEY
          model: gpt-4
    

三、Spring AI框架集成

(一)Spring AI介绍

Spring AI是Spring框架的一个扩展,用于简化AI应用的开发。它提供了与AI大模型的集成,支持智能对话、角色预设、流式响应等功能。

  1. 功能特点
  • 模型调用:通过简单的API调用,可以与各种AI大模型进行交互。
  • 提示词模板:支持定义提示词模板,方便生成高质量的文本内容。
  • 检索增强生成(RAG):结合外部数据源,实现更准确的回答。
  • 多模态支持:支持文生图、文生语音等多模态应用。
  1. 适用场景
  • 智能客服:通过AI大模型生成自然语言回答,提升客服效率。
  • 写作助手:帮助用户生成文章、文案等内容。
  • 智能教育:提供个性化的学习建议和辅导。

(二)集成Spring AI

在Spring Boot项目中集成Spring AI框架,通过添加依赖和配置来实现。

  1. 添加依赖
  • pom.xml文件中添加Spring AI的依赖。例如:

    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-starter</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
    
  • 如果需要使用快照版本,需要配置Spring AI的仓库地址。例如:

    <repositories>
        <repository>
            <id>spring-ai-snapshots</id>
            <url>https://repo.spring.io/snapshot</url>
        </repository>
    </repositories>
    
  1. 初始化ChatClient
  • 使用Spring AI提供的ChatClient进行大模型的调用。ChatClient是与AI大模型交互的核心组件,可以通过配置默认的系统角色和提示词模板来实现特定场景下的对话。

  • 在Spring Boot的配置类中初始化ChatClient。例如:

    @Configuration
    public class AiConfig {
        @Bean
        public ChatClient chatClient() {
            return new ChatClient.Builder()
                    .defaultSystem("你是一个AI智能应用")
                    .build();
        }
    }
    

(三)实现智能对话

通过ChatClient实现一个简单的智能对话接口,接收用户输入并调用大模型生成回答。

  1. 创建控制器
  • 创建一个控制器类,定义一个/chat接口,接收用户输入并调用ChatClient生成回答。例如:

    @RestController
    @RequestMapping("/ai")
    public class ChatController {
        private final ChatClient chatClient;
    
        public ChatController(ChatClient chatClient) {
            this.chatClient = chatClient;
        }
    
        @GetMapping("/chat")
        public String chat(@RequestParam String message) {
            return chatClient.prompt()
                    .user(message)
                    .call()
                    .content();
        }
    }
    
  1. 测试接口
  • 启动Spring Boot应用,使用Postman或curl工具测试/ai/chat接口。例如:

    curl -X GET "http://localhost:8080/ai/chat?message=Hello, how are you?"
    
  • 如果一切正常,您将收到AI大模型生成的回答。

(四)角色预设

通过配置ChatClient的默认系统角色,实现特定场景下的对话。例如,您可以将系统角色设置为“智能客服”,以便生成更符合客服场景的回答。

  1. 配置系统角色
  • ChatClient的初始化中,配置默认的系统角色。例如:

    @Bean
    public ChatClient chatClient() {
        return new ChatClient.Builder()
                .defaultSystem("你是一个智能客服,提供专业的服务")
                .build();
    }
    
  1. 测试角色预设
  • 使用相同的/ai/chat接口测试角色预设的效果。例如:

    curl -X GET "http://localhost:8080/ai/chat?message=我想咨询一下产品信息"
    
  • 您将收到更符合客服场景的回答。

(五)流式响应

实现流式响应,逐步显示生成的文本内容。流式响应可以提升用户体验,尤其是在生成较长文本时。

  1. 修改控制器
  • 修改/ai/chat接口,返回ServerSentEvent类型的数据,实现流式响应。例如:

    @GetMapping("/chat/stream")
    public Flux<ServerSentEvent<String>> chatStream(@RequestParam String message) {
        return Flux.create(sink -> {
            chatClient.prompt()
                    .user(message)
                    .stream()
                    .forEach(response -> {
                        sink.next(ServerSentEvent.builder(response.content()).build());
                    });
        });
    }
    
  1. 测试流式响应
  • 使用支持流式响应的客户端工具测试/ai/chat/stream接口。例如,在浏览器中访问http://localhost:8080/ai/chat/stream?message=Hello, how are you?,您将看到逐步生成的文本内容。

(六)检索增强生成(RAG)

检索增强生成(RAG)是一种结合外部数据源的技术,通过检索相关数据并生成更准确的回答。

  1. 了解RAG原理
  • RAG的基本原理是:在生成回答之前,先从外部数据源(如向量数据库)检索与用户问题相关的上下文信息,然后将这些信息作为提示词的一部分传递给AI大模型,从而生成更准确的回答。
  1. 集成向量数据库
  • 选择一个向量数据库(如Milvus、Faiss等),并将其集成到Spring Boot项目中。例如,使用Milvus作为向量数据库:
    • 安装Milvus服务,并启动Milvus服务。

    • 在Spring Boot项目中添加Milvus的客户端依赖。例如:

      <dependency>
          <groupId>io.milvus</groupId>
          <artifactId>milvus-sdk-java</artifactId>
          <version>2.0.0</version>
      </dependency>
      
    • 初始化Milvus客户端,并连接到Milvus服务。例如:

      @Bean
      public MilvusClient milvusClient() {
          return MilvusClient.builder()
                  .withHost("localhost")
                  .withPort(19530)
                  .build();
      }
      
  1. 实现RAG
  • ChatClient的调用中,结合Milvus检索到的相关上下文信息生成回答。例如:

    @GetMapping("/chat/rag")
    public String chatRag(@RequestParam String message) {
        // 从Milvus检索相关上下文信息
        List<String> contexts = milvusClient.search(message);
    
        // 将上下文信息作为提示词的一部分传递给AI大模型
        return chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(message)
                .context(contexts)
                .call()
                .content();
    }
    

四、功能实现

(一)智能对话

智能对话是AI应用的核心功能之一,通过与用户进行自然语言交互,提供有用的信息和服务。

  1. 简单对话
  • 在前面的章节中,我们已经实现了一个简单的智能对话接口/ai/chat。用户可以通过发送HTTP请求与AI大模型进行对话。
  1. 多轮对话
  • 为了实现多轮对话,需要在ChatClient中维护对话的历史记录。通过将历史记录作为提示词的一部分传递给AI大模型,可以生成更连贯的回答。

  • 修改ChatController,添加一个ChatSession类来维护对话历史。例如:

    @RestController
    @RequestMapping("/ai")
    public class ChatController {
        private final ChatClient chatClient;
        private final Map<String, ChatSession> sessions = new ConcurrentHashMap<>();
    
        public ChatController(ChatClient chatClient) {
            this.chatClient = chatClient;
        }
    
        @GetMapping("/chat")
        public String chat(@RequestParam String sessionId, @RequestParam String message) {
            ChatSession session = sessions.computeIfAbsent(sessionId, ChatSession::new);
            return session.sendMessage(message);
        }
    
        private static class ChatSession {
            private final List<Message> history = new ArrayList<>();
    
            public String sendMessage(String message) {
                // 将用户消息添加到历史记录
                history.add(new Message("user", message));
    
                // 生成回答
                String response = chatClient.prompt()
                        .system("你是一个AI智能应用")
                        .messages(history)
                        .call()
                        .content();
    
                // 将AI回答添加到历史记录
                history.add(new Message("assistant", response));
                return response;
            }
        }
    }
    
  1. 对话上下文管理
  • 在多轮对话中,对话上下文的管理非常重要。可以通过设置上下文有效期、清理历史记录等方式优化对话体验。

  • 例如,设置上下文有效期为10轮对话:

    private static class ChatSession {
        private final List<Message> history = new ArrayList<>();
    
        public String sendMessage(String message) {
            // 清理过期的历史记录
            if (history.size() > 20) {
                history.subList(0, 10).clear();
            }
    
            // 将用户消息添加到历史记录
            history.add(new Message("user", message));
    
            // 生成回答
            String response = chatClient.prompt()
                    .system("你是一个AI智能应用")
                    .messages(history)
                    .call()
                    .content();
    
            // 将AI回答添加到历史记录
            history.add(new Message("assistant", response));
            return response;
        }
    }
    

(二)角色预设

角色预设是通过配置AI大模型的系统角色,使其在特定场景下生成更符合角色的回答。

  1. 自定义角色
  • 在前面的章节中,我们已经通过配置ChatClient的默认系统角色实现了角色预设。例如,将系统角色设置为“智能客服”:

    @Bean
    public ChatClient chatClient() {
        return new ChatClient.Builder()
                .defaultSystem("你是一个智能客服,提供专业的服务")
                .build();
    }
    
  1. 动态角色切换
  • 在某些场景下,可能需要根据用户输入动态切换角色。可以通过在接口中添加角色参数,动态设置系统角色。例如:

    @GetMapping("/chat")
    public String chat(@RequestParam String role, @RequestParam String message) {
        return chatClient.prompt()
                .system(role)
                .user(message)
                .call()
                .content();
    }
    
  1. 角色模板
  • 为了方便管理角色,可以定义角色模板。例如,定义一个“智能客服”角色模板:

    public class RoleTemplates {
        public static final String SMART_CUSTOMER_SERVICE = "你是一个智能客服,提供专业的服务";
    }
    

    在接口中使用角色模板:

    @GetMapping("/chat")
    public String chat(@RequestParam String role, @RequestParam String message) {
        String systemRole = RoleTemplates.SMART_CUSTOMER_SERVICE;
        if ("writer".equals(role)) {
            systemRole = "你是一个写作助手,帮助用户生成文章";
        } else if ("teacher".equals(role)) {
            systemRole = "你是一个智能教育助手,提供学习建议";
        }
    
        return chatClient.prompt()
                .system(systemRole)
                .user(message)
                .call()
                .content();
    }
    

(三)流式响应

流式响应可以逐步显示生成的文本内容,提升用户体验。

  1. 流式响应的实现
  • 在前面的章节中,我们已经通过Flux实现了流式响应。例如:

    @GetMapping("/chat/stream")
    public Flux<ServerSentEvent<String>> chatStream(@RequestParam String message) {
        return Flux.create(sink -> {
            chatClient.prompt()
                    .user(message)
                    .stream()
                    .forEach(response -> {
                        sink.next(ServerSentEvent.builder(response.content()).build());
                    });
        });
    }
    
  1. 前端支持
  • 在前端页面中,可以通过EventSource接口接收流式响应。例如:

    <!DOCTYPE html>
    <html>
    <head>
        <title>AI Chat</title>
    </head>
    <body>
        <h1>AI Chat</h1>
        <input type="text" id="message" placeholder="Enter your message">
        <button onclick="sendMessage()">Send</button>
        <div id="response"></div>
    
        <script>
            function sendMessage() {
                const message = document.getElementById('message').value;
                const eventSource = new EventSource('/ai/chat/stream?message=' + encodeURIComponent(message));
                eventSource.onmessage = function(event) {
                    const responseDiv = document.getElementById('response');
                    responseDiv.innerHTML += event.data + '<br>';
                };
                eventSource.onerror = function(event) {
                    console.error('EventSource failed:', event);
                    eventSource.close();
                };
            }
        </script>
    </body>
    </html>
    
  1. 优化流式响应
  • 在某些场景下,可能需要对流式响应进行优化,例如设置响应间隔、清理历史记录等。可以通过在ChatClient的流式调用中添加逻辑来实现。例如:

    @GetMapping("/chat/stream")
    public Flux<ServerSentEvent<String>> chatStream(@RequestParam String message) {
        return Flux.create(sink -> {
            chatClient.prompt()
                    .user(message)
                    .stream()
                    .forEach(response -> {
                        // 设置响应间隔
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                        sink.next(ServerSentEvent.builder(response.content()).build());
                    });
        });
    }
    

(四)检索增强生成(RAG)

检索增强生成(RAG)是一种结合外部数据源的技术,通过检索相关数据并生成更准确的回答。

  1. RAG的实现
  • 在前面的章节中,我们已经通过Milvus实现了RAG。例如:

    @GetMapping("/chat/rag")
    public String chatRag(@RequestParam String message) {
        // 从Milvus检索相关上下文信息
        List<String> contexts = milvusClient.search(message);
    
        // 将上下文信息作为提示词的一部分传递给AI大模型
        return chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(message)
                .context(contexts)
                .call()
                .content();
    }
    
  1. 优化RAG
  • 在RAG中,检索相关上下文信息的准确性非常重要。可以通过优化向量数据库的索引、调整检索算法等方式提升检索效果。

  • 例如,优化Milvus的索引:

    @Bean
    public MilvusClient milvusClient() {
        return MilvusClient.builder()
                .withHost("localhost")
                .withPort(19530)
                .build();
    }
    
    @PostConstruct
    public void initMilvus() {
        // 创建集合
        milvusClient.createCollection("my_collection", 128, 1024);
    
        // 创建索引
        milvusClient.createIndex("my_collection", "IVF_FLAT", 128, 1024);
    }
    
  1. 结合多种数据源
  • 在某些场景下,可能需要结合多种数据源进行RAG。例如,结合Milvus和Elasticsearch进行检索:

    @GetMapping("/chat/rag")
    public String chatRag(@RequestParam String message) {
        // 从Milvus检索相关上下文信息
        List<String> contexts = milvusClient.search(message);
    
        // 从Elasticsearch检索相关上下文信息
        List<String> esContexts = elasticsearchClient.search(message);
    
        // 将上下文信息作为提示词的一部分传递给AI大模型
        return chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(message)
                .context(contexts)
                .context(esContexts)
                .call()
                .content();
    }
    

(五)多模态应用

多模态应用是指结合文本、图像、语音等多种模态的应用。通过调用支持多模态的大模型,可以实现更丰富的功能。

  1. 文生图
  • 通过调用大模型的文生图功能,根据文本描述生成图像。例如,使用OpenAI的DALL·E模型:

    @GetMapping("/generate/image")
    public String generateImage(@RequestParam String prompt) {
        // 调用OpenAI的DALL·E模型生成图像
        String imageUrl = openaiClient.generateImage(prompt);
        return imageUrl;
    }
    
  1. 文生语音
  • 通过调用大模型的文生语音功能,根据文本生成语音。例如,使用Google Text-to-Speech API:

    @GetMapping("/generate/voice")
    public String generateVoice(@RequestParam String text) {
        // 调用Google Text-to-Speech API生成语音
        String voiceUrl = googleTtsClient.generateVoice(text);
        return voiceUrl;
    }
    
  1. 多模态交互
  • 在某些场景下,可能需要实现多模态交互。例如,用户可以通过语音输入,AI大模型生成文本回答并显示图像。可以通过结合语音识别、文本生成和图像生成实现多模态交互:

    @GetMapping("/multimodal/interaction")
    public String multimodalInteraction(@RequestParam String voice) {
        // 调用语音识别API将语音转换为文本
        String text = voiceRecognitionClient.recognize(voice);
    
        // 调用AI大模型生成文本回答
        String responseText = chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(text)
                .call()
                .content();
    
        // 调用文生图功能生成图像
        String imageUrl = openaiClient.generateImage(responseText);
    
        // 返回文本回答和图像链接
        return "Text: " + responseText + "\nImage: " + imageUrl;
    }
    

五、高级功能

(一)函数调用

函数调用是AI大模型的一个高级功能,允许模型调用外部函数来完成复杂的任务。

  1. 函数调用的实现
  • 通过定义函数接口,允许AI大模型调用外部函数。例如,定义一个天气查询函数:

    @Service
    public class WeatherService {
        public String getWeather(String city) {
            // 调用天气API获取天气信息
            return weatherApiClient.getWeather(city);
        }
    }
    
  • 在AI大模型的调用中,允许模型调用WeatherServicegetWeather函数:

    @GetMapping("/chat/function")
    public String chatFunction(@RequestParam String message) {
        return chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(message)
                .function("getWeather", new WeatherService())
                .call()
                .content();
    }
    
  1. 函数调用的优化
  • 在函数调用中,可能需要对调用结果进行优化,例如缓存结果、限制调用频率等。可以通过在函数接口中添加逻辑来实现。例如:

    @Service
    public class WeatherService {
        private final Cache<String, String> cache = new LRUCache<>(100);
    
        public String getWeather(String city) {
            // 检查缓存
            if (cache.containsKey(city)) {
                return cache.get(city);
            }
    
            // 调用天气API获取天气信息
            String weather = weatherApiClient.getWeather(city);
    
            // 缓存结果
            cache.put(city, weather);
            return weather;
        }
    }
    

(二)结构化输出

结构化输出是指将AI大模型的非结构化文本输出转换为结构化的数据格式,例如JSON、XML等。

  1. 结构化输出的实现
  • 通过定义结构化输出的模板,将AI大模型的输出转换为结构化的数据。例如,定义一个JSON模板:

    @GetMapping("/chat/structured")
    public String chatStructured(@RequestParam String message) {
        String response = chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(message)
                .call()
                .content();
    
        // 将文本输出转换为JSON格式
        Map<String, String> result = new HashMap<>();
        result.put("message", message);
        result.put("response", response);
    
        return new Gson().toJson(result);
    }
    
  1. 结构化输出的优化
  • 在结构化输出中,可能需要对输出数据进行优化,例如提取关键信息、过滤无关内容等。可以通过在模板中添加逻辑来实现。例如:

    @GetMapping("/chat/structured")
    public String chatStructured(@RequestParam String message) {
        String response = chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(message)
                .call()
                .content();
    
        // 提取关键信息
        String keyInfo = extractKeyInfo(response);
    
        // 将文本输出转换为JSON格式
        Map<String, String> result = new HashMap<>();
        result.put("message", message);
        result.put("response", response);
        result.put("keyInfo", keyInfo);
    
        return new Gson().toJson(result);
    }
    
    private String extractKeyInfo(String response) {
        // 提取关键信息的逻辑
        return response.replaceAll("[^a-zA-Z0-9]", "");
    }
    

(三)安全性

在构建AI应用时,安全性是非常重要的。需要保护API Key,防止恶意攻击,并对用户输入进行过滤。

  1. 保护API Key
  • 不要将API Key直接硬编码在代码中,而是通过配置文件或环境变量进行管理。例如,在application.yml文件中配置API Key:

    spring:
      ai:
        openai:
          api-key: ${OPENAI_API_KEY}
    
  • 在运行应用时,通过环境变量设置API Key:

    export OPENAI_API_KEY=YOUR_API_KEY
    
  1. 防止恶意攻击
  • 对用户输入进行过滤,防止SQL注入、XSS攻击等。可以通过使用正则表达式或安全库来实现。例如:

    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        // 对用户输入进行过滤
        message = sanitizeInput(message);
    
        return chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(message)
                .call()
                .content();
    }
    
    private String sanitizeInput(String input) {
        // 使用正则表达式过滤特殊字符
        return input.replaceAll("[^a-zA-Z0-9]", "");
    }
    
  1. 限制调用频率
  • 为了避免恶意用户频繁调用API,可以限制调用频率。例如,使用RateLimiter库限制调用频率:

    @Service
    public class RateLimiterService {
        private final RateLimiter rateLimiter = RateLimiter.create(10); // 每秒最多10次调用
    
        public boolean tryAcquire() {
            return rateLimiter.tryAcquire();
        }
    }
    

    在接口中使用RateLimiterService

    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        if (!rateLimiterService.tryAcquire()) {
            throw new TooManyRequestsException("Too many requests");
        }
    
        return chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(message)
                .call()
                .content();
    }
    

六、测试与优化

(一)接口测试

在开发过程中,需要对API接口进行测试,确保功能正常。

  1. 使用Postman测试
  • Postman是一款流行的API测试工具,支持发送HTTP请求、查看响应等功能。在Postman中创建一个请求,设置请求方法、URL、参数等,然后发送请求并查看响应。
  1. 使用curl测试
  • curl是一个命令行工具,用于发送HTTP请求。例如,测试/ai/chat接口:

    curl -X GET "http://localhost:8080/ai/chat?message=Hello, how are you?"
    
  1. 编写单元测试
  • 使用JUnit等测试框架编写单元测试,测试接口的功能。例如:

    @SpringBootTest
    @AutoConfigureMockMvc
    public class ChatControllerTest {
        @Autowired
        private MockMvc mockMvc;
    
        @Test
        public void testChat() throws Exception {
            mockMvc.perform(MockMvcRequestBuilders.get("/ai/chat")
                    .param("message", "Hello, how are you?"))
                    .andExpect(MockMvcResultMatchers.status().isOk())
                    .andExpect(MockMvcResultMatchers.content().string(containsString("Hello")));
        }
    }
    

(二)性能优化

在生产环境中,性能优化非常重要。需要优化API调用的性能,减少响应时间。

  1. 减少API调用次数
  • 通过缓存结果、合并请求等方式减少API调用次数。例如,使用Cache库缓存结果:

    @Service
    public class ChatService {
        private final Cache<String, String> cache = new LRUCache<>(100);
    
        public String chat(String message) {
            if (cache.containsKey(message)) {
                return cache.get(message);
            }
    
            String response = chatClient.prompt()
                    .system("你是一个AI智能应用")
                    .user(message)
                    .call()
                    .content();
    
            cache.put(message, response);
            return response;
        }
    }
    
  1. 优化API调用性能
  • 通过调整API调用的参数、优化网络连接等方式提升API调用性能。例如,设置API调用的超时时间:

    @Bean
    public ChatClient chatClient() {
        return new ChatClient.Builder()
                .defaultSystem("你是一个AI智能应用")
                .timeout(5000) // 设置超时时间为5秒
                .build();
    }
    
  1. 异步调用
  • 在某些场景下,可以使用异步调用提升性能。例如,使用CompletableFuture实现异步调用:

    @GetMapping("/chat/async")
    public CompletableFuture<String> chatAsync(@RequestParam String message) {
        return CompletableFuture.supplyAsync(() -> {
            return chatClient.prompt()
                    .system("你是一个AI智能应用")
                    .user(message)
                    .call()
                    .content();
        });
    }
    

(三)安全性

在生产环境中,安全性非常重要。需要保护API Key,防止恶意攻击,并对用户输入进行过滤。

  1. 保护API Key
  • 不要将API Key直接硬编码在代码中,而是通过配置文件或环境变量进行管理。例如,在application.yml文件中配置API Key:

    spring:
      ai:
        openai:
          api-key: ${OPENAI_API_KEY}
    
  • 在运行应用时,通过环境变量设置API Key:

    export OPENAI_API_KEY=YOUR_API_KEY
    
  1. 防止恶意攻击
  • 对用户输入进行过滤,防止SQL注入、XSS攻击等。可以通过使用正则表达式或安全库来实现。例如:

    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        // 对用户输入进行过滤
        message = sanitizeInput(message);
    
        return chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(message)
                .call()
                .content();
    }
    
    private String sanitizeInput(String input) {
        // 使用正则表达式过滤特殊字符
        return input.replaceAll("[^a-zA-Z0-9]", "");
    }
    
  1. 限制调用频率
  • 为了避免恶意用户频繁调用API,可以限制调用频率。例如,使用RateLimiter库限制调用频率:

    @Service
    public class RateLimiterService {
        private final RateLimiter rateLimiter = RateLimiter.create(10); // 每秒最多10次调用
    
        public boolean tryAcquire() {
            return rateLimiter.tryAcquire();
        }
    }
    

    在接口中使用RateLimiterService

    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        if (!rateLimiterService.tryAcquire()) {
            throw new TooManyRequestsException("Too many requests");
        }
    
        return chatClient.prompt()
                .system("你是一个AI智能应用")
                .user(message)
                .call()
                .content();
    }
    

七、项目实践

(一)构建完整的AI应用

在前面的章节中,我们已经学习了如何使用Spring Boot和Spring AI框架构建AI应用。现在,我们将通过一个完整的项目实践,将这些知识应用到实际中。

1. 项目需求

假设我们需要构建一个智能客服系统,用户可以通过发送文本消息与客服进行对话。系统需要支持以下功能:

  • 智能对话:用户可以发送文本消息,系统根据消息内容生成回答。
  • 多轮对话:系统可以维护对话历史,生成连贯的回答。
  • 角色预设:系统可以根据用户输入动态切换角色,例如“智能客服”、“写作助手”等。
  • 流式响应:系统可以逐步显示生成的文本内容。
  • 检索增强生成(RAG):系统可以结合外部数据源生成更准确的回答。
  • 多模态应用:系统可以支持文生图、文生语音等功能。
  • 函数调用:系统可以调用外部函数完成复杂任务。
  • 结构化输出:系统可以将非结构化文本输出转换为结构化的数据格式。

2. 项目设计

根据项目需求,我们可以设计以下模块:

  • 用户接口模块:提供用户与系统交互的接口。
  • 对话管理模块:维护对话历史,生成连贯的回答。
  • 角色管理模块:动态切换角色,生成符合角色的回答。
  • 流式响应模块:逐步显示生成的文本内容。
  • RAG模块:结合外部数据源生成更准确的回答。
  • 多模态模块:支持文生图、文生语音等功能。
  • 函数调用模块:调用外部函数完成复杂任务。
  • 结构化输出模块:将非结构化文本输出转换为结构化的数据格式。

3. 项目实现

以下是项目实现的代码示例:

(1)用户接口模块
@RestController
@RequestMapping("/ai")
public class ChatController {
    private final ChatClient chatClient;
    private final RateLimiterService rateLimiterService;
    private final Map<String, ChatSession> sessions = new ConcurrentHashMap<>();

    public ChatController(ChatClient chatClient, RateLimiterService rateLimiterService) {
        this.chatClient = chatClient;
        this.rateLimiterService = rateLimiterService;
    }

    @GetMapping("/chat")
    public String chat(@RequestParam String sessionId, @RequestParam String message) {
        if (!rateLimiterService.tryAcquire()) {
            throw new TooManyRequestsException("Too many requests");
        }

        ChatSession session = sessions.computeIfAbsent(sessionId, ChatSession::new);
        return session.sendMessage(message);
    }

    @GetMapping("/chat/stream")
    public Flux<ServerSentEvent<String>> chatStream(@RequestParam String sessionId, @RequestParam String message) {
        if (!rateLimiterService.tryAcquire()) {
            throw new TooManyRequestsException("Too many requests");
        }

        ChatSession session = sessions.computeIfAbsent(sessionId, ChatSession::new);
        return Flux.create(sink -> {
            chatClient.prompt()
                    .system("你是一个智能客服,提供专业的服务")
                    .messages(session.getHistory())
                    .stream()
                    .forEach(response -> {
                        sink.next(ServerSentEvent.builder(response.content()).build());
                    });
        });
    }
}
(2)对话管理模块
public class ChatSession {
    private final List<Message> history = new ArrayList<>();

    public String sendMessage(String message) {
        // 将用户消息添加到历史记录
        history.add(new Message("user", message));

        // 生成回答
        String response = chatClient.prompt()
                .system("你是一个智能客服,提供专业的服务")
                .messages(history)
                .call()
                .content();

        // 将AI回答添加到历史记录
        history.add(new Message("assistant", response));
        return response;
    }

    public List<Message> getHistory() {
        return history;
    }
}
(3)角色管理模块
@GetMapping("/chat/role")
public String chatRole(@RequestParam String role, @RequestParam String message) {
    String systemRole = "你是一个智能客服,提供专业的服务";
    if ("writer".equals(role)) {
        systemRole = "你是一个写作助手,帮助用户生成文章";
    } else if ("teacher".equals(role)) {
        systemRole = "你是一个智能教育助手,提供学习建议";
    }

    return chatClient.prompt()
            .system(systemRole)
            .user(message)
            .call()
            .content();
}
(4)流式响应模块
@GetMapping("/chat/stream")
public Flux<ServerSentEvent<String>> chatStream(@RequestParam String message) {
    return Flux.create(sink -> {
        chatClient.prompt()
                .user(message)
                .stream()
                .forEach(response -> {
                    sink.next(ServerSentEvent.builder(response.content()).build());
                });
    });
}
(5)RAG模块
@GetMapping("/chat/rag")
public String chatRag(@RequestParam String message) {
    // 从Milvus检索相关上下文信息
    List<String> contexts = milvusClient.search(message);

    // 将上下文信息作为提示词的一部分传递给AI大模型
    return chatClient.prompt()
            .system("你是一个AI智能应用")
            .user(message)
            .context(contexts)
            .call()
            .content();
}
(6)多模态模块
@GetMapping("/generate/image")
public String generateImage(@RequestParam String prompt) {
    // 调用OpenAI的DALL·E模型生成图像
    String imageUrl = openaiClient.generateImage(prompt);
    return imageUrl;
}

@GetMapping("/generate/voice")
public String generateVoice(@RequestParam String text) {
    // 调用Google Text-to-Speech API生成语音
    String voiceUrl = googleTtsClient.generateVoice(text);
    return voiceUrl;
}
(7)函数调用模块
@GetMapping("/chat/function")
public String chatFunction(@RequestParam String message) {
    return chatClient.prompt()
            .system("你是一个AI智能应用")
            .user(message)
            .function("getWeather", new WeatherService())
            .call()
            .content();
}
(8)结构化输出模块
@GetMapping("/chat/structured")
public String chatStructured(@RequestParam String message) {
    String response = chatClient.prompt()
            .system("你是一个AI智能应用")
            .user(message)
            .call()
            .content();

    // 将文本输出转换为JSON格式
    Map<String, String> result = new HashMap<>();
    result.put("message", message);
    result.put("response", response);

    return new Gson().toJson(result);
}

(二)部署与运维

在完成项目开发后,需要将应用部署到服务器,并进行运维管理。

1. 部署

  • 选择服务器:选择合适的服务器,例如阿里云、腾讯云等。
  • 部署应用:将Spring Boot应用打包为JAR文件,上传到服务器,并启动应用。
  • 配置反向代理:使用Nginx等反向代理工具,配置域名解析和HTTPS。

2. 运维

  • 监控应用状态:使用监控工具(如Prometheus、Grafana)监控应用的运行状态。
  • 日志管理:使用日志管理工具(如ELK Stack)收集和分析应用日志。
  • 备份与恢复:定期备份应用数据,确保数据安全。
posted @   软件职业规划  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 35岁程序员的中年求职记:四次碰壁后的深度反思
· 当职场成战场:降职、阴谋与一场硬碰硬的抗争
· 用99元买的服务器搭一套CI/CD系统
· Excel百万数据如何快速导入?
· ShadowSql之.net sql拼写神器
点击右上角即可分享
微信分享提示