译者 | 布加迪
审校 | 重楼
如果你正在构建一个检索增强生成(RAG)应用程序,就知道其功能有多强大,前提是当它顺畅运行时。但是语义嵌入模型并不是什么魔法。大多数RAG实现依赖语义相似性作为唯一的检索机制,将每个文档放入到向量数据库中,并对每个查询运用相同的检索逻辑。这种方法适用于简单直观的问题,但常常检索上下文无关(但语义相似)的文档。当精准的查询需要精确的答案时,仅语义相似性就会导致混淆或不正确的响应。
问题不在于你的模型,而在于你的检索过程。
我们在这里将介绍一种更好的方法:代理混合搜索。通过使用结构化元数据并让大语言模型(LLM)为每个查询选择最佳检索操作,你就可以将RAG应用程序变成真正智能化的助手。我们将从介绍核心概念入手,然后通过一个示例将简单的“信用卡策略QA机器人”变成动态适应用户需求的代理系统。
告别千篇一律的检索,迎接更智能化的RAG体验。
为什么你的RAG应用程序不尽如人意?
究其核心,RAG将LLM与外部知识联系起来。你可以为文档编制索引,使用向量搜索检索语义相似的文档,并让LLM根据这些结果生成响应。是不是觉得听起来很简单?
但简单性是一把双刃剑。虽然许多开发人员致力于改进知识库——用更多的文档或更好的嵌入来丰富知识库,或者针对LLM对提示进行微调,但真正的瓶颈常常在于检索过程本身。大多数RAG实现依赖语义相似性作为一种通用策略。这种方法常常检索错误的文档:要么提取上下文无关的结果,因为语义相似性不是查询的正确方法,要么检索太多重叠或冗余的文档,从而降低了响应的有用性。如果没有一种更智能的方法来过滤和优先处理结果,依赖细微差别的精准查询会继续失败。
想象一下QA机器人负责回答特定的问题,比如“如果我晚10天支付Premium Card账单会发生什么?”或“A银行的基础卡提供购买保障吗?”这些问题需要精确的答案,这些答案取决于策略之间的细微差别。同样,考虑一下像三星这样的公司的支持机器人,支持从智能手机到冰箱的众多产品。比如,“如何重置我的Galaxy S23?”就需要检索该款手机所特有的操作说明,而查询冰箱的保修需要完全不同的文档。借助简单的向量搜索,机器人可能会获取语义相关但上下文无关的文档,因混入用于全然不同的产品或用例的信息而混淆响应或引起幻觉。
无论你的LLM或嵌入有多先进,这个问题始终存在。开发人员常通过微调模型或调整提示作为对策,但真正的解决方案在于改进文档在生成之前的检索方式。简单的检索系统要么检索太多的内容,迫使LLM筛选不相关的信息——这有时可以借助巧妙的提示加以缓解;要么检索太少的内容,使LLM“盲目行动”,没有必要的上下文来生成有意义的响应。通过使检索更智能化、上下文感知,混合搜索解决了这两个问题:它通过针对相关主题进行搜索来减少不相关的干扰信息,并确保检索的文档含有LLM所需的更多精确信息。这大大提高了RAG应用程序的准确性和可靠性。
解决方案:代理混合搜索
解决方案异常简单,却具有变革性:将结构化元数据支持的混合搜索与LLM的代理决策功能相结合,以实现代理混合搜索。这种方法不需要全面改变你的架构或丢弃现有的投入,它立足于已有的系统上,却可以带来全新的智能和灵活性。
从简单到代理:更智能化的流程
一个典型的RAG应用程序遵循简单直观的过程:提问→搜索→生成。用户的问题被传递给检索引擎(常常是向量搜索),检索语义上最相似的文档。然后将这些文档传递给LLM以生成响应。这对于简单的查询很有效,但是面对需要精细的检索策略时却束手无策。
代理混合搜索使用一种更智能化、适应性更强的流程:提问→分析→搜索→生成,取代了这种死板僵硬的流程。LLM不是直接跳转到检索,而是分析问题以确定最佳检索策略。这种灵活性使系统能够更准确地处理更广泛的用例。
功能解锁
有了代理型混合搜索,你的RAG应用程序将变得功能异常强大:
- 多个知识库——LLM可以根据问题动态决定查询哪个知识库。比如说,QA机器人可能从一个数据库提取一般的策略信息,从另一个数据库提取某家银行特有的常见问答(FAQ)。
- 定制的搜索查询——LLM 可以定制自定义搜索查询,而不是仅仅依赖语义相似性。比如说,“A银行的哪些卡提供购买保障?”之类的问题可能会触发对带有“购买保障”标签的卡执行元数据过滤的搜索。
- 元数据过滤器——通过使用结构化的元数据(比如信用卡名、银行名称、部门、日期)丰富文档,可以实现精确的、针对性的搜索,避免了不相关的结果。
- 多个搜索操作——有些问题需要将查询分解成多个子部分。比如说,“Premium Card的资格要求和好处是什么?”可能需要对资格标准执行一次搜索,对好处执行另一次搜索。
这些功能扩展了应用程序可以处理的查询类型。你的RAG应用程序现在可以处理探索性研究、多步骤推理和特定领域的任务,同时保持准确性,而不是局限于简单的事实发现。
工作机理:彻底转变信用卡策略QA机器人
不妨看一个例子。假设你正在构建一个机器人来回答有关多家银行信用卡策略的问题。这是一个简单的实现:
简单的方法
文档在向量数据库中建立索引,机器人执行简单的语义搜索来检索最相似的文档。无论用户查询资格要求、费用或取消策略,检索逻辑都是相同的。
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.
代理方法
在代理混合搜索方法中,我们通过以下手段对系统进行改进:
用元数据丰富文档——在索引策略时,我们添加了结构化元数据,比如:
- 卡名(“Premium Card”)
- 银行名称(“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", "Concisely answer the following question, using information retrieved from tools and the provided information about the user."),
("system", "The following card types are associated with the user: {cards}"),
("system", "Always use the provided tools to retrieve information needed to answer policy-related questions."),
("human", "{question}"),
MessagesPlaceholder("agent_scratchpad"),
])
# First we define the parameters to our search operation
class RetrieveInput(BaseModel):
question: str = Field(description="Question to retrieve content for. Should be a simple question describing the starting point for retrieval likely to have content.")
card_type: str = Field(description=f"Search for documents related to this card type. Value must be one of {pages.keys()}")
# Next, create a "tool" that implements the search logic
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="Retrieve information about a specific card policy.",
args_schema=RetrieveInput,
return_direct=False,
)
# Finally, construct an agent to use the tool we created
agent = create_tool_calling_agent(llm, [policy_tool], prompt)
agent_executor = AgentExecutor(agent=agent, tools=[policy_tool], verbose=True)
在这个示例中,机器人认识到查询具有高度针对性,使用元数据过滤器根据所提供的用户配置文件来检索准确的策略。此外,LLM重写用户的问题,以便完全专注于检索相关文档所需的信息。
agent_executor.invoke({
"question": "How much is my annual membership fee?",
"cards": ["gold"],
})
# > Agent: Invoking: `RetrievePolicy` with `{'question': 'annual membership fee', 'card_type': 'gold'}`
# > 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.
由于LLM选择如何使用搜索工具,我们并不仅限于为每个问题使用相同的过滤器。比如说,LLM可以动态地认识到用户针对与自己的策略不同的策略询问问题,并创建相应的过滤器。
agent_executor.invoke({
"question": "What's the annual membership fee for platinum cards?",
"cards": ["gold"],
})
# > Agent: Invoking: `RetrievePolicy` with `{'question': 'annual membership fee for platinum cards', 'card_type': 'platinum'}`
# > Response: The annual membership fee for Platinum cards is $695. Additionally, each Additional Platinum Card has an annual fee of $195, but there is no annual fee for Companion Platinum Cards.
LLM甚至决定多次使用某个工具。比如说,下列问题需要LLM不仅了解问题中提及的策略,还需要了解用户的当前策略。
agent_executor.invoke({
"question": "How much would my membership fee change if I upgraded to a platinum card?",
"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: The annual membership fee for your current American Express® Gold Card is $325. If you were to upgrade to a Platinum Card, the annual fee would be $695. Therefore, upgrading from a Gold Card to a Platinum Card would increase your annual membership fee by $370.
不妨在这个笔记本:Agentic_Retrieval.ipynb中亲自试一试代码。
为什么这管用?
神奇之处在于利用LLM作为决策者。你不需要硬编码检索逻辑,而是允许LLM分析查询并动态选择最佳方法。这种灵活性使你的系统更智能化、适应性更强,而不需要对基础结构进行重大改变。
回报:更智能化的检索,更精准的响应
采用代理混合搜索可以将你的RAG应用程序变成能够处理复杂细微查询的系统。通过引入更智能化的检索,你可以获得几大好处:
- 提高准确性——更智能化的检索确保为每个查询显示正确的文档,减少幻觉和不相关的结果。这直接提高了LLM响应的质量。
- 增强信任——通过只提取上下文适当的信息,你可以避免混淆关键细节等令人尴尬的错误,确保用户对系统有信心。
- 支持更广泛的用例——动态搜索策略允许你的应用程序处理更复杂的查询、整合多个知识来源,并为更广泛的用户和场景提供服务。
- 简化维护——你可以让LLM动态调整检索策略,减少对持续手动干预的需要,而不是硬编码检索规则或手动管理过滤器。
- 面向未来的可扩展性——随着数据集日益庞大或知识库日益多样化,代理方法可以扩展以应对新的挑战,无需对系统进行根本性改变。
通过使检索更智能化、更具适应性,你可以提高系统的整体性能,无需进行重大改动。
兼顾灵活性和成本
为检索过程添加代理层确实带来了几个不足:
- 增加延迟——每个查询分析都需要额外的LLM调用,执行多个定制的搜索可能比单个操作花费更长的时间。这可能会稍微延长响应时间,尤其对于对延迟敏感的应用程序而言。
- 提高了推理成本——查询分析和编排多个搜索增加了计算开销,对于查询量大的系统而言这可能会增加成本。
- 编排的复杂性——虽然实现很简单,但维护一个动态选择检索策略的系统可能会带来额外的调试或测试问题。
尽管存在这些不足,但代理混合搜索的好处通常压倒成本。对于大多数应用而言,增加的灵活性和精度大大提高了用户满意度和系统可靠性,使投入物有所值。此外,延迟和成本问题通常可以通过缓存、预计算过滤器或仅针对复杂查询进行分析等优化手段来予以缓解。
如何理解和管理这些不足,你可以充分发挥代理混合搜索的潜力,以构建更智能化、更强大的RAG应用程序。
结语
代理混合搜索是充分发挥RAG应用程序的潜力的关键。通过使用结构化元数据丰富文档,并让LLM智能化决定检索策略,你可以不仅限于简单的语义相似性,构建用户可以真正依赖的助手。
这是一个很容易接受的改变,会带来惊人的巨大回报。何不在你的下一个项目中试一下?用户和未来的你会感谢你的。
原文标题:Supercharge Your RAG App With Agentic Hybrid Search,作者:Ryan Michael