【LangChain4J】LangChain4J 第四弹:RAG 的多种实现方式
目录
一、RAG 的定义
二、简单 RAG 的实现
三、高级 RAG 实现
一、RAG 的定义
定义:RAG(Retrieval-Augmented Generation,检索增强生成)是通过从外接数据源检索相关信息,并注入提示词,提升大语言模型(LLM)查询精准度的一种技术。
通过这种实现方式,大语言模型可以获取到特定领域的相关信息,并能够利用这些信息进行回复,从而降低了发生幻觉的可能性。
RAG 解决传统大模型局限性:
-
知识过时:传统大模型依赖预训练数据,无法获取最新信息。
-
幻觉问题:传统模型可能生成不准确或虚假的信息。
RAG 优势:
-
实时更新:允许模型访问最新信息,解决知识局限性问题。
-
提高准确性:通过引用外部知识库,减少幻觉现象。
-
经济高效:与重新训练模型相比,降低了更新成本。
二、简单 RAG 的实现
1. 添加依赖
<dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-easy-rag</artifactId><version>1.0.0-beta3</version>
</dependency>
2. 加载知识库
将知识库加载到集合中(EmbeddingStoreIngestor 内置嵌入模型实现数据转换向量,但是维度是固定的,精确度不会特别高):
// 加载知识库Document document = FileSystemDocumentLoader.loadDocument("D:\\forum_system.sql");// 向量存储器:负责将向量存储到向量数据库EmbeddingStoreIngestor.ingest(document,embeddingStore);
3. 知识库存入向量数据库
Document document = FileSystemDocumentLoader.loadDocument("D:\\forum_system.sql");// 向量存储器:负责将向量存储到向量数据库EmbeddingStoreIngestor.ingest(document,embeddingStore);
实现过程:LangChain4j 选择 bge-small-en-v1.5 作为 Easy RAG 的默认嵌入模型,通过 SPI(服务发现机制)加载实例,并使用分词器将其文本分块,并存储向量数据库中。
4. 调用知识库查询信息
// 首先将知识库加载到数据库中@PostConstructpublic void init() {Document document = FileSystemDocumentLoader.loadDocument("D:\\forum_system.sql");// 向量存储器:负责将向量存储到向量数据库EmbeddingStoreIngestor.ingest(document,embeddingStore);}@RequestMapping("/chat")public String query(String question) {EasyRagService easyRagService = AiServices.builder(EasyRagService.class).chatLanguageModel(qwenChatModel)// 关键代码.contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore)).build();return easyRagService.chat(question);}
三、高级 RAG 实现
它的执行流程如下:
-
用户执行一个[查询]操作,该查询被转换为 UserMessage 对象。
-
QueryTransformer 将[查询]转换成一个或多个 Query 对象。
-
每个 Query 对象通过 QueryRouter 被路由到一个或多个 ContentRetriever。
-
每个 ContentRetriever 为每个 Query 检索相关的 Content 内容。
-
ContentAggregator(内容聚合器)将所有检索到的 Content 合并成一个最终的排序列表。
-
这个 Content 列表被注入到原始的 UserMessage 中。
-
最终,包含原始查询和相关内容的 UserMessage 被发送给大语言模型(LLM)。
/*** 1.索引阶段:将知识库保存到 Milvus*/@PostConstructpublic void init() {createDocumentStore("D:\\data\sanguo.md",qwenEmbeddingModel,sanguoEmbeddingStore);createDocumentStore("D:\\data\\pig.md",qwenEmbeddingModel,pigEmbeddingStore);}private void createDocumentStore(String path, EmbeddingModel qwenEmbeddingModel, EmbeddingStore<TextSegment> pigEmbeddingStore) {//1.文本加载器(文本解析器)Document document = FileSystemDocumentLoader.loadDocument(path);//2.文本转换(数据清除)DocumentTransformer transformer = doc ->{return Document.from(doc.text().replace("#",""),doc.metadata());};document = transformer.transform(document);//3.文本分词器DocumentSplitter documentSplitter = DocumentSplitters.recursive(300,30,new HuggingFaceTokenizer());List<TextSegment> segmentList = documentSplitter.split(document);//4.文本向量化List<Embedding> embeddingList = qwenEmbeddingModel.embedAll(segmentList).content();//5.数据存储到向量数据库sanguoEmbeddingStore.addAll(embeddingList,segmentList);}@RequestMapping("/chat")public String query(String question) {//1.创建上下文对象:两个 三国检索器ContentRetriever sanguoContentRetriever = EmbeddingStoreContentRetriever.builder().embeddingModel(qwenEmbeddingModel).embeddingStore(sanguoEmbeddingStore).maxResults(1).minScore(0.7).build();// 老母猪检索器ContentRetriever pigContentRetriever = EmbeddingStoreContentRetriever.builder().embeddingModel(qwenEmbeddingModel).embeddingStore(pigEmbeddingStore).maxResults(1).minScore(0.7).build();// 构建查询路由Map<ContentRetriever,String> map = new HashMap<>();map.put(sanguoContentRetriever,"三国");map.put(pigContentRetriever,"老母猪");QueryRouter queryRouter = new LanguageModelQueryRouter(qwenChatModel,map);// 检索增强器RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder().queryRouter(queryRouter).build();// 实例化 AiServiceRagService ragService = AiServices.builder(RagService.class).chatLanguageModel(qwenChatModel).retrievalAugmentor(retrievalAugmentor).build();return ragService.query(question);}interface RagService{String query(String question);}