mirror of
https://github.com/microsoft/autogen.git
synced 2026-04-20 03:02:16 -04:00
Create a notebook to demonstrate handoff pattern (#3778)
This commit is contained in:
91
python/packages/autogen-core/docs/drawio/handoffs.drawio
Normal file
91
python/packages/autogen-core/docs/drawio/handoffs.drawio
Normal file
@@ -0,0 +1,91 @@
|
||||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0" version="24.7.14">
|
||||
<diagram name="Page-1" id="H4y8-2MmoI-yFkyBsMKh">
|
||||
<mxGraphModel dx="1796" dy="1158" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Rn_Z-AZpyA3VoKjSUQzu-1" target="Rn_Z-AZpyA3VoKjSUQzu-3">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-10" value="Transfer to sales" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Rn_Z-AZpyA3VoKjSUQzu-6">
|
||||
<mxGeometry x="-0.1586" y="2" relative="1" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Rn_Z-AZpyA3VoKjSUQzu-1" target="Rn_Z-AZpyA3VoKjSUQzu-4">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-11" value="Transfer to issues and repair" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Rn_Z-AZpyA3VoKjSUQzu-8">
|
||||
<mxGeometry x="0.2071" y="3" relative="1" as="geometry">
|
||||
<mxPoint y="1" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-23" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Rn_Z-AZpyA3VoKjSUQzu-1" target="Rn_Z-AZpyA3VoKjSUQzu-22">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-24" value="Escalate to human" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Rn_Z-AZpyA3VoKjSUQzu-23">
|
||||
<mxGeometry x="0.2667" y="3" relative="1" as="geometry">
|
||||
<mxPoint x="-3" y="10" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-1" value="Triage Agent" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="310" y="420" width="100" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-7" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="Rn_Z-AZpyA3VoKjSUQzu-3" target="Rn_Z-AZpyA3VoKjSUQzu-1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-12" value="Transfer to triage" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Rn_Z-AZpyA3VoKjSUQzu-7">
|
||||
<mxGeometry x="-0.1714" y="2" relative="1" as="geometry">
|
||||
<mxPoint y="1" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-3" value="Sales Agent" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="120" y="490" width="100" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="Rn_Z-AZpyA3VoKjSUQzu-4" target="Rn_Z-AZpyA3VoKjSUQzu-1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-13" value="Transfer to triage" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Rn_Z-AZpyA3VoKjSUQzu-9">
|
||||
<mxGeometry x="0.35" y="1" relative="1" as="geometry">
|
||||
<mxPoint x="45" y="-1" as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-4" value="Issue and Repair Agent" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="510" y="490" width="100" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="Rn_Z-AZpyA3VoKjSUQzu-5" target="Rn_Z-AZpyA3VoKjSUQzu-1">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-21" value="Initiate conversation" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="Rn_Z-AZpyA3VoKjSUQzu-20">
|
||||
<mxGeometry x="-0.22" y="-1" relative="1" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-5" value="User Agent" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="310" y="320" width="100" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.855;exitY=0;exitDx=0;exitDy=4.35;exitPerimeter=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="Rn_Z-AZpyA3VoKjSUQzu-14" target="Rn_Z-AZpyA3VoKjSUQzu-3">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-14" value="Catalog" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
|
||||
<mxGeometry x="135" y="560" width="70" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-19" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.855;exitY=0;exitDx=0;exitDy=4.35;exitPerimeter=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="1" source="Rn_Z-AZpyA3VoKjSUQzu-15" target="Rn_Z-AZpyA3VoKjSUQzu-4">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-15" value="Orders" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
|
||||
<mxGeometry x="525" y="560" width="70" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.145;entryY=0;entryDx=0;entryDy=4.35;entryPerimeter=0;" edge="1" parent="1" source="Rn_Z-AZpyA3VoKjSUQzu-3" target="Rn_Z-AZpyA3VoKjSUQzu-14">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.145;entryY=0;entryDx=0;entryDy=4.35;entryPerimeter=0;" edge="1" parent="1" source="Rn_Z-AZpyA3VoKjSUQzu-4" target="Rn_Z-AZpyA3VoKjSUQzu-15">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="Rn_Z-AZpyA3VoKjSUQzu-22" value="Human Agent" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="1">
|
||||
<mxGeometry x="310" y="565" width="100" height="50" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
@@ -20,7 +20,7 @@ implementation of the contracts determines how agents handle messages.
|
||||
The behavior contract is also sometimes referred to as the message protocol.
|
||||
It is the developer's responsibility to implement the behavior contract.
|
||||
Multi-agent patterns emerge from these behavior contracts
|
||||
(see [Multi-Agent Design Patterns](../design-patterns/multi-agent-design-patterns.md)).
|
||||
(see [Multi-Agent Design Patterns](../design-patterns/index.md)).
|
||||
|
||||
## An Example Application
|
||||
|
||||
|
||||
@@ -0,0 +1,757 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Handoffs\n",
|
||||
"\n",
|
||||
"Handoff is a multi-agent design pattern introduced by OpenAI in an experimental project called [Swarm](https://github.com/openai/swarm).\n",
|
||||
"The key idea is to let agent delegate tasks to other agents using a special tool call.\n",
|
||||
"\n",
|
||||
"We can use the AutoGen Core API to implement the handoff pattern using event-driven agents.\n",
|
||||
"Using AutoGen (v0.4+) provides the following advantages over the OpenAI implementation and the previous version (v0.2):\n",
|
||||
"\n",
|
||||
"1. It can scale to distributed environment by using distributed agent runtime.\n",
|
||||
"2. It affords the flexibility of bringing your own agent implementation.\n",
|
||||
"3. The natively async API makes it easy to integrate with UI and other systems.\n",
|
||||
"\n",
|
||||
"This notebook demonstrates a simple implementation of the handoff pattern.\n",
|
||||
"It is recommended to read [Topics and Subscriptions](../core-concepts/topic-and-subscription.md)\n",
|
||||
"to understand the basic concepts of pub-sub and event-driven agents.\n",
|
||||
"\n",
|
||||
"```{note}\n",
|
||||
"We are currently working on a high-level API for the handoff pattern in [AgentChat](../../agentchat-user-guide/index.md) so you can get started\n",
|
||||
"much more quickly.\n",
|
||||
"```\n",
|
||||
"\n",
|
||||
"## Scenario\n",
|
||||
"\n",
|
||||
"This scenario is modified based on the [OpenAI example](https://github.com/openai/openai-cookbook/blob/main/examples/Orchestrating_agents.ipynb).\n",
|
||||
"\n",
|
||||
"Consider a customer service scenario where a customer is trying to get a refund for a product, or purchase a new product from a chatbot.\n",
|
||||
"The chatbot is a multi-agent team consisting of three AI agents and one human agent:\n",
|
||||
"\n",
|
||||
"- Triage Agent, responsible for understanding the customer's request and deciding which other agents to hand off to.\n",
|
||||
"- Refund Agent, responsible for processing refund requests.\n",
|
||||
"- Sales Agent, responsible for processing sales requests.\n",
|
||||
"- Human Agent, responsible for handling complex requests that the AI agents can't handle.\n",
|
||||
"\n",
|
||||
"In this scenario, the customer interacts with the chatbot through a User Agent.\n",
|
||||
"\n",
|
||||
"The diagram below shows the interaction topology of the agents in this scenario.\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"Let's implement this scenario using AutoGen Core. First, we need to import the necessary modules."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 29,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import uuid\n",
|
||||
"from typing import List, Tuple\n",
|
||||
"\n",
|
||||
"from autogen_core.application import SingleThreadedAgentRuntime\n",
|
||||
"from autogen_core.base import MessageContext, TopicId\n",
|
||||
"from autogen_core.components import FunctionCall, RoutedAgent, TypeSubscription, message_handler\n",
|
||||
"from autogen_core.components.models import (\n",
|
||||
" AssistantMessage,\n",
|
||||
" ChatCompletionClient,\n",
|
||||
" FunctionExecutionResult,\n",
|
||||
" FunctionExecutionResultMessage,\n",
|
||||
" LLMMessage,\n",
|
||||
" OpenAIChatCompletionClient,\n",
|
||||
" SystemMessage,\n",
|
||||
" UserMessage,\n",
|
||||
")\n",
|
||||
"from autogen_core.components.tools import FunctionTool, Tool\n",
|
||||
"from pydantic import BaseModel"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Message Protocol\n",
|
||||
"\n",
|
||||
"Before everything, we need to define the message protocol for the agents to communicate.\n",
|
||||
"We are using event-driven pub-sub communication, so these message types will be used as events.\n",
|
||||
"\n",
|
||||
"- `UserLogin` is a message published by the runtime when a user logs in and starts a new session.\n",
|
||||
"- `UserTask` is a message containing the chat history of the user session. When an AI agent hands off a task to other agents, it also publishes a `UserTask` message.\n",
|
||||
"- `AgentResponse` is a message published by the AI agents and the Human Agent, it also contains the chat history as well as a topic type for the customer to reply to."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 30,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class UserLogin(BaseModel):\n",
|
||||
" pass\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class UserTask(BaseModel):\n",
|
||||
" context: List[LLMMessage]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"class AgentResponse(BaseModel):\n",
|
||||
" reply_to_topic_type: str\n",
|
||||
" context: List[LLMMessage]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## AI Agent\n",
|
||||
"\n",
|
||||
"We start with the `AIAgent` class, which is the class for all AI agents \n",
|
||||
"(i.e., Triage, Sales, and Issue and Repair Agents) in the multi-agent chatbot.\n",
|
||||
"An `AIAgent` uses a {py:class}`~autogen_core.components.models.ChatCompletionClient`\n",
|
||||
"to generate responses.\n",
|
||||
"It can use regular tools directly or delegate tasks to other agents using `delegate_tools`.\n",
|
||||
"It subscribes to topic type `agent_topic_type` to receive messages from the customer,\n",
|
||||
"and sends message to the customer by publishing to the topic type `user_topic_type`.\n",
|
||||
"\n",
|
||||
"In the `handle_task` method, the agent first generates a response using the model.\n",
|
||||
"If the response contains a handoff tool call, the agent delegates the task to another agent\n",
|
||||
"by publishing a `UserTask` message to the topic specified in the tool call result.\n",
|
||||
"If the response is a regular tool call, the agent executes the tool and makes\n",
|
||||
"another call to the model to generate the next response, until the response is not a tool call.\n",
|
||||
"\n",
|
||||
"When the model response is not a tool call, the agent sends an `AgentResponse` message to the customer\n",
|
||||
"by publishing to the `user_topic_type`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 31,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class AIAgent(RoutedAgent):\n",
|
||||
" def __init__(\n",
|
||||
" self,\n",
|
||||
" description: str,\n",
|
||||
" system_message: SystemMessage,\n",
|
||||
" model_client: ChatCompletionClient,\n",
|
||||
" tools: List[Tool],\n",
|
||||
" delegate_tools: List[Tool],\n",
|
||||
" agent_topic_type: str,\n",
|
||||
" user_topic_type: str,\n",
|
||||
" ) -> None:\n",
|
||||
" super().__init__(description)\n",
|
||||
" self._system_message = system_message\n",
|
||||
" self._model_client = model_client\n",
|
||||
" self._tools = dict([(tool.name, tool) for tool in tools])\n",
|
||||
" self._tool_schema = [tool.schema for tool in tools]\n",
|
||||
" self._delegate_tools = dict([(tool.name, tool) for tool in delegate_tools])\n",
|
||||
" self._delegate_tool_schema = [tool.schema for tool in delegate_tools]\n",
|
||||
" self._agent_topic_type = agent_topic_type\n",
|
||||
" self._user_topic_type = user_topic_type\n",
|
||||
"\n",
|
||||
" @message_handler\n",
|
||||
" async def handle_task(self, message: UserTask, ctx: MessageContext) -> None:\n",
|
||||
" # Send the task to the LLM.\n",
|
||||
" llm_result = await self._model_client.create(\n",
|
||||
" messages=[self._system_message] + message.context,\n",
|
||||
" tools=self._tool_schema + self._delegate_tool_schema,\n",
|
||||
" cancellation_token=ctx.cancellation_token,\n",
|
||||
" )\n",
|
||||
" print(f\"{'-'*80}\\n{self.id.type}:\\n{llm_result.content}\", flush=True)\n",
|
||||
" # Process the LLM result.\n",
|
||||
" while isinstance(llm_result.content, list) and all(isinstance(m, FunctionCall) for m in llm_result.content):\n",
|
||||
" tool_call_results: List[FunctionExecutionResult] = []\n",
|
||||
" delegate_targets: List[Tuple[str, UserTask]] = []\n",
|
||||
" # Process each function call.\n",
|
||||
" for call in llm_result.content:\n",
|
||||
" arguments = json.loads(call.arguments)\n",
|
||||
" if call.name in self._tools:\n",
|
||||
" # Execute the tool directly.\n",
|
||||
" result = await self._tools[call.name].run_json(arguments, ctx.cancellation_token)\n",
|
||||
" result_as_str = self._tools[call.name].return_value_as_string(result)\n",
|
||||
" tool_call_results.append(FunctionExecutionResult(call_id=call.id, content=result_as_str))\n",
|
||||
" elif call.name in self._delegate_tools:\n",
|
||||
" # Execute the tool to get the delegate agent's topic type.\n",
|
||||
" result = await self._delegate_tools[call.name].run_json(arguments, ctx.cancellation_token)\n",
|
||||
" topic_type = self._delegate_tools[call.name].return_value_as_string(result)\n",
|
||||
" # Create the context for the delegate agent, including the function call and the result.\n",
|
||||
" delegate_messages = list(message.context) + [\n",
|
||||
" AssistantMessage(content=[call], source=self.id.type),\n",
|
||||
" FunctionExecutionResultMessage(\n",
|
||||
" content=[\n",
|
||||
" FunctionExecutionResult(\n",
|
||||
" call_id=call.id, content=f\"Transfered to {topic_type}. Adopt persona immediately.\"\n",
|
||||
" )\n",
|
||||
" ]\n",
|
||||
" ),\n",
|
||||
" ]\n",
|
||||
" delegate_targets.append((topic_type, UserTask(context=delegate_messages)))\n",
|
||||
" else:\n",
|
||||
" raise ValueError(f\"Unknown tool: {call.name}\")\n",
|
||||
" if len(delegate_targets) > 0:\n",
|
||||
" # Delegate the task to other agents by publishing messages to the corresponding topics.\n",
|
||||
" for topic_type, task in delegate_targets:\n",
|
||||
" print(f\"{'-'*80}\\n{self.id.type}:\\nDelegating to {topic_type}\", flush=True)\n",
|
||||
" await self.publish_message(task, topic_id=TopicId(topic_type, source=self.id.key))\n",
|
||||
" if len(tool_call_results) > 0:\n",
|
||||
" print(f\"{'-'*80}\\n{self.id.type}:\\n{tool_call_results}\", flush=True)\n",
|
||||
" # Make another LLM call with the results.\n",
|
||||
" message.context.extend(\n",
|
||||
" [\n",
|
||||
" AssistantMessage(content=llm_result.content, source=self.id.type),\n",
|
||||
" FunctionExecutionResultMessage(content=tool_call_results),\n",
|
||||
" ]\n",
|
||||
" )\n",
|
||||
" llm_result = await self._model_client.create(\n",
|
||||
" messages=[self._system_message] + message.context,\n",
|
||||
" tools=self._tool_schema + self._delegate_tool_schema,\n",
|
||||
" cancellation_token=ctx.cancellation_token,\n",
|
||||
" )\n",
|
||||
" print(f\"{'-'*80}\\n{self.id.type}:\\n{llm_result.content}\", flush=True)\n",
|
||||
" else:\n",
|
||||
" # The task has been delegated, so we are done.\n",
|
||||
" return\n",
|
||||
" # The task has been completed, publish the final result.\n",
|
||||
" assert isinstance(llm_result.content, str)\n",
|
||||
" message.context.append(AssistantMessage(content=llm_result.content, source=self.id.type))\n",
|
||||
" await self.publish_message(\n",
|
||||
" AgentResponse(context=message.context, reply_to_topic_type=self._agent_topic_type),\n",
|
||||
" topic_id=TopicId(self._user_topic_type, source=self.id.key),\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Human Agent\n",
|
||||
"\n",
|
||||
"The `HumanAgent` class is a proxy for the human in the chatbot. It is used\n",
|
||||
"to handle requests that the AI agents can't handle. The `HumanAgent` subscribes to the\n",
|
||||
"topic type `agent_topic_type` to receive messages and publishes to the topic type `user_topic_type`\n",
|
||||
"to send messages to the customer.\n",
|
||||
"\n",
|
||||
"In this implementation, the `HumanAgent` simply uses console to \n",
|
||||
"get your input. In a real-world application, you can improve this design as follows: \n",
|
||||
"\n",
|
||||
"* In the `handle_user_task` method, send a notification via a chat application like Teams or Slack.\n",
|
||||
"* The chat application publishes the human's response via the runtime to the topic specified by `agent_topic_type`\n",
|
||||
"* Create another message handler to process the human's response and send it back to the customer."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 32,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class HumanAgent(RoutedAgent):\n",
|
||||
" def __init__(self, description: str, agent_topic_type: str, user_topic_type: str) -> None:\n",
|
||||
" super().__init__(description)\n",
|
||||
" self._agent_topic_type = agent_topic_type\n",
|
||||
" self._user_topic_type = user_topic_type\n",
|
||||
"\n",
|
||||
" @message_handler\n",
|
||||
" async def handle_user_task(self, message: UserTask, ctx: MessageContext) -> None:\n",
|
||||
" human_input = input(\"Human agent input: \")\n",
|
||||
" print(f\"{'-'*80}\\n{self.id.type}:\\n{human_input}\", flush=True)\n",
|
||||
" message.context.append(AssistantMessage(content=human_input, source=self.id.type))\n",
|
||||
" await self.publish_message(\n",
|
||||
" AgentResponse(context=message.context, reply_to_topic_type=self._agent_topic_type),\n",
|
||||
" topic_id=TopicId(self._user_topic_type, source=self.id.key),\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## User Agent\n",
|
||||
"\n",
|
||||
"The `UserAgent` class is a proxy for the customer that talks to the chatbot.\n",
|
||||
"It handles two message types: `UserLogin` and `AgentResponse`.\n",
|
||||
"When the `UserAgent` receives a `UserLogin` message, it starts a new session with the chatbot\n",
|
||||
"and publishes a `UserTask` message to the AI agent that subscribes to the topic type `agent_topic_type`.\n",
|
||||
"When the `UserAgent` receives an `AgentResponse` message, it prompts the user with the response\n",
|
||||
"from the chatbot.\n",
|
||||
"\n",
|
||||
"In this implementation, the `UserAgent` uses console to get your input.\n",
|
||||
"In a real-world application, you can improve the human interaction using the same\n",
|
||||
"idea described in the `HumanAgent` section above."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 33,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"class UserAgent(RoutedAgent):\n",
|
||||
" def __init__(self, description: str, user_topic_type: str, agent_topic_type: str) -> None:\n",
|
||||
" super().__init__(description)\n",
|
||||
" self._user_topic_type = user_topic_type\n",
|
||||
" self._agent_topic_type = agent_topic_type\n",
|
||||
"\n",
|
||||
" @message_handler\n",
|
||||
" async def handle_user_login(self, message: UserLogin, ctx: MessageContext) -> None:\n",
|
||||
" print(f\"{'-'*80}\\nUser login, session ID: {self.id.key}.\", flush=True)\n",
|
||||
" # Get the user's initial input after login.\n",
|
||||
" user_input = input(\"User: \")\n",
|
||||
" print(f\"{'-'*80}\\n{self.id.type}:\\n{user_input}\")\n",
|
||||
" await self.publish_message(\n",
|
||||
" UserTask(context=[UserMessage(content=user_input, source=\"User\")]),\n",
|
||||
" topic_id=TopicId(self._agent_topic_type, source=self.id.key),\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" @message_handler\n",
|
||||
" async def handle_task_result(self, message: AgentResponse, ctx: MessageContext) -> None:\n",
|
||||
" # Get the user's input after receiving a response from an agent.\n",
|
||||
" user_input = input(\"User (type 'exit' to close the session): \")\n",
|
||||
" print(f\"{'-'*80}\\n{self.id.type}:\\n{user_input}\", flush=True)\n",
|
||||
" if user_input.strip().lower() == \"exit\":\n",
|
||||
" print(f\"{'-'*80}\\nUser session ended, session ID: {self.id.key}.\")\n",
|
||||
" return\n",
|
||||
" message.context.append(UserMessage(content=user_input, source=\"User\"))\n",
|
||||
" await self.publish_message(\n",
|
||||
" UserTask(context=message.context), topic_id=TopicId(message.reply_to_topic_type, source=self.id.key)\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Tools for the AI agents\n",
|
||||
"\n",
|
||||
"The AI agents can use regular tools to complete tasks if they don't need to hand off the task to other agents.\n",
|
||||
"We define the tools using simple functions and create the tools using the\n",
|
||||
"{py:class}`~autogen_core.components.tools.FunctionTool` wrapper."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 34,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def execute_order(product: str, price: int) -> str:\n",
|
||||
" print(\"\\n\\n=== Order Summary ===\")\n",
|
||||
" print(f\"Product: {product}\")\n",
|
||||
" print(f\"Price: ${price}\")\n",
|
||||
" print(\"=================\\n\")\n",
|
||||
" confirm = input(\"Confirm order? y/n: \").strip().lower()\n",
|
||||
" if confirm == \"y\":\n",
|
||||
" print(\"Order execution successful!\")\n",
|
||||
" return \"Success\"\n",
|
||||
" else:\n",
|
||||
" print(\"Order cancelled!\")\n",
|
||||
" return \"User cancelled order.\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def look_up_item(search_query: str) -> str:\n",
|
||||
" item_id = \"item_132612938\"\n",
|
||||
" print(\"Found item:\", item_id)\n",
|
||||
" return item_id\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def execute_refund(item_id: str, reason: str = \"not provided\") -> str:\n",
|
||||
" print(\"\\n\\n=== Refund Summary ===\")\n",
|
||||
" print(f\"Item ID: {item_id}\")\n",
|
||||
" print(f\"Reason: {reason}\")\n",
|
||||
" print(\"=================\\n\")\n",
|
||||
" print(\"Refund execution successful!\")\n",
|
||||
" return \"success\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"execute_order_tool = FunctionTool(execute_order, description=\"Price should be in USD.\")\n",
|
||||
"look_up_item_tool = FunctionTool(\n",
|
||||
" look_up_item, description=\"Use to find item ID.\\nSearch query can be a description or keywords.\"\n",
|
||||
")\n",
|
||||
"execute_refund_tool = FunctionTool(execute_refund, description=\"\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Topic types for the agents\n",
|
||||
"\n",
|
||||
"We define the topic types each of the agents will subscribe to.\n",
|
||||
"Read more about topic types in the [Topics and Subscriptions](../core-concepts/topic-and-subscription.md)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 35,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"sales_agent_topic_type = \"SalesAgent\"\n",
|
||||
"issues_and_repairs_agent_topic_type = \"IssuesAndRepairsAgent\"\n",
|
||||
"triage_agent_topic_type = \"TriageAgent\"\n",
|
||||
"human_agent_topic_type = \"HumanAgent\"\n",
|
||||
"user_topic_type = \"User\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Delegate tools for the AI agents\n",
|
||||
"\n",
|
||||
"Besides regular tools, the AI agents can delegate tasks to other agents using\n",
|
||||
"special tools called delegate tools. The concept of delegate tool is only used\n",
|
||||
"in this design pattern, and the delegate tools are also defined as simple functions.\n",
|
||||
"We differentiate the delegate tools from regular tools in this design pattern\n",
|
||||
"because when an AI agent calls a delegate tool, we transfer the task to another agent\n",
|
||||
"instead of continue generating responses using the model in the same agent."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 36,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def transfer_to_sales_agent() -> str:\n",
|
||||
" return sales_agent_topic_type\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def transfer_to_issues_and_repairs() -> str:\n",
|
||||
" return issues_and_repairs_agent_topic_type\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def transfer_back_to_triage() -> str:\n",
|
||||
" return triage_agent_topic_type\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def escalate_to_human() -> str:\n",
|
||||
" return human_agent_topic_type\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"transfer_to_sales_agent_tool = FunctionTool(\n",
|
||||
" transfer_to_sales_agent, description=\"Use for anything sales or buying related.\"\n",
|
||||
")\n",
|
||||
"transfer_to_issues_and_repairs_tool = FunctionTool(\n",
|
||||
" transfer_to_issues_and_repairs, description=\"Use for issues, repairs, or refunds.\"\n",
|
||||
")\n",
|
||||
"transfer_back_to_triage_tool = FunctionTool(\n",
|
||||
" transfer_back_to_triage,\n",
|
||||
" description=\"Call this if the user brings up a topic outside of your purview,\\nincluding escalating to human.\",\n",
|
||||
")\n",
|
||||
"escalate_to_human_tool = FunctionTool(escalate_to_human, description=\"Only call this if explicitly asked to.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Creating the team\n",
|
||||
"\n",
|
||||
"We have defined the AI agents, the Human Agent, the User Agent, the tools, and the topic types.\n",
|
||||
"Now we can create the team of agents.\n",
|
||||
"\n",
|
||||
"For the AI agents, we use the {py:class}`~autogen_core.components.models.OpenAIChatCompletionClient`\n",
|
||||
"and `gpt-4o-mini` model.\n",
|
||||
"\n",
|
||||
"After creating the agent runtime, we register each of the agent by providing\n",
|
||||
"an agent type and a factory method to create agent instance.\n",
|
||||
"The runtime is responsible for managing the agent lifecycle so we don't need to\n",
|
||||
"instantiate the agents ourselves.\n",
|
||||
"Read more about agent runtime in [Agent Runtime Environments](../core-concepts/architecture.md)\n",
|
||||
"and agent lifecycle in [Agent Identity and Lifecycle](../core-concepts/agent-identity-and-lifecycle.md).\n",
|
||||
"\n",
|
||||
"In the code below, you can see we are using `AIAgent` class to define the Triage, Sales, and Issue and Repair Agents.\n",
|
||||
"We added regular tools and delegate tools to each of them.\n",
|
||||
"We also added subscriptions to the topic types for each of the agents."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 37,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"runtime = SingleThreadedAgentRuntime()\n",
|
||||
"\n",
|
||||
"model_client = OpenAIChatCompletionClient(\n",
|
||||
" model=\"gpt-4o-mini\",\n",
|
||||
" # api_key=\"YOUR_API_KEY\",\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Register the triage agent.\n",
|
||||
"triage_agent_type = await AIAgent.register(\n",
|
||||
" runtime,\n",
|
||||
" type=triage_agent_topic_type, # Using the topic type as the agent type.\n",
|
||||
" factory=lambda: AIAgent(\n",
|
||||
" description=\"A triage agent.\",\n",
|
||||
" system_message=SystemMessage(\n",
|
||||
" content=\"You are a customer service bot for ACME Inc. \"\n",
|
||||
" \"Introduce yourself. Always be very brief. \"\n",
|
||||
" \"Gather information to direct the customer to the right department. \"\n",
|
||||
" \"But make your questions subtle and natural.\"\n",
|
||||
" ),\n",
|
||||
" model_client=model_client,\n",
|
||||
" tools=[],\n",
|
||||
" delegate_tools=[\n",
|
||||
" transfer_to_issues_and_repairs_tool,\n",
|
||||
" transfer_to_sales_agent_tool,\n",
|
||||
" escalate_to_human_tool,\n",
|
||||
" ],\n",
|
||||
" agent_topic_type=triage_agent_topic_type,\n",
|
||||
" user_topic_type=user_topic_type,\n",
|
||||
" ),\n",
|
||||
")\n",
|
||||
"# Add subscriptions for the triage agent: it will receive messages published to its own topic only.\n",
|
||||
"await runtime.add_subscription(TypeSubscription(topic_type=triage_agent_topic_type, agent_type=triage_agent_type.type))\n",
|
||||
"\n",
|
||||
"# Register the sales agent.\n",
|
||||
"sales_agent_type = await AIAgent.register(\n",
|
||||
" runtime,\n",
|
||||
" type=sales_agent_topic_type, # Using the topic type as the agent type.\n",
|
||||
" factory=lambda: AIAgent(\n",
|
||||
" description=\"A sales agent.\",\n",
|
||||
" system_message=SystemMessage(\n",
|
||||
" content=\"You are a sales agent for ACME Inc.\"\n",
|
||||
" \"Always answer in a sentence or less.\"\n",
|
||||
" \"Follow the following routine with the user:\"\n",
|
||||
" \"1. Ask them about any problems in their life related to catching roadrunners.\\n\"\n",
|
||||
" \"2. Casually mention one of ACME's crazy made-up products can help.\\n\"\n",
|
||||
" \" - Don't mention price.\\n\"\n",
|
||||
" \"3. Once the user is bought in, drop a ridiculous price.\\n\"\n",
|
||||
" \"4. Only after everything, and if the user says yes, \"\n",
|
||||
" \"tell them a crazy caveat and execute their order.\\n\"\n",
|
||||
" \"\"\n",
|
||||
" ),\n",
|
||||
" model_client=model_client,\n",
|
||||
" tools=[execute_order_tool],\n",
|
||||
" delegate_tools=[transfer_back_to_triage_tool],\n",
|
||||
" agent_topic_type=sales_agent_topic_type,\n",
|
||||
" user_topic_type=user_topic_type,\n",
|
||||
" ),\n",
|
||||
")\n",
|
||||
"# Add subscriptions for the sales agent: it will receive messages published to its own topic only.\n",
|
||||
"await runtime.add_subscription(TypeSubscription(topic_type=sales_agent_topic_type, agent_type=sales_agent_type.type))\n",
|
||||
"\n",
|
||||
"# Register the issues and repairs agent.\n",
|
||||
"issues_and_repairs_agent_type = await AIAgent.register(\n",
|
||||
" runtime,\n",
|
||||
" type=issues_and_repairs_agent_topic_type, # Using the topic type as the agent type.\n",
|
||||
" factory=lambda: AIAgent(\n",
|
||||
" description=\"An issues and repairs agent.\",\n",
|
||||
" system_message=SystemMessage(\n",
|
||||
" content=\"You are a customer support agent for ACME Inc.\"\n",
|
||||
" \"Always answer in a sentence or less.\"\n",
|
||||
" \"Follow the following routine with the user:\"\n",
|
||||
" \"1. First, ask probing questions and understand the user's problem deeper.\\n\"\n",
|
||||
" \" - unless the user has already provided a reason.\\n\"\n",
|
||||
" \"2. Propose a fix (make one up).\\n\"\n",
|
||||
" \"3. ONLY if not satesfied, offer a refund.\\n\"\n",
|
||||
" \"4. If accepted, search for the ID and then execute refund.\"\n",
|
||||
" ),\n",
|
||||
" model_client=model_client,\n",
|
||||
" tools=[\n",
|
||||
" execute_refund_tool,\n",
|
||||
" look_up_item_tool,\n",
|
||||
" ],\n",
|
||||
" delegate_tools=[transfer_back_to_triage_tool],\n",
|
||||
" agent_topic_type=issues_and_repairs_agent_topic_type,\n",
|
||||
" user_topic_type=user_topic_type,\n",
|
||||
" ),\n",
|
||||
")\n",
|
||||
"# Add subscriptions for the issues and repairs agent: it will receive messages published to its own topic only.\n",
|
||||
"await runtime.add_subscription(\n",
|
||||
" TypeSubscription(topic_type=issues_and_repairs_agent_topic_type, agent_type=issues_and_repairs_agent_type.type)\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Register the human agent.\n",
|
||||
"human_agent_type = await HumanAgent.register(\n",
|
||||
" runtime,\n",
|
||||
" type=human_agent_topic_type, # Using the topic type as the agent type.\n",
|
||||
" factory=lambda: HumanAgent(\n",
|
||||
" description=\"A human agent.\",\n",
|
||||
" agent_topic_type=human_agent_topic_type,\n",
|
||||
" user_topic_type=user_topic_type,\n",
|
||||
" ),\n",
|
||||
")\n",
|
||||
"# Add subscriptions for the human agent: it will receive messages published to its own topic only.\n",
|
||||
"await runtime.add_subscription(TypeSubscription(topic_type=human_agent_topic_type, agent_type=human_agent_type.type))\n",
|
||||
"\n",
|
||||
"# Register the user agent.\n",
|
||||
"user_agent_type = await UserAgent.register(\n",
|
||||
" runtime,\n",
|
||||
" type=user_topic_type,\n",
|
||||
" factory=lambda: UserAgent(\n",
|
||||
" description=\"A user agent.\",\n",
|
||||
" user_topic_type=user_topic_type,\n",
|
||||
" agent_topic_type=triage_agent_topic_type, # Start with the triage agent.\n",
|
||||
" ),\n",
|
||||
")\n",
|
||||
"# Add subscriptions for the user agent: it will receive messages published to its own topic only.\n",
|
||||
"await runtime.add_subscription(TypeSubscription(topic_type=user_topic_type, agent_type=user_agent_type.type))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Running the team\n",
|
||||
"\n",
|
||||
"Finally, we can start the runtime and simulate a user session by publishing\n",
|
||||
"a `UserLogin` message to the runtime.\n",
|
||||
"The message is published to the topic ID with type set to `user_topic_type` \n",
|
||||
"and source set to a unique `session_id`.\n",
|
||||
"This `session_id` will be used to create all topic IDs in this user session and will also be used to create the agent ID\n",
|
||||
"for all the agents in this user session.\n",
|
||||
"To read more about how topic ID and agent ID are created, read\n",
|
||||
"[Agent Identity and Lifecycle](../core-concepts/agent-identity-and-lifecycle.md).\n",
|
||||
"and [Topics and Subscriptions](../core-concepts/topic-and-subscription.md)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 38,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"User login, session ID: 7a568cf5-13e7-4e81-8616-8265a01b3f2b.\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"User:\n",
|
||||
"I want a refund\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"TriageAgent:\n",
|
||||
"I can help with that! Could I ask what item you're seeking a refund for?\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"User:\n",
|
||||
"A pair of shoes I bought\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"TriageAgent:\n",
|
||||
"[FunctionCall(id='call_qPx1DXDL2NLcHs8QNo47egsJ', arguments='{}', name='transfer_to_issues_and_repairs')]\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"TriageAgent:\n",
|
||||
"Delegating to IssuesAndRepairsAgent\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"IssuesAndRepairsAgent:\n",
|
||||
"I see you're looking for a refund on a pair of shoes. Can you tell me what the issue is with the shoes?\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"User:\n",
|
||||
"The shoes are too small\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"IssuesAndRepairsAgent:\n",
|
||||
"I recommend trying a size up as a fix; would that work for you?\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"User:\n",
|
||||
"no I want a refund\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"IssuesAndRepairsAgent:\n",
|
||||
"[FunctionCall(id='call_Ytp8VUQRyKFNEU36mLE6Dkrp', arguments='{\"search_query\":\"shoes\"}', name='look_up_item')]\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"IssuesAndRepairsAgent:\n",
|
||||
"[FunctionExecutionResult(content='item_132612938', call_id='call_Ytp8VUQRyKFNEU36mLE6Dkrp')]\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"IssuesAndRepairsAgent:\n",
|
||||
"[FunctionCall(id='call_bPm6EKKBy5GJ65s9OKt9b1uE', arguments='{\"item_id\":\"item_132612938\",\"reason\":\"not provided\"}', name='execute_refund')]\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"IssuesAndRepairsAgent:\n",
|
||||
"[FunctionExecutionResult(content='success', call_id='call_bPm6EKKBy5GJ65s9OKt9b1uE')]\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"IssuesAndRepairsAgent:\n",
|
||||
"Your refund has been successfully processed! If you have any other questions, feel free to ask.\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"User:\n",
|
||||
"I want to talk to your manager\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"IssuesAndRepairsAgent:\n",
|
||||
"I can help with that, let me transfer you to a supervisor.\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"User:\n",
|
||||
"Okay\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"IssuesAndRepairsAgent:\n",
|
||||
"[FunctionCall(id='call_PpmLZvwNoiDPUH8Tva3eAwHX', arguments='{}', name='transfer_back_to_triage')]\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"IssuesAndRepairsAgent:\n",
|
||||
"Delegating to TriageAgent\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"TriageAgent:\n",
|
||||
"[FunctionCall(id='call_jSL6IBm5537Dr74UbJSxaj6I', arguments='{}', name='escalate_to_human')]\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"TriageAgent:\n",
|
||||
"Delegating to HumanAgent\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"HumanAgent:\n",
|
||||
"Hello this is manager\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"User:\n",
|
||||
"Hi! Thanks for your service. I give you a 5 start!\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"HumanAgent:\n",
|
||||
"Thanks.\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"User:\n",
|
||||
"exit\n",
|
||||
"--------------------------------------------------------------------------------\n",
|
||||
"User session ended, session ID: 7a568cf5-13e7-4e81-8616-8265a01b3f2b.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Start the runtime.\n",
|
||||
"runtime.start()\n",
|
||||
"\n",
|
||||
"# Create a new session for the user.\n",
|
||||
"session_id = str(uuid.uuid4())\n",
|
||||
"await runtime.publish_message(UserLogin(), topic_id=TopicId(user_topic_type, source=session_id))\n",
|
||||
"\n",
|
||||
"# Run until completion.\n",
|
||||
"await runtime.stop_when_idle()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Next steps\n",
|
||||
"\n",
|
||||
"This notebook demonstrates how to implement the handoff pattern using AutoGen Core.\n",
|
||||
"You can continue to improve this design by adding more agents and tools,\n",
|
||||
"or create a better user interface for the User Agent and Human Agent.\n",
|
||||
"\n",
|
||||
"You are welcome to share your work on our [community forum](https://github.com/microsoft/autogen/discussions)."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.12"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 126 KiB |
@@ -1,11 +1,28 @@
|
||||
# Multi-Agent Design Patterns
|
||||
|
||||
Agents can work together in a variety of ways to solve problems.
|
||||
Research works like [AutoGen](https://aka.ms/autogen-paper),
|
||||
[MetaGPT](https://arxiv.org/abs/2308.00352)
|
||||
and [ChatDev](https://arxiv.org/abs/2307.07924) have shown
|
||||
multi-agent systems out-performing single agent systems at complex tasks
|
||||
like software development.
|
||||
|
||||
A multi-agent design pattern is a structure that emerges from message protocols:
|
||||
it describes how agents interact with each other to solve problems.
|
||||
For example, the [tool-equiped agent](../framework/tools.ipynb#tool-equipped-agent) in
|
||||
the previous section employs a design pattern called ReAct,
|
||||
which involves an agent interacting with tools.
|
||||
|
||||
You can implement any multi-agent design pattern using AutoGen agents.
|
||||
In the next two sections, we will discuss two common design patterns:
|
||||
group chat for task decomposition, and reflection for robustness.
|
||||
|
||||
```{toctree}
|
||||
:maxdepth: 1
|
||||
|
||||
group-chat
|
||||
handoffs
|
||||
mixture-of-agents
|
||||
multi-agent-debate
|
||||
multi-agent-design-patterns
|
||||
reflection
|
||||
```
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
# Multi-Agent Design Patterns
|
||||
|
||||
Agents can work together in a variety of ways to solve problems.
|
||||
Research works like [AutoGen](https://aka.ms/autogen-paper),
|
||||
[MetaGPT](https://arxiv.org/abs/2308.00352)
|
||||
and [ChatDev](https://arxiv.org/abs/2307.07924) have shown
|
||||
multi-agent systems out-performing single agent systems at complex tasks
|
||||
like software development.
|
||||
|
||||
A multi-agent design pattern is a structure that emerges from message protocols:
|
||||
it describes how agents interact with each other to solve problems.
|
||||
For example, the [tool-equiped agent](../framework/tools.ipynb#tool-equipped-agent) in
|
||||
the previous section employs a design pattern called ReAct,
|
||||
which involves an agent interacting with tools.
|
||||
|
||||
You can implement any multi-agent design pattern using AutoGen agents.
|
||||
In the next two sections, we will discuss two common design patterns:
|
||||
group chat for task decomposition, and reflection for robustness.
|
||||
Reference in New Issue
Block a user