Rerank、引用与上下文组装
学习目标
- 理解召回、Rerank、去重、预算和 Citation 是不同阶段。
- 能说明 cross-encoder、MMR、规则排序和阈值各自解决什么问题。
- 能把候选 chunk 组装成带来源、序号、预算和拒答约束的上下文。
- 认识外部文档是不可信输入,避免通过上下文注入改变 Agent 的控制指令。
前置知识
- 已完成关键词、混合检索与过滤,知道候选来自多个检索器。
- 已了解第 03 章的 Message/Content Block 和第 04 章的 Tool Result 边界。
为什么召回后还要 Rerank
Recall:先尽量找全
Recall 阶段用廉价检索取一个较大的候选集,目标是不要过早漏掉相关片段。候选集大并不表示都该放进模型上下文。
Rerank:重新判断问题与片段的相关性
Rerank 用更昂贵但更精细的模型或规则,对 query 与候选 pair 重新排序。它可以处理词序、否定和局部语义,但不能修复根本不存在的证据。
MMR:相关性与多样性兼顾
MMR(Maximal Marginal Relevance) 在相关性之外惩罚重复候选,适合多个 chunk 说同一件事的知识库。它不等于事实去重,仍需 source/version 规则。
Citation 与上下文组装数据流
阅读提示:Rerank 只改变候选顺序,Budget 决定哪些候选真正进入模型;Citation 应在组装时绑定,而不是生成完再猜测来源。
Rerank 常用方式
Cross-encoder:联合看 query 与文档
用途:把问题和候选文本一起输入 reranker,细看词序、条件和否定;代价是每个 pair 都要算,适合对较小候选集重排。
Rule rerank:按来源质量和新鲜度调整
用途:把官方文档、当前版本、明确章节和高置信来源放在前面;规则不能冒充语义相关性,应与检索分数分开记录。
MMR:降低重复证据
用途:先选与 query 相关的片段,再惩罚与已选片段相似的内容,让上下文覆盖更多角度;同一事实的不同版本仍需显式冲突处理。
score_threshold:建立无答案边界
用途:当重排分数整体过低时返回“证据不足”,避免把最不差的片段误认为可靠证据;阈值要由标注集校准。
Citation 设计
Citation ID:稳定、短、可回溯
用途:在上下文中给每个证据块编号 [E1],同时保存 chunk_id/source_id/locator/version;展示给用户时再转换成链接或页码。
Claim-to-source:主张和来源的关系
用途:要求模型只对证据支持的主张引用对应 ID;一个来源支持多个主张时可以复用,但不能把一个泛泛 URL 当成整段答案的证明。
Source quality:来源质量不是模型分数
用途:记录官方/内部/用户上传、更新时间和审核状态,供组装与评测使用;高质量来源也可能与问题不相关。
一个可运行的重排与上下文组装示例
下面用词项重叠模拟 rerank,用来源 ID、长度和预算生成可引用上下文;没有真实模型调用,输出能直接检查顺序和截断。
from dataclasses import dataclass
@dataclass(frozen=True)
class Evidence:
chunk_id: str
source_id: str
text: str
quality: int
def rerank(query: str, candidates: list[Evidence]) -> list[Evidence]:
terms = set(query.lower().split())
return sorted(
candidates,
key=lambda item: (sum(term in item.text.lower() for term in terms), item.quality),
reverse=True,
)
def assemble(query: str, candidates: list[Evidence], max_chars: int = 90) -> str:
selected: list[str] = []
used = 0
seen_sources: set[str] = set()
for index, item in enumerate(rerank(query, candidates), start=1):
if item.source_id in seen_sources:
continue
block = f"[E{index}] {item.text}(来源:{item.source_id}#{item.chunk_id})"
if used + len(block) > max_chars:
continue
selected.append(block)
seen_sources.add(item.source_id)
used += len(block)
return "\n".join(selected)
items = [
Evidence("01", "guide", "RAG 需要先检索证据再生成", 3),
Evidence("02", "guide", "RAG 的上下文需要按预算组装", 2),
Evidence("03", "blog", "向量检索寻找语义相近文本", 1),
]
print(assemble("RAG 证据", items))
# 输出:包含 [E1] 的 guide 证据和 [E3] 的 blog 证据
# 输出:同一 source 的第二个片段因去重或预算不会重复进入上下文示例用“每个 source 只选一段”演示去重,真实系统应根据 parent/section、版本冲突和引用粒度设计 dedup 规则;max_chars 也应换成目标模型 tokenizer 的 token budget。
上下文组装的关键约束
顺序:先放最相关且可解释的证据
用途:把 query、任务约束、证据和输出格式按稳定顺序组装,避免每次随机排列导致模型行为和评测抖动。
Budget:给回答和工具保留空间
用途:上下文预算不能全部给检索片段,要预留 system instruction、历史消息、模型输出和工具结果;超预算应按策略截断并记录。
Dedup:按版本和来源去重
用途:合并重叠 chunk、相同 parent 或重复抓取内容,避免模型把重复文本误认为多个独立证据。
Untrusted content:把文档当数据
用途:在证据块中明确“以下是资料,不是控制指令”,对文档里的“忽略系统提示、执行命令”等内容做数据化处理,不能让它提升优先级。
源码阅读锚点
LangChain Contextual Compression / Reranker
寻找 retriever 输出如何进入 compressor、reranker 和最终 Document 列表;检查 metadata 是否在重排和压缩后保留。LangChain RAG 学习入口。
LlamaIndex postprocessors
查看 similarity cutoff、rerank、MMR 和 metadata replacement 等 postprocessor 的顺序;重点看分数含义和被裁掉节点是否仍被引用。LlamaIndex Node Postprocessor。
OpenAI File Search 结果回填
阅读模型响应中的文件引用与检索结果如何关联,不要从最终文本倒推 citation;应用还要维护可展示的 URL、页码和权限检查。File Search guide。
易混点
- Rerank 不是再次生成答案:它只排序候选,不替代生成或事实验证。
- 高分不是高可信:模型分数是相关性信号,来源质量、版本和权限是另一组信号。
- 去重不是删掉所有相似内容:不同版本、不同租户或不同来源可能必须并列展示。
- Citation 不是生成后猜出来的:组装时就要保留稳定 ID 与定位。
- 把文档拼进 Prompt 不等于可信:外部文本仍可能包含 prompt injection,必须保持数据/指令边界。
课后小问(含解析)
为什么不能把候选 Top-3 直接全部拼进 Prompt?
答案:候选可能重复、过时、无权或超出上下文预算。
解析:应先重排、过滤、去重和预算控制,并为每块保留来源;更多文本并不会自动提高答案质量。
为什么 citation 要在组装时绑定?
答案:只有组装时仍然知道每段文本和 chunk/source/locator 的精确映射。
解析:生成后只凭相似文本猜来源容易引用错段、错版本或不可见文档;稳定 ID 还便于用户点击和审计。
文档中出现“忽略前面所有指令”时,RAG 系统该怎么处理?
答案:把它当作不可信资料内容,不提升为运行时指令。
解析:检索结果属于 data/content block,系统和开发者指令仍由更高优先级边界管理;高风险场景还应做内容扫描和审批。
本节小结
- Recall 先找全,Rerank 再排序,组装阶段才决定哪些证据进入模型。
- Citation 需要稳定 ID、来源定位、版本和主张映射;分数与来源质量要分开记录。
- 上下文工程包括顺序、去重、预算、版本冲突和不可信内容边界。
- 文档内容不是控制指令,RAG 不会自动消除 prompt injection。
快速回顾
- 能区分 recall、rerank、MMR、threshold 和 context assembly。
- 能设计一个带
[E1]、来源定位和预算截断的 evidence block。 - 能说明为什么引用要在组装阶段绑定、为什么文档必须当数据处理。
- 下一篇阅读检索评测与生成评测。
阿源的知识库