五、AIGC大模型_09手动实现ReAct_Agent
0、前言
在上一章节中,我们了解到:create_react_agent
是 LangGraph 提供的一个预构建方法(from langgraph.prebuilt import create_react_agent),它可以将语言模型(LLM)和一组工具(Tools)结合起来,创建一个能够根据用户输入自动调用工具的智能代理,这个代理可以根据用户的请求,决定是否需要调用某个工具,并将工具的输出反馈给用户
这个函数如此好用,那么,其实现原理是什么,我们能否自定义方法实现ReAct_Agent呢?
本文基于此问题,介绍如何参考LangGraph的知识体系来手动实现ReAct_Agent
1、LangGraph基本介绍
1.1 定义
LangGraph 是一种基于图结构(Graph)的工作流构建工具,它通过定义节点(Node)、边(Edge)和状态(State)来实现复杂任务的自动化和流程化,这种工具通常用于构建智能代理(Agent)、自动化工作流或复杂任务的调度系统
官网:Home
1.2 相关概念
1.2.1 节点(Node)
节点是工作流中的基本单元,每个节点代表一个具体的动作或操作,节点可以分为以下几种类型:
(1)Tool 节点
- 功能:调用外部工具或函数
- 示例:
- 调用一个 API 获取天气信息
- 调用数据库查询函数
(2)Model 节点
- 功能:调用预训练的模型(如语言模型、图像识别模型等)
- 示例:
- 使用 GPT 模型生成文本
- 使用 YOLO 模型进行目标检测
(3)其他节点
- 除了上述两种常见节点,还可以根据需求定义其他类型的节点,例如:
- 条件节点:用于判断逻辑
- 循环节点:用于重复执行某个操作
- 输入/输出节点:用于接收用户输入或输出结果
1.2.2 边(Edge)
边是连接节点的纽带,用于表示节点之间的状态转移,边可以分为以下两种类型:
(1)固定边
- 功能:无论条件如何,始终从一个节点流向另一个节点
- 示例:从“获取用户输入”节点直接流向“调用模型”节点
(2)条件边
- 功能:根据特定条件决定是否从一个节点流向另一个节点
- 示例:如果模型的输出满足某个条件(如置信度大于 0.8),则流向“成功处理”节点;否则流向“错误处理”节点
1.2.3 状态(State)
状态是节点执行过程中存储的信息,通常用于保存中间结果或上下文信息,最常见的状态类型是 Message State,它用于存储消息或数据
- 示例:
- 在一个对话系统中,Message State 可能存储用户的输入文本、模型的输出文本等
- 在一个数据处理流程中,Message State 可能存储中间计算结果
1.3 LangGraph构建工作流
基于 LangGraph 构建工作流的过程可以分为以下步骤:
Step1:定义节点:根据任务需求,定义需要执行的动作,并将其封装为节点(如 Tool 节点或 Model 节点)
Step2:连接节点:使用边将节点连接起来,定义节点之间的执行顺序和条件。
Step3:配置状态:在每个节点中配置状态,确保信息可以在节点之间正确传递。
Step4:测试与优化:
- 对工作流进行测试,确保所有节点和边的逻辑正确无误
- 根据测试结果优化工作流,调整节点和边的配置
这种基于 LangGraph 的工作流构建方式可以应用于多种场景,例如:
- 智能客服系统:通过定义不同类型的节点(如调用语言模型、调用数据库查询工具)和条件边,实现自动化的问答流程
- 自动化数据处理:通过定义工具节点和模型节点,实现数据的采集、清洗、分析和输出
- 复杂任务调度:通过定义循环节点和条件节点,实现复杂任务的自动化调度
2、使用LangGraph手动实现ReAct_Agent
2.1 构建
# 类型 限制
from typing import Annotated, Literal, TypedDict
# 人类消息
from langchain_core.messages import HumanMessage
# 当做装饰器使用,把一个普通的函数变成一个agent可以调用的工具
from langchain_core.tools import tool
# 消息持久化
from langgraph.checkpoint.memory import MemorySaver
# 引入预定义的一些类或工具
from langgraph.graph import END, START, StateGraph, MessagesState
# 引入一个预编译的工具节点
from langgraph.prebuilt import ToolNode
# 引入一个 datetime
from datetime import datetime
"""
定义获取模型实例的方法(注意DASHSCOPE_API_KEY的环境变量值)
"""
from dotenv import load_dotenv
load_dotenv()
from langchain_community.chat_models import ChatTongyi
def get_chat():
return ChatTongyi(model="qwen-turbo", temperature=0.1)
"""
定义外部工具
"""
@tool
def get_current_datetime() -> str:
"""
查询当前的日期和时间
"""
now = datetime.now()
formatted_date = now.strftime("%Y-%m-%d %H:%M:%S")
return formatted_date
@tool
def get_apple_price(model: str) -> int:
"""
查询苹果手机的价格!
入参:model 是苹果手机的型号,目前,只有如下3个型号 '4s'、'5s'、'6s',其它型号暂不支持
"""
if model == "4s":
return 4000
elif model == "5s":
return 5000
elif model == "6s":
return 6000
else:
raise ValueError("model 是苹果手机的型号,目前,只有如下3个型号 '4s'、'5s'、'6s',其它型号暂不支持")
"""
根据外部方法构建工具节点
"""
call_datetime = ToolNode(tools=[get_current_datetime], name="datetime")
call_apple_price = ToolNode(tools=[get_apple_price], name="apple_price")
# 连接大模型
model = get_chat()
# 绑定工具节点
model_with_tools = model.bind_tools(tools=[get_apple_price, get_current_datetime])
"""
定义一个条件边
"""
def should_continue(state: MessagesState) -> Literal["datetime", "apple_price", END]:
# 获取当前的消息
messages = state['messages']
# 取出最后一条消息
last_message = messages[-1]
# 如果大模型发出工具调用请求,我们就转向 工具节点
# print(last_message)
if not last_message.tool_calls:
return END
elif last_message.tool_calls[0]["name"] == "get_current_datetime":
return "datetime"
else:
return "apple_price"
"""
定义调用模型的函数
"""
def call_model(state: MessagesState):
# 取出消息列表(所有的历史对话)
messages = state['messages']
# 调用大模型
response = model_with_tools.invoke(messages)
# 只需要通过列表形式返回当前这一步的消息即可
# 系统会自动把当前这一步的消息追加到 系统状态中
return {"messages": [response]}
"""
搭建工作流
"""
# 定义一个新的图
workflow = StateGraph(MessagesState)
# 添加一个大模型节点
workflow.add_node(node="model", action=call_model)
# 添加一个查询日期和时间的节点
workflow.add_node(node="datetime", action=call_datetime)
# 添加一个查询苹果手机价格节点
workflow.add_node(node="apple_price", action=call_apple_price)
# 添加一条边
workflow.add_edge(start_key=START, end_key="model")
# 添加一个条件边
workflow.add_conditional_edges(source="model", path=should_continue)
# 工具掉完,都转向大模型
workflow.add_edge(start_key="datetime", end_key='model')
workflow.add_edge(start_key="apple_price", end_key='model')
# 消息持久化
checkpointer = MemorySaver()
# 编译整个图
agent = workflow.compile(checkpointer=checkpointer)
2.2 画图
"""
图形可视化
"""
from IPython.display import Image, display
try:
display(Image(agent.get_graph().draw_mermaid_png()))
except Exception:
print(f"无法显示图形:{e}")
2.3 调用
"""
调用agent
"""
final_state = agent.invoke(input={"messages": [HumanMessage(content="新中国成立多少年,我就买多少部5s,请问我该准备多少钱?")]},
config={"configurable": {"thread_id": 42}})
# 只打印最后一条消息
print("--------只打印最后一条消息--------")
final_message = final_state["messages"][-1].content
print(final_message)
# 打印所有消息
print("--------打印所有消息--------")
for msg in final_state["messages"]:
msg.pretty_print()
3、补充知识
multi-agent-collaboration和plan-and-execute是Agent的两种主流设计思想
-
multi-agent-collaboration官网:Multi-agent network
-
plan-and-execute官网:Plan-and-Execute
3.1 Multi-Agent Collaboration(多智能体协作)
多智能体协作是指多个智能体(如基于大语言模型的智能体)通过分工、协调和合作来共同完成复杂任务,其核心在于通过智能体之间的交互、知识共享和任务分配,实现比单个智能体更高效、更强大的功能
3.1.1 特点
-
分工合作:复杂任务被分解为多个子任务,每个智能体根据自身专长处理特定子任务
-
灵活性与可扩展性:可以根据任务需求动态调整智能体的数量和角色
-
鲁棒性:即使部分智能体失效,系统仍能继续运行
3.1.2 应用场景
- 复杂任务处理:如在模拟环境中完成多步骤任务
- 知识共享:多个智能体共同维护和共享知识库
- 实时交互:适用于需要快速响应的场景,如灾难救援和交通优化
3.2 Plan-and-Execute(计划与执行)
Plan-and-Execute 是一种将任务分解为计划阶段和执行阶段的方法,系统首先制定详细的计划,然后逐步执行每个计划步骤,并在必要时进行重新规划
3.2.1 工作流程
-
计划阶段:系统根据输入生成一个任务计划,将复杂任务分解为多个步骤
-
执行阶段:按照计划逐步执行每个步骤,并记录执行结果
-
重新规划:如果执行过程中出现问题或任务未完成,系统会根据当前状态重新规划
3.2.2 prompt
{instructions}
You have access to the following tools:
{tools}
To use a tool, please use the following format:
Thought: Do I need to use a tool? Yes
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
When you have a response to say to the Human, or if you do not need to use a tool, you MUST use the format:
Thought: Do I need to use a tool? No
Final Answer: [your response here]
Do not attach backticks to any of your outputs, including thoughts, tool calls and final answers.
Begin!
New input: {input}
{agent_scratchpad}
3.2.3 特点
- 结构化流程:通过明确的计划和执行步骤,提高任务处理的清晰度和效率
- 动态调整:能够根据执行结果动态调整计划
- 可扩展性:适用于多领域任务,支持复杂问题的逐步解决
3.2.4 应用场景
- 问答系统:通过计划和执行步骤逐步解决问题
- 复杂任务自动化:如在多步骤任务中,通过动态规划和执行提高效率
3.3 两者的区别与联系
-
区别:
- 多智能体协作侧重于智能体之间的分工与合作,强调通过多个智能体的协同完成任务
- 计划与执行侧重于任务的分解和逐步执行,更注重任务处理的结构化和动态调整
-
联系:
- 两者都可以用于解决复杂任务,且在实际应用中可以结合使用(例如,多智能体系统可以通过计划与执行框架来协调智能体的行动)