From cec4407b9486ddb000d890128c5fbc32f5492b3d Mon Sep 17 00:00:00 2001 From: Mark Sze Date: Wed, 18 Dec 2024 07:47:46 +0000 Subject: [PATCH] Initial commit of notebook Signed-off-by: Mark Sze --- notebook/agentchat_swarm_enhanced.ipynb | 470 ++++++++++++++++++++++++ 1 file changed, 470 insertions(+) create mode 100644 notebook/agentchat_swarm_enhanced.ipynb diff --git a/notebook/agentchat_swarm_enhanced.ipynb b/notebook/agentchat_swarm_enhanced.ipynb new file mode 100644 index 0000000000..3a0a20e249 --- /dev/null +++ b/notebook/agentchat_swarm_enhanced.ipynb @@ -0,0 +1,470 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Enhanced Swarm Orchestration with AG2 \n", + "\n", + "AG2's swarm orchestration provides a flexible and powerful method of managing a conversation with multiple agents, tools, and transitions.\n", + "\n", + "In this notebook, we look at more advanced features of the swarm orchestration.\n", + "\n", + "If you are new to swarm, check out [this notebook](https://ag2ai.github.io/ag2/docs/notebooks/agentchat_swarm), where we introduce the core features of swarms including global context variables, hand offs, and initiating a swarm chat.\n", + "\n", + "In this notebook we're going to demonstrate these features AG2's swarm orchestration:\n", + "\n", + "- Updating an agent's state\n", + "- Conditional handoffs\n", + "- Nested chats" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "````{=mdx}\n", + ":::info Requirements\n", + "Install `ag2`:\n", + "```bash\n", + "pip install ag2\n", + "```\n", + "\n", + "For more information, please refer to the [installation guide](/docs/installation/).\n", + ":::\n", + "````" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Set your API Endpoint\n", + "\n", + "The [`config_list_from_json`](https://ag2ai.github.io/ag2/docs/reference/oai/openai_utils#config_list_from_json) function loads a list of configurations from an environment variable or a json file." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import autogen\n", + "\n", + "config_list = autogen.config_list_from_json(\n", + " \"OAI_CONFIG_LIST\",\n", + " filter_dict={\n", + " \"model\": [\"gpt-4o\"],\n", + " },\n", + ")\n", + "\n", + "llm_config = {\n", + " \"cache_seed\": 42, # change the cache_seed for different trials\n", + " \"temperature\": 1,\n", + " \"config_list\": config_list,\n", + " \"timeout\": 120,\n", + " \"tools\": [],\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Demonstration\n", + "\n", + "We're creating this customer service workflow for an e-commerce platform. Customers can ask about the status of their orders, but they must be authenticated to do so.\n", + "\n", + "![](swarm_enhanced_01.png)\n", + "\n", + "Key aspects of this swarm are:\n", + "\n", + "1. System messages are customised, incorporating the context of the workflow\n", + "2. Handoffs are conditional, only being available when they are relevant\n", + "3. A nested chat handles the order retrieval and summarisation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Any, Dict, List\n", + "\n", + "from autogen import (\n", + " AFTER_WORK,\n", + " ON_CONDITION,\n", + " UPDATE_SYSTEM_MESSAGE,\n", + " AfterWorkOption,\n", + " ConversableAgent,\n", + " SwarmAgent,\n", + " SwarmResult,\n", + " UserProxyAgent,\n", + " config_list_from_json,\n", + " initiate_swarm_chat,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "config_list = config_list_from_json(\n", + " \"/home/autogen/ag2/ms_code/OAI_CONFIG_LIST\",\n", + " filter_dict={\n", + " \"model\": [\"gpt-4o-mini\"],\n", + " },\n", + ")\n", + "\n", + "llm_config = {\n", + " \"config_list\": config_list,\n", + " \"cache_seed\": None, # Disables the cache\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "workflow_context = {\n", + " # customer details\n", + " \"customer_name\": None,\n", + " # workflow status\n", + " \"logged_in\": False,\n", + " \"requires_login\": True,\n", + " # order enquiry details\n", + " \"has_order_id\": False,\n", + " \"order_id\": None,\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Databases\n", + "\n", + "USER_DATABASE = {\n", + " \"mark\": {\n", + " \"full_name\": \"Mark Sze\",\n", + " },\n", + " \"kevin\": {\n", + " \"full_name\": \"Yiran Wu\",\n", + " },\n", + "}\n", + "\n", + "ORDER_DATABASE = {\n", + " \"TR13845\": {\n", + " \"order_number\": \"TR13845\",\n", + " \"status\": \"shipped\", # order status: order_received, shipped, delivered, return_started, returned\n", + " \"return_status\": \"N/A\", # return status: N/A, return_started, return_shipped, return_delivered, refund_issued\n", + " \"product\": \"matress\",\n", + " \"link\": \"https://www.example.com/TR13845\",\n", + " \"shipping_address\": \"123 Main St, State College, PA 12345\",\n", + " },\n", + " \"TR14234\": {\n", + " \"order_number\": \"TR14234\",\n", + " \"status\": \"delivered\",\n", + " \"return_status\": \"N/A\",\n", + " \"product\": \"pillow\",\n", + " \"link\": \"https://www.example.com/TR14234\",\n", + " \"shipping_address\": \"123 Main St, State College, PA 12345\",\n", + " },\n", + " \"TR29384\": {\n", + " \"order_number\": \"TR29384\",\n", + " \"status\": \"delivered\",\n", + " \"return_status\": \"N/A\",\n", + " \"product\": \"bed frame\",\n", + " \"link\": \"https://www.example.com/TR29384\",\n", + " \"shipping_address\": \"123 Main St, State College, PA 12345\",\n", + " },\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# ORDER FUNCTIONS\n", + "def check_order_id(order_id: str, context_variables: dict) -> SwarmResult:\n", + " \"\"\"Check if the order ID is valid\"\"\"\n", + " if order_id in ORDER_DATABASE:\n", + " return SwarmResult(\n", + " context_variables=context_variables, values=f\"Order ID {order_id} is valid.\", agent=order_triage_agent\n", + " )\n", + " else:\n", + " return SwarmResult(\n", + " context_variables=context_variables,\n", + " values=f\"Order ID {order_id} is invalid. Please ask for the correct order ID.\",\n", + " agent=order_triage_agent,\n", + " )\n", + "\n", + "\n", + "def record_order_id(order_id: str, context_variables: dict) -> SwarmResult:\n", + " \"\"\"Record the order ID in the workflow context\"\"\"\n", + "\n", + " if order_id not in ORDER_DATABASE:\n", + " return SwarmResult(\n", + " context_variables=context_variables,\n", + " values=f\"Order ID {order_id} not found. Please ask for the correct order ID.\",\n", + " agent=order_triage_agent,\n", + " )\n", + "\n", + " context_variables[\"order_id\"] = order_id\n", + " context_variables[\"has_order_id\"] = True\n", + " return SwarmResult(\n", + " context_variables=context_variables, values=f\"Order ID Recorded: {order_id}\", agent=order_mgmt_agent\n", + " )\n", + "\n", + "\n", + "# AUTHENTICATION FUNCTIONS\n", + "def login_customer_by_username(username: str, context_variables: dict) -> SwarmResult:\n", + " \"\"\"Get and log the customer in by their username\"\"\"\n", + " if username in USER_DATABASE:\n", + " context_variables[\"customer_name\"] = USER_DATABASE[username][\"full_name\"]\n", + " context_variables[\"logged_in\"] = True\n", + " context_variables[\"requires_login\"] = False\n", + " return SwarmResult(\n", + " context_variables=context_variables,\n", + " values=f\"Welcome back our customer, {context_variables['customer_name']}! Please continue helping them.\",\n", + " agent=order_triage_agent,\n", + " )\n", + " else:\n", + " return SwarmResult(\n", + " context_variables=context_variables,\n", + " values=f\"User {username} not found. Please ask for the correct username.\",\n", + " agent=authentication_agent,\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# AGENTS\n", + "\n", + "# Human customer\n", + "user = UserProxyAgent(\n", + " name=\"customer\",\n", + " code_execution_config=False,\n", + ")\n", + "\n", + "order_triage_prompt = \"\"\"You are an order triage agent, working with a customer and a group of agents to provide support for your e-commerce platform.\n", + "\n", + "An agent needs to be logged in to be able to access their order. The authentication_agent will work with the customer to verify their identity, transfer to them to start with.\n", + "The order_mgmt_agent will manage all order related tasks, such as tracking orders, managing orders, etc. Be sure to check the order as one step. Then if it's valid you can record it in the context.\n", + "\n", + "Ask the customer for further information when necessary.\n", + "\n", + "The current status of this workflow is:\n", + "Customer name: {customer_name}\n", + "Logged in: {logged_in}\n", + "Enquiring for Order ID: {order_id}\n", + "\"\"\"\n", + "\n", + "order_triage_agent = SwarmAgent(\n", + " name=\"order_triage_agent\",\n", + " update_agent_state_before_reply=[\n", + " UPDATE_SYSTEM_MESSAGE(order_triage_prompt),\n", + " ],\n", + " functions=[check_order_id, record_order_id],\n", + " llm_config=llm_config,\n", + ")\n", + "\n", + "authentication_prompt = \"You are an authentication agent that verifies the identity of the customer.\"\n", + "\n", + "authentication_agent = SwarmAgent(\n", + " name=\"authentication_agent\",\n", + " system_message=authentication_prompt,\n", + " functions=[login_customer_by_username],\n", + " llm_config=llm_config,\n", + ")\n", + "\n", + "order_management_prompt = \"\"\"You are an order management agent that manages inquiries related to e-commerce orders.\n", + "\n", + "The order must be logged in to access their order.\n", + "\n", + "Use your available tools to get the status of the details from the customer. Ask the customer questions as needed.\n", + "\n", + "The current status of this workflow is:\n", + "Customer name: {customer_name}\n", + "Logged in: {logged_in}\n", + "Enquiring for Order ID: {order_id}\n", + "\"\"\"\n", + "\n", + "order_mgmt_agent = SwarmAgent(\n", + " name=\"order_mgmt_agent\",\n", + " update_agent_state_before_reply=[\n", + " UPDATE_SYSTEM_MESSAGE(order_management_prompt),\n", + " ],\n", + " functions=[check_order_id, record_order_id],\n", + " llm_config=llm_config,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# NESTED CHAT - Delivery Status\n", + "order_retrieval_agent = ConversableAgent(\n", + " name=\"order_retrieval_agent\",\n", + " system_message=\"You are an order retrieval agent that gets details about an order.\",\n", + " llm_config=llm_config,\n", + ")\n", + "\n", + "order_summariser_agent = ConversableAgent(\n", + " name=\"order_summariser_agent\",\n", + " system_message=\"You are an order summariser agent that provides a summary of the order details.\",\n", + " llm_config=llm_config,\n", + ")\n", + "\n", + "\n", + "def extract_order_summary(recipient: ConversableAgent, messages, sender: ConversableAgent, config):\n", + " \"\"\"Extracts the order summary based on the OrderID in the context variables\"\"\"\n", + " order_id = sender.get_context(\"order_id\")\n", + " if order_id in ORDER_DATABASE:\n", + " order = ORDER_DATABASE[order_id]\n", + " return f\"Order {order['order_number']} for {order['product']} is currently {order['status']}. The shipping address is {order['shipping_address']}.\"\n", + " else:\n", + " return f\"Order {order_id} not found.\"\n", + "\n", + "\n", + "nested_chat_one = {\n", + " \"carryover_config\": {\"summary_method\": \"last_msg\"},\n", + " \"recipient\": order_retrieval_agent,\n", + " \"message\": extract_order_summary, # \"Retrieve the status details of the order using the order id\",\n", + " \"max_turns\": 1,\n", + "}\n", + "\n", + "nested_chat_two = {\n", + " \"recipient\": order_summariser_agent,\n", + " \"message\": \"Summarise the order details provided in a tabulated, text-based, order sheet format\",\n", + " \"max_turns\": 1,\n", + " \"summary_method\": \"last_msg\",\n", + "}\n", + "\n", + "chat_queue = [nested_chat_one, nested_chat_two]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# HANDOFFS\n", + "order_triage_agent.register_hand_off(\n", + " [\n", + " ON_CONDITION(\n", + " target=authentication_agent,\n", + " condition=\"The customer is not logged in, authenticate the customer.\",\n", + " available=\"requires_login\",\n", + " ),\n", + " ON_CONDITION(\n", + " target=order_mgmt_agent,\n", + " condition=\"The customer is logged in, continue with the order triage.\",\n", + " available=\"logged_in\",\n", + " ),\n", + " AFTER_WORK(AfterWorkOption.REVERT_TO_USER),\n", + " ]\n", + ")\n", + "\n", + "authentication_agent.register_hand_off(\n", + " [\n", + " ON_CONDITION(\n", + " target=order_triage_agent,\n", + " condition=\"The customer is logged in, continue with the order triage.\",\n", + " available=\"logged_in\",\n", + " ),\n", + " AFTER_WORK(AfterWorkOption.REVERT_TO_USER),\n", + " ]\n", + ")\n", + "\n", + "\n", + "def has_order_in_context(agent: SwarmAgent, messages: List[Dict[str, Any]]) -> bool:\n", + " return agent.get_context(\"has_order_id\")\n", + "\n", + "\n", + "order_mgmt_agent.register_hand_off(\n", + " [\n", + " ON_CONDITION(\n", + " target={\n", + " \"chat_queue\": chat_queue,\n", + " },\n", + " condition=\"Retrieve the status of the order\",\n", + " available=has_order_in_context,\n", + " ),\n", + " ON_CONDITION(\n", + " target=authentication_agent,\n", + " condition=\"The customer is not logged in, authenticate the customer.\",\n", + " available=\"requires_login\",\n", + " ),\n", + " ON_CONDITION(target=order_triage_agent, condition=\"The customer has no more enquiries about this order.\"),\n", + " AFTER_WORK(AfterWorkOption.REVERT_TO_USER),\n", + " ]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "chat_history = initiate_swarm_chat(\n", + " initial_agent=order_triage_agent,\n", + " agents=[order_triage_agent, authentication_agent, order_mgmt_agent],\n", + " context_variables=workflow_context,\n", + " messages=\"Can you help me with my order.\",\n", + " user_agent=user,\n", + " max_rounds=40,\n", + " after_work=AfterWorkOption.TERMINATE,\n", + ")" + ] + } + ], + "metadata": { + "front_matter": { + "description": "Swarm Ochestration", + "tags": [ + "orchestration", + "group chat", + "swarm" + ] + }, + "kernelspec": { + "display_name": "autodev", + "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.9.19" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}