查询改写与重排
查询改写与重排 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
读完本课你能在检索打不中时主动出手:把用户口语化、带代词的复杂问题改写成检索友好的查询,对初检候选做重排,并且用固定问题集量化「到底改进了多少、多花了多少」。
问题场景
用户在会话里问:「它支持断点续传吗?」——「它」是谁?只有翻回上文才知道是「导入的文档解析模块」。检索器拿到的是裸问题,「断点续传」四个字在资料里可能根本不出现(资料写的是「失败后从已完成步骤继续」)。A21 说中文 LIKE 是子串匹配、要贴原文用词,可用户不会替检索器选词。这一课给检索加两个前置/后置手段:改写(让查询配得上索引)和重排(让候选配得上回答)。
核心解释
**查询改写(query rewrite)**的三种常用手段,全部可以用一次便宜的小模型调用完成:
- 代词消解:把「它」还原成指代对象——需要把最近几轮对话一起给模型。
- 复合问题拆分:「登录接口的超时和重试怎么配」拆成两个子查询分别检索,结果合并。
- 关键词抽取:从自然语言长句抽出检索友好的短词——「怎么让页面不再闪」抽出「重复渲染」。
// 改写也是一次模型调用:输入历史+问题,输出改写后的查询(JSON 约束输出,见 A09)
const rewritePrompt = [
"根据对话历史把用户的最新问题改写成适合关键词检索的查询。",
"要求:还原代词指代;长句拆成多个短查询;只输出 JSON。",
'输出格式:{"queries": ["查询1", "查询2"]}',
].join("\n");
// 输入「它支持断点续传吗」→ 输出 {"queries": ["文档解析 断点续传", "失败后从已完成步骤继续"]}
**重排(rerank)**解决另一半问题:初检(关键词/向量)按字面或语义相似度排序,但「相似」不等于「能回答」。初检宽进(top 20),重排严出(top 5):用模型逐条判断「这个片段能不能回答改写后的问题」,按可用性重新排序。本站 search_courses 里其实已有规则版重排的影子——标题命中 +10、编号精确命中 +20、正文命中 +3,只是没用模型。
// 两段式的完整管道与成本结构
async function retrieve(question: string, history: Message[]) {
const queries = await rewrite(question, history); // 调用 #1:改写(每次提问都有)
const candidates = searchMany(queries, 20); // 初检:免费(本地索引)
const ranked = await rerank(question, candidates); // 调用 #2:重排(可只在候选多时做)
return ranked.slice(0, 5);
}
如何验证改进:拿 8-10 个固定问题(含代词问题、复合问题、口语改写问题各几个),改写前后各跑一遍,记录「期望片段是否进 top-5」。同时记账:每个问题多了几次模型调用、平均延迟加了多少。改进不明显的手段就该撤掉——不是越多层越好。
示例拆解
一次完整管道的效果对比(固定问题集的一行记录):
{
"question": "它支持断点续传吗?",
"rewrite": ["文档解析 断点续传", "失败后从已完成步骤继续"],
"raw_hit": { "top5_has_expected": false, "note": "原文无『断点续传』字样,LIKE 零命中" },
"rewritten_hit": { "top5_has_expected": true, "note": "改写后第二查询命中片段" },
"cost": { "extra_calls": 1, "extra_tokens": 420, "extra_ms": 800 }
}
关键点:
- 改写要带对话历史,但只带必要的:近 3-5 轮足够消解代词;全量历史只会烧 token(A02)。
- 多查询合并要控制数量:拆 2-3 个子查询是合理范围,拆得越多检索越散、合并去重越难。
- 重排的判断标准是「能否回答」,不是「是否相似」:写重排提示时明确「这个片段是否包含回答该问题所需的信息」。
- 成本要记账:改写让每次提问都多一次调用;重排只在候选可疑时才值得。数字摆出来再决定留不留。
动手任务
- 把本站助手的一个口语化问题(带代词)手动改写成 2 个检索友好的短查询,在资料库里验证:原问题零命中、改写后命中。
- 读
search_courses的打分规则(标题/编号/简介/正文加分),解释为什么「A15」这种编号查询它总能排第一——这就是规则版重排。 - 为固定问题集(5 个)做一张本课示例那样的对比表:改写前后的命中与新增成本,得出「改写值得留 / 不值得」的结论。
常见错误
- 改写丢了原问题:模型改写跑偏时,原始问题至少还能兜底——两路都检索,而不是替换。
- 重排prompt写成「打相似度分」:模型又开始评「像不像」,而不是「能不能答」,重排退化成二次检索。
- 每层都上模型调用:改写一次、重排一次、生成一次,一个提问三次调用——成本翻三倍,先测每层的边际收益。
- 改写后不做来源追溯:回答引用的是「改写后查询」的命中,用户问的是原话——引用展示时要把两者关系交代清楚(A22)。
检查与复盘
自查清单:
- 能说出改写三种手段各自针对什么问题形态
- 能解释重排和初检的排序标准差异(相似 vs 可用)
- 能用固定问题集给出「改进了多少、多花了多少」两个数字
参考资料
- OpenAI 文档:Prompt engineering —— 改写与重排提示的写法依据
- OpenAI 文档:Function calling —— 把改写/重排作为受控调用承载
- 下一课 A24 把 A18 以来的检索链路串成完整实战:导入到问答到删除。
读完这一课了?
标记完成后,工作台会安排接下来的学习。