预备知识
在深入了解LangGraph之前,我们需要掌握一些基础知识,包括LangChain中的链与LCEL(LangChain Expression Language),LCEL构建与调度Agent的方法,以及图(Graph)的基本概念。这些知识将为我们后续学习和使用LangGraph打下坚实的基础。
1.1 LangChain中的链与LCEL
LangChain是一个用于构建语言模型应用的框架。在LangChain中,链(Chain)是一个核心概念,用于定义一系列的操作或步骤,这些步骤可以顺序执行以完成特定的任务。链可以包含多个组件,如模型调用、数据处理函数等,它们按照预定的顺序执行。链的设计使得复杂的任务可以通过简单的步骤组合来实现。
LCEL(LangChain Expression Language)是一种用于描述和构建链的语言。它提供了一种简洁的方式来定义链的结构和行为。通过LCEL,开发者可以轻松地创建复杂的链,并对其进行调试和优化。
例如,以下是一个简单的链的定义:
from langchain import LLMChain, PromptTemplate
from langchain.llms import OpenAI
# 定义一个提示模板
prompt_template = PromptTemplate(
input_variables=["product"],
template="What is a good name for a company that makes {product}?"
)
# 定义一个LLM
llm = OpenAI(temperature=0.7)
# 创建一个链
chain = LLMChain(llm=llm, prompt=prompt_template)
# 运行链
result = chain.run("colorful socks")
print(result)
在这个例子中,我们定义了一个简单的链,它包含一个提示模板和一个LLM。当我们运行这个链时,它会根据输入生成一个公司名称。
1.2 LCEL构建与调度Agent
Agent是LangChain中的另一个重要概念,它是一个智能体,可以根据输入执行一系列的操作。Agent通常包含一个或多个链,并且可以根据输入动态地选择和执行这些链。
LCEL不仅可以用于构建链,还可以用于构建和调度Agent。通过LCEL,开发者可以定义Agent的行为和决策逻辑。例如,以下是一个简单的Agent的定义:
from langchain.agents import AgentExecutor, Tool
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
# 定义一个工具
def search(query):
return "Some search results"
tools = [
Tool(
name="Search",
func=search,
description="useful for when you need to answer questions about current events"
)
]
# 定义一个提示模板
prompt_template = PromptTemplate(
input_variables=["input"],
template="You are a helpful assistant. {input}"
)
# 定义一个LLM
llm = OpenAI(temperature=0.7)
# 创建一个链
chain = LLMChain(llm=llm, prompt=prompt_template)
# 创建一个Agent
agent = AgentExecutor.from_agent_and_tools(
agent=chain,
tools=tools,
verbose=True
)
# 运行Agent
result = agent.run("What's the latest news on AI?")
print(result)
在这个例子中,我们定义了一个Agent,它包含一个链和一个工具。当我们运行这个Agent时,它会根据输入选择并执行相应的工具或链。
1.3 什么是图(Graph)
图(Graph)是一种数据结构,由节点(Node)和边(Edge)组成。节点表示实体或对象,边表示节点之间的关系。图可以用于表示复杂的关系和流程,广泛应用于计算机科学和人工智能领域。
在LangGraph中,图被用于表示和构建复杂的智能体应用。每个节点代表一个操作或步骤,边表示节点之间的依赖关系。通过定义节点和边,开发者可以构建复杂的流程和逻辑。
例如,以下是一个简单的图的定义:
from langgraph.graph import StateGraph
# 定义一个图
graph = StateGraph()
# 添加节点
graph.add_node("start")
graph.add_node("process")
graph.add_node("end")
# 添加边
graph.add_edge("start", "process")
graph.add_edge("process", "end")
# 运行图
graph.run()
在这个例子中,我们定义了一个简单的图,它包含三个节点和两条边。当我们运行这个图时,它会按照预定的顺序执行节点。
通过理解这些基本概念,开发者可以更好地利用LangGraph构建复杂的智能体应用。
LangGraph的驱动力
2.1 链(Chain)的局限性
在LangChain中,链(Chain)是一种基本的构建块,用于将多个LLM(语言模型)调用和工具调用链接在一起。然而,链在处理复杂、动态的对话流程时存在一些局限性:
- 线性流程:链通常是线性的,这意味着它们只能按照预定义的顺序执行步骤。这种线性结构限制了在对话中进行动态路由和条件分支的能力。
- 状态管理:链在处理多轮对话时,状态管理变得复杂。每次调用链时,都需要手动传递和更新状态,这增加了代码的复杂性和出错的可能性。
- 工具集成:虽然链可以调用外部工具,但在链的结构中集成和协调多个工具的使用并不直观,尤其是在需要根据对话上下文动态选择工具时。
2.2 AgentExecutor的局限性
AgentExecutor是LangChain中用于执行代理(Agent)的组件,它允许代理根据输入动态选择工具和操作。尽管AgentExecutor提供了一定的灵活性,但它仍然存在一些局限性:
- 复杂性:AgentExecutor的配置和使用相对复杂,尤其是在处理复杂的对话流程和多轮对话时。需要手动管理代理的状态和工具调用,这增加了开发的难度。
- 动态路由:AgentExecutor虽然支持动态选择工具,但在处理复杂的条件分支和动态路由时,仍然不够灵活。缺乏一种直观的方式来定义和执行复杂的对话流程。
- 状态持久性:AgentExecutor在处理长时间运行的对话时,缺乏内置的状态持久性机制。每次对话重启时,都需要从头开始,无法恢复之前的对话状态。
2.3 LangGraph诞生的动力
面对链和AgentExecutor的局限性,LangGraph应运而生。LangGraph的设计目标是解决这些局限性,提供一个更灵活、更强大的框架来构建复杂的智能体应用:
- 图结构:LangGraph采用图(Graph)结构来表示对话流程,允许开发者定义复杂的非线性流程和条件分支。这种图结构提供了更大的灵活性,使得动态路由和条件分支变得直观和简单。
- 状态管理:LangGraph内置了强大的状态管理机制,可以无缝地管理多轮对话的状态。开发者无需手动传递和更新状态,LangGraph会自动处理状态的持久化和恢复。
- 工具集成:LangGraph简化了工具的集成和使用,开发者可以轻松地将多个工具集成到对话流程中,并根据对话上下文动态选择和调用工具。
- 持久性:LangGraph提供了内置的状态持久性机制,支持长时间运行的对话。开发者可以随时暂停和恢复对话,无需担心状态丢失。
通过这些特性,LangGraph使得构建复杂、可扩展的智能体应用变得更加容易和高效。
LangGraph的设计思想
3.1 LangGraph的基本概念
LangGraph是一个用于构建复杂、可扩展AI代理的Python库,它使用基于图的状态机来管理和执行复杂的任务流程。LangGraph的核心概念包括:
- State(状态):表示应用程序当前的快照,可以是任何Python类型,但通常是TypedDict或Pydantic BaseModel。
- Nodes(节点):Python函数,接收当前状态作为输入,执行某些计算或副作用,并返回更新后的状态。
- Edges(边):控制流规则,决定基于当前状态的下一个要执行的节点。可以是条件分支或固定过渡。
通过组合Nodes和Edges,可以创建复杂的、循环的工作流,这些工作流会随着时间的推移而演化状态。
3.2 StateGraph的构建
StateGraph是LangGraph中的核心组件,用于定义和管理状态机的执行流程。构建StateGraph的基本步骤包括:
-
初始化StateGraph:
from langgraph.graph import StateGraph builder = StateGraph(dict)
-
定义节点(Nodes):
def my_node(state: dict): return { "results": f"Hello, { state['input']}!"} builder.add_node("my_node", my_node)
-
定义边(Edges):
builder.add_edge(START, "my_node") builder.add_edge("my_node", END)
3.3 Nodes与Edges的定义
Nodes的定义
Nodes是StateGraph中的基本执行单元,通常是Python函数。以下是一个简单的节点定义示例:
def my_node(state: dict):
print("In node:", state)
return {
"results": f"Hello, {
state['input']}!"}
Edges的定义
Edges定义了节点之间的控制流。以下是一个简单的边定义示例:
builder.add_edge(START, "my_node")
builder.add_edge("my_node", "other_node")
builder.add_edge("other_node", END)
3.4 编译与运行应用
在定义了所有的Nodes和Edges之后,需要编译StateGraph并运行应用。以下是编译和运行StateGraph的步骤:
-
编译StateGraph:
graph = builder.compile()
-
运行StateGraph:
result = graph.invoke({ "input": "Will"}) print(result) # 输出: {'results': 'Hello, Will!'}
通过上述步骤,可以构建一个简单的StateGraph并运行它。LangGraph的强大之处在于它可以处理复杂的、循环的工作流,并且能够管理状态的持久性和恢复。
通过深入理解LangGraph的基本概念、StateGraph的构建、Nodes与Edges的定义以及编译与运行应用的流程,开发者可以利用LangGraph构建复杂的、可扩展的AI代理应用。
LangGraph构建基础Agent
4.1 基础Agent的Graph实现
在LangGraph中,构建基础Agent的核心在于理解如何将Agent的行为和状态管理通过图(Graph)的形式进行组织和协调。LangGraph通过StateGraph的概念,允许开发者定义一个状态对象,并通过节点(Nodes)和边(Edges)来管理状态的更新和流转。
状态对象的定义
首先,我们需要定义一个状态对象,这个对象将作为图的状态传递给每个节点。对于基础Agent,我们通常会跟踪一些基本的状态信息,例如消息列表。
from typing import TypedDict, Annotated, Sequence
import operator
from langchain_core.messages import BaseMessage
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
在这个例子中,AgentState
是一个TypedDict
,其中包含一个messages
键,其值是一个消息列表。我们使用Annotated
和operator.add
来确保每次状态更新时,消息列表都会被添加新的消息,而不是被覆盖。
节点的定义
接下来,我们需要定义图中的节点。每个节点可以是一个函数,负责处理特定的任务。对于基础Agent,我们通常需要以下几个节点:
- Agent节点:负责决定下一步要采取的行动。
- 工具调用节点:如果Agent决定采取行动,这个节点将执行该行动。
from langgraph.prebuilt import ToolInvocation
import json
from langchain_core.messages import FunctionMessage
# 定义Agent节点
def call_model(state):
messages = state['messages']
response = model.invoke(messages)
return