Spring AI實戰之二:Chat API基礎知識大串講(重要)

歡迎訪問我的GitHub

這里分類和匯總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

Spring AI實戰全系列鏈接

  1. Spring AI實戰之一:快速體驗(OpenAI)
  2. Spring AI實戰之二:Chat API基礎知識大串講(重要)
  3. SpringAI+Ollama三部曲之一:極速體驗
  4. SpringAI+Ollama三部曲之二:細說開發

本篇概覽

  • 如果說前文是最簡單的介紹Spring AI,滿足Java程序員的好奇心,那么本篇就是正兒八經的基礎課了:梳理Spring AI框架中的Chat核心API、類、接口,SpringAI的能力就是依靠它們釋放出來的
  • 本篇的目標:學習SpringAI庫的最基本的類、接口、API,并了解它們的具體用途

用一個問題開篇

  • 首先回答一個問題,為什么標題是Chat API基礎知識,而不是Spring AI基礎知識?
  • 因為Spring AI內部由很多部分組成,Chat只是其中之一,或者說大模型提供的能力有很多,聊天只是其中一部分,SpringAI提供的能力如下所示
    在這里插入圖片描述
  • 所以,Spring AI的內容很多,Chat只是其中一部分,但是這部分非常重要且基礎,適合用來入門
  • 接下來開始正式學習吧,東西不多,總結下來就是:六個概念+三個層次,掌握了它們,各種大模型都能輕松駕馭了

關于Chat API的六個核心概念和三個層次

  • 從業務邏輯上看,Chat API涉及到六個概念,分為三類,如下圖所示
    在這里插入圖片描述
  1. client:這個好理解,代表各模型的客戶端,負責請求和響應的
  2. prompt:理解成請求的最外層封裝,里面有message和option
  3. message:這個好理解了,發送到大模型的內容,另外還包含了一些屬性在里面,以及消息類型
  4. option:相當于參數、控制項,例如本次對話的temperature(值越小,大模型回答越嚴謹,值越大,大模型回答越有創造性)
  5. response:響應對象,里面封裝了大模型返回的信息,主要是generation
  6. geenration:這里面是具體的返回內容
  • 再來看三個層次,前面我們知道SpringAI支持大模型的多種能力,聊天只是其中一種,因此就有一個代表最頂層的抽象層,與大模型有關的各種能力,都在此有個定義,然后是代表各種能力的抽象層,如聊天、圖片、嵌入式處理等,最后是每一種能力在各類具體大模型上的實現,如下圖所示
    在這里插入圖片描述
  • 到現在為止咱們還沒有看一行代碼一個API,但是從理論上對Chat API的定位、關系已經基本了解了,是時候結合代碼來看了

官方圖

  • 下圖來自官方文檔,結合前面的分析來看一下,后面有導讀
    在這里插入圖片描述
  • 先看最下面橙色這層,中間是client,這里有兩種,ModelClient代表了常規的請求響應,StreamingModelClient代表了流式響應(數據并非一次性傳輸,而是建立鏈接后源源不斷的輸出)
  • client的左側是request,里面包含了option,至于prompt,那是Chat的概念,所以不會出現在橙色這一層
  • client右側是response,同樣只有抽象的ResponseMeta和ResultMetaData,generation是Chat的概念,不會在橙色這一層出現
  • 再往上看,綠色的就是功能抽象層了,ChatClient繼承了ModelClient,Prompt繼承了ModelRequest,代表Chat領域的請求,同理CharResponse繼承了ModelResponse
  • 有了理論基礎,一張官方圖就讓我們看清了Chat API的大概,現在還缺點東西,就是具體的實現層,畢竟有很多種大模型能,最終編碼時還是要用到實現層的類,有沒有什么方式將實現層完美的展現出來?
  • 感謝SpringAI官方,實現層和功能抽象層的關系,被下面的官方圖梳理得清清楚楚
    在這里插入圖片描述
  • 此刻再來回顧Spring AI實戰之一:快速體驗(OpenAI)一文的代碼,如下所示,盡管調用了OpenAI的接口,但是并未看到OpenAI相關的類,這是因為Spring已經做好了封裝,咱們直接用依賴注入的ChatClient即可,這是抽象層接口,具體實現是SpringAI根據propeties的配置實例化的OpeAIChatClient對象
package com.bolingcavalry.helloopenai.controller;import org.springframework.ai.chat.ChatClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.Map;@RestController
public class SimpleAiController {// 負責處理OpenAI的bean,所需參數來自properties文件private final ChatClient chatClient;public SimpleAiController(ChatClient chatClient) {this.chatClient = chatClient;}@PostMapping("/ai/simple")public Map<String, String> completion(@RequestBody Map<String,String> map) {return Map.of("generation", chatClient.call(map.get("message")));}
}
  • 其實說到這里,您已經對Chat API有了比較清晰的理解了,來看看那六大概念的具體代碼吧,接下來是一段自然的、水到渠成的體驗,畢竟已經領會了其神,現在是觀其形的時候
  • 接下來要看的代碼如下圖所示
    在這里插入圖片描述

ChatClient

  • 大模型聊天功能的客戶端接口,在進程中,其實現就是各大模型對應的客戶端類
public interface ChatClient extends ModelClient<Prompt, ChatResponse> {default String call(String message) {// implementation omitted}@OverrideChatResponse call(Prompt prompt);
}
  • 可見主要是call方法,這就是最常規的聊天功能,調用call發送請求,返回值就是大模型的響應

StreamingChatClient

  • 這也是客戶端類,用于調用大模型的功能,與ChatClient不同的是,ChatClient是請求響應,返回對象ChatResponse就是大模型返回的全部內容,而StreamingChatClient返回的是Flux,這是流式返回,可以講大模型的響應進行流式輸出,如果您使用過各種大模型聊天工具,會發現響應的內容并非一次性展現,而是一段一段的內容,持續不斷的展現出來,這就是流式響應的效果
@FunctionalInterface
public interface StreamingChatClient extends StreamingModelClient<Prompt, ChatResponse> {default Flux<String> stream(String message) {Prompt prompt = new Prompt(message);return stream(prompt).map(response -> (response.getResult() == null || response.getResult().getOutput() == null|| response.getResult().getOutput().getContent() == null) ? "": response.getResult().getOutput().getContent());}@OverrideFlux<ChatResponse> stream(Prompt prompt);}
  • 注意注解FunctionalInterface,表明這是個函數式接口

Prompt

  • 前面看過了ChatClient和StreamingChatClient,會發現入參都是Prompt,可見這就是和大模型一次聊天的入參
  • 下面是Prompt的源碼,去掉了構造函數、toString這些之后就會發現,最重要的是Message和ChatOption,所以Prompt只是個打包,真正要提交到大模型的其實是Message和ChatOption
public class Prompt implements ModelRequest<List<Message>> {private final List<Message> messages;private ChatOptions modelOptions;@Overridepublic ChatOptions getOptions() {..}@Overridepublic List<Message> getInstructions() {...}public String getContents() {StringBuilder sb = new StringBuilder();for (Message message : getInstructions()) {sb.append(message.getContent());}return sb.toString();}// constructors and utility methods omitted
}
  • 如果您對OpenAI有所了解,就知道prompt(提示詞)并非只有用戶輸入的聊天內容那么簡單,而是system、user 、assistant等多種類型 ,所以這里的Prompt并非只是一個外殼那么簡單,它與不同類型的message、不同的輔助類等一起提供了完善的提示詞功能,這個會有單獨的文章來說明和實戰,本篇只要記得它的最終形態就是打好的包用于提交給大模型
  • 如果只是最基本的聊天,下面這個構造方法來創建對象就行了
	public Prompt(String contents) {this(new UserMessage(contents));}

Message

  • Message很好理解:在聊天過程中,聊天內容對應的對象,請求和響應用的都是Message,不過由于消息類型的多樣性,Message被設計成了接口,根據不同類型都有對應的實現,如下圖所示
    在這里插入圖片描述
  • Message自身非常簡單,能保證使用方取到消息內容、類型即可
public interface Message {String getContent();List<Media> getMedia();Map<String, Object> getProperties();MessageType getMessageType();}
  • 另外要注意的是消息類型,一共四種
public enum MessageType {USER("user"),ASSISTANT("assistant"),SYSTEM("system"),FUNCTION("function");

ChatOptions

  • ChatOptions代表可以傳遞給大模型的控制參數,具體有哪些參數和大模型自身開放的特性有關,舉個例子,下面是OpenAI開放的參數
  1. presencePenalty : 影響模型在生成文本時重復詞語或概念的傾向
  2. frequencyPenalty:影響模型在生成文本時對已出現過詞語的偏好程度
  • 按照上面的解釋,既然各種大模型都有自己的參數,那么設計ChatOptions能干啥?應該能放一些通用的控制參數吧,打開代碼一看果然如此,共有三個通用參數,我都加了中文注釋,另外請關注類的注釋,也說明了這些參數是通用的、可移植、夸模型
/*** The ChatOptions represent the common options, portable across different chat models.*/
public interface ChatOptions extends ModelOptions {// 大模型生成的內容應該更嚴謹還是更有創造性Float getTemperature();// 返回概率超過P的所有內容Float getTopP();// 返回概率最高的前K個內容Integer getTopK();
}
  • ChatOptions只是接口,對應的實現是ChatOptionsImpl,源碼沒啥好看的,就是temperature、topP、topK的get和set而已,為了實例化ChatOptionsImpl,還有配套工具ChatOptionsBuilder,用法如下
ChatOptions portablePromptOptions = ChatOptionsBuilder.builder().withTemperature(0.9f).withTopK(100).withTopP(0.6f).build();
  • 代碼看到這里,長期CRUD的我不禁產生一個想法:ChatOptions接口應該很不實用,而且用起來也很別扭,因為各大模型特有的參數和這個接口都沒有關系,去看了下OpenAiChatClient.java(Ollama的客戶端實現類,里面有段代碼是用來封裝請求的),果然,這代碼真是不夠優雅(個人感覺)
    在這里插入圖片描述

ChatResponse

  • 看完請求該看響應了,既然Generation才是真正的響應內容,那么ChatResponse也就是個殼,里面包了Generation,打開源碼一看,只有Generation和ChatResponseMetadata,這個ChatResponseMetadata可以理解為元信息,主要返回了大模型的API的使用情況說明,以及限速的詳細信息
public class ChatResponse implements ModelResponse<Generation> {private final ChatResponseMetadata chatResponseMetadata;private final List<Generation> generations;@Overridepublic ChatResponseMetadata getMetadata() {...}@Overridepublic List<Generation> getResults() {...}// other methods omitted
}

Generation

  • Generation中有響應的具體信息,由ChatGenerationMetadata和AssistantMessage組成
public class Generation implements ModelResult<AssistantMessage> {private AssistantMessage assistantMessage;private ChatGenerationMetadata chatGenerationMetadata;@Overridepublic AssistantMessage getOutput() {...}@Overridepublic ChatGenerationMetadata getMetadata() {...}// other methods omitted
}
  • ChatGenerationMetadata代表返回內容的元信息,包含了結束原因、生成內容的過濾規則
  • AssistantMessage更容易理解了:類型是ASSISTANT的消息,這個assistant就是助理角色,assistant消息就是大模型返回的聊天響應,源碼如下
public class AssistantMessage extends AbstractMessage {public AssistantMessage(String content) {super(MessageType.ASSISTANT, content);}public AssistantMessage(String content, Map<String, Object> properties) {super(MessageType.ASSISTANT, content, properties);}@Overridepublic String toString() {return "AssistantMessage{" + "content='" + getContent() + '\'' + ", properties=" + properties + ", messageType="+ messageType + '}';}}
  • 至此,基礎理論知識已經過了一遍,相信大家和我一樣,進入了一看就會,一用就廢的微妙階段,不急,下一篇就是精彩的實戰篇,這些知識點終究會在實戰中用到,隨著一行行代碼一次次請求被理解,最終融匯貫通

你不孤單,欣宸原創一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 數據庫+中間件系列
  6. DevOps系列

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/15698.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/15698.shtml
英文地址,請注明出處:http://en.pswp.cn/web/15698.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux:進程地址空間、進程控制(一.進程創建、進程終止、進程等待)

上次介紹了環境變量&#xff1a;Linux&#xff1a;進程概念&#xff08;四.main函數的參數、環境變量及其相關操作&#xff09; 文章目錄 1.程序地址空間知識點總結上述空間排布結構是在內存嗎&#xff1f;&#xff08;進程地址空間引入&#xff09; 2.進程地址空間明確幾個點進…

NDIS小端口驅動開發(三)

微型端口驅動程序處理來自過度驅動程序的發送請求&#xff0c;并發出接收指示。 在單個函數調用中&#xff0c;NDIS 微型端口驅動程序可以指示具有多個接收 NET_BUFFER_LIST 結構的鏈接列表。 微型端口驅動程序可以處理對每個NET_BUFFER_LIST結構上具有多個 NET_BUFFER 結構的多…

JAVA -- > 初識JAVA

初始JAVA 第一個JAVA程序詳解 public class Main {public static void main(String[] args) {System.out.println("Hello world");} }1.public class Main: 類型,作為被public修飾的類,必須與文件名一致 2.public static 是JAVA中main函數準寫法,記住該格式即可 …

python皮卡丘動畫代碼

在Python中&#xff0c;我們可以使用多種方法來創建皮卡丘的動畫&#xff0c;例如使用matplotlib庫。 解決方案1&#xff1a;使用matplotlib庫 以下是一個使用matplotlib庫創建皮卡丘動畫的例子&#xff1a; import matplotlib.pyplot as plt import matplotlib.animation …

Slash后臺管理系統代碼閱讀筆記 如何實現環形統計圖表卡片?

目前&#xff0c;工作臺界面的上半部分已經基本梳理完畢了。 接下來&#xff0c;我們看看這個環形圖卡片是怎么實現的&#xff1f; 具體代碼如下&#xff1a; {/*圖表卡片*/} <Row gutter{[16, 16]} className"mt-4" justify"center">{/*環形圖表…

U盤引導盤制作Rufus v4.5.2180

軟件介紹 Rufus小巧實用開源免費的U盤系統啟動盤制作工具和格式化U盤的小工具&#xff0c;它可以快速將ISO鏡像文件制作成可引導的USB啟動安裝盤&#xff0c;支持Windows或Linux啟動&#xff0c;堪稱寫入鏡像速度最快的U盤系統制作工具。 軟件截圖 更新日志 github.com/pbat…

嵌入式全棧開發學習筆記---C語言筆試復習大全24

目錄 內存管理 內存分配 堆和棧的區別&#xff1f;&#xff08;面試重點&#xff09; 申請內存的函數 malloc realloc free gcc工具鏈 編譯的過程&#xff08;面試重點&#xff09; 第一步&#xff0c;預處理&#xff1a; 第二步&#xff0c;編譯&#xff1a; 第三…

【Spring Boot】使用 Redis + Cafeine 實現二級緩存

使用 Redis Caffeine 實現二級緩存可以有效提升應用的性能和緩存的命中率。Caffeine 是一個高效的 Java 本地緩存庫&#xff0c;而 Redis 是一個分布式緩存解決方案。通過將兩者結合&#xff0c;Caffeine 作為一級緩存用于快速訪問常用數據&#xff0c;Redis 作為二級緩存用于…

解決LabVIEW通過OPC Server讀取PLC地址時的錯誤180121602

在使用LabVIEW通過OPC Server讀取PLC地址時&#xff0c;若遇到錯誤代碼180121602&#xff0c;建議檢查網絡連接、OPC Server和PLC配置、用戶權限及LabVIEW設置。確保網絡暢通&#xff0c;正確配置OPC變量&#xff0c;取消緩沖設置以實時讀取數據&#xff0c;并使用診斷工具驗證…

簡述vue常用指令

Vue.js 提供了許多內置指令&#xff0c;這些指令用于在模板中添加特殊功能。以下是一些 Vue 的常用內置指令的簡要說明&#xff1a; v-text&#xff1a; 更新元素的 textContent。示例&#xff1a;<span v-text"message"></span> v-html&#xff1a; 更…

2 使用香橙派AIpro報錯 No module named ‘acllite utils‘

當使用jupyter運行香橙派的notebooks下面的案例的時候啟動使用jupyter lab 然后自動跳轉到jupyter頁面。如下圖: 這是自動跳轉過來的。然后運行下面的包的導入后報錯: 報錯為No module named ‘acllite utils’,那么我們打開notebooks文件夾下面的start_notebooks.sh文件:…

【C++練級之路】【Lv.21】C++11——列表初始化和聲明

快樂的流暢&#xff1a;個人主頁 個人專欄&#xff1a;《算法神殿》《數據結構世界》《進擊的C》 遠方有一堆篝火&#xff0c;在為久候之人燃燒&#xff01; 文章目錄 引言一、列表初始化1.1 內置類型1.2 結構體或類1.3 容器 二、聲明2.1 auto2.2 decltype2.3 nullptr 三、STL的…

A*算法搜索的路徑是最優的么?

A * 算法&#xff08;A* Search Algorithm&#xff09;是一種啟發式搜索算法&#xff0c;它旨在找到從起點到終點的最短路徑。在滿足以下條件時&#xff0c;A*算法能夠保證找到最優路徑&#xff1a; 啟發式函數的一致性&#xff08;Consistency&#xff09;或可采納性&#xf…

從“反超”到“引領”,中國衛浴品牌憑何遙遙領先?

作者 | 曾響鈴 文 | 響鈴說 前不久&#xff0c;第28屆中國國際廚房、衛浴設施展覽會(以下簡稱“中國國際廚衛展”)在上海如期舉行&#xff0c;就結果來說真的讓人大開眼界。 沖水聲比蚊子聲更小的馬桶、能化身無感交互平臺的魔鏡柜、可以語音交互的淋浴器&#xff0c;這些“…

Keli5燒寫STM32程序時出現ST-LINK USB communication error錯誤(USB 通信錯誤)

1錯誤原圖 2錯誤原因 前提驅動安裝正確 原因1 usb接觸不良&#xff08;極少出現&#xff09; 解決方法 更換USB線 還不行連下載器一起更換 原因2&#xff08;出現概率比較大&#xff09; 下載器的固件出現問題或下載器固件版本與Keli5的版本不匹配 解決方法 在Keli5的…

[音視頻]ffmepg常用命令

ffmpeg 在音視頻的世界里&#xff0c;ffmpeg可是如雷貫耳的存在&#xff0c;學習音視頻開發&#xff0c;ffmpeg是必須掌握的技能 常用命令 保存m3u8文件 ffmpeg -i http://xxxxx/test.m3u8 -c copy result.mp4

今日早報 每日精選15條新聞簡報 每天一分鐘 知曉天下事 5月26日,星期日

每天一分鐘&#xff0c;知曉天下事&#xff01; 2024年5月26日 星期日 農歷四月十九 1、 醫保局&#xff1a;支持將符合條件的村衛生室納入醫保定點&#xff0c;方便農村居民就醫。 2、 網傳養老金儲備嚴重不足&#xff1f;央視辟謠&#xff1a;這筆錢二十多年來從未動用過&a…

搭建企業級AI應用的流程

搭建企業級AI應用的流程是一個復雜且系統化的工程&#xff0c;它需要從多個維度出發&#xff0c;確保最終的應用既符合企業的業務需求&#xff0c;也具備高效、穩定和可擴展的特性。以下是詳細的步驟&#xff1a; 初步接觸與需求分析是整個項目的基礎。在這一階段&#xff0c;我…

【C++題解】1698. 請輸出帶有特殊尾數的數

問題&#xff1a;1698. 請輸出帶有特殊尾數的數 類型&#xff1a; 題目描述&#xff1a; 請輸出1~n 中所有個位為 1、3、5、7中任意一個數的整數&#xff0c;每行 1 個。( n<1000 ) 比如&#xff0c;假設從鍵盤讀入 20&#xff0c;輸出結果如下&#xff1a; 1 3 5 7 11 1…

LLMs之PEFT之Llama-2:《LoRA Learns Less and Forgets LessLoRA學得更少但遺忘得也更少》翻譯與解讀

LLMs之PEFT之Llama-2&#xff1a;《LoRA Learns Less and Forgets LessLoRA學得更少但遺忘得也更少》翻譯與解讀 導讀&#xff1a;該論文比較了LoRA與完全微調在代碼與數學兩個領域的表現。 背景問題&#xff1a;微調大規模語言模型需要非常大的GPU內存。LoRA這一參數高效微調方…