很多开发者学 LangChain 的时候,都会遇到一种奇怪的拧巴感:查 RAG 教程,能看到一堆langchain_community拼接代码;查 Agent 教程,又有人告诉你“LangChain 做复杂 Agent 容易乱,要上 LangGraph”;再往后,MCP 这个概念又冒出来了,什么“AI 万能插头”“工具调用新标准”。
一套技术栈里塞了五个热门词:LangChain、LangGraph、Agent、RAG、MCP。每个单拎出来都有人讲,但连在一起是什么关系,从哪开始学,先学哪个,很多人其实没理清楚。更麻烦的是,网上很多教程只讲“怎么调 API”,不解释“为什么要这么设计”。于是你抄完代码能跑,换一个业务场景就不知道怎么改了。
这篇教程想解决的就是这个问题。先说结论:LangChain 负责把大模型和外部数据、模型、输出解析这些“部件”标准化;LangGraph 负责把复杂流程变成有状态、可控制、可回退的图;RAG 解决知识不足问题;Agent 解决任务自动拆解问题;MCP 解决工具和数据接入标准问题。它们不是替代关系,而是不同层级的分工。
读完这篇文章,你可以亲手搭出一个带私有知识库、能调用外部工具、用 LangGraph 控制状态的 Agent 应用。整个过程中,我会把每个概念放到实际场景里讲,并给出完整的可运行代码和排查思路。
1. 2026 年为什么还要重新学一遍 LangChain 和 LangGraph
先回应一个常见的疑问。最近两年经常能看到这样的说法:“LangChain 太抽象了,写复杂业务不如直接调 OpenAI SDK”“LangGraph 不就是一个状态机包装吗,自己写也不难”。
这两个观点都有道理,但都不全面。2026 年看 LangChain 和 LangGraph,重点不是它们能做到单次调用 API 做不到的事,而是它们在工程化上帮你省了三类成本:一是组件标准化成本,你可以无缝切换不同的向量库、模型供应商和文档加载器;二是流程可观测成本,LangGraph 的图结构天然暴露了每个节点的输入输出,方便调试和监控;三是工具接入成本,配合 MCP 之后,Agent 不再需要为每个内部系统单独写一套函数封装。
说句更直白的话:如果你只是做一个“后端调 GPT 的小工具”,确实不需要 LangChain;但如果你要做“一个能回答公司内部问题、能查请假余额、能订会议室、还能在答错时回溯流程”的业务系统,靠硬编码维护成本会非常高。LangGraph 这种有状态编排层的意义就在这里。
本文定位是零基础到实战。所谓的“零基础”,不是说你完全不懂 Python,而是指你对 LangChain、LangGraph、Agent、RAG、MCP 这些术语没有体系化认识。你需要具备最基本的 Python 能力,然后跟着一步步走。
2. Agent、RAG、LangChain、LangGraph、MCP 到底是什么关系
很多教程会把这几件事混在一起讲,这是新手最大的认知负担来源。先把它们拆开。
2.1 用一句话区分五个概念
| 概念 | 一句话解释 | 解决的问题 |
|---|---|---|
| LangChain | 一套大模型应用开发的组件标准 | 把模型、提示词、文档加载、向量库等抽象成可组合的模块 |
| RAG | 检索增强生成 | 让模型回答私域知识问题,而不是全靠训练记忆 |
| Agent | 能自主决策和调用工具的智能体 | 把一个复杂任务拆成多步,并调用外部能力完成 |
| LangGraph | 用图结构编排有状态流程 | 解决 Agent 多步执行中的状态维护、分支、回退、循环问题 |
| MCP | 模型上下文协议 | 统一外部工具和数据源接入方式,避免每个系统一套接入协议 |
2.2 LangChain 和 LangGraph 的分工
很多人问“LangGraph 和 LangChain 的区别是什么”。这两者最关键的差异在于编程模型。
LangChain 的核心抽象是 Chain,也就是“输入经过一系列模块,得到一个输出”。它适合流程相对固定、分支比较少的场景,比如“先检索,再拼提示词,再调模型,再解析输出”。对这种 pipeline,LangChain 的 LCEL 表达式使用起来非常简洁。
LangGraph 的核心抽象是图,也就是“由节点和边组成的有状态工作流”。每个节点是一个函数,每个边决定下一步走向;节点之间通过共享的 State 传递数据。这个模型适合流程中有条件分支、循环、人工审批、需要记录多轮步骤状态的场景。Agent 本质上就是一个循环:模型决定调用什么工具,工具返回结果,模型再决定下一步,直到认为任务完成。
所以,更准确的判断是:LangGraph 在解决 LangChain 不适合处理的那部分复杂流程问题。2026 年的实际项目中,LangChain 组件依然负责底层拼接,LangGraph 负责上层控制流。两者互补而非互斥。
2.3 Agent 和 RAG 的关系
RAG 是一种具体的检索增强生成技术,而 Agent 是一种任务执行范式。Agent 内部完全可以复用 RAG 能力,比如 Agent 发现用户问题涉及私域文档,就调用一个“文档检索工具”去查资料;查完之后,再用查到的内容生成答案。用行业术语说,这种形态就是Agentic RAG:Agent 不再只做一次检索,而是根据对话状态动态决定是否需要检索、查几次、是否追问。
2.4 MCP 在什么时候有用
MCP 要解决的是“每个 AI 应用都要重新连接一遍公司系统”的问题。没有 MCP 的时候,Agent 要接数据库、企业内部 API、文件系统,通常要写大量自定义函数,且每个函数只对当前 Agent 项目生效。有了 MCP 后,数据提供方实现一个标准 MCP Server,Agent 通过 MCP 协议加载同一个 Server 提供的工具,这叫“一次接入,多处复用”。
整条技术链路可以这样理解:RAG 提供知识,LangChain 提供组件,LangGraph 提供控制流,MCP 提供工具接入标准,Agent 把这些组合成最终的应用形态。
3. 环境准备与项目初始化
下面进入实操。本文采用 Python 编写,目标环境以你当前项目为准。由于 LangChain、LangGraph 和 MCP 的工具更新速度都比较快,我不会写死某个固定版本,而是给出通用的依赖安装方式。如果你在安装时遇到版本冲突,建议优先参考官方文档中的版本匹配说明。
3.1 创建虚拟环境
建议使用venv或conda创建独立环境,避免污染全局 Python。
python -m venv .venv source .venv/bin/activate # Windows 下执行 .venv\Scripts\activate3.2 安装依赖
pip install --upgrade langchain langchain-openai langchain-community langchain-text-splitters langgraph langchain-mcp-adapters mcp faiss-cpu pypdf python-dotenv这里简单说明一下每个包的用途:
| 包名 | 用途 |
|---|---|
| langchain | 框架核心,提供 LCEL、链式组合、通用接口 |
| langchain-openai | OpenAI 模型的 LangChain 接入类 |
| langchain-community | 社区贡献的文档加载器、向量库适配器等 |
| langchain-text-splitters | 文本分割器 |
| langgraph | 状态图编排框架 |
| langchain-mcp-adapters | 把 MCP Server 暴露的工具封装成 LangChain 工具 |
| mcp | MCP 官方 Python SDK |
| faiss-cpu | 本地向量检索库 |
| pypdf | 读取 PDF 文档 |
| python-dotenv | 读取 .env 环境变量文件 |
3.3 配置模型访问
在项目根目录新建.env文件:
# 文件路径:.env OPENAI_API_KEY=sk-xxxxx OPENAI_API_BASE=https://api.openai.com/v1 # 如果使用国内兼容 OpenAI 协议的模型服务,可以修改上面的地址如果你的场景不允许调用外部模型服务,可以改用本地模型。LangChain 通过ChatOpenAI兼容 OpenAI 协议,很多本地推理框架也支持该协议,只需要修改base_url指向本地服务地址即可。后面的代码我会统一用环境变量读取的方式,便于切换。
3.4 项目目录规划
langchain-rag-agent/ ├── .env ├── requirements.txt ├── docs/ # 放私有知识文档 ├── rag_demo.py # 第 4 节的 RAG 示例 ├── graph_demo.py # 第 5 节的 LangGraph 示例 ├── agent_demo.py # 第 6 节的 Agent 示例 ├── mcp_server.py # 第 7 节的 MCP Server └── mcp_client_demo.py # 第 7 节的 MCP 接入示例4. RAG 第一阶段:从普通问答到文档知识库
先不要急着写 Agent。把 RAG 跑通,是后续所有复杂功能的基础。
4.1 准备一份私有文档
在docs目录下放一个文本文件,比如company_manual.txt,内容写一段公司内部的请假制度。这里注意,文档内容必须是你能合法授权用于检索的资料。在公司真实场景中,文档的访问权限管理要和 RAG 系统同步考虑,这我放到后面最佳实践里再展开。
4.2 文档加载与分割
# 文件路径:rag_demo.py import os from dotenv import load_dotenv load_dotenv() from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings, ChatOpenAI from langchain_community.vectorstores import FAISS from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough loader = TextLoader("./docs/company_manual.txt") documents = loader.load() text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=50, ) chunks = text_splitter.split_documents(documents) print(f"文档被切割为 {len(chunks)} 个片段")为什么要切分文档?两个原因。一是大模型输入有 token 上限,整份文档放不进提示词;二是“检索相关性”需要有粒度概念,越长的文档,命中后带进来的噪声越多。chunk_size=500表示每个片段大约 500 个字符,chunk_overlap=50表示相邻片段有 50 个字符重叠,避免关键词正好落在切割边缘导致检索丢失。
4.3 向量化与构建检索器
embeddings = OpenAIEmbeddings(model="text-embedding-3-small") vectorstore = FAISS.from_documents(chunks, embedding=embeddings) retriever = vectorstore.as_retriever(search_kwargs={"k": 4})这一段做的事情是:把每个文本片段通过 Embedding 模型转成向量,存入向量库;查询时,用户问题也被转成向量,然后通过向量相似度召回最相关的 4 个片段。
4.4 拼接提示词并生成回答
prompt = ChatPromptTemplate.from_messages([ ( "system", "你是一个企业内部知识库助手,请根据提供的资料回答用户问题。" "如果资料中没有答案,请直接说明不知道,不要编造。", ), ( "human", "资料:\n{context}\n\n问题:{question}", ), ]) llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) def format_docs(docs): return "\n\n".join([doc.page_content for doc in docs]) rag_chain = ( { "context": retriever | format_docs, "question": RunnablePassthrough(), } | prompt | llm | StrOutputParser() ) if __name__ == "__main__": answer = rag_chain.invoke("新员工请假的流程是什么?") print(answer)这是 LangChain 最核心的 LCEL 写法:左边的字典定义了依赖关系,context由 retriever 计算得到并交给format_docs格式化,question直接透传;然后经过prompt格式化,经过llm生成,最后经过StrOutputParser输出字符串。
运行方式:
python rag_demo.py如果输出中能看到根据文档内容整理出的请假流程,而不是模型“自由发挥”,说明 RAG 链路已经通了。
4.5 RAG 阶段的常见认知坑
这个阶段有几点需要提前说明。第一,RAG 的检索质量不等于模型生成质量,很多回答不准确的问题根源是“没召回正确内容”,而不是“模型不行”;第二,k=4只是一个起点,具体 k 值要根据文档粒度调整;第三,向量检索依赖 Embedding 模型,不同 Embedding 模型对中文、代码、表格的支持差异很大,必要时可以做检索评测。
5. LangGraph 入门:用状态图改写普通问答流程
RAG 跑通后,你会发现它有一个明显局限:流程固定,没有分支。用户问的问题可能根本不需要检索,也可能需要连续检索两次。这时候就轮到 LangGraph 上场。
5.1 最小 LangGraph 应用
LangGraph 的抽象可以浓缩为三个概念:State、Node、Edge。
- State:全局共享的数据结构,所有节点都能读取和写入。
- Node:一个普通 Python 函数,接收 State,返回一个新 State 字段。
- Edge:从一个节点到另一个节点的连接,可以带条件。
下面先把第 4 节的 RAG 流程改造成图结构:
# 文件路径:graph_demo.py from typing import TypedDict, List from langgraph.graph import StateGraph, END from langchain_core.output_parsers import StrOutputParser class AgentState(TypedDict): question: str context: List[str] answer: str def retrieve_node(state: AgentState): # 这里简化处理,实际复用上面 RAG 示例中的 retriever docs = retriever.invoke(state["question"]) return {"context": [d.page_content for d in docs]} def generate_node(state: AgentState): context = "\n\n".join(state["context"]) prompt_formatted = prompt.invoke({"context": context, "question": state["question"]}) response = llm.invoke(prompt_formatted) return {"answer": StrOutputParser().invoke(response)} graph = StateGraph(AgentState) graph.add_node("retrieve", retrieve_node) graph.add_node("generate", generate_node) graph.set_entry_point("retrieve") graph.add_edge("retrieve", "generate") graph.add_edge("generate", END) app = graph.compile() if __name__ == "__main__": result = app.invoke({"question": "公司的请假流程是什么?"}) print(result["answer"])运行后效果和之前 RAG 示例一样,但流程被显式地拆成节点了。不要小看这一步变化:把流程变成图之后,你可以在任意节点之间插入“判断逻辑”,可以被外部系统触发,也可以记录每一步的耗时、token 消耗、检索命中情况。
5.2 为什么需要状态
你可能想问,普通链式调用也能传数据,为什么 LangGraph 强调 State?
区别在于普通链式调用是线性的,A 的输出只能传给 B;而图结构中的每个节点都只与 State 交互。比如有一个节点负责“判断用户问题是否需要检索”,它改写question字段后,后面的检索节点和生成节点都能感知。再比如你希望支持“知识库检索不到时,换一个检索器再搜一次”,这在普通链式流程里要写大量 if-else,而在 LangGraph 里只需要加一个条件边。
LangGraph 的 State 还继承了 LangChain 的管道协议,支持流式输出、状态持久化和断点恢复。对生产环境来说,这意味着你的 Agent 执行到一半挂了,可以从最近一次持久化状态恢复,而不是从头重跑。
6. Agent 实战:让模型学会调用外部工具
接下来做真正意义上的 Agent。这里的核心变化是:模型不再只是“生成文字”,而是可以决定“调用工具”。
6.1 什么是工具调用
工具调用(Tool Calling)是模型输出的一种结构化消息。它不是让模型真的去执行代码,而是让模型从预设工具列表中选择一个,并生成调用参数。真正的执行由你的程序完成,执行结果再交回给模型。
比如用户问“我剩余年假还有几天”,模型没有能力查数据库,但它可以决定调用get_user_leave_balance工具,参数是user_id。你的程序执行这段逻辑,返回后,模型根据工具结果生成最终回答。
6.2 用 LangGraph 快速构建 Agent
LangGraph 提供了create_react_agent这个预置入口,适合大部分入门场景。它实现了 ReAct 循环:模型思考 -> 调用工具 -> 观察结果 -> 再思考。
# 文件路径:agent_demo.py from dotenv import load_dotenv load_dotenv() from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langgraph.prebuilt import create_react_agent @tool def get_user_leave_balance(user_id: str) -> str: """查询员工剩余年假天数,参数为员工工号。""" # 实际项目中,这里应该访问权限可控的业务系统或数据库 leave_data = { "1001": "剩余 6 天", "1002": "剩余 3 天", } return leave_data.get(user_id, "查无此员工") tools = [get_user_leave_balance] llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) agent = create_react_agent(llm, tools) if __name__ == "__main__": result = agent.invoke({"messages": [{"role": "user", "content": "工号1001的员工还剩几天年假?"}]}) print(result["messages"][-1].content)这段代码里的@tool装饰器是关键。它把普通函数变成了符合 LangChain 工具规范的对象,函数名、docstring、参数签名会被自动传给模型,作为模型选择工具的参考信息。
6.3 给 Agent 接上 RAG 工具
刚才的 Agent 只能调用一个模拟函数,还不能查询文档。把 RAG 检索封装成工具后,Agent 就同时具备“查文档”和“查内部系统”的能力:
from langchain.tools.retriever import create_retriever_tool retriever_tool = create_retriever_tool( retriever, name="company_manual_search", description="搜索公司内部制度文档。当问题涉及请假制度、报销规范、考勤规则时,请先调用此工具。", ) tools = [get_user_leave_balance, retriever_tool]这样,用户问“请假流程是什么”,Agent 会调用company_manual_search检索文档后回答;用户问“我年假剩几天”,Agent 会调用get_user_leave_balance。这已经是一个有实际业务价值的 Agentic RAG 形态。
6.4 LangGraph 还可以做什么
create_react_agent虽然方便,但它是“约定好的通用 Agent”。当你的流程需要更精细控制时,就需要自定义图了。例如,一个典型的“先判断后执行”的流程可以这样设计:
def should_use_search(state): if state.get("need_search"): return "search" return "direct_answer" graph.add_conditional_edges( "router", should_use_search, {"search": "search_node", "direct_answer": "answer_node"}, )这种条件边让流程可视化程度更高,也更容易加人工审核节点。比如模型搜索结果置信度低时,节点可以转给人工处理,而不是继续自动回答。
7. MCP 接入:打通企业数据和权限边界
现在到了很多人最感兴趣的 MCP 部分。前面已经说过,MCP 是一套标准协议。下面实际演示怎么做一个最简单的 MCP Server,再把它接入 LangChain Agent。
7.1 从例子看 MCP 解决的问题
假设你们公司有一个人力资源系统,提供“查询请假余额”接口。没有 MCP 时,你在每个 Agent 项目里都要写一遍:
def query_leave(user_id): resp = requests.post("http://hr.internal/leave", json={"user_id": user_id}) return resp.json()如果再接财务系统、会议系统、项目管理系统,每个都要写一套。有 MCP 后,这些系统各自实现一个标准化的 MCP Server,任何支持 MCP 的 Agent 都能发现并调用它们的工具。
7.2 编写一个最小 MCP Server
# 文件路径:mcp_server.py from mcp.server.fastmcp import FastMCP # 创建 MCP Server,名称会在连接时展示给客户端 mcp = FastMCP("hr-service") @mcp.tool() def get_leave_balance(user_id: str) -> str: """查询员工剩余年假,参数 user_id 为员工工号。""" # 生产环境必须通过内部权限系统鉴权 balances = {"1001": "6", "1002": "3"} return balances.get(user_id, "查无此员工") if __name__ == "__main__": mcp.run()这段代码的核心是@mcp.tool()装饰器。函数被注册为 MCP Server 的一个工具,客户端可以通过 MCP 协议发现它、读取它的描述、调用它。
7.3 在 LangChain Agent 中接入 MCP 工具
LangChain 社区提供了langchain-mcp-adapters,可以把 MCP Server 的工具加载成 LangChain Agent 可用的工具列表。下面是基于该适配器的典型写法:
# 文件路径:mcp_client_demo.py import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client from langchain_mcp_adapters.tools import load_mcp_tools from langchain_openai import ChatOpenAI from langgraph.prebuilt import create_react_agent server_params = StdioServerParameters( command="python", args=["mcp_server.py"], ) async def main(): async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools = await load_mcp_tools(session) llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) agent = create_react_agent(llm, tools) result = await agent.ainvoke( {"messages": [{"role": "user", "content": "工号1002的员工还剩几天年假?"}]} ) print(result["messages"][-1].content) if __name__ == "__main__": asyncio.run(main())注意,MCP 客户端会有多种传输方式,这里用的是stdio,也就是 Agent 启动 MCP Server 子进程,通过标准输入输出通信。另一种常见方式是streamable-http,适合 MCP Server 部署在远程服务器上的场景。
运行方式:
python mcp_client_demo.py如果正常运行,Agent 会通过 MCP 协议调用mcp_server.py中暴露的get_leave_balance工具,得到结果后生成回答。
7.4 MCP 接入的安全边界
MCP 非常像“给 AI 开了一堆系统接口”。接口越多,潜在风险越大。尤其是企业场景中,MCP Server 后面连着数据库和内部系统,不能默认 Agent 有调用一切权限。至少要做到:MCP Server 必须做身份认证;工具级做最小权限授权;敏感操作单独加审批节点;所有调用留日志,方便审计。不要觉得“只是查个余额”就不重视,工具一旦可以被任意提示词触发,就有越权风险。
8. 组合案例:知识库 + 工具调用 + 状态控制的完整 Agent
到这里,我们已经分别演示了 RAG、LangGraph、工具调用和 MCP。把它们放在一起,就是一个接近生产形态的 Agent。
下面的示例演示这样一个场景:用户询问“我请假还剩几天,同时告诉我请假制度里对提前几天申请的要求”。这个任务需要 Agent 先后完成:查询 HR 系统、检索知识库文档、将两段信息拼接成最终回答。
# 文件路径:full_agent_demo.py from dotenv import load_dotenv load_dotenv() from langchain_openai import ChatOpenAI, OpenAIEmbeddings from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.vectorstores import FAISS from langchain.tools.retriever import create_retriever_tool from langchain_core.tools import tool from langgraph.prebuilt import create_react_agent # 1. 构造知识库检索工具 loader = TextLoader("./docs/company_manual.txt") documents = loader.load() splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50) chunks = splitter.split_documents(documents) embeddings = OpenAIEmbeddings(model="text-embedding-3-small") vectorstore = FAISS.from_documents(chunks, embedding=embeddings) retriever = vectorstore.as_retriever(search_kwargs={"k": 4}) retriever_tool = create_retriever_tool( retriever, name="company_manual_search", description="搜索公司内部制度文档。当问题涉及请假制度、报销规范、考勤规则时,请先调用此工具。", ) # 2. 构造内部系统查询工具 @tool def get_user_leave_balance(user_id: str) -> str: """查询员工剩余年假天数,参数为员工工号。""" leave_data = { "1001": "剩余 6 天", "1002": "剩余 3 天", } return leave_data.get(user_id, "查无此员工") tools = [retriever_tool, get_user_leave_balance] # 3. 构造 Agent llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) agent = create_react_agent(llm, tools) if __name__ == "__main__": result = agent.invoke( { "messages": [ { "role": "user", "content": "工号1001的员工请假还剩几天?" "另外,公司制度里对提前几天申请病假有规定吗?", } ] } ) print(result["messages"][-1].content)把这个示例跑起来,你会看到输出里包含两轮信息:既回答了剩余天数,又引用了手册中的病假申请要求。这就是 Agent 自行拆解任务的结果。
如果你希望整个流程更可控,比如先查 HR 系统再查文档,或者要求“必须得到 HR 系统结果才能生成回答”,那就可以用第 5 节的自定义StateGraph替代create_react_agent。这也是 LangGraph 价值最明显的场景——把业务规则固化在流程里,而不是靠提示词约束模型。
9. 常见问题与排查思路
下面整理这套技术栈里最容易踩的坑。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 运行时报依赖版本冲突 | langchain 各子包版本不匹配 | pip list查看版本,检查官方文档版本对应表 | 统一升级到同一版本族,避免混用旧包 |
| 模型 API 调用 401 | OPENAI_API_KEY未加载或无效 | 检查.env是否放在项目根目录,打印os.getenv结果 | 确认 key 有效,用load_dotenv()加载 |
| 检索结果与问题明显无关 | 文档切分不合理,或 Embedding 模型不适配 | 打印召回片段,观察内容是否完整 | 调整 chunk_size,更换 Embedding 模型 |
| Agent 不调用工具,直接编答案 | 工具描述不清楚,或模型版本不支持 tool calling | 打印模型回包,查看 tool_calls 字段 | 重写工具描述,换支持工具调用的模型 |
| MCP 连接报错“Session not initialized” | 客户端与服务端握手未完成 | 检查调用顺序,确认await session.initialize()已执行 | 按官方示例补齐初始化步骤 |
| LangGraph 节点返回字段不在 State 中 | State 定义与实际返回不一致 | 查看对应报错堆栈中的 key 名称 | 在TypedDict中补全字段定义 |
Agent 执行中途报AgentExecutionError | 某个工具抛异常,或模型输出格式不符合预期 | 查看工具函数日志,是否捕获了所有异常 | 给工具函数加 try/except,返回结构化错误信息 |
| 本地向量检索速度慢 | 文本片段过多或向量维度高 | 统计 chunk 数量和向量库规模 | 使用批量检索、缩减文档量,或迁移专业向量数据库 |
再补充一个通用排查建议:任何一步失败了,第一件事是看原始输入和输出。不要只看最终结果。LangChain 的chain.invoke()可以替换成chain.batch()或者逐步调用节点,LangGraph 可以打开调试日志观察每个节点的前后状态。定位问题永远是从“哪一步和预期的数据变化不一致”开始的。
10. 工程化建议与最佳实践
学完入门示例后,如果你要把它用到真实项目里,下面这些经验比较重要。
10.1 先小后大,不要把五个概念一次性引入
很多团队一开始就想做一个“RAG + Agent + MCP + LangGraph”的全功能平台,结果项目前四周全在联调依赖。更务实的路径是:先只做 RAG,跑通知识库问答,再单独引入 Agent,最后接入 MCP。每一步都能独立上线,风险小,反馈快。
10.2 密钥管理和权限控制
代码示例中用了python-dotenv读取环境变量,这在本地没问题,但生产环境建议使用密钥管理服务,不要把密钥写进镜像或代码仓库。同时,RAG 检索的文档要提前做权限分级。如果所有人共用同一个向量库,低权限用户在检索时极可能把高权限文档内容作为上下文交给大模型,这是一种隐蔽的数据泄露。
10.3 检索质量要在生成之前评估
很多项目“回答质量差”的表象,其实是检索环节召回不准确。建议在项目初期就把检索评估做起来:构造一批“问题-预期文档/预期答案”测试集,用命中率评估检索器。没有评测体系的 RAG 项目,后期优化会非常被动。常见做法是选择多种 Embedding 模型做对比测试,再针对召回结果调整切分策略。
10.4 LangGraph 的状态设计要有边界意识
不要把所有临时变量都塞进同一个 State。State 是流程共享内存,字段越多越难维护。建议把一次性使用的变量放在节点内部,只把跨节点传递的必要字段放到 State 里。长对话场景中,还要考虑消息列表的截断策略,避免状态无限膨胀。
10.5 MCP 是接入层标准,不是业务层架构
MCP 解决的是“客户端如何发现和调用工具”的问题,它不替你解决“这个工具该不该被调用”“并发怎么控制”“失败怎么重试”这类问题。所以不要把 MCP Server 写成包含复杂业务逻辑的巨型服务。MCP Server 越薄越好,后面的业务逻辑仍然应该走你已有的后端服务权限体系。
10.6 日志与监控
Agent 应用比普通接口复杂得多,用户一次提问可能触发多次工具调用、多次模型生成。建议给每次会话生成 trace id,记录:每个节点的输入输出、每次 LLM 调用的 token 数、每个工具的调用参数和耗时、最终答案来源是否包含检索片段。没有 trace,线上问题基本没法排查。
10.7 流式输出与交互体验
真实业务中,用户不喜欢长时间等待。LangChain 和 LangGraph 都支持流式输出。实现流式的思路是在图编译后调用.astream(),把每个节点的输出按事件流发送给前端。如果你做的产品对交互要求高,建议尽早把流式接入方案引入,不然后期加入会涉及整个链路改造。
11. 写在最后
把这篇教程的核心脉络再串一遍。先跑通 RAG,解决“知识从哪来”的问题;再引入 LangGraph,解决“流程怎么控制”的问题;再让 Agent 学会调用工具,解决“模型怎么行动”的问题;最后用 MCP 标准化工具接入,解决“系统之间怎么连通”的问题。五件事层层递进,每一件都落在具体的代码和业务场景上。
学完这篇内容,你可以继续深入的方向有三个:一是 LangGraph 的高级能力,比如状态持久化、多 Agent 协作、人工审批中断与恢复;二是检索质量优化,包括多路召回、重排序、混合检索;三是 MCP 的生产级应用,比如认证鉴权、Server 注册与发现机制。
建议你把这篇文章里最后一个组合案例,替换成自己手头真实的数据和工具,从一个小场景开始,让流程真正跑起来。跑通第一条链路之后,再逐步加复杂度。2026 年,LangChain 和 LangGraph 已经不算新技术,但把它们用对、用好的人,依然稀缺。