简介
LangGraph 是一个低级别的编排框架,用于构建、管理和部署长时间运行、有状态的智能体,受到 Klarna、Replit、Elastic 等塑造智能体未来的公司的信任。
LangGraph VS. LangChain
LangGraph 和 LangChain 均由 LangChain 团队开发,核心目标都是降低大模型应用开发门槛,但定位、核心能力和适用场景差异显著——LangChain 是「大模型应用开发工具箱」,侧重组件化集成;LangGraph 是「复杂工作流编排框架」,侧重有状态、循环式的多步骤流程管理。以下从核心维度对比,并总结选型原则:
一、核心定位与设计理念
| 维度 | LangChain | LangGraph |
|---|---|---|
| 核心定位 | 大模型应用开发的「组件库/工具链」,提供开箱即用的模块(LLM、工具、检索、记忆等) | 大模型工作流的「编排引擎」,专注于以「有向图」构建有状态、可循环的复杂流程 |
| 设计理念 | 「组件化」:拆分大模型应用的通用模块,支持快速拼接线性/简单分支流程 | 「图驱动」:以节点/边/状态为核心,支持动态分支、循环、中断恢复等复杂逻辑 |
| 核心解决问题 | 快速集成 LLM、外部工具(搜索/数据库)、记忆、检索等能力,降低「组件集成」成本 | 解决线性流程无法覆盖的复杂场景(如多智能体协作、重试逻辑、人机交互、状态回溯) |
二、核心能力对比
1. 流程管理
- LangChain:
仅支持线性/简单分支的工作流(如SequentialChain、RouterChain),无原生循环、状态持久化能力;
流程一旦启动,无法动态回溯或中断恢复,适合“一次性”简单任务(如单轮问答、简单工具调用)。 - LangGraph:
基于有向图(支持循环/闭环) 设计,可定义条件边、循环逻辑(如重试3次、多轮审核);
内置检查点(Checkpoint) 机制,支持状态持久化、中断恢复、“时间旅行”(回溯历史状态),适合长周期、动态调整的任务。
2. 状态管理
- LangChain:
状态(如对话历史)依赖「Memory」组件,仅能在链间简单传递,无统一的状态更新规则;
状态分散在不同组件中,难以全局管控(如多步骤修改同一数据易冲突)。 - LangGraph:
核心是全局共享的状态对象,所有节点(执行单元)可读写状态,且通过 Reducer 定义状态更新规则(如覆盖、合并、自定义);
状态是流程的核心载体,支持序列化、持久化,适配多节点协作时的数据一致性需求。
3. 组件生态
- LangChain:
生态极丰富,内置数百种组件:- LLM 集成(OpenAI/Anthropic/国产大模型);
- 工具调用(Google 搜索、SQL 数据库、API 调用);
- 检索增强(RAG)全流程(文档加载、分割、向量化、检索);
- 记忆组件(对话历史、长期记忆)。
- LangGraph:
无独立组件生态,完全兼容 LangChain 生态,可直接复用 LangChain 的 LLM、工具、检索等组件;
专注于“流程编排”,不重复造组件,只解决 LangChain 流程能力的不足。
安装
Requires: Python >=3.10
pip install -U langgraph langsmith
开始案例
from langgraph.graph import START, StateGraph
from typing_extensions import TypedDict
class State(TypedDict):
text: str
def node_a(state: State) -> dict:
return {"text": state["text"] + "a"}
def node_b(state: State) -> dict:
return {"text": state["text"] + "b"}
graph = StateGraph(State)
graph.add_node("node_a", node_a)
graph.add_node("node_b", node_b)
graph.add_edge(START, "node_a")
graph.add_edge("node_a", "node_b")
print(graph.compile().invoke({"text": ""}))
# {'text': 'ab'}
基础概念
LangGraph的核心是以有向图为载体编排有状态的工作流,其所有组件和概念都围绕“图的执行、状态的流转”展开。以下是LangGraph中最核心的基础概念,按“核心基石→执行单元→流程控制→进阶能力”的逻辑拆解。
一、核心基石:状态(State)
1. 定义
状态是贯穿整个LangGraph工作流的全局共享数据容器,所有节点(执行单元)均可读取、修改状态,是节点间通信的唯一载体。
2. 核心特性
- 结构化:封装流程中所有关键数据(如用户问题、工具结果、重试次数)的结构化类型(如dict);
- 可更新:可更新规则(如覆盖、合并、自定义逻辑);
- 可持久化:可通过Checkpoint保存状态快照,支持中断恢复、历史回溯。
3. 示例
# 字典形式(基础用法)
state = {
"question": "什么是LangGraph?",
"answer": None,
"retry_count": 0
}
# Pydantic模型(推荐,强类型校验)
from pydantic import BaseModel
class GraphState(BaseModel):
question: str
answer: str | None = None
retry_count: int = 0
二、图的核心组成:节点(Node)
1. 定义
节点是LangGraph中最小的执行单元,对应工作流中的一个“步骤”(如调用LLM、检索文档、人工审核),本质是一个函数/方法。
2. 核心特性
- 单一职责:一个节点只做一件事(如“判断是否需要检索”“生成回答”);
- 输入输出:输入是全局状态,输出是修改后的状态(或直接返回状态,LangGraph自动合并);
- 无状态性:节点本身不存储数据,所有数据依赖输入的状态。
3. 示例(节点函数)
def generate_answer(state: GraphState) -> GraphState:
# 读取状态中的问题
question = state.question
# 执行核心逻辑(调用LLM)
answer = llm.invoke(f"回答:{question}").content
# 修改状态并返回
state.answer = answer
return state
三、图的流转规则:边(Edge)
1. 定义
边是节点间的有向连接,定义了“一个节点执行完成后,下一个执行哪个节点”,是流程控制的核心。
2. 分类
| 类型 | 定义 | 适用场景 |
|---|---|---|
| 无条件边 | 节点执行后固定跳转到下一个节点 | 线性流程(如“检索→生成”) |
| 条件边 | 根据当前状态动态选择下一个节点(由“分支函数”决定) | 分支/循环流程(如“审核通过→结束,不通过→重试”) |
3. 示例
from langgraph.graph import StateGraph, END
# 初始化图
graph = StateGraph(GraphState)
graph.add_node("generate_answer", generate_answer)
graph.add_node("review_answer", review_answer)
# 1. 无条件边:generate_answer执行后→review_answer
graph.add_edge("generate_answer", "review_answer")
# 2. 条件边:review_answer执行后,根据状态决定下一步
def after_review(state: GraphState) -> str:
if state.retry_count >= 3:
return END # 结束
return "generate_answer" # 重试生成
graph.add_conditional_edges("review_answer", after_review)
四、流程编排核心:StateGraph(状态图)
1. 定义
StateGraph是LangGraph中构建工作流的核心类,用于“注册节点、定义边、配置状态规则”,是图的“容器”。
2. 核心方法
| 方法 | 作用 |
|---|---|
add_node(name, func) | 注册节点(name为节点唯一标识,func为执行函数) |
set_entry_point(name) | 指定图的入口节点(对应__START__) |
add_edge(from, to) | 添加无条件边 |
add_conditional_edges(from, func) | 添加条件边(func返回下一个节点名称) |
compile() | 编译图(生成可执行的CompiledStateGraph) |
3. 示例(构建图)
# 初始化
graph = StateGraph(GraphState)
# 添加节点
graph.add_node("generate_answer", generate_answer)
graph.add_node("review_answer", review_answer)
# 配置入口和边
graph.set_entry_point("generate_answer")
graph.add_edge("generate_answer", "review_answer")
graph.add_conditional_edges("review_answer", after_review)
# 编译(关键:编译后才能执行)
compiled_graph = graph.compile()
五、进阶核心:Checkpoint(检查点)
1. 定义
检查点是LangGraph的状态持久化机制,会自动保存图执行过程中的“状态快照”(包括当前节点、状态数据、执行历史)。
2. 核心作用
- 中断恢复:流程崩溃/中断后,可从最近的检查点恢复执行,无需从头开始;
- 时间旅行:回溯任意历史检查点的状态,查看流程执行轨迹;
- 状态审计:记录所有状态变更,便于调试和合规检查。
3. 示例(启用检查点)
from langgraph.checkpoint.memory import MemorySaver
# 内存型检查点(生产环境可替换为Redis/文件存储)
checkpointer = MemorySaver()
# 编译时指定检查点
compiled_graph = graph.compile(checkpointer=checkpointer)
# 执行时指定session_id(标识唯一流程)
compiled_graph.invoke(
{"question": "什么是LangGraph?"},
config={"configurable": {"session_id": "user_123"}}
)
# 恢复流程(从检查点加载状态)
compiled_graph.invoke(
None, # 无需传入新状态,从检查点加载
config={"configurable": {"session_id": "user_123"}}
)
六、辅助概念:Reducer(归约器)
1. 定义
Reducer是状态更新规则,定义了“新状态如何合并到旧状态”(默认是覆盖,可自定义)。
2. 适用场景
- 多节点修改同一状态字段时,避免冲突(如多智能体协作修改“任务结果”);
- 对话场景中,合并历史消息(而非覆盖)。
3. 示例(自定义Reducer)
from langgraph.graph import StateGraph, Reducer
# 自定义Reducer:合并回答(而非覆盖)
def answer_reducer(prev: str | None, next: str | None) -> str:
if prev is None:
return next
return f"{prev}\n[重试版本]{next}"
# 初始化图时指定Reducer
graph = StateGraph(
GraphState,
reducers={"answer": answer_reducer} # 对answer字段应用自定义规则
)
七、执行结果:CompiledStateGraph
1. 定义
StateGraph.compile()的返回值,是可执行的图实例,提供核心执行方法。
2. 核心方法
| 方法 | 作用 |
|---|---|
invoke(input) | 同步执行图,输入初始状态,返回最终状态 |
stream(input) | 流式输出执行过程(逐节点返回状态) |
async_invoke | 异步执行(适合高并发场景) |
3. 示例
# 同步执行
result = compiled_graph.invoke({"question": "什么是LangGraph?"})
print(result["answer"])
# 流式执行(查看每一步状态)
for step in compiled_graph.stream({"question": "什么是LangGraph?"}):
print("当前节点:", list(step.keys())[0])
print("状态:", step[list(step.keys())[0]])
核心概念关系总结
State(状态)→ 承载所有数据
Node(节点)→ 操作状态的执行单元
Edge(边)→ 定义节点的执行顺序
StateGraph → 组装节点/边的容器
Checkpoint → 保存状态快照,支持恢复/回溯
Reducer → 定义状态更新规则,保证数据一致性
CompiledStateGraph → 可执行的最终图实例
以状态为核心,通过节点操作状态,通过边控制节点流转,通过检查点保障状态持久化。
实战案例
以下是一个 企业级合同审核助手 的完整 LangGraph 实例,涵盖「多节点流程、状态管理、条件分支、循环重试、人工审核、检查点持久化」等核心特性,可直接落地使用。
实例背景
实现一份采购合同的自动化审核流程: 解析合同文本,提取关键信息(金额、有效期、违约责任); 校验关键信息是否符合公司规范(如金额上限、有效期范围); 若校验不通过,自动生成修改建议并重试审核(最多 2 次); 重试 2 次仍不通过则触发人工审核; 最终输出审核报告(通过 / 不通过 + 修改建议); 全程记录状态,支持中断恢复和历史回溯。
环境准备
pip install langgraph langchain-openai python-dotenv pydantic
核心特性说明
- 完整的状态管理
用AuditState封装所有流程数据(合同文本、提取的信息、审核结果、重试次数等);
状态在所有节点间共享,保证数据一致性。 - 复杂流程控制
条件分支:根据信息提取结果 / 审核结果动态跳转;
循环重试:审核不通过时自动生成修改建议并重试(最多 2 次);
人工兜底:重试 2 次仍不通过触发人工审核,兼顾自动化与安全性。 - 状态持久化
启用MemorySaver检查点,支持:
流程中断后从上次节点恢复;
通过session_id追溯历史审核记录;
生产环境可替换为 Redis / 文件存储,适配企业级部署。 - 可扩展能力
节点解耦:每个节点单一职责,可独立修改(如替换信息提取逻辑);
规则配置化:可将审核规则(金额上限、有效期)抽离为配置文件;
模型替换:可无缝替换为国产大模型(如通义千问、智谱 AI)。
代码
import json
import os
from dotenv import load_dotenv
from typing import Dict, List, Optional, Any
from pydantic import BaseModel, Field
from langgraph.graph import StateGraph, END
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
# ====================== 1. 定义核心数据结构 ======================
# 合同关键信息模型
class ContractInfo(BaseModel):
amount: float = Field(description="合同金额(元)")
valid_period: str = Field(description="合同有效期(如'2025-01-01至2025-12-31')")
liability: str = Field(description="违约责任条款")
supplier: str = Field(description="供应商名称")
# 全局状态模型(贯穿整个工作流)
class AuditState(BaseModel):
contract_text: str = Field(description="合同原始文本")
contract_info: Optional[ContractInfo] = Field(default=None, description="提取的合同信息")
audit_result: Optional[bool] = Field(default=None, description="审核是否通过")
modify_suggestions: List[str] = Field(default_factory=list, description="修改建议")
retry_count: int = Field(default=0, description="审核重试次数")
human_review: bool = Field(default=False, description="是否触发人工审核")
final_report: Optional[str] = Field(default=None, description="最终审核报告")
# ====================== 2. 初始化大模型 ======================
# 可替换为国产大模型(如智谱、通义千问)
llm = ChatOpenAI(
model="",
temperature=0.7,
max_tokens=30000,
api_key="",
base_url="", # 自定义API地址
# 如需设置代理/超时,可添加以下参数
# timeout=30,
# http_client=httpx.Client(proxies={"https": "http://127.0.0.1:7890"})
)
# print(llm.invoke("你是谁"))
# ====================== 3. 节点函数(官方要求:纯函数,返回新状态,不修改入参)======================
def extract_contract_info(state: AuditState) -> AuditState:
"""提取合同信息:纯函数,返回新状态(不修改原state)"""
prompt = f"""
请从以下合同文本中提取关键信息,严格按照指定格式返回JSON:
合同文本:{state.contract_text}
需要提取的字段:amount(金额,仅数字)、valid_period、liability、supplier
输出要求:仅返回JSON,不要其他文字
"""
# 初始化新状态(避免修改入参)
new_state = state.model_dump()
new_state["modify_suggestions"] = new_state.get("modify_suggestions", [])
try:
response = llm.invoke(prompt).content.strip()
info_dict = json.loads(response) # 官方推荐 json.loads(避免 eval 安全风险)
new_state["contract_info"] = info_dict
print(f"[节点:提取信息] 成功提取供应商:{info_dict['supplier']},金额:{info_dict['amount']}")
except Exception as e:
new_state["modify_suggestions"].append(f"信息提取失败:{str(e)}")
new_state["audit_result"] = False
return new_state
def audit_contract(state: AuditState) -> AuditState:
"""合规校验:纯函数设计"""
new_state = state.model_dump()
new_state["modify_suggestions"] = new_state.get("modify_suggestions", [])
if not new_state["contract_info"]:
new_state["audit_result"] = False
new_state["modify_suggestions"].append("未提取到合同关键信息,无法审核")
new_state["retry_count"] = new_state.get("retry_count", 0) + 1
print(f"[节点:合规校验] 审核结果:不通过,重试次数:{new_state['retry_count']}")
return new_state
# 合规规则
MAX_AMOUNT = 1000000
VALID_PERIOD_MAX = 36
suggestions = []
if new_state["contract_info"]["amount"] > MAX_AMOUNT:
suggestions.append(f"合同金额{new_state['contract_info']['amount']}元超过上限{MAX_AMOUNT}元")
if "3年" in new_state["contract_info"]["valid_period"] or "36个月" in new_state["contract_info"]["valid_period"]:
suggestions.append(f"合同有效期超过{VALID_PERIOD_MAX}个月上限")
if "无违约责任" in new_state["contract_info"]["liability"]:
suggestions.append("合同未约定违约责任,不符合公司规范")
new_state["modify_suggestions"] = suggestions
new_state["audit_result"] = len(suggestions) == 0
new_state["retry_count"] = new_state.get("retry_count", 0) + 1
print(f"[节点:合规校验] 审核结果:{'通过' if new_state['audit_result'] else '不通过'},重试次数:{new_state['retry_count']}")
return new_state
def generate_modify_suggestions(state: AuditState) -> AuditState:
"""生成修改建议:纯函数"""
new_state = state.model_dump()
contract_info = new_state["contract_info"]
problems = new_state["modify_suggestions"]
prompt = f"""
请根据以下合同审核问题,生成具体的修改建议(专业、可落地):
合同信息:{contract_info}
存在问题:{problems}
输出要求:分点列出修改建议,语言正式,符合合同法规范
"""
response = llm.invoke(prompt).content.strip()
new_state["modify_suggestions"] = [response]
print(f"[节点:生成建议] 已生成修改建议:{response[:100]}...")
return new_state
def human_review_node(state: AuditState) -> AuditState:
"""人工审核:纯函数"""
new_state = state.model_dump()
new_state["human_review"] = True
print("\n===== 触发人工审核 =====")
print(f"合同信息:{new_state['contract_info']}")
print(f"当前问题:{new_state['modify_suggestions']}")
human_feedback = input("请输入人工审核结果(通过/不通过):").strip()
new_state["audit_result"] = human_feedback == "通过"
if not new_state["audit_result"]:
human_suggestion = input("请输入修改建议:").strip()
new_state["modify_suggestions"].append(f"人工审核意见:{human_suggestion}")
return new_state
def generate_final_report(state: AuditState) -> AuditState:
"""生成报告:纯函数"""
new_state = state.model_dump()
contract_info = new_state["contract_info"] or {}
status = "通过" if new_state["audit_result"] else "不通过"
report = f"""
# 合同审核报告
1. 供应商:{contract_info.get('supplier', '未知')}
2. 合同金额:{contract_info.get('amount', '未知')}元
3. 审核结果:{status}
4. 修改建议:
{chr(10).join(new_state['modify_suggestions'])}
5. 重试次数:{new_state['retry_count']}
6. 是否人工审核:{new_state['human_review']}
"""
new_state["final_report"] = report
print(f"[节点:生成报告] 审核完成,最终结果:{status}")
return new_state
# ====================== 4. 分支函数(保持不变,符合官方条件边规范)======================
def after_extract(state: AuditState) -> str:
return "audit_contract" if state.contract_info else "generate_final_report"
def after_audit(state: AuditState) -> str:
if state.audit_result:
return "generate_final_report"
elif state.retry_count >= 2:
return "human_review_node"
else:
return "generate_modify_suggestions"
# ====================== 5. 构建图(严格遵循官方编译规范)======================
def build_contract_audit_graph() -> Any:
# 初始化图:指定状态类型(官方推荐显式声明)
graph = StateGraph(AuditState)
# 添加节点:节点名称与函数名一致(官方最佳实践)
graph.add_node("extract_contract_info", extract_contract_info)
graph.add_node("audit_contract", audit_contract)
graph.add_node("generate_modify_suggestions", generate_modify_suggestions)
graph.add_node("human_review_node", human_review_node)
graph.add_node("generate_final_report", generate_final_report)
# 定义流程:完全对齐官方边配置规范
graph.set_entry_point("extract_contract_info") # 入口节点
graph.add_conditional_edges("extract_contract_info", after_extract) # 条件边
graph.add_conditional_edges("audit_contract", after_audit) # 条件边
graph.add_edge("generate_modify_suggestions", "audit_contract") # 无条件边
graph.add_edge("human_review_node", "generate_final_report") # 无条件边
graph.add_edge("generate_final_report", END) # 终止边
# 编译图:官方推荐显式关闭多余校验(提升兼容性)
checkpointer = MemorySaver()
compiled_graph = graph.compile(
checkpointer=checkpointer,
)
return compiled_graph
# ====================== 6. 执行工作流(符合官方调用规范)======================
if __name__ == "__main__":
audit_graph = build_contract_audit_graph()
# 初始状态:严格按照 AuditState 结构定义(官方要求)
test_contract = """
采购合同
甲方:XX科技有限公司
乙方:XX供应商有限公司
1. 合同金额:1200000元
2. 合同有效期:3年(2025-01-01至2028-01-01)
3. 违约责任:无
4. 付款方式:月结
"""
initial_state: AuditState = {
"contract_text": test_contract,
"contract_info": None,
"audit_result": None,
"modify_suggestions": [],
"retry_count": 0,
"human_review": False,
"final_report": None
}
# 执行:官方推荐传入 config 配置 session_id
config = {"configurable": {"session_id": "contract_audit_001",
"thread_id": "audit_thread_001",
}}
final_state = audit_graph.invoke(initial_state, config=config)
# 输出结果
print("\n" + "="*50)
print("最终审核报告:")
print(final_state["final_report"])
1367

被折叠的 条评论
为什么被折叠?



