Chainlit集成LlamaIndex实现知识库高级检索(自动合并检索)

检索原理

自动合并检索
自动合并检索原理,和我的上一篇文章的检索方案: 将文本分割成512大小(一般对应段落大小)和128(一般对句子大小不是严格的句子长度)大小两种分别存储到索引库,再用llama_index的简单融合寻回器,分别从这里个向量库查询。将查询结果融合排序后交给LLM的方式十分类似,不同点是检索能将子小块的内容合成大块文本返回。上一篇文章《Chainlit集成LlamaIndex实现知识库高级检索(简单融合寻回器)》
自动合并检索主要是将文档按照块大小拆分成不同层级的节点,这些节点包括父节点和子节点,然后在检索过程中找到相似度高的叶子节点,如果一个父节点中有多个子节点被检索到,那么这个父节点就会被自动合并,最终将父节点的所有文档都作为上下文发送给 LLM(大语言模型),下面是自动合并检索的示意图:

在这里插入图片描述

该检索技术的优缺点

LlamaIndex是一个用于将大型语言模型(LLMs)与外部数据连接的工具,它提供了一系列的功能,使得从外部数据源检索信息变得更加容易和高效。其中,自动合并检索(Auto-merging Retrieval)是LlamaIndex的一项重要功能,它能够在检索过程中自动合并相关的小文本片段,形成更大的上下文,以便更好地服务于后续的信息合成或问题回答任务。以下是关于LlamaIndex自动合并检索的一些优缺点分析:

优点

提高检索质量
自动合并检索能够递归地“合并”引用父节点超过给定阈值的叶节点子集,从而将潜在不同的、较小的上下文合并成一个较大的上下文。这意味着,当用户提出一个问题或者需要获取相关信息时,系统不仅仅提供孤立的信息片段,而是将相关的信息整合起来,形成更加完整和连贯的答案,提高了检索结果的相关性和质量。

优化文本合成
自动合并检索不仅有助于提高检索的准确性,还可以优化文本的合成过程。通过合并相关的上下文,系统可以更好地理解用户的需求,并且在合成信息时考虑到更多的背景信息,使得最终生成的文本更加符合用户的预期。

灵活性和高效性
LlamaIndex作为一个文本检索工具,以其灵活性和高效性著称。自动合并检索作为其一部分,同样继承了这些特点,使得用户可以在不同的应用场景下灵活地调整检索参数,以达到最佳的检索效果。

缺点

可能增加计算成本
虽然自动合并检索提高了检索质量,但是由于需要合并多个相关片段,可能会导致计算资源的消耗增加。特别是在处理大量数据的情况下,如果不对合并的阈值和策略进行合理的设定,可能会导致不必要的计算开销。

复杂性提升
相比于简单的检索方式,自动合并检索增加了系统的复杂性。为了实现高效的合并,需要设计合理的层次结构和切割策略,这对于用户来说意味着更高的学习成本和技术门槛。同时,这也要求系统设计者需要对数据结构有深入的理解。

潜在的信息冗余
尽管自动合并检索旨在提供更完整的上下文,但是在某些情况下,这种合并可能会引入不必要的信息冗余。如果合并策略不够精确,可能会导致检索结果中包含过多无关紧要的细节,反而影响了信息的清晰度和可读性。

综上所述,LlamaIndex的自动合并检索功能为提高检索质量和文本合成提供了强大的支持,但在实际应用中也需要考虑其可能带来的额外开销和复杂性。正确地配置和使用这一功能,对于最大化其优势同时减轻潜在的缺点至关重要。总体来看,只要文档切割分块的每个块的内容完整,使用自动合并检索的这种方式优于 我上一篇写的文章《Chainlit集成LlamaIndex实现知识库高级检索(简单融合寻回器)》 的方案,当然你也可以使用 简单融合寻回器和自动合并检索结合,利用QueryFusionRetrievernum_queries参数来生成多个相似问题,使用retrievers参数设置,自动合并检索器和其他检索器整合,提高检索精度。

LlamaIndex官方地址 https://docs.llamaindex.ai/en/stable/

快速上手

创建一个文件,例如“chainlit_chat”

mkdir chainlit_chat

进入 chainlit_chat文件夹下,执行命令创建python 虚拟环境空间(需要提前安装好python sdkChainlit 需要python>=3.8。,具体操作,由于文章长度问题就不在叙述,自行百度),命令如下:

python -m venv .venv
  • 这一步是避免python第三方库冲突,省事版可以跳过
  • .venv是创建的虚拟空间文件夹可以自定义

接下来激活你创建虚拟空间,命令如下:

#linux or mac
source .venv/bin/activate
#windows
.venv\Scripts\activate

在项目根目录下创建requirements.txt,内容如下:

chainlit
llama-index-core
llama-index-llms-dashscope
llama-index-embeddings-dashscope

执行以下命令安装依赖:

pip install -r .\requirements.txt
  • 安装后,项目根目录下会多出.chainlit.files文件夹和chainlit.md文件

代码创建

只使用通义千问的DashScope模型服务灵积的接口

在项目根目录下创建.env环境变量,配置如下:

DASHSCOPE_API_KEY="sk-api_key"
  • DASHSCOPE_API_KEY 是阿里dashscope的服务的APIkey,代码中使用DashScope的sdk实现,所以不需要配置base_url。默认就是阿里的base_url。
  • 阿里模型接口地址 https://dashscope.console.aliyun.com/model

在项目根目录下创建app.py文件,代码如下:

import os
import timeimport chainlit as cl
from llama_index.core import (Settings,VectorStoreIndex,SimpleDirectoryReader, StorageContext, load_index_from_storage, )
from llama_index.core.node_parser import SentenceSplitter, HierarchicalNodeParser, get_leaf_nodes, get_root_nodes, \get_child_nodes
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.retrievers import AutoMergingRetriever
from llama_index.core.storage.docstore import SimpleDocumentStore
from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels, \DashScopeTextEmbeddingType
from llama_index.llms.dashscope import DashScope, DashScopeGenerationModelsSettings.llm = DashScope(model_name=DashScopeGenerationModels.QWEN_MAX, max_tokens=512, api_key=os.environ["DASHSCOPE_API_KEY"]
)
Settings.embed_model = DashScopeEmbedding(model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2,text_type=DashScopeTextEmbeddingType.TEXT_TYPE_DOCUMENT,
)@cl.cache
def get_vector_store_index():storage_dir = "./storage_auto"if os.path.exists(storage_dir):# rebuild storage contextstorage_context = StorageContext.from_defaults(persist_dir=storage_dir)# load indexvector_store_index = load_index_from_storage(storage_context)else:documents = SimpleDirectoryReader("./data_file").load_data(show_progress=True)print(f"documents: {len(documents)}")chunk_sizes = [512, 128]node_parser_ids = [f"chunk_size_{chunk_size}" for chunk_size in chunk_sizes]node_parser_map = {}for chunk_size, node_parser_id in zip(chunk_sizes, node_parser_ids):if chunk_size == 128:chunk_overlap = 10else:chunk_overlap = 20print(chunk_size, chunk_overlap)node_parser_map[node_parser_id] = SentenceSplitter(chunk_size=chunk_size,chunk_overlap=chunk_overlap,)node_parser = HierarchicalNodeParser.from_defaults(node_parser_ids=node_parser_ids, node_parser_map=node_parser_map)nodes = node_parser.get_nodes_from_documents(documents)print(f"nodes: {len(nodes)}")root_nodes = get_root_nodes(nodes)print(f"root_nodes: {len(root_nodes)}")middle_nodes = get_child_nodes(root_nodes, all_nodes=nodes)print(f"middle_nodes: {len(middle_nodes)}")leaf_nodes = get_leaf_nodes(nodes)print(f"leaf_nodes: {len(leaf_nodes)}")doc_store = SimpleDocumentStore()doc_store.add_documents(nodes)storage_context = StorageContext.from_defaults(docstore=doc_store)vector_store_index = VectorStoreIndex(nodes=leaf_nodes, storage_context=storage_context)vector_store_index.storage_context.persist(persist_dir=storage_dir)return vector_store_indexvector_index = get_vector_store_index()@cl.on_chat_start
async def start():await cl.Message(author="Assistant", content="你好! 我是泰山AI智能助手. 有什么可以帮助你的吗?").send()@cl.on_message
async def main(message: cl.Message):start_time = time.time()vector_retriever = vector_index.as_retriever(similarity_top_k=20)retriever = AutoMergingRetriever(vector_retriever=vector_retriever, storage_context=vector_index.storage_context,simple_ratio_thresh=0.4,verbose=True)query_engine = RetrieverQueryEngine.from_args(retriever, streaming=True,)print(f"代码执行时间1: {time.time() - start_time} 秒")msg = cl.Message(content="", author="Assistant")res = await query_engine.aquery(message.content)print(f"代码执行时间1: {time.time() - start_time} 秒")async for token in res.response_gen:await msg.stream_token(token)print(f"代码执行时间3: {time.time() - start_time} 秒")source_names = []for idx, node_with_score in enumerate(res.source_nodes):node = node_with_score.nodesource_name = f"source_{idx}"source_names.append(source_name)msg.elements.append(cl.Text(content=node.get_text(), name=source_name, display="side"))await msg.stream_token(f"\n\n **数据来源**: {', '.join(source_names)}")await msg.send()
  • 代码中的persist_dir=storage_dir 不设置的默认是 ./storage.
  • 代码中chunk_size是将长文档分割的文本块的大小,chunk_overlap 是和上下文本块的重合文本的大小。
  • 代码中 node_parser = HierarchicalNodeParser.from_defaults( node_parser_ids=node_parser_ids, node_parser_map=node_parser_map ) 可以简写为 node_parser = HierarchicalNodeParser.from_defaults() 会按照 [2048,512,128]三种层次分割,经过我测试不使用默认的效果会更好
  • similarity_top_k=20 返回20条最相关的数据
  • simple_ratio_thresh,它的默认值是 0.5,表示自动合并文档的阀值,如果在一个父节点中,子节点被检索到的比例小于这个阀值,那么自动合并功能将不会生效,这样提交给 LLM 的上下文就只会包含检索到的叶子节点。反之如果大于这个阀值,文档就会自动合并,最终提交给 LLM 的上下文就会包含这个父节点的内容。比如父节点有 4 个子节点,检索时发现只有 1 个子节点,那么子节点被检索到的比例就是 0.25(1/4),小于阀值 0.5,所以自动合并功能不会生效,最终提交给 LLM 的上下文就只会包含那个检索到的子节点。

代码解读

这段代码展示了一个使用 chainlitllama_index 库来创建一个基于向量存储索引的问答系统的过程。下面是对这段代码的关键部分进行的解读:

  1. 导入必要的库

    • 导入了 ostime 这两个Python标准库。
    • 导入了来自 llama_index 库的核心组件,如设置、向量存储索引、文档读取器等。
    • 导入了 chainlit 库用于构建交互式聊天应用。
  2. 初始化LLM(Large Language Model)和嵌入模型

    • 设置了使用的LLM为 DashScope 提供的 Qwen Max 模型,并配置了API密钥等参数。
    • 嵌入模型也选择了 DashScopeTEXT_EMBEDDING_V2 模型。
  3. 定义获取向量存储索引的方法

    • 如果存储目录存在,则加载已有的索引;否则,从文档中创建新的索引。
    • 使用 HierarchicalNodeParser 来处理文档,将其拆分为不同粒度的节点,并构建层次结构。
    • 创建 SimpleDocumentStore 并将所有节点存储进去。
    • 创建并保存向量存储索引。
  4. 定义聊天启动函数

    • 使用 chainliton_chat_start 装饰器来定义当聊天开始时发送的消息。
  5. 定义消息处理函数

    • 使用 chainliton_message 装饰器来定义接收用户输入后执行的操作。
    • 创建一个向量检索器,并基于此创建一个自动合并检索器。
    • 创建一个查询引擎来处理检索到的信息,并通过流式传输的方式返回结果。
    • 处理查询引擎返回的结果,并通过 chainlit 发送回给用户。

这段代码主要展示了如何构建一个基于文档知识库的问答系统,并且利用 chainlit 来提供用户界面进行交互。它包括了从文档加载到索引构建,再到查询处理和结果展示的整个流程。需要注意的是,为了使这段代码运行,你需要确保安装了所有必要的依赖库,并且拥有正确的API密钥。此外,代码中的路径(例如 ./data_file./storage_auto)需要根据实际情况调整。

在项目根目录下创建data_file文件夹

在这里插入图片描述
将你的文件放到这里,代码中设置的支持,pdf、doc、csv 、txt格式的文件,后续可以根据自己的需求增加更多,langchain带有很多格式文件的加载器,可以自行修改代码。

运行应用程序

要启动 Chainlit 应用程序,请打开终端并导航到包含的目录app.py。然后运行以下命令:

 chainlit run app.py -w   
  • -w标志告知 Chainlit 启用自动重新加载,因此您无需在每次更改应用程序时重新启动服务器。您的聊天机器人 UI 现在应该可以通过http://localhost:8000访问。
  • 自定义端口可以追加--port 80

启动后界面如下:

在这里插入图片描述
在这里插入图片描述

后续会出关于LlamaIndex高级检查的技术文章教程,感兴趣的朋友可以持续关注我的动态!!!

相关文章推荐

《Chainlit快速实现AI对话应用的界面定制化教程》
《Chainlit接入FastGpt接口快速实现自定义用户聊天界面》
《使用 Xinference 部署本地模型》
《Fastgpt接入Whisper本地模型实现语音输入》
《Fastgpt部署和接入使用重排模型bge-reranker》
《Fastgpt部署接入 M3E和chatglm2-m3e文本向量模型》
《Fastgpt 无法启动或启动后无法正常使用的讨论(启动失败、用户未注册等问题这里)》
《vllm推理服务兼容openai服务API》
《vLLM模型推理引擎参数大全》
《解决vllm推理框架内在开启多显卡时报错问题》
《Ollama 在本地快速部署大型语言模型,可进行定制并创建属于您自己的模型》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1542635.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

架构设计笔记-5-软件工程基础知识

知识要点 按软件过程活动,将软件工具分为软件开发工具、软件维护工具、软件管理和软件支持工具。 软件开发工具:需求分析工具、设计工具、编码与排错工具。 软件维护工具:版本控制工具、文档分析工具、开发信息库工具、逆向工程工具、再工…

快速解决Isaac Sim资源获取不到问题

国内使用Isaac Sim的时候,最常见的问题是加载不了USD或材质资源,这会导致整个Isaac Sim软件卡住或崩溃,以及无法继续开展项目。比如加载realsense或,最新的Isaac Sim 4.2.0 加载一个激光雷达,都要获取相关传感器usd&am…

桶排序和计数排序(非比较排序算法)

桶排序 桶排序是一种基于分配的排序算法,特别适合用来排序均匀分布的数据。它的基本思想是将输入的数据分到有限数量的桶里,然后对每个桶内的数据分别进行排序,最后再将各个桶内的数据合并得到最终的排序结果。(通常用于浮点数,因…

RuntimeError: Maximum Recursion Depth Exceeded - 递归深度超限的完美解决方案

RuntimeError: Maximum Recursion Depth Exceeded - 递归深度超限的完美解决方案 🛠️ RuntimeError: Maximum Recursion Depth Exceeded - 递归深度超限的完美解决方案摘要 📃引言 ✨1. 什么是递归?🔍1.1 递归的基本概念 &#x…

JavaScript可视化示例

JavaScript 可视化是指使用 JavaScript 编程语言来创建和操作图形、图表、动画等视觉元素的过程。以下是一些常见的 JavaScript 可视化库和工具,以及它们的主要特点: 1. D3.js 特点: D3.js(Data-Driven Documents)是一个非常强大…

思维商业篇(4)—产业上下游定

思维商业篇(4)—产业上下游定位(微笑曲线) 产业上下游定位,帮助我们去观察一个企业在产业上下游中处于一个什么样的生态位。 上游 处于产业链开始端,百川东到海,百川的的起始端就是上游,东到海的海就是下游。 处在上游的企业一…

嵌入式系统基础讲解

​ 大家好,我是程序员小羊! 前言: 嵌入式系统是计算机科学与电子工程的交叉领域,广泛应用于消费电子、工业控制、汽车、医疗设备等多个行业。嵌入式系统设计涉及硬件和软件的协同开发,要求开发者掌握多方面的基础知识。…

Python学习——【4.4】数据容器(序列)的切片

文章目录 【4.4】数据容器(序列)的切片一、了解什么是序列二、掌握序列的切片操作 【4.4】数据容器(序列)的切片 一、了解什么是序列 序列是指:内容连续、有序,可使用下标索引的一类数据容器。 列表、元组…

基于单片机的粮仓环境检测系统设计

本设计主要由处理模块、温湿度检测模块、数据显示模块、声光报警模块和按钮的输入模块组成。采用了AT89C52作为主要的控制单元,利用DHT11温湿度传感器,对粮食仓库中的温度和湿度等展开检测,并在LCD1602液晶显示器中进行实时显示。同时&#x…

双向链表:实现、操作与分析【算法 17】

双向链表:实现、操作与分析 引言 双向链表(Doubly Linked List)是链表数据结构的一种重要形式,它允许节点从两个方向进行遍历。与单向链表相比,双向链表中的每个节点不仅包含指向下一个节点的指针(或引用&…

iOS常见锁及应用(笔记版)

什么是锁? 在程序中,当多个任务(或线程)同时访问同一个资源时,比如多个操作同时修改一份数据,可能会导致数据不一致。这时候,我们需要“锁”来确保同一时间只有一个任务能够操作这个数据&#…

django项目——图片上传到阿里云OSS对象存储

文章目录 实现图片上传到阿里云OSS对象存储1. 创建阿里云OSS对象存储2. 查询获取接口访问key和秘钥3. 安装阿里云的SDK集成到项目中使用3.1 python直接操作oss23.2 django配置自定义文件存储上传文件到oss 实现图片上传到阿里云OSS对象存储 1. 创建阿里云OSS对象存储 开发文档…

顶点缓存对象(VBO)与顶点数组对象(VAO)

我们的顶点数组在CPU端的内存里是以数组的形式存在,想要GPU去绘制三角形,那么需要将这些数据传输给GPU。那这些数据在显存端是怎么存储的呢?VBO上场了,它代表GPU上的一段存储空间对象,表现为一个unsigned int类型的变量,GPU端内存对象的一个ID编号、地址、大小。一个VBO对…

Python爬虫之urllib模块详解

Python爬虫入门 此专栏为Python爬虫入门到进阶学习。 话不多说,直接开始吧。 urllib模块 Python中自带的一个基于爬虫的模块,其实这个模块都几乎没什么人用了,我就随便写写了。 - 作用:可以使用代码模拟浏览器发起请求。&…

基于python的文本聚类分析与可视化实现,使用kmeans聚类,手肘法分析

1、数据预处理 由于在数据分析之前数据集通常都存在数据重复、脏数据等问题,所以为了提高 数据分析结果的质量,在应用之前就必须对数据集进行数据预处理。数据预处理的方法通常有清洗、集成、转换、规约这四个方面,接下来详细介绍这对爬取…

leetcode第七题:字符反转

给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] ,就返回 0。 假设环境不允许存储 64 位整数(有符号或无符号)。 示例 1: 输入…

分布式安装LNMP

目录 搭建LNMP架构 安装mysql 1.上传mysql软件包,关闭防火墙和核心防护 2.安装环境依赖包,桌面安装可能有自带的数据库除 3.配置软件模块 4.编译及安装 5.创建mysql用户 6.修改mysql 配置文件 7.更改mysql安装目录和配置文件的属主属组 8.设置…

认识结构体

目录 一.结构体类型的声明 1.结构的声明 2.定义结构体变量 3.结构体变量初始化 4.结构体的特殊声明 二.结构体对齐(重点难点) 1.结构体对齐规则 2.结构体对齐练习 (一)简单结构体对齐 (二)嵌套结构体对齐 3.为什么存在内存对齐 4.修改默认对齐数 三.结构体传参 1…

Object类代码结构

Object Object是所有类的父类。 方法结构如下 一些不知道的方法 private static native void registerNatives(); * JNI机制 * 这里定义了一个 native 方法 registerNatives(),它没有方法体。 * native 关键字表示这个方法的实现是由本地代码 * (通常…

【Pytorch】一文快速教你高效使用torch.no_grad()

🎬 鸽芷咕:个人主页 🔥 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 博主简介 博主致力于嵌入式、Python、人工智能、C/C领域和各种前沿技术的优质博客分享,用最优质的内容带来最舒适的…