1. 概述
本教程將探討 Spring AI 的概念,這些概念可以幫助您使用像 ChatGPT、Ollama、Mistral 等 LLM 創建 AI 助手。
企業正越來越多地採用 AI 助手來增強跨各種現有業務功能的用户體驗:
- 回答用户查詢
- 根據用户輸入執行交易
- 總結冗長的句子和文檔
雖然這些只是 LLM 的幾個基本能力,但其潛力遠不止於此。
2. Spring AI 功能
Spring AI 框架提供一系列令人興奮的功能,以實現人工智能驅動的功能:
- 能夠無縫集成底層 LLM 服務和向量數據庫的接口
- 利用 RAG 和 Function Calling API 實現情境感知響應生成和動作執行
- 結構化輸出轉換器,將 LLM 響應轉換為 POJO 或 JSON 等可讀格式
- 通過 Advisor API 提供的攔截器豐富提示並應用安全措施
- 通過維護對話狀態提升用户參與度
我們可以將其可視化:
為了説明這些功能,我們將在一個遺留訂單管理系統 (OMS) 中構建一個聊天機器人:
典型的 OMS 功能包括:
- 創建訂單
- 獲取用户訂單
3. 先決條件
首先,我們需要一個 OpenAI 訂閲才能使用其 LLM 服務。然後,在 Spring Boot 應用程序中,我們將添加 Spring AI 庫的 Maven 依賴項。我們之前在其他文章中對這些先決條件進行了詳細的説明,因此我們不會在此贅述。
此外,我們將使用內存中的 HSQLDB 數據庫以快速啓動。讓我們為其創建所需的表並插入一些數據:
CREATE TABLE User_Order (
order_id BIGINT NOT NULL PRIMARY KEY,
user_id VARCHAR(20) NOT NULL,
quantity INT
);
INSERT INTO User_Order (order_id, user_id, quantity) VALUES (1, 'Jenny', 2);
INSERT INTO User_Order (order_id, user_id, quantity) VALUES (2, 'Mary', 5);
INSERT INTO User_Order (order_id, user_id, quantity) VALUES (3, 'Alex', 1);
INSERT INTO User_Order (order_id, user_id, quantity) VALUES (4, 'John', 3);
INSERT INTO User_Order (order_id, user_id, quantity) VALUES (5, 'Sophia', 4);
--and so on..我們將在 Spring 的 application.properties文件中使用一些標準配置屬性,用於初始化 HSQLDB 和 OpenAI 客户端:
spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver
spring.datasource.url=jdbc:hsqldb:mem:testdb;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=none
spring.ai.openai.chat.options.model=gpt-4o-mini
spring.ai.openai.api-key=xxxxxxx
選擇合適的模型以適應特定用例是一個複雜且迭代的過程,需要大量的嘗試和錯誤。 儘管如此,為了本文章的簡單演示應用程序,成本效益高的 GPT-4o mini 模型已經足夠。
4. Function Calling API
此功能是基於LLM應用中流行的“代理式”概念的基石之一。它使應用程序能夠執行復雜的一系列精確任務並獨立做出決策。
例如,它可以極大地幫助構建在遺留訂單管理應用程序中的聊天機器人。聊天機器人可以通過自然語言幫助用户提出訂單請求、檢索訂單歷史記錄以及執行更多操作。
這些技能是由一個或多個應用程序函數提供的專業技能。我們通過向LLM發送包含支持函數模式的提示來定義算法。LLM接收到該模式並識別出要執行的正確函數,然後將決策發送給應用程序。
最後,應用程序執行該函數並向LLM返回更多信息:
首先,讓我們來看看遺留應用程序的主要組件:
OrderManagementService類有兩個重要的函數:創建訂單和檢索用户訂單歷史記錄。這兩個函數使用OrderRepository Bean 與 DB 集成:
@Service
public class OrderManagementService {
@Autowired
private OrderRepository orderRepository;
public Long createOrder(OrderInfo orderInfo) {
return orderRepository.save(orderInfo).getOrderID();
}
public Optional<List<OrderInfo>> getAllUserOrders(String userID) {
return orderRepository.findByUserID(userID);
}
}現在,讓我們看看 Spring AI 如何幫助在遺留應用程序中實現聊天機器人:
在類圖中,<em>OmAiAssistantConfiguration</em> 類是一個 Spring 配置 Bean。它註冊了函數回調 Bean,<em>createOrderFn</em> 和 <em>getUserOrderFn</em>:
@Configuration
public class OmAiAssistantConfiguration {
@Bean
@Description("Create an order. The Order ID is identified with orderID. "
+ The order quantity is identified by orderQuantity."
+ "The user is identified by userID. "
+ "The order quantity should be a positive whole number."
+ "If any of the parameters like user id and the order quantity is missing"
+ "then ask the user to provide the missing information.")
public Function<CreateOrderRequest, Long> createOrderFn(OrderManagementService orderManagementService) {
return createOrderRequest -> orderManagementService.createOrder(createOrderRequest.orderInfo());
}
@Bean
@Description("get all the orders of an user. The user ID is identified with userID.")
public Function<GetOrderRequest, List<OrderInfo>> getUserOrdersFn(OrderManagementService orderManagementService) {
return getOrderRequest -> orderManagementService.getAllUserOrders(getOrderRequest.userID()).get();
}
}@Description 註解有助於生成函數方案。隨後,應用程序將該方案作為提示的一部分發送給 LLM。由於函數使用OrderManagementService類中的現有方法,從而促進了可重用性。
此外,CreateOrderRequest 和 GetOrderRequests 是 Record 類,Spring AI 利用它們生成下游服務調用的 POJO:
record GetOrderRequest(String userID) {}
record CreateOrderRequest(OrderInfo orderInfo) {}最後,讓我們來查看一下新的 OrderManagementAIAssistant 類,它會將用户查詢發送到 LLM 服務:
@Service
public class OrderManagementAIAssistant {
@Autowired
private ChatModel chatClient;
public ChatResponse callChatClient(Set<String> functionNames, String promptString) {
Prompt prompt = new Prompt(promptString, OpenAiChatOptions
.builder()
.withFunctions(functionNames)
.build()
);
return chatClient.call(prompt);
}
}callChatClient() 方法用於註冊 Prompt 對象中的函數。隨後,它調用 ChatModel#call() 方法以從 LLM 服務處獲取響應。
5. 函數調用場景
對於用户提出的查詢或指令,我們將會涵蓋幾個基本場景:
- LLM 決定並識別出要執行一個或多個函數
- LLM 抱怨缺少信息以執行該函數
- LLM 條件執行語句
我們已經討論了這些概念;因此,接下來我們將使用此功能構建聊天機器人。
5.1. 執行回調函數一次或多次
現在,讓我們研究一下當使用包含用户查詢和函數模式的提示調用 LLM 時,LLM 的行為。
我們將從一個創建訂單的示例開始:
void whenOrderInfoProvided_thenSaveInDB(String promptString) {
ChatResponse response = this.orderManagementAIAssistant
.callChatClient(Set.of("createOrderFn"), promptString);
String resultContent = response.getResult().getOutput().getText();
logger.info("The response from the LLM service: {}", resultContent);
}令人驚訝的是,使用自然語言,我們能獲得預期的結果:
| 提示 | LLM 響應 | 觀察 |
|---|---|---|
| 創建訂單,數量為 20,用户 ID 為 Jenny,並隨機生成訂單 ID 為正整數 | 訂單已成功創建,詳情如下:
– **訂單 ID:** 123456 – **用户 ID:** Jenny – **訂單數量:** 20 |
程序創建訂單,信息來自提示。 |
| 創建兩個訂單。第一個訂單的用户 ID 為 Sophia,數量為 30。第二個訂單的用户 ID 為 Mary,數量為 40。隨機生成訂單 ID 為正整數。 | 訂單已成功創建:
1. **Sophia 訂單:** 訂單 ID 1,數量為 30。
如果您需要其他幫助,請隨時提問! |
程序創建兩個訂單。LLM 巧妙地要求執行函數兩次。 |
接下來,讓我們看看 LLM 是否能理解請求獲取用户訂單詳情的提示:
void whenUserIDProvided_thenFetchUserOrders(String promptString) {
ChatResponse response = this.orderManagementAIAssistant
.callChatClient(Set.of("getUserOrdersFn"), promptString);
String resultContent = response.getResult().getOutput().getText();
logger.info("The response from the LLM service: {}", resultContent);
}結果與之前案例類似。LLM 成功識別出要執行的註冊函數。隨後,Spring Boot 應用程序調用該函數並返回一個或多個用户的訂單詳情。
5.2. 條件執行回調函數
讓我們考慮一個在滿足特定條件時才創建訂單的程序:
void whenUserIDProvided_thenCreateOrderIfUserHasLessThanTwoOrders(String promptString) {
ChatResponse response = this.orderManagementAIAssistant
.callChatClient(Set.of("getUserOrdersFn", "createOrderFn"), promptString);
String resultContent = response.getResult()
.getOutput()
.getText();
logger.info("The response from the LLM service: {}", resultContent);
}我們已在 ChatModel Bean 中註冊了 getUserOrderFn 和 CreateOrderFn 函數。 此外,LLM 必須確定哪個函數將為請求的動作產生最佳結果。
正如之前一樣,結果令人鼓舞:
| 提示 | 輸出 | 觀察 |
|---|---|---|
| 為用户 ID 為 Alex 創建訂單,數量為 25。 如果用户有超過 2 個訂單,則不要創建任何訂單。 在創建訂單時,隨機生成正整數作為訂單 ID | 用户 Alex 已經有超過 2 個訂單(具體是 4 個訂單)。 因此,我不會創建新的訂單。 | LLM 可以根據先前函數的輸出,條件調用下游函數。 |
| 為用户 ID 為 David 創建訂單,數量為 25。 如果用户有超過 2 個訂單,則不要創建任何訂單。 在創建訂單時,隨機生成正整數作為訂單 ID | 已成功為用户 ID 為“David”創建訂單,訂單數量為 25,訂單 ID 為 42。 | 這次 getUserOrderFn 返回了 David 的訂單數量少於 2 個。 因此,LLM 決定請求執行 createOrderFn。 |
| 創建訂單,數量為 20 | 請提供您的用户 ID 以創建訂單。 | LLM 在開始時識別到用户 ID 丟失,並中止了進一步處理。 |
6. Spring AI Advisors API
在上一節中,我們討論了應用程序的功能方面。然而,在所有功能中都存在一些常見的擔憂,例如:
- 防止用户輸入敏感信息
- 用户查詢的日誌記錄和審計
- 維護對話狀態
- 豐富提示
幸運的是,Advisors APIs 可以幫助我們始終如一地解決這些問題。我們已經在一篇文章中詳細解釋過這些內容。
7. Spring AI 結構化輸出 API 和 Spring AI RAG
LLMs 主要生成自然語言形式的響應消息。然而,下游服務通常以可讀機器格式(如 POJO、JSON 等)理解這些消息。Spring AI 能夠生成結構化輸出的能力在此發揮重要作用。
如 Advisors APIs 一樣,我們已經詳細地在其中一篇文檔中進行了説明,因此我們不會在此進行更深入的討論,而是直接進入下一個主題。
有時,應用程序需要查詢向量數據庫(Vector DB)以執行對存儲數據的語義搜索,從而檢索額外信息。隨後,從向量數據庫檢索到的結果會被用於提示(prompt),從而為 LLM 提供情境化信息。這種技術被稱為 RAG 技術,並且可以使用 Spring AI 實現它。
8. 結論
在本文中,我們探討了 Spring AI 的關鍵功能,這些功能可以幫助創建 AI 助手。Spring AI 正在不斷髮展,並提供了許多預配置的功能。然而,選擇合適的底層 LLM 服務和向量數據庫至關重要,無論編程框架如何,這一點都不可忽視。此外,優化這些服務的配置可能具有挑戰性,需要投入大量精力。儘管如此,為了應用程序的廣泛採用,仍然至關重要。