LlamaIndex中的响应合成器
概念
响应合成器是使用用户查询和给定的一组文本块从LLM生成响应的组件。响应合成器的输出是一个响应对象。
这种方法可以有多种形式,从简单的迭代文本块到复杂的构建树。主要思想是通过LLM简化跨数据生成响应的过程。
在查询引擎中使用时,响应合成器在从检索器检索节点之后,以及运行任何节点后处理器之后使用。
提示
对响应合成器在RAG工作流中的位置感到困惑?阅读高级概念
使用模式
入门
使用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
关键字参数设置指定。
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
进行查询(没有refine查询!)并获取多个答案。
如果只有一个答案(因为只有一个块),那么它就是最终答案。
如果有多个答案,这些本身被视为块,并递归地发送给tree_summarize
过程(连接/拆分-适应/查询)。
适用于总结目的。 -
simple_summarize:将所有文本块截断以适应单个LLM提示。适用于快速总结目的,但由于截断可能会丢失细节。
-
no_text:仅运行检索器以获取本应发送给LLM的节点,而不实际发送它们。然后可以通过检查
response.source_nodes
进行检查。 -
context_only:返回所有文本块的连接字符串。
-
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):
"""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-based Q&A系统特别有用。
如果使用支持函数调用的OpenAI模型,此选项特别有用。其他不支持原生函数调用的LLM提供商或模型在生成此功能依赖的结构化响应时可能不太可靠。
使用自定义提示模板(带额外变量)
你可能想自定义响应合成器中使用的提示,并在查询时添加额外变量。
你可以在get_response
的**kwargs
中指定这些额外变量。
例如:
from llama_index.core import PromptTemplate
from llama_index.core.response_synthesizers import TreeSummarize
# 注意:我们在这里添加了一个额外的tone_name变量
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)
# 初始化响应合成器
summarizer = TreeSummarize(verbose=True, summary_template=qa_prompt)
# 获取响应
response = summarizer.get_response(
"who is Paul Graham?", [text], tone_name="a Shakespeare play"
)
通过这些步骤,你可以利用LlamaIndex的响应合成器功能,根据具体需求生成和处理LLM的响应。
LlamaIndex中的响应合成模块
以下是每个响应合成器的详细输入/输出。
API示例
以下展示了利用所有关键字的设置。
response_mode
指定使用哪个响应合成器service_context
定义用于合成的LLM和相关设置text_qa_template
和refine_template
是在各个阶段使用的提示use_async
目前仅用于tree_summarize
响应模式,以异步构建总结树streaming
配置是否返回流式响应对象structured_answer_filtering
启用主动过滤与给定问题无关的文本块
在synthesize/asynthesize
函数中,你可以选择提供额外的源节点,这些节点将被添加到response.source_nodes
列表中。
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="refine",
service_context=service_context,
text_qa_template=text_qa_template,
refine_template=refine_template,
use_async=False,
streaming=False,
)
# 同步
response = response_synthesizer.synthesize(
"query string",
nodes=[NodeWithScore(node=Node(text="text"), score=1.0), ...],
additional_source_nodes=[
NodeWithScore(node=Node(text="text"), score=1.0),
...,
],
)
# 异步
response = await response_synthesizer.asynthesize(
"query string",
nodes=[NodeWithScore(node=Node(text="text"), score=1.0), ...],
additional_source_nodes=[
NodeWithScore(node=Node(text="text"), score=1.0),
...,
],
)
你也可以直接返回字符串,使用较低级别的get_response
和aget_response
函数:
response_str = response_synthesizer.get_response(
"query string", text_chunks=["text1", "text2", ...]
)
通过这些API,你可以灵活地配置和使用LlamaIndex中的响应合成器,以满足不同的需求和场景。无论是同步还是异步,流式还是非流式,LlamaIndex都提供了丰富的选项来生成和处理LLM的响应。