
开场这段时间在本地搭 LangChain 项目时发现网上很多教程还停留在 0.x 时代照着写会发现类名、导入路径、链的写法全变了排查半天还找不到原因。这次我以 LangChain 1.3 为基线从核心概念讲到代码实战把链、Agent、记忆、工具调用、输出解析等关键点完整走一遍。适合刚接触 LangChain 的初学者也适合想要从旧版本迁移到 1.x 的后端开发者。学完你可以至少独立搭出一个“能调模型、能挂工具、有记忆能力”的对话应用。1. 背景与核心概念1.1 LangChain 是什么LangChain 是一个用于构建大语言模型Large Language ModelLLM应用的开源开发框架。它解决的问题是大模型本身擅长生成文本但很多业务场景不只是“问一句、答一句”而是需要把模型与数据、外部系统、工具、业务逻辑结合起来。例如你要做一个客服机器人它可能需要查数据库、查订单接口、检索文档、记住用户刚才说过什么这些能力如果全部自己实现工作量会非常大而且不同模型提供商OpenAI、Anthropic、通义、文心等之间切换不方便。LangChain 的核心思路是把模型调用统一封装切换模型厂商时尽量少改业务代码。把应用拆成“组件”和“链”组件可以复用链负责编排流程。提供统一的 Runnable 接口让模型、工具、检索器、Agent 都能以相似的方式调用。内置了提示词管理、输出解析、记忆、Agent、向量检索等常用能力。1.2 LangChain 1.x 相对旧版本的变化LangChain 在 0.x 时代有一个非常典型的问题所有功能都集中在同一个包里导入路径很深很多链式调用写法繁琐。到了 1.x 系列项目做了明显的模块化重构核心抽象如 Runnable、Messages、输出解析器被抽到langchain-core中。模型接入按厂商拆分例如langchain-openai、langchain-anthropic。链的推荐写法从“继承 Chain 的子类”逐渐转向“基于 Runnable 的表达式组合”。对工具调用Tool Calling、结构化输出Structured Output做了更原生级别的支持很多旧的LLMChain写法会提示迁移。因此如果你之前是在旧版本文档或一些老教程里学习打开 1.3 的工程时很可能会遇到“导入包不存在”“方法签名变了”之类的报错。本文后面的代码示例会尽量贴近当前 1.x 系列的推荐写法。1.3 典型应用场景LangChain 可覆盖的场景很多最常见的包括文档问答将本地 PDF、Word、Markdown 等文档切片后做向量化用户提问时先检索相关内容再交给大模型生成回答。Agent 智能体模型根据用户请求决定调用哪个工具如查天气、查数据库、发邮件并把多个工具串起来完成任务。结构化信息抽取从一段非结构化文本中提取姓名、日期、金额等字段用输出解析器生成 JSON。对话式客服结合企业知识库和会话记忆让模型在多轮对话中保持上下文。SQL 问答让模型根据自然语言生成 SQL 查询语句再执行查询并把结果转成自然语言。1.4 为什么要掌握 LangChain因为目前大模型应用开发中LangChain 的抽象方式已经成为事实上的行业标准之一。即使你最终选择直接用原生 SDK理解 LangChain 的链式编排、Agent 循环、工具绑定等设计也能帮你更快地理清一个 LLM 应用的架构。另外基于它扩展你自己的工具和组件比从零开始写更省时间。2. 环境准备与版本说明2.1 环境要求由于 LangChain 依赖 Python 较多且 1.x 版本对类型注解、异步支持要求较高建议使用 Python 3.10 及以上版本。如果你本机装了多个 Python 版本推荐用pyenv或conda管理环境。操作系统方面Windows、macOS、Linux 都可以。但要注意一点tiktoken、pydantic这类库在部分 Windows 环境下可能出现编译问题通常安装正式发布的 wheel 包能解决尽量不要用源码编译。python --version建议输出类似Python 3.11.92.2 创建虚拟环境虚拟环境是最基础但最容易忽略的一步。不建虚拟环境的话全局 Python 环境很容易被不同项目的依赖搞乱。# 创建项目目录 mkdir langchain-demo cd langchain-demo # 创建虚拟环境 python -m venv .venv # 激活虚拟环境Windows 使用 .venv\Scripts\activate source .venv/bin/activate激活后命令行前面会出现(.venv)字样表示当前已经进入虚拟环境。2.3 安装依赖本文的示例会用到以下包pip install -U langchain langchain-core langchain-community langchain-openai python-dotenv这里说明一点langchain是主包langchain-core包含核心抽象langchain-openai负责接入 OpenAI 兼容接口python-dotenv用于读取.env文件中的环境变量。如果你的项目不需要接 OpenAI而是接其他模型可以替换为对应的封装包。安装完成后检查版本python -c import langchain; print(langchain.__version__)输出大概是一个 1.x 的版本号。因为版本升级很快具体数字以你安装时为准。如果打印出来是 0.x 版本说明安装缓存有问题建议重新执行pip install -U。2.4 环境变量配置在项目根目录创建.env文件OPENAI_API_KEYsk-你的密钥如果你使用的是本地模型或兼容 OpenAI 协议的代理服务还可以设置OPENAI_BASE_URLhttps://api.example.com/v1注意.env文件不要提交到 Git 仓库建议加入.gitignore。3. 核心模块与实践原理3.1 模型调用从对话到 Runnable在 LangChain 1.x 中模型类通常放在langchain_openai这类封装包里核心类是ChatOpenAI。# 文件路径example/chat_model.py from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage # 创建模型实例 llm ChatOpenAI(modelgpt-4o-mini, temperature0) # 调用模型 response llm.invoke([HumanMessage(content请用一句话介绍 LangChain)]) print(response.content)这里有几个关键点model参数指模型名称具体名称受模型服务商限制本文示例使用gpt-4o-mini你可以按实际服务商调整。temperature控制生成结果的随机性值越大输出越多变值越小越稳定。invoke是 LangChain 1.x 中最重要的调用方法几乎所有可运行对象都有invoke、stream、batch这些统一接口。HumanMessage表示一条用户消息。LangChain 的消息系统还支持SystemMessage系统提示、AIMessage模型回复、ToolMessage工具返回结果。这段代码看起来很简单但它是后面所有链式应用的基础。3.2 Prompt 模板从硬编码到动态组装实际开发中你不会直接写死一段提示词。更常见的做法是定义模板模板中有变量运行时再填充。# 文件路径example/prompt_template.py from langchain_core.prompts import ChatPromptTemplate prompt ChatPromptTemplate.from_template( 你是{role}。请根据以下内容回答问题{question} ) # 填充变量 messages prompt.invoke({ role: 高级 Python 工程师, question: 请解释 Python 装饰器的作用 }) print(messages)ChatPromptTemplate是 1.x 中推荐使用的提示词模板类。它最终生成的不是字符串而是一组消息列表可以直接传给模型。你可以在模板中指定角色from langchain_core.prompts import ChatPromptTemplate prompt ChatPromptTemplate.from_messages([ (system, 你是一位严格的技术评审专家。), (human, 请评审下面这段代码\n{code}), ])这样写的好处是系统消息和用户消息分离后面的模型可以更清楚地理解指令边界。3.3 输出解析器把大模型输出变成结构化数据大模型的输出默认是纯文本。但业务系统往往需要 JSON、列表、枚举等结构化格式这时就需要输出解析器。# 文件路径example/output_parser.py from typing import List from pydantic import BaseModel, Field from langchain_core.output_parsers import JsonOutputParser from langchain_core.prompts import ChatPromptTemplate class Article(BaseModel): title: str Field(description文章标题) tags: List[str] Field(description文章标签列表) parser JsonOutputParser(pydantic_objectArticle) prompt ChatPromptTemplate.from_template( 请为一篇关于{keyword}的文章生成标题和标签。\n{format_instructions} ) # 把解析器的格式指令注入提示词 prompt_with_format prompt.partial( format_instructionsparser.get_format_instructions() ) chain prompt_with_format | llm | parser result chain.invoke({keyword: LangChain 入门}) print(result)JsonOutputParser会让模型按 JSON 格式输出再用 Pydantic 模型校验字段。这里的|是 LangChain 1.x 链式调用的核心语法后面会专门讲。3.4 链与 Runnable用|组装流程LangChain 1.x 的链式写法非常轻盈。只要对象实现了Runnable接口就可以用管道符|串联起来前一个的输出会作为后一个的输入。常见链路PromptTemplate | LLM | OutputParserchain prompt | llm | parser这种方式比旧版LLMChain更直观也更方便调试。你可以在任意环节插入自定义函数from langchain_core.runnables import RunnableLambda def upper(text: str) - str: return text.upper() chain prompt | llm | RunnableLambda(upper)这里RunnableLambda的作用是把普通 Python 函数包装成 Runnable使其可以参与链式调用。3.5 Agent 与工具调用Agent智能体是 LangChain 中比较有难度的部分。和普通链不同Agent 会根据用户问题“思考”要调用什么工具然后根据工具返回的结果决定下一步动作直到收集到足够信息再给出最终答复。一个典型的 Agent 循环是接收用户输入。模型判断需要调哪个工具。调用工具拿到结果。把工具结果返回给模型。重复直到模型给出最终回答。3.5.1 自定义工具先给一个简单工具函数# 文件路径example/tools.py from langchain_core.tools import tool tool def get_city_weather(city: str) - str: 根据城市名称查询当前天气情况。 # 这里只是模拟实际项目中可以调用天气服务接口 if 北京 in city: return 北京晴25℃ elif 上海 in city: return 上海小雨22℃ else: return f{city}多云23℃tool装饰器会把一个普通函数包装成模型可以调用的工具。函数的docstring会被当作工具描述模型开发时主要靠这个描述来决定是否调用所以描述要写清楚。3.5.2 创建并运行 Agent# 文件路径example/agent_demo.py from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from example.tools import get_city_weather llm ChatOpenAI(modelgpt-4o-mini, temperature0) prompt ChatPromptTemplate.from_messages([ (system, 你是一个聪明的助手可以通过工具获取信息。), (human, {input}), (placeholder, {agent_scratchpad}), ]) agent create_tool_calling_agent(llm, [get_city_weather], prompt) agent_executor AgentExecutor(agentagent, tools[get_city_weather], verboseTrue) result agent_executor.invoke({input: 北京今天天气怎么样}) print(result)注意ChatPromptTemplate中使用了placeholder占位符用来存放 Agent 的中间思考过程和工具调用记录。这也是 1.x 中create_tool_calling_agent的常见写法。3.6 记忆与状态多轮对话的上下文管理普通链不保存状态但对话场景需要记住历史消息。LangChain 提供了多种记忆组件常见的是InMemoryChatMessageHistory。# 文件路径example/memory_demo.py from langchain.memory import InMemoryChatMessageHistory from langchain_core.runnables.history import RunnableWithMessageHistory from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate history_store {} def get_session_history(session_id: str): if session_id not in history_store: history_store[session_id] InMemoryChatMessageHistory() return history_store[session_id] prompt ChatPromptTemplate.from_messages([ (system, 你是一个乐于助人的助手。), (placeholder, {history}), (human, {input}), ]) llm ChatOpenAI(modelgpt-4o-mini, temperature0) chain prompt | llm chain_with_history RunnableWithMessageHistory( chain, get_session_history, input_messages_keyinput, history_messages_keyhistory, ) response chain_with_history.invoke( {input: 我叫小明}, config{configurable: {session_id: session-001}} ) print(response.content) response2 chain_with_history.invoke( {input: 我叫什么名字}, config{configurable: {session_id: session-001}} ) print(response2.content)这段代码的核心是RunnableWithMessageHistory它会在每次调用前从会话历史中取出消息并拼接到提示词中。InMemoryChatMessageHistory把历史存在内存里重启进程后清空。生产环境如果要做持久化可以换成 Redis 或数据库实现。4. 完整实战案例构建一个带工具和记忆的智能助手第 3 节介绍了各个模块这一节我们把这些模块串起来做一个相对完整的案例一个既能对话记忆、又能查询天气和做简单计算的小助手。4.1 需求分析这个小助手需要具备以下能力支持多轮对话能记住用户之前说过的话。能调用“天气查询”工具模拟查询城市天气。能调用“计算器”工具处理简单的四则运算。当用户提问不需要工具时直接回答。4.2 项目结构langchain-demo/ ├── .env ├── .gitignore ├── requirements.txt ├── tools/ │ ├── __init__.py │ ├── weather.py │ └── calculator.py ├── agent_app/ │ ├── __init__.py │ └── assistant.py └── run.py4.3 编写工具模块tools/weather.pyfrom langchain_core.tools import tool tool def get_weather(city: str) - str: 根据城市中文名称查询天气情况输入示例北京、上海、广州。 city_weather { 北京: 晴25℃, 上海: 小雨22℃, 广州: 多云30℃, 深圳: 雷阵雨28℃, } if city in city_weather: return f{city}天气{city_weather[city]} return f{city}天气未知请检查城市名称tools/calculator.pyfrom langchain_core.tools import tool tool def calculate(expression: str) - str: 计算简单的四则运算表达式例如 12*3注意只能包含数字和 - * / 等符号。 try: # 安全校验只允许数字和四则运算符号 allowed_chars set(0123456789-*/(). ) if not set(expression).issubset(allowed_chars): return 表达式包含非法字符 result eval(expression, {__builtins__: {}}, {}) return str(result) except Exception: return 表达式无法计算这里需要提醒一下eval在真实项目中存在安全问题不能直接对用户输入执行。上面的示例只是为了演示工具调用流程生产环境应当使用更安全的方式比如把表达式交给专门的表达式解析库或者在受控沙箱中执行。这一点在后面的最佳实践里会再强调。4.4 编写 Agent 入口agent_app/assistant.pyfrom langchain.agents import create_tool_calling_agent, AgentExecutor from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from tools.weather import get_weather from tools.calculator import calculate def build_assistant(model_name: str gpt-4o-mini) - AgentExecutor: llm ChatOpenAI(modelmodel_name, temperature0) tools [get_weather, calculate] prompt ChatPromptTemplate.from_messages([ (system, 你是一个智能助手你可以使用工具获取天气信息、完成数学计算。 先调用工具获取结果再结合工具返回内容回答用户。), (human, {input}), (placeholder, {agent_scratchpad}), ]) agent create_tool_calling_agent(llm, tools, prompt) executor AgentExecutor( agentagent, toolstools, verboseTrue, handle_parsing_errorsTrue, ) return executorhandle_parsing_errorsTrue的作用是当模型返回的中间结果无法解析时不直接报错而是把错误信息反馈给模型让它重试。这对提高 Agent 稳定性很有帮助。run.pyimport os from dotenv import load_dotenv from agent_app.assistant import build_assistant load_dotenv() def main(): assistant build_assistant() print(智能助手已启动输入 q 退出) while True: user_input input(\n你) if user_input.strip().lower() in (q, quit, exit): break response assistant.invoke({input: user_input}) print(f助手{response[output]}) if __name__ __main__: main()4.5 运行结果在项目根目录执行python run.py当我输入“北京今天天气怎么样”时日志中可以看到 Agent 先调用get_weather工具再把结果组合成回答。最终输出类似助手北京今天天气是晴25℃。再输入“计算 (128)*3 的结果”Agent 会调用calculate工具助手(128)*3 的结果是 60。这个案例虽然简单但已经覆盖了 LangChain 1.x 中最重要的几个环节模型调用与提示词模板自定义工具与工具调用Agent 执行器控制台交互循环如果你想加上记忆能力可以把 Agent 执行器包装到RunnableWithMessageHistory中或者直接在AgentExecutor上做一层会话管理。这里不再展开但思路与 3.6 节一致。5. 常见问题与排查思路在 1.x 的实践过程中很多报错是相似的。下面整理一张高频问题表方便你快速定位。问题现象常见原因解决思路导入langchain.llms.OpenAI报错旧写法在 1.x 已废弃改为from langchain_openai import ChatOpenAIlangchain版本一直显示 0.xpip 缓存或未指定包源执行pip install -U langchain必要时清缓存调用模型时报 401 / AuthenticationErrorAPI Key 未配置或配置错误检查.env文件确认环境变量已加载出现OutputParserException模型返回非标准 JSON检查提示词中的格式指令开启重试逻辑Agent 卡住或死循环工具描述不清晰或返回内容不明确优化工具 docstring限制最大迭代次数中文乱码终端编码问题Windows 下设置chcp 65001或调整 IDE 编码为 UTF-8网络超时服务地址不可达或网络不稳定检查OPENAI_BASE_URL确认能连通对应服务地址增加超时时间参数5.1 模型调用返回空内容一种常见情况是llm.invoke返回的content为空字符串。原因可能是模型对 prompt 中的内容做了安全过滤拒绝回答。请求参数里设置了max_tokens太小输出被截断成空。系统的 temperature 设置过高导致输出异常。解决建议先用最简单的提示词测试模型本身是否可用再到复杂链中排查。5.2 环境变量加载问题使用python-dotenv时要注意.env文件必须在当前工作目录下。如果你的项目结构比较复杂建议在入口文件顶部显式指定路径from dotenv import load_dotenv load_dotenv()如果还是拿不到环境变量先手动打印确认import os print(OPENAI_API_KEY:, os.getenv(OPENAI_API_KEY))5.3 Agent 没有调用工具Agent 不调用工具通常是下面几种情况工具描述不够清楚模型不知道什么时候该用。用户的问题本来就不需要工具模型判断可直接回答。提示词中缺少 agent_scratchpad 占位符导致 Agent 无法记录中间步骤。排查时打开verboseTrue观察 Agent 的中间输出。5.4 输出解析器格式校验失败JsonOutputParser依赖大模型输出合法 JSON。如果模型经常输出额外说明文字可以在提示词中更严格地强调“只输出 JSON不要包含任何解释”。同时建议设置重试机制解析失败时让链再次运行。6. 最佳实践与工程建议6.1 密钥管理永远不要把 API Key 硬编码在代码里。生产环境建议使用专用的密钥管理服务或平台环境变量开发环境用.env文件并将.env加入.gitignore。.env .venv/ __pycache__/6.2 结构化输出优先尽可能让模型输出 JSON 或 Pydantic 对象而不是自由文本。结构化输出的好处是后续业务逻辑可以直接读取字段不需要处理字符串。字段校验可以由 Pydantic 完成。方便做单元测试和集成测试。6.3 错误处理与重试调用外部模型服务时网络问题、限流、超时几乎是不可避免的。建议封装一层带重试的客户端from tenacity import retry, stop_after_attempt, wait_exponential retry(stopstop_after_attempt(3), waitwait_exponential(multiplier1, min2, max10)) def call_llm_with_retry(chain, input_data): return chain.invoke(input_data)注意重试要区分错误类型如果是认证错误或参数错误重试没有意义应该直接抛异常。6.4 工具安全边界自定义工具时最怕的是模型根据用户输入触发了危险操作。比如计算器中的eval、让 Agent 执行 Shell 命令、访问数据库删除数据等场景都需要严格校验输入。安全原则可以概括为最小权限工具只给它完成业务所需的最小能力。输入校验所有外部输入都做类型、范围、字符集校验。沙箱执行如果确实需要执行动态代码放进隔离环境。审批机制高危险操作如删除、付款、发送消息必须人工确认。6.5 日志与可观测性Agent 的调用链往往很长调试时非常痛苦。建议在关键节点打日志每次调用模型前记录 prompt 内容。每次工具调用时记录工具名、输入和输出。记录 Agent 的中间步骤。记录耗时和 token 消耗。LangChain 自带 verbose 参数但生产环境建议使用结构化日志把重要信息传给日志平台。6.6 成本控制大模型 API 按 token 计费越复杂的链消耗越多。可以从几个角度控制成本使用便宜的小模型处理简单任务用强模型处理复杂任务。尽可能减少喂给模型的上下文长度不要无脑拼接所有历史记录。对同样的请求考虑加缓存。from langchain_core.runnables import RunnableLambda def simple_cache(func): cache {} def wrapper(*args, **kwargs): key str(args) str(kwargs) if key not in cache: cache[key] func(*args, **kwargs) return cache[key] return wrapper6.7 版本锁定LangChain 版本迭代很快不同版本的 API 差异可能很大。建议项目里用requirements.txt或pyproject.toml锁定大版本范围避免某次依赖升级导致全项目不可用。langchain1.x.x langchain-core1.x.x langchain-openai1.x.x把 x 替换为实际安装的版本号即可。这样团队其他人拉代码后能复现环境。6.8 使用 Flow/LangGraph 编排复杂流程如果只是简单的“提示词 模型 输出解析”用|就足够了。但如果业务涉及多个分支、循环、人工确认、并行调用更推荐使用 LangGraph。LangGraph 在状态图层面管理 Agent 行为可维护性比纯提示词控制好很多。这一点可以作为进阶学习路线来处理未来有需要可以单独写一篇专门的教程。7. 总结与下一步学习路线这篇文章围绕 LangChain 1.3 这条主线逐一拆解了模型调用、Prompt 模板、输出解析器、Runnable 链、Agent、工具调用和记忆管理最后用一个“带工具的小助手”案例把这些能力串起来。你至少应该掌握以下要点LangChain 1.x 中的对象普遍实现了invoke/stream/batch这类统一接口。|是 LangChain 1.x 最核心的链式组合方式。Agent 的核心不只是“调用模型”而是“决定调用什么工具 处理工具结果 不断循环”。输出解析器让大模型输出具备可编程性。记忆组件可以帮你在多轮对话中保持上下文但生产环境必须考虑持久化方案。下一步可以按照这个顺序继续深入熟悉 LangChain 中Runnable的各种方法尤其是stream和batch。尝试接本地模型或国产模型比如通过langchain-openai对接 OpenAI 兼容接口或者使用各自厂商的 LangChain 封装包。学习向量数据库与文档检索做一个基于 RAG 的文档问答系统。学习 LangGraph用它替代直接编写自定义 Agent 循环处理更复杂的状态和流程。尝试把 Agent 挂在 Web 服务上比如用 FastAPI 包一层 HTTP 接口供前端调用。在动手实践时建议先跑通本文的最小示例再逐步增加需求遇到问题优先看日志不要盲改代码。如果本文对你有帮助可以收藏备用后面我会继续更新 LangChain 相关的高级话题。