news 2026/11/5 5:35:24

RAG(4):问题改写、路由、Text2SQL、问题澄清

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RAG(4):问题改写、路由、Text2SQL、问题澄清

学习时整理,如有错误欢迎指正。
注:本笔记只做入门作用,代码仅为demo演示,未作架构 及业务处理,可能存在漏洞隐患

问题改写

在 RAG 的相似度检索中,检索效果很大程度取决于用户问题的表达方式。

用户输入的提问不可控,常存在模糊、信息缺失、上下文依赖等问题,难以直接高效匹配到高质量的相关文档块。

问题重写(改写)是主流的检索优化手段,目标是:让用户的自然语言提问,转化为更适合检索的查询表达,从而显著提高检索准确率和最终生成答案的质量。

主流实现策略共四种:富化、分解、多样化、回溯提示。


分解(Decompose)

将用户复杂、多步骤或包含多个子问题的查询,拆解成若干相互独立、更简单、更具体的子问题。每个子问题可以独立进行检索,最终将多个检索结果合并,用于回答原问题。

  1. 接收到用户请求后,先调用一次大模型,对原始问题进行逻辑解析和拆分
  2. 将拆分后的子查询列表分别进行检索,收集所有相关文档块
  3. 将所有文档块和原始问题一并输入给生成模型,得出最终答案

prompt:

# 角色 你是一名专业的查询逻辑分析专家。 # 任务 将给定的“用户原始问题”分解为一系列**相互独立、逻辑清晰**,且可单独用于检索的子查询列表。 你的输出必须是一个标准的JSON数组格式。 # 用户原始问题 {QUESTION} # 输出格式要求(JSON Array) [ "子查询1", "子查询2", "子查询3", "..." ] (不强制要求数组元素个数,可根据真实情况输出,至少保留1个) # 输出 请直接输出JSON数组,不要包含解释或多余的文字。

代码实现:

/** * 问题分解 * @param question 原始问题 * @return 子问题列表 */publicList<String>decompose(Stringquestion){PromptTemplatepromptTemplate=newPromptTemplate(DECOMPOSE_PROMPT);promptTemplate.add("QUESTION",question);Stringresult=chatModel.call(promptTemplate.create()).getResult().getOutput();returnJSON.parseArray(result,String.class);}

富化

在原始查询中添加上下文信息、背景知识或必要的限制条件,以消除歧义、补充缺失的信息,使查询更完整、更具体。

核心场景:指代消除

  • 示例:对话中用户问「他有什么特点?」,句中的 “他” 是指代表述,需要补充明确的指代对象,才能准确检索。

适用场景:

  • 用户表达模糊,存在指代性表述(省略了主语或关键信息)

  • 问题缺少历史对话细节,需补充时间、地点、实体等限制来缩小检索范围

  • 问题使用了专业术语的简称,需要补全完整概念

    实现流程:

  1. 接收到用户请求的问题后,先调用一次大模型,对用户的原始问题进行重写
  2. 将重写丰富过后的问题,再进行相似度检索,执行后续 RAG 流程

Promp:

# 角色 你是一个专业的问题重写优化器。 # 任务 根据提供的“对话历史”和“用户原始问题”,重写为一个独立、完整、且包含所有必要背景信息的新查询,用于RAG检索。 ## 对话历史: {CHAT_HISTORY} ## 原始问题: {QUESTION} # 输出 输出富化过后的新问题,不要包含多余的解释性内容

代码实现:

/** * 问题富化 * @param chatHistory 对话历史 * @param question 原始问题 * @return 富化后的完整问题 */publicStringenrich(StringchatHistory,Stringquestion){PromptTemplatepromptTemplate=newPromptTemplate(ENRICH_PROMPT);promptTemplate.add(CHAT_HISTORY,chatHistory);promptTemplate.add(QUESTION,question);Stringresult=chatModel.call(promptTemplate.create()).getResult().getOutput().getText();returnresult;}

多样化

对同一个用户查询生成多个语义相近或相关的变体,提升对知识库内容描述多样性的覆盖,从而增强召回率。

适用场景

  1. 描述风格不一致:文档的表述方式不同,例如有的写 “接口性能优化”,有的写 “提升接口响应速度”
  2. 用户表述差异性:不同用户的表达方式习惯不同

实现流程

  1. 接收到用户请求后,先调用一次大模型,为原始问题生成多个语义相近但措辞不同的查询变体
  2. 将原始查询和所有变体并行进行检索
  3. 汇聚所有的检索结果,得到全部文档块,去重后用于后续生成

Prompt

# 角色 你是一名专业的语义扩展专家。 # 任务 为给定的“原始问题”生成**3个**语义相同但**措辞完全不同、且利于检索**的查询变体,以提高检索的召回率。 你的输出必须是一个标准的JSON数组格式。 # 原始问题 {QUESTION} # 输出格式要求(JSON Array) [ "变体1", "变体2", "变体3" ] # 输出 输出富化过后的新问题,不要包含多余的解释性内容

代码实现

/** * 问题多样化 * @param question 原始问题 * @return 语义相近的查询变体列表 */ public List<String> diversify(String question) { PromptTemplate promptTemplate = new PromptTemplate(DIVERSIFY_PROMPT); promptTemplate.add(QUESTION, question); String result = chatModel.call(promptTemplate.create()).getResult().getOutput().getText(); return JSON.parseArray(result, String.class); }

回溯提示(Step-Back Prompting)

核心思想是先引导模型 “后退一步”,从具体问题中抽象出更一般的原理、概念或背景知识;再基于这些抽象信息进行推理或检索,最终回答原始问题。

典型场景

问题过于具体,知识库中没有完全匹配的条目,需要匹配通用规则 / 政策。

  • 原始具体问题:我老舅结婚,我可以请几天假去参加她的婚礼。
  • 抽象回溯问题:探亲假的请假政策是怎么样的?

实现流程:

  1. 将具体问题转化为更抽象的通用问题
  2. 使用这个抽象问题(或与原始问题结合)向知识库检索相关信息
  3. 将检索到的通用背景知识与原始具体问题结合,由 LLM 生成更准确、有深度的回答

Prompt

# 角色 你是一个擅长抽象思维和原理推理的专家。 # 任务 请根据用户提出的具体问题,先“后退一步”,将其转化为一个更通用、更本质的问题,聚焦于背后的原理、规律、概念或一般性知识,而不是具体细节。 # 原始问题 {QUESTION} # 输出 请只输出改写后的“后退问题”,不要解释,不要包含原始问题,也不要回答它。

代码实现

/** * 回溯提示(Step-Back) * @param question 原始具体问题 * @return 抽象后的通用问题 */publicStringstepBack(Stringquestion){PromptTemplatepromptTemplate=newPromptTemplate(STEP_BACK);promptTemplate.add(QUESTION,question);Stringresult=chatModel.call(promptTemplate.create()).getResult().getOutput().getText();returnresult;}

合并

# 角色 你是一名专业的查询优化专家,擅长对用户问题进行分析、重写、拆分、扩展和抽象,以提升RAG检索的准确率与召回率。 # 输入 ## 对话历史 {CHAT_HISTORY} ## 用户原始问题 {QUESTION} # 任务 围绕用户问题完成以下四项工作。 --- ## 任务1:查询重写(Rewrite) 结合对话历史,将用户当前问题改写为一个能够脱离上下文独立理解的完整查询。 要求: - 保留用户真实意图; - 补充必要背景信息; - 消除代词、省略、上下文依赖; - 不添加用户未表达的信息; - 输出一句完整的问题。 --- ## 任务2:查询分解(Decomposition) 如果问题包含多个相互独立的信息需求,则拆分为多个可以单独检索的子查询。 要求: - 每个子查询能够独立用于检索; - 子查询之间逻辑独立; - 不重复; - 如果问题本身无需拆分,则仅输出一个子查询。 --- ## 任务3:语义扩展(Semantic Expansion) 基于重写后的查询,生成3个语义完全一致但表达方式不同的查询变体。 要求: - 不改变原始含义; - 使用不同措辞; - 有利于提升Embedding召回率; - 不增加新的需求。 固定生成3条。 --- ## 任务4:Step-Back 问题 将用户的问题提升到更一般、更本质、更抽象的层次,形成一个能够帮助理解原问题的"后退问题"。 要求: - 聚焦原理、规律、概念、机制; - 不回答原问题; - 不包含具体场景; - 不只是简单改写,而是更高层抽象。 --- # 输出格式 严格输出以下JSON,不允许输出其它内容。 ```json { "rewrite": "独立完整的新问题", "decomposition": [ "子查询1", "子查询2" ], "expansion": [ "变体1", "变体2", "变体3" ], "step_back": "更加抽象、更本质的问题" } ``` # 输出要求 - 必须输出合法JSON。 - 所有字段均必须存在。 - decomposition 至少包含1项。 - expansion 固定包含3项。 - 不输出解释、标题、Markdown或其它文字。

查询路由

RAG 路由机制(Routing)

在实际 RAG 系统中,不同类型的问题往往需要不同的处理方式。

例如:

  • 不同的问题需要不同的 Prompt;
  • 不同的问题需要检索不同的数据源;
  • 不同的问题甚至需要调用不同的 Agent 或 Tool。

因此,在正式进入 RAG 检索之前,通常会增加一层Routing(路由),先由大模型理解用户意图,再决定后续执行路径。

整个 Routing 可以分为两类:

  • Prompt Routing(提示词路由)
  • Query Routing(数据源路由)

Prompt Routing(提示词路由)

Prompt Routing 指的是:

根据用户问题的意图,自动选择最适合的 System Prompt,再交给大模型回答。

如果所有问题都使用同一套 Prompt,很难兼顾不同业务场景。

因此,可以提前设计多套 Prompt,大模型先进行意图分类,再选择对应 Prompt,从而获得更加专业的回答。

整体流程如下:

本质上,就是:

先分类,再回答。

例如,一个医疗 AI 同时支持:

  • 病情咨询
  • 用药咨询

两种问题虽然都属于医疗领域,但需要不同的专业 Prompt。

实现:

可以通过多个@SystemMessage定义不同 Prompt,再增加一个意图识别方法。

@AiServicepublicinterfaceMedicalPromptRoutingService{@SystemMessage(""" 你是一名专业医生,请从疾病、症状、诊断等角度回答患者的问题。 """)Flux<String>doctorConsultation(StringuserMessage);@SystemMessage(""" 你是一名专业药学专家,请从药物适应症、剂量、副作用、禁忌等方面回答问题。 """)Flux<String>pharmacistConsultation(StringuserMessage);@SystemMessage(""" 判断用户问题属于哪种类型: 病情、诊断、症状 —— 返回 DOCTOR 药物、剂量、副作用 —— 返回 PHARMACIST 仅返回 DOCTOR 或 PHARMACIST。 """)StringdetermineConsultationType(StringuserMessage);}

Query Routing(数据源路由)

企业知识通常存储在不同的数据源中:

  • PDF、Word、Markdown 等文档
  • MySQL、PostgreSQL 等关系型数据库
  • Neo4j 等图数据库
  • Elasticsearch 等搜索引擎

不同的数据适合不同的检索方式。

例如:

文档知识适合向量检索;

组织架构、人物关系适合图查询;

统计数据、订单信息适合 SQL 查询。

因此,在检索之前,需要先判断用户的问题最适合查询哪一种数据源。

常见路由类型:

问题类型路由目标典型场景
文档问答、知识检索、语义搜索Vector DatabaseRAG 文档检索
人物关系、组织架构、知识图谱Graph Database实体关系查询
精确查询、统计分析、业务数据Relational DatabaseSQL 查询
实时网页信息Web Search新闻、天气、股票
外部系统能力API / Tool天气接口、订单系统、MCP Tool

实际项目中,Vector Database 通常作为默认路由,无法明确分类时优先进行语义检索。


实现

publicclassQueryRouteService{privatestaticfinalStringROUTE_PROMPT=""" 你需要判断用户问题最适合使用哪种数据源进行检索。 文档知识、语义搜索、知识问答 返回:VECTOR 实体关系、知识图谱、组织架构 返回:GRAPH SQL、统计分析、精确字段查询 返回:RELATIONAL 实时互联网信息 返回:WEB 调用外部接口或工具 返回:TOOL 无法确定时默认返回 VECTOR。 仅返回: VECTOR GRAPH RELATIONAL WEB TOOL 用户问题: {QUESTION} """;@AutowiredprivateChatModelchatModel;publicStringroute(Stringquery){PromptTemplatetemplate=newPromptTemplate(ROUTE_PROMPT);template.add("QUESTION",query);returnchatModel.call(template.create()).getResult().getOutput().getText();}}

查询构造

Text2SQL

在 RAG 的查询路由流程中,不同类型的数据源适配的查询方式不同:

  • 向量数据库:可直接基于用户自然语言做语义相似度检索
  • 关系型数据库、图数据库:无法直接识别自然语言,需要先将用户问题转换为对应的结构化查询语句(SQL / Cypher),再执行检索

查询构造就是这一环节的核心能力:根据路由结果,提前为目标数据源生成可执行的查询语句,最典型的落地场景是 Text-to-SQL(自然语言转 SQL)。

核心概念:

Text2SQL(自然语言到 SQL)是融合自然语言处理(NLP)与数据库查询技术的 AI 能力,目标是将用户用自然语言(中文、英文等)表达的查询意图,自动转换为语法正确、可执行的 SQL 查询语句,降低非技术人员访问数据的门槛,提升数据查询效率。

主流实现方式是基于大模型,结合表结构信息与约束规则,生成符合业务要求的 SQL。

提示词模板:

模板包含 3 个动态占位符,调用时实时替换:

  • {tables}:数据库表结构与字段注释信息,是大模型生成 SQL 的核心依据
  • {user_query}:用户的自然语言查询问题
  • {today}:当前日期,用于处理「最近几天」「今天」等时间类查询,保证时间逻辑准确

完整提示词:

# 角色 你是一个SQL专家。请根据以下表结构信息将用户问题转换为SQL查询语句。特别注意,你只能查询,不能做修改、删除等操作。 # 表结构信息 {tables} # 用户问题 {user_query} # 要求 1. 只返回SQL语句,不需要包含任何解释和说明 2. 确保SQL语法正确 3. 使用上下文中提供的表名和字段名 4. 如果根据所提供的表无法做查询,请直接返回空字符串"" # 其他说明 今天是:{today}

Java 完整实现代码(Spring AI + JdbcTemplate):

importorg.springframework.stereotype.Service;importorg.springframework.ai.chat.client.ChatClient;importorg.springframework.ai.chat.model.ChatModel;importorg.springframework.ai.prompt.Prompt;importorg.springframework.ai.prompt.PromptTemplate;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.util.StringUtils;importjavax.annotation.PostConstruct;importjava.text.SimpleDateFormat;importjava.util.Date;importjava.util.Map;@ServicepublicclassSqlQueryService{privateChatClientchatClient;@AutowiredprivateChatModelchatModel;@AutowiredprivateJdbcTemplatejdbcTemplate;@PostConstructpublicvoidinit(){chatClient=ChatClient.builder(chatModel).build();}

表结构常量(示例:员工信息表)

privatestaticfinalStringTABLES=""" CREATE TABLE "app"."staff_info" ( "id" bigint NOT NULL , "emp_id" varchar(64) NOT NULL , "name" varchar(128) NOT NULL , "job" varchar(128) NOT NULL , "entry_time" date NOT NULL , "gender" varchar(32) NOT NULL DEFAULT 'F'::character varying , "birthday" date NOT NULL , "educational_background" varchar(64) NOT NULL , "director_id" varchar(64) , "dept_id" varchar(64) NOT NULL , "duty" varchar(512) , "motto" varchar(1024) , "pic_url" varchar(512) , CONSTRAINT "pk_app_staff_info" PRIMARY KEY ("id") ) WITH ( FILLFACTOR = 100, OIDS = FALSE ); ALTER TABLE "app"."staff_info" OWNER TO llmentor; COMMENT ON COLUMN "app"."staff_info"."emp_id" IS '工号'; COMMENT ON COLUMN "app"."staff_info"."name" IS '姓名'; COMMENT ON COLUMN "app"."staff_info"."job" IS '岗位'; COMMENT ON COLUMN "app"."staff_info"."entry_time" IS '入职时间'; COMMENT ON COLUMN "app"."staff_info"."gender" IS '性别: M表示女性,F表示男性'; COMMENT ON COLUMN "app"."staff_info"."birthday" IS '出生年月日'; """;

提示词常量 + SQL 生成方法

privatestaticfinalStringTEXT_2_SQL_PROMPT=""" # 角色 你是一个SQL专家。请根据以下表结构信息将用户问题转换为SQL查询语句。特别注意,你只能查询,不能做修改、删除等操作。 # 表结构信息 {tables} # 用户问题 {user_query} # 要求 1. 只返回SQL语句,不需要包含任何解释和说明 2. 确保SQL语法正确 3. 使用上下文中提供的表名和字段名 4. 如果根据所提供的表无法做查询,请直接返回空字符串"" # 其他说明 今天是:{today} """;/** * 自然语言转SQL * @param query 用户自然语言问题 * @return 生成的SQL语句 */publicStringtext2sql(Stringquery){PromptTemplatepromptTemplate=newPromptTemplate(TEXT_2_SQL_PROMPT);SimpleDateFormatsdf=newSimpleDateFormat("yyyy-MM-dd");Promptprompt=promptTemplate.create(Map.of("user_query",query,"tables",TABLES,"today",sdf.format(newDate())));Stringsql=chatClient.prompt(prompt).call().content();System.out.println("sql:"+sql);returnsql;}

执行 SQL 查询方法

/** * 生成SQL并执行查询,返回封装结果 * @param query 用户自然语言问题 * @param clazz 结果映射的实体类 * @return 查询结果 */publicObjectsqlQuery(Stringquery,Classclazz){if(StringUtils.isBlank(query)){returnnull;}Stringsql=text2sql(query);if(StringUtils.isBlank(sql)){returnnull;}returnjdbcTemplate.queryForObject(sql,clazz);}}

整体执行流程

  1. 接收用户的自然语言查询请求
  2. 调用text2sql方法,传入表结构、当前日期与用户问题,通过大模型生成 SQL 语句
  3. 校验生成的 SQL 是否有效,无效则直接返回
  4. 调用sqlQuery方法,通过 JdbcTemplate 执行 SQL,返回结构化查询结果
  5. 查询结果可进一步作为上下文,送入大模型生成最终的自然语言回答

问题澄清

用户提问信息不全、模糊、有歧义时,AI 主动通过多轮对话提问补齐关键参数,拿到全部必要信息后再正式生成答案,

这个交互补齐信息的逻辑就是问题澄清。

实现:

主要依赖两个功能:

  1. 对话记忆存储:保存每一轮用户提问 + AI 回复,汇总已收集到的全部用户需求
  2. Prompt 规则约束:规定什么时候追问澄清、什么时候直接生成方案

系统提示词:

你是旅行规划助手,严格遵循下面规则: 一、对话语气:热情友好,像朋友自然聊天 二、核心流程分两个阶段 【阶段1:信息收集/问题澄清阶段】 1. 先梳理当前对话已获取信息,判断是否缺少核心必填字段:目的地、出行时间、出行人数、大致预算 2. 信息不全时,主动追问补齐,每次最多只提1~2个问题,话术自然,禁止生硬审问 开头模板参考:听起来很棒!具体想... 引导提问示例:这个地方我很推荐!你的出行预算大概在多少合适? 3. 禁止一次性抛出一堆问题,避免用户反感 【阶段2:规划生成阶段】 1. 当目的地、时间、人数、预算四项核心信息全部收集完整,停止追问,直接输出详细旅行方案 2. 方案必须包含:每日详细日程、住宿推荐、交通方案、游玩活动安排 3. 额外补充实用出行贴士、注意事项 三、硬性要求:全程基于历史对话已有信息作答,不能遗忘前面用户说过的内容
publicStringchatWithTraveler(StringmemoryId,StringuserInput){// 1. 获取当前对话记忆ChatMemorymemory=chatMemoryManager.getMemory(memoryId);// 2. 添加用户新消息memory.add(UserMessage.builder().text(userInput).build());// 3. 组装系统提示词+上下文Promptprompt=createTravelPrompt(memory.get(),userInput);// 4. 调用大模型Stringresp=chatClient.call(prompt).getResult().getOutput().getText();// 5. 保存AI回复到记忆memory.add(AssistantMessage.builder().text(resp).build());returnresp;}

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/11/5 5:33:37

NB-IoT 金融监管系统实录:设备续航 2 年、银行合规认证与监管效率提升 50%

摘要:做过一个"物联网 + 金融"的硬核项目:为苗木资产抵押贷款提供物联网监管——银行放款的前提是抵押物(苗木)真实存在且不被移走,我们把 NB-IoT 定位设备、胸径传感器和无人机航拍组合起来,实现位置监测、移位报警、胸径测量,设备续航做到 2 年,通过了农商…

作者头像 李华
网站建设 2026/11/5 5:33:35

日系车不吃香了?曾经风靡全国的日系车问题出在哪了?

曾经&#xff0c;日系车是中国市场上的香饽饽&#xff0c;凭借着皮实耐用的特性&#xff0c;以及省油的特征&#xff0c;日系车逐渐成为了中国市场上销量最好的合资品类&#xff0c;然而就在最近日系车不吃香的话题冲上热搜&#xff0c;曾经风靡全国的日系车问题到底出在哪了&a…

作者头像 李华
网站建设 2026/11/5 5:33:21

企业知识库问答系统设计要点:出处引用、无依据拒答与权限继承

面向企业制度文档的问答系统&#xff0c;和面向公开知识的问答产品&#xff0c;设计约束完全不同。前者一旦答错&#xff0c;代价是流程走错、责任落错人。所以工程行业的企业知识库&#xff0c;实际上是在解决三个工程问题&#xff1a;输出必须带可验证出处、没有依据时必须拒…

作者头像 李华
网站建设 2026/11/5 5:30:53

从一家奶茶店讲透扫码登录:手机怎么让电脑登录

目录 一、扫码登录要解决的真问题 二、完整流程&#xff1a;从生成二维码到换取登录态 三、动手写一个能跑的扫码登录后端 四、为什么是长轮询&#xff0c;而不是 WebSocket 五、一次领取&#xff1a;三层保护缺一不可 六、状态机&#xff1a;pending 到 confirmed 到 con…

作者头像 李华
网站建设 2026/11/5 5:28:54

电池后段测试到底处于电池生产的哪个环节?一文看懂电池制造流程

在了解电池后段测试设备时&#xff0c;很多刚进入新能源行业的朋友都会产生一个疑问&#xff1a;电池在生产过程中&#xff0c;究竟什么时候需要进行测试&#xff1f;测试设备是生产线上的最后一道工序&#xff0c;还是从电芯制造开始就一直参与其中&#xff1f;要理解这个问题…

作者头像 李华
网站建设 2026/11/5 5:28:39

2026继电器厂家选型指南:新能源行业选型标准

目录一、行业现状与选型背景二、新能源继电器的核心选型标准三、应用解析&#xff1a;电动汽车充电站四、应用解析&#xff1a;储能系统五、应用解析&#xff1a;太阳能发电与功率调节六、应用解析&#xff1a;数据中心与智能楼宇七、选型趋势展望八、总结与建议一、行业现状与…

作者头像 李华