查询改写与多查询检索
正在读取学习记录…
查询改写是把客户的说法整理成更适合资料库查找的词语,同时保留原意。多查询检索用几种表达分别查找,再合并候选。它适合口语、省略和资料术语不同的情况;把“退款”误改成“配送”会让后面的检索走错方向。
用退款资料观察改写
客户说“退钱几天到”。政策写“退款到账”,支付说明写“退回原支付渠道”。程序保留原问句,再按明确的同义短语规则生成两条查询,并把不同路径命中的同一文档合并。
固定资料P1讲退款到账,P2讲退回原支付渠道,P3讲退款申请,D1讲配送。每条资料用空格标出词项,查找时任一词项命中就进入候选。改写不会直接生成退款答案,只改变查找资料用的表达。
保存完整程序并运行
先在 PowerShell 创建并进入学习目录:
mkdir "F:\学习\l5-rewrite" -Force
cd "F:\学习\l5-rewrite"保存下面的文件后,在这个终端运行 dir 核对文件名,再执行本节给出的运行命令。
新建学习目录,例如 F:\学习\l5-rewrite。将下面代码完整保存为 rewrite_demo.py;代码包含固定资料和运行入口,保存后就能执行。
先在 PowerShell 创建并进入学习目录:
mkdir "F:\学习\l5-rewrite" -Force
cd "F:\学习\l5-rewrite"保存下面的文件后,在这个终端运行 dir 核对文件名,再执行本节给出的运行命令。
"""保留原始意图,以固定短语规则生成查询并合并文档。"""
DOCUMENTS = {
"P1": "退款 到账 三至五日",
"P2": "退回 原支付渠道 三至五日",
"P3": "退款 申请 订单编号",
"D1": "配送 两天",
}
def rewrite(question):
queries = [question.strip()]
# 短语对应写在规则中;范围仍为退款到账,保留原问句。
if "退钱" in question and "几天" in question:
queries.extend(["退款 到账", "退回 原支付渠道"])
return list(dict.fromkeys(query for query in queries if query))
def retrieve(queries, documents):
hits = {}
for query in queries:
terms = set(query.split())
for doc_id, text in documents.items():
if terms & set(text.split()):
# 按编号去重,同时保留命中路径供排查。
hits.setdefault(doc_id, []).append(query)
return sorted(hits.items())
def main():
question = "退钱几天到"
queries = rewrite(question)
print("原问题:" + question)
for number, query in enumerate(queries, 1):
print(f"查询{number}:{query}")
for doc_id, paths in retrieve(queries, DOCUMENTS):
print(f"候选 {doc_id};命中路径:" + " / ".join(paths))
if __name__ == "__main__":
main()在保存文件的文件夹地址栏输入 powershell 并回车,再执行:
macOS:先在 Python 篇的学习文件夹中打开终端,执行 source .venv/bin/activate。再输入 cd ,把当前例子的文件夹拖入终端并回车,运行下面的命令。
cd "F:\学习\l5-rewrite"
ls
python -X utf8 rewrite_demo.py使用其他目录时,把第一行换成自己的路径。dir 应能列出 rewrite_demo.py。终端输出如下:
原问题:退钱几天到
查询1:退钱几天到
查询2:退款 到账
查询3:退回 原支付渠道
候选 P1;命中路径:退款 到账
候选 P2;命中路径:退回 原支付渠道
候选 P3;命中路径:退款 到账原问句没有空格切词,无法直接命中按空格分开的业务词项;“退款 到账”找到P1和P3,“退回 原支付渠道”补回P2。候选列表每个编号只出现一次,后面记录是哪条查询找到它。P3只匹配退款而非到账,需要在重排时进一步区分。
多条查询怎样保持一个问题
rewrite首先保存原问题,再检查明确短语是否满足条件。dict.fromkeys保持第一次出现的顺序并去重。原问题留在第一条路径,便于对照改写是否改变了商品、时间、订单号或用户限制;额外路径补齐资料里常用的业务表达。
retrieve按每条查询分别做词项匹配,用文档编号合并命中。同一篇资料被三种说法找到,仍是一份资料;保留命中路径可以查清是哪条改写扩大了候选范围。只合并结果并不保证排序合理,可进一步用RRF名次融合或统一重排。
模型改写可以处理更多口语,但也会补出客户没有说的事实。订单号和金额宜作为必须保留字段;“不要退回原卡”这样的否定条件必须留住;问题缺少订单状态时,先追问比猜测已发货更可靠。改写后的查询应与原问题一起记录,权限过滤仍基于当前身份。
多轮对话中的“那要多久”需要结合前文确认“那”指退款还是配送。对复杂问题可拆成“申请条件”和“到账时间”分别查找,再组合依据。HyDE通常译为“假设文档嵌入”:先生成一段假设答案,把它转换成向量辅助检索,再用实际找到的原文组织答案。
查询数增加会扩大召回,也增加延迟、费用与无关候选。固定最多生成几条、保留候选上限,按标注问题集检查多查询是否补回正确依据。当前规则是确定性短语替换,结果可逐项复查;学习型改写需要另做意图保持与质量检查。
改一处输入,核对机制变化
将 main 的问题改为 "配送几天到",运行 python -X utf8 rewrite_demo.py。只保留一条原问句,没有退款改写和候选;当前按空格切词的检索也不会自动把口语切成“配送”。恢复退钱几天到再重跑,三条查询和P1、P2、P3恢复。
运行配套检查
把下面测试保存为 test_rewrite_demo.py,与主程序放在同一目录。5项测试检查上面的实际处理结果,也覆盖意图保留、路径去重和无命中。
查看测试代码:test_rewrite_demo.py
"""核对查询改写与多查询检索的正常结果与具体边界。"""
import unittest
import rewrite_demo as demo
class DemoTests(unittest.TestCase):
def test_original_query_is_preserved(self):
# 原问句保留,再补两种业务表达。
self.assertEqual(demo.rewrite("退钱几天到"), ["退钱几天到", "退款 到账", "退回 原支付渠道"])
def test_irrelevant_question_does_not_become_refund(self):
# 配送问题不会被改成退款问题。
self.assertEqual(demo.rewrite("配送几天到"), ["配送几天到"])
def test_multi_query_deduplicates_document_ids(self):
# 候选按编号去重,但保留两条命中路径。
hits = demo.retrieve(["退款", "退款 到账"], demo.DOCUMENTS)
self.assertEqual([key for key, _ in hits], ["P1", "P3"])
self.assertEqual(len(dict(hits)["P1"]), 2)
def test_rewrite_recovers_payment_channel_wording(self):
# 改写确实补回原支付渠道资料。
self.assertIn("P2", dict(demo.retrieve(demo.rewrite("退钱几天到"), demo.DOCUMENTS)))
def test_empty_and_unmatched_query(self):
# 空问题与无匹配问题不会编造候选。
self.assertEqual(demo.rewrite(" "), [])
self.assertEqual(demo.retrieve(["海外"], demo.DOCUMENTS), [])
if __name__ == "__main__":
unittest.main()python -X utf8 -m unittest -v test_rewrite_demo.py测试名称后应显示 ok,末尾是 Ran 5 tests 和 OK。
运行时怎样定位问题
| 看到的情况 | 检查与操作 |
|---|---|
| 找不到 rewrite_demo.py | 运行 dir,切换到保存主程序的目录 |
| 改写后出现用户没说的订单状态 | 保留原问题并检查事实字段,缺失状态先追问 |
| 同一资料出现多次 | 按稳定doc_id合并,并保留命中路径 |
| 多查询找回更多无关资料 | 限制路径数量,过滤后统一重排 |
多查询补回候选后,重排继续区分哪份真正回答当前问题。
参考阅读:LlamaIndex 源码与检索流程。阅读时可以沿文档、节点、索引和检索入口对照这里的数据流。
课后练习展开习题收起习题
正在读取本地记录…
不调用模型,不执行代码。简答、代码和场景题仅保存答案并展示自查标准,不生成评分、徽章或认证。输入内容不会自动保存,请点击提交;不要填写个人信息或密钥。
选择题 · 先判断边界
关于“查询改写与多查询检索”,哪种做法更稳妥?
对应知识点:L5-rewrite
简答题 · 解释失败
写出一个失败输入并说明记录字段。
对应知识点:L5-rewrite
代码题 · 边界测试
补充正常、边界、失败三组输入和预期结果。
对应知识点:L5-rewrite
场景题 · 结果未知
证据不足时如何设计状态和人工接管?
对应知识点:L5-rewrite
下一篇:重排、过滤与上下文压缩。
描述会发送给本站维护者,用于修订文章,保存90天。请只填写文章问题,省略个人信息、密钥和内部资料。