# RAG多轮对话检索设计:Query重写如何让“那它呢“变成完整问题

RAG多轮对话检索设计:Query重写如何让"那它呢"变成完整问题

导读:用户在 RAG 系统里问完"BGE-M3 怎么做混合检索?"之后,紧接着来一句"那参数怎么调?"------检索器拿到的只有这五个字,完全不知道"那"指的是什么,召回结果全是垃圾。这是多轮 RAG 系统最常见的崩溃场景。本文从这道高频面试题出发,完整拆解多轮对话检索的核心难点、Query 重写方案的完整实现、检索与生成两个阶段为什么要用不同输入、以及工业级落地的五个工程细节。文末附完整可运行的代码和踩坑清单。

适合读者

  • 正在搭建多轮对话 RAG 系统、遇到"后续提问检索效果差"问题的开发者
  • 准备 RAG 方向面试、需要系统回答"多轮对话检索设计"的同学
  • 想理解 Query 重写原理和代码实现的工程师
  • 需要在生产环境中做多轮上下文管理的架构师

阅读收益

  • 理解多轮 RAG 检索的核心难点:指代消解与语义补全
  • 掌握 Query 重写的完整代码实现(LLM 驱动 + 历史窗口截断)
  • 弄清"检索用改写后的 query、生成用原始对话"这一关键区分
  • 学会对话轮次截断、历史窗口管理、拒答兜底等工程细节
  • 获得一套可直接复用的 MultiTurnRAGService 完整实现

目录

  1. 问题背景:多轮对话为什么检索会崩
  2. 核心难点拆解:指代、省略、语义缺失
  3. Query重写:用小模型补全用户意图
  4. 关键区分:检索用改写query,生成用原始对话
  5. 完整代码实现:MultiTurnRAGService
  6. 对话历史窗口管理:截断策略与Token控制
  7. 工程落地:五个生产级细节
  8. 验证脚本:完整多轮对话测试
  9. 踩坑清单:多轮检索中的8个关键问题
  10. 面试速答版:30秒口述
  11. 总结与延伸
  12. 文末互动

1. 问题背景:多轮对话为什么检索会崩

1.1 一个真实的崩溃场景

假设你搭好了一个 RAG 知识库客服系统,第一轮对话很正常:

复制代码
用户:BGE-M3 怎么做混合检索?
AI:BGE-M3 可以同时输出稠密向量与稀疏向量,再用 RRF 融合结果......

第二轮,用户自然地追问:

复制代码
用户:那参数怎么调?

系统拿到"那参数怎么调?"这五个字,去向量库里检索。结果呢?召回的全是"参数调优""性能调优""模型参数"之类的泛化文档,跟 BGE-M3 混合检索的参数调优毫无关系。用户看到一段答非所问的回答,体验直接崩溃。

1.2 为什么会这样

检索器的每一次检索都是独立的。它没有记忆,不知道上一轮聊了什么。当你把"那参数怎么调?"丢给它时,它眼里只有这五个字的语义向量------而"那"这个代词在向量空间里几乎不携带任何信息。

复制代码
检索器的视角:
  "BGE-M3 怎么做混合检索?" → 向量清晰,召回精准
  "那参数怎么调?"          → 向量模糊,召回垃圾

问题的本质是:用户的后续提问是省略式、指代式的,直接拿当前这句话去检索会语义缺失。

1.3 为什么不能把全部历史塞进检索

一个直觉想法是:把历史对话全部拼起来去检索。但这会引入新问题:

python 复制代码
# 错误做法:把全部历史拼成检索query
query = "BGE-M3怎么做混合检索?BGE-M3可以同时输出稠密向量与稀疏向量,"
       "再用RRF融合结果。那参数怎么调?"

# 问题1:噪声膨胀------AI的回答也被拼进去了,检索器会被回答内容干扰
# 问题2:语义模糊------多句话拼在一起,向量语义变成多主题混合体
# 问题3:Token浪费------历史越长,embedding输入越长,成本越高

所以核心思路是:把历史对话信息合理融入检索环节,还原完整用户意图,同时不能把全部历史直接塞进检索。


2. 核心难点拆解:指代、省略、语义缺失

2.1 三种常见的问题形态

问题形态 示例 检索器看到什么 缺失什么
指代词 "那参数怎么调?" "那"+"参数"+"调" "那"指代BGE-M3混合检索
省略主语 "参数怎么调?" "参数"+"调" 主语BGE-M3混合检索
追问细节 "ef设置多少合适?" "ef"+"设置" ef是HNSW参数,属于向量库配置

2.2 为什么检索器无法自己解决

检索器(不管是向量检索还是 BM25)本质上是一个无状态的文本匹配器

复制代码
向量检索:把query转成向量 → 算余弦相似度 → 返回TopK
BM25检索:统计query中词频 → 算TF-IDF分数 → 返回TopK

两者都不具备"理解上下文"的能力。你必须在检索之前就把用户的真实意图还原出来。

2.3 正确的解法:Query重写

复制代码
原始流程(单轮):
  用户问题 → 检索 → 生成

多轮流程(加Query重写):
  用户问题 + 最近几轮历史 → Query重写 → 补全后的query → 检索 → 生成

Query 重写的核心是:调用一个 LLM(通常用小参数量模型,速度快、成本低),结合最近几轮对话历史,把当前问题补全为一个语义完整的独立查询


3. Query重写:用小模型补全用户意图

3.1 重写Prompt设计

Query 重写的提示词需要满足几个要求:

python 复制代码
REWRITE_PROMPT = """你是一个查询重写助手。你的任务是根据对话历史,把用户的最新提问重写为一个语义完整、可独立检索的查询。

规则:
1. 如果用户提问包含指代词(如"它""那""这个"),替换为具体对象
2. 如果用户提问省略了主语,从历史中补全
3. 重写后的查询应该是一个完整的问题,不包含对话历史的回答内容
4. 只输出重写后的查询,不输出任何解释

对话历史:
{chat_history}

用户最新提问:{question}

重写后的查询:"""

设计要点

  • 第3条规则非常关键------不能把AI的回答塞进重写后的query,否则检索器会被回答内容干扰
  • 第4条规则要求模型只输出结果,方便程序直接提取,不需要再做解析
  • chat_history 只传最近2-3轮,不是全部历史

3.2 重写效果对比

复制代码
输入:
  历史:用户问"BGE-M3怎么做混合检索?" → AI回答"BGE-M3可以同时输出稠密向量与稀疏向量......"
  当前:"那参数怎么调?"

重写后:
  "BGE-M3做混合检索时相关参数如何调优"

检索效果:
  原始query "那参数怎么调?" → Top5召回全是无关文档
  重写query "BGE-M3做混合检索时相关参数如何调优" → Top5召回精准命中

3.3 为什么用小模型

Query 重写是一个相对简单的语言理解任务,不需要复杂推理。用小参数量模型(如 deepseek-chat、qwen-turbo)即可:

对比项 小模型重写 大模型重写
延迟 200-500ms 1-3s
成本 高3-5倍
效果 重写质量足够 略好但收益有限
适用 生产环境首选 对话轮次多、指代复杂时

生产建议:默认用小模型,当重写质量不达标时再切大模型做兜底。


4. 关键区分:检索用改写query,生成用原始对话

4.1 最容易踩的坑

很多人在实现多轮 RAG 时,把改写后的 query 同时用于检索和生成。这是错的。

复制代码
错误流程:
  改写query → 检索 → 检索结果 + 改写query → 生成回答

正确流程:
  改写query → 检索 → 检索结果 + 原始对话历史 → 生成回答

4.2 为什么要区分

检索阶段用改写后的 query:因为检索器需要语义完整的查询才能精准召回。

生成阶段用原始对话历史:因为大模型有足够强的上下文理解能力,可以直接理解多轮对话。把改写后的 query 丢给大模型反而会丢失对话的连贯性,让回答显得生硬。

python 复制代码
# 检索阶段:用改写后的query
rewritten_query = rewrite_query(question, chat_history)
retrieved_docs = retriever.invoke(rewritten_query)

# 生成阶段:用原始对话历史 + 检索结果
# 不要用rewritten_query去生成!
messages = [
    SystemMessage(content="基于检索到的资料回答用户问题。"),
    # 把历史对话原样传入
    *chat_history,
    # 当前问题用原始question,不是rewritten_query
    HumanMessage(content=question),
    # 检索结果作为额外上下文
    SystemMessage(content=f"参考资料:\n{format_docs(retrieved_docs)}"),
]
answer = model.invoke(messages)

4.3 一句话记忆

检索用改写后的query,生成用原始对话。检索器无记忆需要补全,大模型有记忆能力不需要。


5. 完整代码实现:MultiTurnRAGService

5.1 整体架构

复制代码
MultiTurnRAGService
├── rewrite_query()          # Query重写(小模型驱动)
├── retrieve()               # 检索(用改写后的query)
├── generate()               # 生成(用原始对话历史)
├── ask()                    # 完整多轮问答入口
└── _format_history()        # 历史格式化

5.2 完整实现

python 复制代码
import os
from dataclasses import dataclass, field
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.documents import Document
from langchain_core.messages import (
    HumanMessage, AIMessage, SystemMessage
)
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings


# ========== 数据结构 ==========

@dataclass
class MultiTurnAnswer:
    answer: str                    # 最终回答
    rewritten_query: str           # 重写后的查询(用于调试)
    retrieved_docs: list           # 检索到的文档
    used_history_turns: int        # 实际使用的历史轮次
    latency_ms: int                # 总耗时


# ========== Query重写Prompt ==========

REWRITE_TEMPLATE = ChatPromptTemplate.from_messages([
    ("system", """你是一个查询重写助手。根据对话历史,把用户最新提问重写为语义完整、可独立检索的查询。

规则:
1. 指代词(它、那、这个)替换为具体对象
2. 省略主语时从历史补全
3. 重写后的查询是完整问题,不包含AI回答内容
4. 只输出重写后的查询,不输出任何解释

对话历史:
{chat_history}"""),
    ("human", "用户最新提问:{question}\n\n重写后的查询:"),
])


# ========== 多轮RAG服务 ==========

class MultiTurnRAGService:
    def __init__(
        self,
        vectorstore: Chroma,
        rewrite_model: ChatOpenAI | None = None,
        generate_model: ChatOpenAI | None = None,
        max_history_turns: int = 3,
        retrieve_top_k: int = 5,
    ):
        self.vectorstore = vectorstore
        # 重写用小模型(快、便宜)
        self.rewrite_model = rewrite_model or ChatOpenAI(
            model="deepseek-chat",
            api_key=os.getenv("LLM_API_KEY"),
            base_url=os.getenv("LLM_BASE_URL"),
            temperature=0,  # 重写要确定性
            max_tokens=100, # 重写不需要长输出
        )
        # 生成用大模型(质量好)
        self.generate_model = generate_model or ChatOpenAI(
            model="deepseek-chat",
            api_key=os.getenv("LLM_API_KEY"),
            base_url=os.getenv("LLM_BASE_URL"),
            temperature=0.3,
        )
        self.max_history_turns = max_history_turns
        self.retrieve_top_k = retrieve_top_k

        # 构建重写链
        self.rewrite_chain = REWRITE_TEMPLATE | self.rewrite_model | StrOutputParser()

    def _format_history(self, messages: list) -> str:
        """把对话历史格式化为文本,供重写Prompt使用"""
        lines = []
        for msg in messages[-self.max_history_turns * 2:]:  # 每轮2条(Human+AI)
            if isinstance(msg, HumanMessage):
                lines.append(f"用户:{msg.content}")
            elif isinstance(msg, AIMessage):
                # AI回答只取前100字,避免历史过长
                lines.append(f"助手:{msg.content[:100]}...")
        return "\n".join(lines)

    def rewrite_query(self, question: str, history: list) -> str:
        """Query重写:结合历史把当前问题补全"""
        if not history:
            # 没有历史,不需要重写
            return question

        chat_history = self._format_history(history)
        rewritten = self.rewrite_chain.invoke({
            "chat_history": chat_history,
            "question": question,
        })
        return rewritten.strip()

    def retrieve(self, query: str) -> list[Document]:
        """检索:用改写后的query"""
        docs = self.vectorstore.similarity_search_with_score(
            query, k=self.retrieve_top_k
        )
        # 过滤低分文档
        return [doc for doc, score in docs if score < 0.5]

    def generate(
        self, question: str, history: list, docs: list[Document]
    ) -> str:
        """生成:用原始对话历史 + 检索结果"""
        context = "\n\n".join(
            f"[{i+1}] {doc.page_content}" for i, doc in enumerate(docs)
        )

        messages = [
            SystemMessage(content=(
                "你是一个知识库问答助手。"
                "请只基于下面提供的参考资料回答用户问题。"
                "如果参考资料中没有相关信息,请如实说明。"
                "回答时标注引用编号,如[1][2]。"
            )),
        ]
        # 原始对话历史原样传入
        messages.extend(history[-self.max_history_turns * 2:])
        # 检索结果作为上下文
        messages.append(SystemMessage(content=f"参考资料:\n{context}"))
        # 当前问题用原始question
        messages.append(HumanMessage(content=question))

        response = self.generate_model.invoke(messages)
        return response.content

    def ask(
        self, question: str, history: list
    ) -> MultiTurnAnswer:
        """完整多轮问答入口"""
        import time
        start = time.time()

        # 第1步:Query重写
        rewritten = self.rewrite_query(question, history)

        # 第2步:检索(用改写后的query)
        docs = self.retrieve(rewritten)

        # 第3步:生成(用原始对话历史)
        if not docs:
            answer = "抱歉,知识库中没有找到相关资料,请尝试换个问法。"
        else:
            answer = self.generate(question, history, docs)

        latency = int((time.time() - start) * 1000)
        return MultiTurnAnswer(
            answer=answer,
            rewritten_query=rewritten,
            retrieved_docs=docs,
            used_history_turns=min(
                len(history) // 2, self.max_history_turns
            ),
            latency_ms=latency,
        )

5.3 代码结构说明

模块 职责 关键设计
rewrite_query 把省略式问题补全为完整查询 无历史时直接返回原问题,省一次LLM调用
retrieve 用改写后的query做向量检索 加分数过滤,低分文档不送入生成
generate 用原始对话历史+检索结果生成回答 历史原样传入,不做改写
ask 编排三步流程的入口 返回MultiTurnAnswer结构体,含调试信息

6. 对话历史窗口管理:截断策略与Token控制

6.1 为什么要截断

多轮对话如果不做截断,历史会无限膨胀:

复制代码
第1轮:1条Human + 1条AI = 2条消息
第10轮:10条Human + 10条AI = 20条消息
第50轮:50条Human + 50条AI = 100条消息

100条消息全部传给LLM,不仅Token爆炸,还会引入大量噪声------早期对话的话题跟当前问题可能完全无关。

6.2 三种截断策略

策略一:固定轮次截断(最常用)

python 复制代码
# 只保留最近3轮对话
max_history_turns = 3
recent_history = history[-(max_history_turns * 2):]

优点:简单直观,容易控制。缺点:不考虑消息长度,某轮回答特别长时Token可能超限。

策略二:Token预算截断

python 复制代码
from langchain_core.messages import get_token_usage

def truncate_by_tokens(messages, max_tokens=2000):
    """按Token预算截断历史"""
    total = 0
    kept = []
    for msg in reversed(messages):
        token_count = len(msg.content) // 3  # 粗略估算
        if total + token_count > max_tokens:
            break
        kept.insert(0, msg)
        total += token_count
    return kept

优点:精确控制Token。缺点:需要Token计算,略复杂。

策略三:滑动窗口+摘要(高级)

python 复制代码
# 最近3轮保留原文
# 更早的对话用LLM生成摘要
def build_context_with_summary(history, summary_model):
    recent = history[-6:]  # 最近3轮
    older = history[:-6]   # 更早的

    if older:
        summary = summary_model.invoke(
            f"请用100字总结以下对话的核心话题:\n{format(older)}"
        )
        return [SystemMessage(content=f"之前对话摘要:{summary}")] + recent
    return recent

优点:兼顾上下文完整性和Token控制。缺点:多一次LLM调用,增加延迟。

生产建议:默认用策略一(固定3轮),当单轮回答较长时切换到策略二。

6.3 历史格式化中的细节

python 复制代码
def _format_history(self, messages: list) -> str:
    lines = []
    for msg in messages[-self.max_history_turns * 2:]:
        if isinstance(msg, HumanMessage):
            lines.append(f"用户:{msg.content}")
        elif isinstance(msg, AIMessage):
            # AI回答只取前100字
            lines.append(f"助手:{msg.content[:100]}...")
    return "\n".join(lines)

为什么要截断AI回答:Query重写只需要知道"上一轮聊了什么话题",不需要AI回答的完整内容。截断到100字足够提供上下文,同时大幅减少Token消耗。


7. 工程落地:五个生产级细节

7.1 重写失败的兜底

python 复制代码
def rewrite_query(self, question: str, history: list) -> str:
    if not history:
        return question
    try:
        chat_history = self._format_history(history)
        rewritten = self.rewrite_chain.invoke({
            "chat_history": chat_history,
            "question": question,
        })
        rewritten = rewritten.strip()
        # 兜底:如果重写结果为空或异常,回退到原始问题
        if not rewritten or len(rewritten) < 3:
            return question
        return rewritten
    except Exception:
        # LLM调用失败,回退到原始问题
        return question

原则:Query重写是"锦上添花",不能因为重写失败导致整个系统不可用。

7.2 重写结果缓存

python 复制代码
from functools import lru_cache
import hashlib

def _cache_key(question: str, history: list) -> str:
    # 把问题+历史哈希成缓存key
    content = question + "".join(m.content for m in history[-6:])
    return hashlib.md5(content.encode()).hexdigest()

# 高频相同问题直接命中缓存,跳过重写LLM调用

场景:用户反复问类似问题时,重写结果可以复用,省掉LLM调用成本。

7.3 第一轮不重写

python 复制代码
def rewrite_query(self, question: str, history: list) -> str:
    if not history:
        # 第一轮对话,问题本身是完整的,不需要重写
        return question
    # ...后续轮次才重写

原因:第一轮没有历史上下文,用户的问题通常是完整的。此时调LLM重写是浪费。

7.4 检索与生成用不同模型

python 复制代码
# 重写:小模型,temperature=0,max_tokens=100
self.rewrite_model = ChatOpenAI(
    model="deepseek-chat",
    temperature=0,    # 确定性输出
    max_tokens=100,   # 重写结果不需要长
)

# 生成:可以用更大模型,temperature=0.3
self.generate_model = ChatOpenAI(
    model="deepseek-chat",
    temperature=0.3,  # 允许一定创造性
)

原因:重写是"理解"任务,需要确定性;生成是"表达"任务,允许适度创造。

7.5 对话历史持久化

python 复制代码
# 对话历史不能只存在内存里
# 服务重启后历史丢失,多轮对话直接断档

# 生产方案:持久化到数据库
class ConversationStore:
    def save_message(self, session_id: str, role: str, content: str):
        """保存一条对话消息"""
        # 写入conversations表
        pass

    def load_history(self, session_id: str, limit: int = 6):
        """加载最近N条对话"""
        # 从conversations表读取
        pass

关键:多轮对话的"多轮"依赖历史。历史丢了,重写就没了依据。


8. 验证脚本:完整多轮对话测试

python 复制代码
"""多轮对话RAG验证脚本"""
from dotenv import load_dotenv
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_core.messages import HumanMessage, AIMessage
from multi_turn_rag import MultiTurnRAGService

load_dotenv()


def main():
    # 初始化向量库(假设已建好索引)
    embeddings = OpenAIEmbeddings(
        model="text-embedding-v4",
        api_key=os.getenv("EMBEDDING_API_KEY"),
        base_url=os.getenv("EMBEDDING_BASE_URL"),
    )
    vectorstore = Chroma(
        persist_directory="./chroma_db",
        embedding_function=embeddings,
    )

    rag = MultiTurnRAGService(vectorstore=vectorstore)

    # 模拟多轮对话
    history = []

    conversations = [
        "BGE-M3 怎么做混合检索?",       # 第1轮:完整问题
        "那参数怎么调?",                # 第2轮:指代词"那"
        "ef设置多少合适?",              # 第3轮:省略主语
        "和IVF_FLAT比哪个快?",         # 第4轮:省略上下文
    ]

    for question in conversations:
        print(f"\n{'='*60}")
        print(f"用户:{question}")

        result = rag.ask(question, history)

        print(f"重写后:{result.rewritten_query}")
        print(f"检索到:{len(result.retrieved_docs)} 条文档")
        print(f"回答:{result.answer}")
        print(f"耗时:{result.latency_ms}ms")
        print(f"使用历史:{result.used_history_turns} 轮")

        # 更新历史
        history.append(HumanMessage(content=question))
        history.append(AIMessage(content=result.answer))

    # 验证点
    print(f"\n{'='*60}")
    print("验证要点:")
    print("1. 第1轮不触发重写(无历史)")
    print("2. 第2轮重写后应包含'BGE-M3混合检索'")
    print("3. 第3轮重写后应包含'HNSW参数'")
    print("4. 第4轮重写后应包含'BGE-M3 HNSW vs IVF_FLAT'")
    print("5. 生成阶段用的是原始对话,不是重写后的query")


if __name__ == "__main__":
    import os
    main()

8.1 预期输出

复制代码
============================================================
用户:BGE-M3 怎么做混合检索?
重写后:BGE-M3 怎么做混合检索?     ← 第1轮不重写
检索到:5 条文档
回答:BGE-M3支持同时输出稠密向量和稀疏向量...
耗时:820ms
使用历史:0 轮

============================================================
用户:那参数怎么调?
重写后:BGE-M3做混合检索时相关参数如何调优  ← 指代消解
检索到:5 条文档
回答:BGE-M3混合检索主要涉及以下参数...
耗时:1150ms
使用历史:1 轮

============================================================
用户:ef设置多少合适?
重写后:BGE-M3混合检索HNSW索引ef参数推荐值  ← 主语补全
检索到:4 条文档
回答:HNSW的ef参数建议设置为...
耗时:980ms
使用历史:2 轮

9. 踩坑清单:多轮检索中的8个关键问题

序号 问题 现象 原因 解决方案
1 检索用原始query 后续轮次召回全是垃圾 指代词在向量空间无语义 必须Query重写后再检索
2 生成用改写query 回答不连贯、生硬 改写丢失对话语境 生成阶段用原始对话历史
3 全部历史塞入重写 Token爆炸、重写质量下降 历史过长噪声多 截断到最近2-3轮
4 AI回答未截断 重写Prompt超长 AI回答可能很长 重写历史中AI回答截断到100字
5 第一轮也重写 浪费一次LLM调用 无历史时问题已完整 history为空时跳过重写
6 重写失败无兜底 系统直接报错 LLM调用不稳定 try-except回退到原始问题
7 历史不持久化 重启后多轮断裂 历史只存内存 持久化到数据库
8 重写和生成用同一模型 延迟高或质量差 两阶段需求不同 重写用小模型,生成用大模型

10. 面试速答版:30秒口述

多轮RAG检索最大的问题是用户存在指代、省略,直接拿当前问句检索效果很差。主流方案是Query重写:结合最近几轮对话,调用LLM把当前问题补全为完整查询,拿补全后的语句做知识库检索。注意区分:检索用改写后的query,最终大模型生成回答仍然要带上原始完整对话。同时要限制对话轮次做截断,防止上下文无限膨胀。

面试加分点(如果面试官追问):

  • 重写用小模型,生成用大模型,分离关注点
  • 重写失败要有兜底,回退到原始问题
  • 第一轮对话不需要重写(无历史上下文)
  • 高频问题加重写结果缓存,省LLM调用
  • 对话历史要持久化,不能只存内存

11. 总结与延伸

11.1 核心知识点回顾

复制代码
多轮RAG检索设计 = Query重写 + 检索生成分离 + 历史窗口管理

Query重写:
  历史 + 当前问题 → 小模型 → 完整查询

检索生成分离:
  检索用改写query(检索器无记忆,需要补全)
  生成用原始对话(大模型有上下文理解能力)

历史窗口管理:
  固定3轮截断(生产默认)
  Token预算截断(长回答场景)
  滑动窗口+摘要(高级方案)

11.2 延伸方向

  • 意图识别 + 路由:先用LLM判断当前问题是否需要检索,闲聊类直接跳过RAG
  • 多路召回 + Query扩展:对改写后的query再做同义词扩展,提升召回率
  • LangGraph状态机:用LangGraph的条件路由替代关键词路由,实现更智能的多轮对话流程控制
  • 对话摘要压缩:超过N轮后自动触发摘要,把早期对话压缩为100字摘要

12.文末互动

你在多轮对话 RAG 系统中遇到过哪些"答非所问"的情况?是用 Query 重写解决的,还是有其他方案?评论区聊聊你的实战经验。

思考题:如果一个用户在10轮对话后突然问了一个跟前面完全无关的新话题,Query 重写系统应该如何识别并处理这种"话题切换"?欢迎在评论区分享你的思路。


本文从面试高频题出发,完整拆解了多轮RAG检索的Query重写方案。如果觉得有帮助,欢迎点赞收藏。

相关推荐
程序员cxuan1 小时前
我用 DeepSeek-V4-Pro,完美复刻了苹果官网
人工智能·后端·程序员
Eric.461 小时前
2026 AI 漫剧叙事可控性深度工程:解决逻辑崩坏、镜头错乱、道具漂移的高阶落地方案
人工智能·stable diffusion·comfyui·ai漫剧
ZhengEnCi1 小时前
LLM13-2026年8月国内AI大模型性价比排行榜:DeepSeek涨价之后
人工智能
小弥儿1 小时前
GitHub今日热榜 | 2026-08-20:Agent 上下文数据库接棒
数据库·学习·开源·github
এ慕ོ冬℘゜1 小时前
前端实战:使用 jQuery 与 CSS3 打造动态数据表格与滑块交互
前端·css3·jquery
Csvn1 小时前
✂️ AbortController:一个 API 统一取消 fetch、事件监听与 AI 流式请求
前端
invicinble1 小时前
对于vue2转vue3相关技术整合
前端
MartinYeung51 小时前
[论文学习]MPMA:针对模型上下文协议的偏好操纵攻击
人工智能·学习·安全
dorky-org1 小时前
03-Python基础
python·具身智能