LangChain与LangGraph:从线性链到状态图的AI应用开发演进
2026/8/6 6:13:37 网站建设 项目流程

大家好,我是专注于AI应用开发的技术博主。在探索大模型应用落地的过程中,你是否曾被 LangChain 和 LangGraph 这两个名字相似、功能又有所重叠的框架搞得一头雾水?它们到底是什么关系?是升级版还是并列关系?在构建AI应用时,又该如何选择?

本文将为你彻底理清 LangChain 与 LangGraph 的从属、区别与核心适用场景。通过动画讲解般的拆解思路,结合代码示例,让你在5分钟内建立起清晰的概念图谱。无论你是零基础的初学者,还是已有一定经验的开发者,都能轻松掌握,并为你的下一个AI应用项目做出正确的技术选型。

1. 核心概念:它们分别是什么?

在深入关系之前,我们必须先明确两个框架各自的定义和核心使命。

1.1 LangChain:大模型应用的“脚手架”与“粘合剂”

你可以把 LangChain 想象成一个功能强大的工具箱脚手架。它的主要目标是简化构建基于大语言模型(LLM)的应用程序的过程。

它解决了什么问题?早期直接调用大模型API(如 OpenAI GPT)开发应用时,开发者面临诸多挑战:

  1. 上下文管理:如何将超长的对话历史或文档有效地喂给模型?
  2. 工具调用:如何让模型不仅能聊天,还能执行代码、查询数据库、调用API?
  3. 数据交互:如何让模型读取本地文件、网页内容或数据库信息?
  4. 流程编排:如何将调用模型、处理输入、解析输出、执行工具等多个步骤串联起来?

LangChain 通过提供一系列模块化组件(如 Models, Prompts, Indexes, Chains, Agents)和高级接口,将上述复杂功能封装成易于使用的模块,让开发者能像搭积木一样快速构建应用。

一个简单的 LangChain “链”(Chain)示例:

# 文件:simple_chain.py # 这是一个使用LangChain构建的简单问答链 from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser # 1. 定义模型 llm = ChatOpenAI(model="gpt-3.5-turbo") # 2. 定义提示词模板 prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个专业的技术翻译助手。"), ("user", "请将以下英文技术术语翻译成中文:{term}") ]) # 3. 构建链:输入 -> 提示词 -> 模型 -> 输出解析 chain = prompt | llm | StrOutputParser() # 4. 调用链 result = chain.invoke({"term": "Neural Network"}) print(result) # 输出:神经网络

这个例子展示了 LangChain 的核心抽象之一Chain,它把多个步骤(提示词填充、模型调用、输出解析)组合成一个可执行单元。

1.2 LangGraph:复杂、有状态工作流的“流程图”与“调度器”

LangGraph 是构建在 LangChain 之上的一个专门库。你可以把它看作是 LangChain 这个工具箱里,用于绘制和运行复杂流程图的高级工具。

它解决了什么问题?当你的应用逻辑不再是简单的线性链(A->B->C),而是包含循环、分支、并行、持久化状态时,基础的Chain就显得力不从心。例如:

  • 一个支持多轮对话、能根据用户反馈回溯或跳转的客服机器人。
  • 一个自主执行多步骤任务(搜索、分析、撰写、审核)的智能体(Agent)。
  • 一个需要持续跟踪会话状态和中间结果的审批流程。

LangGraph 引入了图(Graph)的概念。它将应用中的每个步骤定义为一个节点(Node),步骤之间的流转路径定义为边(Edge)。它特别擅长管理有状态(Stateful)的、可能循环的执行过程。

核心比喻:

  • LangChain像是提供了砖块、水泥、钢筋(Models, Prompts, Tools)。
  • LangGraph则是专门用来设计并建造带有复杂管道系统(循环、条件分支)和记忆房间(状态持久化)的建筑蓝图和施工管理系统

2. 关系辨析:从属、互补与演进

理解了各自是什么,它们的关系就一目了然了。

2.1 从属关系:LangGraph 是 LangChain 生态的“高级成员”

这是最关键的一点:LangGraph 是 LangChain 框架的一部分,但它是一个相对独立、专注于解决特定问题的库。

  • 在安装上,它通常是langchain包的一个子集或独立包(如langgraph)。
  • 在功能上,它深度依赖 LangChain 的核心组件,如 Models、Tools、Runnables。你需要先用 LangChain 定义好你的LLM、工具和提示词,然后才能用 LangGraph 来编排它们。
  • 在定位上,LangGraph 不是来取代 LangChain 的Chain,而是来补充和增强它,处理Chain难以应对的复杂场景。

2.2 核心区别:线性链 vs. 状态图

我们可以通过一个对比表格来清晰区分:

特性LangChain (Chain)LangGraph (Graph)
核心抽象链(Chain)图(Graph)
流程结构线性或少量分支。通常是预定义的顺序执行。任意复杂结构。支持循环、条件分支、并行、嵌套子图。
状态管理隐式或无状态。每次调用相对独立,状态通过上下文传递或外部管理。显式且有状态。内置State对象,在整个图执行过程中持久化并传递数据。
控制流有限。主要通过RunnableBranch等实现简单分支。强大。基于边的条件逻辑(conditional_edge)决定下一个节点,轻松实现循环(END指向START)。
适用场景问答系统、文档总结、简单提取、一次性转换任务。多轮对话代理、复杂决策工作流、多步骤任务执行、需回溯的流程。
复杂度低到中。易于理解和上手。中到高。需要设计节点和边,但表达能力极强。

2.3 演进关系:从 Chain 到 Graph

LangGraph 的出现代表了 LangChain 生态对更复杂应用场景的回应。早期 LangChain 的Agent实现其实已经包含了类似图的循环执行逻辑,但不够直观和灵活。LangGraph 将这种模式抽象成第一公民(first-class citizen),提供了更规范、更强大、更可视化的方式来构建智能体和复杂工作流。

简单说:当你觉得用 Chain 拼接起来很别扭、需要写很多胶水代码来处理循环和状态时,就该考虑使用 LangGraph 了。

3. 环境准备与安装

在开始代码实战前,我们需要设置好开发环境。本文示例将使用 Python。

3.1 基础环境要求

  • Python 版本:建议 3.8 及以上。
  • 包管理工具:使用pip
  • 大模型访问:需要一个可用的 LLM API 密钥(如 OpenAI, Anthropic, 或本地部署的模型)。本文以 OpenAI 为例。
  • IDE:任何你喜欢的代码编辑器(VS Code, PyCharm 等)。

3.2 安装依赖

我们将安装langchain核心包、用于连接 OpenAI 的langchain-openai、以及langgraph。同时,为了更好的开发体验,可以安装langchain-cli(可选)。

打开终端,执行以下命令:

# 创建并进入项目目录(可选) mkdir langchain-langgraph-demo && cd langchain-langgraph-demo # 创建虚拟环境(推荐) python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # Mac/Linux: source venv/bin/activate # 安装核心依赖 pip install langchain langchain-openai langgraph # 安装可选依赖,用于本地模型或工具 # pip install langchain-community

3.3 设置 API 密钥

在代码中直接硬编码密钥是不安全的。推荐使用环境变量。

在终端中设置(临时):

# Linux/Mac export OPENAI_API_KEY="your-api-key-here" # Windows (Command Prompt) set OPENAI_API_KEY=your-api-key-here # Windows (PowerShell) $env:OPENAI_API_KEY="your-api-key-here"

在 Python 代码中读取:

import os from langchain_openai import ChatOpenAI # 从环境变量读取 api_key = os.getenv("OPENAI_API_KEY") if not api_key: raise ValueError("请设置 OPENAI_API_KEY 环境变量") llm = ChatOpenAI(api_key=api_key, model="gpt-3.5-turbo")

4. 实战对比:分别用 Chain 和 Graph 实现对话

让我们通过一个具体的例子来感受区别。任务:构建一个对话助手,它能回答问题,并在用户说“告诉我更多”时,提供关于上一个话题的更多详细信息。

4.1 使用 LangChain Chain 实现(略显笨拙)

用基础的Chain来实现这个“循环”逻辑,我们需要在外部手动管理状态(对话历史)。

# 文件:chain_conversation.py from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain_core.output_parsers import StrOutputParser from langchain.memory import ConversationBufferMemory # 初始化模型和内存 llm = ChatOpenAI(model="gpt-3.5-turbo") memory = ConversationBufferMemory(return_messages=True, memory_key="chat_history") output_parser = StrOutputParser() # 定义提示词模板 prompt_template = ChatPromptTemplate.from_messages([ ("system", "你是一个友好的助手。根据对话历史回答用户问题。如果用户说‘告诉我更多’,请对上一个回答进行扩展和补充。"), ("placeholder", "{chat_history}"), # 这里依赖外部传入的历史 ("user", "{input}") ]) # 构建链 chain = prompt_template | llm | output_parser # 模拟对话循环 def chat_with_chain(): print("助手:你好!我是你的助手。输入‘退出’来结束对话。") chat_history = [] while True: user_input = input("\n你:") if user_input.lower() == '退出': print("助手:再见!") break # 准备链的输入,需要手动拼接历史 context = {"input": user_input, "chat_history": "\n".join([f"{role}: {msg}" for role, msg in chat_history])} # 调用链 response = chain.invoke(context) print(f"助手:{response}") # 手动更新历史 chat_history.append(("用户", user_input)) chat_history.append(("助手", response)) if __name__ == "__main__": chat_with_chain()

痛点分析

  1. 状态外置:对话历史chat_history需要在链的外部(chat_with_chain函数中)手动维护和格式化。
  2. 逻辑混合:判断用户是否说“告诉我更多”的逻辑,被硬编码在系统提示词里,模型可能不理解或执行不准确。
  3. 控制流局限:整个流程的“循环”是由外部的while循环驱动的,而不是链本身的特性。链本身只是一次性转换。

4.2 使用 LangGraph 实现(清晰优雅)

现在,我们用 LangGraph 来重构。我们将“回答问题”和“提供更多细节”定义为两个不同的节点,并通过条件边来控制流程。

# 文件:graph_conversation.py from typing import TypedDict, Annotated, Literal import operator from langgraph.graph import StateGraph, END from langgraph.graph.message import add_messages from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate # 1. 定义状态结构 class State(TypedDict): # 消息历史,LangGraph 提供了专门的注解来处理 messages: Annotated[list, add_messages] # 上一个话题,用于“告诉我更多”的场景 last_topic: str # 2. 初始化模型和提示词 llm = ChatOpenAI(model="gpt-3.5-turbo") # 节点A:普通回答 def answer_node(state: State): prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个友好的助手。请直接回答用户的问题。"), ("placeholder", "{messages}"), ("user", "{input}") ]) # 获取最新的用户消息 last_message = state["messages"][-1].content if state["messages"] else "" chain = prompt | llm response = chain.invoke({"messages": state["messages"], "input": last_message}) # 更新状态:添加助手回复,并记录话题 return { "messages": [response], # add_messages 注解会自动追加到列表 "last_topic": last_message # 记录用户的问题作为上一个话题 } # 节点B:扩展上一个回答 def elaborate_node(state: State): prompt = ChatPromptTemplate.from_messages([ ("system", "用户想了解更多关于上一个话题的信息。请对以下话题进行详细扩展和补充说明。"), ("user", "话题是:{topic}") ]) chain = prompt | llm response = chain.invoke({"topic": state["last_topic"]}) return {"messages": [response]} # 3. 路由函数:决定下一个节点 def route_after_answer(state: State) -> Literal["elaborate", "__end__"]: # 检查用户最新的输入 last_user_msg = state["messages"][-1].content.lower() if state["messages"] else "" if "告诉我更多" in last_user_msg or "more" in last_user_msg: return "elaborate" # 前往扩展节点 else: return "__end__" # 结束本轮对话 # 4. 构建图 workflow = StateGraph(State) # 添加节点 workflow.add_node("answer", answer_node) workflow.add_node("elaborate", elaborate_node) # 设置入口点 workflow.set_entry_point("answer") # 添加条件边:从 answer 节点出来后,根据路由函数决定去向 workflow.add_conditional_edges( "answer", route_after_answer, { "elaborate": "elaborate", # 如果返回 "elaborate",跳转到 elaborate 节点 "__end__": END # 如果返回 "__end__",结束 } ) # 从 elaborate 节点出来后,直接结束 workflow.add_edge("elaborate", END) # 编译图 app = workflow.compile() # 5. 运行图 from IPython.display import Image, display try: # 可视化图结构(需要安装 graphviz) display(Image(app.get_graph().draw_mermaid_png())) except: print("无法显示图形,但图已构建成功。") # 模拟对话 print("=== 开始 LangGraph 对话 ===") initial_state = {"messages": [("user", "什么是机器学习?")], "last_topic": ""} result = app.invoke(initial_state) print(f"助手回答:{result['messages'][-1].content}") # 用户要求更多 new_state = { "messages": result["messages"] + [("user", "告诉我更多")], # 添加新消息 "last_topic": result.get("last_topic", "") } result2 = app.invoke(new_state) print(f"助手扩展:{result2['messages'][-1].content}")

优势分析

  1. 状态内化:所有状态(对话历史、上一个话题)都定义在State对象中,并在节点间自动传递和更新。
  2. 逻辑分离:“普通回答”和“扩展回答”是两个独立的节点,职责单一,易于测试和维护。
  3. 显式控制流route_after_answer函数和add_conditional_edges清晰地定义了业务逻辑:如果用户要求更多,就循环到elaborate节点,否则结束。这比在提示词里描述逻辑要可靠得多。
  4. 可视化app.get_graph().draw_mermaid_png()可以生成流程图,直观展示应用逻辑,对调试和团队沟通极其有利。

5. 核心适用场景与选择指南

通过上面的对比,我们可以总结出各自的“主场”。

5.1 优先选择 LangChain Chain 的场景

  • 简单的线性任务:文档加载->分割->向量化->检索->问答。这种管道式操作,用LCEL(LangChain Expression Language) 写成的链非常简洁。
  • 快速原型验证:当你需要快速测试一个想法,比如“用这个提示词和模型能不能完成摘要”,一个简单的PromptTemplate | LLM链几分钟就能搭好。
  • 无状态或状态简单的应用:每次请求都是独立的,或者状态可以通过简单参数传递。
  • 作为 LangGraph 的“零件”:在 LangGraph 中,每个节点内部的功能,往往就是由一个或多个 LangChain Chain 来实现的。

5.2 优先选择 LangGraph 的场景

  • 构建复杂的智能体(Agent):这是 LangGraph 的“杀手级”应用。智能体需要循环执行“思考->行动->观察”的步骤,直到任务完成,这天然是一个图结构。
  • 多轮、有状态的对话系统:对话状态(用户意图、已填写槽位、历史)需要在整个会话中持久化和流转。
  • 业务流程自动化:例如,一个订单处理流程:接收订单->检查库存-> (有货)创建物流单/ (无货)通知采购->发送确认。其中的分支和节点非常适合用图来定义。
  • 需要清晰可视化的工作流:当你需要向非技术人员(如产品经理)解释应用逻辑时,一张自动生成的流程图比代码更有说服力。
  • 涉及循环和回溯的任务:例如,一个代码生成助手,如果生成的代码有错误,可以回溯到“分析错误”节点,然后重新进入“生成代码”节点。

选择流程图:

开始 | v 你的应用流程是简单的、线性的吗? --> 是 --> 使用 LangChain Chain |否 v 你的应用需要处理循环、分支、或有复杂的状态吗? --> 是 --> 使用 LangGraph |否 v 从 LangChain Chain 开始,随着复杂度增长,自然过渡到 LangGraph。

6. 常见问题与排查思路

在实际使用中,你可能会遇到以下典型问题:

问题现象可能原因排查思路与解决方案
ImportErrorModuleNotFoundError1. 包未正确安装。
2. 包名或导入路径在新版本中已更改。
1. 使用 `pip list
API 密钥错误或模型调用失败1. 环境变量未设置或设置错误。
2. 网络问题或 API 服务不可用。
3. 模型名称拼写错误或额度不足。
1. 在代码中打印os.getenv(“OPENAI_API_KEY”)的前几位确认。
2. 尝试用curl或直接requests调用 API 端点测试连通性。
3. 登录对应平台检查额度和模型可用性。
LangGraph 图编译或运行时报错1.State结构定义与节点返回值不匹配。
2. 边(Edge)指向不存在的节点。
3. 节点函数没有返回有效的状态更新字典。
1. 仔细核对StateTypedDict定义,确保节点返回的字典键名与之匹配。
2. 使用app.get_graph().print()打印图结构,检查节点和边名称。
3. 确保每个节点函数都返回一个字典,用于更新状态。
智能体陷入死循环1. 结束条件(END)设置不当。
2. 路由逻辑有误,导致在两个节点间无限跳转。
1. 在 LangGraph 中,确保有明确的路径通向END
2. 在路由函数中添加日志或打印语句,调试其返回值。
3. 为循环设置最大迭代次数,例如在状态中维护一个step_count,并在路由函数中检查。
提示词效果不佳1. 提示词指令不清晰。
2. 未提供足够的上下文或示例。
3. 系统提示词与用户消息角色混淆。
1. 使用ChatPromptTemplate.from_messages清晰区分system,user,assistant,placeholder等角色。
2. 遵循最佳实践:指令明确、上下文相关、示例具体(Few-shot)。
3. 在 LangChain 生态中,可以利用hub.pull()拉取经过社区验证的优质提示词。

7. 最佳实践与工程建议

将 LangChain/LangGraph 用于生产级项目时,请考虑以下建议:

7.1 项目结构与代码组织

  • 分离配置与逻辑:将模型配置、API密钥、提示词模板等放在配置文件(如config.yaml)或环境变量中,不要硬编码在业务逻辑里。
  • 模块化节点:在 LangGraph 中,将每个节点(Node)的实现放在独立的函数或类方法中,保持功能单一,便于单元测试。
  • 使用类型提示:为State和节点函数参数添加详细的类型提示(如TypedDict,Annotated),这能极大提升代码可读性和 IDE 支持。

7.2 可观察性与调试

  • 启用 LangSmith:LangSmith 是 LangChain 官方的调试和监控平台。通过设置环境变量LANGCHAIN_TRACING_V2=trueLANGCHAIN_API_KEY,可以自动记录每次链和图的调用详情,包括输入、输出、中间步骤和耗时,是排查问题的利器。
  • 日志记录:在关键的节点函数、路由函数中添加日志,记录状态的变化和决策过程。
  • 可视化图:定期使用app.get_graph().draw_mermaid_png()导出当前工作流的图片,作为文档的一部分。

7.3 性能与稳定性

  • 缓存:对于昂贵的操作(如模型调用、向量检索),利用 LangChain 内置的缓存(如InMemoryCache,SQLiteCache)或集成 Redis 等外部缓存,避免重复计算。
  • 超时与重试:为模型调用和外部工具调用配置合理的超时时间和重试策略,增强应用的鲁棒性。可以使用tenacity库或 LangChain 的RunnableWithRetry
  • 流式输出:对于需要长时间运行的链或图,优先支持流式输出(Streaming),通过chain.stream()app.stream()逐步返回结果,提升用户体验。

7.4 生产环境部署

  • 状态持久化:LangGraph 的State默认在内存中。在生产环境中,你需要将其持久化到数据库(如 PostgreSQL, Redis)。可以通过实现自定义的CheckpointSaver来做到这一点。
  • 版本管理:对提示词、图的结构、节点逻辑进行版本控制。任何更改都应经过测试,并考虑蓝绿部署等策略,以平滑升级。
  • 权限与安全:如果图中有调用外部工具或 API 的节点,务必实施严格的权限检查和输入验证,防止越权操作或注入攻击。

8. 总结与学习路线

通过本文的“动画式”拆解,相信你已经清晰地理解了 LangChain 与 LangGraph 的关系:LangChain 是构建LLM应用的综合性基础框架,而 LangGraph 是其生态中专门用于编排复杂、有状态工作流的高级库。它们不是二选一的关系,而是相辅相成。

你的学习路线可以这样规划:

  1. 入门阶段:从 LangChain 开始。掌握Models,Prompts,Chains,Agents这些核心概念,用LCEL编写简单的链。完成一个文档问答或总结的小项目。
  2. 进阶阶段:当你的链变得复杂,需要处理分支或循环时,引入 LangGraph。学习如何定义State、创建NodeEdge,构建一个带条件逻辑的智能体。
  3. 精通阶段:深入 LangGraph 的高级特性,如持久化检查点(Checkpointer)、并行执行、子图(Subgraph)以及如何与LangServe结合快速部署为 API 服务。

记住,技术选型的核心是匹配场景。对于大多数初创想法,一个简单的 LangChain Chain 足以快速验证。当业务逻辑变得复杂和动态时,LangGraph 提供的结构化和可视化能力将成为你不可或缺的利器。现在,就选择一个你感兴趣的场景,动手搭建你的第一个 LangGraph 工作流吧。如果在实践中遇到任何问题,欢迎在评论区交流讨论。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询