Day 14 / 共 20 天 · 第 3 周 检索与查询
后处理与提示词(精修检索结果)
检索回来的 Node 在喂 LLM 前,常要"精修"——过滤噪声、重排、补上下文。今天讲后处理器(尤其 rerank,提升 RAG 准确率最有效的技巧之一)和提示词模板。
📍 你在整条链的位置
⑤ 检索(粗筛)→
后处理(精排/过滤)→
⑥ 合成 D13
L01
后处理的位置
🤔 检索回来的 top-k 都好吗?
检索器返回 top-k(比如 10 个),但这 10 个未必都好——有些相关度低、有些重复、有些需要补上下文。直接全喂 LLM 会引入噪声。
💡 后处理夹在"检索"和"合成"之间,负责"精挑细选"
回想 Day 12:QueryEngine 的
retrieve = 检索 + _apply_node_postprocessors。好比初筛出 10 份简历,HR 再精挑 3 份。后处理是"两阶段检索"的精排阶段——对 RAG 质量提升显著。L02
统一接口,可串成链
postprocessor/ 的 BaseNodePostprocessor:核心 postprocess_nodes(nodes, query_bundle) -> List[NodeWithScore]——输入一批带分数的 Node,输出精修后的。
读法:又是"输入 Node、输出 Node"的统一接口——多个后处理器能串成链(先过滤 → 再重排 → 再取窗口)。像流水线,一步步精修。
L03
相似度过滤:宁缺毋滥
SimilarityPostprocessor:按 similarity_cutoff 过滤——只留分数高于阈值的。
📝 减少幻觉
库里根本没相关内容时,top-k 可能都是"矮子里拔将军"(score 都很低,如 0.3)。
设
设
similarity_cutoff=0.7 → 低于 0.7 的全扔 → 如果全被扔光,说明"知识库里没答案"(比硬喂无关内容让 LLM 瞎编强)。宁可少给,也不给无关的
给 LLM 无关内容 = 诱导它基于错误信息瞎答(幻觉)。相似度过滤守住这道门。
L04
rerank 重排:RAG 提效神器
🤔 向量检索快,但够准吗?
向量检索只看"向量相似",有时排序不够精。有没有办法既保召回、又保精度?
两阶段检索:向量粗检索取 top-20(快、召回全)→ rerank 精排取 top-5(准)。
为什么这是"提效神器"
向量检索一步到位不够准;rerank 用专门的重排模型(cross-encoder)把"查询+每个候选"一起看、给出更准的相关度分。粗检索保召回、rerank 保精度——这是提升 RAG 准确率最有效的技巧之一。代价是 rerank 要额外调模型(慢/花钱),但值得。
L05
上下文窗口(回收 Day 02/04)
MetadataReplacementPostProcessor / PrevNextNodePostprocessor:配合 Day 04 的 SentenceWindow、Day 02 的 PREV/NEXT 关系,把命中的小块"扩展"成带上下文的内容。
💡 "命中一句,给出一段"——解决精度与上下文的矛盾
SentenceWindow 检索(Day04):用单句嵌入检索(精准命中),但单句上下文不够。
MetadataReplacementPostProcessor 检索命中后,把内容替换成 metadata 里存的"前后窗口"(周围几句)——检索用小粒度(准),给 LLM 用大粒度(全)。读法:回收 Day 02/04 埋的关系伏笔——PREV/NEXT 在这里派上用场。
PrevNextNodePostprocessor 用关系取相邻块。这解决了"检索精度 vs 上下文完整"的经典矛盾。L06
prompts:RAG 的"说明书"
prompts/:PromptTemplate(字符串模板+变量)、ChatPromptTemplate(多轮消息模板)。默认 QA 提示词类似:
📝 默认 QA 提示词(简化)
运行时
以下是相关资料:\n{context_str}\n\n请只根据以上资料回答问题:{query_str}运行时
{context_str} 填检索到的 Node 文本、{query_str} 填用户问题。改提示词常有奇效(且便宜)
LLM 怎么用检索内容答题,全看提示词怎么写。加一句"如果资料里没有就说不知道,别编",能大幅减少幻觉。调 RAG 效果时,改提示词比换模型便宜得多、见效快。refine 模式(Day 13)还有专门的"精炼提示词"。
L07
PromptMixin:一行换掉深层提示词
prompts/mixin.py 的 PromptMixin:让组件(检索器/合成器/查询引擎)能暴露和替换自己用的提示词——get_prompts() / update_prompts()。
📝 把默认英文提示换成中文约束
query_engine.update_prompts({
"response_synthesizer:text_qa_template":
PromptTemplate("资料:{context_str}\n只依据资料用中文回答,没有就说'资料中未提及':{query_str}")
})
→ 一行改掉深层合成器的提示词,不用动源码。读法:回收 Day 11
BaseRetriever(PromptMixin,...)——很多组件都 mix 了它。提示词工程在应用层灵活做,不必改框架。提示词是 RAG 最易调、最见效的杠杆。L08
今日小结 + 动手
🧠 今天你应该能回答
- 后处理在检索和合成之间做什么?为什么是"精排"?
- 相似度过滤为什么能减少幻觉?
- rerank(两阶段检索)为什么是提效神器?粗检索保什么、精排保什么?
- 上下文窗口怎么解决"命中精度 vs 上下文完整"?提示词为什么最易调?
✋ 动手
cd /Users/bitmart/work/codes/github/llama_index/llama-index-core/llama_index/core
ls postprocessor/
grep -n "class SimilarityPostprocessor\|similarity_cutoff\|def postprocess_nodes" postprocessor/node.py | head
grep -n "DEFAULT_TEXT_QA_PROMPT\|class PromptTemplate" prompts/default_prompts.py prompts/base.py 2>/dev/null | head
明天预告 · Day 15(第 3 周收官):最后一块拼图——LLM 抽象。各家 LLM 怎么被统一封装(chat/complete/流式)、ChatMessage/角色、全局 Settings,以及工具调用(Agent 的基础)。