SpringBoot快速接入OpenAI大模型的方法(JDK8)
使用AI4J快速接入OpenAI大模型
本博文給大家介紹一下如何使用AI4J
快速接入OpenAI大模型,并且如何實現(xiàn)流式與非流式的輸出,以及對函數(shù)調(diào)用的使用。
介紹
由于SpringAI需要使用JDK17和Spring Boot3,但是目前很多應(yīng)用依舊使用的JDK8版本,所以使用可以支持JDK8的AI4J來接入OpenAI大模型。
AI4J是一款JavaSDK用于快速接入AI大模型應(yīng)用,整合多平臺大模型,如OpenAi、智譜Zhipu(ChatGLM)、深度求索DeepSeek、月之暗面Moonshot(Kimi)、騰訊混元Hunyuan、零一萬物(01)等等,提供統(tǒng)一的輸入輸出(對齊OpenAi)消除差異化,優(yōu)化函數(shù)調(diào)用(Tool Call),優(yōu)化RAG調(diào)用、支持向量數(shù)據(jù)庫(Pinecone),并且支持JDK1.8,為用戶提供快速整合AI的能力。
AI4J-GitHub
快速使用
目前較多的應(yīng)用場景為Spring應(yīng)用,而AI4J接入SpringBoot應(yīng)用也是非常簡單的,本篇博文先帶著大家為SpringBoot應(yīng)用集成OpenAI服務(wù),后續(xù)會介紹如何再非Spring項目中搭建。
創(chuàng)建SpringBoot項目
這里以JDK1.8為例創(chuàng)建SpringBoot2項目,當(dāng)然你也可以創(chuàng)建JDK17、SpringBoot3。
引入AI4J依賴
<!-- Spring應(yīng)用 --> <dependency> <groupId>io.github.lnyo-cly</groupId> <artifactId>ai4j-spring-boot-stater</artifactId> <version>0.5.2</version> </dependency>
如果你使用阿里源無法引入,可能是阿里云鏡像還沒有同步。
配置application.yml
給大家兩種配置方法
第一種:使用官網(wǎng)的url,自己有代理
第二種:使用中轉(zhuǎn)代理地址(或第三方中轉(zhuǎn)平臺)
如:https://api.openai-proxy.com
上面任意配置一種即可。
搭建聊天服務(wù)Controller
下面是一個小的demo演示:
@RestController public class OpenAiController { // 注入Ai服務(wù) @Autowired private AiService aiService; @GetMapping("/chat") public String getChatMessage(@RequestParam String question) throws Exception { // 獲取OpenAi的聊天服務(wù) IChatService chatService = aiService.getChatService(PlatformType.OPENAI); // 創(chuàng)建請求參數(shù) ChatCompletion chatCompletion = ChatCompletion.builder() .model("gpt-4o-mini") .message(ChatMessage.withUser(question)) .build(); System.out.println(chatCompletion); // 發(fā)送chat請求 ChatCompletionResponse chatCompletionResponse = chatService.chatCompletion(chatCompletion); // 獲取聊天內(nèi)容和token消耗 String content = chatCompletionResponse.getChoices().get(0).getMessage().getContent(); long totalTokens = chatCompletionResponse.getUsage().getTotalTokens(); System.out.println("總token消耗: " + totalTokens); return content; } }
實現(xiàn)stream流式輸出(打字機效果)
編寫stream.html
:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Stream Example</title> </head> <body> <input id="question" type="text" placeholder="輸入需要提問的問題"/> <button id="startButton">開始</button> <div id="output"></div> <script> const input = document.getElementById("question"); const outputDiv = document.getElementById('output'); const startButton = document.getElementById('startButton'); async function getResponse(){ const question = input.value; const resp = await fetch("/chatStream" + "?question=" + question,{ method: 'GET' }) const reader = resp.body.getReader(); const textDecoder = new TextDecoder(); while (1){ const { done , value } = await reader.read() if(done) break; const str = textDecoder.decode(value); outputDiv.innerText += str; console.log(str) } } startButton.addEventListener("click", getResponse) </script> </body> </html>
向controller中添加stream接口:
@GetMapping("/chatStream") public void getChatMessageStream(@RequestParam String question, HttpServletResponse response) throws Exception { // 中文亂碼問題 response.setCharacterEncoding("UTF-8"); // 獲取OpenAi的聊天服務(wù) IChatService chatService = aiService.getChatService(PlatformType.OPENAI); // 創(chuàng)建請求參數(shù) ChatCompletion chatCompletion = ChatCompletion.builder() .model("gpt-4o-mini") .message(ChatMessage.withUser(question)) .build(); PrintWriter writer = response.getWriter(); // 發(fā)送chat請求 SseListener sseListener = new SseListener() { @Override protected void send() { writer.write(this.getCurrStr()); writer.flush(); System.out.println(this.getCurrStr()); } }; chatService.chatCompletionStream(chatCompletion, sseListener); writer.close(); System.out.println(sseListener.getOutput()); }
注意:上面只是一個簡單的示例,你也可以使用其它方法,比如:
@GetMapping("/chatStream") public ResponseBodyEmitter getChatMessageStream(@RequestParam String question) { ResponseBodyEmitter emitter = new ResponseBodyEmitter(); // 獲取OpenAi的聊天服務(wù) IChatService chatService = aiService.getChatService(PlatformType.OPENAI); // 創(chuàng)建請求參數(shù) ChatCompletion chatCompletion = ChatCompletion.builder() .model("gpt-4o-mini") .message(ChatMessage.withUser(question)) .build(); Executors.newSingleThreadExecutor().submit(() -> { try { SseListener sseListener = new SseListener() { @Override protected void send() { try { emitter.send(this.getCurrStr()); System.out.println(this.getCurrStr()); // 打印當(dāng)前發(fā)送的內(nèi)容 } catch (IOException e) { emitter.completeWithError(e); } } }; // 發(fā)送流式數(shù)據(jù) chatService.chatCompletionStream(chatCompletion, sseListener); // 完成后關(guān)閉連接 emitter.complete(); } catch (Exception e) { emitter.completeWithError(e); } }); return emitter; }
實現(xiàn)函數(shù)調(diào)用
首先我們需要編寫一個函數(shù),以天氣預(yù)報
為例子:
@FunctionCall(name = "queryWeather", description = "查詢目標(biāo)地點的天氣預(yù)報") public class QueryWeatherFunction implements Function<QueryWeatherFunction.Request, String> { @Override public String apply(Request request) { final String key = "abcdefg"; // https://api.seniverse.com/v3/weather/hourly.json?key=your_api_key&location=beijing&start=0&hours=24 // https://api.seniverse.com/v3/weather/daily.json?key=your_api_key&location=beijing&start=0&days=5 String url = String.format("https://api.seniverse.com/v3/weather/%s.json?key=%s&location=%s&days=%d", request.type.name(), key, request.location, request.days); OkHttpClient client = new OkHttpClient(); okhttp3.Request http = new okhttp3.Request.Builder() .url(url) .get() .build(); try (Response response = client.newCall(http).execute()) { if (response.isSuccessful()) { // 解析響應(yīng)體 return response.body() != null ? response.body().string() : ""; } else { return "獲取天氣失敗 當(dāng)前天氣未知"; } } catch (Exception e) { // 處理異常 e.printStackTrace(); return "獲取天氣失敗 當(dāng)前天氣未知"; } } @Data @FunctionRequest public static class Request{ @FunctionParameter(description = "需要查詢天氣的目標(biāo)位置, 可以是城市中文名、城市拼音/英文名、省市名稱組合、IP 地址、經(jīng)緯度") private String location; @FunctionParameter(description = "需要查詢未來天氣的天數(shù), 最多15日") private int days = 15; @FunctionParameter(description = "預(yù)報的天氣類型,daily表示預(yù)報多天天氣、hourly表示預(yù)測當(dāng)天24天氣、now為當(dāng)前天氣實況") private Type type; } public enum Type{ daily, hourly, now } }
其中有三個核心注解:
@FunctionCall
:標(biāo)識這個類為一個函數(shù)@FunctionRequest
:標(biāo)識為該類為函數(shù)的請求類@FunctionParameter
:標(biāo)識為函數(shù)的具體參數(shù)
接下來稍微修改下剛剛編寫的Stream實現(xiàn)函數(shù):
@GetMapping("/chatStream") public void getChatMessageStream(@RequestParam String question, HttpServletResponse response) throws Exception { // ...... // 創(chuàng)建請求參數(shù) ChatCompletion chatCompletion = ChatCompletion.builder() .model("gpt-4o-mini") .message(ChatMessage.withUser(question)) .functions("queryWeather") // 這里傳入剛剛我們定義的函數(shù)名稱即可 .build(); // ...... }
如果想要知道函數(shù)調(diào)用的參數(shù),只需要在剛剛的代碼中,添加下面這一行即可:
sseListener.setShowToolArgs(true);
更換其它平臺
細心的你可能已經(jīng)發(fā)現(xiàn),我們在創(chuàng)建chatService
的時候傳入了PlatformType.OPENAI
,如果我們想要更換其它平臺,只需要更換PlatformType
即可。如果你不懂,那下篇博文再詳細的說說
IChatService chatService = aiService.getChatService(PlatformType.OPENAI);
其它功能
篇幅有限,其它功能使用,下篇再講
到此這篇關(guān)于SpringBoot快速接入OpenAI大模型(JDK8)的文章就介紹到這了,更多相關(guān)SpringBoot接入OpenAI大模型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java匿名內(nèi)部類和Lambda(->) 的多種寫法總結(jié)
這篇文章主要和大家分享一下Java匿名內(nèi)部類和Lambda(->) 的多種寫法,文中的示例代碼講解詳細,對我們學(xué)習(xí)Java有一定幫助,需要的可以先看一下2022-07-07JSON--List集合轉(zhuǎn)換成JSON對象詳解
這篇文章主要介紹了List集合轉(zhuǎn)換成JSON對象,小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。2017-01-01