【JavaEE】【SpringAI】聊天模型

目录

  • 一、ChatClient
    • [1.1 实现简单对话](#1.1 实现简单对话)
    • [1.2 ⻆⾊预设](#1.2 ⻆⾊预设)
    • [1.3 结构化输出](#1.3 结构化输出)
    • [1.4 实现流式输出](#1.4 实现流式输出)
    • [1.5 打印日志](#1.5 打印日志)
  • 二、流式编程
    • [2.1 SSE协议](#2.1 SSE协议)
    • [2.2 Spring中的SSE实现](#2.2 Spring中的SSE实现)
  • 三、ChatModel
    • [3.1 实现简单对话](#3.1 实现简单对话)
    • [3.2 ⻆⾊预设](#3.2 ⻆⾊预设)
    • [3.3 实现流式输出](#3.3 实现流式输出)
    • [3.4 ChatModel与ChatClient对比](#3.4 ChatModel与ChatClient对比)

Spring AI 的聊天模型,通过标准化的接⼝设计,使开发⼈员可以将AI模型的聊天功能集成到应⽤程序中.它利⽤预先训练的语⾔模型,例如GPT(GenerativePre-trainedTransformer),以⾃然语⾔⽣成类似⼈类的响应.

API 的⼯作原理通常是向AI模型发送提⽰或部分对话,然后AI模型根据其训练数据和对⾃然语⾔模式的理解⽣成响应.然后,把响应将返回给应⽤程序,应⽤程序可以将其呈现给⽤⼾或将其⽤于进⼀步处理.

在SpringAI框架中,ChatModel和ChatClient是构建对话式AI应⽤的两⼤核⼼接⼝.

一、ChatClient

ChatClient 是 Spring AI 框架中封装复杂交互流程的⾼阶API接⼝,旨在简化开发者与⼤语⾔模型(如GPT、通义千问等)的集成过程.

ChatClient 提供了与AI模型通信的FluentAPI(流程接口,链式调用,可读性高),它⽀持同步和反应式(Reactive)编程模型,将与LLM及其他组件交互的复杂性进⾏封装,给⽤⼾提供开箱即⽤的服务

1.1 实现简单对话

根据⽤⼾输⼊的消息,进⾏响应.

参考链接:https://docs.spring.io/spring-ai/reference/api/chatclient.html

  1. 创建ChatClient,并注⼊
    使⽤ ChatClient.Builder 来创建ChatClient对象
java 复制代码
    private final ChatClient client;
    public ChatClientController(ChatClient.Builder clientBuilder) {
        this.client = clientBuilder.build();
    }
  1. 使⽤ChatClient
java 复制代码
    @RequestMapping("/call")
    public String call(String message) {
        return client.prompt()
                //⽤⼾输⼊的信息
                .user(message)
                //请求模型
                .call()
                //获取模型返回的信息
                .content();
    }
  • user 方法 把⽤⼾输⼊的内容设置为⽤⼾消息的内容
  • call ⽅法向AI模型发送请求
  • content ⽅法以字符串的形式返回AI模型的响应
java 复制代码
package com.spring.ai.controller;

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

@RestController
@RequestMapping("/chat")
public class ChatClientController {
    private final ChatClient client;
    public ChatClientController(ChatClient.Builder clientBuilder) {
        this.client = clientBuilder.build();
    }
    @RequestMapping("/call")
    public String call(String message) {
        return client.prompt()
                //⽤⼾输⼊的信息
                .user(message)
                //请求模型
                .call()
                //获取模型返回的信息
                .content();
    }
}

测试:http://127.0.0.1:8080/chat/call?message=2026春节

1.2 ⻆⾊预设

我们应⽤程序接⼊DeepSeek之后,可以设置⾃⼰智能助⼿的名字。

配置角色:

在SpringAI的 ChatClient.Builder 中, 使用 defaultSystem() ⽅法⽤于设置AI模型的默认系统消息(SystemMessage),它会作为对话的基础⻆⾊设定或初始指令。通过ChatClient.Builder链式调⽤设置后,该⽅法定义的⽂本会作为系统消息注⼊到每次对话的上下⽂中,⽤于引导AI的回复⻛格或⾝份设定.

我们通过 defaultSystem()方法来配置系统⻆⾊。并将这个方法提取出来交给spring管理。

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

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatClientConfig {
    @Bean
    public ChatClient ChatClientController(ChatClient.Builder clientBuilder) {
        return clientBuilder
                .defaultSystem("你是⼀个专业的聊天机器人,你叫鸽鸽,你会回答用户的问题")
                .build();
    }
}

测试:http://127.0.0.1:8080/chat/call?message=你是谁

1.3 结构化输出

想从LLM接收结构化输出,SpringAI⽀持将 ChatModel/ChatClient ⽅法的返回类型从 String 更改为其他类型.

通过 entity() ⽅法将模型输出转为⾃定义实体,需确保输出格式符合JSON规范。

借助JDK16提供的新关键词record来定义⼀个实体类:

java 复制代码
    record Recipe(String dish, List<String> ingredients) {}
    @RequestMapping("/entity")
    public String entity(String message) {
        Recipe recipe =  client.prompt()
                //⽤⼾输⼊的信息
                .user(message)
                //请求模型
                .call()
                //获取模型返回的信息
                .entity(Recipe.class);
        return recipe.toString();
    }

测试:http://127.0.0.1:8080/chat/entity?message=你是谁

1.4 实现流式输出

⽤⼾和⼤模型进⾏交互时,由于⼤模型⼀次输出内容较多,等待全部内容⽣成完毕会导致⽤⼾等待时间过⻓,这对⽤⼾的体验⾮常不友好.可以采⽤流式输出的⽅式(例如ChatGPT和DeepSeek逐字显⽰回答).

⼤模型流式输出(StreamingOutput)是通过逐步⽣成内容⽽⾮⼀次性返回完整结果的技术.SpringAI 使⽤

ChatClient 的 stream() ⽅法⽣成 Flux 流,适⽤于需要更轻量级客⼾端控制的场景.

java 复制代码
    @RequestMapping(value = "/stream",produces = "text/html;charset=UTF-8")
    public Flux<String> stream(String message) {
        return client.prompt()
                //⽤⼾输⼊的信息
                .user(message)
                //请求模型,流式输出
                .stream()
                //获取模型返回的信息
                .content();
    }

测试:http://127.0.0.1:8080/chat/stream?message=你是谁

1.5 打印日志

Advisors介绍:

Spring AI中的Advisors是介于⽤⼾请求与AI模型之间的中间件组件,它的核⼼功能就是对请求进⾏拦截过滤和增强,帮助我们在API调⽤前后解决各种问题,例如调⽤前参数如何构建,调⽤后结果如何处理。Spring AI 中的Advisors是基于AOP思想实现的,在具体实现上进⾏了领域适配。其设计核⼼借鉴了Spring AOP的拦截机制,各个Advisor以链式结构运⾏,序列中的每个Advisor都有机会对传⼊的请求和传出的响应进⾏处理。这种链式处理机制确保了每个Advisor可以在请求和响应流中添加⾃⼰的逻辑,从⽽实现更灵活和可定制的功能.

SimpleLoggerAdvisor:Spring AI 内置了⼀些Advisor,SimpleLoggerAdvisor 作为其中之⼀, 主要功能是记录⽇志.使⽤只需把它添加到Advisor链中,即可⾃动记录所有经过该Advisor的聊天请求和响应,并且可以对其进⾏配置,⽐如⽇志级别和⽇志格式。

两种使用方式:

  1. 添加在配置类中:
  2. 为具体对话添加:

配置日志级别:

java 复制代码
logging:
  level:
    org.springframework.ai.chat.client.advisor: debug

测试:http://127.0.0.1:8080/chat/stream?message=你是谁

二、流式编程

2.1 SSE协议

HTTP协议本⾝设计为⽆状态的请求-响应模式,严格来说,是⽆法做到服务器主动推送消息到客⼾端,但通过Server-SentEvents(服务器发送事件,简称SSE)技术可实现流式传输,允许服务器主动向浏览器推送数据流。也就是说,服务器向客⼾端声明,接下来要发送的是流消息(streaming),这时客⼾端不会关闭连接,会⼀直等待服务器发送过来新的数据流.

SSE(Server-Sent Events)是⼀种基于HTTP的轻量级实时通信协议,浏览器通过内置的 EventSource API接收并处理这些实时事件。

核⼼特点

  • 基于HTTP协议
    复⽤标准HTTP/HTTPS协议,⽆需额外端⼝或协议,兼容性好且易于部署
  • 单向通信机制
    SSE仅⽀持服务器向客⼾端的单向数据推送,客⼾端通过普通HTTP请求建⽴连接后,服务器可持续发送数据流,但客⼾端⽆法通过同⼀连接向服务器发送数据.
  • ⾃动重连机制
    ⽀持断线重连,连接中断时,浏览器会⾃动尝试重新连接(⽀持retry字段指定重连间隔)
  • ⾃定义消息类型
    客⼾端发起请求后,服务器保持连接开放,响应头设置 Content-Type: text/event-stream ,标识为事件流格式,持续推送事件流

数据格式:服务端向浏览器发送SSE数据,需要设置必要的HTTP头信息。

java 复制代码
Content-Type: text/event-stream;charset=utf-8
Connection: keep-alive

每⼀次发送的消息,由若⼲个message组成,每个message之间由 \n\n 分隔,每个message内部由若⼲⾏组成,每⼀⾏都是如下格式:[field]: value\n

Field 可以取值为:

  • data[必需]:数据内容
  • event[⾮必需]:表⽰⾃定义的事件类型,默认是message事件
  • id[⾮必需]:数据标识符,相当于每⼀条数据的编号
  • retry[⾮必需]:指定浏览器重新发起连接的时间间隔
  • 除此之外,还可以有冒号 : 开头的⾏,表⽰注释.

服务端实现:

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

import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

public class SSEController {
    @RequestMapping("/data")
    public void data(HttpServletResponse response) throws InterruptedException, IOException {
        response.setContentType("text/event-stream;charset=UTF-8");
        PrintWriter writer = response.getWriter();
        String s = "";
        for (int i = 0; i < 20; i++) {
            s = "data: " +new Date() + "\n\n";
            writer.write(s);
            writer.flush();
            Thread.sleep(1000);
        }
    }
}

客户端API:

java 复制代码
<div id="sse"></div>
<script>
  let eventSource = new EventSource("/sse/data");
  eventSource.onmessage = function(event){
    document.getElementById("sse").innerHTML = event.data;
  }
</script>

会循环发送请求给后端:

2.2 Spring中的SSE实现

Spring 4.2 开始就已经⽀持SSE,从Spring5开始我们可以使⽤WebFlux更优雅的实现SSE协议.Flux是WebFlux的核⼼API.

快速使⽤

Flux的流程分为三个步骤:

  1. 创建Flux
    创建⼀个Flux数据流,并有数据源.
  2. 处理数据
    使⽤操作符对数据进⾏处理
  3. 订阅数据
    订阅Flux来消费数据,触发数据的流动
java 复制代码
public class FluxDemoTest {
    public static void main(String[] args) throws InterruptedException {

        //创建 Flux
        Flux<String> fruitFlux = Flux.just("Apple", "Banana", "Cherry")
                .delayElements(Duration.ofSeconds(1));
        //处理数据并订阅数据
        fruitFlux.map(String::toUpperCase).subscribe(System.out::println);
        //防⽌程序处理完成之前,进程结束
        Thread.sleep(4000);
    }
}
操作 作⽤ ⽰例代码⽚段
map() 元素⼀对⼀转换 .map(String::toUpperCase)
filter() 条件过滤 .filter(s -> s.length() > 5)
take() 限制元素数量 .take(2) //只取前2个元素
merge() 合并多个Flux(不保证顺序) Flux.merge(Flux.just("A"), Flux.just("B"))
concat() 顺序拼接多个Flux(保证顺序) Flux.concat(Flux.just("A"), Flux.just("B"))
delayElements 延迟元素发射 .delayElements(Duration.ofSeconds(1))

三、ChatModel

ChatModel是SpringAI构建对话应⽤的核⼼接⼝,它抽象了应⽤与模型交互的过程,包括使⽤Prompt 作为输⼊,使⽤ ChatResponse 作为输出等.ChatModel的⼯作原理是接收Prompt或部分对话作为输⼊,将输⼊发送给后端⼤模型,模型根据其训练数据和对⾃然语⾔的理解⽣成对话响应,应⽤程序可以将响应呈现给⽤⼾或⽤于进⼀步处理。ChatClient 本质上也是基于ChatModel进⾏的封装和增强。

3.1 实现简单对话

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

import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/ds")
public class DeepSeekChatController {
    @Autowired
    private OpenAiChatModel openAiChatModel;
    @GetMapping("/callByPrompt")
    public String callByPrompt(String message) {
        ChatResponse response = openAiChatModel.call(new Prompt(message));
        return response.getResult().getOutput().getText();
    }
}

测试:http://127.0.0.1:8080/ds/callByPrompt?message=祝大家新年快乐

3.2 ⻆⾊预设

在SpringAI中,ChatModel ⽀持通过Prompt预设⻆⾊,这是引导模型输出特定⻛格或专业内容的

核⼼技术⼿段.

java 复制代码
    @GetMapping(value = "/role")
    public String role(String message) {
        SystemMessage systemMsg = new SystemMessage(" 你叫小黑子,会鸡叫");
        UserMessage userMsg = new UserMessage(message);
        Prompt prompt = new Prompt(List.of(systemMsg, userMsg));
        ChatResponse response = openAiChatModel.call(prompt);
        return response.getResult().getOutput().getText();
    }

测试:http://127.0.0.1:8080/ds/role?message=你是谁

3.3 实现流式输出

java 复制代码
@GetMapping(value = "/callByStream", produces = "text/html;charset=utf-8")
public Flux<String> callByStream(String message) {
	Flux<ChatResponse> response = openAiChatModel.stream(new Prompt(message));
	return response.map(x->x.getResult().getOutput().getText());
}

测试:http://127.0.0.1:8080/ds/callByStream?message=你是谁

3.4 ChatModel与ChatClient对比

维度 ChatModel ChatClient
交互⽅式 ⼿动构建Prompt,解析响应 链式API,⾃动封装请求与响应
结构化输出 ⼿动解析⽂本 ⽀持 .entity(Class) ⾃动映射POJO
扩展能⼒ 依赖外部组件 内置Advisor机制,提供更⾼级的功能,如提供上下⽂记忆,RAG等功能
适合场景 适合需要精细控制模型参数的场景,⽐如模型实验,参数调优等定制需求 适合快速构建AI服务,如带记忆的客服系统
相关推荐
韭菜张师傅2 小时前
Ceph环境完全重置指南:彻底清理集群环境
java·网络·ceph
SunnyDays10112 小时前
使用 Java 实现 Word 文档水印自动化(全面指南)
java·添加水印·word文档
敲代码的小王!2 小时前
prompt开发游戏-哄哄模拟器
java·游戏·ai·prompt
学编程就要猛2 小时前
JavaEE:多线程初阶
java·开发语言·jvm
筱顾大牛2 小时前
缓存更新策略
java·redis·缓存
sheji34162 小时前
【开题答辩全过程】以 慧医疗网上医院管理系统为例,包含答辩的问题和答案
java
子一!!2 小时前
JavaEE初阶第一课时==计算机与系统讨论==
java·java-ee
小二·2 小时前
Go 语言系统编程与云原生开发实战(第37篇)
java·云原生·golang
yxc_inspire2 小时前
大二 Java 后端学习记录:集合框架(List/Queue/Map/Set)+ 泛型 + 迭代器
java·开发语言