ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LangChain实战:构建RAG与Agent结合的智能知识库问答系统

LangChain实战:构建RAG与Agent结合的智能知识库问答系统 这次我们来看一个 LangChain 实战项目主题是结合 RAG、Agent 与知识库。如果你正在寻找一个能跑起来的本地知识库问答系统并且希望它能像智能助手一样不仅能回答问题还能根据知识库内容进行推理和决策那么这个项目值得你关注。它不是一个纯概念演示而是聚焦于如何将 RAG 的精准检索与 Agent 的自主规划能力结合起来构建一个更“聪明”的问答系统。项目的核心在于“实战”。它不满足于简单的文档检索而是引入了 Agent 框架让系统能够理解复杂问题、拆解任务、调用工具如搜索、计算、代码执行并最终基于检索到的知识给出综合答案。这对于构建企业级智能客服、技术文档助手或内部知识查询平台非常有价值。本文将带你从零开始理解其架构完成环境搭建并一步步测试其核心功能让你能快速评估这个方案是否适合你的场景。我们将重点关注几个实际问题这个组合方案对硬件有什么要求启动和部署是否复杂如何构建和加载知识库Agent 在 RAG 基础上到底能多做什么以及如何通过 API 将其集成到自己的应用中。文章会提供详细的代码示例和配置说明确保你可以跟着操作并验证效果。1. 核心能力速览在深入细节之前我们先通过一个表格快速了解这个 LangChain RAG-Agent 项目的核心特性和门槛。能力项说明项目类型基于 LangChain 框架的 RAG (检索增强生成) 与 Agent (智能体) 集成应用核心功能1.知识库构建支持多种格式文档TXT, PDF, Markdown等的解析、切分与向量化存储。2.智能检索基于向量相似度从知识库中精准检索相关上下文。3.Agent 决策集成 Agent可理解复杂问题自主规划步骤调用工具如计算器、网络搜索、代码执行等。4.增强问答结合检索到的知识和 Agent 的推理能力生成准确、可靠的答案。技术栈Python, LangChain, LangChain Community, 向量数据库Chroma / FAISS 等大语言模型如 OpenAI GPT, 本地 Ollama 模型等硬件门槛主要取决于所选用的 LLM- 使用云端 API如 OpenAI对本地硬件无要求只需网络。- 使用本地模型如通过 Ollama需要足够的 CPU/内存或 GPU 加速显存需求由模型决定通常 7B 模型需 8GB 显存。启动方式命令行启动 Python 脚本或封装为 Flask/FastAPI 服务提供 Web 或 API 接口。是否支持 API是。可轻松封装为 RESTful API供其他应用调用。是否支持批量任务是。可批量处理文档构建知识库或批量问答。适合场景企业知识库问答、智能客服、技术文档助手、内部信息查询平台、需要结合外部知识进行复杂推理的应用。2. 适用场景与使用边界这个 RAG-Agent 组合方案并非万能明确其边界能帮助你更好地应用它。它非常适合以下场景复杂问题拆解当用户问题无法通过一次检索直接回答时。例如“对比我们产品A和竞品B在能耗方面的差异并给出优化建议”。Agent 可以规划为1) 检索产品A的规格书2) 检索竞品B的公开数据3) 调用计算工具对比数据4) 综合信息生成建议。动态信息补充当知识库信息不全或需要最新数据时。Agent 可以调用“网络搜索”工具获取实时信息再结合知识库中的静态知识进行回答。多步骤任务执行例如“根据销售报告总结Q3趋势并生成一封给团队的邮件草稿”。Agent 可以检索报告、分析数据、调用文本生成工具。高可靠性要求RAG 确保了答案有据可查来源于知识库减少了 LLM 的“幻觉”。Agent 的规划能力则提升了任务完成的成功率。它可能不适合或需注意简单问答如果所有问题都是“这个参数是多少”这类事实型问题纯 RAG 系统可能更简单高效引入 Agent 会带来不必要的复杂度。对延迟极其敏感Agent 的规划、工具调用步骤会增加响应时间。如果要求毫秒级响应需慎重评估。工具安全边界如果 Agent 被授权调用代码执行、系统命令或数据库写入等工具必须设置严格的权限和安全沙箱防止恶意操作。知识版权与隐私构建知识库时务必确保文档来源合法不涉及未授权的版权材料或敏感个人信息。在使用网络搜索等工具时也需注意合规性。3. 环境准备与前置条件在开始部署前请确保你的开发环境满足以下基本要求。这是一个通用清单具体版本可能随项目更新而变化。操作系统Linux (Ubuntu/CentOS) macOS 或 Windows (建议使用 WSL2 以获得最佳体验)。Python版本 3.8 或更高。推荐使用 3.9 或 3.10这是大多数 AI 库兼容性最好的版本。包管理工具pip或conda。本文以pip为例。版本控制Git用于克隆项目代码。硬件建议CPU/内存至少 4 核 CPU 和 8GB 内存用于运行应用框架和轻量级向量数据库。GPU可选如果计划在本地运行大语言模型如通过 Ollama 部署 Llama 3则需要一张支持 CUDA 的 NVIDIA 显卡。显存需求取决于模型大小例如7B 参数模型通常需要 8GB 以上显存。如果仅使用云端 API则无需本地 GPU。网络能稳定访问 Python PyPI 仓库。如果使用 OpenAI 等云端 API需要能访问相应服务。模型访问权限方案A云端API准备一个有效的 OpenAI API Key或其他支持的云 LLM 服务密钥。方案B本地模型安装并配置好 Ollama并已拉取所需的本地模型如llama3:8b。4. 安装部署与启动方式我们假设你已经有一个基本的项目结构。下面将分步说明如何安装依赖、配置关键组件并启动服务。4.1 克隆项目与安装依赖首先获取项目代码并创建虚拟环境。# 1. 克隆项目这里以假设的项目仓库为例实际需替换为你的项目路径 git clone 你的项目仓库地址 cd langchain-rag-agent-demo # 2. 创建并激活虚拟环境推荐 python -m venv venv # Linux/macOS source venv/bin/activate # Windows venv\Scripts\activate # 3. 安装核心依赖 # 通常项目会提供 requirements.txt如果没有手动安装关键包 pip install langchain langchain-community langchain-openai pip install chromadb # 或 faiss-cpu选择一种向量数据库 pip install pypdf python-dotenv tiktoken # 文档解析和工具包 pip install flask # 或 fastapi用于创建API服务4.2 关键配置在项目根目录创建或修改.env文件用于安全地存储敏感配置。# .env 文件内容示例 # 如果使用 OpenAI API OPENAI_API_KEYsk-your-openai-api-key-here OPENAI_API_BASEhttps://api.openai.com/v1 # 如果是其他兼容接口可修改 MODEL_NAMEgpt-3.5-turbo # 或 gpt-4 # 如果使用本地 Ollama # OLLAMA_API_BASEhttp://localhost:11434 # MODEL_NAMEllama3:8b # 向量数据库持久化路径可选 VECTOR_DB_PATH./vector_db4.3 构建知识库知识库是 RAG 的基石。你需要编写一个脚本将你的文档如 PDF、TXT处理并存入向量数据库。创建一个名为build_knowledge_base.py的脚本import os from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader, TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings # 或使用本地嵌入模型如 from langchain_community.embeddings import OllamaEmbeddings from langchain_community.vectorstores import Chroma from dotenv import load_dotenv load_dotenv() def build_kb(docs_directory./docs, persist_directory./vector_db): 构建知识库加载文档、切分、向量化、存储。 # 1. 加载文档 # 支持多种格式这里示例加载 PDF 和 TXT loaders [] if os.path.exists(os.path.join(docs_directory, *.pdf)): loaders.append(DirectoryLoader(docs_directory, glob**/*.pdf, loader_clsPyPDFLoader)) if os.path.exists(os.path.join(docs_directory, *.txt)): loaders.append(DirectoryLoader(docs_directory, glob**/*.txt, loader_clsTextLoader)) documents [] for loader in loaders: documents.extend(loader.load()) if not documents: print(未在指定目录找到文档。) return # 2. 分割文本 text_splitter RecursiveCharacterTextSplitter( chunk_size1000, # 每个块的大小 chunk_overlap200 # 块之间的重叠部分保持上下文 ) splits text_splitter.split_documents(documents) print(f已将文档切分为 {len(splits)} 个文本块。) # 3. 创建向量存储 # 使用 OpenAI 的嵌入模型 embeddings OpenAIEmbeddings() # 如果使用本地模型例如embeddings OllamaEmbeddings(modelnomic-embed-text) # 将向量数据库持久化到本地 vectordb Chroma.from_documents( documentssplits, embeddingembeddings, persist_directorypersist_directory ) vectordb.persist() print(f知识库构建完成已保存至 {persist_directory}) if __name__ __main__: # 假设你的文档放在项目根目录的 docs 文件夹下 build_kb(docs_directory./docs)运行此脚本前请将你的文档放入./docs目录然后执行python build_knowledge_base.py4.4 启动 RAG-Agent 服务核心服务脚本app.py将整合检索器、LLM 和 Agent。from langchain.agents import AgentExecutor, create_react_agent from langchain import hub from langchain.tools import Tool from langchain_community.vectorstores import Chroma from langchain_openai import ChatOpenAI from langchain_openai import OpenAIEmbeddings from langchain.chains import RetrievalQA from dotenv import load_dotenv import os load_dotenv() # 1. 初始化 LLM llm ChatOpenAI(modelos.getenv(MODEL_NAME, gpt-3.5-turbo), temperature0) # 如果使用本地 Ollama: # from langchain_community.llms import Ollama # llm Ollama(modelos.getenv(MODEL_NAME, llama3:8b)) # 2. 加载向量数据库创建检索器 embeddings OpenAIEmbeddings() vectordb Chroma(persist_directory./vector_db, embedding_functionembeddings) retriever vectordb.as_retriever(search_kwargs{k: 4}) # 检索最相关的4个片段 # 3. 创建 RAG 链作为一个工具 qa_chain RetrievalQA.from_chain_type(llmllm, chain_typestuff, retrieverretriever) rag_tool Tool( nameKnowledge Base, funcqa_chain.run, descriptionUseful for answering questions based on the internal knowledge base. Input should be a clear question. ) # 4. 定义其他工具例如计算、搜索 from langchain.utilities import SerpAPIWrapper, WikipediaAPIWrapper from langchain.tools import WikipediaQueryRun, DuckDuckGoSearchRun # 注意SerpAPI 需要 API Key这里用 DuckDuckGo 作为免费替代 search DuckDuckGoSearchRun() search_tool Tool( nameWeb Search, funcsearch.run, descriptionUseful for searching the internet for current information. Input should be a search query. ) # 5. 创建 Agent tools [rag_tool, search_tool] # 将知识库工具和搜索工具都提供给 Agent prompt hub.pull(hwchase17/react) # 使用一个标准的 ReAct 提示模板 agent create_react_agent(llm, tools, prompt) agent_executor AgentExecutor(agentagent, toolstools, verboseTrue, handle_parsing_errorsTrue) # 6. 简单的对话循环测试用 def chat(): print(RAG-Agent 系统已启动。输入 quit 退出。) while True: query input(\n用户: ) if query.lower() quit: break try: result agent_executor.invoke({input: query}) print(f\n助手: {result[output]}) except Exception as e: print(f执行出错: {e}) if __name__ __main__: chat()这是一个控制台交互版本。要启动它直接运行python app.py5. 功能测试与效果验证现在我们来分层次测试这个系统的能力从简单的 RAG 问答到复杂的 Agent 任务。5.1 测试1纯 RAG 知识检索问答测试目的验证知识库是否构建成功以及系统能否基于内部文档准确回答事实性问题。操作步骤确保./vector_db目录已存在即知识库已构建。运行python app.py。输入一个明确存在于你文档中的问题。例如如果你的文档是关于某软件安装的可以问“安装该软件需要哪些先决条件”预期结果系统应能直接从知识库中检索到相关段落。LLM 生成的答案应紧扣检索到的内容并组织成通顺的回复。在verboseTrue模式下你应该能在控制台看到类似“Action: Knowledge Base”的日志表明它调用了知识库工具。判断成功答案准确且能明显看出来源于你的文档内容。5.2 测试2Agent 规划与工具调用复杂问题测试目的验证 Agent 能否理解多步骤问题并正确规划使用工具如先搜索再结合知识库。操作步骤继续在运行的程序中输入一个更复杂的问题。例如“基于我们知识库中的产品特性对比一下市场上最近有没有出现类似的新技术”观察控制台输出。预期结果规划Agent 应首先“思考”Think可能会计划先使用“Knowledge Base”工具检索内部产品特性再使用“Web Search”工具搜索市场新技术。执行控制台会依次显示“Action: Knowledge Base”和“Action: Web Search”。综合最终答案应结合内部产品特性和外部搜索到的市场信息给出一个对比分析。判断成功Agent 展示了多步思考过程并成功调用了两个不同的工具答案综合了内外信息。5.3 测试3处理知识库中不存在的信息测试目的验证当问题超出知识库范围时Agent 是否会尝试使用其他工具如网络搜索来弥补而不是胡编乱造。操作步骤询问一个知识库中绝对没有的、但属于公共领域的问题。例如“今天北京天气怎么样”假设你的知识库是技术文档。观察 Agent 的行为。预期结果Agent 应首先尝试使用“Knowledge Base”工具但可能检索不到相关内容或相关性很低。随后它应能自主决定调用“Web Search”工具来获取实时天气信息。最终答案应基于网络搜索结果。判断成功系统没有因为知识库缺失而报错或“幻觉”而是通过工具调用获得了有效信息。6. 接口 API 与批量任务将上述系统封装成 API 服务是集成到其他应用的关键。这里使用 Flask 创建一个简单的 REST API。6.1 创建 API 服务创建一个api_server.py文件from flask import Flask, request, jsonify from langchain.agents import AgentExecutor, create_react_agent from langchain import hub from langchain.tools import Tool from langchain_community.vectorstores import Chroma from langchain_openai import ChatOpenAI, OpenAIEmbeddings from langchain.chains import RetrievalQA from langchain.tools import DuckDuckGoSearchRun from dotenv import load_dotenv import os import threading load_dotenv() app Flask(__name__) # 初始化全局组件简单示例生产环境需考虑并发和资源管理 llm ChatOpenAI(modelos.getenv(MODEL_NAME, gpt-3.5-turbo), temperature0) embeddings OpenAIEmbeddings() vectordb Chroma(persist_directory./vector_db, embedding_functionembeddings) retriever vectordb.as_retriever(search_kwargs{k: 4}) qa_chain RetrievalQA.from_chain_type(llmllm, chain_typestuff, retrieverretriever) rag_tool Tool( nameKnowledge Base, funcqa_chain.run, descriptionUseful for answering questions based on the internal knowledge base. ) search DuckDuckGoSearchRun() search_tool Tool( nameWeb Search, funcsearch.run, descriptionUseful for searching the internet for current information. ) tools [rag_tool, search_tool] prompt hub.pull(hwchase17/react) agent create_react_agent(llm, tools, prompt) agent_executor AgentExecutor(agentagent, toolstools, verboseFalse, handle_parsing_errorsTrue) app.route(/query, methods[POST]) def handle_query(): 处理单次问答请求 data request.json user_question data.get(question, ) if not user_question: return jsonify({error: No question provided}), 400 try: result agent_executor.invoke({input: user_question}) return jsonify({ question: user_question, answer: result[output], status: success }) except Exception as e: return jsonify({error: str(e), status: error}), 500 app.route(/batch_query, methods[POST]) def handle_batch_query(): 处理批量问答请求简单串行实现 data request.json questions data.get(questions, []) if not questions: return jsonify({error: No questions provided}), 400 results [] for q in questions: try: result agent_executor.invoke({input: q}) results.append({ question: q, answer: result[output], status: success }) except Exception as e: results.append({ question: q, answer: None, error: str(e), status: error }) return jsonify({results: results}) if __name__ __main__: # 启动服务默认端口 5000 app.run(host0.0.0.0, port5000, debugFalse) # 生产环境请设置 debugFalse6.2 启动与调用 API启动服务python api_server.py服务将在http://127.0.0.1:5000运行。单次问答调用示例使用 curlcurl -X POST http://127.0.0.1:5000/query \ -H Content-Type: application/json \ -d {question: 根据知识库我们的产品主要优势是什么}批量问答调用示例使用 Python requestsimport requests import json url http://127.0.0.1:5000/batch_query payload { questions: [ 产品A的售价是多少, 如何安装产品B, 最新的行业趋势是什么 ] } headers {Content-Type: application/json} response requests.post(url, datajson.dumps(payload), headersheaders) print(response.json())批量任务建议生产环境对于大量问题应考虑使用消息队列如 RabbitMQ, Redis和异步任务框架如 Celery避免 HTTP 请求超时。错误处理在批量处理中必须为每个问题单独捕获异常防止一个任务失败导致整个批次中断。资源限制注意控制并发请求数避免对 LLM API 或本地模型造成过大压力。7. 资源占用与性能观察系统的性能主要取决于 LLM 和向量检索两部分。向量检索内存占用ChromaDB 在加载向量索引时会将部分数据缓存在内存中。对于百万级文档片段内存占用可能在几百 MB 到几 GB。使用persist_directory可以持久化到磁盘减少常驻内存。检索速度检索速度通常很快毫秒到百毫秒级主要受限于硬盘 I/O 和向量相似度计算。确保向量数据库文件位于 SSD 上能提升性能。LLM 推理云端 API如 OpenAI性能取决于网络延迟和 API 的速率限制。响应时间通常在几秒内。你需要监控 API 调用费用和速率限制。本地模型如 OllamaCPU 模式推理速度慢占用高 CPU。适合小模型或测试。GPU 模式推理速度显著加快。使用nvidia-smi命令观察显存占用。例如运行ollama run llama3:8b后观察显存使用量。一个 7B-8B 的模型通常需要 8-16GB 显存才能流畅运行。观察命令在 Linux 下可以使用htop观察 CPU/内存使用watch -n 1 nvidia-smi动态观察 GPU 使用情况。Agent 规划开销Agent 的“思考-行动”循环会增加额外的 Token 消耗和延迟。每一步“思考”和“工具调用”都是一次或多次 LLM 调用。复杂任务可能导致交互次数增多总响应时间变长。优化建议知识库优化优化文本切分策略chunk_size,chunk_overlap使用更好的嵌入模型建立高效的索引如 HNSW都能提升检索质量和速度。缓存对常见问题的检索结果或最终答案进行缓存可以极大减少重复计算。超时设置在 API 调用和 Agent 执行中设置合理的超时时间避免长时间等待。8. 常见问题与排查方法在部署和运行过程中你可能会遇到以下问题。这里提供排查思路。问题现象可能原因排查方式解决方案启动时提示ModuleNotFoundErrorPython 依赖包未安装或虚拟环境未激活。检查当前 Python 环境 (which python或pip list)确认langchain,chromadb等包是否存在。激活虚拟环境运行pip install -r requirements.txt或手动安装缺失包。构建知识库时读取文档失败文档路径错误或缺少对应的文档加载器如pypdf。检查./docs目录是否存在且包含文件。查看错误堆栈信息。确认文档路径。安装必要的加载器pip install pypdf python-docx等。运行时报错OpenAI API相关API Key 未设置、错误或网络不通。检查.env文件中的OPENAI_API_KEY。尝试用curl或 Python 脚本直接调用 OpenAI API 测试。确认 API Key 有效且网络可访问api.openai.com。如用国内环境可能需要配置代理或使用镜像地址。Agent 一直“思考”不输出Agent 陷入循环或提示词导致无法做出决定。开启verboseTrue观察 Agent 的思考链。可能卡在某个工具的选择上。优化工具的描述 (description)使其更清晰。设置max_iterations或max_execution_time限制 Agent 运行时间。检索结果不相关文本切分不合理或嵌入模型不适合领域。检查检索出的文本块内容。调整chunk_size和chunk_overlap。尝试不同的嵌入模型。优化文本分割策略。可以考虑使用专门针对你领域微调过的嵌入模型。本地 Ollama 模型响应慢模型太大硬件资源不足。使用nvidia-smi观察 GPU 利用率使用top观察 CPU 和内存。换用更小的模型如llama3:8b-phi3:mini。确保 Ollama 以 GPU 模式运行 (ollama run llama3:8b)。增加系统内存或显存。API 服务并发请求失败Flask 默认是单线程处理并发请求能力弱。使用压力测试工具如ab,wrk模拟并发请求。使用生产级 WSGI 服务器如gunicorn。部署多个 worker 进程。gunicorn -w 4 api_server:app工具调用如搜索失败网络问题或工具本身 API 变更。单独测试工具函数是否正常工作。检查网络连接。为网络请求添加重试机制和超时设置。考虑使用备用工具。9. 最佳实践与使用建议为了让你的 RAG-Agent 系统更稳定、高效遵循以下实践分阶段验证第一步先用少量文档测试整个流程构建、检索、问答确保基础功能通顺。第二步测试 Agent 的简单工具调用如纯搜索。第三步测试结合知识库和外部工具的复杂任务。知识库质量是根本文档清洗在向量化前尽量去除文档中的无关字符、页眉页脚、广告等。切分策略根据文档类型调整chunk_size。法律合同可能需要大块保持完整性而技术问答可能适合小块。元数据过滤在存储时为每个文本块添加来源、页码等元数据。这有助于后续进行更精细的检索过滤。工具设计要精准描述清晰工具的描述 (description) 是 Agent 选择工具的关键。务必准确描述其功能和输入格式。功能单一一个工具只做一件事。避免创建“万能工具”这会让 Agent 难以理解和使用。安全第一对于代码执行、文件写入等高风险工具必须在沙箱环境中运行并严格限制其权限。生产环境部署服务化使用gunicornnginx或 Docker 容器化部署 API 服务。监控添加日志记录请求、响应、错误监控 API 响应时间、错误率和资源使用情况。版本控制对知识库向量文件、模型版本、代码进行版本管理便于回滚和更新。合规与授权数据来源确保构建知识库的所有文档均已获得使用授权。用户隐私如果系统会处理用户输入需明确隐私政策避免在日志中记录敏感信息。内容审核对于面向公众的服务考虑在最终答案输出前加入内容安全过滤。这个 LangChain RAG-Agent 项目展示了如何将静态知识库与动态推理能力相结合构建出真正能解决复杂问题的智能系统。它的价值不在于单个技术点而在于提供了一套可落地的整合框架。你最应该先验证的是你的业务问题是否真的需要 Agent 的规划能力。如果只是简单问答纯 RAG 可能更合适但如果问题涉及多步推理、信息整合或动态决策那么这个方案的优势就显现出来了。部署时最容易踩的坑通常是环境配置和工具链不通。严格按照本文的步骤从构建知识库开始到启动简单的对话再到封装 API一步步验证。特别注意.env配置文件和向量数据库路径的正确性。接下来你可以探索更强大的 Agent 框架如 LangGraph 用于复杂工作流、尝试不同的本地模型、或者将系统与你的业务平台如 OA、CRM进行深度集成。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进