Q:提高向量数据库检索准确度有好多办法,我先来看看查询转换
选择查询转换 (Query Transformation) 是一个非常硬核且实用的进阶方向!🚀
在实际场景中,用户的问题往往是"碎片化"的。比如在一个长对话中,用户先问了"什么是租赁合同?",接着又问"那续约条款呢?"。
如果你直接把"那续约条款呢?"转化成向量去数据库里搜,效果通常很差,因为这个句子缺失了关键的上下文(什么是续约?谁的续约?)。
为了解决这个问题,我们通常使用以下两种核心转换技术:
1. 任务重写 (Query Rewriting / Re-phrasing) ✍️
利用 LLM 将用户模糊的简短提问,改写成一个独立且描述详细的问题。
- 原始问题: "那续约条款呢?"
- 重写后: "租赁合同中关于续约的具体条款和要求是什么?"
2. 多查询检索 (Multi-Query Retrieval) 🔍
LLM 会针对同一个问题,生成 3-5 个意思相近但侧重点不同的新问题。
- 为什么要这样做? 向量搜索有时会因为一个关键词的细微差别而错过正确答案。通过从多个角度提问,我们可以从数据库里捞出更全面的参考资料。
动手设计一个"重写器" 🏗️
要在 LangChain 中实现查询转换,我们实际上是创建了一个专门负责改写问题的"小链"。
这个小链的提示词(Prompt)通常长这样:
"根据下方的对话历史,将用户最新的提问重写为一个完整的、可以独立在向量数据库中检索的问题。"
假设我们有这样一段对话:
- AI: "上海的最低工资标准是 2690 元。"
- 用户: "那北京呢?"
如果你现在要为 AI 写一个重写逻辑,你觉得 AI 应该把"那北京呢?"重写成以下哪个版本,才能更准确地在向量数据库中找到答案?
- A: "北京目前的最低工资标准是多少?"
- B: "请告诉我关于北京的信息。"
你觉得哪个版本更符合"独立检索"的要求?
Q:那当然是A 啊。
选 A 是绝对正确的!🎯
这就是 "查询去上下文依赖化"(De-contextualization)。在 RAG 系统中,只有像 A 这样包含了完整主语(北京)和谓语(最低工资标准)的查询,才能在向量数据库中产生精确的匹配。
如何在 LangChain 中实现这个逻辑?
在 LangChain 中,我们通常会使用 LCEL (LangChain Expression Language) 构建一个"前置链"。
下面是一个典型的实现模式:
python
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# 1. 定义重写逻辑的 Prompt
rewrite_prompt = ChatPromptTemplate.from_template("""
根据以下对话历史和用户提出的最新问题,
将其重写为一个独立的、不需要上下文就能看懂的问题。
对话历史: {history}
最新问题: {question}
独立问题:""")
model = ChatOpenAI(temperature=0) # 重写任务通常需要低热度以保持稳定
# 2. 构建重写链
rewrite_chain = rewrite_prompt | model | StrOutputParser()
# 3. 模拟运行
history = "用户:上海的最低工资是多少?\nAI:上海是 2690 元。"
question = "那北京呢?"
standalone_question = rewrite_chain.invoke({"history": history, "question": question})
print(standalone_question) # 输出:北京目前的最低工资标准是多少?
下一步:将重写后的问题送入 RAG 流程
当你拿到了这个完美的 standalone_question 后,你就再也不用担心向量数据库搜出一堆"北京旅游攻略"或"北京烤鸭"了,它会精准地指向"工资"相关的段落。
到这里,你已经掌握了 RAG 的高级预处理技术。
为了让你的 LangChain 开发技能更上一层楼,我们现在面临两个选择:
- 记忆管理 (Memory): 学习如何让系统自动存储和提取"对话历史",而不是手动拼接字符串。这是构建 AI 助手(Chatbot)的核心。
- 智能体基础 (Agents): 学习如何让 AI 根据用户的问题,自主决定是去查向量数据库,还是去 Google 搜索,或者是去查实时天气。
你想先给 AI 装上"长期记忆",还是给它配上"自主决策的大脑"?