LangChain+LangGraph+Agent+RAG全流程实战指南

发布时间:2026/8/31 3:49:33
LangChain+LangGraph+Agent+RAG全流程实战指南 刚接触 LangChain 时相信很多人都有一种“方法我都会调但真要做项目却不知道从哪下手”的感觉。网上资料虽然很多但大多停留在单个 API 的演示层面很少有人把 LangChain 的核心抽象、LangGraph 的流程编排、Agent 的决策机制和 RAG 的知识库搭建串成一个完整的体系来讲。本文的目标就是围绕一条清晰的学习主线把这四块内容打通先理解每一层解决了什么问题再通过一个接近真实业务场景的项目把它们串联起来最后给出生产环境落地时常见的坑和工程建议。如果你是刚入门 LLM 应用开发的新人本文可以帮你建立一张完整的地图如果你已经有 LangChain 基础可以直接跳到第 4 节的实战部分和第 6 节的最佳实践重点看流程编排、状态管理、检索质量这几个容易被忽视的点。下面先从一个最基础的问题开始LangChain 到底是做什么的1. 背景与核心概念1.1 LangChain 是什么从“调用模型”到“构建应用”很多初学者会把 LangChain 理解成一个“封装好的 OpenAI SDK”只要会调用model.invoke()就算入门了。这其实是个误区。LangChain 的本质是一个面向 LLM 应用开发的编排框架。它解决的核心问题不是“如何请求大模型”而是“如何把一个真实的业务需求拆解成模型调用、数据处理、工具调用、记忆管理和外部系统交互等多个环节并把它们可靠地组合起来”。举个例子你要做一个“公司内部文档问答机器人”。如果直接用 SDK 调用模型from openai import OpenAI client OpenAI() resp client.chat.completions.create( modelgpt-4o-mini, messages[{role: user, content: 根据这份文档回答报销流程是什么}] ) print(resp.choices[0].message.content)这段代码只能处理“你手动把文档粘贴进去”的情况。但真实场景是文档有几百份、格式各不相同、更新频繁、用户提问还涉及引用来源。这时候你需要考虑文档如何加载、清洗、切分切分后的内容如何存储成向量用户问题如何转换成查询语句如何从向量库中检索最相关的内容检索结果如何塞进 Prompt答案如何附上引用来源上述每一步都有独立的工程方案而 LangChain 的价值在于把文档加载Loader、文本切分Splitter、向量化Embedding、向量存储VectorStore、检索Retriever、提示词构造Prompt Template、模型调用Model、输出解析Output Parser这些环节抽象成标准组件方便自由组合。在 LangChain 中一条最简单的问答链路可以写成from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate llm ChatOpenAI(modelgpt-4o-mini) prompt ChatPromptTemplate.from_messages([ (system, 你是一个严谨的技术助手。), (human, {question}) ]) chain prompt | llm resp chain.invoke({question: 什么是 RAG}) print(resp.content)这里的chain prompt | llm就是 LangChain 最核心的抽象之一LCELLangChain Expression Language。它用|把提示词和模型串联起来形成一条可执行的管道。初学者可以把 LCEL 理解为“工序流水线”——上一环节的输出就是下一环节的输入。1.2 LangGraph 与 LangChain 的区别从线性管道到图编排学习 LangChain 一段时间后你会发现 LCEL 适合处理“固定流程”的任务比如“先检索再生成”。但真实业务里有很多过程是不固定的Agent 需要根据用户的问题决定“要不要调用搜索工具”一个问题可能需要多次调用模型才能得到最终答案多步骤之间需要校验、分支、回退、甚至循环处理。这就是 LangGraph 出现的意义。LangGraph 是 LangChain 团队推出的低层编排框架用图Graph的方式表达应用流程。图中有两类核心元素节点Node执行具体逻辑的 Python 函数比如“调用模型”“调用检索器”“调用工具”。边Edge定义节点之间的流转关系既可以是固定边也可以是根据条件决定走向的条件边Conditional Edge。和 LangChain 的线性管道相比LangGraph 的核心优势是状态管理。每个节点都能读写一个共享的 State 对象这使得“记住上一步的结果”“根据上一步的输出来决定下一步”变得非常自然。很多初学者会问两者到底有什么关系简单来说LangChain 提供了大量开箱即用的能力组件模型封装、检索器、工具、MemoryLangGraph 负责把这些组件放进一个可控的图结构中运行。你可以先基于 LangChain 的组件构建业务能力再用 LangGraph 对复杂流程进行编排。两者是配合关系不是替代关系。在 LangGraph 中一个最简图通常长这样from typing import TypedDict from langgraph.graph import StateGraph, START, END class State(TypedDict): question: str answer: str def call_model(state: State): # 这里可以调用 LangChain 封装的模型 return {answer: f模型已收到问题{state[question]}} graph StateGraph(State) graph.add_node(call_model, call_model) graph.add_edge(START, call_model) graph.add_edge(call_model, END) app graph.compile() result app.invoke({question: LangGraph 是什么}) print(result[answer])这段代码定义了一个只有“开始 - 调用模型 - 结束”的图。虽然简单但已经能看出 LangGraph 的表达方式和 LCEL 有明显的区别流程不再是一个隐式的管道而是显式的节点和边。1.3 Agent 与 RAG两条主线如何结合在 LangChain 生态中有两个词出现频率最高Agent 和 RAG。RAGRetrieval-Augmented Generation检索增强生成解决的是“模型不知道私有知识”的问题。企业知识库、产品文档、法律条文这类数据不在模型训练集内或者更新频繁无法靠微调及时更新。RAG 的做法是把文档切块并向量化存储用户提问时先从向量库检索相关内容再把这些内容拼接到 Prompt 中让模型基于给定材料回答。这样做的好处是不需要重新训练模型知识可以随时更新并且回答时可以引用来源。Agent智能体解决的是“模型只能聊天、不能做事”的问题。Agent 的核心是让模型学会使用工具。典型流程是用户提出需求模型分析需求判断需要调用哪些工具调用工具并获取结果根据工具结果生成最终答案或继续下一步。举个例子用户问“今天北京适合跑步吗” 一个 Agent 可能会先调用天气查询工具获取北京天气再调用空气质量工具获取 PM2.5 数值最后综合判断给出建议。每一步都是由大模型决策的而不是开发者预设的固定流程。在实际项目中Agentic RAG是一个非常流行的架构用 Agent 做问题理解、工具选择和流程控制用 RAG 做知识检索。典型场景是用户问题涉及最新政策Agent 先判断“这个问题需要查知识库”于是触发 RAG 检索用户问“请帮我总结今天会议纪要并发送邮件”Agent 需要判断调用哪个工具会议记录工具、总结模型、邮件工具分步执行。本文后面的实战案例就会把 LangChain LangGraph Agent RAG 这四部分放在同一个项目里串联起来。2. 环境准备与版本说明2.1 环境要求在开始写代码之前先把环境准备好。LangChain 生态的版本迭代比较快不同版本之间 API 可能存在差异。本文示例以目前主流的写法为准如果你用的是旧版本如 0.0.x部分 API 可能需要调整。推荐环境如下依赖推荐版本/说明Python3.10 及以上langchain0.3.x 或 1.x建议使用最新的稳定版langchain-openai与 langchain 配套的最新版langgraph0.2.x 及以上langchain-community文档加载器等第三方组件chromadb轻量级向量数据库适合学习fastapi / uvicorn如果需要将项目封装成 API安装命令pip install langchain langchain-openai langgraph langchain-community chromadb fastapi uvicorn如果网络环境无法访问 OpenAI 接口可以使用兼容 OpenAI 格式的国内模型厂商服务如 DeepSeek、通义千问、智谱等只需要修改base_url和api_key即可代码结构基本不变。本文以 OpenAI 接口为例但思路完全通用。2.2 项目结构为了让实战部分更清晰我们先规划好项目目录langchain_project/ ├── .env # 环境变量配置 ├── requirements.txt # 依赖列表 ├── config.py # 全局配置 ├── data/ │ └── company_handbook.md # 示例知识库文档 ├── src/ │ ├── loader.py # 文档加载与切分 │ ├── retriever.py # 向量库与检索器 │ ├── agent.py # Agent 工具定义 │ └── graph.py # LangGraph 流程编排 └── main.py # 入口提供 Web API这个结构适合中小型项目src下每个文件职责单一方便后续扩展和维护。在config.py中统一管理配置import os from dotenv import load_dotenv load_dotenv() OPENAI_API_KEY os.getenv(OPENAI_API_KEY) OPENAI_BASE_URL os.getenv(OPENAI_BASE_URL, https://api.openai.com/v1) EMBEDDING_MODEL os.getenv(EMBEDDING_MODEL, text-embedding-3-small) LLM_MODEL os.getenv(LLM_MODEL, gpt-4o-mini) VECTOR_DB_PATH os.getenv(VECTOR_DB_PATH, ./chroma_db).env文件示例OPENAI_API_KEYyour_api_key_here OPENAI_BASE_URLhttps://api.openai.com/v1 LLM_MODELgpt-4o-mini EMBEDDING_MODELtext-embedding-3-small这里要提醒一下不要把api_key硬编码在代码里更不要提交到 Git 仓库。用环境变量或.env文件管理是基本要求。3. 核心原理拆解3.1 模型调用与结构化输出在 LangChain 中模型调用是最基础的能力。我们通常使用ChatOpenAI接口它封装了 OpenAI 的 Chat Completion API。from langchain_openai import ChatOpenAI from langchain_core.messages import SystemMessage, HumanMessage from config import OPENAI_API_KEY, OPENAI_BASE_URL, LLM_MODEL llm ChatOpenAI( modelLLM_MODEL, api_keyOPENAI_API_KEY, base_urlOPENAI_BASE_URL, temperature0.3 ) messages [ SystemMessage(content你是一个文档助手只能根据给定资料回答问题。), HumanMessage(content报销的审批流程是什么) ] resp llm.invoke(messages) print(resp.content)在企业项目中模型输出不能只是“一段文本”往往需要结构化的数据。比如让模型从用户问题中提取“意图”和“关键词”LangChain 提供了with_structured_output方法from pydantic import BaseModel, Field class QuestionAnalysis(BaseModel): intent: str Field(description问题意图可选值knowledge_base, general, tool) keywords: list[str] Field(description提取的关键词列表) needs_retrieval: bool Field(description是否需要检索知识库) structured_llm llm.with_structured_output(QuestionAnalysis) result structured_llm.invoke(请查一下远程办公的考勤规定) print(result.intent, result.keywords, result.needs_retrieval)结构化输出在 Agent 和 LangGraph 场景中非常重要因为流程的后续动作往往依赖前一步输出的结构化结果。3.2 RAG从文档加载到向量检索RAG 的完整流程可以拆成以下几步加载文档文本切分文本向量化存储到向量数据库检索。文档加载这一步LangChain Community 提供了大量 Loader。以 PyPDFLoader 和 TextLoader 为例# src/loader.py from langchain_community.document_loaders import PyPDFLoader, TextLoader def load_documents(file_path: str): if file_path.endswith(.pdf): loader PyPDFLoader(file_path) else: loader TextLoader(file_path, encodingutf-8) return loader.load()加载完成后是文本切分。切分这一步很多人容易忽略但其质量直接决定了检索效果。切分太短语义不完整切分太长噪音太多且消耗 Token。推荐使用RecursiveCharacterTextSplitter它会按优先级依次尝试不同的分隔符进行切分from langchain_text_splitters import RecursiveCharacterTextSplitter text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap80, separators[\n\n, \n, 。, , , , ] ) chunks text_splitter.split_documents(documents) print(f切分成 {len(chunks)} 个片段)关于切分参数我做了一张速查表参数作用经验值chunk_size每个块的最大字符数300~800取决于模型上下文窗口chunk_overlap相邻块的重叠字数10%~20% 的 chunk_sizeseparators优先使用的分隔符顺序先大后小优先按段落切接下来是向量化与存储。Embedding 模型负责把文本转成向量一个常见的经验法则是Embedding 模型要选择与业务语言匹配的模型中文场景优先考虑对中文支持好的模型。存储端使用 Chroma 可以做到零配置启动适合学习和原型验证# src/retriever.py from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma from config import OPENAI_API_KEY, OPENAI_BASE_URL, EMBEDDING_MODEL, VECTOR_DB_PATH def build_vector_store(chunks): embeddings OpenAIEmbeddings( modelEMBEDDING_MODEL, api_keyOPENAI_API_KEY, base_urlOPENAI_BASE_URL ) vector_store Chroma.from_documents( documentschunks, embeddingembeddings, persist_directoryVECTOR_DB_PATH ) return vector_store def get_retriever(vector_store, k4): return vector_store.as_retriever(search_kwargs{k: k})在向量数据库中检索本质上是一个“相似度搜索”的过程。用户提出问题后先通过同一个 Embedding 模型把问题转成向量再去数据库中寻找最相似的 k 个片段。这 k 个片段会作为上下文传给大模型。3.3 Agent工具注册与任务规划Agent 是 LangChain 生态中另一个核心能力。如果把 RAG 理解成“给模型配一本参考书”那么 Agent 就是“给模型配一双手”。LangChain 中定义一个工具非常方便。比如定义一个查询员工信息的工具from langchain_core.tools import tool tool def get_employee_info(employee_name: str) - str: 根据员工姓名查询部门、职级和入职日期。 # 这里假设从内部系统读取实际可能会调用 SQL 或 HTTP API data { 张三: 研发部 | 高级工程师 | 2020-03-15, 李四: 市场部 | 运营专员 | 2022-07-01, } return data.get(employee_name, 未找到该员工信息)使用tool装饰器定义工具时函数名、参数类型、docstring 都会被 LangChain 解析作为模型的“工具说明书”。模型会根据用户问题判断是否需要调用这个工具以及传什么参数。创建 Agent 的常见方式是使用create_react_agent其中 ReAct 是一种“思考-行动-观察”的循环范式from langchain.agents import create_react_agent from langchain.agents.output_parsers import ReActOutputParser from langchain_core.prompts import PromptTemplate tools [get_employee_info] prompt PromptTemplate.from_template( 你是一个企业内部助手。请使用工具回答用户问题。\n问题{input} ) agent create_react_agent(llmllm, toolstools, promptprompt)实际开发中建议直接使用create_agent方法LangChain 0.3 / LangGraph 风格因为新版对 Agent 的执行过程做了更好的封装from langchain.agents import create_agent agent create_agent(llmllm, toolstools, promptprompt) result agent.invoke({input: 帮我查一下张三的部门}) print(result[output])Agent 之所以强大是因为它能根据用户需求动态决定调用哪个或多个工具。但这同时也带来一个问题模型可能反复调用工具或陷入死循环。所以生产环境中要用 LangGraph 对 Agent 的执行流程进行更细粒度的控制。3.4 LangGraph用图来管理状态和复杂流程LangGraph 的核心设计理念是“状态流Stateful Graph”。它与 LangChain 的 LCEL 最大的区别在于LCEL 是预定义好的直线流程LangGraph 则允许在运行过程中动态选择路径。再来看一个带条件路由的例子。这个图会根据用户问题判断是否走 RAG 检索分支from typing import TypedDict from langgraph.graph import StateGraph, START, END from langgraph.graph.state import CompiledStateGraph class GraphState(TypedDict): question: str docs: list[str] final_answer: str def analyze_question(state: GraphState): question state[question] # 用结构化输出判断是否需要检索知识库 analysis structured_llm.invoke(question) return {needs_retrieval: analysis.needs_retrieval} def route_after_analysis(state: GraphState): if state.get(needs_retrieval): return retrieve_docs return generate_directly def retrieve_docs(state: GraphState): retriever get_retriever(vector_store, k4) docs retriever.invoke(state[question]) return {docs: [doc.page_content for doc in docs]} def generate_answer(state: GraphState): if state.get(docs): context \n\n.join(state[docs]) prompt_msg f根据以下资料回答问题\n\n{context}\n\n问题{state[question]} else: prompt_msg f直接回答问题{state[question]} resp llm.invoke(prompt_msg) return {final_answer: resp.content} graph StateGraph(GraphState) graph.add_node(analyze_question, analyze_question) graph.add_node(retrieve_docs, retrieve_docs) graph.add_node(generate_answer, generate_answer) graph.add_edge(START, analyze_question) graph.add_conditional_edges(analyze_question, route_after_analysis, { retrieve_docs: retrieve_docs, generate_directly: generate_answer }) graph.add_edge(retrieve_docs, generate_answer) graph.add_edge(generate_answer, END) app graph.compile()这段代码展示了一个非常典型的 RAG 分支控制流程。route_after_analysis是一个条件路由函数它根据分析结果返回下一个节点的名称。LangGraph 会根据返回值找到对应的边。这个能力在真实项目中非常实用。比如闲聊问题直接让大模型回答不需要检索业务知识问题触发 RAG 检索涉及数据库查询的问题路由到 SQL 工具节点。在 LangGraph 中记忆和状态也是显式可管理的。State 中的数据在节点间传递你可以在节点中随时读取或更新。对于更复杂的对话场景可以在图外部维护一个消息历史列表并在调用节点时将其注入状态。4. 完整实战知识库问答 Agent 路由编排理论部分看完了下面进入真正的实战。这个案例会实现一个比较接近真实业务的小系统企业内部智能助手。需求如下用户提问时先判断问题类型如果是企业内部制度问题进入 RAG 知识库检索如果是“查询员工信息”这类需要工具的问题交给 Agent 调用工具如果是普通闲聊直接让大模型回答。整个流程用 LangGraph 编排同时用到 LangChain 的模型封装、Retriever 和 Agent 工具能力。4.1 编写入口文件 main.py先实现一个简单的 API 入口使用 FastAPI 提供服务# main.py from fastapi import FastAPI from pydantic import BaseModel from src.graph import app as graph_app api FastAPI(title内部智能助手) class QueryRequest(BaseModel): question: str class QueryResponse(BaseModel): answer: str api.post(/ask, response_modelQueryResponse) def ask(request: QueryRequest): result graph_app.invoke({question: request.question}) return QueryResponse(answerresult[final_answer]) if __name__ __main__: import uvicorn uvicorn.run(api, host0.0.0.0, port8000)4.2 文档加载与向量库构建准备一份简单的手册文件data/company_handbook.md# 员工手册 ## 考勤制度 员工实行弹性工作制核心工作时间为上午10点至下午4点。 每月允许迟到3次超过后每次扣发当日绩效的10%。 ## 报销制度 差旅报销需在行程结束后7天内提交申请。 单笔金额超过2000元需要部门负责人审批。 ## 远程办公 员工每周可申请最多2天远程办公。 申请需提前1天在OA系统提交。然后构建向量库。实际项目中如果数据量较大或更新频繁建议单独写一个构建脚本避免每次启动都重新向量化。这里为了演示方便在src/loader.py中写一个构建函数# src/loader.py from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from src.retriever import build_vector_store def build_and_save_vector_store(file_path: str ./data/company_handbook.md): loader TextLoader(file_path, encodingutf-8) documents loader.load() splitter RecursiveCharacterTextSplitter( chunk_size300, chunk_overlap50, separators[\n\n, \n, 。, , ] ) chunks splitter.split_documents(documents) build_vector_store(chunks) print(f向量库构建完成共 {len(chunks)} 个文档块)4.3 定义检索器# src/retriever.py from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma from config import OPENAI_API_KEY, OPENAI_BASE_URL, EMBEDDING_MODEL, VECTOR_DB_PATH def get_retriever(k: int 3): embeddings OpenAIEmbeddings( modelEMBEDDING_MODEL, api_keyOPENAI_API_KEY, base_urlOPENAI_BASE_URL ) vector_store Chroma( embedding_functionembeddings, persist_directoryVECTOR_DB_PATH ) return vector_store.as_retriever(search_kwargs{k: k})4.4 定义 Agent 工具在src/agent.py中定义内部工具。为了体现 Agent 的能力我们定义两个工具一个查员工信息一个查部门 KPI。# src/agent.py from langchain_core.tools import tool tool def get_employee_info(employee_name: str) - str: 根据员工姓名查询部门、职级、入职日期。参数为员工中文姓名。 database { 张三: 研发部 | 高级工程师 | 2020-03-15, 李四: 市场部 | 运营专员 | 2022-07-01, } return database.get(employee_name, f未找到员工 {employee_name} 的信息) tool def get_department_kpi(department_name: str) - str: 根据部门名称查询本季度 KPI 完成率。参数为部门中文名称。 kpi_data { 研发部: 92%, 市场部: 87%, 销售部: 78%, } return kpi_data.get(department_name, f未找到部门 {department_name} 的 KPI 数据)4.5 使用 LangGraph 编排完整流程现在进入核心部分src/graph.py。我们需要定义三个阶段意图分析节点决定走 RAG、Agent 还是直接回答执行节点分别执行 RAG、Agent 或直接生成汇总输出节点。# src/graph.py from typing import TypedDict, Optional from langgraph.graph import StateGraph, START, END from langchain_openai import ChatOpenAI from langchain.agents import create_agent from langchain_core.prompts import ChatPromptTemplate from pydantic import BaseModel, Field from config import OPENAI_API_KEY, OPENAI_BASE_URL, LLM_MODEL from src.retriever import get_retriever from src.agent import get_employee_info, get_department_kpi # ---------- 初始化 ---------- llm ChatOpenAI( modelLLM_MODEL, api_keyOPENAI_API_KEY, base_urlOPENAI_BASE_URL, temperature0.2 ) class RouteDecision(BaseModel): route: str Field(description路由决策rag / agent / general) reasoning: str Field(description判断理由) router_llm llm.with_structured_output(RouteDecision) # 定义 Agent工具调用 agent_tools [get_employee_info, get_department_kpi] agent_prompt ChatPromptTemplate.from_messages([ (system, 你是企业内部助手请根据用户问题选择合适的工具。), (human, {input}) ]) agent_executor create_agent( llmllm, toolsagent_tools, promptagent_prompt ) # ---------- 状态定义 ---------- class WorkflowState(TypedDict): question: str route: Optional[str] final_answer: Optional[str] # ---------- 节点函数 ---------- def decide_route(state: WorkflowState): question state[question] decision router_llm.invoke(question) return {route: decision.route} def run_rag(state: WorkflowState): retriever get_retriever(k3) docs retriever.invoke(state[question]) context \n\n.join([doc.page_content for doc in docs]) prompt_msg ( 你是企业知识库助手。请严格根据以下资料回答问题 如果资料中没有答案请明确说明。\n\n f资料\n{context}\n\n问题{state[question]} ) resp llm.invoke(prompt_msg) return {final_answer: resp.content} def run_agent(state: WorkflowState): result agent_executor.invoke({input: state[question]}) return {final_answer: result[output]} def run_general(state: WorkflowState): resp llm.invoke( f你是一个友好的助手请用简洁的方式回答{state[question]} ) return {final_answer: resp.content} # ---------- 条件路由 ---------- def route_decision(state: WorkflowState): route state.get(route, general) if route rag: return run_rag elif route agent: return run_agent return run_general # ---------- 构建图 ---------- graph StateGraph(WorkflowState) graph.add_node(decide_route, decide_route) graph.add_node(run_rag, run_rag) graph.add_node(run_agent, run_agent) graph.add_node(run_general, run_general) graph.add_edge(START, decide_route) graph.add_conditional_edges(decide_route, route_decision, { run_rag: run_rag, run_agent: run_agent, run_general: run_general }) graph.add_edge(run_rag, END) graph.add_edge(run_agent, END) graph.add_edge(run_general, END) app graph.compile()代码说明WorkflowState定义了图上共享的数据结构目前有question、route、final_answer三个字段。decide_route节点用with_structured_output让模型输出一个标准结构方便后续条件路由。add_conditional_edges是关键它告诉 LangGraph从decide_route节点结束后不要直接走某条固定边而是调用route_decision函数动态判断下一步。三条执行分支最终都汇聚到END当然你也可以在它们之后再加一个统一的输出格式化节点。4.6 运行与验证启动前先构建向量库如果还没有构建的话python -c from src.loader import build_and_save_vector_store; build_and_save_vector_store()然后启动 APIuvicorn main:api --host 0.0.0.0 --port 8000 --reload用 curl 或者 Postman 测试curl -X POST http://localhost:8000/ask \ -H Content-Type: application/json \ -d {question: 公司远程办公制度是什么}预期返回结果类似{answer: 根据公司手册员工每周可申请最多2天远程办公申请需提前1天在OA系统提交。}再测试 Agent 场景curl -X POST http://localhost:8000/ask \ -H Content-Type: application/json \ -d {question: 张三在哪个部门}预期返回结果会调用get_employee_info工具返回类似{answer: 张三在研发部职级为高级工程师入职日期为2020年3月15日。}如果路由判断为 generalcurl -X POST http://localhost:8000/ask \ -H Content-Type: application/json \ -d {question: 你好你能做什么}这时直接由大模型回答不触发知识库检索和工具调用。这个示例虽然简单但已经把四个核心模块串起来了LangChain 负责模型封装、Prompt、结构化输出和 Agent 工具LangGraph 负责流程控制、状态管理和条件路由RAG 负责知识库检索Agent 负责工具调用。你可以在此基础上扩展增加更多工具、接入真实数据库、把检索结果做重排优化、加对话历史记忆等。5. 常见问题与排查思路在实际写代码的过程中你可能会遇到下面这些高频问题。我整理成表格方便快速对照问题现象常见原因解决思路ModuleNotFoundError: No module named langchain_openai未安装对应包执行pip install langchain-openaiAPI 请求超时网络无法访问模型接口检查网络代理设置或切换为国内兼容接口向量库返回结果不相关文本切分不合理或 Embedding 模型不匹配调整 chunk_size/overlap换用更优质的中文 Embedding 模型Agent 反复调用工具或死循环缺少停止条件或 tool 描述不清晰在 LangGraph 中增加最大迭代次数限制优化 tool 的 docstring结构化输出报错模型版本不支持 function calling更换支持结构化输出的模型或版本CSV/PDF 加载乱码编码问题或 PDF 扫描件检查文件编码PDF 使用 OCR 预处理Chroma 持久化目录已存在导致冲突重复创建向量库确认无需更新时可复用已有目录或清空后重建下面再展开几个比较隐蔽的问题。5.1 Agent 工具不再被调用很多开发者会碰到的场景是明明定义了工具模型却一直不调用直接编造答案。根本原因通常是工具描述不够清晰或者模型能力不足以理解何时使用工具。工具描述直接影响模型对工具的理解。以get_employee_info为例tool def get_employee_info(employee_name: str) - str: 根据员工姓名查询部门、职级、入职日期。参数为员工中文姓名。这段描述清晰地说明了“什么场景用”“参数是什么”“返回什么”。如果你只写“员工信息查询”模型就不容易判断使用时机。另外如果使用的是较小的模型如 7B 量级工具调用的准确性会明显下降。在生产项目中要么换用更强模型处理工具调用要么在系统中加入规则预判。5.2 检索结果质量差RAG 最让人头疼的问题是“检索到的东西不对”。常见原因有以下几类文本切分时把完整语义切断了。比如把“员工每周可申请最多 2 天远程办公”这句拆成两半。解决方法是调大chunk_overlap并优先按段落切。Embedding 模型与语言不匹配。如果业务是中文建议使用对中文理解更好的 Embedding 模型。用户问题表述和文档原文差别太大。比如文档写“弹性工作制”用户问“上班时间”。解决方法是可以增加一层 query 改写先让模型把口语化问题改写为更接近文档语序的关键词。只取 top-k没有做相关性过滤。对于 3 个结果中有 1 个明显不相关的场景建议在 Prompt 中明确要求“如果资料中没有答案请说明”避免模型强行编造。5.3 LangGraph 状态更新不符合预期在编写 LangGraph 节点时一个容易忽视的点是节点函数返回的 dict 只会更新 State 中同名字段但不会触发其他节点的额外逻辑。如果你发现某个状态没有被正确传递可以先在节点函数中加日志def decide_route(state: WorkflowState): question state[question] decision router_llm.invoke(question) print(路由决策:, decision.route) return {route: decision.route}另外注意StateGraph 中每个节点函数的参数类型是dict而不是WorkflowState实例但你可以用TypedDict做类型提示方便 IDE 自动补全。6. 最佳实践与工程建议这部分是落地经验总结也是从“能跑”走向“能上线”的关键。6.1 提示词与路由设计在 Agentic RAG 架构中路由决策的质量决定了整个系统的效果。建议路由分类不要太细最开始保持 3 类左右rag / agent / general后续再根据日志扩展路由 Prompt 中明确给出示例比如“涉及考勤、报销、差旅等制度问题属于 rag”结构化输出定义好后不要频繁修改字段名否则需要重新验证模型输出如果路由判断和 RAG 效果都不稳定可以先做“同时抓取”再让模型在汇总节点选择用结果质量来兜底。6.2 向量库与 Embedding 选型向量库选型上如果数据量在百万级以下、并发量不高Chroma 或 FAISS 足够如果数据量大、需要分布式部署和高并发生产环境可以考虑 Milvus、Qdrant 等专业向量数据库。Embedding 模型是 RAG 效果的上限。建议离线用小数据集评估检索准确率中英文混合场景不要使用纯英文 Embedding 模型上线后持续收集“检索为空但用户给出了满意答案”和“检索到了结果但被模型忽略”的案例针对性地调整。6.3 Agent 安全边界Agent 能调用工具这带来了安全风险。企业项目必须做到工具默认最小权限Agent 只能访问完成任务所需的数据和接口不能随意执行任意 SQL 或删除操作所有工具调用需记录日志包括模型决策、传入参数、工具返回结果对危险工具设置二次确认比如“发送邮件”“执行删除操作”这类动作建议在流程中加入人工确认节点限制最大迭代步数在 LangGraph 中设置合理的recursion_limit避免 Agent 陷入循环导致资源浪费。在 LangGraph 中控制最大步数很简单result app.invoke( {question: ...}, config{recursion_limit: 10} )6.4 可观测性与日志LLM 应用的不确定性比传统软件高很多日志系统至关重要。建议给每个请求分配 trace_id贯穿入口、路由、检索、模型调用、工具调用全过程记录每次调用的 Token 消耗用于成本分析记录每个节点的耗时定位性能瓶颈对模型输出做版本管理方便回滚。目前 LangSmith 是 LangChain 生态的可观测性方案但即使是简单项目也建议至少把关键日志打到本地文件import logging logging.basicConfig( filenameapp.log, levellogging.INFO, format%(asctime)s | %(name)s | %(levelname)s | %(message)s )6.5 性能优化方向在企业级项目中性能优化通常集中在以下几方面检索优化构建向量索引、设置合理的 embedding batch size、必要时引入重排序Rerank模型流式输出大模型生成本质是流式的用 FastAPI 的 StreamingResponse 可以显著提升用户体验缓存对高频、重复的问题做语义缓存命中缓存时不再走模型调用链路并发控制LLM API 通常有速率限制需要使用 Token Bucket 或 Semaphore 做限流。下面给出一段简单的流式 API 参考from fastapi.responses import StreamingResponse def stream_answer(question: str): # 这里简化处理实际应使用 langchain 的 stream 方法 for chunk in llm.stream(question): yield chunk.content api.post(/ask/stream) def ask_stream(request: QueryRequest): return StreamingResponse(stream_answer(request.question), media_typetext/plain)7. 总结与学习路线现在再回头看开头的问题LangChain 学习其实有一条主线先用 LCEL 把模型调用和 Prompt 组装起来理解 LangChain 的抽象然后引入 RAG解决私有知识问答问题接着引入 Agent学习如何让模型调用工具最后用 LangGraph 把整个流程编排成可控的图。如果你是在校学生或转行开发者建议按以下顺序推进第一周掌握 LCEL 语法理解 Prompt、Model、Output Parser 的组合方式第二周实现一个最简 RAG跑通文档加载、切分、检索、生成全流程第三周学习 Agent 工具定义理解 ReAct 循环原理第四周用 LangGraph 重构前几周的代码加入条件路由和状态管理第五周以后关注生产化议题包括流式输出、缓存、可观测性、安全控制和评估体系。对于已经在企业做落地的开发者优先关注两个风险点RAG 的检索质量和 Agent 的安全边界。这两个点如果做得不好项目很容易在 demo 阶段漂亮却无法支撑真实业务。最后想强调一点LangChain、LangGraph 这些框架发展速度非常快API 会变化版本会迭代但底层的思路是稳定的。LLM 应用的本质无非是“模型能力 外部数据 工具调用 可控流程”的组合。把这套思路吃透无论框架怎么变你都能快速上手。如果本文对你有帮助欢迎收藏备用。下一步可以试着把示例中的代码改成你自己的场景比如换成公司真实文档、接入内部接口或者把 API 服务部署到测试环境。动手写一遍比看十遍效果都好。