☰
LlamaIndex Response Modes 全解析:从 refine 到 tree_summarize 的响应合成机制与选型实战
2026/9/29 13:21:06 网站建设 项目流程

LlamaIndex Response Modes 全解析:从 refine 到 tree_summarize 的响应合成机制与选型实战

【免费下载链接】llama_indexLlamaIndex is the leading document agent and OCR platform项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index

本指南以 LlamaIndex 官方文档 Response Modes 为骨架,系统梳理检索增强生成(RAG)管线中「响应合成(Response Synthesis)」环节的九种响应模式:refine、compact、tree_summarize、simple_summarize、no_text、accumulate、compact_accumulate、generation与context_only。读完本文,你将掌握每种模式的内部运行机制、上下文窗口的利用策略、LLM 调用次数差异、适用场景与取舍,并能在QueryEngine与get_response_synthesizer中正确配置response_mode。

一、什么是 Response Mode:查询引擎的最后一步

在 LlamaIndex 中,一次查询通常经历两条路径:检索(retrieval)→ 合成(synthesis)。检索器从索引中召回与查询相关的节点(Node),而 Response Synthesizer 负责把这些检索到的文本块(text chunk)与用户查询组合成最终答案。response_mode正是决定「如何把 N 个文本块变成一段回答」的核心参数——它直接控制 LLM 调用次数、上下文利用效率与回答质量。

从源码看,ResponseMode是一个字符串枚举,定义于 type.py:

class ResponseMode(str, Enum): REFINE = "refine" COMPACT = "compact" SIMPLE_SUMMARIZE = "simple_summarize" TREE_SUMMARIZE = "tree_summarize" GENERATION = "generation" NO_TEXT = "no_text" CONTEXT_ONLY = "context_only" ACCUMULATE = "accumulate" COMPACT_ACCUMULATE = "compact_accumulate"

模式本身并不直接执行合成,而是通过工厂函数get_response_synthesizer(见 factory.py)映射到对应的合成器类,例如ResponseMode.REFINE → Refine、ResponseMode.COMPACT → CompactAndRefine、ResponseMode.TREE_SUMMARIZE → TreeSummarize等。这些合成器类都继承自BaseSynthesizer(base.py),对外统一暴露synthesize(query, nodes)与异步版本asynthesize,内部再把节点文本提取为text_chunks交给具体的get_response实现。

注:BaseSynthesizer.synthesize在节点为空时直接返回空响应(base.py#L245-L265),但Generation模式重写了该方法——无论是否有节点都会调用 LLM,详见后文。

二、refine:逐个文本块「创建并精炼」答案

refine是逐块迭代式的回答生成方式,其思想是「先答后改」:依次遍历每个检索到的文本块,不断用新块精炼已有答案。每次精炼都会产生一次独立的 LLM 调用,因此调用次数等于文本块数量。

工作细节

  • 第一个文本块与原始查询一起,使用text_qa_template提示词生成初始答案;
  • 随后,已有答案 + 下一个文本块 + 原始查询,使用refine_template提示词进行精炼;
  • 如此循环,直到所有文本块处理完毕;
  • 若某个文本块过大、无法与提示词一同放入上下文窗口,会通过TokenTextSplitter将其拆分(块与块之间允许一定重叠),拆分产生的新块会被追加进原块集合,同样用refine_template查询。

这段逻辑在 refine.py 的_run_refine_loop中有着完整的实现证据:

chunks_deque = deque( [ tc for chunk in chunks for tc in prompt_helper.repack( max_prompt, [chunk], llm=self._llm, padding=self._response_padding_size ) ] ) response = prev_response while chunks_deque: chunk = chunks_deque.popleft() if response is None: prompt_template = qa_template.partial_format(query_str=query_str) prompt_kwargs = make_qa_prompt_kwargs(chunk) # 首块走 text_qa_template else: prompt_template = refine_template.partial_format( query_str=query_str, existing_answer=response ) # 后续块走 refine_template ...

值得注意的细节:

  • 循环开始前,max_prompt = get_biggest_prompt([qa_template, refine_template]),即取两个提示词中较大者作为容量基准;
  • DEFAULT_RESPONSE_PADDING_SIZE = 500(refine.py#L49)为答案预留 token 空间;
  • 由于每次精炼时existing_answer都在变长,代码会对新块重新 repack(refine.py#L466-L478),若拆出多个块则推入 deque 前端继续处理;
  • Refine还支持structured_answer_filtering:通过StructuredRefineResponse(含query_satisfied与answer两个字段)过滤无关来源,实现流式场景下的提前终止。

适用场景:需要细节丰富、逐步推理的答案。缺点明显——每个块都要调用一次 LLM,成本与延迟最高。

三、compact:默认模式,先压缩再精炼

compact是 LlamaIndex 的默认响应模式。以 retriever_query_engine.py 中的RetrieverQueryEngine为例,其构造参数声明为response_mode: ResponseMode = ResponseMode.COMPACT;citation_query_engine.py 同样默认ResponseMode.COMPACT。工厂函数 factory.py 的默认值也是ResponseMode.COMPACT。

与 refine 的关系

compact在语义上与refine完全一致,差别只在于前置压缩:

  • 先把检索到的所有文本块尽量拼接、填满上下文窗口(容量取text_qa_template与refine_template中较大的提示词上限);
  • 若拼接后的文本仍然过长,则用TokenTextSplitter拆成尽量少的若干部分(允许重叠);
  • 每个部分视为一个「块」,送入refine合成器处理。

从类继承关系看,CompactAndRefine直接继承自Refine(compact_and_refine.py#L13),其核心就是重写get_response,在调用父类 refine 循环前先执行_make_compact_text_chunks:

def _make_compact_text_chunks(self, query_str, text_chunks): text_qa_template = self._text_qa_template.partial_format(query_str=query_str) refine_template = self._refine_template.partial_format(query_str=query_str) max_prompt = get_biggest_prompt([text_qa_template, refine_template]) return self._prompt_helper.repack( max_prompt, text_chunks, llm=self._llm, padding=self._response_padding_size )

结果:LLM 调用次数从「块数」降为「压缩后的块数」,通常大幅减少,同时保留 refine 的逐步精炼质量。这是绝大多数 RAG 查询场景(质量与成本均衡)的推荐默认选择。

四、tree_summarize:自底向上的树形摘要

tree_summarize面向多文档 / 长文本的全局总结场景。它不采用「精炼」思路,而是用summary_template提示词反复查询 LLM,直到只剩一个答案:

工作机制

  1. 把所有拼接后的文本块尽量填满上下文窗口(容量基于summary_template),必要时用TokenTextSplitter拆分(允许重叠);
  2. 对每个块/拆分分别用summary_template查询——注意这里没有 refine 步骤,得到同样数量的答案;
  3. 若只有一个答案,即为最终答案;
  4. 若有多个答案,则把这些答案自身视为新的文本块,递归执行「拼接/拆分适配/查询」流程,直到合并为一个最终答案。

源码 tree_summarize.py 的get_response完整印证了这一递归过程:

text_chunks = self._prompt_helper.repack(summary_template, text_chunks=text_chunks, llm=self._llm) if len(text_chunks) == 1: response = self._llm.predict(summary_template, context_str=text_chunks[0], ...) return response else: summaries = [self._llm.predict(summary_template, context_str=chunk, ...) for chunk in text_chunks] return self.get_response(query_str=query_str, text_chunks=summaries, ...) # 递归

类注释(tree_summarize.py#L20-L31)将其概括为:像建树一样自底向上递归合并摘要(leaves → root)。

关键参数

  • use_async:同步路径中是否用异步并发方式并行调用多个 chunk 的摘要请求(默认False);
  • summary_template/chat_summary_template:分别对应文本模式与多模态消息模式的摘要提示词,默认取DEFAULT_TREE_SUMMARIZE_PROMPT_SEL与CHAT_CONTENT_TREE_SUMMARIZE_PROMPT(factory.py#L65-L71)。

适用场景:对整批文档做全局总结、长文档归纳。相比simple_summarize,它不会因截断而丢失细节,代价是多次 LLM 调用(调用次数与树的层数相关)。

五、simple_summarize:单次调用,快速但有截断风险

simple_summarize是最快的摘要模式:把所有文本块拼接成一段文本,直接放进一个LLM 提示词中完成总结。

  • 优点:只有一次 LLM 调用,非常适合快速摘要;
  • 缺点:若拼接后的文本超过上下文窗口,会被PromptHelper.truncate直接截断,可能丢失尾部细节;更严重的是,若整体超出窗口太多,单次调用可能直接失败。

从 simple_summarize.py 的实现可以看到,它对所有块做"\n".join(text_chunks)拼接后,用self._prompt_helper.truncate(prompt=text_qa_template, text_chunks=[single_text_chunk], llm=self._llm)截断到可容纳范围,再交给self._llm.predict(text_qa_template, context_str=truncated_chunks)。

适用场景:文本总量明显小于上下文窗口的快速总结、原型验证。type.py中的枚举注释也明确警告:This will fail if the merged text chunk exceeds the context window size(type.py#L25-L29)。

六、accumulate 与 compact_accumulate:逐块独立作答

这两个模式解决的是「需要对每个文本块分别应用同一查询」的需求——例如逐文档抽取结构化信息、逐章节问答。

accumulate

  • 对每个文本块独立应用查询(使用text_qa_template),把各块响应累积进一个数组;
  • 最终返回所有响应的拼接字符串,默认分隔符为"\n---------------------\n",每条响应以Response {index + 1}: {item}的形式编号(见 accumulate.py#L67-L74);
  • 适合「同一查询分别作用于每个文本块」的场景。

实现细节(accumulate.py#L150-L172):

tasks = [ self._give_responses(query_str, text_chunk, use_async=self._use_async, **response_kwargs) for text_chunk in text_chunks ] outputs = self.flatten_list(tasks) return self._format_response(outputs, separator)

注意两点:

  1. accumulate不支持流式输出——if self._streaming: raise ValueError("Unable to stream in Accumulate response mode")(accumulate.py#L157-L158),compact_accumulate同样如此;
  2. use_async为True时,同步入口会通过run_async_tasks并发执行各块查询以降低延迟。

compact_accumulate

与accumulate行为一致,但会像compact一样先压缩提示词:先以text_qa_template为容量基准对文本块执行repack(compact_and_accumulate.py#L43-L55),再对压缩后的每个块执行 accumulate 查询。类继承关系为CompactAndAccumulate(Accumulate)(compact_and_accumulate.py#L10)。

结果:相比accumulate,LLM 调用次数更少,尤其当单块很小而块数很多时收益明显。

七、no_text、context_only 与 generation:三个特殊模式

no_text:只检索,不合成

no_text模式只运行检索器,把本应发给 LLM 的节点取回来,但不真正调用 LLM 合成答案。返回的response文本为空字符串,真正有价值的信息在response.source_nodes中——你可以通过遍历source_nodes检查实际被召回、本应送入 LLM 的节点内容。源码 no_text.py 的get_response直接return "",而合成器基类会把这些节点挂到响应的source_nodes上。

适用场景:调试检索质量、评估 RAG 检索阶段、构建「仅检索」pipeline、在真正调用 LLM 之前预览上下文内容。

context_only:仅拼接上下文

context_only不做任何 LLM 调用,只是把各文本块用"\n\n"连接后原样返回(context_only.py#L18-L24)。从源码结构看,它适合下游自行处理上下文、或把「检索 + 拼接」结果交给外部 LLM 管道的场景。

generation:忽略上下文,纯 LLM 生成

generation模式完全忽略检索到的文本块,仅用查询本身调用 LLM 生成回答(generation.py#L64-L83):

def get_response(self, query_str, text_chunks, **response_kwargs): del text_chunks # 文本块被直接丢弃 return self._llm.predict(self._input_prompt, query_str=query_str, **response_kwargs)

它使用simple_template(DEFAULT_SIMPLE_INPUT_PROMPT)作为输入提示词。与其它模式最大的不同是:Generation重写了synthesize,绕开基类「空节点早退」逻辑,即使没有检索到节点也必定调用 LLM(generation.py#L148-L208),保证在任何节点输入下都有输出。

适用场景:无需 RAG 上下文的纯生成任务、Agent 场景下的自由回答、或对检索质量没有把握时的兜底。

八、实战:如何配置与切换 response_mode

方式一:通过 QueryEngine 直接指定

最常见的用法是在构建查询引擎时传入response_mode(字符串或枚举均可,因为ResponseMode继承自str):

from llama_index.core.query_engine import RetrieverQueryEngine query_engine = RetrieverQueryEngine( retriever=retriever, response_mode="tree_summarize", # 或 ResponseMode.TREE_SUMMARIZE ) response = query_engine.query("请总结以上所有文档的核心观点")

方式二:通过 get_response_synthesizer 显式构建

需要精细控制提示词、流式、结构化输出时,可先构建合成器再传入查询引擎:

from llama_index.core.response_synthesizers import get_response_synthesizer from llama_index.core.query_engine import RetrieverQueryEngine synthesizer = get_response_synthesizer( response_mode="compact", # 默认即为 compact streaming=False, text_qa_template=my_qa_template, # 自定义 text_qa 提示词 refine_template=my_refine_template, ) query_engine = RetrieverQueryEngine(retriever=retriever, response_synthesizer=synthesizer)

get_response_synthesizer的完整签名(factory.py#L38-L60)包含:llm、prompt_helper、text_qa_template、refine_template、summary_template、simple_template、response_mode、streaming、use_async、structured_answer_filtering、output_cls、program_factory、verbose、multimodal等参数。未显式传入的llm、callback_manager、prompt_helper会回退到全局Settings(factory.py#L73-L88),其中PromptHelper会根据llm.metadata自动构建。

方式三:多模态 / 消息模式

所有合成器同时提供get_response(文本 chunks)与get_response_from_messages(ChatMessagechunks)两套接口;当multimodal=True时,BaseSynthesizer.synthesize会走消息路径(base.py#L274-L295),把节点的get_content_blocks内容封装成ChatMessage传入,对应使用chat_content_qa_template、chat_content_refine_template、chat_summary_template等对话式提示词。

响应对象与 source_nodes

合成结果由_prepare_response_output(base.py#L184-L229)封装:字符串 →Response,生成器 →StreamingResponse,结构化 LLM →PydanticResponse。无论哪种模式,检索到的节点都会附加到响应的source_nodes上,因此no_text模式也可以统一用response.source_nodes检查检索结果。

九、模式速查与选型建议

模式LLM 调用次数上下文利用典型场景备注
refine= 文本块数逐块需要逐步推理的细节问答成本最高
compact≈ 压缩后块数尽量填满窗口通用问答(默认)refine 的优化版
tree_summarize随树层数增长递归填满窗口多文档全局总结自底向上递归
simple_summarize1 次超限即截断快速摘要可能丢失细节/失败
accumulate= 块数逐块独立同一查询作用于每块不支持流式
compact_accumulate≈ 压缩后块数先压缩再逐块accumulate 的优化版不支持流式
no_text0 次不调用调试检索、仅检索管线答案在 source_nodes
context_only0 次原样拼接上下文透传无 LLM
generation1 次忽略上下文纯生成、Agent 兜底空节点也调用 LLM

选型建议:

  • 通用生产问答:compact(默认,质量与成本均衡);
  • 多文档总结归纳:tree_summarize;
  • 追求极致速度、文本可控:simple_summarize;
  • 需要逐文档结构化输出:accumulate/compact_accumulate(后者更省调用);
  • 检索质量排查与评测:no_text;
  • 不需要上下文的自由生成:generation。

十、深入阅读

  • 响应模式的枚举定义:type.py
  • 合成器工厂与默认提示词装配:factory.py
  • refine / compact 的实现与精炼循环:refine.py、compact_and_refine.py
  • 树形摘要与逐块累积:tree_summarize.py、accumulate.py、compact_and_accumulate.py
  • 特殊模式:no_text.py、context_only.py、generation.py
  • 合成器基类与响应封装:base.py
  • 查询引擎中的默认配置:retriever_query_engine.py、citation_query_engine.py
  • 各模式的单元测试:llama-index-core/tests/response_synthesizers/下的test_refine.py、test_tree_summarize.py、test_accumulate.py、test_compact_and_refine.py等,可作为理解每种模式预期行为的补充参考。

【免费下载链接】llama_indexLlamaIndex is the leading document agent and OCR platform项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询