LangChain、LangGraph、RAG与MCP:一条链路讲透Agent应用开发

发布时间:2026/9/8 9:27:29
LangChain、LangGraph、RAG与MCP:一条链路讲透Agent应用开发 先给你一个结论LangChain、LangGraph、Agent、RAG、MCP 这几个词放到一起不是五个并列的技术而是一条完整的 Agent 应用落地链路。很多人啃书啃不明白是因为书本按“框架功能”讲而实际项目按“问题链路”组织。这篇教程换个思路从一条真实可跑的代码链路切入把每个组件解决什么问题、在哪里接入、踩坑点在哪讲清楚争取让你读完能直接动手搭一个带 RAG 检索和工具调用的多智能体应用。1. 为什么 Agent 开发这么难先弄清楚五个概念的真实分工先说一个很多新手会踩的误区以为 LangChain 是一个 AI 框架LangGraph 是它的升级版Agent 是一种模型RAG 是一种算法MCP 是一种接口协议。这五个东西确实都围绕大模型应用但它们的层级完全不一样。从工程视角看它们的分工可以这样理解技术解决的核心问题类比LangChain把 LLM 调用、提示词、外部数据、工具调用封装成标准组件一个工具库相当于“零件超市”LangGraph用图结构编排多个步骤让流程有状态、可循环、可分支工作流引擎相当于“流水线控制台”Agent让 LLM 根据目标自主决定调用哪些工具、执行哪些步骤一种程序范式相当于“自动驾驶策略”RAG把外部知识库接入 LLM缓解幻觉、补充私有知识一种检索方案相当于“外接记忆”MCP统一 LLM 应用与外部工具/数据源之间的通信协议一种接口标准相当于“USB-C 接口”这个表建议保存下来。后续学 LangChain 不会懵学 LangGraph 不会混学 MCP 不会觉得它是另一个 LangChain因为它们本来就不在同一层。这篇文章真正要解决的问题是当你准备开发一个真实的 AI Agent 应用时应该按什么顺序理解这些技术如何用一条最小可运行的链路把它们串起来。2. LangChain 与 LangGraph 的核心概念与适用场景2.1 LangChain 不是框架是组件库LangChain 最早给人留下的印象是“LLM 应用开发框架”但经历了多个版本迭代后更准确的理解是LangChain 提供了一组标准化组件让开发者不必每次从零实现提示词管理、模型调用、输出解析、文档加载、向量存储等琐碎工作。一个典型的 LangChain 调用长这样# 文件路径langchain_basic_demo.py from langchain_openai import ChatOpenAI from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import ChatPromptTemplate llm ChatOpenAI(modelgpt-4o-mini, temperature0) prompt ChatPromptTemplate.from_messages([ (system, 你是一名擅长用通俗语言解释技术的工程师。), (human, 请用三句话解释{topic}), ]) chain prompt | llm | StrOutputParser() result chain.invoke({topic: 什么是RAG}) print(result)这段代码里出现了 LangChain 最核心的三个概念ChatPromptTemplate消息模板把提示词和变量分离避免字符串拼接。ChatOpenAI模型封装统一不同厂商模型的调用差异。StrOutputParser输出解析器把模型返回的 AIMessage 转成纯字符串。prompt | llm | StrOutputParser()这种管道式写法是 LangChain 的 LCELLangChain Expression Language语法它把组件像 Unix 管道一样串起来。LCEL 支持并行、流式、异步、重试等能力是用 LangChain 组织逻辑时最值得花时间掌握的部分。2.2 LangGraph 解决的是“有状态、可循环、可分支”的流程问题如果你只是做一个“问答机器人”LangChain 的 Chain 已经够用。但 Agent 应用不同LLM 需要自主决定调用哪个工具、观察结果、再决定下一步这是一个循环过程不是线性管道。用 Chain 硬写循环代码会变得非常别扭。LangGraph 的核心贡献是把 Agent 流程建模为一张有向图包含三类元素State全局状态对象在节点之间传递是流程的“内存”。Node具体的处理步骤例如“调用模型”“执行工具”“检索知识库”。Edge节点之间的连接分为普通边和条件边条件边根据状态决定下一步走向哪个节点。用 LangGraph 实现一个带工具调用的 Agent 循环最小代码如下# 文件路径langgraph_agent_demo.py from typing import Annotated, TypedDict from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langgraph.graph import StateGraph, START, END from langgraph.graph.message import add_messages from langgraph.prebuilt import ToolNode, tools_condition tool def get_weather(city: str) - str: 查询指定城市的当前天气。 # 这里替换为真实天气 API return f{city} 今天晴气温 24 摄氏度。 tools [get_weather] class AgentState(TypedDict): messages: Annotated[list, add_messages] llm ChatOpenAI(modelgpt-4o-mini, temperature0).bind_tools(tools) def chatbot(state: AgentState): return {messages: [llm.invoke(state[messages])]} graph StateGraph(AgentState) graph.add_node(chatbot, chatbot) graph.add_node(tools, ToolNode(tools)) graph.add_edge(START, chatbot) graph.add_conditional_edges(chatbot, tools_condition) graph.add_edge(tools, chatbot) app graph.compile() result app.invoke({messages: [{role: user, content: 北京今天天气怎么样}]}) for message in result[messages]: print(f{message.type}: {message.content})这段代码的重点不是语法而是理解 LangGraph 的循环控制add_messages是一个归约器reducer它告诉 LangGraph 当多个节点都往messages里追加内容时应该合并而不是覆盖。tools_condition是 LangGraph 预置的条件函数如果模型返回了工具调用请求就走tools节点执行工具否则直接结束。graph.add_edge(tools, chatbot)让工具执行完后再回到模型节点形成“模型 - 工具 - 模型”的循环直到模型认为无需再调用工具。这里真正容易踩坑的地方是状态设计。很多新手把TypedDict理解成普通 Python 字典忽略了Annotated[list, add_messages]这个归约器的作用。如果没有add_messages每次节点返回的messages都会覆盖前一轮结果导致对话历史丢失。3. RAG 检索增强生成从文档到知识库的完整链路3.1 为什么需要 RAG大模型的知识来自训练数据存在两个天然限制一是训练数据有截止时间无法覆盖最新信息二是私有知识、企业内部文档根本不会出现在训练数据里。微调可以解决一部分问题但成本高、更新慢。RAG 的思路更轻量在模型回答之前先从外部知识库检索出相关内容把这些内容拼进提示词让模型基于检索结果回答。从材料看RAG 相关搜索热度一直很高说明大家关注的重点已经不只是“RAG 是什么”而是“RAG 怎么落地”“RAG 怎么测评”“Agentic RAG 怎么做”。这一节先把基础链路跑通后面再讲 Agentic RAG。3.2 最小可运行的 RAG 示例一个标准的 RAG 链路包含五个环节文档加载 - 文本切分 - 向量化 - 向量存储 - 检索生成。# 文件路径rag_demo.py from langchain_community.document_loaders import TextLoader from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_openai import OpenAIEmbeddings from langchain_chroma import Chroma from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate # 1. 加载文档 loader TextLoader(./knowledge.txt, encodingutf-8) documents loader.load() # 2. 文本切分 text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, ) chunks text_splitter.split_documents(documents) # 3. 向量化并存储 embeddings OpenAIEmbeddings(modeltext-embedding-3-small) vectorstore Chroma.from_documents(documentschunks, embeddingembeddings) # 4. 构建检索器 retriever vectorstore.as_retriever(search_kwargs{k: 4}) # 5. 检索增强生成 llm ChatOpenAI(modelgpt-4o-mini, temperature0) prompt ChatPromptTemplate.from_messages([ (system, 你是一个知识库问答助手。请严格根据以下资料回答问题不要编造。\n\n{context}), (human, {question}), ]) def rag_answer(question: str) - str: docs retriever.invoke(question) context \n\n.join([doc.page_content for doc in docs]) chain prompt | llm response chain.invoke({context: context, question: question}) return response.content print(rag_answer(知识库中提到的部署注意事项有哪些))这段代码里有几个参数是 RAG 效果的关键chunk_size500和chunk_overlap50切分粒度直接影响检索质量。切太小语义不完整切太大混入无关信息。chunk_overlap用来保留相邻切片之间的上下文衔接。k4检索返回的文档数量。返回太少可能漏信息太多可能把噪声带进上下文。temperature0问答场景建议设为 0减少模型自由发挥的空间。3.3 为什么向量检索不是唯一选择搜索热词里出现了dense vector search和ontology rag这里值得多讲一句。RAG 早期的检索通常只用稠密向量检索dense vector search把文本映射成高维向量用余弦相似度找最相似的片段。它的优点是理解语义缺点是对专有名词、精确数字、ID 类查询不敏感。比如用户问“订单号 20240815001 的状态”向量检索很可能找不到精确匹配。实际项目中更稳妥的做法是混合检索向量检索负责语义召回关键词检索BM25负责精确匹配再用重排序rerank把两路结果融合排序。搜索词里的ontology rag则是另一种增强思路在 RAG 之上引入知识图谱本体用实体关系辅助检索适合关系复杂的企业知识场景。从工程落地角度建议优先级是先做好基础 RAG - 再上混合检索与重排序 - 最后按业务复杂度评估是否需要图谱增强。4. Agentic RAG 与多智能体协同实战4.1 从普通 RAG 到 Agentic RAG普通 RAG 的流程是固定的用户提问 - 检索 - 生成。问题是所有问题都走同一条检索路径效果并不好。比如用户问“对比一下 A 方案和 B 方案的优缺点”固定 RAG 会直接把两段文档拼进上下文模型很难组织出真正的对比结构。Agentic RAG 的思路是让 Agent 自己决定怎么检索、检索几次、是否需要调用其他工具。它可以先判断问题类型再决定走哪个检索器甚至可以在第一轮检索结果不足时二次检索或者调用外部 API 补充数据。搜索热词里agentic rag的热度上升本质上就是开发者发现固定 RAG 链路在面对复杂问题时的天花板。4.2 一个多智能体协同的最小实现多智能体协同听起来很高端但核心可以拆成路由 子任务 汇总三步。下面实现一个简单的双层结构router_agent分析用户问题决定交给哪个子 Agent。research_agent负责多轮检索返回带引用的资料。writer_agent基于资料生成最终答案。# 文件路径agentic_rag_demo.py from typing import Annotated, TypedDict, Literal from langchain_openai import ChatOpenAI, OpenAIEmbeddings from langchain_chroma import Chroma from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_community.document_loaders import TextLoader from langgraph.graph import StateGraph, START, END from langgraph.graph.message import add_messages # ---------- 准备知识库 ---------- loader TextLoader(./knowledge.txt, encodingutf-8) documents loader.load() text_splitter RecursiveCharacterTextSplitter(chunk_size500, chunk_overlap50) chunks text_splitter.split_documents(documents) embeddings OpenAIEmbeddings(modeltext-embedding-3-small) vectorstore Chroma.from_documents(documentschunks, embeddingembeddings) retriever vectorstore.as_retriever(search_kwargs{k: 4}) class AgentState(TypedDict): messages: Annotated[list, add_messages] question: str context: list route: str llm ChatOpenAI(modelgpt-4o-mini, temperature0) # ---------- 节点 1路由 ---------- def route_question(state: AgentState): prompt f请判断以下问题更适合哪种处理方式 - database问题涉及数据库配置、表结构、SQL - general问题属于通用知识库问答 - coding问题涉及代码示例或编程 问题{state[question]} 只回答一个词database / general / coding response llm.invoke(prompt) route response.content.strip().lower() return {route: route} # ---------- 节点 2检索 ---------- def retrieve(state: AgentState): docs retriever.invoke(state[question]) return {context: docs} # ---------- 节点 3生成 ---------- def generate(state: AgentState): context \n\n.join([doc.page_content for doc in state[context]]) prompt f你是知识库问答助手的最终回答模块。 请基于以下资料回答用户问题如果资料不足明确说“资料中未找到相关信息”。 资料 {context} 用户问题{state[question]} 请给出完整、结构化的回答 response llm.invoke(prompt) return {messages: [response]} # ---------- 条件路由 ---------- def after_route(state: AgentState) - Literal[database, general, coding]: return state[route] if state[route] in (database, general, coding) else general graph StateGraph(AgentState) graph.add_node(route, route_question) graph.add_node(retrieve, retrieve) graph.add_node(generate, generate) graph.add_edge(START, route) graph.add_conditional_edges(route, after_route, { database: retrieve, general: retrieve, coding: retrieve, }) graph.add_edge(retrieve, generate) graph.add_edge(generate, END) app graph.compile() result app.invoke({ question: 知识库中推荐使用什么数据库, messages: [], }) print(result[messages][-1].content)这个示例里route_question看起来只做了“分类”所有分类最终都走retrieve好像路由没有实际意义。但在真实项目中你可以让database路由去连接 SQL 查询工具让coding路由去调用代码解释器让general路由走普通向量检索。路由节点存在的意义是让不同类型的请求进入不同的处理管道而不是所有请求都走同一条链路。从 LangGraph 的角度看这个示例体现了一个常用模式条件边conditional edges 多个子图或节点的组合。后续做更复杂的多智能体协作时可以在这个模式上继续扩展把某个节点替换成独立的子图子图内部再维护自己的状态循环就形成了“父图 - 子图”的多层 Agent 架构。5. MCP 协议打通 Agent 与外部工具的最后一百米5.1 MCP 到底是什么MCPModel Context Protocol是一个开放协议目的是统一大模型应用与外部工具、数据源之间的交互方式。它的设计思路可以理解为让工具提供方把能力封装成标准接口让 LLM 应用通过统一客户端去发现和调用这些能力而不是每家工具都要为每个 Agent 框架定制适配器。从架构上看MCP 包含三个角色MCP Host运行 LLM 应用的进程比如 Claude Desktop、IDE 插件、你自研的 Agent。MCP ClientHost 内部的连接组件负责与 Server 建立通信。MCP Server暴露工具、资源、提示词能力的服务端可以是本地进程也可以是远程服务。搜索热词里出现了蓝湖 MCP、MasterGo MCP、Cocos Creator MCP、Unity MCP、Matlab MCP 等这说明一个明显趋势设计工具、游戏引擎、科学计算软件都在往 MCP Server 方向靠拢。对开发者来说这意味着未来不需要为每个工具分别写接入代码只要工具厂商提供 MCP Server就能用统一方式调用。5.2 用 FastMCP 实现一个最小 MCP ServerFastMCP 是官方 Python SDK 提供的高层封装它大幅简化了 MCP Server 的编写。下面实现一个带数据库查询工具和静态资源的 MCP Server# 文件路径mcp_demo_server.py from mcp.server.fastmcp import FastMCP # 创建 MCP Server 实例名称会暴露给客户端 mcp FastMCP(demo-server) mcp.tool() def query_order_status(order_id: str) - str: 查询订单状态。 Args: order_id: 订单号例如 20240815001 # 实际项目里在这里查询数据库 if order_id 20240815001: return 订单已发货物流单号 SF1234567890 return 未查询到该订单 mcp.resource(config://app) def get_config() - str: 暴露一个名为 config://app 的静态配置资源。 return version1.0.0\nenvtest if __name__ __main__: mcp.run()这个 Server 不需要 Web 框架不需要写 HTTP 路由mcp.tool()会把函数自动暴露成可供 LLM 调用工具mcp.resource()则暴露一个可被读取的资源。在客户端接入之前可以用官方命令行工具验证 Server 是否正常mcp run demo_server.py如果 MCP 开发环境配置正确这条命令会启动本地 MCP Server 并等待客户端连接。更完整的调试方式是在mcp.run()时传入transportstdio或transporthttp并在客户端配置中指定对应传输方式。5.3 在 LangGraph Agent 中接入 MCP 工具目前 LangGraph 生态里对接 MCP 的方式还在持续演进不同版本可能提供不同的辅助类。更稳妥的做法是先把 MCP Server 里的工具通过 MCP Client 拉取回来再转换成一个普通的 LangChain Tool 接入 Agent。伪代码思路如下# 文件路径mcp_to_langchain_adapter.py from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client from langchain_core.tools import tool server_params StdioServerParameters( commandpython, args[mcp_demo_server.py], ) async def load_mcp_tools_as_langchain_tools(): async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools_result await session.list_tools() langchain_tools [] for t in tools_result.tools: tool def make_func(tool_namet.name, tool_schemat.inputSchema): async def _call(**kwargs): result await session.call_tool(tool_name, kwargs) return result.content[0].text return _call langchain_tools.append(make_func()) return langchain_tools这段代码不追求直接运行而是想说明一个核心思路MCP 是一个协议LangChain Tool 是一个本地抽象两者之间只需要一个适配层。在真实项目中建议先确认你当前使用的 LangGraph 版本是否已经内置 MCP 辅助模块如果有可以优先使用官方封装如果没有就按上面的思路自行封装。6. 运行环境准备与依赖安装6.1 环境要求本文所有示例基于以下环境版本请以实际安装为准操作系统Windows 10/11、macOS、Linux 均可Python3.10 及以上包管理pip 或 poetryLLM API示例使用 OpenAI 兼容接口你可以替换为任何兼容接口的模型服务6.2 创建虚拟环境并安装依赖# 创建虚拟环境建议每个项目独立环境 python -m venv .venv # Windows 激活 .venv\Scripts\activate # macOS / Linux 激活 source .venv/bin/activate # 升级 pip python -m pip install --upgrade pip # 安装核心依赖 pip install langchain langchain-openai langchain-community langchain-text-splitters langchain-chroma langgraph # 安装 MCP 相关依赖 pip install mcp fastmcp # 向量数据库 pip install chromadb # 如果需要处理 PDF/网页可以按需安装 pip install pypdf beautifulsoup46.3 配置模型 API 密钥在项目根目录创建.env文件并安装 dotenv 支持pip install python-dotenv# 文件路径.env OPENAI_API_KEY你的密钥 OPENAI_BASE_URLhttps://api.openai.com/v1在代码中加载环境变量from dotenv import load_dotenv load_dotenv()如果你使用的是国内大模型厂商的 OpenAI 兼容接口只需要修改OPENAI_BASE_URL和模型名称即可LangChain 的ChatOpenAI可以直接对接兼容接口。6.4 验证环境是否安装成功python -c from langchain_openai import ChatOpenAI; from langgraph.graph import StateGraph; from mcp.server.fastmcp import FastMCP; print(环境OK)如果输出环境OK说明核心依赖已就绪。如果报ModuleNotFoundError优先检查是否激活了正确的虚拟环境以及依赖是否安装到同一个环境。7. 运行结果与效果验证7.1 LangGraph Agent 示例预期输出运行langgraph_agent_demo.py后你会在控制台看到两类消息human: 北京今天天气怎么样 ai: [{type: tool_call, name: get_weather, args: {city: 北京}}] tool: 北京 今天晴气温 24 摄氏度。 ai: 北京今天晴气温 24 摄氏度。判断成功的标准有两条中间出现了tool_call说明 LLM 正确识别出需要调用工具。最终回答引用了工具返回结果而不是模型自己编造的天气信息。如果环境中没有配置工具调用模型或者模型版本不支持bind_tools你可能会看到模型直接返回“很抱歉我无法获取实时天气”这说明工具调用链路没有生效。第一步先检查模型是否支持 function calling第二步检查bind_tools(tools)是否在模型初始化时正确绑定。7.2 RAG 示例预期输出运行rag_demo.py前先在项目根目录准备一个knowledge.txt文件内容可以是任意知识库文档比如部署注意事项 1. 生产环境建议使用独立数据库实例。 2. 首次部署前需要执行数据库迁移脚本。 3. 建议配置日志采集和告警规则。运行后如果问题改成“部署时有哪些注意事项”模型应该基于文档内容回答而不是泛泛而谈。7.3 MCP Server 验证方法MCP Server 不像普通 Web 服务那样可以简单用浏览器访问。验证分两步启动服务运行mcp run mcp_demo_server.py观察是否报错。用 MCP 官方客户端或支持 MCP 的工具如 Claude Desktop 或支持 MCP 的 IDE连接同一 Server查看工具列表里是否出现query_order_status。如果启动时报“Failed to load MCP server”优先检查 Python 环境中是否安装了mcp包以及当前命令执行环境是否与安装依赖的虚拟环境一致。8. 常见问题与排查思路问题现象可能原因排查方式解决方案运行时报ModuleNotFoundError: No module named langchain_openai依赖未安装或环境不对执行pip list查看已安装包确认虚拟环境已激活重新安装依赖API 调用报 401 或 AuthenticationErrorAPI Key 未加载或配置错误打印os.getenv(OPENAI_API_KEY)检查值确认.env文件被load_dotenv()正确加载Agent 执行报agent execution terminated due to error.工具执行异常或节点返回格式不对查看完整异常堆栈定位是模型调用还是工具调用出错单独测试工具函数确认返回类型符合 LangGraph 状态要求RAG 回答内容与文档无关切分粒度不合理或检索召回不相关打印检索到的docs内容调整chunk_size、k或改用混合检索LangGraph 节点状态被覆盖状态字段没有使用add_messages归约器检查 TypedDict 中Annotated定义对消息列表字段添加Annotated[list, add_messages]MCP Server 启动后客户端连不上传输方式不匹配查看mcp.run()的 transport 参数客户端与 Server 使用相同传输方式stdio 或 http模型不支持工具调用所选模型没有 function calling 能力查询模型文档更换支持工具调用的模型或使用兼容接口中文向量检索效果差使用的是通用英文 embedding 模型对比中英文测试集效果选用支持中文的 embedding 模型或本地中文模型这里重点说一条agent execution terminated due to error.是 LangGraph 执行器对节点异常的通用包装。看到这个错误不要只盯着“LangGraph 崩溃了”先用 traceback 定位到具体的节点函数。大多数情况下问题出在工具函数内部抛了异常或者节点返回值没有匹配 State 中定义的字段类型。排查时可以先在节点函数里加打印再用小输入逐步验证。9. 最佳实践与工程建议9.1 先跑通最简链路再叠加复杂度初学者最容易犯的错误是一开始就想搭一个“全功能平台”同时接向量库、多智能体、MCP Server。更建议的路径是先用prompt | llm | parser跑通一个最简单的问答。再接入 RAG用 5 个文档验证检索和生成链路。然后引入 LangGraph把一个 RAG 流程改造成状态图。最后接入 MCP把外部工具变成 Agent 可调用的能力。每一步都留下可验证的产出再进入下一步。9.2 提示词里的系统提示词要区分角色在多智能体场景里每个节点的系统提示词决定了这个 Agent 的“职责边界”。建议把提示词独立成配置文件而不是硬编码在 Python 代码里。这样调整提示词不需要重新部署代码。# 文件路径prompts.yaml route_prompt: | 你是一个问题路由器。请判断用户问题属于哪一类database / general / coding。 只返回一个词不要输出其他内容。 retrieve_prompt: | 你是一个检索规划器。请根据用户问题判断需要检索哪些主题。# 文件路径load_prompts.py import yaml with open(prompts.yaml, r, encodingutf-8) as f: PROMPTS yaml.safe_load(f)9.3 状态设计是 LangGraph 的核心设计决策建议遵循以下原则消息历史用add_messages归约器不要手动拼接。自定义业务字段如question、context、route尽量保持简单不要在状态里放无法序列化的对象。如果状态里需要传递 DataFrame、文档对象等复杂数据建议在节点内部处理完只把序列化后的文本或 ID 写入状态。生产环境使用 LangGraph 的持久化功能如MemorySaver或外部存储让对话状态在服务重启后仍可恢复。9.4 RAG 效果的评估不能靠感觉搜索热词里rag测评怎么做热度不低说明很多人意识到 RAG 不能“跑起来就算成功”。建议从四个维度建立评估集维度评估内容说明召回质量检索返回的前 k 条是否包含正确答案可以计算命中率生成忠实度回答是否严格基于检索内容重点检查是否编造抗干扰能力知识库存在无关文档时回答是否被带偏构建含噪声的测试集边界处理问知识库之外的问题时是否明确拒绝检查“不知道”的表达不需要一开始就上复杂的 RAG 评测框架先用几十条真实问题建立回归集每次修改切分参数、检索策略、提示词后都跑一遍比较前后效果。9.5 生产环境接入外部工具的安全边界用 MCP 接入外部工具时必须考虑权限和风险MCP Server 暴露的每个工具都应该有明确的输入校验不能直接把 LLM 生成的参数拼进 SQL 或 Shell 命令。数据库操作类工具坚持最小权限原则只给只读账号或只能操作指定表涉及写操作必须加人工确认。工具执行要有超时和失败降级策略。当前是大模型决定调用哪个工具一旦工具调用失控必须有终止机制。涉及生产环境的变更先在测试环境验证工具行为再逐步放开。10. 总结与后续学习路径这篇教程没有按“LangChain 官方文档”的顺序讲而是按一条实际的 Agent 应用链路展开先用 LangChain 的组件快速构建 LLM 调用再用 LangGraph 把流程改造成有状态、可循环的图结构然后引入 RAG 解决知识实时性和私有化问题最后用 MCP 统一接入外部工具。几个核心判断再重复一遍LangChain 是组件库LangGraph 是流程引擎两者不是替代关系而是配合关系。固定 RAG 适合简单问答Agentic RAG 适合复杂推理和多步检索不要一上来就上复杂架构。MCP 不是另一个 Agent 框架它解决的是“工具接入标准化”问题。未来会有更多工具厂商直接提供 MCP Server开发者需要掌握的是对接和适配能力。多智能体协同不等于“多个模型各说各话”核心是路由 状态共享 结果汇总。先用一个最小双层结构跑通再扩展子图。下一步建议按这个顺序实践把文中的 LangGraph Agent 示例改造成调用你本地已有的 API 工具。用 10 篇你自己的文档搭一个 RAG 知识库记录不同切分参数下的检索效果差异。用 FastMCP 封装一个你常用的小工具比如天气查询、数据库查询再接入 LangGraph Agent。把固定 RAG 升级成带路由的 Agentic RAG体验不同问题自动分流的效果。学习新技术最大的障碍不是资料少而是不知道从哪条链路切入。建议你先跑通本文最小示例再根据实际项目需求去查官方文档。框架版本迭代很快最稳妥的学习方式永远是“先把链路跑通再深入研究细节”。