在 LangGraph 中使用 工具(Tools) 以及 ReAct 代理(ReAct Agent)创建一个基于 ReAct 机制的智能代理(Agent),理论版
示例代码:
from typing import List
from langchain_core.tools import tool
from langchain_core.runnables.config import RunnableConfig
import os
from dotenv import load_dotenv
import yaml
from pyprojroot import here
from langchain_openai import ChatOpenAI
load_dotenv()
os.environ['OPENAI_API_KEY'] = os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_API_BASE'] = os.getenv("OPENAI_API_BASE")
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
OPENAI_API_BASE= os.environ.get("OPENAI_API_BASE")
from langchain_openai import ChatOpenAI
chat = ChatOpenAI(
openai_api_base=OPENAI_API_BASE,
openai_api_key=OPENAI_API_KEY,
# tream=True,
temperature=0)
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello, how are you?"}
]
# Call the model with the messages
response = chat.invoke(messages)
# Print the response
print(response)
print('\n')
print(response.content)
from typing import List
from langchain_core.tools import tool
from langchain_core.runnables.config import RunnableConfig
from langgraph.prebuilt import ToolNode
user_to_pets = {}
@tool(parse_docstring=True)
def update_favorite_pets(
# NOTE: config arg does not need to be added to docstring, as we don't want it to be included in the function signature attached to the LLM
pets: List[str],
config: RunnableConfig,
) -> None:
"""Add the list of favorite pets.
Args:
pets: List of favorite pets to set.
"""
user_id = config.get("configurable", {}).get("user_id")
user_to_pets[user_id] = pets
@tool
def delete_favorite_pets(config: RunnableConfig) -> None:
"""Delete the list of favorite pets."""
user_id = config.get("configurable", {}).get("user_id")
if user_id in user_to_pets:
del user_to_pets[user_id]
@tool
def list_favorite_pets(config: RunnableConfig) -> None:
"""List favorite pets if asked to."""
user_id = config.get("configurable", {}).get("user_id")
return ", ".join(user_to_pets.get(user_id, []))
tools = [update_favorite_pets, delete_favorite_pets, list_favorite_pets]
user_to_pets = {}
from langgraph.prebuilt import create_react_agent
from IPython.display import Image, display
graph = create_react_agent(chat, tools)
try:
display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
pass
from langchain_core.messages import HumanMessage
user_to_pets.clear() # Clear the state
print(f"User information prior to run: {user_to_pets}")
inputs = {"messages": [HumanMessage(content="my favorite pets are cats and dogs")]}
for chunk in graph.stream(
inputs, {"configurable": {"user_id": "123"}}, stream_mode="values"
):
chunk["messages"][-1].pretty_print()
print(f"User information after the run: {user_to_pets}")
from langchain_core.messages import HumanMessage
print(f"User information prior to run: {user_to_pets}")
inputs = {"messages": [HumanMessage(content="what are my favorite pets")]}
for chunk in graph.stream(
inputs, {"configurable": {"user_id": "123"}}, stream_mode="values"
):
chunk["messages"][-1].pretty_print()
print(f"User information prior to run: {user_to_pets}")
print(f"User information prior to run: {user_to_pets}")
inputs = {
"messages": [
HumanMessage(content="please forget what i told you about my favorite animals")
]
}
for chunk in graph.stream(
inputs, {"configurable": {"user_id": "123"}}, stream_mode="values"
):
chunk["messages"][-1].pretty_print()
print(f"User information prior to run: {user_to_pets}")
下面我将详细解释这段代码的每个部分,并用简单的例子说明其作用。
1. 导入依赖库和加载环境变量
from typing import List
from langchain_core.tools import tool
from langchain_core.runnables.config import RunnableConfig
- 解释:
List
用于类型提示,说明某个变量或者函数参数是一个列表(例如字符串列表)。tool
是一个装饰器,用来将普通函数包装成工具,使得语言模型(LLM)在处理对话时可以调用这些函数。RunnableConfig
用于传递一些运行时配置,比如用户信息。
import os
from dotenv import load_dotenv
import yaml
from pyprojroot import here
from langchain_openai import ChatOpenAI
load_dotenv()
- 解释:
os
模块用于和操作系统交互,例如操作环境变量。load_dotenv()
会自动从项目根目录下的.env
文件中加载环境变量,这样你就可以在代码中使用 API 密钥等敏感信息。yaml
和pyprojroot.here
分别用于解析 YAML 配置和确定项目根目。ChatOpenAI
是一个与 OpenAI 聊天模型交互的封装类。
os.environ['OPENAI_API_KEY'] = os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_API_BASE'] = os.getenv("OPENAI_API_BASE")
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
OPENAI_API_BASE= os.environ.get("OPENAI_API_BASE")
- 解释:
- 从环境变量中获取
OPENAI_API_KEY
和OPENAI_API_BASE
,并把它们设置到os.environ
中,确保程序运行时可以正确使用这些密钥。 - 同时把它们存入变量
OPENAI_API_KEY
和OPENAI_API_BASE
,后面创建 ChatOpenAI 实例时会用到。
- 从环境变量中获取
- 举例说明:假如你的
.env
文件中包含一行OPENAI_API_KEY=abcdef123456
,那么加载后OPENAI_API_KEY
变量就会保存这个值,用于身份验证。
2. 创建 ChatOpenAI 实例并发送初始消息
from langchain_openai import ChatOpenAI
chat = ChatOpenAI(
openai_api_base=OPENAI_API_BASE,
openai_api_key=OPENAI_API_KEY,
# tream=True,
temperature=0)
- 解释:
- 利用上一步获取的 API 基础地址和密钥,创建一个
ChatOpenAI
对象,这个对象负责与 OpenAI 的 API 进行通信。 - 参数
temperature=0
表示生成回复时没有随机性,保证输出结果稳定。 - 注释掉的
# tream=True
是希望开启流式输出,但这里没有启用。
- 利用上一步获取的 API 基础地址和密钥,创建一个
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello, how are you?"}
]
- 解释:
- 定义了一个消息列表,告诉模型如何扮演角色。
- 第一条消息是系统消息,设定了助手的行为(“你是一个乐于助人的助手”);第二条消息是用户问候。
response = chat.invoke(messages)
- 解释:
- 将消息列表传递给
ChatOpenAI
对象,并调用invoke
方法,从模型那里获得回复。
- 将消息列表传递给
print(response)
print('\n')
print(response.content)
- 解释:
- 打印整个回复对象(可能包含元数据)以及仅打印回复的文本内容。
- 举例说明:如果模型回复
"I'm doing well, thank you!"
,则response.content
就显示这句话,而print(response)
可能还会包含其它调试信息。
3. 定义管理用户喜爱宠物信息的工具函数
首先,定义一个全局变量来存储每个用户的宠物信息:
user_to_pets = {}
- 解释:
- 这个空字典用于保存用户与他们喜爱宠物的对应关系,比如形如
{"123": ["cats", "dogs"]}
。
- 这个空字典用于保存用户与他们喜爱宠物的对应关系,比如形如
3.1 更新宠物列表的函数
@tool(parse_docstring=True)
def update_favorite_pets(
# NOTE: config arg does not need to be added to docstring, as we don't want it to be included in the function signature attached to the LLM
pets: List[str],
config: RunnableConfig,
) -> None:
"""Add the list of favorite pets.
Args:
pets: List of favorite pets to set.
"""
user_id = config.get("configurable", {}).get("user_id")
user_to_pets[user_id] = pets
- 解释:
- 使用
@tool(parse_docstring=True)
装饰器,将这个函数注册为工具,方便后续由语言模型调用。 - 参数
pets
是一个字符串列表,代表用户喜爱的宠物。 - 参数
config
传递额外的配置信息(例如用户的 ID)。 - 函数内部通过
config.get("configurable", {}).get("user_id")
提取用户 ID,并将传入的宠物列表存入全局字典user_to_pets
中。
- 使用
- 举例说明:当调用
update_favorite_pets
时,如果传入pets=["cats", "dogs"]
,且config={"configurable": {"user_id": "123"}}
,那么字典user_to_pets
将更新为{"123": ["cats", "dogs"]}
。
3.2 删除宠物列表的函数
@tool
def delete_favorite_pets(config: RunnableConfig) -> None:
"""Delete the list of favorite pets."""
user_id = config.get("configurable", {}).get("user_id")
if user_id in user_to_pets:
del user_to_pets[user_id]
- 解释:
- 同样通过
@tool
装饰器将这个函数变成一个工具。 - 仅通过传入的
config
参数获得用户 ID,并检查该用户是否在user_to_pets
中;如果存在,则删除其对应的宠物信息。
- 同样通过
- 举例说明:假如用户 “123” 已经存储了宠物列表,调用这个函数后就会删除
{"123": [...]}
这一项。
3.3 查询宠物列表的函数
@tool
def list_favorite_pets(config: RunnableConfig) -> None:
"""List favorite pets if asked to."""
user_id = config.get("configurable", {}).get("user_id")
return ", ".join(user_to_pets.get(user_id, []))
- 解释:
- 该函数用于返回当前用户的喜爱宠物信息。
- 通过
config
获取用户 ID,然后从user_to_pets
中查找该用户的宠物列表,最后将列表转换成用逗号分隔的字符串返回。
- 举例说明:如果用户 “123” 的宠物列表是
["cats", "dogs"]
,那么函数返回的字符串就是"cats, dogs"
。
tools = [update_favorite_pets, delete_favorite_pets, list_favorite_pets]
user_to_pets = {}
- 解释:
- 将上面定义的三个工具函数放入列表
tools
,方便后面传递给代理。 - 重新清空
user_to_pets
,以确保状态干净。
- 将上面定义的三个工具函数放入列表
4. 创建 React Agent 并显示工作图
from langgraph.prebuilt import create_react_agent
from IPython.display import Image, display
graph = create_react_agent(chat, tools)
- 解释:
- 这里调用
create_react_agent
函数,根据之前创建的chat
模型实例和工具列表tools
构造一个“反应式代理”(react agent)。 - 这个代理可以根据用户的输入自动决定是否调用某个工具函数,从而管理内部状态(例如用户的宠物信息)。
- 这里调用
try:
display(Image(graph.get_graph().draw_mermaid_png()))
except Exception:
pass
- 解释:
- 尝试将代理内部的工作流程图转换为 Mermaid 格式的 PNG 图片,并在 Jupyter Notebook 中显示。如果环境不支持(例如在非 Notebook 环境中),则忽略异常不报错。
5. 使用 Agent 处理对话并测试工具
5.1 设置(更新)用户的宠物信息
from langchain_core.messages import HumanMessage
user_to_pets.clear() # Clear the state
print(f"User information prior to run: {user_to_pets}")
inputs = {"messages": [HumanMessage(content="my favorite pets are cats and dogs")]}
for chunk in graph.stream(
inputs, {"configurable": {"user_id": "123"}}, stream_mode="values"
):
chunk["messages"][-1].pretty_print()
print(f"User information after the run: {user_to_pets}")
- 解释:
- 首先调用
user_to_pets.clear()
清空之前的状态,确保没有旧数据干扰。 - 打印当前的用户信息(此时应该是空字典
{}
)。 - 创建一个输入,消息内容为
"my favorite pets are cats and dogs"
,表示用户在告诉代理自己的喜好。 - 调用
graph.stream(...)
方法:- 传入 输入消息 和一个 配置字典
{"configurable": {"user_id": "123"}}
,这样代理在处理消息时知道这是用户 “123”。 - 通过流式处理,每个返回的消息块都会调用
pretty_print()
方法进行格式化输出。
- 传入 输入消息 和一个 配置字典
- 最后,再次打印
user_to_pets
,此时应该已经更新为{"123": ["cats", "dogs"]}
。
- 首先调用
- 举例说明:你在聊天中说“my favorite pets are cats and dogs”,代理就会调用
update_favorite_pets
,将用户 “123” 的宠物信息存储下来。
5.2 查询用户的宠物信息
from langchain_core.messages import HumanMessage
print(f"User information prior to run: {user_to_pets}")
inputs = {"messages": [HumanMessage(content="what are my favorite pets")]}
for chunk in graph.stream(
inputs, {"configurable": {"user_id": "123"}}, stream_mode="values"
):
chunk["messages"][-1].pretty_print()
print(f"User information prior to run: {user_to_pets}")
- 解释:
- 这段代码发送一条消息
"what are my favorite pets"
,询问当前用户喜爱的宠物。 - 代理在接收到这条消息后,会调用
list_favorite_pets
工具函数,根据用户 ID “123” 查找并返回宠物列表。
- 这段代码发送一条消息
- 举例说明:如果之前已存储了
["cats", "dogs"]
,代理就会回答"cats, dogs"
。
5.3 删除(遗忘)用户的宠物信息
print(f"User information prior to run: {user_to_pets}")
inputs = {
"messages": [
HumanMessage(content="please forget what i told you about my favorite animals")
]
}
for chunk in graph.stream(
inputs, {"configurable": {"user_id": "123"}}, stream_mode="values"
):
chunk["messages"][-1].pretty_print()
print(f"User information prior to run: {user_to_pets}")
- 解释:
- 最后一部分发送消息
"please forget what i told you about my favorite animals"
,要求代理删除之前存储的宠物信息。 - 代理解析这条消息后,会调用
delete_favorite_pets
工具函数,删除用户 “123” 的记录。 - 最终打印时,
user_to_pets
应该为空{}
,表示信息已被删除。
- 最后一部分发送消息
- 举例说明:当你说“please forget what i told you about my favorite animals”,系统会清除之前存储的宠物信息,相当于把记忆清空。
总结
这段代码实现了一个基于 OpenAI 模型的对话系统,并结合了自定义工具来管理用户状态(这里是用户喜欢的宠物信息)。其主要功能包括:
- 初始化与环境配置:加载必要的库,读取环境变量,创建 OpenAI 聊天模型实例。
- 工具函数定义:定义三个工具函数:
update_favorite_pets
用于存储或更新用户的宠物列表。delete_favorite_pets
用于删除用户的宠物记录。list_favorite_pets
用于查询用户的宠物列表。
- ****创建代理(Agent):将聊天模型和工具函数结合起来,构造一个能够在对话中根据用户意图自动调用工具的代理。
- 对话与状态管理演示:
- 用户通过对话告诉系统自己的宠物喜好,系统存储该信息;
- 后续查询时,系统根据存储返回相应数据;
- 当用户要求“忘记”信息时,系统清除存储的数据。
通俗例子:
- 你第一次告诉代理 “my favorite pets are cats and dogs”,系统就把这条信息记在了“123”号用户的档案里。
- 然后你问 “what are my favorite pets”,系统就会查找并回复 “cats, dogs”。
- 最后你说 “please forget what i told you about my favorite animals”,系统就删除了这个档案,恢复到初始状态。
通过这种设计,不仅实现了对话功能,还能够在对话中管理状态,让模型的回答和操作更加灵活和有针对性。