代理混合搜索如何创建更智能的 RAG 应用程序
By Ryan Michael
如果您正在构建一个 retrieval-augmented generation (RAG) 应用,您就知道它们在工作良好时是多么强大。但语义嵌入模型并不是魔法。大多数 RAG 实现仅依赖语义相似性作为唯一的检索机制,将每个文档投放到向量数据库中,并对每个查询应用相同的检索逻辑。这种方法适用于简单的问题,但往往会检索到上下文无关(但语义相似)的文档。当细致的查询需要精确的答案时,单靠语义相似性会导致混淆或错误的响应。
问题不在于您的模型——而在于您的检索过程。
在这里,我们将介绍一种更好的方法:代理混合搜索。通过使用结构化元数据并让 large language model (LLM) 为每个查询选择最佳的检索操作,您可以将您的 RAG 应用转变为一个真正智能的助手。我们将首先介绍核心概念,然后通过一个示例,展示如何将一个简单的“信用卡政策问答机器人”转变为一个能够动态适应用户需求的代理系统。
告别千篇一律的检索,迎接更智能的 RAG 体验。
为什么你的 RAG 应用无法交付
在其核心,RAG 将 LLM 连接到外部知识。你索引你的文档,使用向量搜索来检索语义上相似的文档,并 让 LLM 根据这些结果生成响应。听起来很简单,对吧?
但简单可能是把双刃剑。虽然许多开发者专注于改善知识库——通过更多文档或更好的嵌入来丰富它——或微调他们的 LLM 的提示,但真正的瓶颈往往在于检索过程本身。大多数 RAG 实现依赖于语义相似性作为一种通用策略。这种方法往往会检索到错误的文档:要么因为语义相似性不是查询的正确方法而引入上下文不相关的结果,要么检索到过多重叠或冗余的文档,稀释了响应的有效性。如果没有更智能的方式来过滤和优先排序结果,依赖微妙区别的细致查询将继续失败。
想象一个 QA 机器人负责回答特定问题,例如,“如果我晚交 10 天 Premium Card 账单会发生什么?”或“Bank A 的 Basic Card 提供购买保护吗?”这些查询需要精确的答案,取决于政策之间的微妙区别。同样,考虑一个支持机器人,例如三星公司,它提供从智能手机到冰箱的广泛产品。像“我该如何重置我的 Galaxy S23?”这样的问题需要检索特定于该型号的说明,而关于冰箱保修的查询则需要完全不同的文档。使用简单的向量搜索,机器人可能会拉入语义相关但上下文不相关的文档,模糊响应或通过混合完全不同产品或用例的信息而导致幻觉。
这个问题无论你的 LLM 或嵌入多么先进都存在。开发者通常通过微调模型或调整提示来回应,但真正的解决方案在于改善在生成之前文档的 检索方式。简单的检索系统要么检索过多——迫使 LLM 在无关信息中筛选,这有时可以通过巧妙的提示来缓解——要么检索过少,使 LLM “盲目飞行”,没有生成有意义响应所需的上下文。通过使检索更智能和更具上下文感知,混合搜索解决了这两个问题:它通过将搜索限制在相关主题上减少无关噪音,并确保检索到的文档包含 LLM 需要的更精确的信息。这大大提高了你的 RAG 应用的准确性和可靠性。
解决方案:自主混合搜索
解决方案出奇地简单却具有变革性:将基于结构化元数据的混合搜索与大型语言模型的自主决策能力相结合,以实现自主混合搜索。这种方法不需要彻底改造您的架构或放弃现有的投资;它建立在您已有的基础上,以释放新的智能和灵活性。
从简单到智能:更聪明的流程
一个典型的RAG应用遵循一个简单的流程:问题 → 搜索 → 生成。用户的问题被传递给检索引擎——通常是向量搜索——它检索出最语义相似的文档。这些文档随后被传递给LLM以生成响应。这对于简单查询效果良好,但在需要细致检索策略时会遇到困难。
智能混合搜索用更聪明、更灵活的流程替代了这种僵化的流程:问题 → 分析 → 搜索 → 生成。LLM并不是直接跳到检索,而是分析问题以确定最佳的检索策略。这种灵活性使系统能够以更高的准确性处理更多样化的用例。
解锁的能力
通过代理混合搜索,您的 RAG 应用程序变得更加强大:
- 多个知识库 — LLM 可以根据问题动态决定查询哪个知识库。例如,一个问答机器人可能从一个数据库中提取一般政策信息,而从另一个数据库中提取银行特定的常见问题解答。
- 量身定制的搜索查询 — LLM 可以制作自定义搜索查询,而不仅仅依赖语义相似性。例如,一个问题 “银行 A 的哪些卡提供购买保护?” 可能会触发针对带有“购买保护”标签的卡的元数据过滤搜索。
- 元数据过滤器 — 通过用结构化元数据(如卡片名称、银行名称、部分、日期)丰富文档,您可以实现精确、针对性的搜索,避免无关结果。
- 多种搜索操作 — 有些问题需要将查询分解为子部分。例如,“优质卡的资格要求和福利是什么?” 可能涉及一次关于资格标准的搜索和一次关于福利的搜索。
这些能力扩展了您的应用程序可以处理的查询类型。您的 RAG 应用程序现在可以处理探索性研究、多步骤推理和特定领域的任务,而不仅限于简单的事实查找——同时保持准确性。
工作原理:转换信用卡政策问答机器人
让我们通过一个示例来了解。假设您正在构建一个机器人,以回答有关多个银行的信用卡政策的问题。以下是一个简单实现的样子:
天真的方法
文档被索引在一个向量数据库中,机器人执行简单的语义搜索以检索最相似的文档。无论用户询问的是资格要求、费用还是取消政策,检索逻辑都是相同的。
from langchain_core.runnables import (
RunnablePassthrough,
ConfigurableField,
)
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_astradb.graph_vectorstores import AstraDBVectorStore
llm = ChatOpenAI()
embeddings = OpenAIEmbeddings()
vectorstore = AstraDBVectorStore(
collection_name="knowledge_store",
embedding=embeddings,
)
ANSWER_PROMPT = (
"Use the information in the results to provide a concise answer the original question.\n\n"
"Original Question: {question}\n\n"
"Vector Store Results:\n{'\n\n'.join(c.page_content for c in context)}\n\n"
)
retriever = vectorstore.as_retriever()
## Construct the LLM execution chain
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| ChatPromptTemplate.from_messages([ANSWER_PROMPT])
| llm
)
结果是什么?对于像*“我的年度会员费是多少?”*这样的问题,系统可能会检索到无关卡片的政策,因为嵌入优先考虑广泛相似性而非特异性。
chain.invoke("How much is my annual membership fee?",)
## > Response: Your annual membership fee could be $250, $95, $695, or $325, depending on the specific plan or card you have chosen. Please refer to your specific card member agreement or plan details to confirm the exact amount of your annual membership fee.
代理方法
在代理混合搜索方法中,我们通过以下方式改进了该系统:
用元数据丰富文档 — 在索引政策时,我们添加结构化元数据,如:
- 卡片名称(“高级卡”)
- 银行名称(“银行A”)
- 政策部分(“费用”,“奖励”,“资格”)
- 生效日期
使用LLM选择检索操作 — 机器人不是盲目执行向量搜索,而是利用查询上下文来决定:
- 是否应该搜索语义上相似的政策?
- 是否应该按卡片或银行元数据进行过滤?
- 是否应该针对特定政策部分发出多个查询?
从多个搜索中组合响应 — 机器人智能地结合结果,以生成精确和值得信赖的答案。
以下是实际操作的示例:
示例代码
from typing import List, Literal
from pydantic import BaseModel, Field
from langchain_core.documents.base import Document
from langchain_core.tools import StructuredTool
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import MessagesPlaceholder
prompt = ChatPromptTemplate.from_messages([
("system", "简洁地回答以下问题,使用从工具和提供的用户信息中检索到的信息。"),
("system", "以下卡片类型与用户相关:{cards}"),
("system", "始终使用提供的工具来检索回答与政策相关问题所需的信息。"),
("human", "{question}"),
MessagesPlaceholder("agent_scratchpad"),
])
## 首先,我们定义搜索操作的参数
class RetrieveInput(BaseModel):
question: str = Field(description="用于检索内容的问题。应该是一个简单的问题,描述检索的起点,可能会有内容。")
card_type: str = Field(description=f"搜索与此卡片类型相关的文档。值必须是 {pages.keys()} 中的一个")
## 接下来,创建一个实现搜索逻辑的“工具”
def retrieve_policy(question: str, card_type: str) -> List[Document]:
print(f"retrieve_policy(card_type: {card_type}, question: {question})")
retriever = graph_vectorstore.as_retriever(
search_type = "similarity",
search_kwargs = {"metadata_filter": {"card-type": card_type}},
)
return list(retriever.invoke(question))
policy_tool = StructuredTool.from_function(
func=retrieve_policy,
name="RetrievePolicy",
description="检索有关特定卡片政策的信息。",
args_schema=RetrieveInput,
return_direct=False,
)
## 最后,构建一个代理来使用我们创建的工具
agent = create_tool_calling_agent(llm, [policy_tool], prompt)
agent_executor = AgentExecutor(agent=agent, tools=[policy_tool], verbose=True)
在这个例子中,机器人识别到查询非常具体,并使用元数据过滤器根据提供的用户资料检索确切的政策。此外,LLM 重新编写用户的问题,使其更加专注于检索相关文档所需的信息。
agent_executor.invoke({
"question": "我的年费是多少?",
"cards": ["gold"],
})
## > Agent: Invoking: `RetrievePolicy` with `{'question': 'annual membership fee', 'card_type': 'gold'}`
## > Response: 您的年费可能是 $250、$95、$695 或 $325,具体取决于您选择的特定计划或卡片。请查阅您的具体卡片会员协议或计划细节以确认您年费的确切金额。
由于 LLM 决定如何使用搜索工具,我们并不局限于对每个问题使用相同的过滤器。例如,LLM 可以动态识别用户询问的是与他们自己的政策不同的政策,并创建适当的过滤器。
agent_executor.invoke({
"question": "白金卡的年费是多少?",
"cards": ["gold"],
})
## > Agent: Invoking: `RetrievePolicy` with `{'question': 'annual membership fee for platinum cards', 'card_type': 'platinum'}`
## > Response: 白金卡的年费为 $695。此外,每张额外的白金卡年费为 $195,但伴侣白金卡没有年费。
LLM 甚至可能选择多次使用某个工具。例如,以下问题要求 LLM 了解用户当前的政策以及问题中提到的政策。
agent_executor.invoke({
"question": "如果我升级到白金卡,我的年费会变多少?",
"cards": ["gold"],
})
## > Agent: Invoking: `RetrievePolicy` with `{'question': 'membership fee for gold card', 'card_type': 'gold'}`
## > Agent: Invoking: `RetrievePolicy` with `{'question': 'membership fee for platinum card', 'card_type': 'platinum'}`
## > Response: 您当前的美国运通® 金卡年费为 $325。如果您升级到白金卡,年费将为 $695。因此,从金卡升级到白金卡将使您的年费增加 $370。
在这个笔记本中尝试代码:Agentic_Retrieval.ipynb。
为什么这有效
其魔力在于利用 LLM 作为决策者。你不是硬编码检索逻辑,而是让 LLM 分析查询并动态选择最佳方法。这种灵活性使你的系统更智能、更适应,而无需对基础设施进行大规模更改。
回报:更智能的检索,更好的响应
采用代理混合搜索 将您的 RAG 应用程序转变 为一个能够处理复杂、细微查询的系统。通过引入更智能的检索,您可以提供几个关键好处:
- 提高准确性 — 更智能的检索确保每个查询都能显示正确的文档,减少幻觉和无关结果。这直接提高了 LLM 响应的质量。
- 增强信任 — 通过仅提取上下文适当的信息,避免了混淆关键细节等尴尬错误,确保用户对您的系统充满信心。
- 更广泛的使用案例 — 动态搜索策略使您的应用能够处理更复杂的查询,整合多个知识源,并服务于更广泛的用户和场景。
- 简化维护 — 不再硬编码检索规则或手动策划过滤器,您让 LLM 动态调整检索策略,减少了持续手动干预的需求。
- 未来可扩展性 — 随着您的 数据集增长或知识库多样化,代理方法能够扩展以应对新挑战,而无需对系统进行根本性更改。
通过使检索更加智能和自适应,您提升了系统的整体性能,而无需进行重大改造。
权衡:平衡灵活性和成本
在您的检索过程中添加一个自主层确实会带来一些权衡:
- 增加延迟 — 每个查询分析都涉及额外的 LLM 调用,发出多个定制搜索可能比单次操作耗时更长。这可能会稍微延迟响应时间,特别是对于对延迟敏感的应用程序。
- 更高的推理成本 — 查询分析和协调多个搜索增加了计算开销,这可能会提高高查询量系统的成本。
- 编排复杂性 — 尽管实现相对简单,但维护一个动态选择检索策略的系统可能会引入额外的调试或测试考虑。
尽管存在这些权衡,自主混合搜索的好处通常超过成本。对于大多数应用程序,增加的灵活性和精确性显著提高了用户满意度和系统可靠性,使投资变得值得。此外,延迟和成本问题通常可以通过缓存、预计算过滤器或将分析限制在复杂查询上来减轻。
通过理解和管理这些权衡,您可以充分利用自主混合搜索的潜力,构建更智能、更强大的 RAG 应用程序。
结论
具备主动性的混合搜索是释放您 RAG 应用程序全部潜力的关键。通过为您的文档丰富结构化元数据,并让 LLM 智能地决定检索策略,您可以超越简单的语义相似性,构建一个用户真正可以依赖的助手。
这是一项易于采用的改变,带来的回报却出乎意料地丰厚。为什么不在下一个项目中尝试一下呢?您的用户——以及未来的您——会感谢您。