前言 我们主要学习了如何构建多智能体系统,让大模型不仅能够“回答问题”,还能够在复杂任务中进行规划、决策,并通过工具调用完成更高层次的操作。然而,再强的智能体依然离不开一个核心前提——获取准确、可靠、上下文相关的信息 。
大模型本身并不具备实时知识库,它的知识来源于训练数据,天然具有时效性、覆盖不完整、无法访问企业内部资料 等限制。而解决这一问题的关键技术,就是本节课的核心:Agentic RAG 。
与传统 RAG(检索增强生成)不同,Agentic RAG 不只是“把检索结果塞给模型”,而是让智能体主动决定如何检索、如何组合信息、如何重试检索、如何判断信息是否足够 。
换言之,它让 RAG 不再是一个固定流程,而是变成一个智能体可调度、可动态优化的能力模块,从而构建出真正更智能、更可靠的系统。
环境准备 本节课我们将以 LangChain 1.0 的 API 为基础,结合阿里云百炼 DashScope 的向量模型与 Chroma 向量数据库。从零开始搭建一个可被 Agent 调用的 RAG 系统。
请提前安装以下依赖:
pip install langchain langchain-community dashscope langchain_chroma beautifulsoup4接下来我们将基于这些模块正式构建向量数据库,并将其封装为 Agent 可用的工具。
RAG 简介 定义 RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合外部知识库 + 大模型生成能力 的技术,其核心流程包括:
它的作用是“补充知识”,解决大模型知识滞后、不完整的问题。
传统 RAG vs Agentic RAG 传统 RAG Agentic RAG 智能体主动:提出检索请求 → 多轮检索 → 自我判断 → 生成结果 能判断检索是否足够、是否需要重新查询,且可组合工具、反思与规划能力
Agentic RAG 的优势在于:
智能体可以自主生成检索指令(Retrieval as a Tool) 因此,本节课不仅仅实现一个 RAG,而是构建一个可以:
的Agentic RAG 工具模块 。
代码实战 简介 假如我们希望将 RAG 变成 Agent 可用的工具,首先我们需要创建出一个向量数据库,然后在这个向量数据库的基础上根据问题搜索里面有用的信息并进行使用。
因此我们的步骤分为:
信息获取 为了能够完成构建向量数据库,第一步,也是最重要的一步就是找到合适的信息源。这里我们就使用开源的书籍《动手学习深度学习》里的简介章节的内容作为我们向量数据库的信息源,后续所有的对话都将在此基础上进行。
fromlangchain_community.document_loadersimportWebBaseLoader loader = WebBaseLoader("https://zh.d2l.ai/chapter_introduction/index.html") docs = loader.load()文本分割 在获取到了信息后,此时这个是一大段的字符串信息,假如直接将其载入数据库中,未来调用的时候就会非常的耗费 token 。因此对于所有 RAG 系统都必须要做的事情就是将其进行切分,比如下面这个就是将一大段字符切分为一块块,每一块包含 1500 个字符。同时为了能够让上下文信息连贯, 也额外的添加了上下各 150 个 token。
fromlangchain_text_splittersimportRecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=150) splits = text_splitter.split_documents(docs)那这就意味着将可能几万 token 的内容拆分成了几十个 1800 token 的分块,这样的话每次检索的时候,我们只要找到最相关的前几块并给到大模型就好。这样比起直接将所有内容都传给大模型要更节省 token 的消耗。
文本向量化 在将文本字符分块后,我们就要准备将其存储到向量数据库当中。但是此时的分块里还是字符串,而字符串并不能直接载入到向量数据库中。我们需要先将这些分块转为向量信息,然后再将向量信息存储到向量数据库当中。
这里我们使用的就是阿里云百炼大模型平台的 text-embedding-v1 词向量模型,其会将传入的信息转为 1,536 维的向量,这样信息就能够保存到向量数据库中了:
fromlangchain_community.embeddingsimportDashScopeEmbeddings importos embeddings = DashScopeEmbeddings( dashscope_api_key=os.getenv("DASHSCOPE_API_KEY"), model="text-embedding-v1" )向量数据库创建及载入 在准备好了文本分割完的字符串块体、embedding 模型后,我们就可以使用 LangChain 中比较推荐的 Chroma 向量数据库,并将转化好的向量载入到向量数据库中。这里我们还需要设置一下向量数据库保存的位置(目前在当前文件夹下的 chroma 文件夹中):
fromlangchain_chromaimportChroma vectordb = Chroma.from_documents( documents=splits, embedding=embeddings, persist_directory="./chroma" )这样我们把向量数据库准备好了,后续就可以当做工具进行使用了。以上的内容我们可以单独制作成一个文件(vectordb.py),未来假如我们想要对向量数据库进行调整,就可以在该文件进行修改使用,完整的代码如下所示:
fromlangchain_community.document_loadersimportWebBaseLoader importos loader = WebBaseLoader("https://zh.d2l.ai/chapter_introduction/index.html") docs = loader.load() fromlangchain_text_splittersimportRecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=150) splits = text_splitter.split_documents(docs) fromlangchain_community.embeddingsimportDashScopeEmbeddings embeddings = DashScopeEmbeddings( dashscope_api_key=os.getenv("DASHSCOPE_API_KEY"), model="text-embedding-v1" ) fromlangchain_chromaimportChroma vectordb = Chroma.from_documents( documents=splits, embedding=embeddings, persist_directory="./chroma" )加载向量数据库 在创建完向量数据库后,接下来我们就可以基于这个数据库进行进一步的工具开发。这里我们可以重新新建一个文件(RAG_Agent.py),然后先载入 embedding 模型及已经创建好的 Chroma 数据库,为后续的调用打下基础。
fromlangchain_community.embeddingsimportDashScopeEmbeddings fromlangchain_chromaimportChroma importos embeddings = DashScopeEmbeddings( dashscope_api_key=os.getenv("DASHSCOPE_API_KEY"), model="text-embedding-v1" ) vectordb = Chroma( embedding_function=embeddings, persist_directory="./chroma"# 必须与上一步保持一致 )应用实践 接下来我们就可以基于这个 vectordb 进行向量数据库的检索工作了。这里介绍两个应用场景:
根据用户问题在向量数据库中找到相关片段并拼接到系统提示词中,后续智能体在发出工具指令以及调用工具时就能够基于这部分信息更智能的完成任务。 将向量数据库作为检索工具,根据问题提炼出检索的语句,然后看返回的结果是否能够满足。假如能够满足则返回整理后的信息,假如不满足则调整检索语句再次检索,直至智能体认为符合要求为止。这种方式也被称为是 Agentic RAG 的形式。 载入系统提示词中 假如我们希望根据用户的提问实时的修改系统提示词,我们可以使用 LangChain 的内置中间件来实现。这里我们使用的是 wrap_model_call 的特殊形式 dynamic_prompt 来实现。相比起 wrap_model_call 作用在整个模型调用的过程中,并需要传入的参数 request 和 handler。dynamic_prompt 只作用在第一次模型调用前,并且也只对系统提示词产生影响,无法真正执行调用信息:
fromlangchain.agents.middlewareimportdynamic_prompt, ModelRequest fromlangchain.agentsimportcreate_agent importos @dynamic_prompt defprompt_with_context(request: ModelRequest)-> str: """自动根据用户问题检索知识并注入到系统提示""" last_query = request.state["messages"][-1].text retrieved_docs = vectordb.similarity_search(last_query, k=3) docs_content ="\n\n".join(doc.page_contentfordocinretrieved_docs) system_message = ( "You are a helpful assistant. " "Use the following retrieved context to answer the user:\n\n" f"{docs_content}" ) returnsystem_message # 创建智能体 agent = create_agent(llm, tools=[], middleware=[prompt_with_context]) # 测试查询 query ="What is computer vision?" forstepinagent.stream( {"messages": [{"role":"user","content": query}]}, stream_mode="values", ): step["messages"][-1].pretty_print()那这个 prompt_with_context 函数实际上实现了:
从 request.state 中获取最新一条的 messages 里的信息(用户提问) 然后根据用户提问使用相似度检索的方式找到前 3 条最相关的片段 然后把这三条片段拼接起来,形成一段字符串 docs_content 然后将这段字符串传给 system_message 并最后进行返回 然后在创建智能体时将这个函数以 middleware 的形式传入到 agent 中,后续就能够实现根据用户提问动态调整系统提示词。
RAG 工具 那这里的第二种方案就是将 RAG 作为工具进行使用。首先我们需要将其变为工具:
fromlangchain.toolsimporttool @tool(response_format="content_and_artifact") defretrieve_context(query: str): """Retrieve information to help answer a query.""" retrieved_docs = vectordb.similarity_search(query, k=2) serialized ="\n\n".join( (f"Source:{doc.metadata}\nContent:{doc.page_content}") fordocinretrieved_docs ) returnserialized, retrieved_docs这里由于我们不仅仅导出一段文本,并且我们还要导出原始的对象,因此在 response_format 参数里我们设置为 content_and_artifact。这部分信息将会自动被保存在系统层中,后续我们可以在中间件或节点中进行调用。
然后我们需要将大模型、工具、提示词统一配置到 create_agent 中,并对其进行调用:
fromlangchain_community.chat_modelsimportChatTongyi llm = ChatTongyi(model="qwen-max") tools = [retrieve_context] # If desired, specify custom instructions prompt = ( "You have access to a tool that retrieves context from a deep learning book. " "Use the tool to help answer user queries." ) fromlangchain.agentsimportcreate_agent agent = create_agent(llm, tools, system_prompt=prompt) query = ( "What is the deep learning model used in computer vision tasks? " ) foreventinagent.stream( {"messages": [{"role":"user","content": query}]}, stream_mode="values", ): event["messages"][-1].pretty_print()这个时候就会根据我们的问题去数据库里检索对应的内容,找到足够的内容后整理这些信息并返回结果。
总结 本节课围绕Agentic RAG 展开,从基础概念、向量数据库构建到实际封装为智能体可用的工具,完成了从“静态检索增强”到“可调度可控制的智能信息获取能力”的转变。
我们首先通过网页加载—文本切分—向量化—Chroma 存储 这一完整流程构建了知识库,然后演示了两种将 RAG 引入智能体的方法:
动态系统提示词(dynamic_prompt)
智能体在第一次模型调用前自动检索知识并注入上下文,使得回答内容更加准确与可靠。
RAG 工具(@tool)
将检索过程封装为可调用的工具,使智能体能够自主发起检索、判断信息是否足够,并将其整合到推理链中,形成真正的 Agentic RAG。
通过这些内容,我们可以看到 RAG 不再是一个静态的“补充知识”模块,而成为智能体体系中的关键能力,使智能体能够在面对复杂任务时主动检索、反思与修正,显著提升其知识覆盖度、准确性与可解释性。