响应合成器#
概念#
响应合成器
是从 LLM 中使用用户查询和给定的文本块集生成响应的工具。响应合成器的输出是一个 响应
对象。
这种方法可以采用多种形式,从简单的遍历文本块,到构建树形结构的复杂形式。主要思想是简化使用 LLM 在数据中生成响应的过程。
在查询引擎中使用响应合成器时,它会在从检索器检索到节点之后以及运行任何节点后处理器之后使用。
提示
对于响应合成器在流水线中的位置感到困惑?阅读 高级概念
使用模式#
单独使用响应合成器:
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(
"查询文本", nodes=[Node(text="文本"), ...]
)
或者在创建索引后在查询引擎中使用:
query_engine = index.as_query_engine(response_synthesizer=response_synthesizer)
response = query_engine.query("查询文本")
您可以在下面找到有关所有可用响应合成器、模式以及如何构建自己的更多详细信息。
使用模式#
入门指南#
使用 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(
"查询文本", nodes=[NodeWithScore(node=Node(text="文本"), score=1.0), ...]
)
或者,更常见的是,在创建索引后在查询引擎中使用:
query_engine = index.as_query_engine(response_synthesizer=response_synthesizer)
response = query_engine.query("查询文本")
提示
要了解如何构建索引,请参阅 索引
配置响应模式#
通常通过 response_mode
关键字参数设置响应合成器。
在 LlamaIndex 中已经实现了几种响应合成器:
refine
: 逐个创建和完善 答案,通过逐个处理每个检索到的文本块来实现。这使得每个节点/检索到的文本块都需要单独的 LLM 调用。
详情: 使用 text_qa_template
提示中的第一个文本块进行查询。然后使用答案和下一个文本块(以及原始问题)在另一个查询中使用 refine_template
提示。依此类推,直到解析完所有文本块。
如果某个文本块太大,无法适应窗口(考虑到提示的大小),则使用 TokenTextSplitter
进行拆分(允许一些文本重叠),并将(新的)额外文本块视为原始文本块集的一部分(因此也使用 refine_template
进行查询)。
适用于更详细的答案。
compact
(默认):类似于refine
,但是在此之前紧凑(连接)文本块,从而减少了 LLM 调用次数。
详情: 将尽可能多的文本(从检索到的文本块中连接/打包而成)塞入上下文窗口中(考虑到 text_qa_template
和 refine_template
之间的最大提示大小)。如果文本太长而无法适应一个提示中,它将被拆分为需要的部分(使用 TokenTextSplitter
,因此允许一些文本重叠)。
每个文本部分被视为一个“文本块”,并发送到 refine
合成器。
简而言之,就像 refine
,但是 LLM 调用次数更少。
tree_summarize
:使用summary_template
提示多次查询 LLM,以便查询所有连接的文本块,得到与连接的文本块数量相同的答案,这些答案本身又被递归地用作tree_summarize
LLM 调用中的文本块,依此类推,直到只剩下一个文本块,因此只剩下一个最终答案。
详情: 尽可能连接文本块以适应上下文窗口,使用 summary_template
提示,并在需要时进行拆分(再次使用 TokenTextSplitter
和一些文本重叠)。然后,对每个结果的文本块/拆分使用 summary_template
进行查询(没有完善查询!),并获得尽可能多的答案。
如果只有一个答案(因为只有一个文本块),那么这就是最终答案。
如果有多个答案,这些答案本身被视为文本块,并递归地发送到 tree_summarize
过程中(连接/拆分以适应/查询)。
适用于总结目的。
simple_summarize
:将所有文本块截断以适应单个 LLM 提示中。适用于快速 为了总结的目的,但可能由于截断而丢失细节。no_text
: 仅运行检索器以获取将发送到LLM的节点,而不实际发送它们。然后可以通过检查response.source_nodes
来检查它们。accumulate
: 给定一组文本块和查询,在对每个文本块应用查询的同时将响应累积到数组中。返回所有响应的连接字符串。当您需要针对每个文本块单独运行相同查询时很有用。compact_accumulate
: 与accumulate相同,但将“压缩”每个LLM提示类似于compact
,并针对每个文本块运行相同查询。
自定义响应合成器#
每个响应合成器都继承自llama_index.response_synthesizers.base.BaseSynthesizer
。基本API非常简单,这使得创建自己的响应合成器变得容易。
也许您想要自定义tree_summarize
中每个步骤使用的模板,或者可能有一篇新的研究论文详细介绍了生成对查询的响应的新方法,您可以创建自己的响应合成器并将其插入任何查询引擎中,或者单独使用它。
下面我们展示了__init__()
函数,以及每个响应合成器必须实现的两个抽象方法。基本要求是处理查询和文本块,并返回一个字符串(或字符串生成器)响应。
from llama_index.core import Settings
class BaseSynthesizer(ABC):
"""响应生成器类。"""
def __init__(
self,
llm: Optional[LLM] = None,
streaming: bool = False,
) -> None:
"""初始化参数。"""
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:
"""获取响应。"""
...
@abstractmethod
async def aget_response(
self,
query_str: str,
text_chunks: Sequence[str],
**response_kwargs: Any,
) -> RESPONSE_TEXT_TYPE:
"""获取响应。"""
...
使用结构化答案过滤#
在使用"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的问答系统特别有用。
如果您正在使用支持函数调用的OpenAI模型,此选项尤其有用。其他不具有本机函数调用支持的LLM提供商或模型可能在生成此功能依赖的结构化响应方面不够可靠。
使用自定义提示模板(带有额外变量)#
您可能希望自定义我们响应合成器中使用的提示,并在查询时添加额外的变量。
您可以在get_response
的**kwargs
中指定这些额外变量。
例如,
from llama_index.core import PromptTemplate
from llama_index.core.response_synthesizers import TreeSummarize
# 注意:我们在这里添加了额外的tone_name变量
qa_prompt_tmpl = (
"下面是上下文信息。\n"
"---------------------\n"
"{context_str}\n"
"---------------------\n"
"根据上下文信息和非先验知识,"
"回答查询。\n"
"请还以{tone_name}的语气写出答案。\n"
"查询:{query_str}\n"
"答案:"
)
qa_prompt = PromptTemplate(qa_prompt_tmpl)
# 初始化响应合成器
summarizer = TreeSummarize(verbose=True, summary_template=qa_prompt)
# 获取响应
response = summarizer.get_response(
"保罗·格雷厄姆是谁?", [text], tone_name="莎士比亚的一部戏剧"
)
模块#
有关更多详细信息,请参阅完整的模块指南。