跳到内容

响应合成器#

概念#

响应合成器(Response Synthesizer)是使用用户查询和给定文本块集从大型语言模型(LLM)生成响应的组件。响应合成器的输出是一个 Response 对象。

生成响应的方法有很多种形式,从简单地迭代文本块到复杂地构建树。核心思想是简化使用 LLM 处理数据并生成响应的过程。

在查询引擎中使用时,响应合成器会在从检索器检索到节点后,以及运行任何节点后处理器后使用。

提示

对响应合成器在 RAG 工作流中的位置感到困惑?请阅读高级概念

使用模式#

独立使用响应合成器

from llama_index.core.data_structs import Node
from llama_index.core.response_synthesizers import ResponseMode
from llama_index.core import get_response_synthesizer

response_synthesizer = get_response_synthesizer(
    response_mode=ResponseMode.COMPACT
)

response = response_synthesizer.synthesize(
    "query text", nodes=[Node(text="text"), ...]
)

或者在创建索引后在查询引擎中使用

query_engine = index.as_query_engine(response_synthesizer=response_synthesizer)
response = query_engine.query("query_text")

您可以在下方找到所有可用响应合成器、模式以及如何构建自己的响应合成器的更多详细信息。

使用模式#

开始使用#

使用 response_mode 为查询引擎配置响应合成器

from llama_index.core.data_structs import Node
from llama_index.core.schema import NodeWithScore
from llama_index.core import get_response_synthesizer

response_synthesizer = get_response_synthesizer(response_mode="compact")

response = response_synthesizer.synthesize(
    "query text", nodes=[NodeWithScore(node=Node(text="text"), score=1.0), ...]
)

或者,更常见的是,在创建索引后在查询引擎中使用

query_engine = index.as_query_engine(response_synthesizer=response_synthesizer)
response = query_engine.query("query_text")

提示

要了解如何构建索引,请参阅索引

配置响应模式#

响应合成器通常通过 response_mode kwarg 设置来指定。

LlamaIndex 中已实现了多种响应合成器

  • refine:通过依次处理每个检索到的文本块来**_创建和完善_**答案。这会为每个节点/检索到的块进行一次单独的 LLM 调用。

详细信息:第一个块使用 text_qa_template 提示进行查询。然后,答案和下一个块(以及原始问题)使用 refine_template 提示进行另一次查询。如此循环,直到所有块都被解析。

如果一个块太大而无法适应窗口(考虑提示大小),则使用 TokenTextSplitter 进行拆分(允许块之间有一些文本重叠),并将(新的)附加块视为原始块集合的一部分(因此也使用 refine_template 进行查询)。

适用于更详细的答案。

  • compact(默认):类似于 refine,但会预先**_压缩_**(连接)块,从而减少 LLM 调用次数。

详细信息:将尽可能多的文本(从检索到的块连接/打包)填充到上下文窗口内(考虑 text_qa_templaterefine_template 之间的最大提示大小)。如果文本太长无法放入一个提示中,则根据需要将其分成多部分(使用 TokenTextSplitter 并因此允许文本块之间有一些重叠)。

每个文本部分都被视为一个“块”,并发送到 refine 合成器。

简而言之,它类似于 refine,但 LLM 调用次数更少。

  • tree_summarize:根据需要多次使用 summary_template 提示查询 LLM,以便查询所有连接的块,从而获得多个答案,这些答案本身又被递归地用作 tree_summarize LLM 调用中的块,依此类推,直到只剩下一个块,从而只有一个最终答案。

详细信息:尽可能多地连接块,使其适应使用 summary_template 提示的上下文窗口,并在需要时拆分它们(再次使用 TokenTextSplitter 和一些文本重叠)。然后,对每个结果块/拆分使用 summary_template 进行查询(没有 **_refine_** 查询!),并获得多个答案。

如果只有一个答案(因为只有一个块),那么这就是最终答案。

如果答案不止一个,则这些答案本身被视为块,并递归地发送到 tree_summarize 过程(连接/拆分以适应/查询)。

适用于摘要目的。

  • simple_summarize:截断所有文本块以适应单个 LLM 提示。适用于快速摘要目的,但由于截断可能会丢失细节。
  • no_text:仅运行检索器来获取本应发送给 LLM 的节点,而不实际发送它们。然后可以通过检查 response.source_nodes 来进行检查。
  • context_only:返回所有文本块的连接字符串。
  • accumulate:给定一组文本块和查询,将查询应用于每个文本块,同时将响应累积到一个数组中。返回所有响应的连接字符串。适用于需要针对每个文本块单独运行相同查询的情况。
  • compact_accumulate:与 accumulate 相同,但会像 compact 一样“压缩”每个 LLM 提示,并对每个文本块运行相同的查询。

自定义响应合成器#

每个响应合成器都继承自 llama_index.response_synthesizers.base.BaseSynthesizer。基本 API 非常简单,这使得创建自己的响应合成器变得容易。

也许您想自定义 tree_summarize 中每一步使用的模板,或者也许有新的研究论文详细介绍了一种生成查询响应的新方法,您可以创建自己的响应合成器并将其插入任何查询引擎或独立使用。

下面我们展示了 __init__() 函数以及每个响应合成器必须实现的两个抽象方法。基本要求是处理查询和文本块,并返回字符串(或字符串生成器)响应。

from llama_index.core import Settings


class BaseSynthesizer(ABC):
    """Response builder class."""

    def __init__(
        self,
        llm: Optional[LLM] = None,
        streaming: bool = False,
    ) -> None:
        """Init params."""
        self._llm = llm or Settings.llm
        self._callback_manager = Settings.callback_manager
        self._streaming = streaming

    @abstractmethod
    def get_response(
        self,
        query_str: str,
        text_chunks: Sequence[str],
        **response_kwargs: Any,
    ) -> RESPONSE_TEXT_TYPE:
        """Get response."""
        ...

    @abstractmethod
    async def aget_response(
        self,
        query_str: str,
        text_chunks: Sequence[str],
        **response_kwargs: Any,
    ) -> RESPONSE_TEXT_TYPE:
        """Get response."""
        ...

使用结构化答案过滤#

在使用 "refine""compact" 响应合成模块时,您可能会发现尝试使用 structured_answer_filtering 选项会很有益。

from llama_index.core import get_response_synthesizer

response_synthesizer = get_response_synthesizer(structured_answer_filtering=True)

structured_answer_filtering 设置为 True 后,我们的 refine 模块能够过滤掉与所提问题无关的任何输入节点。这对于涉及从外部向量存储检索文本块以回答给定用户查询的基于 RAG 的问答系统特别有用。

如果您使用的是支持函数调用(function calling)的 OpenAI 模型,此选项特别有用。不支持原生函数调用的其他 LLM 提供商或模型在生成此功能所依赖的结构化响应方面可能不太可靠。

使用自定义提示模板(带附加变量)#

您可能想要自定义我们的响应合成器中使用的提示,并在查询时添加附加变量。

您可以在 get_response**kwargs 中指定这些附加变量。

例如,

from llama_index.core import PromptTemplate
from llama_index.core.response_synthesizers import TreeSummarize

# NOTE: we add an extra tone_name variable here
qa_prompt_tmpl = (
    "Context information is below.\n"
    "---------------------\n"
    "{context_str}\n"
    "---------------------\n"
    "Given the context information and not prior knowledge, "
    "answer the query.\n"
    "Please also write the answer in the tone of {tone_name}.\n"
    "Query: {query_str}\n"
    "Answer: "
)
qa_prompt = PromptTemplate(qa_prompt_tmpl)

# initialize response synthesizer
summarizer = TreeSummarize(verbose=True, summary_template=qa_prompt)

# get response
response = summarizer.get_response(
    "who is Paul Graham?", [text], tone_name="a Shakespeare play"
)

模块#

有关更多详细信息,请参阅完整的模块指南