检索已经给出 8 条 Node。下一段失败通常不是「没召回」,而是怎么把块喂给 LLM:一次塞爆窗口、或每块单独问却把无关块炼进答案。ResponseSynthesizer 吃 query + list[NodeWithScore],吐 Response。response_mode 决定打包策略和调用次数,不决定召回质量。
段末注释:ResponseSynthesizer 在 Retriever 与(可选)Postprocessor 之后运行;
compact是默认模式,先拼接再按需 refine。

1. 一句话定位
| 维度 | 内容 |
|---|---|
| 角色 | 知识层的生成打包:多 Node → 一次或多次 LLM 调用 → 答案字符串 |
| 输入 → 输出 | (query: str, nodes: list[NodeWithScore]) → Response |
| 典型调用入口 | get_response_synthesizer(response_mode=...)、synthesize()、as_query_engine(response_mode=...) |
| 与 LangChain / LangGraph | 近邻是 Stuff / Refine / Map-reduce 链;LangGraph 不替代 mode,只决定何时调用本对象 |
出现背景:窗口有限而块数不定。LlamaIndex 用若干已实现 mode 覆盖「塞得下就塞、塞不下就拆、要综述就递归汇总」,避免每个项目手写 for 循环调 LLM。
2. 前置依赖与环境
1 | pip install -U llama-index-core llama-index-llms-ollama llama-index-embeddings-ollama |
- 合成器只认 LLM;本篇示例仍建一个小索引以便取出真实 Node(禁止 Fake LLM)
streaming=True时synthesize走生成器;aget_response/ 异步 QueryEngine 与同步 mode 语义相同
3. 实现逻辑
1 | 1. retriever.retrieve(q) → nodes |
字段级变形(compact):
1 | nodes = [methods块, lang块] |
refine 则是:第 1 块 QA → (答案, 第 2 块) 进 refine_template → 滚到第 n 块。
4. 原理说明
主轴是:mode 只改变「同一批 Node 被切成几次 LLM 调用」;不回头改检索名单。
1 | 1. synthesize 读取每个 Node 的 get_content(MetadataMode.LLM) |
get_response_synthesizer(response_mode=..., **kwargs) 出现在步骤 2。response_mode 可传字符串或 ResponseMode 枚举。
synthesize(query, nodes) 出现在步骤 3。底层调 get_response(query_str, text_chunks)。
structured_answer_filtering=True 挂在 refine/compact:让模型结构化声明「本块无关则跳过」。对 OpenAI function calling 较稳;本地小模型容易格式失败,默认别开。
常用 mode 对照:
| mode | LLM 次数(直觉) | 适合 |
|---|---|---|
compact |
少(能塞下则 1) | 单事实、默认 |
refine |
≈ 块数 | 要强迫读完每一块 |
tree_summarize |
对数级~线性 | 多段综述 |
no_text |
0 | 只评检索、只展示引用 |
context_only |
0 | 自己拼 prompt |
accumulate |
= 块数 | 每块独立抽取字段 |
5. 最小可运行示例
1 | pip install -U llama-index-core llama-index-llms-ollama llama-index-embeddings-ollama |
1 | from llama_index.core import Document, Settings, VectorStoreIndex, get_response_synthesizer |
接到引擎:
1 | qe = index.as_query_engine(response_mode="tree_summarize", similarity_top_k=3) |
6. 重要配置参数
| 参数(API 名) | 类型 / 默认值 | 功能说明 | 作用与影响 | 参考起点 / 常用范围 | 配置指导 |
|---|---|---|---|---|---|
response_mode |
str / enum,默认 compact | 选择打包与调用策略 | 直接决定延迟与费用 | 见上表 | 先 compact,评测不过再 refine |
llm |
LLM,默认 Settings.llm | 合成用模型 | 与 embed 无关;未设打 OpenAI | 与检索共用本地模型即可 | 事实题 temperature=0 |
streaming |
bool,默认 False | 是否流式 decode | 不改变 mode 的块调度 | 对前端 True | compact 流式最直观 |
structured_answer_filtering |
bool,默认 False | refine/compact 时丢掉无关块 | 本地模型易坏 JSON;OpenAI tool 较稳 | 默认关 | 先用 Postprocessor 滤,再考虑它 |
text_qa_template |
PromptTemplate | 首块/compact 的 QA 模板 | 不约束「仅资料」则抄参数记忆 | 加拒答句 | 与 refine_template 成对改 |
refine_template |
PromptTemplate | 后续块如何改写已有答案 | 写得弱则无关块覆盖正确答案 | 强调「无关则保持原答案」 | refine 模式必看 |
summary_template |
PromptTemplate | tree_summarize 每层问句 | 不适合单槽事实(剂量/物种) | 综述任务 | 不要拿来答 yes/no 事实 |
verbose |
bool | 打印每跳调用 | 只影响日志 | 调试 refine/tree 时 True | 生产关 |
7. 适用 / 不适用
| 维度 | 适用 | 不适用 |
|---|---|---|
| 任务形态 | 已有固定名单,只需改变「怎么读这些块」 | 名单本身是错的——改 Retriever/rerank,换 mode 无效 |
| 集成约束 | 本地 LLM 可多次调用 | 严格 1 次 LLM 预算——用 compact 并减小 k,或 context_only 自己拼 |
| 工程阶段 | 调生成侧忠实度 | 要工具/再检索——合成器没有这个钩子 |
8. 易踩坑
- 用 tree_summarize 答「剂量是多少」:递归摘要会丢掉数字。事实槽用 compact。
- refine + 噪声块:后块可以把正确物种改错;先 Similarity/rerank 再 refine。
- 把 no_text 当坏了:它本来就不生成;看
source_nodes才是目的。 - structured_answer_filtering + 小模型:解析失败表现为乱答案或异常,不是「过滤很强」。
小结
- 合成器不检索,只决定 Node 如何变成几次 LLM 调用。
- 默认 compact;逐块强迫阅读用 refine;综述用 tree_summarize;只评检索用 no_text。
- 模板与 filtering 改的是生成契约,召回名单不变。
- 换 mode 救不了空召回——那是 Retriever 的事。