直接調(diào)用大模型:看起來簡單,坑全在細(xì)節(jié)里

咱們先還原一下,直接調(diào)用大模型API通常是怎么寫的。無非就是構(gòu)造請(qǐng)求、發(fā)HTTP、解析結(jié)果,大概長這樣:

java

// 直接裸寫調(diào)用OpenAI API

public String callOpenAI(String userPrompt) {

? ? String apiUrl = "https://api.openai.com/v1/chat/completions";

? ? String apiKey = "sk-xxxxxxxxxxxxxxxx";

? ? // 1. 手動(dòng)構(gòu)造請(qǐng)求JSON

? ? JSONObject reqBody = new JSONObject();

? ? reqBody.put("model", "gpt-3.5-turbo");

? ? JSONArray messages = new JSONArray();

? ? messages.add(new JSONObject()

? ? ? ? .put("role", "user")

? ? ? ? .put("content", userPrompt));

? ? reqBody.put("messages", messages);

? ? // 2. 設(shè)置請(qǐng)求頭、發(fā)請(qǐng)求

? ? HttpHeaders headers = new HttpHeaders();

? ? headers.setContentType(MediaType.APPLICATION_JSON);

? ? headers.setBearerAuth(apiKey);

? ? HttpEntity<String> entity = new HttpEntity<>(reqBody.toJSONString(), headers);

? ? ResponseEntity<String> response = restTemplate.postForEntity(apiUrl, entity, String.class);

? ? // 3. 手動(dòng)解析返回結(jié)果

? ? JSONObject resJson = JSON.parseObject(response.getBody());

? ? return resJson.getJSONArray("choices")

? ? ? ? .getJSONObject(0)

? ? ? ? .getJSONObject("message")

? ? ? ? .getString("content");

}

看起來代碼不多,挺簡單對(duì)不對(duì)?但真放到企業(yè)項(xiàng)目里做個(gè)生產(chǎn)可用的AI應(yīng)用,問題就全冒出來了:

1. 切換大模型=重寫一半代碼

今天項(xiàng)目要用OpenAI,明天老板說要換成國產(chǎn)的文心一言,后天又要接通義千問做備選。每個(gè)大模型的請(qǐng)求格式不一樣,返回結(jié)構(gòu)不一樣,錯(cuò)誤碼也不一樣,你得把原來的請(qǐng)求構(gòu)造、結(jié)果解析全改一遍,純純出力不討好。

2. 重試、限流、日志、監(jiān)控全要自己搭

大模型API不是百分百穩(wěn)定的,超時(shí)、限流、500錯(cuò)誤都是常有的事。這些異常你得處理吧?失敗了要重試吧?請(qǐng)求耗時(shí)要打日志吧?線上出問題要監(jiān)控報(bào)警吧?這些通用邏輯每個(gè)接口都要寫一遍,純純重復(fù)造輪子。

3. 復(fù)雜提示詞根本沒法維護(hù)

正經(jīng)業(yè)務(wù)里的提示詞往往很長,還要?jiǎng)討B(tài)替換參數(shù),全堆在Java字符串里,代碼亂成一鍋粥,改個(gè)提示詞還要重新發(fā)布項(xiàng)目,體驗(yàn)極差。

4. RAG開發(fā)全靠自己,從零搭到懷疑人生

現(xiàn)在做AI應(yīng)用基本都離不開RAG(檢索增強(qiáng)生成),從文檔讀取、文本切片、向量生成、存儲(chǔ)到向量數(shù)據(jù)庫,檢索的時(shí)候再把相關(guān)片段拼到提示詞里,這套流程全自己寫沒有幾百行代碼根本下不來,太折磨人了。

Spring AI:把這些破事全給你搞定了

Spring AI本質(zhì)上做的事情,就是把AI開發(fā)中這些通用、重復(fù)的工作全都封裝好,讓你用熟悉的Spring風(fēng)格開發(fā)AI應(yīng)用,不用從零造輪子,專注寫業(yè)務(wù)就行。

給大家看看用Spring AI調(diào)用大模型是什么體驗(yàn):

java

// Spring AI調(diào)用大模型,就這么點(diǎn)代碼

@RestController

public class ChatController {

? ? private final ChatClient chatClient;

? ? // 自動(dòng)注入配置好的ChatClient

? ? public ChatController(ChatClient.Builder builder) {

? ? ? ? this.chatClient = builder.build();

? ? }

? ? @GetMapping("/chat")

? ? public String chat(String prompt) {

? ? ? ? return chatClient.call(prompt);

? ? }

}

代碼寫完了,剩下的配置全丟在application.yml里:

yaml

spring:

? ai:

? ? openai:

? ? ? api-key: ${OPENAI_API_KEY}

? ? ? chat:

? ? ? ? options:

? ? ? ? ? model: gpt-3.5-turbo

就搞定了?沒錯(cuò)!如果哪天你想換成百度文心一言,只需要改依賴和配置,業(yè)務(wù)代碼一行都不用動(dòng):

yaml

spring:

? ai:

? ? qianfan:

? ? ? api-key: ${QIANFAN_API_KEY}

? ? ? secret-key: ${QIANFAN_SECRET_KEY}

? ? ? chat:

? ? ? ? options:

? ? ? ? ? model: ERNIE-Speed-8K

就問你香不香?除了統(tǒng)一API屏蔽差異,Spring AI還給我們準(zhǔn)備了一大堆實(shí)用功能:

1. 提示詞模板支持外部化,好維護(hù)太多

Spring AI內(nèi)置了提示詞模板,支持把長提示詞放到單獨(dú)的文件里,動(dòng)態(tài)替換參數(shù),改提示詞不用動(dòng)代碼:

java

// 從resources加載提示詞模板,動(dòng)態(tài)替換參數(shù)

PromptTemplate promptTemplate = new PromptTemplate(

? ? ResourceLoaderUtils.getResource("classpath:prompts/customer-service.tpl")

);

Prompt prompt = promptTemplate.create(Map.of(

? ? "serviceName", "我的電商網(wǎng)站",

? ? "userQuestion", "我的訂單什么時(shí)候發(fā)貨?"

));

ChatResponse response = chatClient.call(prompt);

2. RAG全流程封裝,一行代碼接入向量存儲(chǔ)

Spring AI把RAG需要的文檔讀取、切片、向量生成、存儲(chǔ)檢索全做好了,想接向量數(shù)據(jù)庫只需要改配置:

java

// 基于RAG實(shí)現(xiàn)問答,就這么簡單

public String ragChat(String userQuestion) {

? ? // 從向量數(shù)據(jù)庫找相關(guān)文檔

? ? List<Document> relatedDocs = vectorStore.similaritySearch(userQuestion);

? ? // 拼到提示詞里調(diào)用大模型

? ? String prompt = STR."根據(jù)以下內(nèi)容回答問題:\{relatedDocs}\n問題:\{userQuestion}";

? ? return chatClient.call(prompt);

}

Redis、PGVector、Milvus、Chroma這些常用的向量數(shù)據(jù)庫全都支持,切換存儲(chǔ)只需要改依賴,不用改業(yè)務(wù)代碼。

3. 函數(shù)調(diào)用開箱即用,不用自己解析

現(xiàn)在大模型都支持函數(shù)調(diào)用,讓大模型能調(diào)用你的本地方法獲取數(shù)據(jù)。Spring AI直接把這個(gè)流程封裝好了,只需要定義方法加注解就行:

java

// 定義查詢天氣的函數(shù)

@Bean

public Function<GetWeatherRequest, GetWeatherResponse> getCurrentWeather() {

? ? return request -> weatherService.getWeather(request.getCity());

}

// 直接調(diào)用大模型,函數(shù)調(diào)用自動(dòng)處理

ChatResponse response = chatClient.call(new Prompt("北京今天天氣怎么樣?"));

不用自己解析大模型返回的函數(shù)調(diào)用請(qǐng)求,也不用自己把結(jié)果塞回給大模型,全流程自動(dòng)完成。

4. 無縫集成Spring生態(tài),復(fù)用你熟悉的能力

如果你本來就是Spring Boot項(xiàng)目,集成Spring AI簡直不要太舒服:自動(dòng)配置、依賴注入、切面日志、異常處理全都和原生Spring打通,你可以直接用Spring Security做權(quán)限,用Spring Retry做重試,用Micrometer做監(jiān)控,完全不用額外適配。

什么時(shí)候用Spring AI,什么時(shí)候直接調(diào)用?

Spring AI不是銀彈,也不是所有場景都必須用,我給大家總結(jié)了判斷標(biāo)準(zhǔn):

? 推薦用Spring AI的場景

? ? 企業(yè)級(jí)Java/Spring項(xiàng)目,需要長期維護(hù)

? ? 需要對(duì)接多個(gè)大模型,方便后續(xù)切換擴(kuò)展

? ? 需要做RAG、函數(shù)調(diào)用這類復(fù)雜AI應(yīng)用

? ? 團(tuán)隊(duì)熟悉Spring生態(tài),想快速落地AI功能

? 可以直接調(diào)用的場景

? ? 簡單的Demo、測試項(xiàng)目,寫完就扔

? ? 只對(duì)接一個(gè)大模型,未來也不會(huì)換

? ? 對(duì)包體積要求極高的輕量項(xiàng)目

最后嘮兩句

Spring AI 不是要替代直接調(diào)用大模型,它存在的意義就是降低Java生態(tài)開發(fā)AI應(yīng)用的門檻:把不同大模型的差異給你屏蔽掉,把通用的異常處理、重試、日志、RAG這些能力給你封裝好,讓Spring開發(fā)者不用從零開始造輪子,把精力放在業(yè)務(wù)邏輯上就行。

你寫個(gè)幾十行的helloworldDemo,直接發(fā)HTTP當(dāng)然沒問題;但要是做一個(gè)生產(chǎn)環(huán)境可用、需要長期維護(hù)的AI應(yīng)用,Spring AI真的能幫你省至少80%的重復(fù)工作,這就是它比直接調(diào)用大模型好用的原因。

如果你最近正在做Java+AI相關(guān)的項(xiàng)目,不妨試試Spring AI,相信你會(huì)回來給我點(diǎn)zan的~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容