LangGraph 是 LangChain 生态系统中的一个高级库,它允许开发者使用有向无环图 (DAG) 的方式构建健壮、有状态且可控的 LLM 应用。它特别适用于需要多步骤推理、代理 (Agent) 行为、循环和人工干预的复杂工作流。LangGraph 的核心优势在于其明确的状态管理和对图结构的直接建模能力,使得构建和调试复杂代理系统变得更加直观和可靠。
核心思想:将多步骤的 LLM 应用程序建模为状态机,其中每个节点代表一个操作(LLM 调用、工具调用、函数等),边代表状态转换。通过在节点之间传递和修改状态,实现复杂、有循环的工作流。它解决了传统 LangChain Chain 在处理复杂逻辑(特别是循环和条件分支)时的局限性。
一、LangGraph 核心概念 LangGraph 的设计基于图论和状态机的思想。理解以下核心概念是使用 LangGraph 的基础:
State (状态) :
表示整个应用程序在某个时间点的数据快照。
通过 StateDict 对象传递,它是一个字典或类似字典的结构。
节点操作通常会接收当前状态,并返回一个表示状态更新的 StateDict。
支持 TypedDict 或 dataclass 来定义强类型状态,提高可读性和健壮性。
LangGraph 会智能地合并每个节点返回的状态更新(例如,对于字典类型的状态,它会执行深合并)。
Node (节点) :
图中的基本处理单元。每个节点封装了一个独立的操作。
可以是一个 LLM 调用、一个工具调用、一个 Python 函数、或另一个 LangChain Chain。
节点接收当前状态作为输入,执行其逻辑,并返回一个状态更新。
Edge (边) :
连接节点,决定了执行流程的路径。
分为两种主要类型:
Conditional Edges (条件边) :根据节点的输出(通常是一个字符串或字典中的特定字段)来决定下一个要执行的节点。例如,代理可能会输出一个“tool_call”指令,系统根据此指令跳转到工具执行节点。
Direct Edges (直接边) :无条件地从一个节点直接跳转到另一个节点。
Graph (图) :
由节点和边构成,定义了应用程序的完整工作流。
StateGraph 是带有明确状态管理的图,是我们主要使用的构建块。
可以通过 add_node(), add_edge(), add_conditional_edges(), set_entry_point(), set_finish_point() 等方法来构建。
Agent (代理) :
在 LangGraph 中,代理通常是一个特殊的节点,它利用 LLM 的推理能力,根据当前状态决定下一步行动(是调用工具还是直接给出答案)。
LangGraph 的图结构非常适合实现复杂的 ReAct (Reasoning and Acting) 或 COT (Chain of Thought) 代理模式。
二、安装与基础构建块 首先,确保安装了 LangGraph 和必要的 LLM 库(如 OpenAI):
1 pip install langgraph langchain_openai
2.1 定义应用程序状态 (State) 使用 TypedDict 来定义应用程序的状态,这是最佳实践。它使状态结构清晰、易于理解和调试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from typing import List , TypedDict, Annotated, Sequence import operatorfrom langchain_core.messages import BaseMessageclass AgentState (TypedDict ): """ 一个 TypedDict,用于在图中的节点之间传递状态。 Attributes: messages: 这是一个列表,包含对话历史中的所有消息。 original_query: 原始的用户输入,有时需要保留。 tool_output: 上一个工具调用的输出。 # 可以根据需要添加更多状态变量,例如: # num_steps: int # current_task: str """ messages: Annotated[List [BaseMessage], operator.add] original_query: str tool_output: Annotated[List [dict ], operator.add]
messages: Annotated[List[BaseMessage], operator.add] : 这是一个关键的 LangGraph 特性。Annotated 结合 operator.add 告诉 LangGraph,当不同节点提供 messages 更新时,应该将它们简单地追加 到现有列表中,而不是替换。这对于维护对话历史非常有用。
operator.add : 对于列表类型,它会执行 list.extend() 操作。对于数字,它会相加。对于字典,它会执行合并操作。
2.2 定义图节点 (Nodes) 每个节点都是一个 Python 函数,接收当前 state 作为输入,并返回一个 StateDict 来更新状态。
LLM 调用节点示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from langchain_openai import ChatOpenAIfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langchain_core.messages import HumanMessage, AIMessage, ToolMessagefrom langchain_core.tools import toolimport osllm = ChatOpenAI(model="gpt-4o" , temperature=0 ) def call_llm (state: AgentState ): print ("---CALL LLM NODE---" ) messages = state["messages" ] prompt = ChatPromptTemplate.from_messages([ ("system" , "你是一个强大的AI助手,可以回答问题和使用工具。" ), MessagesPlaceholder(variable_name="messages" ), ]) llm_with_tools = llm response = llm_with_tools.invoke(messages) return {"messages" : [response]}
工具调用节点示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 @tool def search_web (query: str ) -> str : """根据查询执行网络搜索并返回结果。""" print (f"---EXECUTING WEB SEARCH FOR: {query} ---" ) if "天气" in query: return " today is sunny in Shanghai, 28 degrees celsius." elif "Python LangGraph" in query: return "LangGraph is a library for building stateful, multi-actor applications with LLMs." else : return f"Simulated search result for '{query} '" tools = [search_web] def call_tool (state: AgentState ): print ("---CALL TOOL NODE---" ) messages = state["messages" ] last_message = messages[-1 ] if not last_message.tool_calls: raise ValueError("LLM did not request a tool call." ) tool_outputs = [] for tool_call in last_message.tool_calls: tool_name = tool_call["name" ] tool_args = tool_call["args" ] tool_found = False for t in tools: if t.name == tool_name: print (f" Executing tool: {tool_name} with args: {tool_args} " ) output = t.invoke(tool_args) tool_outputs.append(output) messages.append(ToolMessage(content=str (output), tool_call_id=tool_call["id" ])) tool_found = True break if not tool_found: print (f" Tool {tool_name} not found." ) return {"messages" : messages[-1 :], "tool_output" : tool_outputs}
2.3 定义条件边函数 (Conditional Edges Function) 条件边函数接收当前状态,并返回一个字符串,该字符串对应着图中的某个节点名称,或特定的终止指令 (END)。
1 2 3 4 5 6 7 8 9 10 11 12 13 def should_continue (state: AgentState ): print ("---CHECK LLM OUTPUT (CONDITIONAL EDGE)---" ) last_message = state["messages" ][-1 ] if last_message.tool_calls: print (" Decision: TOOL CALL" ) return "call_tool" else : print (" Decision: END (or direct answer)" ) return "end_chat"
三、构建与编译图 (Build and Compile the Graph) 使用 StateGraph 来定义整个工作流。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 from langgraph.graph import StateGraph, ENDfrom langchain_core.messages import AIMessageworkflow = StateGraph(AgentState) workflow.add_node("llm" , call_llm) workflow.add_node("call_tool" , call_tool) workflow.set_entry_point("llm" ) workflow.add_conditional_edges( "llm" , should_continue, { "call_tool" : "call_tool" , "end_chat" : END } ) workflow.add_edge("call_tool" , "llm" ) app = workflow.compile () print ("---GRAPH COMPILED---" )
Mermaid 图示 (简化代理工作流):
graph TD
start("开始 (用户输入)") --> llm_node(("LLM (思考/生成/决策)"))
llm_node --> |条件:需要工具| call_tool_node("调用工具")
call_tool_node --> llm_node
llm_node --> |条件:直接回复| end_node("结束 (返回答案)")
end_node(["END"])
四、调用和执行图 (Invoke and Execute the Graph) 编译后的图可以通过 app.invoke() 方法执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 import uuidos.environ["LANGCHAIN_TRACING_V2" ] = "true" os.environ["LANGCHAIN_API_KEY" ] = "sk-..." os.environ["LANGCHAIN_PROJECT" ] = "LangGraph_Demo" user_query = "上海今天的天气怎么样?" unique_thread_id = str (uuid.uuid4()) initial_state = { "messages" : [HumanMessage(content=user_query)], "original_query" : user_query, "tool_output" : [] } print (f"\n---STARTING GRAPH EXECUTION FOR '{user_query} '---" )final_state = app.invoke( initial_state, {"configurable" : {"thread_id" : unique_thread_id}} ) print ("\n---GRAPH EXECUTION FINISHED---" )print ("Final State Messages:" )for msg in final_state["messages" ]: print (f" {msg.type .upper()} : {msg.content} " ) print (f"\nFinal AI Response: {final_state['messages' ][-1 ].content} " )print ("\n-------------------------------------------------" )user_query_2 = "什么是 LangGraph?" unique_thread_id_2 = str (uuid.uuid4()) initial_state_2 = { "messages" : [HumanMessage(content=user_query_2)], "original_query" : user_query_2, "tool_output" : [] } print (f"\n---STARTING GRAPH EXECUTION FOR '{user_query_2} '---" )final_state_2 = app.invoke( initial_state_2, {"configurable" : {"thread_id" : unique_thread_id_2}} ) print ("\n---GRAPH EXECUTION FINISHED---" )print ("Final State Messages:" )for msg in final_state_2["messages" ]: print (f" {msg.type .upper()} : {msg.content} " ) print (f"\nFinal AI Response: {final_state_2['messages' ][-1 ].content} " )
五、高级特性和注意事项 5.1 检查点 (Checkpoints) LangGraph 允许为每个线程保存图的状态(检查点),以便中断后从上次的状态继续执行。这对于长时间运行的代理会话或人工审核流程非常有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from langgraph.checkpoint.memory import MemorySavermemory_checkpoint = MemorySaver() app_with_checkpoint = workflow.compile (checkpointer=memory_checkpoint) thread_id = "user-session-123" initial_state_c = {"messages" : [HumanMessage(content="你好,帮我查一下旧金山的天气。" )]} app_with_checkpoint.invoke(initial_state_c, {"configurable" : {"thread_id" : thread_id}}) next_state = app_with_checkpoint.invoke( {"messages" : [HumanMessage(content="好的,谢谢。" )]}, {"configurable" : {"thread_id" : thread_id}} ) print (f"\n--- Resumed state for thread {thread_id} ---" )for msg in next_state["messages" ]: print (f" {msg.type .upper()} : {msg.content} " )
5.2 并行执行 (Parallel Execution) LangGraph 节点默认是同步执行的。如果需要并行执行多个不互相依赖的操作(例如同时调用多个工具),可以手动在单个节点内部实现多线程或异步编程。LangGraph 本身并不直接支持节点间的并行分支执行(因为它是一个序列图),但它非常适合编排那些可以在节点内部并行化的操作。
或者,如果需要更复杂的子图并行,可以考虑将某个并行执行的逻辑封装成一个嵌套的 LangGraph 子图,然后在主图中作为一个节点调用。
5.3 人工审核 (Human-in-the-Loop) LangGraph 的状态机特性使其非常适合构建人工审核或干预的工作流。可以在某个节点之后添加一个条件边,判断是否需要人工干预,如果需要,则将状态暂停,等待人工输入后,再恢复执行。
5.4 动态图构建 虽然通常是预先定义图,但 LangGraph 也允许一定程度的动态图构建。例如,可以根据用户的输入或当前状态,在运行时选择性地添加、修改或跳过节点和边。不过,这会增加图的复杂性。
六、总结 LangGraph 是构建复杂、有状态、多步骤 LLM 应用程序的强大工具。通过将应用程序建模为状态机和图,它克服了传统 LangChain Chain 在处理循环、条件分支和显式状态管理方面的局限性。
核心优势包括:
显式状态管理 :通过 AgentState 明确定义和更新应用程序状态。
灵活的控制流 :支持条件边和图循环,实现复杂的代理行为(如 ReAct 模式)。
模块化和可组合性 :将独立操作封装成节点,易于测试、调试和重用。
内置检查点 :支持会话的暂停和恢复,实现鲁棒的长会话应用。
适用于代理 :是实现高级自主代理和多代理系统的理想选择。
掌握 LangGraph 的核心组件和调用方法,将使您能够构建出更具韧性、更智能、更接近通用人工智能的 LLM 应用程序。