Spring Boot集成LangChain來實現(xiàn)Rag應用的問題小結
1.什么是rag?
檢索增強生成(RAG)是指對大型語言模型輸出進行優(yōu)化,使其能夠在生成響應之前引用訓練數(shù)據(jù)來源之外的權威知識庫。大型語言模型(LLM)用海量數(shù)據(jù)進行訓練,使用數(shù)十億個參數(shù)為回答問題、翻譯語言和完成句子等任務生成原始輸出。在 LLM 本就強大的功能基礎上,RAG 將其擴展為能訪問特定領域或組織的內部知識庫,所有這些都無需重新訓練模型。這是一種經濟高效地改進 LLM 輸出的方法,讓它在各種情境下都能保持相關性、準確性和實用性。
為什么檢索增強生成很重要?
LLM 是一項關鍵的人工智能(AI)技術,為智能聊天機器人和其他自然語言處理(NLP)應用程序提供支持。目標是通過交叉引用權威知識來源,創(chuàng)建能夠在各種環(huán)境中回答用戶問題的機器人。不幸的是,LLM 技術的本質在 LLM 響應中引入了不可預測性。此外,LLM 訓練數(shù)據(jù)是靜態(tài)的,并引入了其所掌握知識的截止日期。 LLM 面臨的已知挑戰(zhàn)包括:
- 在沒有答案的情況下提供虛假信息。
- 當用戶需要特定的當前響應時,提供過時或通用的信息。
- 從非權威來源創(chuàng)建響應。
- 由于術語混淆,不同的培訓來源使用相同的術語來談論不同的事情,因此會產生不準確的響應。
您可以將大型語言模型看作是一個過于熱情的新員工,他拒絕隨時了解時事,但總是會絕對自信地回答每一個問題。不幸的是,這種態(tài)度會對用戶的信任產生負面影響,這是您不希望聊天機器人效仿的! RAG 是解決其中一些挑戰(zhàn)的一種方法。它會重定向 LLM,從權威的、預先確定的知識來源中檢索相關信息。組織可以更好地控制生成的文本輸出,并且用戶可以深入了解 LLM 如何生成響應。
檢索增強生成的工作原理是什么?
如果沒有 RAG,LLM 會接受用戶輸入,并根據(jù)它所接受訓練的信息或它已經知道的信息創(chuàng)建響應。RAG 引入了一個信息檢索組件,該組件利用用戶輸入首先從新數(shù)據(jù)源提取信息。用戶查詢和相關信息都提供給 LLM。LLM 使用新知識及其訓練數(shù)據(jù)來創(chuàng)建更好的響應。以下各部分概述了該過程。
創(chuàng)建外部數(shù)據(jù)
LLM 原始訓練數(shù)據(jù)集之外的新數(shù)據(jù)稱為外部數(shù)據(jù)。它可以來自多個數(shù)據(jù)來源,例如 API、數(shù)據(jù)庫或文檔存儲庫。數(shù)據(jù)可能以各種格式存在,例如文件、數(shù)據(jù)庫記錄或長篇文本。另一種稱為嵌入語言模型的 AI 技術將數(shù)據(jù)轉換為數(shù)字表示形式并將其存儲在向量數(shù)據(jù)庫中。這個過程會創(chuàng)建一個生成式人工智能模型可以理解的知識庫。
檢索相關信息
下一步是執(zhí)行相關性搜索。用戶查詢將轉換為向量表示形式,并與向量數(shù)據(jù)庫匹配。例如,考慮一個可以回答組織的人力資源問題的智能聊天機器人。如果員工搜索:“我有多少年假?”,系統(tǒng)將檢索年假政策文件以及員工個人過去的休假記錄。這些特定文件將被退回,因為它們與員工輸入的內容高度相關。相關性是使用數(shù)學向量計算和表示法計算和建立的。
增強 LLM 提示
接下來,RAG 模型通過在上下文中添加檢索到的相關數(shù)據(jù)來增強用戶輸入(或提示)。此步驟使用提示工程技術與 LLM 進行有效溝通。增強提示允許大型語言模型為用戶查詢生成準確的答案。
更新外部數(shù)據(jù)
下一個問題可能是——如果外部數(shù)據(jù)過時了怎么辦? 要維護當前信息以供檢索,請異步更新文檔并更新文檔的嵌入表示形式。您可以通過自動化實時流程或定期批處理來執(zhí)行此操作。這是數(shù)據(jù)分析中常見的挑戰(zhàn)——可以使用不同的數(shù)據(jù)科學方法進行變更管理。 下圖顯示了將 RAG 與 LLM 配合使用的概念流程。
2.什么是LangChain?
LangChain 是一個用于開發(fā)由語言模型驅動的應用程序的框架。他主要擁有 2 個能力:
- 可以將 LLM 模型與外部數(shù)據(jù)源進行連接
- 允許與 LLM 模型進行交互
LLM 模型:Large Language Model,大型語言模型
3.代碼工程
實驗目的
利用LangChain實現(xiàn)rag應用
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.2.1</version> <relativePath/> <!-- lookup parent from repository --> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>rag</artifactId> <properties> <java.version>17</java.version> <langchain4j.version>0.23.0</langchain4j.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j</artifactId> <version>${langchain4j.version}</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai</artifactId> <version>${langchain4j.version}</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-embeddings</artifactId> <version>${langchain4j.version}</version> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-embeddings-all-minilm-l6-v2</artifactId> <version>${langchain4j.version}</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
controller
package com.et.rag.controller; import com.et.rag.service.SBotService; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @Controller @RequiredArgsConstructor public class SBotController { private final SBotService sBotService; @GetMapping public String home() { return "index"; } @PostMapping("/ask") public ResponseEntity<String> ask(@RequestBody String question) { try { return ResponseEntity.ok(sBotService.askQuestion(question)); } catch (Exception e) { return ResponseEntity.badRequest().body("Sorry, I can't process your question right now."); } } }
service
package com.et.rag.service; import dev.langchain4j.chain.ConversationalRetrievalChain; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Service @RequiredArgsConstructor @Slf4j public class SBotService { private final ConversationalRetrievalChain chain; public String askQuestion(String question) { log.debug("======================================================"); log.debug("Question: " + question); String answer = chain.execute(question); log.debug("Answer: " + answer); log.debug("======================================================"); return answer; } }
EmbeddingStoreLoggingRetriever
package com.et.rag.retriever; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.retriever.EmbeddingStoreRetriever; import dev.langchain4j.retriever.Retriever; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import java.util.List; /** * EmbeddingStoreLoggingRetriever is a logging-enhanced for an EmbeddingStoreRetriever. * <p> * This class logs the relevant TextSegments discovered by the supplied * EmbeddingStoreRetriever for improved transparency and debugging. * <p> * Logging happens at INFO level, printing each relevant TextSegment found * for a given input text once the findRelevant method is called. */ @RequiredArgsConstructor @Slf4j public class EmbeddingStoreLoggingRetriever implements Retriever<TextSegment> { private final EmbeddingStoreRetriever retriever; @Override public List<TextSegment> findRelevant(String text) { List<TextSegment> relevant = retriever.findRelevant(text); relevant.forEach(segment -> { log.debug("======================================================="); log.debug("Found relevant text segment: {}", segment); }); return relevant; } }
components
初始化documents
package com.et.rag.configuration; import dev.langchain4j.data.document.Document; import dev.langchain4j.data.document.UrlDocumentLoader; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.List; import static com.et.rag.constant.Constants.SPRING_BOOT_RESOURCES_LIST; @Configuration public class DocumentConfiguration { @Bean public List<Document> documents() { return SPRING_BOOT_RESOURCES_LIST.stream() .map(url -> { try { return UrlDocumentLoader.load(url); } catch (Exception e) { throw new RuntimeException("Failed to load document from " + url, e); } }) .toList(); } }
初始化langchain
package com.et.rag.configuration; import com.et.rag.retriever.EmbeddingStoreLoggingRetriever; import dev.langchain4j.chain.ConversationalRetrievalChain; import dev.langchain4j.data.document.Document; import dev.langchain4j.data.document.splitter.DocumentSplitters; import dev.langchain4j.data.segment.TextSegment; import dev.langchain4j.model.embedding.AllMiniLmL6V2EmbeddingModel; import dev.langchain4j.model.embedding.EmbeddingModel; import dev.langchain4j.model.input.PromptTemplate; import dev.langchain4j.model.openai.OpenAiChatModel; import dev.langchain4j.retriever.EmbeddingStoreRetriever; import dev.langchain4j.store.embedding.EmbeddingStore; import dev.langchain4j.store.embedding.EmbeddingStoreIngestor; import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.time.Duration; import java.util.List; import static com.et.rag.constant.Constants.PROMPT_TEMPLATE_2; @Configuration @RequiredArgsConstructor @Slf4j public class LangChainConfiguration { @Value("${langchain.api.key}") private String apiKey; @Value("${langchain.timeout}") private Long timeout; private final List<Document> documents; @Bean public ConversationalRetrievalChain chain() { EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel(); EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>(); EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder() .documentSplitter(DocumentSplitters.recursive(500, 0)) .embeddingModel(embeddingModel) .embeddingStore(embeddingStore) .build(); log.info("Ingesting Spring Boot Resources ..."); ingestor.ingest(documents); log.info("Ingested {} documents", documents.size()); EmbeddingStoreRetriever retriever = EmbeddingStoreRetriever.from(embeddingStore, embeddingModel); EmbeddingStoreLoggingRetriever loggingRetriever = new EmbeddingStoreLoggingRetriever(retriever); /*MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder() .maxMessages(10) .build();*/ log.info("Building ConversationalRetrievalChain ..."); ConversationalRetrievalChain chain = ConversationalRetrievalChain.builder() .chatLanguageModel(OpenAiChatModel.builder() .apiKey(apiKey) .timeout(Duration.ofSeconds(timeout)) .build() ) .promptTemplate(PromptTemplate.from(PROMPT_TEMPLATE_2)) //.chatMemory(chatMemory) .retriever(loggingRetriever) .build(); log.info("Spring Boot knowledge base is ready!"); return chain; } }
application.yaml
langchain: api: # "demo" is a free API key for testing purposes only. Please replace it with your own API key. key: demo # key: OPEN_API_KEY # API call to complete before it is timed out. timeout: 30
index.html
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Spring Boot Doc Bot</title> <link rel="external nofollow" rel="stylesheet"> <link rel="stylesheet" rel="external nofollow" > </head> <body> <nav class="bg-dark text-white py-3"> <div class="text-center d-flex justify-content-center align-items-center"> <img src="/logo.png" alt="Logo" style="width:60px; margin-right: 10px;"> <h2 style="margin: 0;">Welcome to Spring Boot Documentation Bot</h2> </div> </nav> <div class="container mt-5"> <div class="row"> <div class="col-md-8 offset-2"> <h3 class="text-center mb-3">Ask your Spring related queries here!</h3> <form> <div class="mb-3"> <label for="questionInput" class="form-label">Question</label> <input type="text" class="form-control" id="questionInput" name="question" placeholder="Enter your question" required> </div> <div class="mb-3 text-center"> <button id="submitBtn" type="button" class="btn btn-primary">Ask!</button> <button id="clearBtn" type="button" class="btn btn-secondary">Clear</button> </div> </form> </div> </div> <div class="row my-5"> <div class="col-md-8 offset-md-2"> <label for="answerBox" class="form-label"><h5>Answer</h5></label> <div class="position-relative my-3"> <textarea class="form-control" rows="10" id="answerBox" disabled></textarea> <a href="#" rel="external nofollow" class="position-absolute top-0 end-0 m-2" id="copyBtn"> <i class="far fa-copy"></i> </a> </div> </div> </div> </div> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <script> $(document).ready(function () { $("#submitBtn").click(function () { let questionValue = $("#questionInput").val(); if (!questionValue) { alert('Please enter your question'); return; } $("#answerBox").val('Please wait... fetching answer...'); $.ajax({ type: "POST", url: "/ask", data: JSON.stringify({ question: $("#questionInput").val() }), //contentType: "application/json; charset=utf-8", dataType: "text", success: function (data) { //console.log(typeof data); //console.log(data); $("#answerBox").val(data); }, error: function (errMsg) { alert(errMsg); } }); }); $("#clearBtn").click(function () { $("#questionInput").val(''); $("#answerBox").val(''); }); document.getElementById("copyBtn").addEventListener("click", function() { var copyText = document.getElementById("answerBox"); copyText.select(); copyText.setSelectionRange(0, 99999); document.execCommand("copy"); alert("Copied: " + copyText.value); }); }); </script> </body> </html>
只是一些關鍵代碼,所有代碼請參見下面代碼倉庫
代碼倉庫
4.測試
啟動Spring Boot應用,訪問http://127.0.0.1:8080/
5.引用
GitHub - liaokongVFX/LangChain-Chinese-Getting-Started-Guide: LangChain 的中文入門教程
Spring Boot集成LangChain來實現(xiàn)Rag應用 | Harries Blog™
到此這篇關于Spring Boot集成LangChain來實現(xiàn)Rag應用的文章就介紹到這了,更多相關Spring Boot集成LangChain Rag應用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
springboot+websocket實現(xiàn)并發(fā)搶紅包功能
本文主要介紹了springboot+websocket實現(xiàn)并發(fā)搶紅包功能,主要包含了4種步驟,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-12-12SpringBoot整合Spring?Boot?Admin實現(xiàn)服務監(jiān)控的方法
這篇文章主要介紹了SpringBoot整合Spring?Boot?Admin實現(xiàn)服務監(jiān)控,內容包括Server端服務開發(fā),Client端服務開發(fā)其中Spring Boot Admin還可以對其監(jiān)控的服務提供告警功能,如服務宕機時,可以及時以郵件方式通知運維人員,感興趣的朋友跟隨小編一起看看吧2022-03-03Maven項目引用第三方jar包找不到類ClassNotFoundException
這篇文章主要為大家介紹了Maven項目引用第三方jar包找不到類ClassNotFoundException解決及原因分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07淺談MyBatisPlus中LocalDateTime引發(fā)的一些問題和解決辦法
MyBatisPlus進行數(shù)據(jù)庫操作時,我們經常會遇到處理日期時間類型的需求,本文主要介紹了淺談MyBatisPlus中LocalDateTime引發(fā)的一些問題和解決辦法,具有一定的參考價值,感興趣的可以了解一下2024-07-07一文詳解SpringBoot使用Kafka如何保證消息不丟失
這篇文章主要為大家詳細介紹了SpringBoot使用Kafka如何保證消息不丟失的相關知識,文中的示例代碼講解詳細,有需要的小伙伴可以參考一下2025-01-01java.imageIo給圖片添加水印的實現(xiàn)代碼
最近項目在做一個商城項目, 項目上的圖片要添加水印①,添加圖片水印;②:添加文字水印;一下提供下個方法,希望大家可以用得著2013-07-07