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
英文地址,請注明出處:http://en.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…