意图感知 RAG 重排
RAG 检索结果的二次排序:根据用户当前意图(问定义、找动作、查原因)对 cosine similarity 结果做类型 boost,让最相关的知识类型排在前面。
#type / concept
#status / growing
#tech / ai
[!info] related notes
- 前置: RAG 知识库设计, Embedding 模型
- 并列: Reranker 模型
- 应用: 咨询 Agent 工作流
意图感知 RAG 重排
核心问题
纯 cosine similarity 搜出来的结果”语义相关”但不一定”意图匹配”。用户问”颈椎前伸怎么练”,语义最接近的可能是”颈椎前伸的定义”(都提到了”颈椎前伸”),但用户真正想要的是”矫正动作”。
设计思路
在 cosine similarity 基础上,根据用户意图给匹配的知识类型加分:
最终分数 = cosine_similarity + intent_boost
- cosine_similarity: 语义相关性(0~1)
- intent_boost: 意图匹配加分(0 或 0.1~0.2)
意图→知识类型映射
INTENT_KEYWORDS = {
"definition": ["什么是", "定义", "是什么意思"],
"self_check": ["自测", "判断", "筛查", "怎么测"],
"exercise": ["练什么", "动作", "训练", "拉伸", "改善", "矫正"],
"cause": ["原因", "为什么", "影响", "导致"],
"warning": ["风险", "疼", "不适", "禁忌", "就医"],
"habit": ["日常", "习惯", "坐姿", "低头"],
}
检测用户消息命中了哪些意图,对应 unit_type 的结果加 boost。
重排算法
def rerank(results: list[SearchResult], user_message: str) -> list[SearchResult]:
# 1. 检测意图
matched_types = set()
for intent_type, keywords in INTENT_KEYWORDS.items():
if any(kw in user_message for kw in keywords):
matched_types.add(intent_type)
# 2. 计算融合分数
for r in results:
boost = 0.15 if r.unit_type in matched_types else 0.0
r.rerank_score = r.similarity + boost
# 3. 排序
results.sort(key=lambda r: r.rerank_score, reverse=True)
return results
实际效果
用户问”颈椎前伸怎么练”,命中 exercise 意图:
| 排名 | cosine | unit_type | title | 最终分数 |
|---|---|---|---|---|
| 1 | 0.78 | exercise | 颈椎前伸矫正动作 | 0.93 |
| 2 | 0.85 | definition | 什么是颈椎前伸 | 0.85 |
| 3 | 0.72 | cause | 颈椎前伸的原因 | 0.72 |
没有重排时 definition 排第一(cosine 更高),有重排后 exercise 排第一(意图匹配)。
与 Reranker 模型的区别
| 维度 | 意图感知重排 | Reranker 模型 |
|---|---|---|
| 方法 | 关键词匹配 + boost | 交叉编码器精排 |
| 延迟 | <1ms | 10~100ms |
| 精度 | 粗粒度 | 细粒度 |
| 适用 | 有明确 unit_type 分类 | 通用场景 |
如果你的知识库有 unit_type 分类,意图感知重排是性价比最高的方案。如果没有分类,用 Reranker 模型。
设计要点
- 知识入库时必须标记 unit_type — 这是重排的前提
- boost 权重要适中 — 太大会让不相关的高分结果也排上来,太小没效果
- 可以叠加多意图 — 用户同时问”原因和怎么练”,同时 boost cause 和 exercise
- 可以做负 boost — 用户已经看过的类型降权,避免重复
常见错误
没有 unit_type 就想做意图重排
# ❌ 知识库没有分类,硬做重排
# 没有 unit_type,无法匹配意图
boost 权重太大
# ❌ boost = 0.5
# 一个 cosine=0.3 但类型匹配的结果会排到 cosine=0.7 的前面
# ✅ boost = 0.1~0.2
# 只在 cosine 接近时起决定作用