Chainlit集成LlamaIndex实现知识库高级检索(从小到大递归检索器)

ops/2024/10/19 7:31:30/

检索原理

从小到大的检索是指我们在切割文档时可以同时设置多个不同的chunk_size的颗粒度,比如我们可以同时设置chunk_size为128,256,512即按这三个不同的颗粒度对同时对所有文档都切割一遍。利用LlamaIndex中的RecursiveRetriever递归检索器实现对不同颗粒度的文本块检索。

递归检索的概念是我们不仅探索最直接相关的节点,还探索节点关系到额外的检索器/查询引擎并执行它们。例如,一个节点可以表示一个结构化表格的简洁摘要,并链接到该结构化表格之上的SQL/Pandas查询引擎。那么如果这个节点被检索出来,我们也希望查询底层的查询引擎以获得答案。

这对于具有层次关系的文档尤其有用。在这个例子中,我们浏览一篇关于亿万富翁的维基百科文章(以PDF形式),它包含文本和各种嵌入的结构化表格。我们首先为每个表格创建一个Pandas查询引擎,同时用一个节点(存储了一个指向查询引擎的链接)来表示每个表格;这个节点与其他节点一起存储在一个向量存储中,我们称之为IndexNode

RecursiveRetriever检索器的优缺点

LlamaIndex 是一个用于创建索引并从文档中检索信息的框架。在 LlamaIndex 中,RecursiveRetriever 是一种用于从复杂的数据结构中递归地提取信息的方法。这种检索器主要用于处理嵌套数据结构的情况,例如文档中有多个子文档,而这些子文档又可能包含更深层次的子文档。

优点

  1. 深度检索RecursiveRetriever 能够遍历整个嵌套数据结构,确保不会遗漏任何相关信息。
  2. 灵活性:对于具有层次或嵌套结构的信息,如多级目录或复杂的文档集合,RecursiveRetriever 提供了很好的灵活性。
  3. 适应性:对于那些需要在不同层级上进行搜索的场景,如知识图谱或树状结构数据,RecursiveRetriever 可以提供有效的解决方案。

缺点

  1. 性能问题:由于需要遍历整个嵌套结构,因此在处理大规模数据集时可能会遇到性能瓶颈。随着数据量的增长,检索速度可能会变慢。
  2. 资源消耗:递归操作可能会导致较高的内存使用,尤其是在处理深度嵌套的数据时。
  3. 复杂度增加:实现和维护递归逻辑可能会增加代码的复杂性,这可能会导致更高的开发和维护成本。
  4. 过拟合风险:如果递归层次过深,可能会导致检索结果过于具体化,忽略了更广泛的相关信息。

在使用 RecursiveRetriever 时,应该考虑到上述优点和缺点,并根据实际应用场景来决定是否采用这种方法。此外,还可以考虑结合其他检索技术来优化检索效果和提高效率。

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

快速上手

chainlit_chat_29">创建一个文件,例如“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
llama-index-retrievers-bm25~=0.3.0

执行以下命令安装依赖:

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 SimpleNodeParser, SentenceSplitter
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.retrievers import RecursiveRetriever
from llama_index.core.schema import IndexNode
from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels, \DashScopeTextEmbeddingType
from llama_index.llms.dashscope import DashScope, DashScopeGenerationModelsSettings.llm = DashScope(model_name=DashScopeGenerationModels.QWEN_MAX, 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_recursion"all_nodes = []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)for node in storage_context.docstore.docs.values():all_nodes.append(node)print(f"11 all_nodes: {len(storage_context.docstore.docs)}")return vector_store_indexelse:documents = SimpleDirectoryReader("./data_file").load_data(show_progress=True)print(f"documents: {len(documents)}")node_parser = SentenceSplitter.from_defaults(chunk_size=512, chunk_overlap=20)base_nodes = node_parser.get_nodes_from_documents(documents)print(f"base_nodes: {len(base_nodes)}")sub_chunk_sizes = [128]sub_node_parsers = [SentenceSplitter.from_defaults(chunk_size=size, chunk_overlap=(int(size / 10))) for size in sub_chunk_sizes]for base_node in base_nodes:for sub_node_parser in sub_node_parsers:sub_nodes = sub_node_parser.get_nodes_from_documents([base_node])sub_inodes = [IndexNode.from_text_node(sn, base_node.node_id) for sn in sub_nodes]all_nodes.extend(sub_inodes)# 添加父节点文档original_node = IndexNode.from_text_node(base_node, base_node.node_id)all_nodes.append(original_node)print(f"all_nodes: {len(all_nodes)}")vector_store_index = VectorStoreIndex(all_nodes)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()msg = cl.Message(content="", author="Assistant")vector_retriever = vector_index.as_retriever(similarity_top_k=10)all_ids = vector_index.docstore.docsnode_ids = []for ids in all_ids:print(ids)node_ids.append(ids)all_nodes = vector_index.docstore.get_nodes(node_ids=node_ids)print(f"all_nodes: {len(all_nodes)}")all_nodes_dict = {n.node_id: n for n in all_nodes}recursive_retriever = RecursiveRetriever("vector",retriever_dict={"vector": vector_retriever},node_dict=all_nodes_dict,verbose=True,)query_engine = RetrieverQueryEngine.from_args(retriever=recursive_retriever, streaming=True)res = await query_engine.aquery(message.content)print('res', type(res), res)async for token in res.async_response_gen():await msg.stream_token(token)print(f"代码执行时间: {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 是和上下文本块的重合文本的大小。
  • 如何想流式输出,请将代码中的print('res', type(res), res)注释掉,异步响应时,打印res,会变成同步。
  • 本代码展示出从向量文档库里获取所有节点的方法

代码解读

这段代码展示了如何使用 LlamaIndex 框架来构建一个基于向量存储的索引,并通过 Chainlit 创建一个聊天应用。下面是对代码的逐行解读:

  1. 导入必要的模块

    python">import os
    import time
    import chainlit as cl
    from llama_index.core import (Settings,VectorStoreIndex,SimpleDirectoryReader,StorageContext,load_index_from_storage,
    )
    from llama_index.core.node_parser import SimpleNodeParser, SentenceSplitter
    from llama_index.core.query_engine import RetrieverQueryEngine
    from llama_index.core.retrievers import RecursiveRetriever
    from llama_index.core.schema import IndexNode
    from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels, \DashScopeTextEmbeddingType
    from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels
    
  2. 设置 LLM 和 Embedding 模型

    python">Settings.llm = DashScope(model_name=DashScopeGenerationModels.QWEN_MAX, api_key=os.environ["DASHSCOPE_API_KEY"]
    )
    Settings.embed_model = DashScopeEmbedding(model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2,text_type=DashScopeTextEmbeddingType.TEXT_TYPE_DOCUMENT,
    )
    

    这里配置了大模型(LLM)和嵌入模型(Embedding Model),使用的是来自 DashScope 的模型。

  3. 定义函数 get_vector_store_index

    python">@cl.cache
    def get_vector_store_index():...
    

    此函数负责获取或构建向量存储索引。如果存储目录存在,则加载已有的索引;否则,从指定目录读取文档,解析节点,并构建新的索引。

  4. 定义 start 函数

    python">@cl.on_chat_start
    async def start():await cl.Message(author="Assistant", content="你好! 我是泰山AI智能助手. 有什么可以帮助你的吗?").send()
    

    当聊天开始时发送欢迎消息。

  5. 定义 main 函数

    python">@cl.on_message
    async def main(message: cl.Message):...
    

    此函数处理用户输入的消息。它首先初始化一个向量检索器,然后构建一个递归检索器,最后使用这个检索器来查询用户输入,并将结果流式传输给用户。

  6. 主逻辑

    • 加载索引或创建新的索引。
    • 使用向量检索器(vector_retriever)和递归检索器(recursive_retriever)。
    • 构建查询引擎(query_engine),并使用异步查询方法 aquery 处理用户输入的消息。
    • 将结果通过 stream_token 方法逐字流式传输给用户。
    • 记录并显示数据来源。

这段代码主要展示了如何利用 LlamaIndex 进行文档索引和检索,并且通过 Chainlit 实现了一个简单的聊天应用界面。值得注意的是,这里使用了异步处理方式来提高用户体验,使得响应更加流畅。

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

在这里插入图片描述
将你的文件放到data_file文件夹下。
llama_index 库支持多种文件格式的加载,以便从中提取文本内容用于索引构建和后续的信息检索或问答任务。以下是一些常见的文件格式支持:

  1. 文本文件 (.txt):简单的纯文本文件。
  2. PDF 文件 (.pdf):便携文档格式,广泛用于书籍、报告等文档。
  3. Microsoft Word 文档 (.doc, .docx):Word 文档格式。
  4. CSV 文件 (.csv):逗号分隔值文件,常用于表格数据。
  5. HTML 文件 (.html, .htm):超文本标记语言文件。
  6. Markdown 文件 (.md, .markdown):轻量级标记语言。
  7. JSON 文件 (.json):JavaScript 对象表示法,常用于数据交换。
  8. EPUB 文件 (.epub):电子书格式。
  9. PPTX 文件 (.pptx):PowerPoint 演示文稿。

除了上述文件格式外,llama_index 可能还支持其他一些格式,具体取决于其内部依赖库的支持情况。例如,它可能通过第三方库支持解析像 .xls, .xlsx 这样的 Excel 文件。

为了加载这些不同类型的文件,llama_index 提供了多个不同的读取器(readers),如 SimpleDirectoryReader 可以用来加载一个目录中的多个文件,而针对特定文件格式(如 PDF 或 Word 文档),则有专门的读取器类。

例如,如果你有一个包含多种文件格式的目录,你可以使用 SimpleDirectoryReader 来加载它们。如果你只处理一种类型的文件,比如 PDF 文件,你可以选择使用更具体的读取器,比如 PDFReader

运行应用程序

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

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

启动后界面如下:

在这里插入图片描述
在这里插入图片描述
BM25Retriever索引器还可以与向量检索器等其他索引器,利用QueryFusionRetriever类将其融合查询。

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

相关文章推荐

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


http://www.ppmy.cn/ops/118151.html

相关文章

PHP爬虫:获取商品销量详情API的利器

在电子商务时代,商品的销量数据对于商家来说至关重要。它不仅能够帮助商家了解市场动态,还能够指导库存管理和营销策略。PHP作为一种流行的服务器端脚本语言,结合其强大的HTTP请求处理能力,可以有效地用于编写爬虫程序&#xff0c…

python/requests库的使用/爬虫基础工具/

requests 是一个 Python 库,它允许你发送 HTTP 请求。这个库需要单独安装,因为它不是 Python 标准库的一部分 1.让我们安装requests 在控制台运行 pip install requests 使用 requests 发送请求 1.GET 请求: import requestsresponse …

S1_02_第一章_计算机网络概述

1、什么是计算机网络 那么,到底什么是计算机网络呢?用通信设备和线路将处于不同地理位置、操作相对独立的多 台计算机连接起来,并配置相应的系统和应用软件,在原本各自独立的计算机之间实现软硬件资源 共享和信息传递等形成的系统就是计算机网络。 1.1、计算机网络的功能 1&…

Ubuntu 离线安装 docker

1、下载离线包,网址:https://download.docker.com/linux/ubuntu/dists/xenial/pool/stable/ 离线安装docker需要下载3个包,containerd.io ,docker-ce-cli,docker-ce 2、下载完毕后拷贝到ubuntu上用 dpkg 命令安装&am…

Python爬虫爬取王者荣耀英雄信息并保存到图数据库

爬取信息说明 英雄名称英雄类型英雄包含的所有皮肤名称 创建英雄类型节点 王者荣耀官方给出的英雄类型是以下几种: 直接准备好英雄类型词典 hero_type_dict [战士, 法师, 坦克, 刺客, 射手, 辅助 ]添加到图数据库中 def create_hero_type_node():for hero_ty…

MacOS Catalina 从源码构建Qt6.2开发库之02: 配置QtCreator

安装Qt-creator-5.0.2 在option命令中配置Qt Versions指向 /usr/local/bin/qmake6 Kits选入CLang

Docker实践与应用举例

Docker是一种开源的容器化平台,它使得开发者和系统管理员能够方便地打包、分发和运行应用程序。利用Docker,您可以将应用程序和其所有依赖项封装成一个独立的容器,确保应用能够在任何环境中一致运行。随着微服务架构的发展,Docker…

如何用Shell命令结合 正则表达式 统计文本中的ip地址数量

文章目录 简介问题回答 简介 IP 地址(Internet Protocol Address)是互联网协议地址的简称,是互联网上为联网的设备(如计算机、服务器、路由器、手机等)分配的唯一标识符。IP 地址的主要功能是实现不同网络设备之间的通…