利用Spring Boot以及Spring AI構(gòu)建生成式人工智能應(yīng)用
Spring AI,作為行業(yè)領(lǐng)導(dǎo)者,通過(guò)其強(qiáng)大、靈活的API和先進(jìn)的功能,為各種行業(yè)提供了顛覆性的解決方案。在本專題中,我們將深入探討Spring AI在各領(lǐng)域的應(yīng)用示例,每個(gè)案例都將展示Spring AI如何滿足特定需求,實(shí)現(xiàn)目標(biāo),并將這些LESSONS LEARNED擴(kuò)展到更廣泛的應(yīng)用。希望這個(gè)專題能對(duì)你有所啟發(fā),更深入地理解和利用Spring AI的無(wú)限可能。
Spring框架在軟件開(kāi)發(fā)領(lǐng)域已經(jīng)有超過(guò)20年的歷史,自Spring Boot 1.0版本發(fā)布以來(lái)已有10年?,F(xiàn)在,無(wú)人會(huì)質(zhì)疑,Spring創(chuàng)造了一種獨(dú)特的風(fēng)格,使開(kāi)發(fā)者從重復(fù)任務(wù)中解放出來(lái),專注于提供業(yè)務(wù)價(jià)值。隨著時(shí)間的推移,Spring的技術(shù)深度不斷增強(qiáng),涵蓋了廣泛的開(kāi)發(fā)領(lǐng)域和技術(shù)。另一方面,隨著更多的專用解決方案得到嘗試,概念的驗(yàn)證被創(chuàng)建,并最終在項(xiàng)目的保護(hù)下得到推廣,其技術(shù)廣度不斷擴(kuò)大。
Spring AI 項(xiàng)目就是一個(gè)實(shí)例,根據(jù)其參考文檔,該項(xiàng)目旨在簡(jiǎn)化當(dāng)生成式人工智能層需要被引入應(yīng)用時(shí)的開(kāi)發(fā)過(guò)程。開(kāi)發(fā)者再次從重復(fù)任務(wù)中解放出來(lái),可以直接通過(guò)簡(jiǎn)單的接口與預(yù)先訓(xùn)練的模型(包含實(shí)際處理算法)進(jìn)行交互。
通過(guò)直接或者通過(guò) Spring AI 以編程方式與生成式預(yù)訓(xùn)練的轉(zhuǎn)換器(GPTs)交互,用戶(開(kāi)發(fā)者)不需要擁有廣泛的機(jī)器學(xué)習(xí)知識(shí)。但作為一名工程師,我強(qiáng)烈認(rèn)為,即使這些開(kāi)發(fā)者工具可以方便快捷地使用并產(chǎn)生結(jié)果,我建議我們需要克制自己,警覺(jué)觀察,嘗試首先理解基本概念。此外,通過(guò)遵循這條路徑,產(chǎn)出的結(jié)果可能會(huì)更有價(jià)值。
目的
本文介紹了如何將 Spring AI 集成到Spring Boot應(yīng)用,與OpenAI進(jìn)行編程交互。我們假定prompt設(shè)計(jì)一般來(lái)說(shuō)是一種最先進(jìn)的活動(dòng)。因此,在實(shí)驗(yàn)過(guò)程中使用的prompt非常有教導(dǎo)性,但應(yīng)用性不大。此處的重點(diǎn)是通訊接口,即 Spring AI API。
實(shí)施前
首先要明確自身使用GPT解決方案的理由,除了希望提供更好的質(zhì)量,節(jié)省時(shí)間和降低成本。
生成式人工智能據(jù)說(shuō)擅長(zhǎng)執(zhí)行大量耗時(shí)的任務(wù),速度更快,效率更高,生成結(jié)果。此外,如果這些結(jié)果進(jìn)一步經(jīng)過(guò)經(jīng)驗(yàn)豐富且智慧的人類驗(yàn)證,獲得有用結(jié)果的可能性會(huì)增加。
接下來(lái),應(yīng)抵制立即跳入實(shí)施的誘惑,至少要花一些時(shí)間熟悉一下一般概念。對(duì)生成式人工智能概念的深入探索遠(yuǎn)遠(yuǎn)超出了本文的范圍。然而,出現(xiàn)在交互中的“主要角色”在下面簡(jiǎn)要概述。
舞臺(tái) - 生成式人工智能是人工智能的一部分
輸入 - 提供的數(shù)據(jù)(輸入)
輸出 - 計(jì)算結(jié)果(輸出)
大型語(yǔ)言模型(LLM)- 根據(jù)解釋的輸入產(chǎn)生輸出的微調(diào)算法
提示詞- 一種最先進(jìn)的接口,通過(guò)它將輸入傳入模型
提示詞模板 - 允許構(gòu)造結(jié)構(gòu)化參數(shù)化提示的組件
令牌 - 算法在內(nèi)部將輸入轉(zhuǎn)換為令牌,然后使用這些令牌來(lái)編譯結(jié)果,并最終從中構(gòu)造輸出
模型的環(huán)境窗口 - 模型限制每次調(diào)用的令牌數(shù)量的閾值(通常,使用的令牌越多,操作就越昂貴)
最后,可以開(kāi)始實(shí)施,但隨著實(shí)施的進(jìn)行,建議回顧和優(yōu)化前兩個(gè)步驟。
提示詞
在本次練習(xí)中,我們請(qǐng)求如下:
Write {count = three} reasons why people in {location = Romania} should consider a {job = software architect} job.
These reasons need to be short, so they fit on a poster.
For instance, "{job} jobs are rewarding."
上面內(nèi)容代表了提示詞模板。按照建議,應(yīng)作為提示詞的一部分提供清晰的主題,清晰的任務(wù)含義以及額外的有用信息,以提高結(jié)果的準(zhǔn)確性。
提示詞包含三個(gè)參數(shù)
count - 希望作為輸出的原因數(shù)量
job - 感興趣的領(lǐng)域或工作
location - 工作申請(qǐng)者所在的國(guó)家,城鎮(zhèn),地區(qū)等
概念驗(yàn)證
在這篇文章中,簡(jiǎn)單的概念驗(yàn)證目標(biāo)如下:
將 Spring AI 集成到Spring Boot應(yīng)用程序并使用它
允許客戶端通過(guò)應(yīng)用程序與 Open AI 進(jìn)行通信
客戶端向應(yīng)用程序發(fā)出參數(shù)化的HTTP請(qǐng)求
應(yīng)用程序使用一個(gè)提示詞來(lái)創(chuàng)建輸入,發(fā)送給 Open AI 并獲取輸出
應(yīng)用程序?qū)㈨憫?yīng)發(fā)送給客戶端
圖片
項(xiàng)目設(shè)置
Java 21
Maven 3.9.2
Spring Boot – v. 3.2.2
Spring AI – v. 0.8.0-SNAPSHOT (仍在開(kāi)發(fā),實(shí)驗(yàn)性)
代碼實(shí)現(xiàn)
Spring AI 集成
通常,這是一個(gè)基本步驟,不一定值得一提。然而,因?yàn)?Spring AI 目前以快照形式發(fā)布,為了能夠集成 Open AI 自動(dòng)配置依賴,你需要添加一個(gè)引用到 Spring 快照倉(cāng)庫(kù)。
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
下一步是添加 spring-ai-openai-spring-boot-starter Maven 依賴項(xiàng)。
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.8.0-SNAPSHOT</version>
</dependency>
Open AI ChatClient 現(xiàn)在是應(yīng)用程序類路徑的一部分。它是用來(lái)向 Open AI 發(fā)送輸入并獲取輸出的組件。
為了能夠連接到AI模型,需要在 application.properties 文件中設(shè)置 spring.ai.openai.api-key 屬性。
spring.ai.openai.api-key = api-key-value
它的值代表了用戶的有效API密鑰,用戶將通過(guò)此密鑰進(jìn)行通信。通過(guò)訪問(wèn)[資源2],可以注冊(cè)或登錄并生成一個(gè)。
客戶端 - Spring Boot應(yīng)用程序通信
概念驗(yàn)證的第一部分是客戶端應(yīng)用程序(例如瀏覽器,curl等)與開(kāi)發(fā)的應(yīng)用程序之間的通信。這是通過(guò)一個(gè) REST 控制器實(shí)現(xiàn)的,可以通過(guò)HTTP GET請(qǐng)求訪問(wèn)。
URL路徑是 /job-reasons,還有之前在定義提示時(shí)概述的三個(gè)參數(shù),這將導(dǎo)致如下格式:
/job-reasons?count={count}&job={job}&locatinotallow={location}
和相應(yīng)的控制器:
@RestController
public class OpenAiController {
@GetMapping("/job-reasons")
public ResponseEntity<String> jobReasons(@RequestParam(value = "count", required = false, defaultValue = "3") int count,
@RequestParam("job") String job,
@RequestParam("location") String location) {
return ResponseEntity.ok().build();
}
}
由于來(lái)自 Open AI 的響應(yīng)將是一個(gè)字符串,因此控制器返回一個(gè)封裝了字符串的ResponseEntity。如果我們運(yùn)行應(yīng)用程序并發(fā)出請(qǐng)求,當(dāng)前響應(yīng)體部分沒(méi)有返回任何內(nèi)容。
客戶端 - Open AI 通信
Spring AI 目前主要關(guān)注處理語(yǔ)言并產(chǎn)生語(yǔ)言或數(shù)字的AI模型。在前一類別中, Open AI 模型的例子包括GPT4-openai或GPT3.5-openai。
為了與這些AI模型(實(shí)際上是指 Open AI 算法)進(jìn)行交互, Spring AI 提供了一個(gè)統(tǒng)一的接口。
ChatClient接口目前支持文本輸入和輸出,并具有簡(jiǎn)單的契約。
@FunctionalInterface
public interface ChatClient extends ModelClient<Prompt, ChatResponse> {
default String call(String message) {
Prompt prompt = new Prompt(new UserMessage(message));
return call(prompt).getResult().getOutput().getContent();
}
ChatResponse call(Prompt prompt);
}
確實(shí)如此,功能接口的實(shí)際方法通常是被使用的方法。
在我們的概念驗(yàn)證中,這正是我們所需要的,一種調(diào)用 Open AI 并發(fā)送目標(biāo)參數(shù)化 Prompt 作為參數(shù)的方式。我們定義了以下的OpenAiService,在其中注入了一個(gè) ChatClient 的實(shí)例。
@Service
public class OpenAiService {
private final ChatClient client;
public OpenAiService(OpenAiChatClient aiClient) {
this.client = aiClient;
}
public String jobReasons(int count, String domain, String location) {
final String promptText = """
Write {count} reasons why people in {location} should consider a {job} job.
These reasons need to be short, so they fit on a poster.
For instance, "{job} jobs are rewarding."
""";
final PromptTemplate promptTemplate = new PromptTemplate(promptText);
promptTemplate.add("count", count);
promptTemplate.add("job", domain);
promptTemplate.add("location", location);
ChatResponse response = client.call(promptTemplate.create());
return response.getResult().getOutput().getContent();
}
}
如果應(yīng)用程序正在運(yùn)行,那么可以從瀏覽器執(zhí)行以下請(qǐng)求:
http://localhost:8080/gen-ai/job-reasons?count=3&job=software%20architect&locatinotallow=Romania
這下面的結(jié)果被檢索出來(lái)的結(jié)果:
利潤(rùn)豐裕的職業(yè):軟件架構(gòu)師的工作提供了有競(jìng)爭(zhēng)力的薪酬和出色的增長(zhǎng)機(jī)會(huì),確保在羅馬尼亞的財(cái)務(wù)穩(wěn)定和成功。
熱門職業(yè):隨著技術(shù)需求的持續(xù)增長(zhǎng),軟件架構(gòu)師在羅馬尼亞和全世界都備受追捧,提供了豐富的就業(yè)前景和就業(yè)保障。
創(chuàng)造性問(wèn)題解決:軟件架構(gòu)師在設(shè)計(jì)和開(kāi)發(fā)創(chuàng)新軟件解決方案中扮演著至關(guān)重要的角色,使他們可以釋放他們的創(chuàng)造力,并對(duì)各種行業(yè)產(chǎn)生重大影響。
這就是我們所期望的——一個(gè)簡(jiǎn)易的接口,通過(guò)它,可以要求 Open AI GPT模型寫出一些原因,解釋為何在特定地點(diǎn)的特定工作具有吸引力。
調(diào)整和觀察
到目前為止,開(kāi)發(fā)的簡(jiǎn)單概念驗(yàn)證主要使用了默認(rèn)的配置。
ChatClient實(shí)例可以通過(guò)各種屬性根據(jù)所需需要來(lái)配置。雖然這超出了本文的范圍,但在這里舉兩個(gè)例子。
spring.ai.openai.chat.options.model 指定要使用的AI模型。默認(rèn)為'gpt-35-turbo',但'gpt-4'和'gpt-4-32k'指定的是最新版本。雖然這些版本都是可用的,但你可能無(wú)法使用按使用量付費(fèi)的計(jì)劃來(lái)訪問(wèn)這些版本,但 Open AI 網(wǎng)站上有更多的信息可以幫助你了解如何適應(yīng)它。
另一個(gè)值得一提的屬性是 spring.ai.openai.chat.options.temperature。根據(jù)參考文檔,采樣溫度控制了“回應(yīng)的創(chuàng)新性”。據(jù)說(shuō),較高的值會(huì)讓輸出“更隨機(jī)”,而較低的值會(huì)“更專注和決定性”。默認(rèn)值為0.8,如果我們將其降低到0.3,重啟應(yīng)用并再次使用相同的請(qǐng)求參數(shù)詢問(wèn),下面的結(jié)果將被檢索出來(lái)。
有利可圖的職業(yè)機(jī)會(huì):羅馬尼亞的軟件架構(gòu)師工作提供有競(jìng)爭(zhēng)力的薪水和極好的成長(zhǎng)前景,對(duì)于尋求財(cái)務(wù)穩(wěn)定和職業(yè)發(fā)展的個(gè)人來(lái)說(shuō),這是一個(gè)吸引人的職業(yè)選擇。
具有挑戰(zhàn)性和智能刺激的工作:作為一名軟件架構(gòu)師,你將負(fù)責(zé)設(shè)計(jì)和實(shí)現(xiàn)復(fù)雜的軟件系統(tǒng),解決復(fù)雜的技術(shù)問(wèn)題,并與有才華的團(tuán)隊(duì)合作。這個(gè)角色提供了持續(xù)的學(xué)習(xí)機(jī)會(huì)和在尖端技術(shù)上工作的機(jī)會(huì)。
高需求和工作保障:隨著對(duì)技術(shù)和數(shù)字化轉(zhuǎn)型的依賴增加,各行各業(yè)對(duì)熟練軟件架構(gòu)師的需求在上升。選擇在羅馬尼亞的軟件架構(gòu)師工作確保了工作安全和廣泛的就業(yè)選擇,無(wú)論是在本地還是國(guó)際上。
可以看出,這種情況下的輸出更具描述性。
最后一個(gè)考慮因素是與獲取的輸出的結(jié)構(gòu)相關(guān)的。擁有將實(shí)際接收的有效載荷映射到Java對(duì)象(例如,類或記錄)的能力將非常方便。截至目前,表示形式是文本形式,實(shí)現(xiàn)也是如此。輸出解析器可能實(shí)現(xiàn)這一點(diǎn),類似于Spring JDBC的映射結(jié)構(gòu)。
在這個(gè)概念驗(yàn)證中,我們使用了一個(gè)BeanOutputParser,它允許直接將結(jié)果反序列化到Java記錄中,如下所示:
public record JobReasons(String job,
String location,
List<String> reasons) {
}
通過(guò)將 {format} 作為提示文本的一部分,并將其作為指示提供給 AI 模型。
OpenAiService 方法變?yōu)椋?/span>
public JobReasons formattedJobReasons(int count, String job, String location) {
final String promptText = """
Write {count} reasons why people in {location} should consider a {job} job.
These reasons need to be short, so they fit on a poster.
For instance, "{job} jobs are rewarding."
{format}
""";
BeanOutputParser<JobReasons> outputParser = new BeanOutputParser<>(JobReasons.class);
final PromptTemplate promptTemplate = new PromptTemplate(promptText);
promptTemplate.add("count", count);
promptTemplate.add("job", job);
promptTemplate.add("location", location);
promptTemplate.add("format", outputParser.getFormat());
promptTemplate.setOutputParser(outputParser);
final Prompt prompt = promptTemplate.create();
ChatResponse response = client.call(prompt);
return outputParser.parse(response.getResult().getOutput().getContent());
}
再次調(diào)用時(shí),輸出如下:
{
"job":"software architect",
"location":"Romania",
"reasons":[
"High demand",
"Competitive salary",
"Opportunities for growth"
]
}
格式符合預(yù)期,但解釋的原因似乎較少,這意味著需要進(jìn)行額外的調(diào)整以達(dá)到更好的可用性。然而,從概念驗(yàn)證的角度來(lái)看,這是可接受的,因?yàn)榻裹c(diǎn)是形式。
結(jié)論
提示設(shè)計(jì)是任務(wù)的重要部分 - 提示越清晰,輸入越好,輸出的質(zhì)量也就越高。
使用 Spring AI 與各種聊天模型進(jìn)行集成非常簡(jiǎn)單 - 本篇文章展示了一個(gè) Open AI 的集成。
然而,對(duì)于 Gen AI 或者幾乎任何技術(shù)來(lái)說(shuō),首先至少要熟悉基本概念是非常重要的。然后,嘗試?yán)斫馊绾芜M(jìn)行通訊的魔法,最后再開(kāi)始編寫“生產(chǎn)”代碼。
最后但同樣重要的是,建議進(jìn)一步探索 Spring AI API,以了解實(shí)現(xiàn)并隨著其不斷發(fā)展和改進(jìn)保持最新?tīng)顟B(tài)。