檢索增強生成

檢索增強生成(RAG)是一種技術,用於克服大型語言模型在處理長篇內容、事實準確性和上下文感知方面遇到的侷限性。

Spring AI 透過提供模組化架構來支援 RAG,該架構允許您自行構建自定義 RAG 流程,或使用 Advisor API 使用開箱即用的 RAG 流程。

概念部分了解更多關於檢索增強生成的資訊。

Advisors

Spring AI 使用 Advisor API 為常見的 RAG 流程提供開箱即用的支援。

要使用 QuestionAnswerAdvisorRetrievalAugmentationAdvisor,您需要將 spring-ai-advisors-vector-store 依賴項新增到您的專案中

<dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>

QuestionAnswerAdvisor

向量資料庫儲存 AI 模型不知道的資料。當用戶問題傳送到 AI 模型時,QuestionAnswerAdvisor 查詢向量資料庫以獲取與使用者問題相關的文件。

向量資料庫的響應被附加到使用者文字中,為 AI 模型生成響應提供上下文。

假設您已將資料載入到 VectorStore 中,您可以透過向 ChatClient 提供一個 QuestionAnswerAdvisor 例項來執行檢索增強生成 (RAG)。

ChatResponse response = ChatClient.builder(chatModel)
        .build().prompt()
        .advisors(new QuestionAnswerAdvisor(vectorStore))
        .user(userText)
        .call()
        .chatResponse();

在此示例中,QuestionAnswerAdvisor 將對向量資料庫中的所有文件執行相似度搜索。為了限制搜尋的文件型別,SearchRequest 接受類似 SQL 的過濾表示式,該表示式可在所有 VectorStore 中移植。

此過濾表示式可以在建立 QuestionAnswerAdvisor 時配置,因此將始終應用於所有 ChatClient 請求,或者可以在執行時按請求提供。

以下是如何建立一個 QuestionAnswerAdvisor 例項,其中閾值為 0.8 並返回前 6 個結果。

var qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
        .searchRequest(SearchRequest.builder().similarityThreshold(0.8d).topK(6).build())
        .build();

動態過濾表示式

使用 FILTER_EXPRESSION advisor 上下文引數在執行時更新 SearchRequest 過濾表示式

ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(QuestionAnswerAdvisor.builder(vectorStore)
        .searchRequest(SearchRequest.builder().build())
        .build())
    .build();

// Update filter expression at runtime
String content = this.chatClient.prompt()
    .user("Please answer my question XYZ")
    .advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
    .call()
    .content();

FILTER_EXPRESSION 引數允許您根據提供的表示式動態過濾搜尋結果。

自定義模板

QuestionAnswerAdvisor 使用預設模板用檢索到的文件增強使用者問題。您可以透過 .promptTemplate() builder 方法提供自己的 PromptTemplate 物件來定製此行為。

此處提供的 PromptTemplate 定製了 advisor 如何將檢索到的上下文與使用者查詢合併。這與在 ChatClient 本身(使用 .templateRenderer())上配置 TemplateRenderer 不同,後者影響 advisor 執行之前的初始使用者/系統提示內容渲染。有關客戶端級別模板渲染的更多詳細資訊,請參見ChatClient 提示詞模板

自定義 PromptTemplate 可以使用任何 TemplateRenderer 實現(預設情況下,它使用基於 StringTemplate 引擎的 StPromptTemplate)。重要的要求是模板必須包含一個佔位符來接收檢索到的上下文,advisor 在鍵 question_answer_context 下提供此上下文。

PromptTemplate customPromptTemplate = PromptTemplate.builder()
    .renderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build())
    .template("""
            Context information is below.

			---------------------
			<question_answer_context>
			---------------------

			Given the context information and no prior knowledge, answer the query.

			Follow these rules:

			1. If the answer is not in the context, just say that you don't know.
			2. Avoid statements like "Based on the context..." or "The provided information...".
            """)
    .build();

    String question = "Where does the adventure of Anacletus and Birba take place?";

    QuestionAnswerAdvisor qaAdvisor = QuestionAnswerAdvisor.builder(vectorStore)
        .promptTemplate(customPromptTemplate)
        .build();

    String response = ChatClient.builder(chatModel).build()
        .prompt(question)
        .advisors(qaAdvisor)
        .call()
        .content();
建議使用 .promptTemplate() 進行更靈活的定製,而不是使用已棄用的 QuestionAnswerAdvisor.Builder.userTextAdvise() 方法。

RetrievalAugmentationAdvisor (孵化中)

Spring AI 包含一個RAG 模組庫,您可以使用它來構建自己的 RAG 流程。RetrievalAugmentationAdvisor 是一個 Advisor,它基於模組化架構,為最常見的 RAG 流程提供開箱即用的實現。

順序 RAG 流程

樸素 RAG
Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .user(question)
        .call()
        .content();

預設情況下,RetrievalAugmentationAdvisor 不允許檢索到的上下文為空。發生這種情況時,它會指示模型不回答使用者查詢。您可以按如下方式允許空上下文。

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .queryAugmenter(ContextualQueryAugmenter.builder()
                .allowEmptyContext(true)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .user(question)
        .call()
        .content();

VectorStoreDocumentRetriever 接受一個 FilterExpression,用於根據元資料過濾搜尋結果。您可以在例項化 VectorStoreDocumentRetriever 時或在執行時按請求提供,使用 FILTER_EXPRESSION advisor 上下文引數。

Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .advisors(a -> a.param(VectorStoreDocumentRetriever.FILTER_EXPRESSION, "type == 'Spring'"))
        .user(question)
        .call()
        .content();

有關詳細資訊,請參見VectorStoreDocumentRetriever

高階 RAG
Advisor retrievalAugmentationAdvisor = RetrievalAugmentationAdvisor.builder()
        .queryTransformers(RewriteQueryTransformer.builder()
                .chatClientBuilder(chatClientBuilder.build().mutate())
                .build())
        .documentRetriever(VectorStoreDocumentRetriever.builder()
                .similarityThreshold(0.50)
                .vectorStore(vectorStore)
                .build())
        .build();

String answer = chatClient.prompt()
        .advisors(retrievalAugmentationAdvisor)
        .user(question)
        .call()
        .content();

模組

Spring AI 實現了一種模組化 RAG 架構,其靈感來自於論文 "Modular RAG: Transforming RAG Systems into LEGO-like Reconfigurable Frameworks" 中詳細闡述的模組化概念。

檢索前

檢索前模組負責處理使用者查詢,以獲得最佳的檢索結果。

查詢轉換

用於轉換輸入查詢以使其更有效地執行檢索任務的元件,解決諸如格式不規範的查詢、歧義術語、複雜詞彙或不支援的語言等挑戰。

使用 QueryTransformer 時,建議將 ChatClient.Builder 配置為低溫(例如 0.0),以確保更確定性和準確的結果,從而提高檢索質量。大多數聊天模型的預設溫度對於最優的查詢轉換來說通常過高,會導致檢索效果降低。
CompressionQueryTransformer

CompressionQueryTransformer 使用大型語言模型將對話歷史和後續查詢壓縮成一個獨立的查詢,該查詢捕獲了對話的精髓。

當對話歷史很長且後續查詢與對話上下文相關時,此轉換器非常有用。

Query query = Query.builder()
        .text("And what is its second largest city?")
        .history(new UserMessage("What is the capital of Denmark?"),
                new AssistantMessage("Copenhagen is the capital of Denmark."))
        .build();

QueryTransformer queryTransformer = CompressionQueryTransformer.builder()
        .chatClientBuilder(chatClientBuilder)
        .build();

Query transformedQuery = queryTransformer.transform(query);

此元件使用的提示詞可以透過 builder 中提供的 promptTemplate() 方法進行定製。

RewriteQueryTransformer

RewriteQueryTransformer 使用大型語言模型重寫使用者查詢,以便在查詢目標系統(如向量儲存或網路搜尋引擎)時提供更好的結果。

當用戶查詢冗長、模糊或包含可能影響搜尋結果質量的不相關資訊時,此轉換器非常有用。

Query query = new Query("I'm studying machine learning. What is an LLM?");

QueryTransformer queryTransformer = RewriteQueryTransformer.builder()
        .chatClientBuilder(chatClientBuilder)
        .build();

Query transformedQuery = queryTransformer.transform(query);

此元件使用的提示詞可以透過 builder 中提供的 promptTemplate() 方法進行定製。

TranslationQueryTransformer

TranslationQueryTransformer 使用大型語言模型將查詢翻譯成目標語言,該目標語言由用於生成文件嵌入的嵌入模型支援。如果查詢已經處於目標語言,則原樣返回。如果查詢的語言未知,也原樣返回。

當嵌入模型在一個特定語言上訓練而使用者查詢在另一個不同語言時,此轉換器非常有用。

Query query = new Query("Hvad er Danmarks hovedstad?");

QueryTransformer queryTransformer = TranslationQueryTransformer.builder()
        .chatClientBuilder(chatClientBuilder)
        .targetLanguage("english")
        .build();

Query transformedQuery = queryTransformer.transform(query);

此元件使用的提示詞可以透過 builder 中提供的 promptTemplate() 方法進行定製。

查詢擴充套件

將輸入查詢擴充套件為查詢列表的元件,透過提供替代查詢表述或將複雜問題分解為更簡單的子查詢來解決諸如格式不規範的查詢等挑戰。

MultiQueryExpander

MultiQueryExpander 使用大型語言模型將一個查詢擴充套件為多個語義上不同的變體,以捕捉不同的視角,有助於檢索額外的上下文資訊並增加找到相關結果的機會。

MultiQueryExpander queryExpander = MultiQueryExpander.builder()
    .chatClientBuilder(chatClientBuilder)
    .numberOfQueries(3)
    .build();
List<Query> queries = queryExpander.expand(new Query("How to run a Spring Boot app?"));

預設情況下,MultiQueryExpander 在擴充套件查詢列表中包含原始查詢。您可以透過 builder 中的 includeOriginal 方法停用此行為。

MultiQueryExpander queryExpander = MultiQueryExpander.builder()
    .chatClientBuilder(chatClientBuilder)
    .includeOriginal(false)
    .build();

此元件使用的提示詞可以透過 builder 中提供的 promptTemplate() 方法進行定製。

檢索

檢索模組負責查詢資料系統(如向量儲存)並檢索最相關的文件。

負責從底層資料來源(如搜尋引擎、向量儲存、資料庫或知識圖譜)檢索 Documents 的元件。

VectorStoreDocumentRetriever

VectorStoreDocumentRetriever 從向量儲存中檢索與輸入查詢在語義上相似的文件。它支援基於元資料、相似度閾值和 top-k 結果的過濾。

DocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
    .vectorStore(vectorStore)
    .similarityThreshold(0.73)
    .topK(5)
    .filterExpression(new FilterExpressionBuilder()
        .eq("genre", "fairytale")
        .build())
    .build();
List<Document> documents = retriever.retrieve(new Query("What is the main character of the story?"));

過濾表示式可以是靜態的或動態的。對於動態過濾表示式,您可以傳遞一個 Supplier

DocumentRetriever retriever = VectorStoreDocumentRetriever.builder()
    .vectorStore(vectorStore)
    .filterExpression(() -> new FilterExpressionBuilder()
        .eq("tenant", TenantContextHolder.getTenantIdentifier())
        .build())
    .build();
List<Document> documents = retriever.retrieve(new Query("What are the KPIs for the next semester?"));

您還可以透過 Query API,使用 FILTER_EXPRESSION 引數提供特定於請求的過濾表示式。如果同時提供了特定於請求和特定於檢索器的過濾表示式,則特定於請求的過濾表示式優先。

Query query = Query.builder()
    .text("Who is Anacletus?")
    .context(Map.of(VectorStoreDocumentRetriever.FILTER_EXPRESSION, "location == 'Whispering Woods'"))
    .build();
List<Document> retrievedDocuments = documentRetriever.retrieve(query);

文件合併

用於將基於多個查詢和來自多個數據源檢索到的文件合併為一個文件集合的元件。作為合併過程的一部分,它還可以處理重複文件和倒數排序策略。

ConcatenationDocumentJoiner

ConcatenationDocumentJoiner 透過將基於多個查詢和來自多個數據源檢索到的文件串聯起來,將它們合併成一個文件集合。如果存在重複文件,則保留第一次出現。每個文件的分數保持不變。

Map<Query, List<List<Document>>> documentsForQuery = ...
DocumentJoiner documentJoiner = new ConcatenationDocumentJoiner();
List<Document> documents = documentJoiner.join(documentsForQuery);

檢索後

檢索後模組負責處理檢索到的文件,以獲得最佳的生成結果。

文件後處理

用於根據查詢對檢索到的文件進行後處理的元件,解決諸如中間遺失、模型上下文長度限制以及減少檢索資訊中的噪聲和冗餘的需求等挑戰。

例如,它可以根據文件與查詢的相關性進行排名,刪除不相關或冗餘的文件,或壓縮每個文件的內容以減少噪聲和冗餘。

生成

生成模組負責根據使用者查詢和檢索到的文件生成最終響應。

查詢增強

用於使用附加資料增強輸入查詢的元件,有助於為大型語言模型提供回答使用者查詢所需的上下文。

ContextualQueryAugmenter

ContextualQueryAugmenter 使用所提供文件內容中的上下文資料增強使用者查詢。

QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder().build();

預設情況下,ContextualQueryAugmenter 不允許檢索到的上下文為空。發生這種情況時,它會指示模型不回答使用者查詢。

您可以啟用 allowEmptyContext 選項,即使檢索到的上下文為空,也允許模型生成響應。

QueryAugmenter queryAugmenter = ContextualQueryAugmenter.builder()
        .allowEmptyContext(true)
        .build();

此元件使用的提示詞可以透過 builder 中提供的 promptTemplate()emptyContextPromptTemplate() 方法進行定製。