LangChain 1.0 RAG Agent智能检索问答系统完整代码
???? 本文所有代码均为生产可用的完整脚本,基于 LangChain 最新稳定版 API 构建,无任何过时语法,直接复制即可运行。
在大模型落地场景中,RAG(检索增强生成)是解决“知识滞后、幻觉生成、领域知识缺失”三大痛点的最有效手段。本指南将带您从零搭建一套基于 LangChain 的 RAG Agent 智能检索问答系统,涵盖依赖安装、两大主流实现方案,所有代码均可直接运行。
一、安装项目依赖包
一次性安装所有核心依赖,一条命令搞定:
pip install langchain langchain-text-splitters langchain-community bs4 python-dotenv
安装完成后,即可进入三大核心组件的配置阶段。
二、初始化三大核心组件(核心配置)
构建 RAG Agent 的本质是将三部分衔接:大语言模型、文本嵌入模型、向量存储器。下面逐一完成初始化。
1. 配置大语言模型(LLM)——对话生成核心
# 导入对话模型及环境变量配置依赖from langchain_openai import ChatOpenAIimport osfrom dotenv import load_dotenv# 加载.env文件中的环境变量(私密密钥管理,规范开发)load_dotenv()# 配置通义千问大模型(qwen-max)参数model_name = "qwen-max"api_key = os.getenv("DASHSCOPE_API_KEY")base_url = os.getenv("DASHSCOPE_BASE_URL")# 初始化聊天模型:低随机性保证回答严谨,适配知识类问答场景model = ChatOpenAI(model=model_name,api_key=api_key,base_url=base_url,temperature=0.1,# 温度值越低,回答越精准、确定性越强)
此处将 temperature 设为 0.1,属于低随机性配置,确保回答高度精准、避免输出发散。在知识密集型问答场景中,这一设置尤为关键。
2. 配置文本嵌入模型——文本向量化核心
# 导入HuggingFace嵌入模型from langchain_huggingface import HuggingFaceEmbeddings# 初始化嵌入模型,行业主流轻量级高质量选型# 特点:文本表征能力强、适配中文+英文,首次运行自动缓存至本地,无需重复下载embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")
选定 all-mpnet-base-v2 作为嵌入模型,该模型轻量高效,中英文语义表征能力均衡。首次运行会自动下载并缓存至本地,后续调用无需重复加载。
3. 配置向量存储器——向量数据存储核心
# 导入内存级向量存储库from langchain_core.vectorstores import InMemoryVectorStore# 初始化内存向量库,轻量化无部署,无需持久化磁盘,适合快速开发/演示场景# 绑定已初始化的嵌入模型,实现文本-向量的自动转换与匹配vector_store = InMemoryVectorStore(embeddings)
采用内存级向量库,便于开发调试和原型验证,免去部署和持久化成本。若需上生产环境,可无缝切换至 Chroma 或 Pinecone 等持久化向量数据库。
三、文档处理全流程:加载 → 分片 → 入库(构建检索知识库)
RAG 的核心前置工作是将非结构化网页文本转化为结构化的向量知识库。以下三步均为标准工业流程。
1. 文档加载:精准爬取网页核心内容
# 导入网页加载器及网页解析依赖import bs4from langchain_community.document_loaders import WebBaseLoader# 网页解析规则:只提取指定class的核心内容(标题/头部/正文),过滤冗余HTML标签,提升数据质量bs4_strainer = bs4.SoupStrainer(class_=("post-title", "post-header", "post-content"))# 初始化网页加载器,指定爬取路径+解析规则loader = WebBaseLoader(web_paths=("https://lilianweng.github.io/posts/2023-06-23-agent/",),bs_kwargs={"parse_only": bs4_strainer},)# 加载网页文档至上下文docs = loader.load()# 校验加载结果:确保成功加载1篇文档assert len(docs) == 1# 输出文档总字符数,直观查看文本体量print(f"✅ 文档加载完成,总字符数:{len(docs[0].page_content)}")
通过 bs4.SoupStrainer 指定只提取 post-title、post-header、post-content 三个 class 内的内容,有效剔除页面侧边栏、页脚等无关元素,提升数据纯度。
2. 文档分片:最优策略切割文本,保留上下文关联
# 导入递归字符分割器from langchain_text_splitters import RecursiveCharacterTextSplitter# 初始化文本分割器,采用行业最优的递归分割策略text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000,# 单个文本块最大字符数,适配嵌入模型输入长度限制chunk_overlap=200,# 相邻文本块重叠字符数,避免关键信息被截断,保留上下文关联性add_start_index=True, # 记录文本块在原始文档中的起始索引,便于溯源匹配内容)# 对加载的文档进行分片处理all_splits = text_splitter.split_documents(docs)# 输出分片结果print(f"✅ 文档分片完成,原始文档切分为 {len(all_splits)} 个子文档")
chunk_size=1000、chunk_overlap=200 的搭配在实践中表现优异——既能控制单块长度,又通过重叠区域防止边界信息丢失。开启 add_start_index 便于后续精确定位与溯源。
3. 向量入库:将分片文本存入向量库,完成知识库构建
# 将所有文本分片存入向量存储器,自动完成「文本→向量」转换+存储document_ids = vector_store.add_documents(documents=all_splits)# 预览前3个文本块的唯一标识IDprint(f"✅ 向量入库完成,文本块ID预览:{document_ids[:3]}")
入库过程完全自动化:传入分片后的文档列表,向量存储器自动完成文本到向量的转换并建立索引。输出前三个 ID 作为校验即可。
四、核心实现:构建 RAG Agent 检索问答智能体(两种主流方案)
RAG 的精髓在于让大模型在生成回答前,先从知识库中检索相关上下文,再基于检索结果作答。下面提供两种工业界主流方案,按需选用,均可直接运行。
方案一:工具调用型 RAG Agent(推荐)
核心逻辑
自定义一个检索工具,让大模型自主决定何时调用该工具,再根据检索结果生成最终答案。这种方式赋予模型“工具调用思维”,特别适用于复杂多轮问答或多步骤查询场景,灵活性极高。
# 导入工具装饰器,封装自定义检索工具from langchain.tools import tool# 封装检索工具:返回检索到的上下文内容+原始文档对象,指定返回格式规范def retrieve_context(query: str):"""核心检索工具:根据用户查询语句,从向量库中检索相关上下文信息,辅助回答问题"""# 相似度检索:返回匹配度最高的2条文本内容(k值可按需调整)retrieved_docs = vector_store.similarity_search(query, k=2)# 格式化拼接检索结果:带上元数据+文本内容,提升大模型理解效率serialized_context = "nn".join((f"Source: {doc.metadata}nContent: {doc.page_content}")for doc in retrieved_docs)# 返回格式化文本 + 原始文档,兼顾可读性与溯源性return serialized_context, retrieved_docs# 构建工具调用型智能体from langchain.agents import create_agent# 配置智能体核心参数:绑定大模型+检索工具+系统提示词tools = [retrieve_context]# 系统提示词:明确智能体能力边界与行为准则,精准引导大模型调用工具system_prompt = ("你是一个专业的问答助手,你可以调用检索工具获取外部知识库的上下文信息。n""请务必使用检索工具辅助回答用户的查询问题,确保答案的准确性和事实性。")# 初始化工具调用型RAG Agentagent = create_agent(model, tools, system_prompt=system_prompt)
测试:复杂多步骤查询(智能体自主调用工具)
query = ("What is the standard method for Task Decomposition?nn""Once you get the answer, look up common extensions of that method.")print("===== 工具调用型RAG Agent 回答结果 =====")for event in agent.stream({"messages": [{"role": "user", "content": query}]},stream_mode="values",):event["messages"][-1].pretty_print()
该测试案例设计精妙:先询问“任务分解的标准方法”,再要求查找该方法的常见扩展。智能体需自主判断何时调用检索工具、甚至可能连续调用多次。流式输出可清晰呈现其决策与推理链条。
方案二:上下文注入型 RAG Agent(极简高效)
核心逻辑
通过中间件(middleware)实现“全自动检索+上下文注入”。无需显式定义工具,智能体在回答前自动完成检索,并将结果注入系统提示词。整个过程对开发者透明,代码量极少,执行效率高,适用于简单单轮问答场景。
# 构建上下文注入型智能体from langchain.agents.middleware import dynamic_prompt, ModelRequest# 定义动态提示词中间件:自动检索+注入上下文,无感知完成检索逻辑def prompt_with_context(request: ModelRequest) -> str:"""核心中间件:从请求中提取用户最新问题,检索相关上下文并注入系统提示词"""# 提取用户最新的查询语句last_query = request.state["messages"][-1].text# 自动执行相似度检索,获取相关上下文retrieved_docs = vector_store.similarity_search(last_query)# 格式化拼接检索到的文本内容docs_content = "nn".join(doc.page_content for doc in retrieved_docs)# 构造带上下文的系统提示词,让大模型基于外部知识回答system_message = ("你是一个专业且乐于助人的问答助手,请严格基于下方提供的上下文信息回答用户问题:nn"f"{docs_content}")return system_message# 初始化上下文注入型RAG Agent:无需传入工具,中间件自动完成检索逻辑agent = create_agent(model, tools=[], middleware=[prompt_with_context])
测试:基础单轮查询(极简高效)
# 测试提问:基础单轮查询,验证上下文注入效果query = "What is task decomposition?"# 流式输出回答结果print("n===== 上下文注入型RAG Agent 回答结果 =====")for step in agent.stream({"messages": [{"role": "user", "content": query}]},stream_mode="values",):step["messages"][-1].pretty_print()
单轮查询场景下,中间件自动完成检索与注入,代码量较方案一显著减少。对于简单问答需求,该方案可做到“一键调用”。
核心知识点补充
两种Agent方案对比 & 选型建议
- 工具调用型(方案一):适用于复杂查询、多轮对话、多步骤推理。大模型自主决策是否调用工具,灵活性强,是工业界主流架构。
- 上下文注入型(方案二):适用于简单单轮问答、快速原型开发。代码量少,检索过程透明,但灵活性相对有限。
核心优化点说明
- temperature=0.1:低温度保障回答精准、输出稳定,特别适合知识问答场景,避免内容发散。
- chunk_overlap=200:相邻文本块重叠,有效消除关键信息被截断的风险,提升检索召回率。
- 流式输出stream():实时返回答案片段,提升用户交互体验,无需等待完整结果。
- InMemoryVectorStore:轻量免部署,适合快速迭代。生产环境可替换为 Chroma、Pinecone 等持久化向量库。
总结
本文完整实现了基于 LangChain 的 RAG Agent 智能检索问答系统,覆盖环境配置、文档处理到两种 Agent 方案的构建全流程。所有代码直接复制即可运行,两种主流方案适配不同业务场景。RAG 作为 LLM 落地的关键核心技术,这套代码稍加改造即可迁移至 PDF、本地文档等多种数据源,实用价值极高。
