searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Langchain4j实现本地RAG和联网查询

2025-02-27 06:16:40
15
0

前言

使用Langchain4j(vectorDB+AiService+SearXNG)实现自建RAG+联网查询这里提供一些初步的使用例子,具体可以查看官方文档(docs.langchain4j.dev/tutorials/ai-services#rag),官方文档很细且有很多例子。

代码实现

官方Demo参考文档(github.com/langchain4j/langchain4j-examples/blob/main/rag-examples/src/main/java/_3_advanced/_08_Advanced_RAG_Web_Search_Example.java)。

maven库

# manven库
<properties>
    <langchain4j.version>1.0.0-beta1</langchain4j.version>
    <chromadb-java-client.version>0.1.7</chromadb-java-client.version>
    <searxng.version>0.36.0</searxng.version>
</properties>
  
  
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-community-web-search-engine-searxng</artifactId>
    <version>${searxng.version}</version>
</dependency>
  
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-document-parser-apache-pdfbox</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-chroma</artifactId>
    <version>${langchain4j.version}</version>
</dependency>
<dependency>
    <groupId>io.github.amikos-tech</groupId>
    <artifactId>chromadb-java-client</artifactId>
    <version>${chromadb-java-client.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</artifactId>
    <version>${langchain4j.version}</version>
</dependency>
  
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-ollama</artifactId>
    <version>${langchain4j.version}</version>
</dependency>

AIService

public interface Assistant {
      
    //--使用@SystemMessage首先提示词
    @SystemMessage("使用知识库查询{{query}}是否有结果,如果没有相关内容,则使用searxng联网搜索")
    String answer(String query);
}

核心实现

import dev.langchain4j.community.web.search.searxng.SearXNGWebSearchEngine;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentParser;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.model.ollama.OllamaEmbeddingModel;
import dev.langchain4j.rag.DefaultRetrievalAugmentor;
import dev.langchain4j.rag.RetrievalAugmentor;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.rag.content.retriever.WebSearchContentRetriever;
import dev.langchain4j.rag.query.router.DefaultQueryRouter;
import dev.langchain4j.rag.query.router.QueryRouter;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import dev.langchain4j.web.search.WebSearchEngine;
 
import java.io.InputStream;
import java.util.List;
 
import static com.whg.aijmldemo.examples.common.SysConstants.OLLAMA_BASEURL;
import static com.whg.aijmldemo.examples.common.SysConstants.OLLAMA_CHAT_MODEL_NAME;
 
/**
 * 联网查询demo
 * docs.langchain4j.dev/tutorials/ai-services
 *
 * 各种函数方法的官方文档说明
 * docs.langchain4j.dev/tutorials/rag#retrieval-augmentor
 */
public class SearxngSearchDemo {
 
    /**文本向量化模型*/
    private static final String MODEL_NAME = "shaw/dmeta-embedding-zh:latest";
 
    /**本地知识库,这里使用一个本地文件系统,也可以使用S3,只要能读取到文件流就行*/
    private static final String RAG_FILE_PATH = "assert/X3VS2VSCOS.txt";
 
    /**ollama地址*/
    private static final String SEARXNG_BASEURL = "ip:8081";
 
    public static void main(String[] args) {
        //--创建一个chat接口
        Assistant assistant = createAssistant();
        //--开始chat
        LangChainUtils.startConversationWith(assistant);
    }
 
    public static Assistant createAssistant() {
        //--初始化文本向量化模型
        EmbeddingModel embeddingModel =
                OllamaEmbeddingModel.builder().baseUrl(OLLAMA_BASEURL).modelName(MODEL_NAME).build();
 
        //--向量数据库,也称为向量存储或向量搜索引擎,是一种专门设计用于存储和管理向量(固定长度的数字列表)及其他数据项的数据库
        //--这些向量是数据点在高维空间中的数学表示,其中每个维度对应数据的一个特征,向量数据库的主要目的是通过近似最近邻(ANN)算法实现高效的相似性搜索
        //--读取需要作为知识库的文本,将向量化后的文本数据存储到向量数据库中
        EmbeddingStore<TextSegment> embeddingStore =
                embed(embeddingModel);
 
        //--文本库检索query,返回最大3个结果,最少分数要0.6
        ContentRetriever embeddingStoreContentRetriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                .maxResults(3)
                .minScore(0.6)
                .build();
 
        //--联网搜索引擎,这里使用自建的searxng,官方文档:github.com/searxng/searxng
        WebSearchEngine webSearchEngine =
                SearXNGWebSearchEngine.builder().logRequests(true).logResponses(true).baseUrl(SEARXNG_BASEURL).build();
        //--联网检索query,最大返回3个结果
        ContentRetriever webSearchContentRetriever = WebSearchContentRetriever.builder()
                .webSearchEngine(webSearchEngine)
                .maxResults(3)
                .build();
 
        //--路由查询QueryRouter,将对应的查询路由到对应的ContentRetriever
        QueryRouter queryRouter = new DefaultQueryRouter(embeddingStoreContentRetriever, webSearchContentRetriever);
 
        //--检索query增强器,通过从不同的数据源查询检索,给chat返回增强后的对话信息
        RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
                .queryRouter(queryRouter)
                .build();
 
        //--语音对话模型,这里使用ollama本地的,也可以使用官方的,第三方的,或者公司息壤的接口
        OllamaChatModel ollamaLanguageModel =
                OllamaChatModel.builder().baseUrl(OLLAMA_BASEURL).modelName(OLLAMA_CHAT_MODEL_NAME).build();
 
        //--.chatMemoryProvider() 用于区分不同的用户账号
        return AiServices.builder(Assistant.class)
                .chatLanguageModel(ollamaLanguageModel)
                .retrievalAugmentor(retrievalAugmentor)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
                .build();
    }
 
    private static EmbeddingStore<TextSegment> embed(EmbeddingModel embeddingModel) {
        //--将已拆分的知识片段文本存储向量库以便后续可以进行检索,而向量库存储的数据是向量不是文本.
        //--将一个字符串转换为一个N维数组,这个过程在自然语言处理(NLP)领域称为文本嵌入(Words Embedding),像Dify和RagFlow中使用的是elasticsearch,使用的是KNN算法
        //--地区需要作为知识库的文件流,转换成Document对象
        DocumentParser documentParser = new TextDocumentParser();
        InputStream inputStream = SearxngSearchDemo.class.getClassLoader().getResourceAsStream(RAG_FILE_PATH);
        Document document = documentParser.parse(inputStream);
 
        //--切割文档
        //--分段大小(一个分段中最大包含多少个token)、重叠度(段与段之前重叠的token数)重叠度的设计是为了减少按大小拆分后切断原来文本的语义、分词器(将一段文本进行分词,得到token)
        //--Token是经过分词后的文本单位,即将一个文本分词后得到的词、子词等的个数,具体取决于分词器(Tokenizer),这里使用默认的。这个向量化的效果分段大小,重叠度,分词器有关,那情况调优能提供很好的匹配度
        DocumentSplitter splitter = DocumentSplitters.recursive(500, 0);
 
        //--文档拆分的目的:与LLM交互的时候输入的文本对应的token长度是有限制的,输入过长的内容,LLM会无响应或直接该报错
        List<TextSegment> segments = splitter.split(document);
        List<Embedding> embeddings = embeddingModel.embedAll(segments).content();

        //--这里使用简单的内存向量库存储,可以换成自己的elasticsearch,chroma,clickhouse等向量库
        EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
        embeddingStore.addAll(embeddings, segments);
        return embeddingStore;
    }
 
}

模拟调用

public class LangChainUtils {
 
    public static void startConversationWith(Assistant assistant) {
        Logger log = LoggerFactory.getLogger(Assistant.class);
        try (Scanner scanner = new Scanner(System.in)) {
            while (true) {
                log.info("==================================================");
                log.info("User: ");
                String userQuery = scanner.nextLine();
                if (StringUtils.isBlank(userQuery)) {
                    log.info("请输入对话内容: ");
                    continue;
                }
                log.info("==================================================");
                if ("exit".equalsIgnoreCase(userQuery)) {
                    break;
                }
                String agentAnswer = assistant.answer(userQuery);
                log.info("==================================================");
                log.info("Assistant: " + agentAnswer);
            }
        }
    }
}

结果

本地知识库库查询结果

image.png

联网查询结果

image.png

企业微信截图_17405525384369.png

0条评论
0 / 1000
wanghg11
16文章数
2粉丝数
wanghg11
16 文章 | 2 粉丝
原创

Langchain4j实现本地RAG和联网查询

2025-02-27 06:16:40
15
0

前言

使用Langchain4j(vectorDB+AiService+SearXNG)实现自建RAG+联网查询这里提供一些初步的使用例子,具体可以查看官方文档(docs.langchain4j.dev/tutorials/ai-services#rag),官方文档很细且有很多例子。

代码实现

官方Demo参考文档(github.com/langchain4j/langchain4j-examples/blob/main/rag-examples/src/main/java/_3_advanced/_08_Advanced_RAG_Web_Search_Example.java)。

maven库

# manven库
<properties>
    <langchain4j.version>1.0.0-beta1</langchain4j.version>
    <chromadb-java-client.version>0.1.7</chromadb-java-client.version>
    <searxng.version>0.36.0</searxng.version>
</properties>
  
  
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-community-web-search-engine-searxng</artifactId>
    <version>${searxng.version}</version>
</dependency>
  
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-document-parser-apache-pdfbox</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-chroma</artifactId>
    <version>${langchain4j.version}</version>
</dependency>
<dependency>
    <groupId>io.github.amikos-tech</groupId>
    <artifactId>chromadb-java-client</artifactId>
    <version>${chromadb-java-client.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</artifactId>
    <version>${langchain4j.version}</version>
</dependency>
  
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-ollama</artifactId>
    <version>${langchain4j.version}</version>
</dependency>

AIService

public interface Assistant {
      
    //--使用@SystemMessage首先提示词
    @SystemMessage("使用知识库查询{{query}}是否有结果,如果没有相关内容,则使用searxng联网搜索")
    String answer(String query);
}

核心实现

import dev.langchain4j.community.web.search.searxng.SearXNGWebSearchEngine;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentParser;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.parser.TextDocumentParser;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.model.ollama.OllamaEmbeddingModel;
import dev.langchain4j.rag.DefaultRetrievalAugmentor;
import dev.langchain4j.rag.RetrievalAugmentor;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.rag.content.retriever.WebSearchContentRetriever;
import dev.langchain4j.rag.query.router.DefaultQueryRouter;
import dev.langchain4j.rag.query.router.QueryRouter;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import dev.langchain4j.web.search.WebSearchEngine;
 
import java.io.InputStream;
import java.util.List;
 
import static com.whg.aijmldemo.examples.common.SysConstants.OLLAMA_BASEURL;
import static com.whg.aijmldemo.examples.common.SysConstants.OLLAMA_CHAT_MODEL_NAME;
 
/**
 * 联网查询demo
 * docs.langchain4j.dev/tutorials/ai-services
 *
 * 各种函数方法的官方文档说明
 * docs.langchain4j.dev/tutorials/rag#retrieval-augmentor
 */
public class SearxngSearchDemo {
 
    /**文本向量化模型*/
    private static final String MODEL_NAME = "shaw/dmeta-embedding-zh:latest";
 
    /**本地知识库,这里使用一个本地文件系统,也可以使用S3,只要能读取到文件流就行*/
    private static final String RAG_FILE_PATH = "assert/X3VS2VSCOS.txt";
 
    /**ollama地址*/
    private static final String SEARXNG_BASEURL = "ip:8081";
 
    public static void main(String[] args) {
        //--创建一个chat接口
        Assistant assistant = createAssistant();
        //--开始chat
        LangChainUtils.startConversationWith(assistant);
    }
 
    public static Assistant createAssistant() {
        //--初始化文本向量化模型
        EmbeddingModel embeddingModel =
                OllamaEmbeddingModel.builder().baseUrl(OLLAMA_BASEURL).modelName(MODEL_NAME).build();
 
        //--向量数据库,也称为向量存储或向量搜索引擎,是一种专门设计用于存储和管理向量(固定长度的数字列表)及其他数据项的数据库
        //--这些向量是数据点在高维空间中的数学表示,其中每个维度对应数据的一个特征,向量数据库的主要目的是通过近似最近邻(ANN)算法实现高效的相似性搜索
        //--读取需要作为知识库的文本,将向量化后的文本数据存储到向量数据库中
        EmbeddingStore<TextSegment> embeddingStore =
                embed(embeddingModel);
 
        //--文本库检索query,返回最大3个结果,最少分数要0.6
        ContentRetriever embeddingStoreContentRetriever = EmbeddingStoreContentRetriever.builder()
                .embeddingStore(embeddingStore)
                .embeddingModel(embeddingModel)
                .maxResults(3)
                .minScore(0.6)
                .build();
 
        //--联网搜索引擎,这里使用自建的searxng,官方文档:github.com/searxng/searxng
        WebSearchEngine webSearchEngine =
                SearXNGWebSearchEngine.builder().logRequests(true).logResponses(true).baseUrl(SEARXNG_BASEURL).build();
        //--联网检索query,最大返回3个结果
        ContentRetriever webSearchContentRetriever = WebSearchContentRetriever.builder()
                .webSearchEngine(webSearchEngine)
                .maxResults(3)
                .build();
 
        //--路由查询QueryRouter,将对应的查询路由到对应的ContentRetriever
        QueryRouter queryRouter = new DefaultQueryRouter(embeddingStoreContentRetriever, webSearchContentRetriever);
 
        //--检索query增强器,通过从不同的数据源查询检索,给chat返回增强后的对话信息
        RetrievalAugmentor retrievalAugmentor = DefaultRetrievalAugmentor.builder()
                .queryRouter(queryRouter)
                .build();
 
        //--语音对话模型,这里使用ollama本地的,也可以使用官方的,第三方的,或者公司息壤的接口
        OllamaChatModel ollamaLanguageModel =
                OllamaChatModel.builder().baseUrl(OLLAMA_BASEURL).modelName(OLLAMA_CHAT_MODEL_NAME).build();
 
        //--.chatMemoryProvider() 用于区分不同的用户账号
        return AiServices.builder(Assistant.class)
                .chatLanguageModel(ollamaLanguageModel)
                .retrievalAugmentor(retrievalAugmentor)
                .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
                .build();
    }
 
    private static EmbeddingStore<TextSegment> embed(EmbeddingModel embeddingModel) {
        //--将已拆分的知识片段文本存储向量库以便后续可以进行检索,而向量库存储的数据是向量不是文本.
        //--将一个字符串转换为一个N维数组,这个过程在自然语言处理(NLP)领域称为文本嵌入(Words Embedding),像Dify和RagFlow中使用的是elasticsearch,使用的是KNN算法
        //--地区需要作为知识库的文件流,转换成Document对象
        DocumentParser documentParser = new TextDocumentParser();
        InputStream inputStream = SearxngSearchDemo.class.getClassLoader().getResourceAsStream(RAG_FILE_PATH);
        Document document = documentParser.parse(inputStream);
 
        //--切割文档
        //--分段大小(一个分段中最大包含多少个token)、重叠度(段与段之前重叠的token数)重叠度的设计是为了减少按大小拆分后切断原来文本的语义、分词器(将一段文本进行分词,得到token)
        //--Token是经过分词后的文本单位,即将一个文本分词后得到的词、子词等的个数,具体取决于分词器(Tokenizer),这里使用默认的。这个向量化的效果分段大小,重叠度,分词器有关,那情况调优能提供很好的匹配度
        DocumentSplitter splitter = DocumentSplitters.recursive(500, 0);
 
        //--文档拆分的目的:与LLM交互的时候输入的文本对应的token长度是有限制的,输入过长的内容,LLM会无响应或直接该报错
        List<TextSegment> segments = splitter.split(document);
        List<Embedding> embeddings = embeddingModel.embedAll(segments).content();

        //--这里使用简单的内存向量库存储,可以换成自己的elasticsearch,chroma,clickhouse等向量库
        EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
        embeddingStore.addAll(embeddings, segments);
        return embeddingStore;
    }
 
}

模拟调用

public class LangChainUtils {
 
    public static void startConversationWith(Assistant assistant) {
        Logger log = LoggerFactory.getLogger(Assistant.class);
        try (Scanner scanner = new Scanner(System.in)) {
            while (true) {
                log.info("==================================================");
                log.info("User: ");
                String userQuery = scanner.nextLine();
                if (StringUtils.isBlank(userQuery)) {
                    log.info("请输入对话内容: ");
                    continue;
                }
                log.info("==================================================");
                if ("exit".equalsIgnoreCase(userQuery)) {
                    break;
                }
                String agentAnswer = assistant.answer(userQuery);
                log.info("==================================================");
                log.info("Assistant: " + agentAnswer);
            }
        }
    }
}

结果

本地知识库库查询结果

image.png

联网查询结果

image.png

企业微信截图_17405525384369.png

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0