Langchain4j的一些使用Demo

前言

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

AiService+RAG+联网搜索能力

代码实现

官方Demo参考文档

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);
}

核心实现

package com.whg.aijmldemo.examples.search;
 
import com.whg.aijmldemo.examples.utils.LangChainUtils;
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
 * https://docs.langchain4j.dev/tutorials/ai-services
 *
 * 各种函数方法的官方文档说明
 * https://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/xxx.txt";
 
    /**ollama地址*/
    private static final String SEARXNG_BASEURL = "http://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,官方文档:https://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();
        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);
            }
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容