LlamaIndex Querying 自定义查询
查询执行流程
完整的查询流程包含三个关键阶段:
检索阶段(Retrieval) 从索引(Index)中查找并返回与查询最相关的文档。如先前在索引部分所述,最常用的检索方式是"top-k"语义检索,但实际还存在多种其他检索策略。
后处理阶段(Postprocessing) 对检索到的节点(Nodes)进行可选操作,包括:重新排序、格式转换或过滤(例如要求节点必须包含关键词等特定元数据)。
响应合成阶段(Response Synthesis) 将用户查询语句、检索到的最相关数据以及提示模板组合后,发送给大语言模型(LLM)生成最终响应。
自定义查询
LlamaIndex 提供了一套底层组合式API,可实现对查询流程的精细化控制。
在这个示例中,我们自定义了检索器,使用不同的 top_k 数值,并添加了一个后处理步骤,要求检索到的节点必须达到最低相似度分数才能被包含在内。这样可以在有相关结果时为您提供大量数据,但如果没有任何相关内容,也可能导致没有数据返回。
from llama_index.core import Settings, SimpleDirectoryReader, VectorStoreIndex, get_response_synthesizer
from llama_index.core.indices.vector_store import VectorIndexRetriever
from llama_index.core.postprocessor import SimilarityPostprocessor
from llama_index.core.query_engine import RetrieverQueryEnginefrom my_llms.dash_scope_client import DashScopeClientSettings.llm = DashScopeClient.get_llm()
Settings.embed_model = DashScopeClient.get_embedder()documents = SimpleDirectoryReader(input_dir="../data/本草纲目").load_data()index = VectorStoreIndex.from_documents(documents)# 配置检索器
retriever = VectorIndexRetriever(index=index,similarity_top_k=10,
)# 配置响应合成器
response_synthesizer = get_response_synthesizer()# 组装查询引擎
query_engine = RetrieverQueryEngine(retriever=retriever,response_synthesizer=response_synthesizer,node_postprocessors=[SimilarityPostprocessor(similarity_cutoff=0.6)],
)response = query_engine.query("蒲公英有什么作用?")print(response)print("\n支持依据:")# print(response.source_nodes)
for idx, node in enumerate(response.source_nodes, 1):meta = node.metadataprint("-------------------------------------------")print(f"来源文件:{meta['file_path']}")print(f"来源文件名:{meta['file_name']}")print(f"文件内容:{node.text[:100]}...")print(f"相关度得分:{node.score:.4f}")print("\n")
让我们进一步详细说明如何自定义每个步骤:
配置检索器(Retriever)
retriever = VectorIndexRetriever(index=index,similarity_top_k=10,
)
配置节点后处理器(Node Postprocessors)
我们支持高级节点过滤与增强功能,可显著提升检索节点(Node)的相关性。该功能能有效减少大语言模型(LLM)的调用次数/时间成本,或直接提升响应质量。
例如:
关键词节点后处理器(KeywordNodePostprocessor):通过required_keywords(必需关键词)和exclude_keywords(排除关键词)过滤节点。
相似度后处理器(SimilarityPostprocessor):通过设置相似度分数阈值来过滤节点(仅支持基于嵌入向量的检索器)。
前后节点后处理器(PrevNextNodePostprocessor):根据节点关系为检索到的Node对象添加额外的相关上下文信息。
要配置所需的节点后处理器,请按以下步骤操作:
node_postprocessors = [KeywordNodePostprocessor(required_keywords=["Combinator"], exclude_keywords=["Italy"])
]
query_engine = RetrieverQueryEngine.from_args(retriever, node_postprocessors=node_postprocessors
)
response = query_engine.query("What did the author do growing up?")
响应合成配置说明
在检索器获取相关节点后,BaseSynthesizer(基础合成器)会通过整合这些信息来生成最终的响应。
你可以通过以下方式对其进行配置:
query_engine = RetrieverQueryEngine.from_args(retriever, response_mode=response_mode
)
目前我们支持以下选项:
default(默认):通过依次遍历每个检索到的 Node 来“逐条创建并优化”答案;每次对一个 Node 进行一次 LLM 调用。适合生成更详细的回答。
compact(紧凑):在每次 LLM 调用中将尽可能多的 Node 文本块“压缩”进提示词中,前提是不超过最大提示长度。如果有太多文本块无法放入单个提示词中,则会通过多个提示词来“创建并优化”答案。
tree_summarize(树形总结):给定一组 Node 对象和查询语句,递归地构建一棵树,并将根节点作为响应返回。适用于摘要类任务。
no_text(无文本):仅运行检索器以获取那些本应发送给 LLM 的节点,但并不实际调用 LLM。可以通过检查 response.source_nodes 来查看这些节点。Response 对象将在第5节中详细介绍。
accumulate(累加):给定一组 Node 对象和查询语句,对每个文本块分别执行查询并将结果累积成一个数组,最后返回所有结果拼接成的字符串。适用于需要对每个文本块单独执行相同查询的场景