本文介绍了如何使用 LangGraph 来构建和管理基于大型语言模型(LLM)的复杂工作流。从安装开始,逐步讲解核心步骤、关键概念、代码示例,以及如何在实际场景中应用 LangGraph。
目录
- 安装和环境准备
- LangGraph 的核心概念回顾
- 构建一个 LangGraph 工作流:分步指南
- 代码示例:简单工作流
- 高级功能:条件边、循环和工具调用
- 结合 LangChain 和 LangSmith
- 实际应用场景示例
- 调试和优化
- 常见问题和注意事项
- 学习资源和下一步
1. 安装和环境准备
在使用 LangGraph 之前,你需要设置开发环境并安装必要的依赖。
1.1 安装 LangGraph 和 LangChain
LangGraph 是 LangChain 生态的一部分,因此需要安装 LangChain 和 LangGraph。
pip install langchain langgraph
如果你计划使用特定的 LLM(如 OpenAI 的 GPT 模型),还需要安装对应的客户端库。例如:
pip install langchain-openai # 用于 OpenAI API
1.2 配置 API 密钥
如果使用外部 LLM 服务(如 OpenAI、Anthropic),需要配置 API 密钥。可以通过环境变量设置:
export OPENAI_API_KEY="your-api-key"
或者在代码中手动设置(不推荐用于生产环境):
import os
os.environ["OPENAI_API_KEY"] = "your-api-key"
1.3 验证安装
运行以下代码,检查是否正确安装:
from langgraph.graph import StateGraph
print("LangGraph installed successfully!")
2. LangGraph 的核心概念回顾
在开始使用之前,快速回顾 LangGraph 的核心概念(详细介绍见前文):
- 状态(State):一个数据结构(如字典或类),用于存储工作流的上下文信息,在节点间传递和更新。
- 节点(Node):执行具体任务的函数(如调用 LLM、处理数据),接收状态并返回更新后的状态。
- 边(Edge):定义节点之间的连接,可以是无条件的(直接跳转)或条件边(基于状态跳转)。
- 图(Graph):由节点和边组成的结构,表示整个工作流,使用
StateGraph
类定义。
LangGraph 的执行逻辑是:输入初始状态 → 图按节点和边定义的顺序执行 → 不断更新状态 → 直到达到终止条件。
3. 构建一个 LangGraph 工作流:分步指南
以下是使用 LangGraph 构建工作流的标准步骤:
3.1 定义状态
状态是工作流的核心,用于存储和传递信息。可以使用 Python 的 TypedDict
或自定义类来定义状态。
示例状态定义:
from typing import TypedDict
class State(TypedDict):
input: str # 用户输入
output: str # 处理结果
3.2 创建节点
节点是执行任务的函数,接收状态并返回更新后的状态。每个节点是一个 Python 函数。
示例节点:
def process_input(state: State) -> State:
state["output"] = f"处理输入: {state['input']}"
return state
def finalize_output(state: State) -> State:
state["output"] += " -> 已完成"
return state
3.3 定义边
边指定节点之间的执行顺序。可以使用:
add_edge
添加无条件边。add_conditional_edges
添加条件边。
3.4 构建图
使用 StateGraph
类创建图,添加节点、边,并设置入口和出口。
from langgraph.graph import StateGraph, START, END
# 创建图
workflow = StateGraph(State)
# 添加节点
workflow.add_node("process_input", process_input)
workflow.add_node("finalize_output", finalize_output)
# 添加边
workflow.add_edge("process_input", "finalize_output")
workflow.add_edge(START, "process_input") # 设置入口
workflow.add_edge("finalize_output", END) # 设置出口
# 编译图
graph = workflow.compile()
3.5 执行工作流
使用 invoke
方法运行图,传入初始状态。
result = graph.invoke({"input": "Hello, LangGraph!", "output": ""})
print(result)
4. 代码示例:简单工作流
以下是一个完整的简单工作流示例,展示如何处理用户输入并生成输出。
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
# 定义状态
class State(TypedDict):
input: str
output: str
# 定义节点
def process_input(state: State) -> State:
state["output"] = f"处理输入: {state['input']}"
return state
def finalize_output(state: State) -> State:
state["output"] += " -> 已完成"
return state
# 创建图
workflow = StateGraph(State)
workflow.add_node("process_input", process_input)
workflow.add_node("finalize_output", finalize_output)
workflow.add_edge(START, "process_input")
workflow.add_edge("process_input", "finalize_output")
workflow.add_edge("finalize_output", END)
# 编译和运行
graph = workflow.compile()
result = graph.invoke({"input": "Hello, LangGraph!", "output": ""})
print(result)
输出:
{'input': 'Hello, LangGraph!', 'output': '处理输入: Hello, LangGraph! -> 已完成'}
这个示例展示了:
- 一个简单的状态(包含
input
和output
)。 - 两个节点:
process_input
和finalize_output
。 - 线性工作流:从
START
到process_input
,再到finalize_output
,最后到END
。
5. 高级功能:条件边、循环和工具调用
LangGraph 的强大之处在于支持动态逻辑、循环和工具调用。以下是实现这些功能的方法。
5.1 条件边
条件边允许根据状态动态选择下一个节点。使用 add_conditional_edges
定义条件逻辑。
示例:根据输入内容决定下一步:
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
input: str
output: str
needs_processing: bool
def check_input(state: State) -> State:
state["needs_processing"] = len(state["input"]) > 5 # 输入长度大于5需要处理
return state
def process_input(state: State) -> State:
state["output"] = f"处理输入: {state['input']}"
return state
def skip_processing(state: State) -> State:
state["output"] = "输入太短,无需处理"
return state
# 条件函数
def route(state: State) -> str:
return "process_input" if state["needs_processing"] else "skip_processing"
# 创建图
workflow = StateGraph(State)
workflow.add_node("check_input", check_input)
workflow.add_node("process_input", process_input)
workflow.add_node("skip_processing", skip_processing)
workflow.add_edge(START, "check_input")
workflow.add_conditional_edges("check_input", route, {
"process_input": "process_input",
"skip_processing": "skip_processing"
})
workflow.add_edge("process_input", END)
workflow.add_edge("skip_processing", END)
# 编译和运行
graph = workflow.compile()
result = graph.invoke({"input": "Hi", "output": "", "needs_processing": False})
print(result)
输出(输入 “Hi”):
{'input': 'Hi', 'output': '输入太短,无需处理', 'needs_processing': False}
输出(输入 “Hello, LangGraph!”):
{'input': 'Hello, LangGraph!', 'output': '处理输入: Hello, LangGraph!', 'needs_processing': True}
5.2 循环
LangGraph 支持循环,适合需要反复执行的场景(如 agent 多次调用工具)。可以通过条件边返回到之前的节点。
示例:循环直到满足条件:
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
class State(TypedDict):
counter: int
output: str
def increment(state: State) -> State:
state["counter"] += 1
state["output"] = f"当前计数: {state['counter']}"
return state
def should_continue(state: State) -> str:
return "increment" if state["counter"] < 3 else END
# 创建图
workflow = StateGraph(State)
workflow.add_node("increment", increment)
workflow.add_edge(START, "increment")
workflow.add_conditional_edges("increment", should_continue, {
"increment": "increment",
END: END
})
# 编译和运行
graph = workflow.compile()
result = graph.invoke({"counter": 0, "output": ""})
print(result)
输出:
{'counter': 3, 'output': '当前计数: 3'}
5.3 工具调用
LangGraph 可以与 LangChain 的工具集成,动态调用外部 API 或函数。
示例:使用 LLM 和搜索工具:
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
# 定义工具
@tool
def search(query: str) -> str:
"""模拟搜索工具"""
return f"搜索结果 for {query}: 找到了一些信息!"
# 定义状态
class State(TypedDict):
input: str
output: str
needs_search: bool
# 节点:调用 LLM 判断是否需要搜索
def call_llm(state: State) -> State:
llm = ChatOpenAI(model="gpt-4o-mini")
prompt = f"用户输入: {state['input']}\n是否需要搜索?回答 'yes' 或 'no'。"
response = llm.invoke(prompt).content
state["needs_search"] = response.lower() == "yes"
state["output"] = "正在处理..."
return state
# 节点:执行搜索
def call_search(state: State) -> State:
result = search.invoke(state["input"])
state["output"] = result
return state
# 节点:直接回答
def direct_answer(state: State) -> State:
state["output"] = f"直接回答: {state['input']}"
return state
# 条件函数
def route(state: State) -> str:
return "call_search" if state["needs_search"] else "direct_answer"
# 创建图
workflow = StateGraph(State)
workflow.add_node("call_llm", call_llm)
workflow.add_node("call_search", call_search)
workflow.add_node("direct_answer", direct_answer)
workflow.add_edge(START, "call_llm")
workflow.add_conditional_edges("call_llm", route, {
"call_search": "call_search",
"direct_answer": "direct_answer"
})
workflow.add_edge("call_search", END)
workflow.add_edge("direct_answer", END)
# 编译和运行
graph = workflow.compile()
result = graph.invoke({"input": "今天的天气", "output": "", "needs_search": False})
print(result)
输出(假设 LLM 认为需要搜索):
{'input': '今天的天气', 'output': '搜索结果 for 今天的天气: 找到了一些信息!', 'needs_search': True}
6. 结合 LangChain 和 LangSmith
6.1 与 LangChain 集成
LangGraph 通常与 LangChain 一起使用,LangChain 提供:
- LLM 封装:如
ChatOpenAI
、ChatAnthropic
。 - 工具支持:通过
langchain_core.tools
定义工具。 - Prompt 管理:使用
PromptTemplate
构建结构化提示。
示例:结合 PromptTemplate 和 LLM:
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
prompt = PromptTemplate.from_template("处理用户输入: {input}")
llm = ChatOpenAI(model="gpt-4o-mini")
def llm_node(state: State) -> State:
response = llm.invoke(prompt.format(input=state["input"])).content
state["output"] = response
return state
6.2 与 LangSmith 集成
LangSmith 是 LangChain 提供的调试和监控工具,可以可视化 LangGraph 的执行过程。
-
启用 LangSmith:
export LANGCHAIN_TRACING_V2="true" export LANGCHAIN_API_KEY="your-langsmith-api-key"
-
运行代码:LangGraph 会自动将执行日志发送到 LangSmith。
-
查看日志:在 LangSmith 界面中,可以看到每个节点的输入输出、状态变化和执行路径。
7. 实际应用场景示例
以下是一个实际场景:构建一个客服 Agent,能够回答用户问题或调用搜索工具。
场景描述
- 用户输入问题。
- Agent 判断是否需要搜索。
- 如果需要搜索,调用搜索工具并生成回答。
- 如果无需搜索,直接生成回答。
代码实现
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.prompts import PromptTemplate
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
# 定义工具
@tool
def search(query: str) -> str:
"""模拟搜索工具"""
return f"搜索结果: {query} 的信息"
# 定义状态
class State(TypedDict):
input: str
output: str
needs_search: bool
# 定义节点
def decide_action(state: State) -> State:
llm = ChatOpenAI(model="gpt-4o-mini")
prompt = PromptTemplate.from_template(
"用户问题: {input}\n回答 'yes' 如果需要搜索,'no' 如果可以直接回答。"
)
response = llm.invoke(prompt.format(input=state["input"])).content
state["needs_search"] = response.lower() == "yes"
return state
def search_node(state: State) -> State:
result = search.invoke(state["input"])
state["output"] = result
return state
def answer_node(state: State) -> State:
llm = ChatOpenAI(model="gpt-4o-mini")
prompt = PromptTemplate.from_template("直接回答用户问题: {input}")
response = llm.invoke(prompt.format(input=state["input"])).content
state["output"] = response
return state
# 条件函数
def route(state: State) -> str:
return "search_node" if state["needs_search"] else "answer_node"
# 创建图
workflow = StateGraph(State)
workflow.add_node("decide_action", decide_action)
workflow.add_node("search_node", search_node)
workflow.add_node("answer_node", answer_node)
workflow.add_edge(START, "decide_action")
workflow.add_conditional_edges("decide_action", route, {
"search_node": "search_node",
"answer_node": "answer_node"
})
workflow.add_edge("search_node", END)
workflow.add_edge("answer_node", END)
# 编译和运行
graph = workflow.compile()
result = graph.invoke({"input": "什么是 LangGraph?", "output": "", "needs_search": False})
print(result)
输出(假设 LLM 认为需要搜索):
{'input': '什么是 LangGraph?', 'output': '搜索结果: 什么是 LangGraph? 的信息', 'needs_search': True}
8. 调试和优化
8.1 使用 LangSmith 调试
- 检查每个节点的输入输出。
- 分析条件边的跳转逻辑。
- 识别性能瓶颈(如 LLM 调用时间过长)。
8.2 优化技巧
- 减少 LLM 调用:尽量在节点中缓存结果或合并逻辑。
- 精简状态:只存储必要的数据,减少内存占用。
- 错误处理:在节点中添加 try-except,处理工具调用或 LLM 失败的情况。
9. 常见问题和注意事项
9.1 常见问题
- Q:状态未正确更新?
- A:确保节点返回了更新后的状态,且状态结构与定义一致。
- Q:条件边跳转错误?
- A:检查条件函数的返回值是否与
add_conditional_edges
的映射一致。
- A:检查条件函数的返回值是否与
- Q:图执行卡住?
- A:可能是循环未正确终止,检查条件边逻辑。
9.2 注意事项
- 状态不可变:LangGraph 默认状态是可变的,建议在节点中创建状态副本以避免意外修改。
- 线程安全:如果在多线程环境中使用,确保状态和节点函数是线程安全的。
- 工具调用成本:频繁调用外部工具(如搜索 API)可能增加延迟和费用,需优化调用频率。
10. 学习资源和下一步
10.1 学习资源
- 官方文档:https://langchain-ai.github.io/langgraph/
- LangChain 文档:https://python.langchain.com/docs/
- LangSmith:https://smith.langchain.com/
- GitHub 仓库:https://github.com/langchain-ai/langgraph
- 教程和示例:LangChain 官方博客和 YouTube 频道。
10.2 下一步
- 尝试更复杂的场景,如多 Agent 协作或 RAG 工作流。
- 探索 LangGraph 的异步支持(
graph.ainvoke
),提高性能。 - 使用 LangSmith 优化你的工作流,分析性能瓶颈。
总结
使用 LangGraph 的核心在于:
- 定义清晰的状态结构。
- 编写模块化的节点函数。
- 通过边和条件边实现动态逻辑。
- 结合 LangChain 的 LLM 和工具,构建强大的工作流。