LangChain 实现灵活 Agents 与 RAG 集成实战
背景与目标
在传统的检索增强生成(RAG)系统中,通常的做法是无论用户问题是否涉及外部知识,都会强制调用检索器去查询向量数据库。这种机制虽然保证了信息的准确性,但在处理通用对话或模型自身已掌握的知识时,会引入不必要的延迟和成本。
为了优化这一过程,我们需要让程序具备判断能力:仅在必要时才去检索,不必要时直接使用大模型的原有数据来回答。本文将介绍如何使用 LangChain 的 Agents(智能体)模块,将 Retriever(检索器)封装为工具,赋予大模型自主决定是否使用检索能力的权限。
1. 构建基础检索器 (Retriever)
首先,我们需要一个可用的检索器实例。这通常涉及加载文档、分块、向量化并存储到向量数据库中。以下是基于 Web 页面内容的标准构建流程:
import bs4
from langchain import hub
from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import Chroma
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
loader = WebBaseLoader(
web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),
bs_kwargs=dict(
parse_only=bs4.SoupStrainer(
class_=("post-content", "post-title", "post-header")
)
),
)
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
vectorstore = Chroma.from_documents(documents=splits, embedding=OpenAIEmbeddings())
retriever = vectorstore.as_retriever()
在此步骤中,我们使用了 WebBaseLoader 加载特定博客文章,并通过 RecursiveCharacterTextSplitter 进行文本切分,最后利用 Chroma 向量数据库存储嵌入向量。生成的 retriever 对象将在后续作为 Agent 的工具被调用。
2. 封装检索工具 (Retriever Tool)
为了让 Agent 能够理解并使用检索器,我们需要将其封装为 LangChain 标准的 Tool 格式。LangChain 提供了 create_retriever_tool 函数来完成这一封装。
关键点: 该函数的第三个参数是工具的描述(Description)。这个描述相当于给大模型的一个 Prompt,直接决定了大模型是否会调用这个工具。描述越清晰、语义越准确,Agent 调用的准确率越高。
from langchain.tools.retriever import create_retriever_tool
tool = create_retriever_tool(
retriever,
"search_agents_answer",
"Searches and returns context from LLM Powered Autonomous Agents. Answering questions about the agents.",
)
tools = [tool]
在这个例子中,我们将检索器命名为 search_agents_answer,并描述了其功能:搜索并返回关于 LLM 驱动自治代理的上下文信息。当 Agent 接收到相关问题时,它会识别到这个工具的存在并尝试调用它。
3. 配置 Prompt 模板与模型
接下来,我们需要加载合适的 Prompt 模板并初始化语言模型。对于使用工具调用的 Agent,推荐使用支持 Function Calling 的 Prompt 模板。
from langchain import hub
from langchain_openai import ChatOpenAI
prompt = hub.pull("hwchase17/openai-tools-agent")
llm = ChatOpenAI(temperature=0)
这里使用了 hub.pull 获取了 LangChain Hub 上的 openai-tools-agent 模板。该模板已经预置了系统指令,指导模型如何根据输入和用户问题选择工具。设置 temperature=0 是为了确保输出结果的确定性和稳定性,减少幻觉。
提示: 可以使用 prompt.pretty_print() 函数打印 Prompt 模板的具体内容,以便调试和理解模型接收到的指令结构。
4. 创建 Agent 与执行器
准备好语言模型、工具和 Prompt 后,即可创建 Agent 及其执行器。LangChain 提供了 create_openai_tools_agent 来简化这一过程。
from langchain.agents import AgentExecutor, create_openai_tools_agent
agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
create_openai_tools_agent 内部会将工具列表转换为 OpenAI 兼容的工具描述格式,并将其绑定到语言模型上(即 Function Calling 机制)。AgentExecutor 则负责管理整个推理循环,包括解析模型输出、调用工具、收集结果并再次输入模型直到生成最终答案。
原理简述
底层实现逻辑是将 tools 转换为 OpenAI API 支持的 function 定义,通过 llm.bind(tools=[...]) 传递给模型。模型在生成回复时,如果决定调用工具,会输出特定的 JSON 格式;如果不需要工具,则直接生成自然语言回复。AgentExecutor 会拦截这些输出并执行相应的动作。
5. 运行测试与分析
通过调用 invoke 接口,我们可以观察 Agent 在不同场景下的行为。
result = agent_executor.invoke({"input": "hi, 我是【同学小张】"})
print(result["output"])
result = agent_executor.invoke(
{
"input": "What is Task Decomposition?"
}
)
print("output2: ", result["output"])
场景一:通用问候
当用户输入简单的问候语(如 "hi, 我是【同学小张】")时,Agent 会判断该问题不需要检索外部知识库。此时,大模型会直接利用其预训练知识进行回复,不会触发 search_agents_answer 工具调用。这有效节省了检索时间和 Token 消耗。
场景二:专业领域提问
当用户询问具体概念(如 "What is Task Decomposition?")时,Agent 识别到该问题可能涉及特定文档中的定义。此时,它会优先调用 search_agents_answer 工具,从向量库中检索相关片段,结合检索到的上下文生成更准确的回答。
6. 最佳实践与扩展建议
在实际生产环境中,使用 Agent 模式进行 RAG 需要关注以下几点:
- 工具描述的精确性:工具的 Description 字段至关重要。应包含具体的适用场景、输入预期和输出格式,帮助模型做出正确决策。
- 温度参数控制:对于任务分解和工具调用,建议使用较低的 temperature(如 0 或 0.1),以减少随机性导致的工具调用错误。
- 超时与重试机制:Agent 的执行可能涉及多次工具调用,需设置合理的超时时间,防止死循环或长时间等待。
- 混合检索策略:除了向量检索,还可以结合关键词检索(BM25)提高召回率,并将多种检索方式封装为不同的 Tool。
- 成本与延迟权衡:虽然 Agent 模式能避免不必要的检索,但每次请求都需要经过一次完整的推理循环来判断是否调用工具。对于高频简单问答,直接 RAG 可能效率更高。
总结
本文演示了如何利用 LangChain 的 Agents 模块实现灵活的 RAG 系统。通过将 Retriever 封装为 Tool,我们赋予了大模型自主判断是否需要检索的能力。这种架构不仅提升了系统的响应效率,还增强了处理复杂多步任务的能力。相比传统固定流程的 RAG,Agent-based RAG 更加智能且适应性强,是构建高级 AI 应用的重要方向。
参考教程:https://python.langchain.com/docs/use_cases/question_answering/conversational_retrieval_agents