检索原理
llamaindex
是一个用于构建和部署基于文档的问答系统的框架。其中,RouterRetriever
是一个特定的检索器组件,它设计用于根据输入查询选择最佳的检索策略。RouterRetriever
的主要功能是在多个不同的检索器之间进行路由,根据输入的特性选择最适合的检索器来处理请求。下面是 RouterRetriever
的检索原理概览:
-
多策略支持:
RouterRetriever
可以集成多种检索策略,比如基于关键词的检索、基于向量相似性的检索、甚至是更复杂的多模态检索方法。每种策略都有其特定的优势场景。 -
输入分析:当接收到一个查询时,
RouterRetriever
会对输入进行分析,这可能包括理解查询的意图、识别查询的关键元素或者是评估查询的复杂性等。 -
策略选择:根据输入分析的结果,
RouterRetriever
决定哪一个检索策略最适合当前查询。这个决策过程可能会依据预先定义的规则或通过机器学习模型来实现。 -
路由执行:一旦确定了最佳策略,
RouterRetriever
就会将查询传递给相应的检索器。检索器然后根据其特定的方法来执行检索操作。 -
结果整合:如果使用了多个检索策略,则
RouterRetriever
可能还需要负责整合不同检索器返回的结果,以便提供一个统一的答案或结果集给用户。 -
反馈优化:一些高级的
RouterRetriever
实现可能会包含反馈机制,允许系统根据用户的反馈调整其路由决策逻辑,从而不断优化选择策略的能力。
这种设计的好处在于它能够灵活地适应不同类型的数据源和查询需求,通过智能地选择最适合的检索策略来提高检索的准确性和效率。同时,这也意味着 RouterRetriever
需要有一个强大的输入理解和策略选择机制,以确保每次都能做出正确的决策。
RouterRetriever检索器的优缺点
RouterRetriever
是一种用于在多个检索策略之间进行智能路由的检索器,它的主要目标是根据输入查询的特点选择最优的检索策略。下面分别概述 RouterRetriever
的优点和缺点:
优点
-
灵活性:
RouterRetriever
可以整合多种检索技术,如基于关键词的检索、基于向量的检索等,因此能够在面对不同类型的查询时选择最合适的方法。 -
性能优化:通过智能选择检索策略,
RouterRetriever
可以减少不必要的计算负担,从而提高整体检索性能。 -
适应性强:它可以针对不同场景和数据类型进行优化,适用于多种应用场景,如问答系统、信息检索等。
-
扩展性好:随着新的检索技术和策略的发展,
RouterRetriever
可以轻松地添加新的检索器,从而增强系统的功能。 -
个性化推荐:通过学习用户的查询习惯和偏好,
RouterRetriever
可以为用户提供更加个性化的检索体验。
缺点
-
复杂性增加:由于需要集成多种检索策略并进行智能路由,
RouterRetriever
的实现和维护相对复杂,增加了系统的复杂度。 -
决策难度:正确地选择合适的检索策略需要强大的输入理解和决策机制,这本身就是一个挑战,特别是在面对模糊或多样化的查询时。
-
训练成本:如果使用机器学习模型来辅助决策,那么需要收集大量的训练数据,并且训练模型可能会消耗较多的时间和资源。
-
潜在延迟:虽然理论上
RouterRetriever
能够提高检索效率,但在实际应用中,额外的决策层可能会引入一定的延迟,尤其是在系统负载较高时。 -
依赖质量:
RouterRetriever
的表现依赖于所集成的各种检索器的质量及其相互之间的协调性。如果某个检索器表现不佳,可能会影响到整个系统的性能。
总体而言,RouterRetriever
提供了一种灵活的方式来管理不同检索策略,但同时也带来了一些管理和技术上的挑战。正确地设计和实现 RouterRetriever
对于发挥其优势至关重要。
LlamaIndex官方地址 https://docs.llamaindex.ai/en/stable/
快速上手
创建一个文件,例如“chainlit_chat”
mkdir chainlit_chat
进入 chainlit_chat
文件夹下,执行命令创建python 虚拟环境空间(需要提前安装好python sdk
。 Chainlit
需要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文件,代码如下:
- 此代码使用摘要索引和向量索引,利用
RetrieverQueryEngine
路由检索器,根据问题分类提示,选择摘要索引和向量索引进行索引。
import os
import timeimport chainlit as cl
from llama_index.core import (Settings,VectorStoreIndex,SimpleDirectoryReader, load_index_from_storage, StorageContext, SummaryIndex, )
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.retrievers import RouterRetriever
from llama_index.core.selectors import LLMMultiSelector
from llama_index.core.tools import RetrieverTool
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"], max_tokens=512
)
Settings.embed_model = DashScopeEmbedding(model_name=DashScopeTextEmbeddingModels.TEXT_EMBEDDING_V2,text_type=DashScopeTextEmbeddingType.TEXT_TYPE_DOCUMENT,
)
Settings.node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=20)
Settings.num_output = 512
Settings.context_window = 6000@cl.cache
def get_vector_store_index():storage_dir = "./storage_summary_index"if os.path.exists(storage_dir):summary_storage_context = StorageContext.from_defaults(persist_dir="./storage_summary_index")summary_index1 = load_index_from_storage(summary_storage_context)vector_storage_context = StorageContext.from_defaults(persist_dir="./storage_vector_index")vector_index1 = load_index_from_storage(vector_storage_context)else:documents = SimpleDirectoryReader("./data_file").load_data(show_progress=True)summary_index1 = SummaryIndex.from_documents(documents)vector_index1 = VectorStoreIndex.from_documents(documents)summary_index1.storage_context.persist(persist_dir="./storage_summary_index")vector_index1.storage_context.persist(persist_dir="./storage_vector_index")return summary_index1, vector_index1summary_index, vector_index = get_vector_store_index()DEFAULT_SUMMARY_TEXT = "Use this index for summarization queries"
DEFAULT_QA_TEXT = ("Use this index for queries that require retrieval of specific ""context from documents."
)@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()summary_retriever = summary_index.as_retriever(streaming=True, similarity_top_k=5, response_mode="tree_summarize")vector_retriever = vector_index.as_retriever(streaming=True, similarity_top_k=5)list_tool = RetrieverTool.from_defaults(retriever=summary_retriever,description=DEFAULT_SUMMARY_TEXT,)vector_tool = RetrieverTool.from_defaults(retriever=vector_retriever,description=DEFAULT_QA_TEXT,)retriever = RouterRetriever(selector=LLMMultiSelector.from_defaults(),retriever_tools=[list_tool,vector_tool,],verbose=True)query_engine = RetrieverQueryEngine.from_args(retriever, streaming=True)msg = cl.Message(content="", author="Assistant")res = await query_engine.aquery(message.content)async for token in res.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
是和上下文本块的重合文本的大小。 RouterRetriever
中selector选择器总共有四种,分别是LLMSingleSelector
LLM单选选择器(任意模型的抽象对象都能使用)LLMMultiSelector
LLM多选选择器(任意模型的抽象对象都能使用)PydanticSingleSelector
Pydantic单选选择器 (只能open ai的模型才能使用)PydanticMultiSelector
Pydantic多选选择器(只能open ai的模型才能使用)
代码解读
这段代码是一个使用 chainlit
框架构建的聊天应用示例,该应用利用 llama_index
库来处理文档索引和检索任务。以下是对代码各部分的详细解读:
导入模块
首先导入所需的模块,包括 os
、time
、chainlit
、llama_index
相关类和函数。
设置环境变量
设置 DashScope
API 的密钥,这是用于访问 DashScope
平台上的 LLM 和嵌入模型的。
初始化设置
通过 Settings
类初始化一些全局配置,包括使用的 LLM 模型、嵌入模型、节点分割器等参数。
定义获取向量存储索引的函数
get_vector_store_index()
函数检查是否存在预先存储的索引文件。如果存在,则加载这些索引;否则,读取指定目录下的文档,创建 SummaryIndex
和 VectorStoreIndex
,并将它们存储到指定路径。
初始化索引
调用 get_vector_store_index()
函数初始化 summary_index
和 vector_index
。
启动聊天会话
@cl.on_chat_start
装饰器定义了聊天开始时发送的消息。
处理消息
@cl.on_message
装饰器定义了当用户发送消息时触发的事件处理函数:
- 初始化检索器:分别为
summary_index
和vector_index
创建检索器实例。 - 创建工具:使用
RetrieverTool
封装检索器,并提供描述信息。 - 创建路由检索器:使用
RouterRetriever
组件,根据输入查询选择合适的检索工具。 - 创建查询引擎:使用
RetrieverQueryEngine
封装检索器,并启用流式传输响应。 - 处理查询:接收用户输入的消息,并异步执行查询。使用
stream_token
方法逐个流式传输响应给用户。 - 记录来源信息:记录用于生成响应的文档片段,并将其作为来源信息返回给用户。
总结
这段代码展示了如何使用 llama_index
来构建一个具有智能路由能力的检索系统,并通过 chainlit
提供实时的聊天界面。用户可以输入查询,系统将根据查询内容智能选择最适合的检索策略,并返回相应的答案和数据来源。此外,还记录了每次查询的执行时间,以便监控性能。
在项目根目录下创建data_file文件夹
将你的文件放到data_file
文件夹下。
llama_index
库支持多种文件格式的加载,以便从中提取文本内容用于索引构建和后续的信息检索或问答任务。以下是一些常见的文件格式支持:
- 文本文件 (
.txt
):简单的纯文本文件。 - PDF 文件 (
.pdf
):便携文档格式,广泛用于书籍、报告等文档。 - Microsoft Word 文档 (
.doc
,.docx
):Word 文档格式。 - CSV 文件 (
.csv
):逗号分隔值文件,常用于表格数据。 - HTML 文件 (
.html
,.htm
):超文本标记语言文件。 - Markdown 文件 (
.md
,.markdown
):轻量级标记语言。 - JSON 文件 (
.json
):JavaScript 对象表示法,常用于数据交换。 - EPUB 文件 (
.epub
):电子书格式。 - 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
启动后界面如下:
总结
在chunk_size大小为512,chunk_overlap为20时,整体回复表现良好。但是也有很大的局限性,模型在选择问题时,能否正确选择,这个变得很关键,但实际上往往很难做到正确选择,就比如,我提问2023年的财务报表,模型会倾向于这是一个总结摘要类的问题,会去摘要索引里查找,但是实际上023年的财务报表数据实在向量索引存储的。
相关文章推荐
《Chainlit快速实现AI对话应用的界面定制化教程》
《Chainlit接入FastGpt接口快速实现自定义用户聊天界面》
《使用 Xinference 部署本地模型》
《Fastgpt接入Whisper本地模型实现语音输入》
《Fastgpt部署和接入使用重排模型bge-reranker》
《Fastgpt部署接入 M3E和chatglm2-m3e文本向量模型》
《Fastgpt 无法启动或启动后无法正常使用的讨论(启动失败、用户未注册等问题这里)》
《vllm推理服务兼容openai服务API》
《vLLM模型推理引擎参数大全》
《解决vllm推理框架内在开启多显卡时报错问题》
《Ollama 在本地快速部署大型语言模型,可进行定制并创建属于您自己的模型》