LangGraph 深度学习教案
目录
- 引言
- LangChain 与 LangGraph 的关系
- 为什么选择 LangGraph
- LangGraph 基础概念
- 节点(Nodes)
- 边(Edges)
- 状态(State)
- 环境配置
- 安装必要的库
- 配置数据库和模型
- 构建单代理工作流程
- 定义 SQL 执行工具
- 定义代理状态
- 构建代理
- 可视化代理图
- 测试代理
- 持久化和流式处理
- 持久化状态
- 流式处理数据
- 多线程交互
- 构建多代理系统
- 定义多代理状态
- 构建路由器节点
- 构建专家节点
- 构建编辑器和人类反馈节点
- 测试多代理系统
- 总结
1. 引言
LangChain 与 LangGraph 的关系
LangChain 是一个用于构建由大型语言模型(LLM)驱动的应用程序的领先框架。它允许开发者使用 LangChain 表达语言(LCEL)来定义和执行操作序列,这些序列被称为链(Chains)。这些链可以视为有向无环图(DAG),用于线性执行任务。
然而,随着 LLM 应用程序的发展,特别是在构建代理(Agents)时,我们需要更复杂的交互,包括循环、条件逻辑和多步骤推理。这就引入了 LangGraph,它是 LangChain 的一个新模块,允许我们将交互建模为循环图,从而支持更复杂的工作流和代理行为。
为什么选择 LangGraph
LangGraph 提供了以下优势:
- 灵活性:支持循环和条件逻辑,能够构建复杂的对话和推理流程。
- 模块化设计:通过节点和边的组合,可以轻松地添加或修改代理的行为。
- 持久化和恢复:状态在每个步骤后自动保存,允许在任何时刻暂停和恢复执行,这对于需要人机交互的应用程序非常重要。
2. LangGraph 基础概念
在开始实际编码之前,我们需要理解 LangGraph 的三个核心概念:节点、边和状态。
节点(Nodes)
节点代表实际的操作,可以是以下之一:
- LLM 调用:调用大型语言模型进行推理或生成文本。
- 代理:可以是另一个嵌套的代理,负责特定任务。
- 函数或工具:执行特定的功能,如数据库查询、API 调用等。
边(Edges)
边连接节点,确定图的执行流程。边有两种类型:
- 基本边:简单地将一个节点连接到下一个节点,表示顺序执行。
- 条件边:包含条件逻辑,根据状态决定下一步执行哪个节点。
状态(State)
状态是图的快照,包含执行过程中所有需要共享或持久化的数据。状态在节点之间传递,并可以被节点读取或修改。状态的设计对于代理的正确执行至关重要,因为它允许节点之间共享信息,并支持复杂的交互。
3. 环境配置
在开始编写代码之前,我们需要设置开发环境,安装必要的库,并配置模型和数据库。
安装必要的库
确保您的计算机上安装了 Python 3.6 或更高版本。
# 安装 langchain_community
pip install langchain_community
# 更新安装 langgraph
pip install -U langgraph
# 安装 graphviz(用于绘制图形)
brew install graphviz # 如果您使用的是 macOS
# 安装 pygraphviz(用于在 Python 中使用 graphviz)
pip install pygraphviz
注意:在安装 pygraphviz 时,可能需要指定编译选项,以正确找到 graphviz 的头文件和库文件。如果遇到安装问题,请参考 pygraphviz 的安装指南。
配置数据库和模型
在本教案中,我们将使用 ClickHouse 数据库和 OpenAI 的 GPT-4 模型(或其替代模型)。
import os
import json
# 读取配置文件(假设您有一个包含 API 密钥的 config.json 文件)
with open('config.json') as f:
config = json.loads(f.read())
# 设置环境变量
os.environ["OPENAI_MODEL_NAME"] = 'gpt-4o-mini' # 或者您选择的模型
os.environ["OPENAI_API_KEY"] = config['OPENAI_API_KEY']
os.environ["TAVILY_API_KEY"] = config["TAVILY_API_KEY"]
4. 构建单代理工作流程
在这一部分中,我们将构建一个简单的代理,能够根据用户输入执行 SQL 查询,并返回结果。
4.1 定义 SQL 执行工具
首先,我们需要定义一个工具,用于执行 SQL 查询。
from langchain_core.tools import tool
from pydantic.v1 import BaseModel, Field
# 定义 SQL 查询的参数模型
class SQLQuery(BaseModel):
query: str = Field(description="要执行的 SQL 查询")
# 定义执行 SQL 的工具
@tool(args_schema=SQLQuery)
def execute_sql(query: str) -> str:
"""返回 SQL 查询执行的结果"""
return get_clickhouse_data(query)
我们还需要定义 get_clickhouse_data 函数,用于实际执行 SQL 查询。
CH_HOST = 'http://localhost:8123' # ClickHouse 默认地址
import requests
def get_clickhouse_data(query, host=CH_HOST, connection_timeout=1500):
r = requests.post(host, params={
'query': query}, timeout=connection_timeout)
if r.status_code == 200:
return r.text
else:
return '数据库返回以下错误:\n' + r.text
说明:
- 我们使用
@tool装饰器将execute_sql函数注册为 LangChain 工具。 SQLQuery是一个 Pydantic 模型,用于定义工具的参数和描述,帮助 LLM 正确地调用工具。execute_sql函数调用了get_clickhouse_data来执行实际的 SQL 查询。
4.2 定义代理状态
接下来,我们需要定义代理的状态,以便在节点之间共享信息。
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated
import operator
from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage, ToolMessage
# 定义代理状态
class AgentState(TypedDict):
messages: Annotated[list[AnyMessage], operator.add]
说明:
AgentState是一个字典类型,包含一个messages列表,用于存储对话历史。Annotated和operator.add用于指示在状态更新时,新的消息将添加到列表中,而不是替换。
4.3 构建代理
现在,我们开始构建代理,定义其节点和执行逻辑。
class SQLAgent:
def __init__(self, model, tools, system_prompt=""):
self.system_prompt = system_prompt
graph = StateGraph(AgentState)
# 添加节点
graph.add_node("llm", self.call_llm)
graph.add_node("function", self.execute_function)
# 添加条件边:如果存在函数调用,则执行函数节点,否则结束
graph.add_conditional_edges(
"llm",
self.exists_function_calling,
{
True: "function", False: END}
)
# 添加边:从函数节点回到 llm 节点
graph.add_edge("function", "llm")
# 设置起点
graph.set_entry_point("llm")
# 编译图
self.graph = graph.compile()
self.tools = {
t.name: t for t in tools}
self.model = model.bind_tools(tools)
# 检查是否存在函数调用
def exists_function_calling(self, state: AgentState):
result = state['messages'][-1]
return len(result.tool_calls) > 0
# 调用 LLM
def call_llm(self, state: AgentState):
messages = state['messages']
if self.system_prompt:
messages = [SystemMessage(content=self.system_prompt)] + messages
message = self.model.invoke(messages)
return {
'messages': [message]}
# 执行函数调用
def execute_function(self, state: AgentState):
tool_calls = state['messages'][-1].tool_calls
results = []
for t in tool_calls:
if not t['name'] in self.tools:
result = "错误:没有这样的工具,请重试"
else:
result = self.tools[t['name']].invoke(t

最低0.47元/天 解锁文章
1011

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



