Spring AI簡介
1. 概述
Spring框架透過Spring AI專案正式啟用了AI生成提示的力量。本文旨在對 Spring Boot 應用程式中的生成式 AI 整合進行深入介紹。在本教程中,我們將熟悉基本的人工智慧概念。
此外,我們還將了解 Spring AI 如何與模型交互,並創建一個應用程式來展示其功能。
2. Spring AI主要概念
在開始之前,讓我們回顧一下一些關鍵領域術語和概念。
Spring AI 最初專注於設計用於處理語言輸入和生成語言輸出的模型。該專案背後的想法是為開發人員提供一個抽象接口,這是將生成式 AI API 作為獨立組件添加到應用程式中的基礎。
其中一個抽像是介面AiClient,
它有兩個基本實作- OpenAI 和 Azure OpenAI。
public interface AiClient {
default String generate(String message);
AiResponse generate(Prompt prompt);
}
AiClient
為生成功能提供了兩種選擇。簡化的generate(String message)
-使用String
作為輸入和輸出,它可以用來避免Promt
和AiResponse
類別的額外複雜性。
現在,讓我們仔細看看它們的差異。
2.1.高級Prompt
和AiResponse
在AI領域,提示是指提供給AI的簡訊。它由上下文和問題組成,該模型用於生成答案。
從Spring AI專案的角度來看, Prompt
是一個參數化的Message
清單。
public class Prompt {
private final List<Message> messages;
// constructors and utility methods
}
public interface Message {
String getContent();
Map<String, Object> getProperties();
MessageType getMessageType();
}
Prompt
使開發人員能夠更好地控製文字輸入。一個很好的例子是提示模板,它由預先定義的文字和一組佔位符構成。然後,我們可以使用傳遞給Message
建構子的Map<String, Object>
值來填入它們。
Tell me a {adjective} joke about {content}.
Message
介面還保存有關 AI 模型可以處理的訊息類別的高級資訊。例如,OpenAI 實作區分對話角色,由MessageType
有效映射。對於其他模型,它可以反映訊息格式或一些其他自訂屬性。更多細節請參考官方文件。
public class AiResponse {
private final List<Generation> generations;
// getters and setters
}
public class Generation {
private final String text;
private Map<String, Object> info;
}
AiResponse
由Generation
物件清單組成,每個物件都保存對應提示的輸出。此外, Generation
物件提供AI響應的元資料資訊。
然而,雖然 Spring AI 專案仍處於測試階段,但並非所有功能都已完成並記錄下來。請關注GitHub 儲存庫上問題的進度。
3. Spring AI 專案入門
首先, AiClient
需要 API 金鑰才能與 OpenAI 平台進行所有通訊。為此,我們將在API 金鑰頁面上建立一個令牌。
Spring AI 專案定義了設定屬性: spring.ai.openai.api-key
。我們可以在application.yml
檔案中進行設定。
spring:
ai:
openai.api-key: ${OPEN_AI_KEY}
下一步是配置依賴項儲存庫。 Spring AI 專案在Spring Milestone Repository中提供工件。
因此,我們需要添加存儲庫定義:
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
之後,我們準備導入open-ai-spring-boot-starter
:
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>0.7.1-SNAPSHOT</version>
</dependency>
請記住,Spring AI 專案正在積極發展,因此請查看官方 GitHub 頁面以獲取最新版本
就這樣!現在,讓我們將這個概念付諸實現。
4. Spring AI 的實際應用
現在,我們將編寫一個簡單的 REST API 來進行示範。它將由兩個端點組成,返回我們想要的任何主題和流派的詩歌:
-
/ai/cathaiku
— 將實作基本的generate()
方法並傳回一個關於貓的俳句純字串值; -
/ai/poetry?theme={{theme}}&genre={{genre}}
— 將示範PromtTemplate
和AiResponse
類別的功能;
4.1.在 Spring Boot 應用程式中註入AiClient
為了簡單起見,我們從 cat haiku 端點開始。使用@RestController
註解,我們將設定PoetryController
並加入GET
方法映射:
@RestController
@RequestMapping("ai")
public class PoetryController {
private final PoetryService poetryService;
// constructor
@GetMapping("/cathaiku")
public ResponseEntity<String> generateHaiku(){
return ResponseEntity.ok(poetryService.getCatHaiku());
}
}
接下來,遵循 DDD 概念,服務層將定義所有領域邏輯。呼叫generate
()方法所需要做的就是將AiClient
注入PoetryService
。現在,我們可以定義字串提示,在其中指定產生俳句的請求。
@Service
public class PoetryServiceImpl implements PoetryService {
public static final String WRITE_ME_HAIKU_ABOUT_CAT = """
Write me Haiku about cat,
haiku should start with the word cat obligatory""";
private final AiClient aiClient;
// constructor
@Override
public String getCatHaiku() {
return aiClient.generate(WRITE_ME_HAIKU_ABOUT_CAT);
}
}
端點已啟動並準備好接收請求。回應將包含一個純字串:
Cat prowls in the night,
Whiskers twitch with keen delight,
Silent hunter's might.
到目前為止看起來還不錯;然而,目前的解決方案存在一些缺陷。首先,純字串的回應並不是REST
合約的最佳解決方案。
此外,始終使用相同的舊提示查詢ChatGPT
並沒有多大價值。因此,我們的下一步是添加參數化值:主題和流派。這時候PromtTemplate
就能為我們提供最好的服務!
4.2.使用PromptTemplate
進行可設定查詢
從本質上講, PromptTemplate
工作方式與StringBuilder
和字典的組合非常相似.
與/cathaiku
端點類似,我們首先定義提示的基本字串。此外,這次,我們將透過名稱定義用實際值填充的佔位符:
String promptString = """
Write me {genre} poetry about {theme}
""";
PromptTemplate promptTemplate = new PromptTemplate(promptString);
promptTemplate.add("genre", genre);
promptTemplate.add("theme", theme);
接下來,我們可能想做的是標準化端點輸出。為此,我們將引入簡單的記錄類別 - PoetryDto
,它將包含詩歌標題、名稱和流派:
public record PoetryDto (String title, String poetry, String genre, String theme){}
進一步的步驟是在BeanOutputParser
類別中註冊PoetryDto
;它提供序列化和反序列化 OpenAI API 輸出的功能。
然後,我們將這個解析器提供給promtTemple
,從現在開始,我們的訊息將會被序列化到 DTO 物件中。
最後,我們的生成函數將如下所示:
@Override
public PoetryDto getPoetryByGenreAndTheme(String genre, String theme) {
BeanOutputParser<PoetryDto> poetryDtoBeanOutputParser = new BeanOutputParser<>(PoetryDto.class);
String promptString = """
Write me {genre} poetry about {theme}
{format}
""";
PromptTemplate promptTemplate = new PromptTemplate(promptString);
promptTemplate.add("genre", genre);
promptTemplate.add("theme", theme);
promptTemplate.add("format", poetryDtoBeanOutputParser.getFormat());
promptTemplate.setOutputParser(poetryDtoBeanOutputParser);
AiResponse response = aiClient.generate(promptTemplate.create());
return poetryDtoBeanOutputParser.parse(response.getGeneration().getText());
}
我們的客戶現在收到的回應看起來好多了,更重要的是,它符合REST API
標準和最佳實踐:
{
"title": "Dancing Flames",
"poetry": "In the depths of night, flames dance with grace,
Their golden tongues lick the air with fiery embrace.
A symphony of warmth, a mesmerizing sight,
In their flickering glow, shadows take flight.
Oh, flames so vibrant, so full of life,
Burning with passion, banishing all strife.
They consume with ardor, yet do not destroy,
A paradox of power, a delicate ploy.
They whisper secrets, untold and untamed,
Their radiant hues, a kaleidoscope unnamed.
In their gentle crackling, stories unfold,
Of ancient tales and legends untold.
Flames ignite the heart, awakening desire,
They fuel the soul, setting it on fire.
With every flicker, they kindle a spark,
Guiding us through the darkness, lighting up the dark.
So let us gather 'round, bask in their warm embrace,
For in the realm of flames, magic finds its place.
In their ethereal dance, we find solace and release,
And in their eternal glow, our spirits find peace.",
"genre": "Liric",
"theme": "Flames"
}
5. 錯誤處理
Spring AI 專案透過OpenAiHttpException
類別提供了對 OpenAPI 錯誤的抽象。不幸的是,它不提供每個錯誤類型的類別的單獨映射。然而,由於這種抽象,我們可以在一個處理程序中使用RestControllerAdvice
處理所有異常。
下面的程式碼使用 Spring 6 框架的ProblemDetail
標準。如果您不熟悉,請查看官方文件。
@RestControllerAdvice
public class ExceptionTranslator extends ResponseEntityExceptionHandler {
public static final String OPEN_AI_CLIENT_RAISED_EXCEPTION = "Open AI client raised exception";
@ExceptionHandler(OpenAiHttpException.class)
ProblemDetail handleOpenAiHttpException(OpenAiHttpException ex) {
HttpStatus status = Optional
.ofNullable(HttpStatus.resolve(ex.statusCode))
.orElse(HttpStatus.BAD_REQUEST);
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(status, ex.getMessage());
problemDetail.setTitle(OPEN_AI_CLIENT_RAISED_EXCEPTION);
return problemDetail;
}
}
現在,如果 OpenAPI 回應包含錯誤,我們將對其進行處理。以下是回應的範例:
{
"type": "about:blank",
"title": "Open AI client raised exception",
"status": 401,
"detail": "Incorrect API key provided: sk-XG6GW***************************************wlmi.
You can find your API key at https://platform.openai.com/account/api-keys.",
"instance": "/ai/cathaiku"
}
可能的異常狀態的完整清單位於官方文件頁面上。
六,結論
在本文中,我們熟悉了 Spring AI 專案及其在 REST API 背景下的功能。儘管在撰寫本文時, spring-ai-starter
仍在積極開發中,並且可以在快照版本中存取。它為將生成式 AI 整合到 Spring Boot 應用程式中提供了可靠的介面。
在本文中,我們介紹了與 Spring AI 的基本集成和高級集成,
包括AiClient
在幕後的工作方式。作為概念證明,我們實現了一個生成詩歌的基本 REST 應用程式。除了產生端點的基本範例之外,我們還提供了使用進階 Spring AI 功能的範例: PromtTemplate, AiResponse,
和BeanOutputParser
。 此外,我們也實作了錯誤處理功能。
完整的範例可以在 GitHub 上找到。