iT邦幫忙

2024 iThome 鐵人賽

DAY 20
0
生成式 AI

使用 Spring AI 打造企業 RAG 知識庫系列 第 20

Day20 - 自行開發Spring AI插件

  • 分享至 

  • xImage
  •  

揭開Advisor面紗

https://ithelp.ithome.com.tw/upload/images/20240820/20161290CbVKKopXpq.png
延續昨天的 Advisor,RequestResponseAdvisor 的設計方式就如同 Filter Chain,每個 Chain 節點都會回傳 Request 與 Response,原始碼如下

原始碼說明

public interface RequestResponseAdvisor {
	default AdvisedRequest adviseRequest(AdvisedRequest request, Map<String, Object> context) {
		return request;
	}
	default ChatResponse adviseResponse(ChatResponse response, Map<String, Object> context) {
		return response;
	}
	default Flux<ChatResponse> adviseResponse(Flux<ChatResponse> fluxResponse, Map<String, Object> context) {
		return fluxResponse;
	}
}

要實作 Advisor 非常簡單,主要分成 Request 及 Response,Response 因為有 call 及 stream 流式呼叫,所以提供兩個方法,以下是相關參數說明

  • AdvisedRequest request: 送給 AI 的所有內容都可在這找到,下面就是 AdvisedRequest 帶的參數
public record AdvisedRequest(ChatModel chatModel, String userText, String systemText, ChatOptions chatOptions, List<Media> media, List<String> functionNames, List<FunctionCallback> functionCallbacks, List<Message> messages,	
Map<String, Object> userParams, Map<String, Object> systemParams, List<RequestResponseAdvisor> advisors, Map<String, Object> advisorParams)
  • ChatResponse response: AI 回應的訊息,包含 MetaData 以及 回應的內容
	private final ChatResponseMetadata chatResponseMetadata;
	private final List<Generation> generations;
  • Map<String, Object> context: 貫穿所有 Advisor 的上下文參數,若要讓不同 Advisor 可以共用 request / response 以外的參數,只能透過 context 傳遞

程式實作

目標: 寫一隻 Log Advisor,記錄傳送訊息以及使用 Token 數

首先實作 TokenUsageLogAdvistor 類別,在送出 Request 前印出 ChatId 以及傳送的 Message
收到 Response 後從 ChatResponse 取出 AI 生成的內容以及 MetaData,將生成的內容以及花費的 Token 數量印出

@Slf4j
public class TokenUsageLogAdvistor implements RequestResponseAdvisor {
	@Override
	public AdvisedRequest adviseRequest(AdvisedRequest request, Map<String, Object> context) {
		log.info("Chat ID:{} User Message:{}",context.get("chatId"), request.userText());
		return RequestResponseAdvisor.super.adviseRequest(request, context);
	}
	@Override
	public ChatResponse adviseResponse(ChatResponse response, Map<String, Object> context) {
		log.info("Chat ID:{} Assistant Message:{}",context.get("chatId"), response.getResult().getOutput().getContent());
		log.info("PromptTokens:{}",response.getMetadata().getUsage().getPromptTokens());
		log.info("GenerationTokens:{}",response.getMetadata().getUsage().getGenerationTokens());
		log.info("TotalTokens:{}",response.getMetadata().getUsage().getTotalTokens());
		return RequestResponseAdvisor.super.adviseResponse(response, context);
	}
}

在昨天的 ChatService 中加入增強器

@RequiredArgsConstructor
@Service
public class ChatService {
	private final ChatClient chatClient;
	private final ChatMemory chatMemory = new InMemoryChatMemory();
	
	public String chat(String chatId, String userMessage) {	   
		return this.chatClient.prompt()
		.advisors( new MessageChatMemoryAdvisor(chatMemory, chatId, 30), 
		      new TokenUsageLogAdvistor()) //將自訂的 Advistor 附加其後
		.advisors(context -> {context.param("chatId", chatId);
							  context.param("lastN", 30);})
		.user(userMessage)
    .call().content();
	}
}

程式碼重點說明

  • @Slf4j 是 Lombok 提供的標註,可省去撰寫設定 Class 的程式碼,之後直接使用 log 變數就能輸出 log
  • chatId、lastN 並不包含在 request 中,若要讓 log advisor 取得只能透過 context 傳送,傳送參數的程式碼如下
.advisors(context -> {context.param("chatId", chatId);
                      context.param("lastN", 30);})
  • context 是一個 map,只要用下面方式就能取得內容
context.get("chatId")
  • ChatResponse 提供 getMetadata() 可取得資料回傳的額外資訊
  • 一般 AI 的 Token 會區分請求的 Prompt Token 與生成的 Generation Token,兩者費用不太相同
  • 若要進一步控制使用者的使用額度,可以將資料存於資料庫,並在調用前檢查

驗收成果

https://ithelp.ithome.com.tw/upload/images/20240820/20161290zCJN7gXZPG.png
可以看到 log 成功輸出,Chat ID 也能夠過 context 取得,另外多了 Token 數量輸出後可以很清楚知道每次發問使用的 Token 數量

回顧

今天學到了甚麼?

  • 如何自行開發 Advisor 程式
  • 多個 Advisor 如何串接
  • 額外參數如何透過 context 傳送

RequestResponseAdvisor 的方法皆為 default,不用全部實作

Source Code:

程式碼下載: https://github.com/kevintsai1202/SpringBoot-AI-Day20.git


認識凱文大叔

凱文大叔使用 Java 開發程式超過 20 年,對於 Java 生態非常熟悉,曾使用反射機制開發 ETL 框架,對 Spring 背後的原理非常清楚,目前以 Spring Boot 作為後端開發框架,前端使用 React 搭配 Ant Design
下班之餘在 Amazing Talker 擔任程式語言講師,並獲得學員的一致好評

最近剛成立一個粉絲專頁-凱文大叔教你寫程式 歡迎大家多追蹤,我會不定期分享實用的知識以及程式開發技巧

想討論 Spring 的 Java 開發人員可以加入 FB 討論區 Spring Boot Developer Taiwan

我是凱文大叔,歡迎一起加入學習程式的行列


上一篇
Day19 - Spring AI的鏈式增強器
下一篇
Day21 - 安裝 Neo4j 向量資料庫(docker)
系列文
使用 Spring AI 打造企業 RAG 知識庫21
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言