LangChain RAG Agent实战:从零构建智能文档问答与任务执行系统
发布时间:2026/8/25 1:35:03 作者:尧图编辑部 阅读量:1,286

这类工具最值得先看的不是功能列表而是能不能在普通环境里稳定跑起来以及从单条问答到批量处理、再到接入真实业务流中间到底有多少坑要踩。LangChain 的 RAG Agent 组合听起来是把检索增强生成和智能体决策绑在一起但实际落地时很多人会卡在“知识库怎么喂”、“Agent 怎么调”、“两者怎么连”这几个环节。如果你手头有文档想快速搭建一个能理解文档内容、并能根据内容执行多步骤任务比如查询、总结、对比、生成报告的系统那这个主题就值得往下看。我建议先从最小样例开始把流程跑通再考虑知识库更新、Agent 工具扩展和性能优化。下面按实际落地顺序拆一遍。1. 先理清 RAG Agent 到底在做什么别急着写代码很多人一上来就pip install langchain然后对着教程抄跑不通就开始怀疑人生。其实问题往往出在没想清楚架构。RAG Agent 不是两个独立模块的简单拼接它本质上是一个能主动使用知识库来辅助决策和执行的智能体。1.1 RAG 负责“知道什么”Agent 负责“做什么”RAG (Retrieval-Augmented Generation)它的核心是“检索-增强”。你有一堆文档知识库当用户问一个问题时RAG 系统会从文档里找出最相关的几段检索然后把这几段文本和用户问题一起塞给大语言模型LLM让 LLM 基于这些“证据”来生成回答增强生成。所以RAG 解决了 LLM 的“幻觉”和知识陈旧问题让回答有据可依。Agent它的核心是“决策-执行”。你给 Agent 一个目标比如“帮我分析一下上季度的销售数据”Agent 会自己规划步骤先调用工具A获取数据再调用工具B做图表最后用工具C生成报告并选择合适的工具Tool去执行每一步直到完成任务。Agent 解决了单一任务模型的局限性能处理复杂、多步骤的工作流。那么 RAG Agent 呢就是让这个 Agent 在决策和执行过程中不仅能调用常规的 API 工具如计算器、搜索引擎还能把“查询我的私有知识库”也作为一个核心工具来使用。当 Agent 遇到需要领域知识、内部文档或历史数据才能回答的问题时它会主动去知识库检索用检索到的内容来支撑它的思考和下一步行动。1.2 你的场景适合用 RAG Agent 吗先别管技术多酷看看你的需求场景A智能客服。用户问“我的订单12345物流到哪了” Agent 需要先调用“查询订单系统”的工具但如果用户问“你们公司的退货政策是什么”Agent 就应该转向调用“检索知识库员工手册/政策文档”这个工具。适合。场景B数据分析助手。用户说“帮我总结一下Q3市场报告的核心发现。” Agent 需要读取“Q3市场报告.pdf”理解内容后总结。这本身就是 RAG 的典型场景用 Agent 来调度很合适。适合。场景C简单的文档问答。用户只问“文档里说了啥”没有多步骤任务。这种情况下一个单纯的 RAG 管道Pipeline就足够了上 Agent 反而增加了复杂度。不适合。如果你的需求集中在场景C可以先专注于构建一个健壮的 RAG 系统。如果涉及场景A或B那么继续往下看。2. 环境准备别在依赖版本上栽跟头LangChain 生态更新快依赖冲突是新手第一道坎。我建议创建一个干净的 Python 环境用 conda 或 venv然后按功能模块分批安装。2.1 基础环境与核心库# 1. 创建并激活虚拟环境以 conda 为例 conda create -n langchain-rag-agent python3.10 conda activate langchain-rag-agent # 2. 安装 LangChain 核心及常用组件 # 注意避免使用 langchain 这个元包它可能包含你不需要的依赖建议按需安装 pip install langchain-core langchain-community langchain-openai # 3. 安装文本嵌入Embedding和向量数据库客户端 # 这里以 OpenAI Embeddings 和 Chroma轻量级向量数据库为例 pip install openai pip install chromadb # 如果需要其他嵌入模型如 sentence-transformers # pip install sentence-transformers2.2 大语言模型LLM接入你需要一个 LLM 作为 Agent 的“大脑”。OpenAI GPT 系列是常见选择也可以使用开源模型通过 Ollama、vLLM 等方式本地部署。# 使用 OpenAI API # 确保已设置环境变量 OPENAI_API_KEY pip install openai # 或者使用 Ollama 在本地运行开源模型如 Llama 3, Qwen2.5 # 首先安装并启动 Ollama 服务请参考 Ollama 官网 # 然后安装 LangChain 的 Ollama 集成 pip install langchain-ollama2.3 Agent 相关工具与框架# 安装 LangChain 的 Agent 相关包 pip install langchain-agents # 如果需要更复杂的工作流有状态、循环可以考虑 LangGraph # pip install langgraph2.4 其他可能用到的工具# 文档加载器用于知识库构建 pip install pypdf # 读取PDF pip install python-docx # 读取Word pip install unstructured # 解析多种格式文档 # 文本分割器 # 通常包含在 langchain-text-splitters 中但 langchain-community 里也有常用实现 # 如果单独安装 pip install langchain-text-splitters关键检查点安装完成后跑一个最简单的导入测试确保没有ImportError。# test_imports.py import langchain_core import langchain_openai import chromadb print(基础导入成功)3. 构建知识库RAG 部分从文档到向量这是整个系统的基石。知识库构建不扎实后面的 Agent 再聪明也没用。3.1 文档加载与预处理不要假设所有文档都能被完美读取。不同格式PDF, Word, TXT, HTML需要不同的加载器并且原始文档常有噪音页眉页脚、乱码、图片。from langchain_community.document_loaders import PyPDFLoader, TextLoader, UnstructuredFileLoader from langchain_text_splitters import RecursiveCharacterTextSplitter def load_and_split_documents(file_paths): 加载并分割文档。 Args: file_paths: 文件路径列表支持多种格式。 Returns: 分割后的文档块列表。 all_docs [] for path in file_paths: if path.endswith(.pdf): loader PyPDFLoader(path) elif path.endswith(.txt): loader TextLoader(path, encodingutf-8) else: # Unstructured 是万能加载器但可能慢一些 loader UnstructuredFileLoader(path) docs loader.load() all_docs.extend(docs) # 文本分割这是关键步骤影响检索精度 text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每个块的大小字符数 chunk_overlap50, # 块之间的重叠避免上下文断裂 length_functionlen, separators[\n\n, \n, 。, , , , , , ] # 中文友好分隔符 ) splits text_splitter.split_documents(all_docs) print(f共加载 {len(all_docs)} 个原始文档分割为 {len(splits)} 个文本块。) return splits # 使用示例 documents load_and_split_documents([./data/公司制度.pdf, ./data/Q3报告.docx])参数解释与避坑chunk_size太小会丢失上下文太大会引入无关信息。一般 300-1000 字符之间尝试。对于技术文档可以稍大对于对话记录可以稍小。chunk_overlap必要的重叠可以保证一个完整的句子或概念不被切碎。通常设为chunk_size的 10%-20%。实测建议分割后随机打印几个splits的内容肉眼检查切割是否合理。不合理的切割是后续检索不准的元凶。3.2 向量化与存储将文本块转换为向量Embeddings并存入向量数据库。from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma def create_vectorstore(documents, persist_directory./chroma_db): 创建向量存储。 Args: documents: 分割后的文档列表。 persist_directory: 向量数据库持久化目录。 Returns: 向量存储对象。 # 1. 初始化嵌入模型 # 使用 OpenAI 的 text-embedding-3-small性价比高 embedding_model OpenAIEmbeddings(modeltext-embedding-3-small) # 如果使用本地模型例如 sentence-transformers # from langchain_community.embeddings import HuggingFaceEmbeddings # embedding_model HuggingFaceEmbeddings(model_nameBAAI/bge-small-zh-v1.5) # 2. 创建向量存储并持久化 vectorstore Chroma.from_documents( documentsdocuments, embeddingembedding_model, persist_directorypersist_directory ) # 显式持久化某些版本需要 vectorstore.persist() print(f向量数据库已创建并保存至 {persist_directory}) return vectorstore # 使用示例 vectorstore create_vectorstore(documents)关键检查点嵌入模型选择英文为主选 OpenAI 或all-MiniLM-L6-v2中文为主强烈推荐BAAI/bge-*系列如bge-small-zh-v1.5它们在中文语义匹配上表现更好。向量数据库Chroma 轻量易用适合学习和中小规模数据。生产环境可以考虑 Qdrant、Weaviate、Milvus 等它们支持分布式、更快的检索和更丰富的过滤条件。持久化确保persist_directory存在且有写入权限。每次启动应用时可以加载已存在的数据库避免重复向量化。3.3 构建检索器Retriever检索器是 RAG 系统的“搜索引擎”它定义了如何从向量库中查找相关文档。def get_retriever(vectorstore, search_typesimilarity, k4): 获取检索器。 Args: vectorstore: 向量存储对象。 search_type: 检索类型可选 similarity相似度/ mmr最大边际相关性兼顾相关性和多样性。 k: 返回的文档块数量。 Returns: 检索器对象。 retriever vectorstore.as_retriever( search_typesearch_type, search_kwargs{k: k} ) return retriever # 使用示例 retriever get_retriever(vectorstore, search_typemmr, k4)参数解释search_typesimilarity纯粹按余弦相似度排序返回最相似的 k 个。search_typemmr在保证相关性的同时增加结果的多样性避免返回内容过于同质。对于开放式问题mmr有时效果更好。k需要根据你的chunk_size调整。如果块小200字k 可以大一些6-8如果块大800字k 小一些2-4否则容易超出 LLM 上下文窗口。4. 创建 Agent 并集成知识库工具现在我们让 Agent 学会使用这个知识库。4.1 定义知识库查询工具首先我们把检索器包装成一个 LangChain Tool这是 Agent 能理解和使用的基本单元。from langchain_core.tools import Tool from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI # 假设我们已经有了 retriever # retriever get_retriever(vectorstore) def knowledge_base_search(query: str) - str: 知识库查询工具函数。 Args: query: 用户的问题或查询词。 Returns: 检索到的相关文档内容拼接成的字符串。 docs retriever.invoke(query) # 检索相关文档 content \n\n---\n\n.join([doc.page_content for doc in docs]) # 用分隔符连接 return f根据知识库相关信息如下\n{content} # 创建 Tool 对象 kb_tool Tool( nameCompany_Knowledge_Base, # 工具名称Agent会根据名称决定是否调用 description当问题涉及公司内部信息、政策、历史数据或产品文档时使用此工具进行查询。输入应为具体的问题或关键词。, funcknowledge_base_search )工具定义要点name清晰、具体让 LLM 能准确理解工具的用途。description这是最重要的部分LLM 根据描述决定是否调用该工具。描述要写明何时使用涉及公司内部信息...和输入格式具体的问题或关键词。func实际执行的函数。这里我们简单地将检索到的文档内容拼接返回。更复杂的处理如重排序、摘要可以在这里进行。4.2 组装 Agent我们将使用 LangChain 的create_react_agent来构建一个 ReAct 风格的 Agent。ReAct 让 Agent 以“思考-行动-观察”的循环来解决问题。from langchain import hub from langchain.agents import create_react_agent, AgentExecutor from langchain_openai import ChatOpenAI # 1. 准备 LLM llm ChatOpenAI(modelgpt-4o-mini, temperature0) # temperature0 使输出更确定 # 2. 定义工具列表 tools [kb_tool] # 目前只有知识库工具你可以继续添加其他工具如计算器、网络搜索等。 # 例如from langchain_community.tools import DuckDuckGoSearchRun # search_tool DuckDuckGoSearchRun() # tools.append(search_tool) # 3. 获取 ReAct 提示词模板LangChain Hub 上有官方维护的优质模板 prompt hub.pull(hwchase17/react) # 4. 创建 Agent agent create_react_agent(llm, tools, prompt) # 5. 创建 Agent 执行器它负责运行循环 agent_executor AgentExecutor( agentagent, toolstools, verboseTrue, # 设为 True 可以看到 Agent 的思考过程调试必备 handle_parsing_errorsTrue, # 优雅处理解析错误 max_iterations5, # 限制最大循环次数防止死循环 early_stopping_methodgenerate # 当 Agent 认为任务完成时停止 )关键参数解析verboseTrue务必在开发阶段开启。你会在控制台看到 Agent 完整的思考链Thought/Action/Observation这是排查 Agent 是否“犯傻”的最直接方式。handle_parsing_errorsTrue当 LLM 的输出格式不符合 Tool 调用格式时尝试让 LLM 重新生成。避免因格式错误直接崩溃。max_iterations安全阀。防止 Agent 陷入无限循环。根据任务复杂度设置一般 5-10 次足够。early_stopping_method“generate”表示当 Agent 输出最终答案而非工具调用时停止。5. 实战测试与问题排查现在让我们用几个问题来测试这个系统。5.1 测试案例与观察# 测试1纯知识库问题 question1 我们公司的年假制度是怎么规定的 result1 agent_executor.invoke({input: question1}) print(测试1结果:, result1[output]) # 测试2需要结合知识库推理的问题 question2 我司员工张三今年工作了3年他能休多少天年假 result2 agent_executor.invoke({input: question2}) print(测试2结果:, result2[output]) # 测试3知识库无法回答应明确告知 question3 明天北京的天气怎么样 result3 agent_executor.invoke({input: question3}) print(测试3结果:, result3[output])预期观察开启 verboseTrue对于问题1你应该能看到 Agent 的 Thought 类似“用户问的是公司制度我需要查询知识库。” 然后 Action 调用Company_Knowledge_Base工具并将查询结果作为 Observation最后生成答案。对于问题2过程类似但 LLM 需要从检索到的年假制度文本中提取“工作N年享受M天”的规则并结合“3年”这个条件进行计算和回答。这考验 RAG 的检索精度和 LLM 的信息提取能力。对于问题3由于我们的工具列表里没有天气查询工具并且知识库中也不会有此信息一个设计良好的 Agent 应该输出“我无法回答这个问题”或“我没有获取天气信息的功能”而不是胡编乱造。5.2 常见问题与排查清单如果测试不顺利按以下顺序排查问题1Agent 不调用知识库工具。检查工具描述description是否清晰指明了使用场景LLM 可能因为描述模糊而选择不调用。尝试将描述写得更具体、更具引导性。检查 verbose 日志看 Thought 部分Agent 是否意识到了需要内部知识如果没有可能是提示词Prompt不够强。可以尝试自定义 Prompt在系统消息中强调“当你需要公司内部信息时务必使用知识库工具”。简化测试先问一个极度明显、必须用知识库回答的问题如“请背诵《员工手册》第一章标题”看工具是否被调用。问题2检索结果不相关导致回答错误。检查文本分割回到第3.1步打印几个splits看是不是把句子切碎了或者把不相关的内容页眉页脚也向量化了。调整chunk_size和chunk_overlap。检查检索器参数尝试将search_type从similarity改为mmr或调整k值。检查嵌入模型如果是中文问题确保使用了好的中文嵌入模型如BAAI/bge-*。用 OpenAI 的text-embedding-3-small对中文效果也不错但非最优。检查查询词Agent 传递给工具的query是否准确查看 verbose 日志中Action Input的内容。有时 Agent 会“改写”用户问题改得不好会影响检索。可以考虑在工具函数内部对查询词进行简单优化如同义词扩展。问题3Agent 陷入循环或迭代次数过多。设置max_iterations如上面代码所示这是一个硬性保护。优化工具描述确保每个工具都能干净利落地完成任务。如果一个工具返回信息过于冗长或杂乱LLM 可能无法理解导致反复调用。增强 LLM 能力尝试换用更强大的 LLM如从gpt-3.5-turbo切换到gpt-4或gpt-4o。更强的推理能力有助于规划更高效的步骤。问题4速度慢。瓶颈分析检索慢向量数据库检索慢。考虑换用性能更好的向量数据库如 Qdrant或对向量进行索引优化。LLM 调用慢这是主要瓶颈。考虑使用更快的模型如gpt-4o-mini比gpt-4快或为 LLM 调用设置合理的超时和重试。工具执行慢如果你的知识库查询工具内部还有复杂处理优化它。启用缓存LangChain 支持缓存如InMemoryCache,SQLiteCache对于重复的查询可以显著提速。from langchain.globals import set_llm_cache from langchain.cache import InMemoryCache set_llm_cache(InMemoryCache())6. 进阶让系统更健壮、更实用基础流程跑通后可以考虑以下优化方向。6.1 知识库的更新与维护静态的知识库很快就会过时。你需要一个更新机制。增量更新大多数向量数据库支持add_documents。你可以定期运行脚本将新文档分割、向量化后加入现有库。注意处理重复文档根据内容哈希去重。删除与更新对于需要修改或删除的文档Chroma 等数据库支持按元数据过滤删除。更常见的做法是版本化管理每次全量重建一个新版本的知识库然后更新应用指向新版本。对于小规模数据这是最稳妥的。6.2 优化检索质量重排序Rerank第一轮向量检索返回 top-k 个结果后使用一个更精细的交叉编码器Cross-Encoder模型对它们进行重新排序可以显著提升 top-1 的准确率。例如可以用BAAI/bge-reranker模型。混合检索结合关键词检索如 BM25和向量检索取长补短。LangChain 有EnsembleRetriever支持此功能。元数据过滤在存储时为每个文档块添加元数据如“文档类型财报”、“年份2023”、“部门销售”。检索时可以让用户指定过滤条件或让 Agent 自动推断过滤条件从而缩小搜索范围。6.3 扩展 Agent 的能力一个只有知识库的 Agent 是有限的。你可以为其添加更多工具使其成为真正的“全能助手”。计算工具langchain_community.tools中有WolframAlphaQueryRun,ArxivQueryRun等。搜索工具如DuckDuckGoSearchRun让 Agent 能获取最新公共信息。自定义 API 工具用Tool.from_function包装你公司内部的任何 API查询数据库、提交工单、发送邮件等。from langchain_community.tools import DuckDuckGoSearchRun from langchain_community.utilities import ArxivAPIWrapper search DuckDuckGoSearchRun() arxiv ArxivAPIWrapper() tools [ kb_tool, Tool( nameWeb_Search, description当问题涉及实时信息、新闻、公开事件或知识库中没有的最新动态时使用此工具。, funcsearch.run ), Tool( nameArxiv_Research, description当需要查找学术论文、研究资料时使用此工具。, funcarxiv.run ), # ... 其他自定义工具 ]6.4 生产化部署考虑异步处理对于 Web 应用使用ainvoke进行异步调用避免阻塞。超时与重试为 LLM 调用和工具调用配置超时和重试机制增加系统鲁棒性。日志与监控记录每一次用户交互、Agent 的思考步骤、工具调用和最终输出。这对于分析效果、排查问题和优化提示词至关重要。成本控制监控 Token 使用量特别是当知识库内容被大量送入 LLM 上下文时。可以通过优化k值、对检索结果进行摘要等方式控制成本。7. 总结从 Demo 到可用的关键点把 LangChain RAG Agent 从教程代码变成一个真正可用的系统关键在于处理好细节和边界条件。不要一上来就追求大而全。先确保最小闭环单文档、单问题、单工具能稳定运行。然后逐步加入更多文档、更复杂的问题、更多的工具。每加一步都充分测试。日志verboseTrue是你的最佳调试伙伴。Agent 的思考过程一目了然哪里出问题就改哪里——可能是提示词、工具描述也可能是检索结果本身。知识库的质量决定上限。花在文档清洗、文本分割和嵌入模型上的时间最终都会在回答准确率上回报你。定期评估检索效果就像维护搜索引擎一样维护你的知识库。Agent 的智能程度受限于 LLM 和工具设计。如果 Agent 总是“犯傻”先别怪框架检查你的工具描述是否清晰易懂LLM 是否足够强大以及提示词是否给予了明确的指令。最后这个架构是灵活的。你可以用 LangGraph 来编排更复杂、有状态的工作流可以用不同的向量数据库来应对数据规模也可以用微调Fine-tuning来让 LLM 更适应你领域的对话风格。但所有这些进阶操作都建立在当前这个稳定运行的 RAG Agent 基础之上。先把它跑稳再想下一步。