SpringAI——ChatModel

 我的前面一篇文章(SpringAI——ChatClient配置与使用)中讲了ChatClient,它是一个构建于 ChatModel 之上的高层封装,它提供了更丰富的对话交互能力。可以这么说ChatModel相当于发动机,ChatClient相当于一台含有发动机、方向盘、座椅等的一台可以由驾驶员操控的的完整的车。

什么是 Chat Model?

 Chat Model 是 Spring AI 定义的文本对话模型接口,抽象了应用与大模型的交互逻辑,对话模型,接收一系列消息(Message)作为输入,与模型 LLM 服务进行交互,并接收返回的聊天消息(Chat Message)作为输出:

    输入 :使用 Prompt封装用户输入,,支持纯文本及多角色对话(如系统指令、用户问题)。

    输出 :通过 ChatResponse 返回结构化结果,包含模型生成的文本内容及元数据(如 Token 消耗)。


ChatModel工作原理

  1. 模型封装与适配层:ChatModel 是一个统一接口层,它的作用是将不同平台的语言模型(如 OpenAI、阿里云 DashScope、百度文心一言、自建模型等)统一抽象为相同的调用方式。
  2. 核心方法说明:支持同步调用和流式调用
  3. 内部工作机制流程图

  

 ChatModel 的核心组件解析

1. Message:对话的基本单元

Spring AI 使用 Message 表示对话中的每一条消息,包括:

  • 系统提示词:SystemMessage
  • 用户输入:UserMessage
  • 模型输出:AssistantMessage

2. ChatOptions:控制模型行为的参数   

 每个 ChatModel 实现都需要支持一组通用的配置参数,比如:temperature,maxTokens,topP,frequencyPenalty,presencePenalty
 

3. ChatResponse:模型输出的标准化封装

ChatModel的运用

ChatModel

文本聊天交互模型,支持纯文本格式作为输入,并将模型的输出以格式化文本形式返回。

spring-ai-alibaba-starter自动配置了ChatModel,所以使用的时候就直接引入就可以了。

chatModel.call()方法可以传入Prompt,Message集合,或者直接传入字符串。

首先是Prompt形式:

@RestController
@RequestMapping("/ai/v1")
public class ChatModelController {@Autowiredprivate ChatModel chatModel;@RequestMapping("/chatModel/chat")public String chat(String input) {ChatOptions options = ChatOptions.builder().temperature(0.9).maxTokens(1024).build();Prompt query = new Prompt(input,options);ChatResponse call = chatModel.call(query);return call.getResult().getOutput().getContent();}

我也可以是配置模型参数,通过 ChatOptions 在每次调用中调整模型参数 

Message形式:

@Autowiredprivate ChatModel chatModel;@RequestMapping("/chatModel/chat")public String chat(String input) {UserMessage userMessage = new UserMessage(input);SystemMessage systemMessage = new SystemMessage("你作为一个资深的java程序员,请根据你的专业知识回答下面问题,如果不是你专业范围内的问题,请直接说不知道,不要做任何解释:");return chatModel.call(userMessage,systemMessage);}

Message有不同的角色,其实现类有UserMessage,SystemMessage, AssistantMessage

结果:因为我通过SystemMessage限制了大模型的回答

字符串形式的我就不解释了,接下来我们看一下流式响应

ChatModel的流式响应

我是通过SseEmitter来实现服务器与客户端的单向通道消息推送,这里就不过多解释,如果不熟悉可以提前了解一下。

简单写一个SseEmitter的工具类:

@Component
@Slf4j
public class SSEUtils {private final Map<String, SseEmitter> pool = new ConcurrentHashMap<String, SseEmitter>();/*** sse发送消息** @param ids* @param content*/public void sendMessageBySSE(List<String> ids, String content) {log.info("SSE对象暂存池数据={}" ,pool );for (String id : ids) {SseEmitter sseEmitter = pool.get(id);if (sseEmitter == null) {
//0L表示一致存在sseEmitter = new SseEmitter(0L);
// sseEmitter.onCompletion(()->pool.remove(id));sseEmitter.onTimeout(() -> pool.remove(id));pool.put(id, sseEmitter);}try {sseEmitter.send(content);} catch (IOException e) {e.printStackTrace();}}}public SseEmitter subscribe(String id) {SseEmitter sseEmitter = pool.get(id);if (sseEmitter == null) {
//1000秒 3600000LsseEmitter = new SseEmitter(0L);
// sseEmitter.onCompletion(() -> pool.remove(id));sseEmitter.onTimeout(() -> pool.remove(id));pool.put(id, sseEmitter);}return sseEmitter;}public void loginOut(String id) {SseEmitter sseEmitter = pool.get(id);if (sseEmitter != null) {pool.remove(id);}}
}

 连接SSE的接口:

@RequestMapping("/ai/v1")
@RestController
public class SseController {@AutowiredSSEUtils sseUtils;@GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public SseEmitter subscribe(@RequestParam("id") String id){return sseUtils.subscribe(id);}
}

 流式消息推送的接口:

@Autowired
SSEUtils sseUtils;@RequestMapping("/chatModel/stream")
public String chatStream(String id,String input) {Prompt query = new Prompt( input);Flux<ChatResponse> stream = chatModel.stream(query);// 订阅流并处理每个响应stream.subscribe(response -> {// 提取并打印响应内容String content = response.getResults().get(0).getOutput().getContent();sseUtils.sendMessageBySSE(Collections.singletonList(id),content);});return "success";}

使用apiPost 测试:

ImageModel

接收用户文本输入,并将模型生成的图片作为输出返回。

 @Autowiredprivate ImageModel imageModel;@RequestMapping("/chatModel/image")public String image(String input) throws IOException {ImageOptions options = ImageOptionsBuilder.builder().height(1024).width(1024).build();ImagePrompt query = new ImagePrompt(input,options);return  "redirect:" + imageModel.call(query).getResult().getOutput().getUrl();}

 ImageOptions 可以设置模型名称,图片的大小等。

AudioModel

接收用户文本输入,并将模型合成的语音作为输出返回。支持流式响应

文字转语音:

  @Autowiredprivate SpeechSynthesisModel speechModel;@RequestMapping("/chatModel/speech")public void speech(String input, HttpServletResponse response) throws IOException {SpeechSynthesisPrompt prompt = new SpeechSynthesisPrompt( input);SpeechSynthesisResponse call = speechModel.call(prompt);ByteBuffer audio = call.getResult().getOutput().getAudio();response.getOutputStream().write(audio.array());}

 语音转文字:

 @AutowiredAudioTranscriptionModel audioTranscriptionModel;@PostMapping("/chatModel/audio")public String audio2() throws IOException {Resource resource = new UrlResource("https://dashscope.oss-cn-beijing.aliyuncs.com/samples/audio/paraformer/hello_world_female2.wav");DashScopeAudioTranscriptionOptions options = DashScopeAudioTranscriptionOptions.builder().withModel("sensevoice-v1").build();AudioTranscriptionPrompt query = new AudioTranscriptionPrompt(resource,options);return audioTranscriptionModel.call(query).getResult().getOutput();}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/89612.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89612.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Zabbix监控K8S的PV信息详细教程!

文将介绍如何使用Zabbix自定义键值脚本方式监控K8S的PV卷状态等信息。 在Kubernetes (K8S) 中&#xff0c;PersistentVolume (PV) 是集群中的一个抽象层&#xff0c;它代表了底层存储资源&#xff0c;例如网络存储系统&#xff08;如NFS、Ceph、GlusterFS等&#xff09;或本地存…

wx小程序原生开发使用高德地图api

第一步&#xff1a;注册高德地图api的key第二步&#xff1a;下载amap-wx.js 放到项目的某个目录第三步&#xff1a;配置app.json&#xff08;必须&#xff0c;因为需要定位功能&#xff0c;&#xff09;"requiredPrivateInfos": ["getLocation"],"per…

如何通过mac的前24bit,模糊确认是那一台什么样的设备

MAC Address Lookup - MAC/OUI/IAB/IEEE Vendor Manufacturer Search Wireshark • Go Deep 上面这两个网址提供了&#xff0c;正对mac 的前24位&#xff0c;查找对应的网络设备厂商信息&#xff0c;可以让我们在运维过程中模糊的判断大约是什么型号的设备 使用macvendorloo…

【爬虫】04 - 高级数据存储

爬虫04 - 高级数据存储 文章目录爬虫04 - 高级数据存储一&#xff1a;加密数据的存储二&#xff1a;JSON Schema校验三&#xff1a;云原生NoSQL(了解)四&#xff1a;Redis Edge近端计算(了解)五&#xff1a;二进制存储1&#xff1a;Pickle2&#xff1a;Parquet一&#xff1a;加…

UDP和TCP的主要区别是什么?

在网络通信中&#xff0c;TCP&#xff08;传输控制协议&#xff09;和UDP&#xff08;用户数据报协议&#xff09;是两种核心的传输层协议。它们各自的特点和应用场景截然不同&#xff0c;理解两者的区别对于选择合适的通信方式至关重要。本文将通过几个关键点&#xff0c;用简…

Softhub软件下载站实战开发(十八):软件分类展示

Softhub软件下载站实战开发&#xff08;十八&#xff09;&#xff1a;软件分类展示 &#x1f5a5;️ 在之前文章中&#xff0c;我们实现了后台管理相关部分&#xff0c;本篇文章开始我们来实现用户端页面&#xff0c;由于内网使用&#xff0c;不需要sso优化等特性&#xff0c;我…

linux--------------------BlockQueue的生产者消费模型

1.基础BlockingQueue的生产者消费模型 1.1 BlockQueue 在多线程编程中阻塞队列是一种常用于实现生产者和消费者模型的数据结构&#xff0c;它与普通的队列区别在于&#xff0c;当队列为空时&#xff0c;从队列获取元素的操作将被阻塞&#xff0c;直到队列中放入了新的数据。当…

堆排序算法详解:原理、实现与C语言代码

堆排序&#xff08;Heap Sort&#xff09;是一种高效的排序算法&#xff0c;利用二叉堆数据结构实现。其核心思想是将待排序序列构造成一个大顶堆&#xff08;或小顶堆&#xff09;&#xff0c;通过反复调整堆结构完成排序。下面从原理到实现进行详细解析。一、核心概念&#x…

SSM框架——注入类型

引用类型的注入&#xff1a;Setter方法简单类型的注入&#xff1a;定义简单实例和方法在配置文件中对bean进行配置&#xff0c;使用porperty属性 值用value&#xff08;ref是用来获取bean的&#xff09;构造器方法&#xff1a;构造器方法中需要写name&#xff0c;这样程序就会耦…

信息学奥赛一本通 1552:【例 1】点的距离

【题目链接】 ybt 1552&#xff1a;【例 1】点的距离 【题目考点】 1. 最近公共祖先&#xff08;LCA&#xff09;&#xff1a;倍增求LCA 知识点讲解见&#xff1a;洛谷 P3379 【模板】最近公共祖先&#xff08;LCA&#xff09; 【解题思路】 首先用邻接表保存输入的无权图…

1Panel中的OpenResty使用alias

问题 在服务器上使用了1Panel的OpenResty来管理网站服务&#xff0c;当作是一个Nginx用&#xff0c;想做一个alias来直接管理某个文件夹的文件&#xff0c;于是直接在其中一个网站中使用了alias配置。 location /upload {alias /root/upload;autoindex on;charset utf-8;charse…

小明记账簿焕新记:从单色到多彩的主题进化之路

【从冷静蓝到多彩世界&#xff0c;这一次我们重新定义记账美学】 曾经&#xff0c;打开“小明记账簿”是一片沉稳的蓝色海洋&#xff0c;它像一位理性的财务管家&#xff0c;默默守护着你的每一笔收支。但总有人悄悄问&#xff1a;“能不能多一些颜色&#xff1f;”今天&#x…

Apache IoTDB(1):时序数据库介绍与单机版安装部署指南

目录一、Apache IoTDB 是什么&#xff1f;1.1 产品介绍1.2 产品体系1.3 产品架构二、IoTDB 环境配置2.1 Linux系统需准备环境2.2 Windows系统需准备环境2.3 网络配置2.3.1 关闭防火墙2.3.2 查看端口是否占用2.3.3 避雷经验三、IoTDB 单机版系统部署安装指南3.1 产品下载3.2 注意…

Python 图片爬取入门:从手动下载到自动批量获取

前言 想批量下载网页图片却嫌手动保存太麻烦&#xff1f;本文用 Python 带你实现自动爬取&#xff0c;从分析网站到代码运行&#xff0c;步骤清晰&#xff0c;新手也能快速上手&#xff0c;轻松搞定图片批量获取。 1.安装模块 在开始爬取图片前&#xff0c;我们需要准备好工具…

aspect-ratio: 1 / 1样式在部分手机浏览器中失效的问题怎么解决?

最近在uniapp开发时又遇到了安卓手机不兼容问题&#xff0c;ios系统无影响。开发背景&#xff1a;小编想通过网格布局来实现答题卡的布局&#xff0c;实现五列多行的形式。代码片段&#xff1a;<view class"question-grid"><viewv-for"(question, inde…

RecyclerView与ListView深度对比分析

1. 使用流程对比ListView: 布局XML&#xff1a; 在布局文件中放置 <ListView> 控件&#xff0c;指定 id (如 android:id"id/listView")。数据适配器 (Adapter)&#xff1a; 继承 BaseAdapter 或 ArrayAdapter / CursorAdapter / SimpleAdapter。 重写 getCount…

deepseekAI对接大模型的网页PHP源码带管理后台(可实现上传分析文件)

前端后端都已进行优化&#xff0c;新增可上传文件功能&#xff08;拖拽进去也可以&#xff09;&#xff0c;后端进行风格主题设置&#xff0c;优化数据结构&#xff01;依旧测试网站&#xff1a;iEPMS我的工具箱&#xff0c;你的智慧助手&#xff01;还是那句话兄弟们轻点搞我的…

NJU 凸优化导论(9) 对偶(II)KKT条件+变形重构

https://www.lamda.nju.edu.cn/chengq/optfall24/slides/Lecture_9.pdf 目录 关于对偶的一些解释 1. Max-min characterization 最大最小准则 2. Saddle-point Interpretation 鞍点解释 3. Game interpretation 博弈论里的对偶 Optimality Conditions 最优条件 1. Certi…

Vue Swiper组件

Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue Swiper组件实现笔记 目录 Swiper组件 下载swiper 创建swiper组件 保存时修复 编写swiper内容 引入swiper 使用swiper Swiper子组件 创建Swiper列表组件 使用子组件 增加生命周期 增加图片显示 加载数据 渲染…

Linux:lvs集群技术

一.集群和分布式1.1 集群集群是为了解决某个特定问题将多台计算机组合起来形成的单个系统。即当单独一台主机无法承载现有的用户请求量&#xff1b;或者一台主机因为单一故障导致业务中断的时候&#xff0c;就可以增加服务主机数&#xff0c;这些主机在一起提供服务&#xff0c…