查询执行流程

完整的查询流程包含三个关键阶段:

  1. 检索阶段(Retrieval) 从索引(Index)中查找并返回与查询最相关的文档。如先前在索引部分所述,最常用的检索方式是"top-k"语义检索,但实际还存在多种其他检索策略。

  2. 后处理阶段(Postprocessing) 对检索到的节点(Nodes)进行可选操作,包括:重新排序、格式转换或过滤(例如要求节点必须包含关键词等特定元数据)。

  3. 响应合成阶段(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 RetrieverQueryEngine

from my_llms.dash_scope_client import DashScopeClient

Settings.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.metadata
    print("-------------------------------------------")
    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 对象和查询语句,对每个文本块分别执行查询并将结果累积成一个数组,最后返回所有结果拼接成的字符串。适用于需要对每个文本块单独执行相同查询的场景

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐