使用 Redis 和 Spring AI 創建 RAG(檢索增強生成)應用程式
1. 概述
在本教程中,我們將使用 Spring AI 框架和 RAG(檢索增強生成)技術來建立一個 ChatBot。在Spring AI的幫助下,我們將與Redis Vector資料庫整合來儲存和檢索數據,以增強LLM(大型語言模型)的提示。一旦LLM收到相關資料的提示,它就會有效地以自然語言產生最新資料對使用者查詢的回應。
2.什麼是RAG?
LLM 是根據網路上大量的資料集預先訓練的機器學習模型。為了使LLM在私人企業中發揮作用,我們必須根據組織特定的知識庫對其進行微調。然而,微調通常是一個耗時的過程,需要大量的運算資源。此外,經過微調的 LLM 很可能會對查詢產生不相關或誤導性的回應。這種行為通常被稱為法學碩士幻覺。
在這種情況下, RAG 是一種限製或情境化 LLM 回應的出色技術。向量資料庫在 RAG 架構中發揮重要作用,為法學碩士提供上下文資訊。但是,在應用程式可以在 RAG 架構中使用它之前,ETL(提取轉換和載入)過程必須填充它:
閱讀器從不同來源檢索組織的知識庫文件。然後,Transformer 將檢索到的文件分割成更小的區塊,並使用嵌入模型對內容進行向量化。最後,作者將向量或嵌入載入到向量資料庫中。向量資料庫是專門的資料庫,可以將這些嵌入儲存在多維空間中。
在 RAG 中,如果向量資料庫從組織的知識庫中定期更新,法學碩士可以對幾乎即時的資料做出回應。
一旦向量資料庫準備好數據,應用程式就可以使用它來檢索使用者查詢的上下文資料:
該應用程式將使用者查詢與向量資料庫中的上下文資料結合起來形成提示,並最終將其發送給法學碩士。 LLM 在上下文資料的範圍內以自然語言產生回應,並將其發送回應用程式。
3. 使用 Spring AI 和 Redis 實現 RAG
Redis 堆疊提供向量搜尋服務,我們將使用 Spring AI 框架與其整合並建立基於 RAG 的 ChatBot 應用程式。此外,我們將使用 OpenAI 的 GPT-3.5 Turbo LLM 模型來產生最終響應。
3.1.先決條件
對於 ChatBot 服務,要驗證 OpenAI 服務,我們需要 API 金鑰。建立OpenAI 帳戶後,我們將建立一個:
我們還將建立一個Redis 雲端帳戶來存取免費的 Redis Vector DB:
為了與 Redis Vector DB 和 OpenAI 服務集成,我們將使用 Spring AI 庫更新Maven 依賴項:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
<version>1.0.0-M1</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-transformers-spring-boot-starter</artifactId>
<version>1.0.0-M1</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis-spring-boot-starter</artifactId>
<version>1.0.0-M1</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pdf-document-reader</artifactId>
<version>1.0.0-M1</version>
</dependency>
3.2.將資料載入到 Redis 的關鍵類
在 Spring Boot 應用程式中,我們將建立用於從 Redis Vector DB 載入和檢索資料的元件。例如,我們將員工手冊 PDF 文件載入到 Redis DB 中。
現在,我們來看看涉及到的類別:
DocumentReader
是一個用來讀取文件的 Spring AI 介面。我們將使用DocumentReader
的現成PagePdfDocumentReader
實作。類似地, DocumentWriter
和VectorStore
是將資料寫入儲存系統的介面。 RedisVectorStore
是VectorStore,
我們將使用它在 Redis Vector DB 中載入和搜尋資料。我們將使用到目前為止討論的 Spring AI 框架類別來編寫DataLoaderService
。
3.3.實施資料加載服務
讓我們來了解一下DataLoaderService
類別中的load()
方法:
@Service
public class DataLoaderService {
private static final Logger logger = LoggerFactory.getLogger(DataLoaderService.class);
@Value("classpath:/data/Employee_Handbook.pdf")
private Resource pdfResource;
@Autowired
private VectorStore vectorStore;
public void load() {
PagePdfDocumentReader pdfReader = new PagePdfDocumentReader(this.pdfResource,
PdfDocumentReaderConfig.builder()
.withPageExtractedTextFormatter(ExtractedTextFormatter.builder()
.withNumberOfBottomTextLinesToDelete(3)
.withNumberOfTopPagesToSkipBeforeDelete(1)
.build())
.withPagesPerDocument(1)
.build());
var tokenTextSplitter = new TokenTextSplitter();
this.vectorStore.accept(tokenTextSplitter.apply(pdfReader.get()));
}
}
load()
方法使用PagePdfDocumentReader
類別讀取 PDF 檔案並將其載入到 Redis Vector DB。 Spring AI 框架使用命名VectoreStore
**spring.ai.vectorstore** :
spring:
ai:
vectorstore:
redis:
uri: redis://:PQzkkZLOgOXXX@redis-19438.c330.asia-south1-1.gce.redns.redis-cloud.com:19438
index: faqs
prefix: "faq:"
initialize-schema: true
此框架將RedisVectorStore
物件( VectorStore
介面的實作)注入到DataLoaderService
中。
TokenTextSplitter
類別分割文檔,最後, VectorStore
類別將區塊載入到 Redis Vector DB 中。
3.4.產生最終響應的關鍵類
一旦 Redis Vector DB 準備就緒,我們就可以檢索與使用者查詢相關的上下文資訊。然後,該上下文用於形成 LLM 產生最終回應的提示。讓我們來看看關鍵的類別:
DataRetrievalService
類別中的searchData()
方法接受查詢,然後從VectorStore
中擷取上下文資料。 ChatBotService
使用此資料透過PromptTemplate
類別形成提示,然後將其傳送至 OpenAI 服務。 Spring Boot 框架從application.yml
檔案讀取與 OpenAI 相關的相關屬性,然後自動設定OpenAIChatModel
物件。
讓我們跳到實作來詳細了解。
3.5.實施聊天機器人服務
我們來看看ChatBotService
類別:
@Service
public class ChatBotService {
@Autowired
private ChatModel chatClient;
@Autowired
private DataRetrievalService dataRetrievalService;
private final String PROMPT_BLUEPRINT = """
Answer the query strictly referring the provided context:
{context}
Query:
{query}
In case you don't have any answer from the context provided, just say:
I'm sorry I don't have the information you are looking for.
""";
public String chat(String query) {
return chatClient.call(createPrompt(query, dataRetrievalService.searchData(query)));
}
private String createPrompt(String query, List<Document> context) {
PromptTemplate promptTemplate = new PromptTemplate(PROMPT_BLUEPRINT);
promptTemplate.add("query", query);
promptTemplate.add("context", context);
return promptTemplate.render();
}
}
SpringAI 框架使用命名空間spring.ai.openai
中的OpenAI配置屬性來建立ChatModel
bean:
spring:
ai:
vectorstore:
redis:
# Redis vector store related properties...
openai:
temperature: 0.3
api-key: ${SPRING_AI_OPENAI_API_KEY}
model: gpt-3.5-turbo
#embedding-base-url: https://api.openai.com
#embedding-api-key: ${SPRING_AI_OPENAI_API_KEY}
#embedding-model: text-embedding-ada-002
該框架還可以從環境變數SPRING_AI_OPENAI_API_KEY
讀取 API 金鑰,這是一個非常安全的選項。我們可以啟用從文字embedding
開始的鍵來建立OpenAiEmbeddingModel
bean,該 bean 用於從知識庫文件建立向量嵌入。
OpenAI 服務的提示必須明確。因此,我們在提示藍圖中PROMPT_BLUEPRINT
中嚴格指示僅根據上下文資訊形成回應。
在chat()
方法中,我們檢索文檔,以匹配來自 Redis Vector DB 的查詢。然後,我們使用這些文件和使用者查詢在createPrompt()
方法中產生提示。最後,我們呼叫ChatModel
類別的call()
方法來接收來自 OpenAI 服務的回應。
現在,讓我們透過詢問先前載入到 Redis Vector DB 的員工手冊中的問題來檢查正在運行的聊天機器人服務:
@Test
void whenQueryAskedWithinContext_thenAnswerFromTheContext() {
String response = chatBotService.chat("How are employees supposed to dress?");
assertNotNull(response);
logger.info("Response from LLM: {}", response);
}
然後,我們將看到輸出:
Response from LLM: Employees are supposed to dress appropriately for their individual work responsibilities and position.
輸出與載入到 Redis Vector DB 中的員工手冊 PDF 文件一致。
讓我們看看如果我們問一些員工手冊中沒有的問題會發生什麼:
@Test
void whenQueryAskedOutOfContext_thenDontAnswer() {
String response = chatBotService.chat("What should employees eat?");
assertEquals("I'm sorry I don't have the information you are looking for.", response);
logger.info("Response from the LLM: {}", response);
}
這裡是結果輸出:
Response from the LLM: I'm sorry I don't have the information you are looking for.
法學碩士在提供的上下文中找不到任何內容,因此無法回答查詢。
4。
在本文中,我們討論了使用 Spring AI 框架實作基於 RAG 架構的應用程式。使用上下文資訊形成提示對於法學碩士產生正確的回應至關重要。因此,Redis Vector DB 是儲存文件向量並執行相似性搜尋的絕佳解決方案。此外,對文件進行分塊對於取得正確的記錄和限制提示令牌的成本同樣重要。
本文使用的程式碼可在 GitHub 上取得。