一、多智能体监督器简介
在多智能体系统里,监督器有着重要作用。就如同一个团队需要管理者来协调成员工作一样,多智能体监督器负责管理和协调不同智能体之间的任务分配与协作。
上一个示例根据初始研究员智能体的输出自动路由消息,而我们也能够选择使用大语言模型(LLM)来协调不同的智能体。下面我们就来看看如何创建一个智能体组,并使用一个智能体监督器来帮助分配任务。
二、代码实现过程
(一)设置
首先要安装所需的包并设置API密钥。代码如下:
%pip install -U langgraph langchain_community langchain_anthropic langchain_experimental
import getpass
import os
def _set_if_undefined(var: str):
if not os.environ.get(var):
os.environ[var] = getpass.getpass(f"Please provide your {var}")
_set_if_undefined("ANTHROPIC_API_KEY")
_set_if_undefined("TAVILY_API_KEY")
这段代码的作用是安装运行所需的库,并通过用户输入来设置必要的API密钥。
(二)创建工具
在这个示例中,我们要创建一个使用搜索引擎进行网络研究的智能体,以及一个创建图表的智能体。为此定义了相应的工具:
from typing import Annotated
from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_core.tools import tool
from langchain_experimental.utilities import PythonREPL
tavily_tool = TavilySearchResults(max_results=5)
repl = PythonREPL()
@tool
def python_repl_tool(
code: Annotated[str, "The python code to execute to generate your chart."],
):
"""使用此工具执行Python代码并进行数学运算。如果你想查看某个值的输出,应该使用`print(...)`将其打印出来。这对用户是可见的。"""
try:
result = repl.run(code)
except BaseException as e:
return f"执行失败。错误: {repr(e)}"
result_str = f"成功执行:\n```python\n{code}\n```\n标准输出: {result}"
return result_str
这里定义了两个工具,tavily_tool用于网络搜索,python_repl_tool用于执行Python代码来创建图表。不过要注意,python_repl_tool在本地执行代码,存在一定安全风险。
(三)创建智能体监督器
智能体监督器将使用具有结构化输出的大语言模型来选择下一个工作节点或完成处理。代码如下:
from typing import Literal
from typing_extensions import TypedDict
from langchain_anthropic import ChatAnthropic
from langgraph.graph import MessagesState, END
from langgraph.types import Command
members = ["researcher", "coder"]
我们的团队监督器是一个大语言模型节点。它只是选择下一个要处理的智能体,并决定工作何时完成
options = members + ["FINISH"]
system_prompt = (
"你是一名监督者,负责管理以下工作者之间的对话:{members}。给定以下用户请求,回复接下来要行动的工作者。每个工作者将执行一项任务,并回复其结果和状态。完成后,回复FINISH。"
)
class Router(TypedDict):
"""接下来要路由到的工作者。如果不需要工作者,则路由到FINISH。"""
next: Literal[*options]
llm = ChatAnthropic(model="claude-3-5-sonnet-latest")
class State(MessagesState):
next: str
def supervisor_node(state: State) -> Command[Literal[*members, "__end__"]]:
messages = [
{"role": "system", "content": system_prompt},
] + state["messages"]
response = llm.with_structured_output(Router).invoke(messages)
goto = response["next"]
if goto == "FINISH":
goto = END
return Command(goto=goto, update={"next": goto})
这里定义了智能体监督器的相关逻辑,它根据用户请求和当前状态,通过大语言模型选择下一个要执行任务的智能体或者决定工作是否完成。
(四)构建图
from langchain_core.messages import HumanMessage
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import create_react_agent
research_agent = create_react_agent(
llm, tools=[tavily_tool], prompt="You are a researcher. DO NOT do any math."
)
def research_node(state: State) -> Command[Literal["supervisor"]]:
result = research_agent.invoke(state)
return Command(
update={
"messages": [
HumanMessage(content=result["messages"][-1].content, name="researcher")
]
},
goto="supervisor",
)
# NOTE: THIS PERFORMS ARBITRARY CODE EXECUTION, WHICH CAN BE UNSAFE WHEN NOT SANDBOXED
code_agent = create_react_agent(llm, tools=[python_repl_tool])
def code_node(state: State) -> Command[Literal["supervisor"]]:
result = code_agent.invoke(state)
return Command(
update={
"messages": [
HumanMessage(content=result["messages"][-1].content, name="coder")
]
},
goto="supervisor",
)
builder = StateGraph(State)
builder.add_edge(START, "supervisor")
builder.add_node("supervisor", supervisor_node)
builder.add_node("researcher", research_node)
builder.add_node("coder", code_node)
graph = builder.compile()
这段代码创建了研究智能体和代码执行智能体,并定义了它们对应的节点函数。然后通过StateGraph构建图结构,添加边和节点,最后编译得到可执行的图。
(五)显示图
from IPython.display import display, Image
display(Image(graph.get_graph().draw_mermaid_png()))
这部分代码用于在Jupyter Notebook等环境中显示图的Mermaid格式的PNG图像,方便我们直观了解图的结构。
(六)调用图
for s in graph.stream(
{"messages": [("user", "What's the square root of 42?")]}, subgraphs=True
):
print(s)
print("----")
使用graph.stream方法来执行图,传入用户消息"What’s the square root of 42?",并设置subgraphs=True表示在执行过程中包含子图信息。通过循环打印出图执行过程中的每一步状态。
(七)处理新的用户请求
for s in graph.stream(
{
"messages": [
(
"user",
"Find the latest GDP of New York and California, then calculate the average"
)
]
}, subgraphs=True
):
print(s)
print("----")
这部分尝试使用相同的图结构来处理新的用户请求"Find the latest GDP of New York and California, then calculate the average",同样通过graph.stream方法执行图,并打印每一步的状态。不过这里由于没有找到纽约和加利福尼亚最新GDP数据的相关信息,无法完整处理这个请求。如果要完整实现这个功能,可能需要确保llm和tools能够获取到纽约和加利福尼亚的GDP数据,并处理数据计算平均值。