Ollama 应用实践:基于 Ollama + LangChain4j 的 RAG 实现

LangChain4j 提供了一个可以让我们快速了解RAG 实现过程的

简易
LangChain4j 具有“Easy RAG”功能,可让您尽可能轻松地开始使用 RAG。您无需了解嵌入、选择向量存储、找到正确的嵌入模型、弄清楚如何解析和拆分文档等。只需指向您的文档,LangChain4j 就会发挥其魔力。

如果您需要可定制的 RAG,请跳至下一部分。

1、导入langchain4j-easy-rag依赖项:

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

2、让我们加载您的文档:

List<Document> documents = FileSystemDocumentLoader.loadDocuments("/home/langchain4j/documentation");

3、现在,我们需要预处理文档并将其存储在专门的嵌入存储(也称为矢量数据库)中。当用户提出问题时,这对于快速找到相关信息是必要的。我们可以使用我们支持的 15 多个嵌入存储中的任何一个,但为了简单起见,我们将使用内存中的嵌入存储:

InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
EmbeddingStoreIngestor.ingest(documents, embeddingStore);

4、最后一步是创建一个AI 服务,它将作为我们的 LLM API:

interface Assistant {

    String chat(String userMessage);
}

Assistant assistant = AiServices.builder(Assistant.class)
    .chatLanguageModel(OpenAiChatModel.withApiKey(OPENAI_API_KEY))
    .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
    .contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore))
    .build();

在这里,我们配置Assistant使用 OpenAI LLM 来回答用户问题,记住对话中最新的 10 条消息,并从EmbeddingStore包含我们文档的中检索相关内容。

5、现在我们就可以与它聊天了!

String answer = assistant.chat("How to do Easy RAG with LangChain4j?");

访问

如果您希望访问源(Content用于扩充消息的检索),您可以通过在Result类中包装返回类型轻松地实现此目的:

interface Assistant {

    Result<String> chat(String userMessage);
}

Result<String> result = assistant.chat("How to do Easy RAG with LangChain4j?");

String answer = result.content();
List<Content> sources = result.sources();

RAG 阶段

LangChain4j 提供了一组丰富的 API,让您可以轻松构建自定义 RAG 管道,从简单到高级。在本节中,我们将介绍主要的领域类和 API。

类Document代表整个文档,例如单个 PDF 文件或网页。目前,它Document只能表示文本信息,但未来的更新将使其能够支持图像和表格。

有用的方法

  • Document.text()返回文本Document
  • Document.metadata()返回Metadata的Document(见下文)
  • Document.toTextSegment()将其转换Document为TextSegment(见下文)
  • Document.from(String, Metadata)创建一个Document来自文本并Metadata
  • Document.from(String)创建Document带有空文本的Metadata

每个都Document包含Metadata。它存储有关 的元信息Document,例如其名称、来源、上次更新日期、所有者或任何其他相关详细信息。

Metadata以键值对的形式存储,其中键为 类型,String值可以是以下类型之一:String,Integer,Long,Float,Double。

Metadata 很有用,原因如下:

  • Document 在向 LLM 提交提示时,还可以添加元数据条目,为 LLM 提供需要考虑的其他信息。例如,提供Document名称和来源可以帮助 LLM 更好地理解内容。
  • 在搜索要包含在提示中的相关内容时,可以按Metadata条目进行过滤。例如,您可以将语义搜索范围缩小到仅Document属于特定所有者的条目。
  • 当的来源Document更新时(例如,文档的特定页面),可以Document通过其元数据条目(例如,“id”,“source”等)轻松找到相应的内容,并在中更新它EmbeddingStore以保持同步。

文档加载器

您可以Document从创建一个String,但更简单的方法是使用库中包含的文档加载器之一:

  • FileSystemDocumentLoader从langchain4j模块
  • UrlDocumentLoader从langchain4j模块
  • AmazonS3DocumentLoader从langchain4j-document-loader-amazon-s3模块
  • AzureBlobStorageDocumentLoader从langchain4j-document-loader-azure-storage-blob模块
  • GitHubDocumentLoader从langchain4j-document-loader-github模块
  • TencentCosDocumentLoader从langchain4j-document-loader-tencent-cos模块

文档解析器

Documents 可以表示各种格式的文件,例如 PDF、DOC、TXT 等。为了解析每一种格式,DocumentParser库中包含一个具有多种实现的接口:

  • TextDocumentParser来自langchain4j模块,可以解析纯文本格式的文件(例如TXT,HTML,MD等)
  • ApachePdfBoxDocumentParser来自langchain4j-document-parser-apache-pdfbox可以解析 PDF 文件的模块
  • ApachePoiDocumentParser来自langchain4j-document-parser-apache-poi模块,它可以解析 MS Office 文件格式(例如 DOC、DOCX、PPT、PPTX、XLS、XLSX 等)
  • ApacheTikaDocumentParser该langchain4j-document-parser-apache-tika模块可以自动检测和解析几乎所有现有的文件格式

Document下面是如何从文件系统加载一个或多个的示例:

// Load a single document
Document document = FileSystemDocumentLoader.loadDocument("/home/langchain4j/file.txt", new TextDocumentParser());

// Load all documents from a directory
List<Document> documents = FileSystemDocumentLoader.loadDocuments("/home/langchain4j", new TextDocumentParser());

// Load all *.txt documents from a directory
PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:*.txt");
List<Document> documents = FileSystemDocumentLoader.loadDocuments("/home/langchain4j", pathMatcher, new TextDocumentParser());

// Load all documents from a directory and its subdirectories
List<Document> documents = FileSystemDocumentLoader.loadDocumentsRecursively("/home/langchain4j", new TextDocumentParser());

您也可以加载文档而不明确指定DocumentParser。在这种情况下,DocumentParser将使用默认值。默认值是通过 SPI 加载的(例如来自langchain4j-document-parser-apache-tika或langchain4j-easy-rag)。如果DocumentParser通过 SPI 未找到 ,TextDocumentParser则将使用 a 作为后备。

文档转换器

DocumentTransformer实现可以执行各种文档转换,例如:

清理:这涉及从Document文本中删除不必要的噪音,这可以节省标记并减少干扰。
Document过滤:从搜索中完全排除特定的内容。
丰富:可以添加附加信息Document以潜在地增强搜索结果。
总结:Document可以进行总结,并且它的简短摘要可以存储在中,Metadata 以便稍后包含在每个中TextSegment(我们将在下面介绍),以潜在地改进搜索。
ETC。
Metadata在此阶段还可以添加、修改或删除条目。

目前,唯一现成提供的实现是HtmlTextExtractor在langchain4j模块中,它可以从原始 HTML 中提取所需的文本内容和元数据条目。

由于没有一刀切的解决方案,我们建议您DocumentTransformer根据自己的独特数据量身定制解决方案。

文本片段

一旦Document加载了 ,就该将它们拆分(分块)成更小的段(块)。 LangChain4j 的域模型包括一个TextSegment表示 的片段的类Document。顾名思义,TextSegment只能表示文本信息。

分裂还是不分裂?

出于多种原因,您可能只想在提示中包含几个相关段而不是整个知识库:

  • LLM 的上下文窗口有限,因此整个知识库可能不适合
  • 你在提示中提供的信息越多,LLM 处理和回复所需的时间就越长
  • 提示中提供的信息越多,支付的费用就越高
  • 提示中的不相关信息可能会分散法学硕士的注意力,并增加出现幻觉的可能性
  • 提示中提供的信息越多,LLM 的回复基于哪些信息就越难解释
  • 我们可以通过将知识库拆分成更小、更易于理解的部分来解决这些问题。这些部分应该有多大?这是一个好问题。一如既往,这取决于具体情况。

目前有两种广泛使用的方法:

1、每个文档(例如 PDF 文件、网页等)都是原子且不可分割的。在 RAG 管道中进行检索时,将检索 N 个最相关的文档并将其注入提示中。在这种情况下,您很可能需要使用长上下文 LLM,因为文档可能很长。如果检索完整文档很重要,例如当您不能错过某些细节时,这种方法是合适的。

  • 优点:没有丢失任何背景信息。
  • 缺点:
    • 消耗了更多代币。
    • 有时,文档可能包含多个部分/主题,但并非所有部分/主题都与查询相关。
    • 由于各种大小的完整文档被压缩成单个固定长度的向量,因此向量搜索质量会受到影响。
      2、文档被分成更小的片段,例如章节、段落,有时甚至是句子。在 RAG 管道中进行检索时,会检索 N 个最相关的片段并将其注入提示中。挑战在于确保每个片段都提供足够的上下文/信息,以便 LLM 理解它。缺少上下文可能会导致 LLM 误解给定的片段并产生幻觉。一种常见的策略是将文档分成重叠的片段,但这并不能完全解决问题。几种高级技术可以提供帮助,例如“句子窗口检索”、“自动合并检索”和“父文档检索”。我们不会在这里详细介绍,但本质上,这些方法有助于获取检索到的片段周围的更多上下文,为 LLM 提供检索到的片段之前和之后的附加信息。
  • 优点:
    • 更好的矢量搜索质量。
    • 减少代币消耗。
  • 缺点:某些上下文可能仍然会丢失。

有用的方法
TextSegment.text()返回文本TextSegment
TextSegment.metadata()返回Metadata的TextSegment
TextSegment.from(String, Metadata)创建一个TextSegment来自文本并Metadata
TextSegment.from(String)创建TextSegment带有空文本的Metadata

  • 简易 RAG
  • 天真的 RAG
  • 具有查询压缩功能的高级 RAG
  • 具有查询路由的高级 RAG
  • 具有重新排序功能的高级 RAG
  • 包含元数据的高级 RAG
  • 具有元数据过滤功能的高级 RAG
  • 配备多只猎犬的高级 RAG
  • 具有 Web 搜索功能的高级 RAG
  • 带有 SQL 数据库的高级 RAG
  • 跳过检索
  • RAG +工具
  • 加载文档
作者:Jeebiz  创建时间:2024-08-18 12:10
最后编辑:Jeebiz  更新时间:2024-08-29 20:41