1_agentic-design-ptn/03_multi-agent/SK/01.1_multi-agent-collabration.ipynb (428 lines of code) (raw):
{
"cells": [
{
"cell_type": "markdown",
"id": "5c95e9bf",
"metadata": {},
"source": [
"# Multi-Agent Collaboration \n",
"---\n",
"\n",
"### What is Multi-Agent Collaboration?\n",
"Multi-Agent Collaboration refers to the process where multiple autonomous agents—each capable of independent decision-making—work together to achieve common or complementary objectives. This concept is widely used in fields like artificial intelligence, robotics, distributed computing, and simulation, and it involves several key aspects:\n",
"\n",
"- **Effective Communication and Coordination**:\n",
"Agents exchange information and align their actions to collectively achieve a goal, ensuring that tasks are organized and synchronized.\n",
"\n",
"- **Autonomous, Distributed Decision-Making**:\n",
"Each agent operates independently, making local decisions while contributing to a broader strategy, which enhances flexibility and fault tolerance.\n",
"\n",
"- **Adaptive Task Specialization**:\n",
"Agents focus on specific roles or subtasks based on their capabilities, and they adjust their strategies through iterative feedback, leading to improved overall performance.\n",
"\n",
"\n",
"### Key Advantages\n",
"- **Efficiency Through Task Specialization**:\n",
"By assigning specific roles to different agents, the system can handle complex tasks in parallel. This specialization allows each agent to focus on its area of expertise, resulting in faster and more effective problem-solving.\n",
"\n",
"- **Scalability and Flexibility**:\n",
"AutoGen's structured communication and dynamic task allocation enable the system to scale easily. It can adapt to varying project complexities by adding or reassigning agents as needed, ensuring that the collaboration remains robust even as demands change.\n",
"\n",
"- **Enhanced Iterative Refinement**:\n",
"The framework’s built-in feedback loops and iterative dialogue facilitate continuous improvement. Agents can refine their outputs based on real-time feedback, leading to more accurate and cohesive final results.\n",
"\n",
"**Reference**\n",
"- [AutoGen paper: Enabling Next-Gen LLM Applications via Multi-Agent Conversation](https://arxiv.org/abs/2308.08155)\n",
"- [Multi-Agent Collabration Concept](https://langchain-ai.github.io/langgraph/concepts/multi_agent/#network) "
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "eb1ad59b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import asyncio\n",
"from typing import Annotated\n",
"from dotenv import load_dotenv\n",
"import json\n",
"import os\n",
"from enum import Enum\n",
"from semantic_kernel import Kernel\n",
"from semantic_kernel.kernel_pydantic import KernelBaseSettings\n",
"from semantic_kernel.agents import AzureAIAgent, AzureAIAgentSettings\n",
"from azure.identity.aio import DefaultAzureCredential\n",
"from semantic_kernel.agents.strategies.termination.termination_strategy import TerminationStrategy\n",
"from semantic_kernel.agents import AgentGroupChat\n",
"from semantic_kernel.contents.utils.author_role import AuthorRole\n",
"from semantic_kernel.contents import AuthorRole\n",
"from semantic_kernel.agents import ChatCompletionAgent, ChatHistoryAgentThread\n",
"\n",
"\n",
"\n",
"kernel = Kernel()\n",
"\n",
"\n",
"\n",
"class Service(Enum):\n",
" \"\"\"Attributes:\n",
" OpenAI (str): Represents the OpenAI service.\n",
" AzureOpenAI (str): Represents the Azure OpenAI service.\n",
" HuggingFace (str): Represents the HuggingFace service.\n",
" \"\"\"\n",
"\n",
" OpenAI = \"openai\"\n",
" AzureOpenAI = \"azureopenai\"\n",
" HuggingFace = \"huggingface\"\n",
"\n",
"class ServiceSettings(KernelBaseSettings):\n",
" \"\"\"The Learn Resources Service Settings.\n",
"\n",
" The settings are first loaded from environment variables. If the\n",
" environment variables are not found, the settings can be loaded from a .env file with the\n",
" encoding 'utf-8' as default or the specific encoding. If the settings are not found in the\n",
" .env file, the settings are ignored; however, validation will fail alerting that the settings\n",
" are missing.\n",
"\n",
" Args:\n",
" global_llm_service (str | None): The LLM service to use for the samples, either \"OpenAI\" or \"AzureOpenAI\"\n",
" If not provided, defaults to \"AzureOpenAI\".\n",
" \"\"\"\n",
"\n",
" global_llm_service: str | None = None\n",
" \n",
"load_dotenv(override=True)"
]
},
{
"cell_type": "markdown",
"id": "149de0e8",
"metadata": {},
"source": [
"We will load our settings and get the LLM service to use for the notebook."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "1a6efcd5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Using service type: Service.AzureOpenAI\n"
]
}
],
"source": [
"service_settings = ServiceSettings.create()\n",
"\n",
"# Select a service to use for this notebook (available services: OpenAI, AzureOpenAI, HuggingFace)\n",
"selectedService = (\n",
" Service.AzureOpenAI\n",
" if service_settings.global_llm_service is None\n",
" else Service(service_settings.global_llm_service.lower())\n",
")\n",
"print(f\"Using service type: {selectedService}\")"
]
},
{
"cell_type": "markdown",
"id": "886c5a94",
"metadata": {},
"source": [
"We now configure our Chat Completion service on the kernel."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "f4862f89",
"metadata": {},
"outputs": [],
"source": [
"# Remove all services so that this cell can be re-run without restarting the kernel\n",
"kernel.remove_all_services()\n",
"\n",
"service_id = None\n",
"if selectedService == Service.OpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion\n",
"\n",
" service_id = \"default\"\n",
" kernel.add_service(\n",
" OpenAIChatCompletion(\n",
" service_id=service_id,\n",
" ),\n",
" )\n",
"elif selectedService == Service.AzureOpenAI:\n",
" from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion\n",
"\n",
" service_id = \"default\"\n",
" kernel.add_service(\n",
" AzureChatCompletion(\n",
" service_id=service_id,\n",
" ),\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "3a9c06ee",
"metadata": {},
"source": [
"<br>\n",
"\n",
"\n",
"## 🧪 Step 1. Define the Agentic Architecture\n",
"- Before building the agentic pipeline, we need to design the message, topic, agent and message routing logic. \n",
"- You should define the terminate condition for the pipeline."
]
},
{
"cell_type": "markdown",
"id": "b88a7250",
"metadata": {},
"source": [
"### Define Agents\n",
"\n",
"In the next section we will define the agents that will be used in the travel planning team."
]
},
{
"cell_type": "markdown",
"id": "7400eb1c",
"metadata": {},
"source": [
"<br>\n",
"\n",
"## 🧪 Step 2. Execute the Agents TeamChat\n",
"\n",
"### Execute the group chat with the termination condition"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "df12d945",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"# AuthorRole.USER: 'a slogan for a new line of electric cars.'\n",
"# AuthorRole.ASSISTANT - CopyWriter: '\"Drive the Future: Shockingly Efficient.\"'\n",
"# AuthorRole.ASSISTANT - ArtDirector: 'This slogan is clever and taps into the notion of electric cars being innovative. However, it may benefit from a slightly clearer connection to sustainability or environmental benefits, which are critical in the electric vehicle market. Focus on evoking feelings of responsibility or enthusiasm towards eco-friendliness, ensuring it resonates with the target audience's values.'\n",
"# AuthorRole.ASSISTANT - CopyWriter: '\"Electrify Your Drive: Go Green, Go Far.\"'\n",
"# AuthorRole.ASSISTANT - ArtDirector: 'This slogan has potential, as it connects the electric aspect to driving and incorporates a sustainability angle. However, it could be strengthened by ensuring it is more concise and impactful. Consider focusing on the unique benefits of the electric cars, like performance, innovation, or a more vivid image of the future of driving. Strive for a punchier phrase that leaves a memorable impression.'\n",
"# AuthorRole.ASSISTANT - CopyWriter: '\"Feel the Power: Drive Electric.\"'\n",
"# AuthorRole.ASSISTANT - ArtDirector: 'This slogan captures the excitement and appeal of electric vehicles effectively. However, it could be refined to enhance its uniqueness and emotional resonance. Consider incorporating a sense of environmental consciousness or the transformational aspect of driving electric. This will help convey not just power, but also the positive impact of choosing an electric vehicle.'\n",
"# AuthorRole.ASSISTANT - CopyWriter: '\"Charge Ahead: Drive the Change.\"'\n",
"# AuthorRole.ASSISTANT - ArtDirector: 'This slogan presents a strong call to action and cleverly ties together the idea of charging with making a positive change. It works well on multiple levels, evoking a sense of progress and sustainability. This copy is approved for print.'\n"
]
}
],
"source": [
"class ApprovalTerminationStrategy(TerminationStrategy):\n",
" \"\"\"A strategy for determining when an agent should terminate.\"\"\"\n",
"\n",
" async def should_agent_terminate(self, agent, history):\n",
" \"\"\"Check if the agent should terminate.\"\"\"\n",
" return \"approved\" in history[-1].content.lower()\n",
"\n",
"\n",
"REVIEWER_NAME = \"ArtDirector\"\n",
"REVIEWER_INSTRUCTIONS = \"\"\"\n",
"You are an art director who has opinions about copywriting born of a love for David Ogilvy.\n",
"The goal is to determine if the given copy is acceptable to print.\n",
"If so, state that it is approved. Do not use the word \"approve\" unless you are giving approval.\n",
"If not, provide insight on how to refine suggested copy without example.\n",
"\"\"\"\n",
"\n",
"COPYWRITER_NAME = \"CopyWriter\"\n",
"COPYWRITER_INSTRUCTIONS = \"\"\"\n",
"You are a copywriter with ten years of experience and are known for brevity and a dry humor.\n",
"The goal is to refine and decide on the single best copy as an expert in the field.\n",
"Only provide a single proposal per response.\n",
"You're laser focused on the goal at hand.\n",
"Don't waste time with chit chat.\n",
"Consider suggestions when refining an idea.\n",
"\"\"\"\n",
"\n",
"TASK = \"a slogan for a new line of electric cars.\"\n",
"\n",
"ai_agent_settings = AzureAIAgentSettings.create()\n",
"\n",
"async with (\n",
" DefaultAzureCredential() as creds,\n",
" AzureAIAgent.create_client(credential=creds) as client,\n",
"):\n",
" # 1. Create the reviewer agent on the Azure AI agent service\n",
" reviewer_agent_definition = await client.agents.create_agent(\n",
" model=ai_agent_settings.model_deployment_name,\n",
" name=REVIEWER_NAME,\n",
" instructions=REVIEWER_INSTRUCTIONS,\n",
" )\n",
"\n",
" # 2. Create a Semantic Kernel agent for the reviewer Azure AI agent\n",
" agent_reviewer = AzureAIAgent(\n",
" client=client,\n",
" definition=reviewer_agent_definition,\n",
" )\n",
"\n",
" # 3. Create the copy writer agent on the Azure AI agent service\n",
" copy_writer_agent_definition = await client.agents.create_agent(\n",
" model=ai_agent_settings.model_deployment_name,\n",
" name=COPYWRITER_NAME,\n",
" instructions=COPYWRITER_INSTRUCTIONS,\n",
" )\n",
"\n",
" # 4. Create a Semantic Kernel agent for the copy writer Azure AI agent\n",
" agent_writer = AzureAIAgent(\n",
" client=client,\n",
" definition=copy_writer_agent_definition,\n",
" )\n",
"\n",
" # 5. Place the agents in a group chat with a custom termination strategy\n",
" chat = AgentGroupChat(\n",
" agents=[agent_writer, agent_reviewer],\n",
" termination_strategy=ApprovalTerminationStrategy(agents=[agent_reviewer], maximum_iterations=10),\n",
" )\n",
" \n",
" try:\n",
" # 6. Add the task as a message to the group chat\n",
" await chat.add_chat_message(message=TASK)\n",
" print(f\"# {AuthorRole.USER}: '{TASK}'\")\n",
" # 7. Invoke the chat\n",
" async for content in chat.invoke():\n",
" print(f\"# {content.role} - {content.name or '*'}: '{content.content}'\")\n",
" finally:\n",
" # 8. Cleanup: Delete the agents\n",
" await chat.reset()\n",
" await client.agents.delete_agent(agent_reviewer.id)\n",
" await client.agents.delete_agent(agent_writer.id)\n",
"\n",
" "
]
},
{
"cell_type": "markdown",
"id": "4b181aaf",
"metadata": {},
"source": [
"## 🧪 Case 3 group chat with ChatCompletionAgent\n",
"---"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5363bd68",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"# User: a slogan for a new line of electric cars.\n",
"# CopyWriter: \"Drive the Future. Charge Your Journey.\"\n",
"# ArtDirector: This slogan captures an optimistic and forward-thinking approach, but it may benefit from refining to create a stronger emotional connection and clarity about the unique selling proposition of the electric cars. Consider focusing on the key benefits of electric vehicles—like sustainability, innovation, or performance—while ensuring that it resonates with the target audience's aspirations.\n",
"# CopyWriter: \"Silent Power. Unplug the Ordinary.\"\n",
"# ArtDirector: This slogan has potential, especially with its emphasis on \"Silent Power,\" which suggests a modern and innovative vehicle. However, \"Unplug the Ordinary\" could be more specific in its messaging. Consider refining it to clarify how the product stands apart from traditional vehicles, possibly by highlighting a key benefit or feature that users would find appealing. Strengthening the emotional pull and clarity around the electric car experience can make it more compelling.\n",
"# CopyWriter: \"Quietly Revolutionary. Drive Different.\"\n",
"# ArtDirector: This slogan is intriguing and hints at innovation, but \"Drive Different\" could be perceived as vague. To make it stronger, consider incorporating a more concrete concept of how the driving experience differs with your electric cars. Highlighting a specific aspect of the vehicle—like performance, efficiency, or environmental benefits—can foster a deeper connection with the audience and provide clearer motivation to explore the product further.\n",
"# CopyWriter: \"Powerful. Efficient. Unstoppable.\"\n",
"# ArtDirector: This slogan emphasizes strength and efficiency, which are valuable traits for electric cars. However, it lacks a memorable twist or emotional appeal that would distinguish your brand. Consider incorporating a playful or evocative element that speaks to the experience of driving an electric car, or perhaps the lifestyle it promotes. Engaging the audience's imagination or aspirations can elevate the impact of this message.\n",
"# CopyWriter: \"Charge Ahead. Conquer Tomorrow.\"\n",
"# ArtDirector: This slogan effectively combines action with a forward-looking perspective, which is compelling for a new line of electric cars. However, it could benefit from greater specificity to highlight what sets this brand apart. Consider integrating a unique aspect of the electric cars that aligns with the notion of \"conquering tomorrow.\" This could make the message even more impactful and resonant with potential customers.\n"
]
}
],
"source": [
"TASK = \"a slogan for a new line of electric cars.\"\n",
"\n",
"def _create_kernel_with_chat_completion(service_id: str) -> Kernel:\n",
" kernel = Kernel()\n",
" kernel.add_service(AzureChatCompletion(service_id=service_id))\n",
" return kernel\n",
"\n",
"# 1. Create the reviewer agent based on the chat completion service\n",
"agent_reviewer = ChatCompletionAgent(\n",
" kernel=_create_kernel_with_chat_completion(\"artdirector\"),\n",
" name=REVIEWER_NAME,\n",
" instructions=REVIEWER_INSTRUCTIONS,\n",
")\n",
"\n",
"# 2. Create the copywriter agent based on the chat completion service\n",
"agent_writer = ChatCompletionAgent(\n",
" kernel=_create_kernel_with_chat_completion(\"copywriter\"),\n",
" name=COPYWRITER_NAME,\n",
" instructions=COPYWRITER_INSTRUCTIONS,\n",
")\n",
"\n",
"# 3. Place the agents in a group chat with a custom termination strategy\n",
"group_chat = AgentGroupChat(\n",
" agents=[\n",
" agent_writer,\n",
" agent_reviewer,\n",
" ],\n",
" termination_strategy=ApprovalTerminationStrategy(\n",
" agents=[agent_reviewer],\n",
" maximum_iterations=10,\n",
" ),\n",
")\n",
"\n",
"# 4. Add the task as a message to the group chat\n",
"await group_chat.add_chat_message(message=TASK)\n",
"print(f\"# User: {TASK}\")\n",
"\n",
"# 5. Invoke the chat\n",
"async for content in group_chat.invoke():\n",
" print(f\"# {content.name}: {content.content}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bc829b39",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "venv_agent",
"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.11.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}