首页 > 编程语言 >Java开发者LLM实战——使用LangChain4j构建本地RAG系统

Java开发者LLM实战——使用LangChain4j构建本地RAG系统

时间:2024-07-02 10:21:37浏览次数:21  
标签:RAG LangChain4j Java langchain4j 冰淇淋 tech main data 向量

1、引言

由于目前比较火的chatGPT是预训练模型,而训练一个大模型是需要较长时间(参数越多学习时间越长,保守估计一般是几个月,不差钱的可以多用点GPU缩短这个时间),这就导致了它所学习的知识不会是最新的,最新的chatGPT-4o只能基于2023年6月之前的数据进行回答,距离目前已经快一年的时间,如果想让GPT基于近一年的时间回复问题,就需要RAG(检索增强生成)技术了。

 


 

此外,对于公司内部的私有数据,为了数据安全、商业利益考虑,不能放到互联网上的数据,因此GPT也没有这部分的知识,如果需要GPT基于这部分私有的知识进行回答,也需要使用RAG技术。

 


 

本文将通过实战代码示例,意在帮助没有大模型实战经验的Java工程师掌握使用LangChain4j框架进行大模型开发。

2、基本概念

2.1 什么是RAG

RAG(Retrieval-Augmented Generation)的核心思想是:将传统的信息检索(IR)技术与现代的生成式大模型(如chatGPT)结合起来。

具体来说,RAG模型在生成答案之前,会首先从一个大型的文档库或知识库中检索到若干条相关的文档片段。再将这些检索到的片段作为额外的上下文信息,输入到生成模型中,从而生成更为准确和信息丰富的文本。

RAG的工作原理可以分为以下几个步骤:

1.接收请求:首先,系统接收到用户的请求(例如提出一个问题)。 2.信息检索(R):系统从一个大型文档库中检索出与查询最相关的文档片段。这一步的目标是找到那些可能包含答案或相关信息的文档。 3.生成增强(A):将检索到的文档片段与原始查询一起输入到大模型(如chatGPT)中,注意使用合适的提示词,比如原始的问题是XXX,检索到的信息是YYY,给大模型的输入应该类似于:请基于YYY回答XXXX。 4.输出生成(G):大模型基于输入的查询和检索到的文档片段生成最终的文本答案,并返回给用户。

第2步骤中的信息检索,不一定必须使用向量数据库,可以是关系型数据库(如MySQL)或全文搜索引擎(如Elasticsearch, ES),

但大模型应用场景广泛使用向量数据库的原因是:在大模型RAG的应用场景中,主要是要查询相似度高的某几个文档,而不是精确的查找某一条(MySQL、ES擅长)。

相似度高的两个文档,可能不包含相同的关键词。 例如,句子1: "他很高兴。" 句子2: "他感到非常快乐。" 虽然都是描述【他】很开心快乐的心情,但是不包含相同的关键词;

包含相同的关键词的两个文档可能完全没有关联,例如:句子1: "他喜欢苹果。" 句子2: "苹果是一家大公司。" 虽然都包含相同的关键词【苹果】,但两句话的相似度很低。

2.2 LangChain4j简介

LangChain4j是LangChiain的java版本,

LangChain的Lang取自Large Language Model,代表大语言模型,

Chain是链式执行,即把语言模型应用中的各功能模块化,串联起来,形成一个完整的工作流。

它是面向大语言模型的开发框架,意在封装与LLM对接的细节,简化开发流程,提升基于LLM开发的效率。

更多介绍,详见: https://github.com/langchain4j/langchain4j/blob/main/README.md

2.3 大模型开发 vs. 传统JAVA开发

大模型开发——大模型实现业务逻辑:

开发前,开发人员关注数据准备(进行训练)、选择和微调模型(得到更好的效果,更能匹配业务预期),

开发过程中(大多数时候),重点在于如何有效的与大模型(LLM)进行沟通,利用LLM的专业知识解决特定的业务问题,

开发中更关注如何描述问题(提示工程 Propmt Engineering)进行有效的推理,关注如何将大模型的使用集成到现有的业务系统中。

传统的JAVA开发——开发者实现业务逻辑:

开发前,开发人员关注系统架构的选择(高并发、高可用),功能的拆解、模块化等设计。

开发过程中(大多数时候)是根据特定的业务问题,设计特定的算法、数据存储等以实现业务逻辑,以编码为主。

3. 实战经验

3.1 环境搭建

3.1.1 向量库(Chroma)

Windows:

先安装python,参考: https://docs.python.org/zh-cn/3/using/windows.html#the-full-installer

PS:注意需要配置环境变量

验证-执行:

python --version

 


 

再安装chroma,参考:https://docs.trychroma.com/getting-started

验证-执行:

chroma run

 


 

Mac:

现先安装python

brew install python

或者下载安装: https://www.python.org/downloads/macos/

验证-执行:

python --version

 


 

安装chroma(同上),参考:https://docs.trychroma.com/getting-started

验证-执行:

chroma run

 


 

 

3.1.2 集成LangChain4j

<properties>
        <langchain4j.version>0.31.0</langchain4j.version>
</properties>
<dependency>
	<groupId>dev.langchain4j</groupId>
	<artifactId>langchain4j-core</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-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-chroma</artifactId>
	 <version>${langchain4j.version}</version> 
</dependency>
<dependency>
	<groupId>io.github.amikos-tech</groupId>
	<artifactId>chromadb-java-client</artifactId>
	 <version>${langchain4j.version}</version> 
</dependency>

3.2 程序编写

3.2.1 项目结构

LangChain ├── core │ ├── src │ │ ├── main │ │ │ ├── java │ │ │ │ └── cn.jdl.tech_and_data.ka │ │ │ │ ├── ChatWithMemory │ │ │ │ ├── Constants │ │ │ │ ├── Main │ │ │ │ ├── RagChat │ │ │ │ └── Utils │ │ │ ├── resources │ │ │ │ ├── log4j2.xml │ │ │ │ └── 笑话.txt │ │ ├── test │ │ │ └── java │ ├── target ├── pom.xml ├── parent [learn.langchain.parent] ├── pom.xml

 

3.2.2 知识采集

一般是公司内网的知识库中或互联网上进行数据采集,获取到的文本文件、WORD文档或PDF文件,本文使用resources目录下的【笑话.txt】作为知识采集的结果文件

URL docUrl = Main.class.getClassLoader().getResource("笑话.txt");
if(docUrl==null){
    log.error("未获取到文件");
}
Document document = getDocument(docUrl);
if(document==null){
    log.error("加载文件失败");
}
private static Document getDocument(URL resource) {
    Document document = null;
    try{
        Path path = Paths.get(resource.toURI());
        document = FileSystemDocumentLoader.loadDocument(path);
    }catch (URISyntaxException e){
        log.error("加载文件发生异常", e);
    }
    return document;
}

3.2.3 文档切分

使用dev.langchain4j.data.document.splitter.DocumentSplitters#recursize

它有三个参数:分段大小(一个分段中最大包含多少个token)、重叠度(段与段之前重叠的token数)、分词器(将一段文本进行分词,得到token)

其中,重叠度的设计是为了减少按大小拆分后切断原来文本的语义,使其尽量完整。

 


 

DocumentSplitter splitter = DocumentSplitters.recursive(150,10,new OpenAiTokenizer());
splitter.split(document);

关于Token(标记)

Token是经过分词后的文本单位,即将一个文本分词后得到的词、子词等的个数,具体取决于分词器(Tokenizer),

比如:我喜欢吃苹果,可以拆分成我/喜欢/吃/苹果,token数量=4, 也可以拆分成我/喜/欢/吃/苹果,token数量=5

chatGPT使用的是BPE(Byte Pair Encoding)算法进行分词,参见: https://en.wikipedia.org/wiki/Byte_pair_encoding

对于上面文本的分词结果如下:

18:17:29.371 [main] INFO  TokenizerTest - 待分词的文本:我喜欢吃苹果
18:17:30.055 [main] INFO  cn.jdl.tech_and_data.ka.Utils - 当前的模型是:gpt-4o
18:17:31.933 [main] INFO  TokenizerTest - 分词结果:我 / 喜欢 / 吃 / 苹果

关于token与字符的关系:GPT-4o的回复:

 

关于文档拆分的目的

由于与LLM交互的时候输入的文本对应的token长度是有限制的,输入过长的内容,LLM会无响应或直接该报错,

因此不能将所有相关的知识都作为输入给到LLM,需要将知识文档进行拆分,存储到向量库,

每次调用LLM时,先找出与提出的问题关联度最高的文档片段,作为参考的上下文输入给LLM。

入参过长,LLM报错:

 

虽然根据响应,允许输入1048576个字符=1024K个字符=1M个字符,

但官网文档给的32K tokens,而一般1个中文字符对应1-2个Token,因此字符串建议不大于64K,实际使用中,为了保障性能,也是要控制输入不要过长。

如下是常见LLM给定的token输入上限:

 Token 输入上限(最大长度)
GPT-3 (davinci) 4096 tokens
GPT-3.5 (text-davinci-003) 4096 tokens
GPT-4 (8k context) 8192 tokens
GPT-4 (32k context) 32768 tokens
LLaMA (7B) 2048 tokens
LLaMA (13B) 2048 tokens
LLaMA (30B) 2048 tokens
LLaMA (65B) 2048 tokens
讯飞星火(SparkDesk) 8192 tokens
文心一言(Ernie 3.0) 4096 tokens
智源悟道(WuDao 2.0) 2048 tokens
阿里巴巴 M6 2048 tokens
华为盘古(Pangu-Alpha) 2048 tokens
言犀大模型(ChatJd) 2048 tokens

文档拆分的方案langchain4j中提供了6种:

 


 

1、基于字符的:逐个字符(含空白字符)分割

2、基于行的:按照换行符(\n)分割

3、基于段落的:按照连续的两个换行符(\n\n)分割

4、基于正则的:按照自定义正则表达式分隔

5、基于句子的(使用Apache OpenNLP,只支持英文,所以可以忽略

标签:RAG,LangChain4j,Java,langchain4j,冰淇淋,tech,main,data,向量
From: https://www.cnblogs.com/Jcloud/p/18279368

相关文章

  • Java 并发 - ThreadLocal详解
    ThreadLocal是通过线程隔离的方式防止任务在共享资源上产生冲突,线程本地存储是一种自动化机制,可以为使用相同变量的每个不同线程都创建不同的存储。@立刀旁目录#带着BAT大厂的面试问题去理解#ThreadLocal简介#ThreadLocal理解#ThreadLocal原理#如何实现线程隔......
  • 秋招Java后端开发冲刺——基础篇5(String&集合)
    一、StringString类是Java中字符串操作类,位于java.lang包下String类型对象的底层使用字符数组char[]存储字符串,由final修饰且没有提供公共的修改方法,因此String对象是不可变的。常见方法方法名作用trim()去掉字符串首尾空字符split(分隔符/正则表达式)分割字符串substring......
  • 基于java+springboot+vue实现的家政服务平台(文末源码+Lw)299
    摘 要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本家政服务平台就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达......
  • 基于java+springboot+vue实现的旅游管理系统(文末源码+Lw)227
    摘 要现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本旅游管理系统就是在这样的大环境下诞生,其可以帮助使用者在短时间内处理完毕庞大的数据信息,使用这种软件工具可以帮助管理人员提高事务处理效率,达......
  • 探索Java编程的深邃宇宙 —— 走进【zhhll.icu】技术博客
    在浩瀚无垠的编程世界里,Java以其强大的跨平台能力、丰富的生态系统和广泛的应用场景,成为了众多开发者心中的“语言之星”。而对于每一位热爱Java、渴望在编程道路上不断前行的你来说,一个能够深入探索、持续学习、并与同行交流的平台显得尤为重要。今天,就让我带你走进这样一......
  • Java计算机毕业设计研究生招生管理系统(开题+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着高等教育规模的不断扩大和研究生教育的深入发展,研究生招生工作面临着前所未有的挑战。传统的手工操作方式已难以满足招生工作的高效性、准确性和......
  • Java计算机毕业设计新能源汽车动力电池回收系统(开题+源码+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着新能源汽车产业的迅猛发展,动力电池作为其核心部件,其需求量急剧增加。然而,动力电池的有限使用寿命使得废旧电池的处理问题日益凸显。大量废旧动力......
  • 基于Java英语学习网站设计和实现(源码+LW+调试文档+讲解等)
    ......
  • 蓝桥杯Java组常用知识点
    基本数据类型int的取值范围:-2^31~2^31-1-2147483648~2147483647(约等于10的9次方)longlong的取值范围:-2^63~(2^63-1)-9223372036854775808~9223372036854775807(约等于10的18次方)输入输出使用文件流对输入输出的重要性:https://blog.csdn.net/weixin_43554580/article......
  • Java高手的30k之路|面试宝典|精通MyBatis(一)
    基础知识架构图MyBatis的基本架构图,展示MyBatis核心组件及其相互关系:Configuration......