使用 AgentWorkflow 的多智能体系统#
到目前为止,你一直在使用 AgentWorkflow
创建单个智能体。但 AgentWorkflow
也旨在支持多智能体系统,其中多个智能体协作完成你的任务,并在需要时将控制权互相移交。
在此示例中,我们的系统将包含三个智能体
- 一个
ResearchAgent
,它将搜索网络以查找给定主题的信息。 - 一个
WriteAgent
,它将使用 ResearchAgent 找到的信息来撰写报告。 - 一个
ReviewAgent
,它将审查报告并提供反馈。
我们将使用 AgentWorkflow
创建一个多智能体系统,该系统将按顺序执行这些智能体。
有很多方法可以构建一个系统来执行此任务。在此示例中,我们将使用一些工具来协助研究和写作过程。
- 一个
web_search
工具,用于搜索给定主题的信息(我们将使用 Tavily,就像我们在之前的示例中所做的那样) - 一个
record_notes
工具,它将把在网络上找到的研究保存到状态中,以便其他工具可以使用它(请参阅状态管理以回顾其工作原理) - 一个
write_report
工具,用于使用ResearchAgent
找到的信息撰写报告 - 一个
review_report
工具,用于审查报告并提供反馈。
利用 Context 类,我们可以在智能体之间传递状态,并且每个智能体都可以访问系统的当前状态。
我们将简单地通过使用从 TavilyToolSpec
获取的工具来定义我们的 web_search
工具
tavily_tool = TavilyToolSpec(api_key=os.getenv("TAVILY_API_KEY"))
search_web = tavily_tool.to_tool_list()[0]
我们的 record_notes
工具将访问当前状态,将笔记添加到状态,然后返回一条消息,表明笔记已被记录。
async def record_notes(ctx: Context, notes: str, notes_title: str) -> str:
"""Useful for recording notes on a given topic."""
current_state = await ctx.get("state")
if "research_notes" not in current_state:
current_state["research_notes"] = {}
current_state["research_notes"][notes_title] = notes
await ctx.set("state", current_state)
return "Notes recorded."
write_report
和 review_report
也将是访问状态的工具
async def write_report(ctx: Context, report_content: str) -> str:
"""Useful for writing a report on a given topic."""
current_state = await ctx.get("state")
current_state["report_content"] = report_content
await ctx.set("state", current_state)
return "Report written."
async def review_report(ctx: Context, review: str) -> str:
"""Useful for reviewing a report and providing feedback."""
current_state = await ctx.get("state")
current_state["review"] = review
await ctx.set("state", current_state)
return "Report reviewed."
现在我们将引入一个新的类来创建一个独立的函数调用智能体,即 FunctionAgent
(我们也支持 ReactAgent
和 CodeActAgent
)
from llama_index.core.agent.workflow import FunctionAgent
使用它,我们将创建我们的第一个智能体,即 ResearchAgent
,它将使用 search_web
工具搜索网络信息,并使用 record_notes
工具将这些笔记保存到状态中供其他智能体使用。这里需要注意的关键语法元素包括:* name
,用于向其他智能体标识该智能体,我们很快就会看到;* description
,其他智能体使用它来决定下一步将控制权移交给谁;* system_prompt
,定义智能体的行为;* can_handoff_to
是一个可选的智能体名称列表,智能体可以将控制权移交给这些智能体。默认情况下,它可以将控制权移交给任何其他智能体。
research_agent = FunctionAgent(
name="ResearchAgent",
description="Useful for searching the web for information on a given topic and recording notes on the topic.",
system_prompt=(
"You are the ResearchAgent that can search the web for information on a given topic and record notes on the topic. "
"Once notes are recorded and you are satisfied, you should hand off control to the WriteAgent to write a report on the topic."
),
llm=llm,
tools=[search_web, record_notes],
can_handoff_to=["WriteAgent"],
)
我们的另外两个智能体也类似地定义,使用不同的工具和系统提示词。
write_agent = FunctionAgent(
name="WriteAgent",
description="Useful for writing a report on a given topic.",
system_prompt=(
"You are the WriteAgent that can write a report on a given topic. "
"Your report should be in a markdown format. The content should be grounded in the research notes. "
"Once the report is written, you should get feedback at least once from the ReviewAgent."
),
llm=llm,
tools=[write_report],
can_handoff_to=["ReviewAgent", "ResearchAgent"],
)
review_agent = FunctionAgent(
name="ReviewAgent",
description="Useful for reviewing a report and providing feedback.",
system_prompt=(
"You are the ReviewAgent that can review a report and provide feedback. "
"Your feedback should either approve the current report or request changes for the WriteAgent to implement."
),
llm=llm,
tools=[review_report],
can_handoff_to=["WriteAgent"],
)
定义好智能体后,我们现在可以直接实例化 AgentWorkflow
来创建一个多智能体系统。我们为其提供一个智能体数组,并使用 root_agent
定义哪个智能体应最初拥有控制权。我们还可以定义 state
变量的初始值,正如我们之前所见,它是一个所有智能体都可以访问的字典。
agent_workflow = AgentWorkflow(
agents=[research_agent, write_agent, review_agent],
root_agent=research_agent.name,
initial_state={
"research_notes": {},
"report_content": "Not written yet.",
"review": "Review required.",
},
)
现在我们准备运行我们的多智能体系统。我们添加了一些使用流式事件(streaming events)的事件处理,以便更清楚地了解内部发生的情况。
handler = agent_workflow.run(
user_msg="""
Write me a report on the history of the web. Briefly describe the history
of the world wide web, including the development of the internet and the
development of the web, including 21st century developments.
"""
)
current_agent = None
current_tool_calls = ""
async for event in handler.stream_events():
if (
hasattr(event, "current_agent_name")
and event.current_agent_name != current_agent
):
current_agent = event.current_agent_name
print(f"\n{'='*50}")
print(f"🤖 Agent: {current_agent}")
print(f"{'='*50}\n")
elif isinstance(event, AgentOutput):
if event.response.content:
print("📤 Output:", event.response.content)
if event.tool_calls:
print(
"🛠️ Planning to use tools:",
[call.tool_name for call in event.tool_calls],
)
elif isinstance(event, ToolCallResult):
print(f"🔧 Tool Result ({event.tool_name}):")
print(f" Arguments: {event.tool_kwargs}")
print(f" Output: {event.tool_output}")
elif isinstance(event, ToolCall):
print(f"🔨 Calling Tool: {event.tool_name}")
print(f" With arguments: {event.tool_kwargs}")
这将产生非常详细的输出,为简洁起见,我们在此处进行了截断。
==================================================
🤖 Agent: ResearchAgent
==================================================
🛠️ Planning to use tools: ['search']
🔨 Calling Tool: search
With arguments: {'query': 'history of the world wide web and internet development', 'max_results': 6}
🔧 Tool Result (search):
Arguments: {'query': 'history of the world wide web and internet development', 'max_results': 6}
Output: [Document(id_='2e977310-2994-4ea9-ade2-8da4533983e8', embedding=None, metadata={'url': 'https://www.scienceandmediamuseum.org.uk/objects-and-stories/short-history-internet'}, excluded_embed_metadata_keys=[], ...
🛠️ Planning to use tools: ['record_notes', 'record_notes']
🔨 Calling Tool: record_notes
With arguments: {'notes': 'The World Wide Web (WWW) was created by Tim Berners-Lee...','notes_title': 'History of the World Wide Web and Internet Development'}
🔧 Tool Result (record_notes):
Arguments: {'notes': 'The World Wide Web (WWW) was created by Tim Berners-Lee...', 'notes_title': 'History of the World Wide Web and Internet Development'}
Output: Notes recorded.
🔨 Calling Tool: record_notes
With arguments: {'notes': "The internet's origins trace back to the 1950s....", 'notes_title': '21st Century Developments in Web Technology'}
🔧 Tool Result (record_notes):
Arguments: {'notes': "The internet's origins trace back to the 1950s... .", 'notes_title': '21st Century Developments in Web Technology'}
Output: Notes recorded.
🛠️ Planning to use tools: ['handoff']
🔨 Calling Tool: handoff
With arguments: {'to_agent': 'WriteAgent', 'reason': 'I have recorded the necessary notes on the history of the web and its developments.'}
🔧 Tool Result (handoff):
Arguments: {'to_agent': 'WriteAgent', 'reason': 'I have recorded the necessary notes on the history of the web and its developments.'}
Output: Agent WriteAgent is now handling the request due to the following reason: I have recorded the necessary notes on the history of the web and its developments..
Please continue with the current request.
你可以看到 ResearchAgent
找到了一些笔记,并将控制权移交给了 WriteAgent
,后者生成了 report_content
==================================================
🤖 Agent: WriteAgent
==================================================
🛠️ Planning to use tools: ['write_report']
🔨 Calling Tool: write_report
With arguments: {'report_content': '# History of the World Wide Web...'}
🔧 Tool Result (write_report):
Arguments: {'report_content': '# History of the World Wide Web...'}
Output: Report written.
🛠️ Planning to use tools: ['handoff']
🔨 Calling Tool: handoff
With arguments: {'to_agent': 'ReviewAgent', 'reason': 'The report on the history of the web has been completed and requires review.'}
🔧 Tool Result (handoff):
Arguments: {'to_agent': 'ReviewAgent', 'reason': 'The report on the history of the web has been completed and requires review.'}
Output: Agent ReviewAgent is now handling the request due to the following reason: The report on the history of the web has been completed and requires review..
Please continue with the current request.
最后,控制权被传递给 ReviewAgent
以审查报告
==================================================
🤖 Agent: ReviewAgent
==================================================
🛠️ Planning to use tools: ['review_report']
🔨 Calling Tool: review_report
With arguments: {'review': 'The report on the history of the web is well-structured ... Approval is granted.'}
🔧 Tool Result (review_report):
Arguments: {'review': 'The report on the history of the web is well-structured ... Approval is granted.'}
Output: Report reviewed.
📤 Output: The report on the history of the web has been reviewed and approved. It effectively covers the key developments from the inception of the internet to the 21st century, including significant contributions and advancements. If you need any further assistance or additional reports, feel free to ask!
你可以查看此示例的完整代码。
作为此示例的扩展,你可以创建一个系统,接收来自 ReviewAgent
的反馈,并将其传回给 WriteAgent
以更新报告。
恭喜!#
你已经了解了使用 AgentWorkflow
构建智能体的所有知识。在工作流教程中,你将把在此学到的许多概念应用于构建更精确、更底层的智能体系统。