ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

LangChain 主线学习:从 ReAct、Agent 到 MCP 与 Skills

LangChain 主线学习:从 ReAct、Agent 到 MCP 与 Skills 很多同学刚开始学 LangChain 时都会遇到同一个困惑网上既有 LangChain 入门教程又有 ReAct、AI Agent、MCP、Skills 的专题文章每个单独看都能跑通但想拼成一个真正能上线的智能体项目时却发现彼此之间是脱节的。LangChain 的问题从来不在某个 API 会不会用而在于它发展速度太快各类概念像是散落一地的积木。如果你没有一条主线把这些积木串起来就会陷入一种“今天学 Chain、明天学 Graph、后天学 MCP最后依然不会写业务代码”的状态。这篇文章想帮你解决的就是这个问题。文章会以一条完整的学习链路为主线先搞清楚 LangChain 在整个大模型应用开发里的位置再从环境配置与模型初始化开始逐步进入新版 Middleware 中间件、ReAct 推理循环、AI Agent 编排、MCP 协议接入、Skills 与 Prompt 工程。读完你可以形成一个整体认知哪些层负责能力、哪些层负责流程、哪些层负责协议以及如何把它们组装成一个最小可用项目。1. 为什么 LangChain 越学越乱先把握住一条主线先抛一个判断LangChain 值得学的不是它封装的那几十个类名而是它把“大模型”这种不确定对象变成可控工程组件的方法。这是整个框架存在的最底层理由。大模型应用开发和传统后端开发有一个本质区别传统程序里的每个函数、每个接口都是确定行为而大模型调用返回什么完全不可控。LangChain 的解决思路是用代码把模型包进一条更宽的流水线里让模型只负责某些环节的判断和生成其他环节仍然由代码控制。这条流水线就是理解 LangChain 的主线大致分三层数据与上下文层负责读文档、切分数据、做向量检索解决模型“不知道你业务数据”的问题。模型调用层负责构造 Prompt、调用 LLM、解析输出解决“模型怎么回答”的问题。执行编排层负责决定调用哪些工具、步骤是否重试、Agent 何时终止解决“模型自己无法完成复杂任务”的问题。很多人卡住是因为一上来就看 Agent 那一层结果遇到工具调用失败、上下文混乱、死循环等问题时完全不知道排错方向。实际上这些问题往往根在前面两层。因此后面各章节的顺序是有意安排的先把环境、模型、中间件这些底座能力讲清楚再进入 ReAct、Agent、MCP、Skills。你只有知道某个环节出了问题应该去改哪一层才算是真正“会用 LangChain”。2. 从旧版 Chain 到 LCEL理解框架的核心抽象 RunnableLangChain 版本演进非常快不同代码风格差异明显。早期大量资料里会出现LLMChain、SimpleSequentialChain、ConversationChain这类类而现在官方主推的是 LCELLangChain Expression Language。LCEL 的核心价值在于它把组件统一抽象成Runnable。几乎任何东西——模型、Prompt、输出解析器、甚至一个普通 Python 函数——都可以被看成Runnable然后用管道符|组合起来。这种设计的好处有三个。第一组件可以替换。你今天接 OpenAI 的ChatOpenAI明天换成本地部署的ChatOllama只要它们都实现了 Runnable 接口上下游的编排代码基本不用动。第二调用方式统一。统一使用invoke、batch、stream等方法不用为不同组件记忆不同的调用方式。第三便于组合。你可以把一个小 Runnable 作为中间节点继续拼接到更大的链路里这让“中间件”模式成为可能。看一个最简单的组合示例from langchain_core.prompts import ChatPromptTemplate from langchain_openai import ChatOpenAI from langchain_core.output_parsers import StrOutputParser prompt ChatPromptTemplate.from_template(用一句话解释{topic}) model ChatOpenAI(modelgpt-4o-mini, temperature0) parser StrOutputParser() chain prompt | model | parser这段代码里的chain就是一个Runnable。调用时不需要去操作model或prompt对象只需要对chain执行 invokeresult chain.invoke({topic: 什么是 LCEL}) print(result)这里值得注意一点|不是把字符串拼接起来而是把前一个组件的输出作为后一个组件的输入。prompt接收一个字典生成ChatPromptValuemodel接收 Prompt返回模型消息parser把消息解析成纯字符串。如果只看表面很容易误以为 LangChain 只是在做“模板和模型的封装”。实际上LCEL 的核心意义在接口统一。中间件、Agent、Graph 这些更复杂的能力都是建立在 Runnable 体系之上的。LangChain 与 LangGraph 的区别也是在这个语境下产生的LangChain 更侧重模型、Prompt、工具等基础组件编排LangGraph 则负责把 Agent 的执行过程变成一张明确的状态图解决循环、分支、节点恢复这类控制流问题。你可以先用 LangChain 搭出能力单元再用 LangGraph 管理复杂流程。对比项LangChainLangGraph定位LLM 应用开发框架Agent 流程编排框架核心抽象Runnable、ChainStateGraph、State擅长场景Prompt、工具、模型调用多步决策、循环、分支控制关系提供组件能力依赖 LangChain 组件3. 环境准备与模型初始化从最小可运行开始开始写代码前建议先准备一个干净的 Python 环境。不要直接在系统全局环境里安装因为 LangChain 的依赖更新很快很容易和项目里其他包产生版本冲突。推荐使用 Python 3.10 至 3.12用 venv 或 conda 隔离环境。python -m venv langchain-demo source langchain-demo/bin/activate安装核心依赖。下面的包名以当前主流版本的官方发布为准不同小版本的函数签名会略有变化但整体路径一致pip install --upgrade langchain langchain-core langchain-openai python-dotenvlangchain是主包负责 Agent、工具、链的高级编排能力langchain-core包含 Runnable 等基础抽象即使其他包更新核心接口也相对稳定langchain-openai是模型厂商适配包负责把 OpenAI 兼容接口封装成 LangChain 的BaseChatModel。如果打算使用本地模型可以再加一个适配包pip install langchain-ollama对函数调用和 Agent 场景建议优先使用 OpenAI 兼容接口的模型或者本地中能较好支持工具调用的模型如 Qwen、GLM 的最新版本。纯文本补全类模型在 Agent 场景中会非常难用因为它不一定跟得上 ReAct 的输出格式。模型密钥不要硬编码在代码中。先在项目根目录创建.env文件OPENAI_API_KEYsk-your-key-here OPENAI_BASE_URLhttps://your-compatible-endpoint/v1然后写一个model_loader.py统一初始化模型import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI load_dotenv() def get_openai_model(): return ChatOpenAI( modelos.getenv(OPENAI_MODEL, gpt-4o-mini), temperature0, api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL), ) def get_ollama_model(): from langchain_ollama import ChatOllama return ChatOllama( modelos.getenv(OLLAMA_MODEL, qwen2.5:7b), temperature0, )如果使用本地 Ollama 模型需要先在本机启动 Ollama 并拉取模型。在命令行执行ollama serve ollama pull qwen2.5:7b初始化模型后可以先做一次最小验证确认网络、密钥和模型都能正常联通from model_loader import get_openai_model model get_openai_model() resp model.invoke(请回复OK) print(resp.content)能够输出OK说明模型调用链路已经通了。之后所有上层组件包括中间件、Agent、MCP都可以基于这个模型继续展开。最怕的是前面这一步没验证后面 Agent 一报错你根本分不清是模型问题还是框架问题。4. 新版 Middleware 中间件把横切逻辑统一收拢中间件这个名词在后端领域并不陌生。日志记录、权限校验、限流、超时控制这些都是横切逻辑。如果分散在每一个业务函数里代码会非常冗余如果写在一个统一入口里管理起来就清晰很多。LangChain 早期对这种横切逻辑的处理方式比较朴素要么在调用前后手动写代码要么依赖 CallbackHandler 监听事件。但 CallbackHandler 更像是一种事件订阅机制它不能直接修改链路输入输出也很难按统一顺序组合。新版 Middleware 的出现把控制方式从“事件监听”推进到了“请求拦截”。你可以在模型调用或 Runnable 链执行过程的前后插入处理函数类似 Web 框架里的洋葱模型。这里很重要的一点是中间件的 API 形态在不同版本中有过调整很多网上教程会贴出某个特定版本的调用方式。为了避免被版本细节锁死先理解它的工作方式再结合自己环境的实际 API 做调整。先看一个通用的中间件包装实现# middleware_demo.py import time from langchain_core.middleware import Middleware def log_middleware(app, before_handlerNone, after_handlerNone): def wrapper(payload, config): start time.time() if before_handler: before_handler(payload) result app.invoke(payload, config) if after_handler: after_handler(result, time.time() - start) return result return wrapper def before(message): print(f[middleware] 请求前: {message}) def after(result, duration): print(f[middleware] 请求后: {result}耗时 {duration:.2f}s)在这段代码中log_middleware接收一个原始的 Runnable 应用对象app并返回一个经过包装的新函数。这个新函数在调用内部app.invoke之前执行before_handler在之后执行after_handler。如果你的环境中的 LangChain 已经支持语言链中间件可以这样把中间件挂到一条链上middleware Middleware(log_middleware, before_handlerbefore, after_handlerafter) chain prompt | model | parser chain chain.with_middleware(middleware) resp chain.invoke({topic: LangChain 中间件})上面的代码以较新的框架形态演示如果本地版本里找不到with_middleware或Middleware不要慌可以先在命令行里确认本地 API 形态python -c from langchain_core.middleware import Middleware; print(Middleware)如果当前版本不支持可以采用纯 Python 包装方式达到类似效果。实际项目中用中间件处理频率最高的是下面几类问题请求日志与 Token 统计每次调用模型前后记录输入字符数、输出 Token 数、耗时。兜底重试模型偶发超时或返回不合法格式时自动重试一次。敏感信息脱敏往日志里写内容之前先过滤 API Key、手机号等敏感字段。服务降级当某个供应商模型持续报错时自动切换到备用模型。使用中间件时应遵循一条原则中间件只做旁路控制和增强不要把业务逻辑写进中间件。业务逻辑属于 Agent 的决策层中间件负责的是让每次执行都可观测、可控制。5. ReAct没有外部行动模型就只是“嘴上专家”单独让模型回答“今天北京天气适合穿什么”它会基于训练数据给一个泛泛而谈的答案。因为模型无法访问实时信息它只能依靠记忆而记忆是过时的、不完整的。ReAct 机制解决的就是这个问题。它的名字由 Reasoning 和 Acting 组成核心思路是让模型在一个循环里交替进行两种行为思考下一步应该做什么然后调用工具去执行。ReAct 循环中的每一步大致如下Thought模型分析当前任务决定是否需要调用外部工具。Action选择一个工具并给出工具输入。Action Input具体的工具参数。Observation执行工具后返回的真实结果。回到第 1 步继续思考直到可以给出 Final Answer。理解 ReAct 的关键是它不是模型主动去执行代码而是模型在输出一串符合约定的文本真正执行工具的是外层代码。模型写好 Action 和 Action Input 后外面代码解析这些文本调用真正的 Python 函数然后把结果作为 Observation 放回上下文。这会带来一个容易踩的坑模型生成的 Action 名称和实际工具名不一致或者 Action Input 的格式不对。解决这类问题通常有两个办法把工具描述写得更清晰或者在执行器上开启解析错误兜底。在 LangChain 中ReAct 通常用create_react_agent来构建。你不需要手动写循环LangChain 会负责把模型输出解析成动作并循环执行。一个最小 Agent 可以这样搭建from datetime import datetime from langchain.agents import create_react_agent, AgentExecutor from langchain.tools import tool from langchain_core.prompts import PromptTemplate tool def get_current_date() - str: 获取今天日期。当用户询问日期、星期、天数时使用。 return datetime.now().strftime(%Y-%m-%d %A) tools [get_current_date] react_prompt PromptTemplate.from_template( 你是能调用工具的智能助手。请严格按下面格式回答 Thought: 说说你现在的想法 Action: 工具名称 Action Input: 工具的输入参数 Observation: 工具返回的结果 如果还没完成继续上面的步骤 当你已经有足够信息时用下面的格式收尾 Thought: 我已知晓答案 Final Answer: 最终回答 可用工具如下 {tools} 工具名称列表{tool_names} 用户的问题是{input} {agent_scratchpad} ) model get_openai_model() react_agent create_react_agent(model, tools, react_prompt) executor AgentExecutor( agentreact_agent, toolstools, verboseTrue, handle_parsing_errorsTrue, max_iterations3, )调用方式result executor.invoke({input: 今天的日期是什么}) print(result[output])ReAct 看起来简单但它决定了 Agent 的基础智商。很多 Agent 表现不佳并不是模型能力不够而是 ReAct 循环里的 Prompt 没有把“思考节奏”讲清楚。一个可复用的经验是Prompt 里的示例最好包括“需要工具”和“不需要工具”两类。模型只有见过不需要工具也能回答的例子才不会被强制套进工具循环里。6. Agent 与 LangGraph把不确定的循环放进确定的状态机ReAct 已经让模型具备了单轮“思考-行动-观察”能力但它只是一个循环结构。复杂的业务场景通常存在多个工具阶段、需要中途停下来等用户输入、需要在某些条件下提前终止甚至需要人工审批环节。这些要求单靠一个 ReAct 循环很难优雅实现。Agent 从工程角度看是一个根据目标动态决定动作序列的程序。它并不神秘本质是一套循环控制逻辑加上工具集合加上模型决策。困难在于模型每一步可能走不同分支你无法像传统代码一样预先写好所有 if-else 路径。这时需要一种能管理“循环、分支、状态恢复”的结构LangGraph 正是为此而生的。LangGraph 引入了 StateGraph 概念。你可以把 Agent 的执行过程定义成一张图每个节点是处理步骤。每条边是从一个状态到另一个状态的迁移。整个图维护一个共享状态对象。节点读取状态修改状态然后决定下一步进入哪个节点。一个最简单的手写 Agent 循环可以不用 LangGraph但代码会越写越乱。下面是 LangGraph 风格的状态机骨架帮助你理解它和普通循环的区别# agent_graph_demo.py from typing import TypedDict, Literal from langgraph.graph import StateGraph, START, END class AgentState(TypedDict): user_input: str messages: list next_step: str def call_model(state: AgentState) - AgentState: 调用模型的节点 user_input state[user_input] # 这里省略模型调用与工具调用逻辑 state[messages] state.get(messages, []) [fmodel 收到: {user_input}] return state def should_continue(state: AgentState) - Literal[model, __end__]: 条件边决定下一步去哪个节点 if state.get(user_input) quit: return END return model graph StateGraph(AgentState) graph.add_node(model, call_model) graph.add_edge(START, model) graph.add_conditional_edges(model, should_continue) app graph.compile()这个示例省去了很多细节但核心思想已经体现出来了Agent 的运行不再是一层层调用链而是一个带状态的图每一步的走向由前面步骤的结果动态决定。在真实项目选型时可以这样判断若只是“调用一个工具返回结果”不需要 LangGraph一个普通 AgentExecutor 就够如果有“需要多次工具调用、涉及状态共享、步骤之间强依赖、需要人工中断或审查”的需求LangGraph 会更合适。LangGraph 和 LangChain 不是二选一的关系。LangChain 提供 Prompt、模型封装、工具适配LangGraph 使用这些组件来绘制流程状态图。一个复杂 Agent 项目里通常两者都会用到。7. MCP为拆不完的工具适配器画上句号如果你开发过两个以上的 Agent一定会遇到同一个问题每接一个新的数据系统或工具就要写一次适配代码。接数据库要写数据库工具接 Jira 要写 Jira 工具接内部系统又要写 HTTP 封装。工具越多适配层就越膨胀项目维护起来非常痛苦。MCP 的出现是为了给“模型如何获取工具、如何连接外部数据”这个问题提供一个统一的开放协议。可以这样理解MCP Server 就像工具的 USB 接口。你不需要为每个设备单独设计连接线只要设备符合这个接口规范Agent 就能像使用标准接口一样发现并调用它的能力。一个 MCP Server 可以暴露给任意支持 MCP 的客户端使用不用重复开发适配器。MCP 的工作关系分为三层MCP Host运行在 Agent 或 AI 应用进程中负责发起请求。MCP ClientHost 与 Server 之间的连接器。MCP Server负责提供工具、资源和提示能力。LangChain 生态中可以通过langchain-mcp-adapters或自己封装 MCP Client 来对接。与其在 LangChain 内部手动写一个工具不如让 Agent 去连接一个独立运行的 MCP Server这样可以做到工具能力与服务进程解耦也方便多个 Agent 复用同一个工具服务。先用 FastMCP 搭一个本地工具服务示例是一个天气查询接口# mcp_server_demo.py from mcp.server.fastmcp import FastMCP mcp FastMCP(weather-service) mcp.tool() def get_weather(city: str) - str: 获取指定城市的天气信息 # 实际项目中这里可以调用天气服务 return f{city}晴26 度风力 2 级 mcp.tool() def get_city_advice(city: str) - str: 根据城市天气给出穿衣建议 weather get_weather(city) if 26 度 in weather: return f{weather}建议穿短袖或薄长袖 return f{weather}请根据实时天气增减衣物 if __name__ __main__: mcp.run()启动服务python mcp_server_demo.py如果安装的是 MCP 官方命令行工具也可以将它注册到支持 MCP 的客户端里进行联调。联调时重点关注三类问题Server 是否启动成功。工具能否被发现。Agent 是否正确传入了结构化参数。MCP 对项目的影响不只是少写几行代码它会改变团队的协作边界工具能力可以由独立的服务团队维护Agent 团队只需要关心 MCP Server 暴露出来的接口描述。协议统一后换工具、升级工具、恢复工具都不会影响 Agent 主流程。需要提醒的是MCP 不是必须一开始就引入。如果你的 Agent 只有两三个本地函数工具直接定义 Tool 更简单。当工具数量超过十个或者工具需要独立部署、高频复用时再引入 MCP 才有足够收益。8. Skills 与 Prompt让“会做事”和“做对事”分开管理在讨论 Agent 能力时Tool、Skill、MCP、Prompt 这几个概念经常被混在一起。它们确实相关但职责不同。Tool 是原子能力它是一段可执行的代码完成单一、确定的功能。比如“查天气”“发邮件”“读数据库”各自是一个 Tool。Skill 是一组能力的集合通常包含任务描述、触发条件、执行步骤以及用到的多个 Tool。它像一份“操作手册”某个任务来了Agent 知道该按什么步骤、调哪些工具、如何兜底。可以举一个实际例子。假设你要做一个“周报助手”Tool 层读取 Git 提交记录、查询项目进度、生成 Markdown。Skill 层有一个generate_weekly_report技能它规定先读 Git 提交再查询本周关联的项目任务最后把所有信息汇总并生成指定格式的周报。Prompt 层规定输出语气、语言、格式要求。如果只靠 Prompt你写一个几千字的提示词也能让模型完成这件事。但问题在于Prompt 的描述能力有限它描述的是“步骤”而不是“可执行能力”。Tool、Skill、MCP 的加入让模型从“按文本描述想象”变成“按实际接口执行”。三者之间的关系可以参考这张对比表概念作用层次典型粒度是否包含执行逻辑Prompt引导模型输出文本指令否Tool执行单一功能一个函数是Skill组合完成一类任务多个 Tool 流程是MCP统一外部工具接入协议多个 Tool 的外部服务是在 LangChain 中Tool 通常用tool声明from langchain.tools import tool tool def send_todo_msg(todo: str) - str: 把任务记录到今日待办中 # 这里写入待办系统或本地文件 return f已添加待办: {todo}Skill 的工程化方式可以从两个方向理解。第一种是把它变成 Python 类或独立服务在 Agent 决策前提前注册好。第二种是把 Skill 当成一段结构化输入让模型根据用户任务自动选择合适的 Skill 执行。真正落地 Skills 时最容易陷入的误区是“一开始就抽象一大堆 Skill”。正确的做法是先让 Agent 在普通 Tool 上工作观察哪些流程反复出现哪些步骤组合稳定再把它们沉淀成 Skill。Prompt 工程在 Skills 体系里的位置并不是被替代而是被分得更细。每一个 Skill 都应该有自己的系统性提示词设计任务定义、输入要求、输出格式、边界条件、失败兜底。而不只是把所有要求堆在最外层给模型。还有一点被很多人忽略Prompt 是模型“做对事”的下限模型调用和工具执行是“会做事”的基础。两者必须一起优化而不是把希望全部压在某个超长 Prompt 上。9. 完整实战跑通一个带日期工具和穿衣建议的 ReAct Agent把前面各章内容串起来写一个真正可运行的最小项目。最终效果用户询问日期相关问题Agent 会调用日期工具如果用户要求给出穿衣建议Agent 会基于日期和合理的天气设定给出回答。完整代码可以放在一个文件里# agent_example.py import os from datetime import datetime from dotenv import load_dotenv from langchain.agents import create_react_agent, AgentExecutor from langchain.tools import tool from langchain_core.prompts import PromptTemplate from langchain_openai import ChatOpenAI load_dotenv() def get_model(): 根据环境变量选择 OpenAI 兼容接口或本地 Ollama if os.getenv(USE_OLLAMA) 1: from langchain_ollama import ChatOllama return ChatOllama(modelos.getenv(OLLAMA_MODEL, qwen2.5:7b), temperature0) return ChatOpenAI( modelos.getenv(OPENAI_MODEL, gpt-4o-mini), temperature0, api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL), ) tool def get_current_date() - str: 获取今天日期和星期。需要回答今天是几号、星期几时使用此工具。 return datetime.now().strftime(%Y-%m-%d %A) tool def get_temperature_season() - str: 根据当前月份推断大致季节和温度穿衣建议。不要用它查精确天气。 month datetime.now().month if 3 month 5: return 春季中午约 20 度建议穿长袖或薄外套 if 6 month 8: return 夏季白天约 30 度建议穿短袖 if 9 month 11: return 秋季早晚较凉约 18 度建议穿长袖加外套 return 冬季约 5 度建议穿厚外套或羽绒服 tools [get_current_date, get_temperature_season] react_prompt PromptTemplate.from_template( 你是智能助手。如果不知道答案请使用下面的工具。 工具如下 {tools} 工具名称列表{tool_names} 请始终使用以下格式 Thought: 分析用户问题说明为什么需要调用工具 Action: 要使用的工具名称 Action Input: 工具参数如果不需要参数就写“无” Observation: 工具返回的结果 继续思考并循环直到你得到答案 最终回答时使用 Thought: 我已经知道答案 Final Answer: 给用户的最终答案 用户问题{input} {agent_scratchpad} ) def main(): model get_model() agent create_react_agent(model, tools, react_prompt) executor AgentExecutor( agentagent, toolstools, verboseTrue, handle_parsing_errorsTrue, max_iterations3, ) questions [ 今天的日期是什么, 今天是几月适合穿什么衣服, ] for q in questions: print(f\n 用户提问{q} ) result executor.invoke({input: q}) print(f最终回答{result[output]}) if __name__ __main__: main()运行前确认.env已经设置好密钥然后执行python agent_example.py如果你的环境使用本地 Ollama则只需设置一个环境变量再运行export USE_OLLAMA1 python agent_example.py预期输出类似下面这样核心是出现Action和Observation 用户提问今天的日期是什么 Entering new AgentExecutor chain... Thought: 用户想知道今天的日期我需要调用日期工具。 Action: get_current_date Action Input: 无 Observation: 2026-05-11 Monday Thought: 我已经知道今天的日期了。 Final Answer: 今天是 2026 年 5 月 11 日星期一。 Finished chain. 最终回答今天是 2026 年 5 月 11 日星期一。这段输出是不依赖真实模型的内容示意。实际运行时文本有可能不完全一致但流程结构应该相同。如果运行时出现Could not parse LLM output之类的错误优先检查两点第一使用的模型是否能稳定输出指定格式第二Prompt 里是否给足了格式示例。正常来说新版模型在temperature0场景下都能较稳定地走完 ReAct 循环。这个最小项目可以继续扩展例如把get_temperature_season改成从真实的天气 API 拉数据或者把某个工具改成 MCP Server 提供的远程工具思路都是相通的先有稳定的工具定义再让 Agent 通过 ReAct 循环去组合调用。10. 常见问题与排查思路LangChain 生态变化快报错信息经常不是一次能定位。下面是实际项目里频率比较高的几类问题按“现象-原因-排查-方案”整理出来遇到问题时可以对照处理。问题现象可能原因排查方式解决方案安装后import langchain失败Python 版本过低或包之间冲突执行python --version和pip list换到 Python 3.10-3.12 或用虚拟环境重新安装调用 ChatOpenAI 超时或报 401API Key 错误、base_url 不匹配先打印model初始化参数手工 curl 测试兼容接口检查.env中OPENAI_API_KEY确认 base_url 是否带/v1使用本地 Ollama 模型时 ReAct 输出格式乱模型对工具格式理解不强打开 verbose 观察输出文本换支持工具调用的模型或在 Prompt 中加入更明确的示例Agent 执行陷入无限循环模型反复选择同样工具或任务无法终止看日志中是否出现重复 Action给 AgentExecutor 设置max_iterations3并检查工具是否返回了足够信息工具返回“并未使用”工具名与模型调用名不一致打印tools的 name 字段用tool装饰并定义清晰的 docstring模型会参考 name 和描述选择工具中间件相关导入报错本地版本不支持新 API执行检查命令确认版本先升级 langchain-core或改用通用包装模式替代MCP Server 启动后无法发现工具Server 没有注册工具或协议版本不匹配查看 Server 启动日志和工具列表确认mcp.tool()正确声明确保 Client 与 Server 版本一致模型回答内容没问题但不调用工具Prompt 中缺少行动意图或温度过高查看模型原始输出将temperature调低为 0并在 Prompt 中给出必须使用工具的场景说明除了这些表面的问题有一个底层排查思路值得记住把 Layer 分开排查。先直接调用模型确认模型本身可用再单独执行 Tool 确认工具本身正确最后再进入 Agent 链路。多数问题都出在“两个模块之间的格式约定不一致”而不是模块本身坏了。11. 最佳实践与工程建议当你可以跑通一个最小 Agent 后接下来要思考的是怎么让它在真实项目里稳定工作。这里分享几条工程经验。第一条先跑通再抽象再美化。不要一开始就设计一堆中间件、Skill、MCP Server。先把一个最简单的 ReAct Agent 放到真实场景里跑一天把每天真正遇到的问题记录下来再决定加什么能力。很多项目失败不是架构不够先进而是基础链路还没稳定就急着加复杂度。第二条密钥与配置不写在代码里。LangChain 项目涉及模型参数、工具地址、数据库连接等大量配置。建议统一用.env管理并保证.env不进 Git。安全权限要遵循最小化原则Agent 能访问的工具和数据范围尽量只覆盖完成任务所必需的最小集合。尤其是带写操作的工具比如删除、更新、发送必须有二次确认或审计日志。第三条日志和可观测性比功能更重要。Agent 的执行会包含多轮 Thought、Action、Observation不把中间过程记录下来出了问题根本无法定位。本地可以在 AgentExecutor 打开 verbose生产环境建议接入独立的追踪平台记录每次模型调用的输入输出、Token 使用量、工具执行耗时和报错堆栈。第四条给所有外部调用设置超时和重试。模型接口可能慢工具服务可能挂。中间件最适合处理这类横切逻辑统一设置超时、重试和熔断。不要让 Agent 在某个外部依赖卡死时无限等待。第五条明确成本边界。Agent 比普通模型调用贵很多这是最容易被忽略的成本坑。一次用户请求可能触发五六次模型调用每次调用都附带历史和工具结果Token 消耗会成倍上涨。上线前要规划max_iterations、模型 token 上限、上下文裁剪策略同时统计单次会话的平均成本。第六条Prompt、Skill、Tool 分开维护。Prompt 是给模型的“表达说明”Skill 是给 Agent 的“操作手册”Tool 是可执行函数。三者职责不同不要混在一个巨型 Prompt 里。这样模型出问题改 Prompt工具出问题查 Tool流程出问题调 Skill边界清楚协作也更高效。如果还想继续往深走建议按这几个方向展开一是把 LCEL 和 Runnable 的细节吃透尤其是流式输出和异步调用二是用 LangGraph 把目前的 ReAct 循环重写为状态图处理更多分支场景三是把项目中的高频函数整理成可独立部署的 MCP Server让多个 Agent 共用四是系统化学习 Prompt 工程并逐步沉淀属于自己业务的 Skills 库。先跑通一个最小闭环再根据真实业务痛点逐层加复杂度这是 LangChain 项目最稳妥的成长路径。希望这篇文章能成为你这条路上的一份可用地图。
返回列表