LangChain框架深度解析:AI项目开发的技术选型与最佳实践指南
在AI应用开发的教学与实践中,一个频繁被提及的核心问题是:构建AI项目时应如何选择开发框架? 常见的选项包括Coze、Dify、FastGPT、n8n以及LangChain。对于偏好高度可控性的技术开发者而言,自主编写代码往往是首选方案,拖拽式低代码平台通常仅用于原型演示。若必须在框架中做出选择,LangChain通常被视为最优解。
LangChain及其扩展LangGraph是目前主流的AI智能体(Agent)开发框架,它们为开发者提供了一套从基础组件封装到复杂流程编排的完整工具链。随着LangChain 1.x与LangGraph 1.x版本的日臻完善,整个技术栈的生态分工与工程化实践路径已变得更加清晰。本文将系统性地剖析这两个框架的核心概念、演进历史、关键功能及实际应用场景。
LangChain的演进历程
LangChain由Harrison Chase于2022年10月创立,最初定位为一个专注于“利用大语言模型(LLM)构建应用程序”的开源框架。彼时,ChatGPT正引发全球性的AI热潮,开发者急需一种能够快速连接LLM与外部数据源、工具及API的解决方案。
阶段一:抢占先机
LangChain的诞生恰逢其时,其本质是将一系列LLM应用开发的最佳实践进行抽象化,形成了几大关键抽象层:
- Models(模型层):对不同厂商的大语言模型进行统一封装,提供标准化的推理与对话接口,构成系统的基础能力。
- Chains(链):将多个独立组件串联起来,形成可执行的工作流程。
- Tools(工具):为LLM提供调用外部能力(如API、数据库)的标准接口。
- Agents(代理):实现基于自主决策的工具调用机制,使LLM能够主动选择并执行工具。
- Memory(记忆):负责管理对话历史与中间状态,确保LLM在多轮交互中保持上下文连贯性。
该框架显著简化了LLM应用的开发流程,使开发者能快速搭建问答系统、文本摘要和对话机器人。在早期阶段,许多高级功能并不突出,开发者可以近似地将LangChain等同于一个增强的检索增强生成(RAG)框架。
然而,LangChain的早期架构采用单体式(Monolithic)设计,各组件间耦合紧密。虽然这有利于快速集成,但也带来了扩展性挑战。因此,许多团队在实际生产中更倾向于参考其设计思想,而非直接使用全部组件。

阶段二:快速迭代进化
如前所述,早期ChatGPT自身能力有限,算力成本较高,因此在生产环境中直接使用LangChain的场景并不多。但从2023年开始,模型能力以每半年一代的速度飞速进化,LangChain也随之持续迭代,不断补足自身短板,例如:
- 模块化设计趋于合理,组件间可以更灵活地组合。
- 支持OpenAI、Anthropic、Google在内的多家模型提供商。
- 生态系统日益丰富,社区贡献了大量第三方集成。
与此同时,所有AI开源框架在此阶段都面临共性问题:
- API变更频繁,破坏性更新较多。
- 代理(Agent)逻辑分散,难以维护复杂业务流程。
- 状态管理能力相对薄弱。
- 对需要长期记忆和状态持久化的应用支持不足(这不仅是框架问题,也与当时模型自身能力有限有关)。
值得一提的是,如今大热的智能体(Agent)概念在2023年尚不成熟。当时的AgentExecutor是实现智能体的核心,它通过一个**硬编码的循环(Hardcoded Loop)**来执行ReAct逻辑。这种“黑盒”设计使得开发者难以定制复杂的执行流程,例如融入人机交互或错误重试机制。

阶段三:应对复杂性——LangGraph登场
随着模型能力的持续增强,AI项目的复杂性也水涨船高。LangChain团队意识到,简单的链式结构已无法满足高级智能体开发的需求。因此,专注于工作流编排的LangGraph框架应运而生。
LangGraph的核心设计理念包括:
- 基于图结构:使用节点(Node)、边(Edge)、状态(State)三大抽象来定义流程。
- 支持复杂流程:原生支持循环、条件分支、并行执行等控制模式。
- 状态持久化:通过检查点(Checkpoint)机制实现执行状态的保存与恢复。
- 人工介入支持:内置“人在回路(Human-in-the-Loop)”机制,允许在关键节点进行人工干预。

这仍然是一个过渡阶段。直到2025年,模型能力达到新的高度,LangChain 1.0 与 LangGraph 1.0 才正式发布。
里程碑:1.0 正式版发布
2025年10月,LangChain 1.0和LangGraph 1.0同步发布,这标志着两个框架进入了首个稳定版本阶段。以“1.x”开头的版本号意味着:开发者可以放心地将其用于生产环境。
在后续版本中,版本稳定性将得到更多重视:与早期的快速迭代相比,1.x阶段更强调向后兼容性和清晰的迁移路径,从而降低维护成本。同时,两个框架的职责边界也更加清晰:
- LangChain:更侧重于应用层的组件封装与集成,强调易用性和快速组装。
- LangGraph:更侧重于底层的流程编排与状态管理,强调可控性、可恢复性和可扩展性。

那么,1.0版本与旧版本究竟有何区别?
LangChain 1.0 的核心特性
首先,整体架构发生了显著变化。在1.x生态中,一个明确的趋势是:LangChain更加聚焦于应用层能力与集成,而LangGraph则更多地作为流程编排与状态管理的底层引擎,被引入到复杂的智能体场景中。
实际项目中,两者的组合方式可能因版本、语言包和团队选型而异,但整体方向是使流程控制更加显式化、更易于维护。这一架构转变使LangChain从一个流程执行框架,演进为面向开发者的应用层SDK,并带来以下优势:
- 运行时职责下沉,各层边界更加清晰。
- 智能体执行模型趋于统一,减少了隐式行为。
- 提供了更强的可扩展性与
可观测性。 - 为复杂智能体场景提供了工程级别的稳定性保障。
其次,在应用层使用体验上,LangChain 1.0具备了更出色的易用性和生态整合能力:
- 提供了高层API抽象,例如
create_agent等统一的智能体构建接口。 - 内置了丰富的可复用组件,包括:
- 智能体构建器
- 预构建的链(Chains)
- 检索器(Retrievers)
- 工具(Tools)与工具调用(Tool Calling)抽象
- 中间件(Middleware)与回调(Callbacks)机制
最后是编排层,LangGraph充当了面向系统的统一智能体运行与编排引擎,是LangChain 1.0的核心基础设施,扮演着“总控制器”的角色:
- 作为所有复杂智能体与链的统一运行时。
- 基于**状态图(State Graph)**的显式流程编排模型。
- 提供关键的底层能力:
- 状态管理与持久化(检查点机制)
- 流式响应(Streaming)
- 人工介入(Human-in-the-loop)
- 错误恢复、重试与流程回溯
以下是新旧版本的核心对比:
| 对比维度 | 旧版(0.x) | LangChain 1.0 |
|---|---|---|
| 智能体构建方式 | 构建入口与写法分散,不同版本或示例间差异较大 | 更倾向于使用统一的构建入口与模板化写法(如create_agent) |
| API 稳定性 | 迭代速度快,升级成本和兼容性不确定 | 更强调兼容性与明确的迁移路径,升级更具可预测性 |
| 与 LangGraph 的关系 | 可按需选择性引入编排能力 | 更常见于复杂流程中,与图式编排/状态管理能力紧密结合 |
| 中间件支持 | 多依赖于回调或自定义封装 | 提供了更系统化的回调/中间件能力支持 |
| 结构化输出 | 更多依赖输出解析器(OutputParser)的组合 | 更强调结构化输出与工具调用的工程化用法(如with_structured_output) |
| 包结构设计 | 功能模块分散,命名空间相对复杂 | 命名空间经过精简,结构更清晰 |
| 文档与学习成本 | 文档相对分散,上手成本较高 | 拥有全新的文档体系,示例与最佳实践更为完善 |
梳理完框架的发展历史与关键升级后,接下来将解答开发者最关切的实际问题。
为何在AI项目中选用LangChain
许多程序员初次接触LangChain时都会产生疑问:既然直接用Python调用OpenAI API就能实现对话,为何还要引入LangChain这样一个看似复杂的框架?
通过场景对比可以更清晰地解答这个问题,即何种场景适合手写代码,何种场景适合使用框架。
简而言之,当您只需要实现相对简单的功能时,例如调用大模型进行文本翻译,或构建一个基础的聊天机器人,直接编写代码是更合适的选择,其优势明显:
- 完全自主可控:每一段代码的执行逻辑都清晰可见,没有额外的抽象层,整个过程透明直接。
- 环境轻量:无需引入庞大的第三方框架,项目依赖简单,更适合小型或一次性任务。
- 易于调试:可以直接查看和验证API返回的原始数据,问题定位直观高效。
然而,直接编码的缺点同样突出,尤其对于架构能力不足的团队:
- 重复开发工作多:当功能逐渐复杂(例如需要联网搜索、保存对话历史、处理多种文件格式),往往需要自行编写大量“粘合”代码,存在重复造轮子的情况。
- 维护成本较高:如果需要更换模型提供商,或应对模型API的升级变更,通常涉及较大范围的代码调整。
- 缺乏统一模式:在团队协作中,不同开发者对LLM的封装方式可能不一致,导致代码复用性和可维护性较差。
如果采用LangChain框架,在AI项目复杂度提升后,上述问题大多可以得到规避。该框架能有效提升一个技术基础薄弱团队的下限。例如,面对以下场景,使用框架是更明智的选择:
- 构建要求较高的、基于检索增强生成(RAG)的问答系统。
- 开发能够自主调用外部工具(如计算器、搜索引擎)的智能助手。
- 创建需要在多个大语言模型之间灵活切换和对比效果的应用。
使用框架的优势在于:
- 统一的模型接口:无论对接OpenAI、Anthropic还是Hugging Face等模型,LangChain都提供一致的调用方式,通常只需调整少量配置即可完成切换。
- 丰富的预制组件:内置文档加载、文本分割、向量存储、对话记忆等常见模块,可直接组合使用,显著提升开发效率。
- 完善的工具集成:能够方便地接入搜索引擎、知识库以及各类业务API,快速扩展应用能力。
- 成熟的高阶模式支持:针对智能体(Agent)、复杂链式调用等高级场景,提供了清晰且可复用的实现模式,降低了自行设计流程的复杂度。
以下表格为技术选型提供参考:
| 维度 | 纯代码实现 | LangChain 1.x 框架 |
|---|---|---|
| 项目复杂度 | 简单 :单轮对话、基础的文本处理任务 | 复杂 :多轮对话、多步骤推理、RAG、智能体 |
| 模型依赖 | 单一 :只使用单一提供商/单一模型,且不计划切换 | 多模型 :需要支持多家提供商或本地模型等多种后端 |
| 外部工具 | 无/少 :不需要或仅需调用极少数外部工具 | 多 :需要频繁调用搜索、数据库、API等外部能力 |
| 开发阶段 | 原型验证 :快速验证提示词(Prompt)效果 | 生产级应用 :需要可观测性、链路追踪、效果评估、状态持久化 |
| 团队协作 | 个人/小团队 :对代码规范要求不高 | 中大型团队 :需要统一的代码规范和抽象层以利于协作 |
总结而言:如果您的目标仅仅是体验大语言模型,或构建一个极简功能,直接调用API是最高效的路径;如果您要构建一个生产级的AI应用,尤其是涉及RAG或**智能体(Agent)**架构,LangChain能为您节省大量的工程化时间,使您能更专注于业务逻辑而非基础设施。
接下来,我们将深入介绍LangChain的几个核心概念与能力。
核心能力一:create_agent
create_agent是LangChain 1.0中用于创建智能体的核心API,它统一并简化了智能体的构建方式。
LLM智能体通过循环运行工具来实现目标,其运行会持续直到满足停止条件,例如模型输出最终结果或达到预设的迭代次数上限。

下面的代码展示了如何定义一个简单的工具函数,并将其绑定到智能体上进行调用(本例使用DeepSeek模型):
import os
from langchain.agents import create_agent
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
# 1. 定义工具
@tool
def get_weather(city: str) -> str:
"""获取指定城市的天气信息"""
return f"{city}今天天气晴朗,温度23°C"
# 2. 配置模型 (使用 DeepSeek)
llm = ChatOpenAI(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com",
temperature=0
)
# 3. 创建智能体
agent = create_agent(
model=llm,
tools=[get_weather],
system_prompt="你是一个专业的天气助手"
)
# 4. 执行查询
result = agent.invoke({
"messages": [{"role": "user", "content": "北京今天天气怎么样?"}]
})
# 输出结果
print(result["messages"][-1].content)
# 输出:北京今天天气晴朗,温度23°C
再分享一个真实场景案例:客户服务智能体。在此场景中,我们定义了两个工具:查询订单状态和处理退货申请。智能体会根据用户的自然语言指令,自动判断并调用相应的工具。
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_openai import ChatOpenAI
import os
@tool
def check_order_status(order_id: str) -> str:
"""查询订单状态"""
# 实际应用中这里会调用数据库或API
return f"订单{order_id}已发货,预计明天送达"
@tool
def process_return(order_id: str, reason: str) -> str:
"""处理退货申请"""
return f"订单{order_id}的退货申请已受理,原因是:{reason}"
# 配置 DeepSeek 模型
llm = ChatOpenAI(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com",
temperature=0
)
service_agent = create_agent(
model=llm,
tools=[check_order_status, process_return],
system_prompt="""你是一个专业的客户服务助手。
你可以帮助客户查询订单状态和处理退货申请。
始终保持礼貌和专业的态度。"""
)
# 客户咨询
response = service_agent.invoke({
"messages": [{
"role": "user",
"content": "我的订单ORD12345发了吗?"
}]
})
print(response["messages"][-1].content)
核心能力二:Callbacks 与 Middleware
在 LangChain 1.0 中,Callbacks (回调) 和 Middleware (中间件) 是实现对智能体执行流程进行控制与观测的主要机制。Callbacks 更侧重于被动的观测和简单的钩子函数,而 Middleware 则提供了更强大的生命周期管理和请求拦截能力。
Callbacks 允许开发者在智能体执行的关键节点(如LLM开始调用、结束调用、工具调用发生时)插入自定义逻辑,用于记录日志、监控性能或修改行为。
自定义 LoggingHandler 示例
以下示例展示了如何实现一个简单的日志回调处理器,并将其注入到使用 DeepSeek 的 LLM 实例中:
from typing import Dict, Any, List
import os
from langchain_core.callbacks import BaseCallbackHandler
from langchain_core.outputs import LLMResult
from langchain_openai import ChatOpenAI
class LoggingHandler(BaseCallbackHandler):
"""
一个简单的中间件/回调,用于记录 LLM 的交互过程
"""
def on_llm_start(
self, serialized: Dict[str, Any], prompts: List[str], **kwargs: Any
) -> None:
"""LLM 开始处理请求时的回调"""
print("\n[Middleware] LLM 开始处理请求...")
def on_llm_end(self, response: LLMResult, **kwargs: Any) -> None:
"""LLM 处理完成时的回调"""
print(f"\n[Middleware] LLM 处理完成。消耗 Token: {response.llm_output.get('token_usage', 'N/A')}")
# 使用 Callbacks 配置 DeepSeek
llm = ChatOpenAI(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com",
callbacks=[LoggingHandler()] # 注入中间件
)
# 当 Agent 使用这个 LLM 时,所有操作都会被 LoggingHandler 捕获
Middleware 提供了更强大的生命周期钩子,允许我们在模型调用前后、工具执行前后进行深度干预。

中间件生命周期钩子
| 钩子函数 | 触发时机 | 典型应用场景 |
|---|---|---|
before_agent |
调用代理之前 | 加载用户历史记录、验证输入参数合法性 |
before_model |
每次模型调用前 | 动态修改提示词、精简过长的对话历史 |
wrap_model_call |
围绕模型调用(前后) | 记录详细日志、修改请求/响应内容、实现结果缓存 |
wrap_tool_call |
围绕工具调用(前后) | 进行权限检查、验证输入参数、对工具结果进行缓存 |
after_model |
模型返回响应后 | 内容安全检查、敏感信息过滤或脱敏 |
after_agent |
代理完成运行后 | 保存完整的对话历史、记录使用统计信息 |
1. PIIMiddleware:敏感信息保护
PII(个人身份信息)中间件可以在数据发送给大模型之前,自动识别并处理其中的敏感信息。以下配置展示了如何自动屏蔽邮箱地址,并完全阻断包含手机号的请求:
from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware
agent = create_agent(
model="gpt-4o-mini",
tools=[email_tool],
middleware=[
# 自动屏蔽邮箱地址
PIIMiddleware("email", strategy="redact", apply_to_input=True),
# 完全阻止电话号码传递给模型
PIIMiddleware(
"phone_number",
detector=r"(?:\+?\d{1,3}[\s.-]?)?\(?\d{2,4}\)?[\s.-]?\d{3,4}[\s.-]?\d{4}",
strategy="block"
),
# 屏蔽身份证号
PIIMiddleware(
"id_card",
detector=r"\d{17}[\dXx]",
strategy="redact"
)
]
)
# 即使用户输入包含敏感信息,也会被自动处理
result = agent.invoke({
"messages": [{"role": "user", "content": "我的邮箱是test@example.com,电话13812345678"}]
})
# 实际传递给模型的内容:我的邮箱是***,电话***
2. SummarizationMiddleware:对话历史管理
当对话轮数过多导致上下文长度超出模型限制时,摘要中间件会自动触发压缩历史记录。以下代码设置了 3000 token 的阈值,一旦对话历史超过该值就会自动生成摘要:
from langchain.agents.middleware import SummarizationMiddleware
agent = create_agent(
model="gpt-4o-mini",
tools=[search_tool],
middleware=[
SummarizationMiddleware(
model="gpt-4o-mini",
max_tokens_before_summary=3000, # 超过3000 tokens自动摘要
summary_prompt="将以下对话内容摘要为关键要点" # 自定义摘要提示词
)
]
)
# 长对话自动管理
for i in range(20):
result = agent.invoke({
"messages": [{"role": "user", "content": f"问题{i+1}"}]
})
# 当对话历史过长时,会自动进行摘要压缩
3. HumanInTheLoopMiddleware:人工审批机制
from langchain.agents.middleware import HumanInTheLoopMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import Command
agent = create_agent(
model="gpt-4o",
tools=[delete_file, send_email, transfer_money],
checkpointer=InMemorySaver(), # 需要记忆管理支持
middleware=[
HumanInTheLoopMiddleware(
interrupt_on={
"delete_file": {
"allowed_decisions": ["approve", "edit", "reject"],
"require_reason": True # 拒绝时必须提供原因
},
"send_email": {
"allowed_decisions": ["approve", "reject"]
},
"transfer_money": {
"allowed_decisions": ["approve", "reject"],
"require_reason": True
}
}
)
]
)
config = {"configurable": {"thread_id": "session_1"}}
result = agent.invoke(
{"messages": [{"role": "user", "content": "删除重要文件.doc"}]},
config
)
if "__interrupt__" in result:
# 程序暂停,等待人工决策
decision = input("批准删除文件?(approve/reject): ")
if decision == "approve":
# 继续执行
result = agent.invoke(
Command(resume={"decisions": [{"type": "approve"}]}),
config
)
else:
# 拒绝执行
result = agent.invoke(
Command(resume={
"decisions": [{
"type": "reject",
"message": "用户取消操作"
}]
}),
config
)
最后展示一个自定义中间件的案例:基于用户级别的动态路由
我们通过自定义中间件获取用户上下文(如会员等级),并据此动态调整智能体使用的模型版本。以下实现展示了如何拦截请求并修改模型参数,从而为高级用户提供更强大的模型服务:
from dataclasses import dataclass
from typing import Callable
from langchain.chat_models import init_chat_model
from langchain.agents.middleware import AgentMiddleware
from langchain.agents.middleware.types import ModelRequest, ModelResponse
@dataclass
class Context:
user_id: str
user_tier: str = "free" # free, pro, enterprise
request_count: int = 0
class TierBasedRoutingMiddleware(AgentMiddleware):
"""根据用户等级路由到不同的模型"""
def __init__(self):
super().__init__()
# 定义不同等级使用的模型
self.tier_models = {
"free": "gpt-4o-mini",
"pro": "gpt-4o",
"enterprise": "gpt-4o-turbo"
}
def wrap_model_call(
self,
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse]
) -> ModelResponse:
# 获取用户等级
user_tier = request.runtime.context.user_tier
# 动态选择模型
model_name = self.tier_models.get(user_tier, "gpt-4o-mini")
request.model = init_chat_model(model=model_name)
# 记录请求次数
request.runtime.context.request_count += 1
print(f"[中间件] 用户等级: {user_tier}, 使用模型: {model_name}")
return handler(request)
# 应用中间件
agent = create_agent(
model="gpt-4o-mini", # 默认模型,会被中间件覆盖
tools=[search_tool],
middleware=[TierBasedRoutingMiddleware()],
context_schema=Context
)
# 不同用户使用不同模型
result_free = agent.invoke(
{"messages": [{"role": "user", "content": "搜索新闻"}]},
context=Context(user_id="user_001", user_tier="free")
)
result_enterprise = agent.invoke(
{"messages": [{"role": "user", "content": "搜索新闻"}]},
context=Context(user_id="user_002", user_tier="enterprise")
)
核心能力三:获取规范的结构化数据
在实际应用中,我们往往需要AI返回特定格式的数据(如 JSON),而非纯粹的自然语言文本。常见场景包括:
- 电商价格比较:需要返回结构化的价格、型号、评分列表。
- 数据提取:从非结构化文档中提取特定字段并以JSON格式返回。
- API调用:需要严格按照预定义的参数格式生成请求。
LangChain 提供了三种主要策略来获取结构化数据,以适应不同的模型能力和应用场景。
1. AutoStrategy(推荐):自动选择最佳策略
自动策略是获取结构化数据最简洁的方式。开发者只需定义一个 Pydantic 模型,LangChain 会自动处理底层的提示词构造和结果解析逻辑。
from langchain.agents import create_agent
from langchain.agents.structured_output import AutoStrategy
from pydantic import BaseModel
class ProductComparison(BaseModel):
"""产品比较结果"""
product_name: str
price: float
rating: float
pros: list[str]
cons: list[str]
verdict: str # 购买建议
agent = create_agent(
model="gpt-4o",
tools=[web_search],
response_format=AutoStrategy(ProductComparison),
system_prompt="你是一个产品比较专家"
)
result = agent.invoke({
"messages": [{
"role": "user",
"content": "比较iPhone 15和Samsung S24的优缺点"
}]
})
# 直接获得结构化对象
comparison: ProductComparison = result["structured_response"]
print(f"产品: {comparison.product_name}")
print(f"价格: ${comparison.price}")
print(f"评分: {comparison.rating}/5")
print(f"优点: {', '.join(comparison.pros)}")
print(f"缺点: {', '.join(comparison.cons)}")
print(f"建议: {comparison.verdict}")
2. ToolStrategy:利用工具调用能力
工具策略通过模拟工具调用的方式来获取结构化输出,适用于支持 Function Calling 的模型。它利用了模型对工具参数格式严格遵循的特性。
from langchain.agents.structured_output import ToolStrategy
class WeatherForecast(BaseModel):
city: str
temperature: int
condition: str # sunny, cloudy, rainy
humidity: int
wind_speed: int
agent = create_agent(
model="gpt-4o-mini",
tools=[get_weather_data],
response_format=ToolStrategy(WeatherForecast)
)
# 适用于任何支持工具调用的模型
# 但依赖模型自身的结构化推理能力
3. ProviderStrategy:使用提供商原生功能
对于 OpenAI 等提供商的原生结构化输出 API(如 JSON mode),可以使用 ProviderStrategy 直接调用。这种方式通常比通过提示词工程实现更稳定可靠。
from langchain.agents.structured_output import ProviderStrategy
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-4o")
agent = create_agent(
model=model,
tools=[],
response_format=ProviderStrategy(WeatherForecast)
)
# 直接使用OpenAI的结构化输出API
# 更稳定可靠,但仅限于支持此功能的提供商
核心能力四:记忆管理
如果自行实现AI多轮对话的记忆功能,对系统架构复杂度的要求很高。LangChain 1.0 通过几个核心概念,为一般开发团队提供了可靠的下限保障:
- 短期记忆(对话历史):
- 使用
InMemorySaver将状态存储在内存中。 - 适合单次会话场景。
- 程序重启后状态会丢失。
- 使用
- 长期记忆(跨会话):
- 使用数据库(如PostgreSQL、Redis)进行持久化存储。
- 可以跨多次用户会话保持状态。
- 需要配置额外的存储后端。
使用 InMemorySaver 可以实现短期记忆。通过唯一的 thread_id 来区分不同的对话会话。
import os
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com",
temperature=0
)
# 创建带记忆的智能体
agent = create_agent(
model=llm,
tools=[],
checkpointer=InMemorySaver() # 启用内存检查点
)
# 使用thread_id区分不同会话
config = {"configurable": {"thread_id": "user_session_123"}}
# 第一轮对话
agent.invoke(
{"messages": [{"role": "user", "content": "我叫张三,今年25岁"}]},
config
)
# 第二轮对话(智能体记得之前的信息)
result = agent.invoke(
{"messages": [{"role": "user", "content": "我今年多大?"}]},
config
)
print(result["messages"][-1].content)
# 输出:你今年25岁
对于生产环境,需要将状态持久化到数据库。以下示例展示了如何使用 PostgreSQL 保存对话状态,即使应用程序重启,用户也能无缝接续之前的对话上下文。
from langgraph.checkpoint.postgres import PostgresSaver
# 配置PostgreSQL检查点
checkpointer = PostgresSaver.from_conn_string(
"postgresql://user:password@localhost:5432/langchain"
)
agent = create_agent(
model="gpt-4o-mini",
tools=[search_tool],
checkpointer=checkpointer
)
# 即使程序重启,对话历史仍然保留
config = {"configurable": {"thread_id": "user_persistent_001"}}
# 第一次运行(程序A)
result1 = agent.invoke(
{"messages": [{"role": "user", "content": "我喜欢编程"}]},
config
)
# 第二次运行(程序B,重启后)
result2 = agent.invoke(
{"messages": [{"role": "user", "content": "我有什么爱好?"}]},
config
)
# 输出:你之前提到喜欢编程
核心能力五:LangGraph 深度解析
LangGraph 是一个低级别的编排框架,专门用于构建、管理和部署长期运行、有状态的智能体。与 LangChain 提供的高级抽象不同,LangGraph 更关注底层的流程编排与控制能力:
- 持久化执行(Durable execution):智能体可以在执行失败后恢复,支持长时间运行的任务,并能从上次停止的地方继续执行。
- 人工干预(Human-in-the-loop):允许在任何执行节点暂停,以便人工检查或修改智能体状态。
- 全面的记忆系统:同时支持短期工作记忆与长期跨会话记忆。
- 通过 LangSmith 调试:提供可视化的执行路径,能够捕获并展示状态转换过程。
- 生产就绪的部署:为有状态、长期运行的工作流设计了可扩展的基础设施。
在深入代码之前,需要理解 LangGraph 的五个核心支柱:
- 状态(State):在所有节点之间共享的内存,存储执行过程中的所有数据。
- 节点(Nodes):执行单元(Python 函数),接收状态作为输入,并返回状态的更新。
- 边(Edges):控制流逻辑,决定执行流程下一步应该前往哪个节点。
- 图(Graph):编排器,将节点和边组合成一个可运行的工作流。
- 检查点(Checkpointer):持久化层,负责保存和管理整个执行历史。

状态定义与共享内存
状态是图的共享内存。
- 它保存了智能体运行过程中的所有数据(例如消息历史、提取的变量、中间结果)。
- 它在节点之间传递。每个节点接收当前状态,并返回状态的更新。
- 模式定义:通常使用
TypedDict或 Pydantic 模型来定义状态的结构。 - 更新机制:节点返回的字典会与现有状态合并(默认是覆盖式合并),你也可以定义自定义的归约器函数(例如对列表进行追加操作而非覆盖)。
from typing import Annotated, TypedDict
from langgraph.graph.message import add_messages
class AgentState(TypedDict):
"""智能体状态定义"""
# 消息历史(使用add_messages注解实现自动追加而非覆盖)
messages: Annotated[list, add_messages]
# 自定义字段
user_input: str
search_results: list[dict]
analysis_result: str
current_step: str # 追踪当前执行步骤
深入理解 Annotated 与 add_messages:
在上面的代码中,messages: Annotated[list, add_messages] 是一个非常关键的设计。
- 默认情况下,LangGraph 的状态更新是覆盖式的。如果节点返回
{"messages": [new_message]},它会完全覆盖掉状态中原有的messages列表。 - 对于消息列表,我们通常需要的是追加而非覆盖。
Annotated配合add_messages告诉 LangGraph:“当有新消息需要更新到这个字段时,请调用add_messages函数将其追加到现有列表中,而不是替换整个列表。”
节点作为执行单元
节点是图的执行单元。
- 本质上,节点就是一个 Python 函数。
- 输入:接收当前状态(State)。
- 输出:返回一个字典,包含要更新的状态键值对。
- 节点负责具体的业务逻辑,如调用 LLM、查询数据库、执行代码等。
- 特殊节点:
START(图的入口点)和END(图的出口点)。
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o")
def search_node(state: AgentState, config):
"""搜索节点:执行网络搜索"""
query = state["user_input"]
# 调用搜索工具
results = search_web(query)
print(f"[搜索节点] 找到 {len(results)} 条结果")
return {
"search_results": results,
"current_step": "search_completed"
}
def analysis_node(state: AgentState, config):
"""分析节点:分析搜索结果"""
results = state["search_results"]
# 构造分析提示词
prompt = f"分析以下搜索结果:\n{results}"
# 调用LLM分析
response = llm.invoke([
{"role": "system", "content": "你是一个信息分析专家"},
{"role": "user", "content": prompt}
])
print(f"[分析节点] 分析完成")
return {
"analysis_result": response.content,
"current_step": "analysis_completed"
}
def report_node(state: AgentState, config):
"""报告节点:生成最终报告"""
analysis = state["analysis_result"]
report = f"""
分析报告
=========
{analysis}
"""
print(f"[报告节点] 报告生成完毕")
return {
"messages": [{"role": "assistant", "content": report}],
"current_step": "report_generated"
}
边定义控制流逻辑
边定义了控制流,即决定“执行完当前节点后,下一步去哪里”:
- 普通边 (Normal Edge): 例如
START -> Node A -> Node B。这种边是确定的,A 执行完总是去 B。 - 条件边 (Conditional Edge): 根据当前状态或函数输出来动态决定下一个节点。例如,如果 LLM 决定调用工具,则跳转到
ToolNode,否则跳转到END。 - 在 LangGraph 新版中,推荐在节点函数内部通过返回
Command(goto="next_node")来直接控制流向,这比外部定义的条件边更直观。
from langgraph.graph import StateGraph, END
def should_search(state: AgentState) -> str:
"""条件边:决定是否需要搜索"""
user_input = state["user_input"]
# 如果用户问的是事实性问题,需要搜索
question_words = ["什么", "哪里", "谁", "何时", "如何"]
if any(word in user_input for word in question_words):
return "search"
# 否则直接分析
return "analyze"
def has_results(state: AgentState) -> str:
"""条件边:检查搜索是否有结果"""
if len(state["search_results"]) > 0:
return "analyze"
else:
return "no_results"
图作为工作流编排器
图是工作流的编排器。
- 它定义了应用程序的整体结构和执行逻辑。
StateGraph是最常用的类,它利用定义好的 State Schema 来管理数据在各节点间的流转。- 编译后的图(
CompiledGraph)是一个可运行的对象,支持invoke,stream,batch等标准 LangChain 调用方法。
from langgraph.graph import StateGraph, END
# 创建状态图
builder = StateGraph(AgentState)
# 添加节点
builder.add_node("search", search_node)
builder.add_node("analyze", analysis_node)
builder.add_node("report", report_node)
builder.add_node("no_results", lambda state: {
"messages": [{"role": "assistant", "content": "未找到相关信息"}]
})
# 设置入口点
builder.set_entry_point("search")
# 添加边
# 从search节点出发,根据是否有结果决定下一步
builder.add_conditional_edges(
"search",
has_results,
{
"analyze": "analyze",
"no_results": "no_results"
}
)
# analyze完成后总是到report
builder.add_edge("analyze", "report")
# report和no_results都结束
builder.add_edge("report", END)
builder.add_edge("no_results", END)
# 编译图
graph = builder.compile()
# 执行
result = graph.invoke({
"user_input": "什么是LangChain?",
"messages": [],
"search_results": [],
"analysis_result": "",
"current_step": ""
})
print(result["messages"][-1]["content"])
检查点机制赋予记忆与持久化能力
检查点机制是 LangGraph 的核心特性之一,它赋予了智能体记忆和持久化能力。
- 自动保存:在图执行的每个超级步骤(Super-step)后,它会自动保存图的状态快照。
- 线程 (Thread): 通过
thread_id来隔离不同的对话或执行流。 - 多级隔离:除了
thread_id,Checkpointer 还支持更细粒度的隔离(如用户级别)。在配置configurable时,可以传递自定义的键值对(例如user_id),Checkpointer 能够利用这些信息来管理和检索状态,从而实现多用户、多会话的状态管理。 - 核心能力解锁:
- 记忆 (Memory): 允许智能体跨多轮交互记住上下文。
- 人工干预 (Human-in-the-loop): 允许在特定节点暂停,等待人工批准或修改状态后再继续。
- 时间旅行 (Time Travel): 允许查看历史执行步骤,甚至回滚到之前的某个状态,修改数据后重新执行(分支)。
- 故障恢复:如果任务因故中断,可以从上次保存的状态恢复执行。
高级特性与应用模式
实现复杂的条件分支逻辑
通过编写自定义的路由函数,可以实现基于业务逻辑的复杂分流。例如,根据用户问题的关键词,将请求分发给不同的专业处理节点(如客服接待、技术支持、销售咨询)。
from typing import Literal
def route_question(state: AgentState) -> Literal["web_search", "database", "direct_answer"]:
"""根据问题类型路由到不同处理节点"""
question = state["user_input"].lower()
# 技术问题 -> 网络搜索
if any(word in question for word in ["如何", "怎么", "教程", "技术"]):
return "web_search"
# 数据查询问题 -> 数据库查询
if any(word in question for word in ["查询", "数据", "记录", "统计"]):
return "database"
# 简单问题 -> 直接回答
return "direct_answer"
builder.add_conditional_edges(
"router",
route_question,
{
"web_search": "web_search_node",
"database": "database_query_node",
"direct_answer": "direct_answer_node"
}
)
原生支持循环执行
LangGraph 原生支持循环逻辑,非常适合需要反复优化、迭代或重试的场景。以下代码展示了一个最大迭代次数为 10 的循环流程,并设置了收敛条件以防止无限循环。
class IterationState(TypedDict):
messages: Annotated[list, add_messages]
iteration: int
max_iterations: int
result: str
converged: bool
def process_step(state: IterationState):
"""处理步骤"""
iteration = state["iteration"]
print(f"[迭代] 第 {iteration} 次处理")
# 模拟处理逻辑
result = f"处理结果 {iteration}"
# 检查是否收敛(假设5次后收敛)
converged = iteration >= 5
return {
"result": result,
"converged": converged,
"iteration": iteration + 1
}
def should_continue(state: IterationState) -> Literal["continue", "end"]:
"""决定是否继续迭代"""
if state["converged"]:
return "end"
if state["iteration"] >= state["max_iterations"]:
return "end"
return "continue"
builder = StateGraph(IterationState)
builder.add_node("process", process_step)
builder.set_entry_point("process")
# 添加循环边
builder.add_conditional_edges(
"process",
should_continue,
{
"continue": "process", # 循环回process节点
"end": END
}
)
graph = builder.compile()
# 执行(会循环5次直到收敛)
result = graph.invoke({
"messages": [],
"iteration": 1,
"max_iterations": 10,
"result": "",
"converged": False
})
状态持久化与检查点应用
SQLite 是一种轻量级的持久化方案,非常适合本地开发和测试。无需部署额外的数据库服务,只需一行代码即可启用状态保存功能。
from langgraph.checkpoint.sqlite import SqliteSaver
# 创建SQLite检查点
checkpointer = SqliteSaver.from_conn_string("agent_state.db")
builder = StateGraph(AgentState)
# ... 添加节点和边 ...
graph = builder.compile(
checkpointer=checkpointer # 启用检查点
)
# 使用thread_id追踪会话
config = {"configurable": {"thread_id": "user_session_001"}}
# 第一次执行
result1 = graph.invoke({"user_input": "搜索AI新闻"}, config)
# 第二次执行(从上次停止的状态继续)
result2 = graph.invoke({"user_input": "继续搜索"}, config)
在生产环境中,推荐使用 PostgreSQL 等成熟的数据库系统。需要先建立数据库连接,再将其传递给图的编译方法,以确保高可用性和数据安全性。
from langgraph.checkpoint.postgres import PostgresSaver
import psycopg
# 配置PostgreSQL连接
connection = psycopg.connect(
host="localhost",
port=5432,
database="langchain",
user="postgres",
password="password"
)
# 创建检查点
checkpointer = PostgresSaver(connection)
# 初始化表(首次运行)
checkpointer.setup()
graph = builder.compile(checkpointer=checkpointer)
LangGraph 的一大特色是支持“时间旅行”调试。我们可以查看历史状态,甚至回滚到之前的某个步骤重新执行,这对于问题诊断和人工纠错非常有用。
# 获取指定会话的所有历史状态
history = graph.get_state_history(config)
# 查看历史状态记录
for state in history:
print(f"步骤: {state.step}, 时间: {state.timestamp}")
# 回溯到特定的历史状态
past_state = history[5] # 回到第5步的状态
restored_result = graph.invoke(None, config)
# 从某个历史状态开始重新执行(分支)
graph.update_state(config, past_state.values)
new_result = graph.invoke({"user_input": "基于历史状态的新指令"}, config)
集成人工介入流程
在执行过程中暂停并等待人工审核是智能体的常见需求。以下代码展示了如何在 human_review 节点主动暂停流程,并在接收到人工决策指令后恢复执行。
from langgraph.types import interrupt, Command
def human_review_node(state: AgentState):
"""需要人工审核的节点"""
analysis = state["analysis_result"]
# 暂停执行,等待人工输入
feedback = interrupt({
"type": "human_review",
"content": analysis,
"question": "请审核以上分析结果"
})
# 处理人工反馈
if feedback["approved"]:
return {"messages": [{"role": "assistant", "content": analysis}]}
else:
# 根据反馈意见修改分析结果
revised = revise_analysis(analysis, feedback["comments"])
return {"messages": [{"role": "assistant", "content": revised}]}
builder.add_node("human_review", human_review_node)
配套的人工干预执行流程:
# 第一次执行,会在human_review节点暂停
result = graph.invoke({"user_input": "分析市场趋势"}, config)
if "__interrupt__" in result:
# 获取需要人工审核的内容
review_data = result["__interrupt__"]
print("需要审核的内容:")
print(review_data["content"])
# 人工做出决策
approved = input("是否批准?(y/n): ") == "y"
comments = "" if approved else input("请提供修改意见: ")
# 携带人工决策结果继续执行
result = graph.invoke(
Command(resume={
"approved": approved,
"comments": comments
}),
config
)
构建多智能体协同系统
复杂的多智能体系统通常包含多个具有专门角色的智能体。我们需要定义一个包含 current_agent 字段的共享状态,并编写路由逻辑在不同智能体之间流转任务与状态。
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, START, END
from langgraph.graph import add_messages, StateGraph
class MultiAgentState(TypedDict):
messages: Annotated[list, add_messages]
current_agent: str
research_data: dict
code: str
test_results: str
def researcher_agent(state: MultiAgentState):
"""研究Agent:负责收集信息"""
print("[研究Agent] 开始研究...")
research_data = {
"topic": state["messages"][-1].content,
"sources": ["source1", "source2", "source3"],
"summary": "研究摘要..."
}
return {
"research_data": research_data,
"current_agent": "researcher"
}
def coder_agent(state: MultiAgentState):
"""编程Agent:负责编写代码"""
print("[编程Agent] 开始编码...")
research = state["research_data"]
code = f"# 基于 {research['topic']} 生成的代码\nprint('Hello World')"
return {
"code": code,
"current_agent": "coder"
}
def tester_agent(state: MultiAgentState):
"""测试Agent:负责测试代码"""
print("[测试Agent] 开始测试...")
code = state["code"]
test_results = "测试通过!所有测试用例均通过。"
return {
"test_results": test_results,
"current_agent": "tester"
}
def route_to_next_agent(state: MultiAgentState) -> str:
"""根据当前Agent路由到下一个Agent"""
current = state["current_agent"]
if current == "researcher":
return "coder"
elif current == "coder":
return "tester"
else:
return "end"
builder = StateGraph(MultiAgentState)
builder.add_node("researcher", researcher_agent)
builder.add_node("coder", coder_agent)
builder.add_node("tester", tester_agent)
builder.set_entry_point("researcher")
# 添加路由边
builder.add_conditional_edges(
"researcher",
route_to_next_agent,
{"coder": "coder"}
)
builder.add_conditional_edges(
"coder",
route_to_next_agent,
{"tester": "tester"}
)
builder.add_conditional_edges(
"tester",
route_to_next_agent,
{"end": END}
)
graph = builder.compile()
# 执行多Agent协同任务
result = graph.invoke({
"messages": [{"role": "user", "content": "开发一个待办事项应用"}],
"current_agent": "researcher",
"research_data": {},
"code": "",
"test_results": ""
})
print("\n最终结果:")
print(f"研究数据: {result['research_data']}")
print(f"代码: {result['code']}")
print(f"测试结果: {result['test_results']}")
使用子图实现模块化
随着智能体系统变得庞大,单张图会变得难以维护。LangGraph 允许我们将一个已经编译好的图(CompiledGraph)直接作为另一个图的节点来使用。这种子图机制使得我们可以像搭积木一样,模块化地构建复杂的系统。
# 定义子图
sub_builder = StateGraph(SubState)
# ... 构建子图逻辑 ...
sub_graph = sub_builder.compile()
# 在父图中将子图作为一个节点使用
parent_builder = StateGraph(ParentState)
parent_builder.add_node("sub_task", sub_graph) # 子图直接作为父图的一个节点
总结
本文对LangChain与LangGraph框架进行了系统性介绍,涵盖了其发展脉络、核心架构、关键特性及实际应用模式。作为当前AI应用开发,特别是智能体(Agent)构建领域的主流框架,它们通过提供高层次的抽象和低级别的编排能力,显著降低了复杂AI系统的开发门槛与工程复杂度。对于致力于构建生产级、可维护、可扩展AI应用的团队而言,深入理解并合理运用这两个框架,无疑是提升开发效率与系统稳健性的关键一步。