LangChain4j(11)——Easy RAG

大模型的知识来自受训练的数据,具有一定的局限性。 如果想让大模型了解特定领域的知识或专有数据,可以使用RAG或者数据微调的方式。

RAG介绍

RAG (Retrieval-Augmented Generation),内容检索增强生成。通过RAG我们可以构建专有领域的知识库,在向大模型发送问题之前,可以先通过RAG检索相应的内容,然后将我们的问题和检索到的内容一起发给大模型,大模型根据我们给的内容进行回答,可以使回答的内容更准确。

RAG 过程分为 2 个不同的阶段:索引和检索。 

索引阶段

在索引阶段,对文档进行预处理。简单来说,会对文档进行分块,并对分块的数据进行嵌入存储(数据存入向量数据库中)。

其流程大致如下:

该流程图来自LangChain4j的官网 

对文档进行分块,借助嵌入模型(Embeding Model)对分块数据进行向量化处理,然后将分块和向量化后的数据存入向量数据库中。

检索阶段

向大模型提问时,通过嵌入模型对问题进行向量化处理,并通过相似度分析,获取与问题相似的分段信息,然后将分段和问题作为提示词,一起发送给大模型进行处理。

其流程大致如下:

该流程图来自LangChain4j的官网 

Easy RAG

LangChain4j中提供了3中风格的实现:

  • Easy RAG
  • Naive RAG
  • Advanced RAG

Easy RAG是LangChain4j中提供的针对RAG的一种简单实现方式,对外隐藏了很多实现细节。

导入jar

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-easy-rag</artifactId>
    <version>1.0.0-beta2</version>
</dependency>

准备一个文档

注意:文档放在resources目录下

测试代码

package com.renr.langchain4jnew.app4;

import com.renr.langchain4jnew.constant.CommonConstants;
import dev.langchain4j.community.model.zhipu.ZhipuAiChatModel;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import lombok.extern.slf4j.Slf4j;

import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.List;
import java.util.Scanner;

@Slf4j
public class EasyRAG {

    public static void main(String[] args) throws URISyntaxException {

        ChatLanguageModel model = ZhipuAiChatModel.builder()
                // 模型key
                .apiKey(CommonConstants.API_KEY)
                // 精确度
                .temperature(0.9)
                .model("GLM-4-Flash")
                .maxRetries(3)
                .callTimeout(Duration.ofSeconds(60))
                .connectTimeout(Duration.ofSeconds(60))
                .writeTimeout(Duration.ofSeconds(60))
                .readTimeout(Duration.ofSeconds(60))
                .logRequests(true)
                .logResponses(true)
                .build();

        // 获取待加载的文档路径
        URL fileUrl = EasyRAG.class.getClassLoader().getResource("document/a.txt");
        Path path = Paths.get(fileUrl.toURI());
        // 加载文档数据
        Document documents = FileSystemDocumentLoader.loadDocument(path);
        // 向量数据存储在内存中
        InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();

        // 将文档存储到指定的“向量存储对象”中
        EmbeddingStoreIngestor.ingest(documents, embeddingStore);

        // 返回内容检索对象
        ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.from(embeddingStore);

        Assistant assistant = AiServices.builder(Assistant.class)
                .chatLanguageModel(model)
                // 指定内容检索对象
                .contentRetriever(contentRetriever)
                .build();

        String answer = assistant.chat("河南省直第三人民医院的地址");
        System.out.println(answer);
    }


}

不启用RAG的执行情况

我们先注释掉contentRetriever(contentRetriever):

Assistant assistant = AiServices.builder(Assistant.class)
                .chatLanguageModel(CHAT_MODEL)
                // .contentRetriever(contentRetriever)
                .build();

发送请求数据如下:

18:59:15.336 [main] DEBUG dev.langchain4j.community.model.zhipu.RequestLoggingInterceptor - Request:
- method: POST
- url: https://open.bigmodel.cn/api/paas/v4/chat/completions
- headers: [Authorization: Bearer ey...fQ.ew0KICAiZXhwIiA6IDE3NDQ4ODkzNTUyNjksDQogICJhcGlfa2V5IiA6ICJkMjMzNGIyZTk4NTE0YWUwOTIzMDQ4YTM4NDEzNTMwNiIsDQogICJ0aW1lc3RhbXAiIDogMTc0NDg4NzU1NTI2OQ0KfQ.PwhX7_zca8YRl0XSfiu_ASRBC-ifMhz6udigGY4pOAc]
- body: {
  "model" : "GLM-4-Flash",
  "messages" : [ {
    "role" : "user",
    "content" : "河南省直第三人民医院的地址"
  } ],
  "stream" : false,
  "temperature" : 0.9,
  "max_tokens" : 512,
  "tool_choice" : "auto"
}

执行结果如下:

启用RAG的执行情况

取消上述代码注释,启用检索增强,发送的请求如下:

18:36:34.497 [main] DEBUG dev.langchain4j.community.model.zhipu.RequestLoggingInterceptor - Request:
- method: POST
- url: https://open.bigmodel.cn/api/paas/v4/chat/completions
- headers: [Authorization: Bearer ey...fQ.ew0KICAiZXhwIiA6IDE3NDQ4ODc5OTQ0MjksDQogICJhcGlfa2V5IiA6ICJkMjMzNGIyZTk4NTE0YWUwOTIzMDQ4YTM4NDEzNTMwNiIsDQogICJ0aW1lc3RhbXAiIDogMTc0NDg4NjE5NDQyOQ0KfQ.XX5DZAh3qINeAO-PLBSe0aGPnveBzJOSwUaYDS_FGTk]
- body: {
  "model" : "GLM-4-Flash",
  "messages" : [ {
    "role" : "user",
    "content" : "河南省直第三人民医院的地址\n\nAnswer using the following information:\n河南省直第三人民医院简介\r\n(修订日期 2025年3月)\n\n河南省直第三人民医院简介\n(修订日期 2025年3月)\n河南省直第三人民医院是河南省卫健委直属的一家“医教研转工作统筹推进、防治康养手段综合应用、吃动睡想行为全面科学”的省三级公立综合医院。是河南省干部保健定点医院。\n医院位于河南省省会郑州,有三个院区,郑东院区(郑东新区人民医院)比邻河南省政府。西院区位于中原区伏牛路陇海路交叉口;857院区位于中原区陇海路328号。医院有2个急救站,5个急救联盟单位、4个社区卫生服务中心;下设司法鉴定中心。\n\n成立了“河南省慈善联合总会.道健基金”,开展公益行动,救助贫困患者及资助诊断不明、疗效不佳、复杂病例的手术患者享受国内知名医院专家手术。"
  } ],
  "stream" : false,
  "temperature" : 0.9,
  "max_tokens" : 512,
  "tool_choice" : "auto"
}

执行结果如下:

 从两次执行结果看,启用RAG后,提示词中增加了从文档中检索的内容,并且和原来的问题一起发送给了大模型,大模型根据提示词内容,返回了文档中给出的医院的准确地址数据。

而没有启用RAG时,大模型给出的结果明显和文档中的结果不符。

注意:Easy RAG只是为了体验下RAG的作用,由于内部隐藏了很多实现细节,灵活性差,实际使用效果有限。比如,如果我们询问大模型:河南省直第三人民医院有多少职工,RAG并不能检索出有效的数据,大模型也无法返回准确的信息。

### 使用 LangChain4J 实现 RAG 在构建基于检索增强生成(RAG)的服务时,LangChain 提供了强大的框架来集成不同的组件。对于希望使用 Java 生态系统的开发者来说,`langchain4j` 是一个合适的选择。 为了实现带有 Solr 作为检索器的 RAG 应用程序,在 `langchain4j` 中定义服务器凭证以及配置嵌入模型的方式如下: #### 定义 Server Credentials 和 Embedding Model 配置 通过创建自定义的 `SolrVectorStoreConfig` 类可以管理 Solr 连接参数,包括认证信息。这允许应用程序安全地访问远程部署的 Solr 实例[^1]。 ```java public class CustomSolrConfig extends SolrVectorStoreConfig { private String username; private String password; public CustomSolrConfig(String solrUrl, String collectionName, String username, String password) { super(solrUrl, collectionName); this.username = username; this.password = password; } @Override protected void configure(HttpClientBuilder builder) { CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials( AuthScope.ANY, new UsernamePasswordCredentials(username, password)); builder.setDefaultCredentialsProvider(credsProvider); } } ``` 接着,当初始化向量存储库实例时传入上述配置对象即可完成设置。 关于已经运行于服务器上的嵌入模型,可以通过 REST API 或者 gRPC 接口与其交互。假设该服务提供了一个 HTTP 端点用于获取文档表示,则可以在应用逻辑中调用此接口并处理返回的数据以适应下游任务的需求[^2]。 ```java // 假设有一个方法可以从给定URL获得Embeddings List<Double> getDocumentEmbeddingFromServer(String documentText); // 将其应用于实际场景中的代码片段可能看起来像这样: CustomSolrConfig config = new CustomSolrConfig("http://solr.example.com", "myCollection", "admin", "secret"); SolrVectorStore store = new SolrVectorStore(config); String queryText = "example question"; List<Double> embeddings = getDocumentEmbeddingFromServer(queryText); // 获取查询文本对应的embedding store.add(embeddings, metadata); // 存储到索引中以便后续检索 ``` 以上展示了如何利用现有的嵌入模型服务于新的数据条目,并将其加入到由 Solr 支持的知识库内。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值