如何从头开始创建一个 ReAct Agent (功能 API)¶
本指南将使用 LangGraph 功能 API 演示如何实现 ReAct Agent。
ReAct Agent 是一个 工具调用 Agent,其工作流程如下:
这是一个简单且功能丰富的设置,可以扩展内存、人工干预功能以及其他特性。请参阅专门的操作指南以获取示例。
设置¶
首先,让我们安装所需的软件包并设置我们的 API 密钥:
import getpass
import os
def _set_env(var: str):
if not os.environ.get(var):
os.environ[var] = getpass.getpass(f"{var}: ")
_set_env("OPENAI_API_KEY")
设置 LangSmith 以便更好地调试
注册 LangSmith,以便快速发现问题并提高 LangGraph 项目的性能。LangSmith 允许您使用跟踪数据来调试、测试和监控您使用 LangGraph 构建的 LLM 应用 — 了解更多关于如何开始的信息,请参阅 文档。
创建 ReAct Agent¶
现在你已经安装了所需的包并设置了环境变量,我们就可以创建 Agent 了。
定义模型和工具¶
首先,我们来定义示例将要使用的工具和模型。在这个示例中,我们将使用一个占位工具,它能获取某个地点的天气描述。
我们将在本例中使用 OpenAI 聊天模型,但任何 支持工具调用的 模型都可以。
API Reference: ChatOpenAI | tool
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
model = ChatOpenAI(model="gpt-4o-mini")
@tool
def get_weather(location: str):
"""Call to get the weather from a specific location."""
# This is a placeholder for the actual implementation
if any([city in location.lower() for city in ["sf", "san francisco"]]):
return "It's sunny!"
elif "boston" in location.lower():
return "It's rainy!"
else:
return f"I am not sure what the weather is in {location}"
tools = [get_weather]
定义任务¶
接下来我们定义要执行的 任务。这里有两个不同的任务:
- 调用模型: 我们想用一系列消息来查询我们的聊天模型。
- 调用工具: 如果我们的模型生成了工具调用,我们希望执行它们。
API Reference: ToolMessage | entrypoint | task
from langchain_core.messages import ToolMessage
from langgraph.func import entrypoint, task
tools_by_name = {tool.name: tool for tool in tools}
@task
def call_model(messages):
"""Call model with a sequence of messages."""
response = model.bind_tools(tools).invoke(messages)
return response
@task
def call_tool(tool_call):
tool = tools_by_name[tool_call["name"]]
observation = tool.invoke(tool_call["args"])
return ToolMessage(content=observation, tool_call_id=tool_call["id"])
定义入口点¶
我们的 入口点 将负责协调这两个任务。如上所述,当我们的 call_model
任务生成工具调用时,call_tool
任务将为每个调用生成响应。我们将所有消息追加到单个消息列表中。
Tip
请注意,由于任务返回类似 future 的对象,因此下面的实现会并行执行工具。
API Reference: add_messages
from langgraph.graph.message import add_messages
@entrypoint()
def agent(messages):
llm_response = call_model(messages).result()
while True:
if not llm_response.tool_calls:
break
# Execute tools
tool_result_futures = [
call_tool(tool_call) for tool_call in llm_response.tool_calls
]
tool_results = [fut.result() for fut in tool_result_futures]
# Append to message list
messages = add_messages(messages, [llm_response, *tool_results])
# Call model again
llm_response = call_model(messages).result()
return llm_response
用法¶
要使用我们的 Agent,我们需要通过消息列表来调用它。根据我们的实现,这些可以是 LangChain 的 message 对象或 OpenAI 风格的字典:
user_message = {"role": "user", "content": "What's the weather in san francisco?"}
print(user_message)
for step in agent.stream([user_message]):
for task_name, message in step.items():
if task_name == "agent":
continue # Just print task updates
print(f"\n{task_name}:")
message.pretty_print()
{'role': 'user', 'content': "What's the weather in san francisco?"}
call_model:
================================== Ai Message ==================================
Tool Calls:
get_weather (call_tNnkrjnoz6MNfCHJpwfuEQ0v)
Call ID: call_tNnkrjnoz6MNfCHJpwfuEQ0v
Args:
location: san francisco
call_tool:
================================= Tool Message =================================
It's sunny!
call_model:
================================== Ai Message ==================================
The weather in San Francisco is sunny!
get_weather
工具,并在从工具接收到信息后回复了用户。请在这里查看 LangSmith 追踪记录:[https://smith.langchain.com/public/d5a0d5ea-bdaa-4032-911e-7db177c8141b/r](https://smith.langchain.com/public/d5a0d5ea-bdaa-4032-911e-7db177c8141b/r
添加线程级持久化¶
添加 线程级持久化 让我们能够支持与代理的对话式体验:后续调用将追加到先前的消息列表中,保留完整的对话上下文。
要为我们的代理添加线程级持久化:
- 选择一个 checkpointer:这里我们将使用 MemorySaver,一个简单的内存中 checkpointer。
- 更新我们的入口点,使其接受前一个消息状态作为第二个参数。在这里,我们只是将消息更新追加到前一个消息序列中。
- 使用
entrypoint.final
选择哪些值将从工作流返回,哪些将由 checkpointer 作为previous
保存(可选)
API Reference: MemorySaver
from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()
@entrypoint(checkpointer=checkpointer)
def agent(messages, previous):
if previous is not None:
messages = add_messages(previous, messages)
llm_response = call_model(messages).result()
while True:
if not llm_response.tool_calls:
break
# Execute tools
tool_result_futures = [
call_tool(tool_call) for tool_call in llm_response.tool_calls
]
tool_results = [fut.result() for fut in tool_result_futures]
# Append to message list
messages = add_messages(messages, [llm_response, *tool_results])
# Call model again
llm_response = call_model(messages).result()
# Generate final response
messages = add_messages(messages, llm_response)
return entrypoint.final(value=llm_response, save=messages)
现在,我们需要在运行应用程序时传入一个配置。该配置将指定对话线程的标识符。
我们像之前一样启动一个线程,这次传入配置:
user_message = {"role": "user", "content": "What's the weather in san francisco?"}
print(user_message)
for step in agent.stream([user_message], config):
for task_name, message in step.items():
if task_name == "agent":
continue # Just print task updates
print(f"\n{task_name}:")
message.pretty_print()
{'role': 'user', 'content': "What's the weather in san francisco?"}
call_model:
================================== Ai Message ==================================
Tool Calls:
get_weather (call_lubbUSdDofmOhFunPEZLBz3g)
Call ID: call_lubbUSdDofmOhFunPEZLBz3g
Args:
location: San Francisco
call_tool:
================================= Tool Message =================================
It's sunny!
call_model:
================================== Ai Message ==================================
The weather in San Francisco is sunny!
user_message = {"role": "user", "content": "How does it compare to Boston, MA?"}
print(user_message)
for step in agent.stream([user_message], config):
for task_name, message in step.items():
if task_name == "agent":
continue # Just print task updates
print(f"\n{task_name}:")
message.pretty_print()
{'role': 'user', 'content': 'How does it compare to Boston, MA?'}
call_model:
================================== Ai Message ==================================
Tool Calls:
get_weather (call_8sTKYAhSIHOdjLD5d6gaswuV)
Call ID: call_8sTKYAhSIHOdjLD5d6gaswuV
Args:
location: Boston, MA
call_tool:
================================= Tool Message =================================
It's rainy!
call_model:
================================== Ai Message ==================================
Compared to San Francisco, which is sunny, Boston, MA is experiencing rainy weather.