Chainlit集成LlamaIndex实现知识库高级检索(子问题查询引擎)
检索原理
llama_index
的 SubQuestionQueryEngine
是一个用于处理复杂查询的机制,它的主要目的是将复杂的查询问题分解成多个较小的、更容易管理和处理的子问题。这种技术有助于提高查询效率和准确性,尤其是在处理大量文档或者需要多步骤推理的情况下。
以下是 SubQuestionQueryEngine
的工作原理概览:
1. 查询分析
当用户提交一个查询请求时,SubQuestionQueryEngine
首先会对这个查询进行分析,以确定如何最佳地将其拆分为一系列更简单的子问题。这个过程涉及到理解查询的语义,识别查询的关键部分,以及确定哪些信息是相关的。
2. 子问题创建
根据查询分析的结果,SubQuestionQueryEngine
会创建一系列子问题。每个子问题都是对原始查询的一个方面或部分的关注。这些子问题的设计是为了让它们可以独立解决,同时又能够组合起来形成对原始查询的全面回答。
3. 子问题处理
对于每个创建的子问题,SubQuestionQueryEngine
会使用合适的查询引擎来寻找答案。这里使用的查询引擎可以是任何 llama_index
支持的类型,例如基于向量存储的查询引擎、树结构查询引擎等。查询引擎会选择最相关的文档片段来回答子问题。
4. 结果整合
一旦所有的子问题都被解决,SubQuestionQueryEngine
将整合这些子问题的答案,形成对原始查询的最终响应。这个整合过程可能涉及到合并相似的答案、去除重复信息、排序答案的重要性等步骤。
5. 返回最终结果
最终,SubQuestionQueryEngine
会返回一个综合的答案,这个答案应该能够全面地覆盖原始查询的所有方面。此外,它还可能包括关于答案来源的信息,帮助用户验证答案的可信度。
技术细节
- 动态规划:在某些情况下,
SubQuestionQueryEngine
可能会采用动态规划的方法来优化子问题的分割,确保找到最优的分割方案。 - 递归处理:对于特别复杂的查询,子问题本身可能也需要进一步分解,这时就会使用递归的方式处理。
- 并行处理:为了提高处理速度,不同的子问题可以在不同的线程或进程中并行处理。
总的来说,SubQuestionQueryEngine
的设计旨在通过将复杂任务分解为更小的任务来简化查询处理流程,同时利用现有的查询引擎技术来高效地检索相关信息。这使得 llama_index
在面对复杂查询时依然能够提供快速、准确的响应。
SubQuestionQueryEngine 查询引擎的优缺点
“LLamaIndex SubQuestionQueryEngine” 是一种用于处理复杂查询的技术,它通过将大问题分解为子问题来优化查询过程。下面概述了 SubQuestionQueryEngine 的一些优点和缺点:
优点
-
复杂查询处理:SubQuestionQueryEngine 能够处理复杂的查询请求,通过将其拆分成更小、更易于管理的子问题,从而提高了查询效率和准确性。
-
资源优化:通过只关注于解决特定子问题所需的信息,可以减少处理整个查询所需的计算资源和时间。
-
可扩展性:这种方法允许系统随着数据量的增长而扩展,因为每个子问题可以独立处理,且可以根据需要并行化处理。
-
灵活性:子问题可以根据上下文和可用信息进行动态调整,使得系统能够适应不同类型的查询请求。
-
增强理解:通过将大问题分解成多个小问题,可以帮助更好地理解用户意图,并提供更精确的答案。
缺点
-
子问题依赖性:如果子问题之间存在强依赖关系,则错误或不完整的答案可能会传播到其他部分,导致最终结果不准确。
-
分割逻辑复杂性:确定如何有效地分割原始查询可能是一个挑战,特别是当查询包含多个维度或复杂条件时。
-
性能开销:虽然从理论上讲,将问题分解可以提高效率,但在实际应用中,分割和重组子问题的过程可能会引入额外的延迟或计算成本。
-
一致性保证:在处理分布式或多阶段查询时,保持数据的一致性和完整性可能变得更加困难。
-
实现难度:设计一个能够智能地将查询分解成有效子问题的算法可能需要大量的工程努力和专业知识。
总体来说,SubQuestionQueryEngine 是一种有潜力的技术,特别是在处理大规模、复杂的查询场景下,但它也伴随着一系列挑战,特别是在确保分割逻辑正确性和系统整体性能方面。
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文件,代码如下:
import os
import time
import chainlit as cl
from llama_index.core import (
Settings,
VectorStoreIndex,
SimpleDirectoryReader, load_index_from_storage, StorageContext,
)
from llama_index.core.node_parser import SentenceSplitter
from llama_index.core.query_engine import SubQuestionQueryEngine
from llama_index.core.question_gen import LLMQuestionGenerator
from llama_index.core.tools import ToolMetadata, QueryEngineTool
from llama_index.embeddings.dashscope import DashScopeEmbedding, DashScopeTextEmbeddingModels, \
DashScopeTextEmbeddingType
from llama_index.llms.dashscope import DashScope, DashScopeGenerationModels
Settings.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_sub_question"
if os.path.exists(storage_dir):
storage_context = StorageContext.from_defaults(persist_dir=storage_dir)
index = load_index_from_storage(storage_context)
else:
documents = SimpleDirectoryReader("./data_file").load_data(show_progress=True)
index = VectorStoreIndex.from_documents(documents)
index.storage_context.persist(persist_dir=storage_dir)
return index
vector_store_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_query_engine = vector_store_index.as_query_engine(streaming=True, similarity_top_k=5)
# setup base query engine as tool
query_engine_tools = [
QueryEngineTool(
query_engine=vector_query_engine,
metadata=ToolMetadata(
name="知识库",
description="石家庄医专大学的相关介绍和问答",
),
),
]
question_gen = LLMQuestionGenerator.from_defaults()
query_engine = SubQuestionQueryEngine.from_defaults(
query_engine_tools=query_engine_tools, question_gen=question_gen
)
msg = cl.Message(content="", author="Assistant")
res = await query_engine.aquery(message.content)
await msg.stream_token(res.response)
print(f"代码执行时间: {time.time() - start_time} 秒")
source_names = []
for idx, node_with_score in enumerate(res.source_nodes):
node = node_with_score.node
source_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
是和上下文本块的重合文本的大小。 SubQuestionQueryEngine
不支持流式输出。而且使用不同LLM
,在问答的时候会解析报错,稳定性比较差,可能是针对open ai的模型,我使用qwen系列的模型,turbo模型相对稳定,但是不够智能,qwen-plus和qwen-max,运行查询有时候会报错,大概率是qwen模型不能严格按照提示词,生成固定格式的返回,导致解析报错。- 使用
SubQuestionQueryEngine
回答问题
代码解读
这段代码展示了一个使用 chainlit
和 llama_index
框架构建的简单聊天机器人应用程序。以下是代码的主要组成部分及其功能的解释:
导入模块
首先,导入必要的模块,包括 os
和 time
用于文件系统操作和计时,chainlit
用于构建交互式 Web 应用程序,以及 llama_index
的相关模块用于索引创建、文档加载、查询引擎设置等。
设置
定义了 Settings
类的实例,配置了 LLM(大型语言模型)和嵌入模型,以及节点解析器和输出长度等参数。这些设置用于初始化 llama_index
的核心组件。
获取向量存储索引
get_vector_store_index
函数检查是否已经存在一个持久化的索引文件。如果存在,则加载该索引;否则,读取指定目录中的文档,创建一个新的向量存储索引,并将其持久化到磁盘上。
启动聊天会话
start
函数用于初始化聊天会话,并发送一条欢迎消息给用户。
处理用户消息
main
函数在每次接收到用户的消息时调用。它记录开始时间,然后创建一个基于向量索引的查询引擎,并将其设置为工具。之后,它创建了一个 SubQuestionQueryEngine
实例,该实例使用上述工具来处理子问题查询。
接下来,函数发送一个空白的消息,以保持与用户的交互。然后,它使用异步方式查询 SubQuestionQueryEngine
并获取响应。响应的内容被流式传输给用户,并记录了源数据的信息。
最后,函数显示了数据来源,并发送完整消息。
总结
这段代码展示了如何集成 chainlit
和 llama_index
来创建一个基于文档的聊天机器人,它可以回答关于特定主题的问题,并引用其答案的数据来源。这个示例还演示了如何处理异步请求和流式响应,这对于实时聊天应用非常重要。
在项目根目录下创建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
启动后界面如下:
后续会出更多关于[LlamaIndex
高级检查的技术文章教程](https://blog.csdn.net/weixin_40986713/category_12606825.html),感兴趣的朋友可以持续关注我的动态!!!
相关文章推荐
《Chainlit快速实现AI对话应用的界面定制化教程》
《Chainlit接入FastGpt接口快速实现自定义用户聊天界面》
《使用 Xinference 部署本地模型》
《Fastgpt接入Whisper本地模型实现语音输入》
《Fastgpt部署和接入使用重排模型bge-reranker》
《Fastgpt部署接入 M3E和chatglm2-m3e文本向量模型》
《Fastgpt 无法启动或启动后无法正常使用的讨论(启动失败、用户未注册等问题这里)》
《vllm推理服务兼容openai服务API》
《vLLM模型推理引擎参数大全》
《解决vllm推理框架内在开启多显卡时报错问题》
《Ollama 在本地快速部署大型语言模型,可进行定制并创建属于您自己的模型》