欢迎光临
我们一直在努力

Day 015|Query Rewrite:用户问得含糊,Agent 怎么搜得精准

系列:100 天系统学习 AI Agent 开发
当前阶段:RAG、知识库与工具边界
今日目标:查询改写可以把口语化问题转成更适合检索的关键词、子问题或多路查询。

“这个怎么部署”不是一个完整查询

“这个”指哪个项目?部署到本地、容器还是云平台?用户想要安装命令,还是正在排查报错?如果直接把这六个字拿去搜,检索系统只能猜,召回一堆看似相关的部署页面。

Query Rewrite 的意义不是把句子润色得更正式,而是把隐含条件显式化,让检索器得到可执行查询。最危险的副作用也在这里:改写器可能替用户补了一个并不存在的条件,悄悄改变原意。

四种不同的改写动作

动作适用情况示例风险
规范化 同义词、缩写、错别字 “py 环境”→“Python 运行环境” 专有名词被误改
补上下文 代词依赖当前会话 “这个”→“当前讨论的 LangGraph 项目” 引用对象解析错
拆子问题 一个问题含多个信息需求 环境、命令、常见错误 子问题失去共同约束
多路查询 用户表达与文档措辞可能不同 “恢复任务”“checkpoint resume” 查询过多引入噪声和成本

改写前应先判断信息是否足够。缺少关键实体时,最好的 rewrite 是不改写,先追问。

把一句话拆成三条可检索查询

假设会话已明确对象为“某个 Python Agent 项目”,目标是“本地部署”。在这个前提下,可以得到:

  • 环境:该项目本地部署需要的 Python 版本、系统依赖和环境变量;
  • 命令:从安装依赖到启动服务的官方命令顺序;
  • 错误:本地启动时端口占用、缺少密钥、依赖冲突的排查方法。
  • 如果对象和环境没有被确认,上述内容只能作为模板,不能直接当真实查询。结构化输出可以把“已知”和“未知”分开:

    {
    "original_query": "这个怎么部署",
    "resolved_context": {
    "target": null,
    "environment": null,
    "version": null
    },
    "missing_fields": ["target", "environment"],
    "decision": "ask_user",
    "rewritten_queries": []
    }

    信息齐全后才生成查询:

    {
    "original_query": "这个怎么部署",
    "resolved_context": {
    "target": "example-agent",
    "environment": "local",
    "version": "2.4"
    },
    "decision": "search",
    "rewritten_queries": [
    "example-agent 2.4 本地部署 环境依赖",
    "example-agent 2.4 本地安装 启动命令",
    "example-agent 2.4 本地启动 常见错误"
    ]
    }

    一条防止意图漂移的流程

    #mermaid-svg-5ky4nVGfeob5qEDI{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-5ky4nVGfeob5qEDI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-5ky4nVGfeob5qEDI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-5ky4nVGfeob5qEDI .error-icon{fill:#552222;}#mermaid-svg-5ky4nVGfeob5qEDI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-5ky4nVGfeob5qEDI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-5ky4nVGfeob5qEDI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-5ky4nVGfeob5qEDI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-5ky4nVGfeob5qEDI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-5ky4nVGfeob5qEDI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-5ky4nVGfeob5qEDI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-5ky4nVGfeob5qEDI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-5ky4nVGfeob5qEDI .marker.cross{stroke:#333333;}#mermaid-svg-5ky4nVGfeob5qEDI svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-5ky4nVGfeob5qEDI p{margin:0;}#mermaid-svg-5ky4nVGfeob5qEDI .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-5ky4nVGfeob5qEDI .cluster-label text{fill:#333;}#mermaid-svg-5ky4nVGfeob5qEDI .cluster-label span{color:#333;}#mermaid-svg-5ky4nVGfeob5qEDI .cluster-label span p{background-color:transparent;}#mermaid-svg-5ky4nVGfeob5qEDI .label text,#mermaid-svg-5ky4nVGfeob5qEDI span{fill:#333;color:#333;}#mermaid-svg-5ky4nVGfeob5qEDI .node rect,#mermaid-svg-5ky4nVGfeob5qEDI .node circle,#mermaid-svg-5ky4nVGfeob5qEDI .node ellipse,#mermaid-svg-5ky4nVGfeob5qEDI .node polygon,#mermaid-svg-5ky4nVGfeob5qEDI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-5ky4nVGfeob5qEDI .rough-node .label text,#mermaid-svg-5ky4nVGfeob5qEDI .node .label text,#mermaid-svg-5ky4nVGfeob5qEDI .image-shape .label,#mermaid-svg-5ky4nVGfeob5qEDI .icon-shape .label{text-anchor:middle;}#mermaid-svg-5ky4nVGfeob5qEDI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-5ky4nVGfeob5qEDI .rough-node .label,#mermaid-svg-5ky4nVGfeob5qEDI .node .label,#mermaid-svg-5ky4nVGfeob5qEDI .image-shape .label,#mermaid-svg-5ky4nVGfeob5qEDI .icon-shape .label{text-align:center;}#mermaid-svg-5ky4nVGfeob5qEDI .node.clickable{cursor:pointer;}#mermaid-svg-5ky4nVGfeob5qEDI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-5ky4nVGfeob5qEDI .arrowheadPath{fill:#333333;}#mermaid-svg-5ky4nVGfeob5qEDI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-5ky4nVGfeob5qEDI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-5ky4nVGfeob5qEDI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5ky4nVGfeob5qEDI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-5ky4nVGfeob5qEDI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5ky4nVGfeob5qEDI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-5ky4nVGfeob5qEDI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-5ky4nVGfeob5qEDI .cluster text{fill:#333;}#mermaid-svg-5ky4nVGfeob5qEDI .cluster span{color:#333;}#mermaid-svg-5ky4nVGfeob5qEDI div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-5ky4nVGfeob5qEDI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-5ky4nVGfeob5qEDI rect.text{fill:none;stroke-width:0;}#mermaid-svg-5ky4nVGfeob5qEDI .icon-shape,#mermaid-svg-5ky4nVGfeob5qEDI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-5ky4nVGfeob5qEDI .icon-shape p,#mermaid-svg-5ky4nVGfeob5qEDI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-5ky4nVGfeob5qEDI .icon-shape .label rect,#mermaid-svg-5ky4nVGfeob5qEDI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-5ky4nVGfeob5qEDI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-5ky4nVGfeob5qEDI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-5ky4nVGfeob5qEDI :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    偏移

    覆盖

    原始问题 + 会话上下文

    提取实体与约束

    关键字段齐全?

    向用户追问

    生成单路或多路查询

    保留原问题并执行检索

    检查召回是否覆盖原意

    回退原查询或再次追问

    基于证据回答

    原始问题必须进入 trace。只保存改写结果,失败时无法判断是用户表达含糊、上下文解析错,还是检索本身有问题。

    怎么评测改写好不好

    不要给改写句子打“更自然”的分,而要看它是否改善检索:

    样本原查询命中改写后命中意图是否保持结论
    有明确对象的部署问题 待运行 待运行 待审查 比较 Top K
    缺少对象 不应搜索 不应搜索 应追问 测路由
    指定旧版本 待运行 待运行 必须保留版本 测约束
    专有名词拼写相近 待运行 待运行 不得擅自替换 测实体保护

    有些短查询已经足够精确,例如错误码、函数名、订单号,不必为了使用技术而改写。多路查询也要限制数量并去重,否则延迟和噪声会同时上升。

    我的阶段性判断

    好的 Query Rewrite 更像一个谨慎的翻译员:把口语转成检索语言,但不替用户做主。它要敢于保留未知、敢于追问,也要把每次补充条件的来源说清。下一篇进入 RAG 评测后,我会把“意图保持”与“检索提升”拆成两项,而不是只看最终答案。

    面试官会追问:Query Rewrite 会不会把用户意思改错?

    会,所以改写不能只追求“更专业”。我会让改写器同时输出 original_intent、rewritten_queries、assumptions 和 must_keep_entities;订单号、产品型号、时间范围等实体必须原样保留。信息不足时生成澄清问题,而不是偷偷补一个假设。

    评测时准备三类样本:口语省略、上下文指代、带精确实体的混合查询。除了检索 Recall,还要检查“意图保持率”。例如“它支持私有部署吗”若上一轮讨论的是 Qdrant,改写必须包含 Qdrant;若上下文里有两个产品,就应该追问。

    面试回答可以总结为:Query Rewrite 是受约束的检索适配器,不是自由发挥的文案润色器。

    今日检查清单

    • 永远保留 original_query 和上下文来源
    • 缺关键实体时先追问,不靠模型脑补
    • 版本、权限、租户等硬约束不能被改写掉
    • 用同一问题集比较改写前后检索命中
    • 多路查询有数量、去重和成本限制
    赞(0)
    未经允许不得转载:171主机测评 » Day 015|Query Rewrite:用户问得含糊,Agent 怎么搜得精准
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址