287 lines
10 KiB
Plaintext
287 lines
10 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "a1b2c3d4",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Lesson 07 - Planning Design Pattern\n",
|
|
"\n",
|
|
"This notebook demonstrates the **Planning Design Pattern** for AI agents using the Microsoft Agent Framework.\n",
|
|
"You will learn how to break complex travel requests into structured subtasks, assign them to specialist agents,\n",
|
|
"and execute the resulting plan — all using structured output powered by Pydantic models."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "b2c3d4e5",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Setup"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "c3d4e5f6",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"%pip install agent-framework azure-ai-projects azure-identity python-dotenv -q"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "d4e5f6g7",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import logging\n",
|
|
"logging.getLogger(\"agent_framework.foundry\").setLevel(logging.ERROR)\n",
|
|
"\n",
|
|
"import os, asyncio\n",
|
|
"import dotenv\n",
|
|
"from typing import Annotated\n",
|
|
"from pydantic import BaseModel\n",
|
|
"from agent_framework import tool\n",
|
|
"from agent_framework.foundry import FoundryChatClient\n",
|
|
"from azure.identity import DefaultAzureCredential\n",
|
|
"\n",
|
|
"dotenv.load_dotenv()\n",
|
|
"\n",
|
|
"endpoint = os.getenv(\"AZURE_AI_PROJECT_ENDPOINT\")\n",
|
|
"deployment_name = os.getenv(\"AZURE_AI_MODEL_DEPLOYMENT_NAME\")\n",
|
|
"\n",
|
|
"missing = [k for k, v in {\n",
|
|
" \"AZURE_AI_PROJECT_ENDPOINT\": endpoint,\n",
|
|
" \"AZURE_AI_MODEL_DEPLOYMENT_NAME\": deployment_name\n",
|
|
"}.items() if not v]\n",
|
|
"\n",
|
|
"if missing:\n",
|
|
" raise ValueError(\n",
|
|
" f\"Missing required environment variables: {', '.join(missing)}. \"\n",
|
|
" \"Please set them as environment variables (e.g., in your .env file or shell environment).\"\n",
|
|
" )"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "e5f6g7h8",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Create the Microsoft Foundry client\n",
|
|
"client = FoundryChatClient(\n",
|
|
" project_endpoint=endpoint,\n",
|
|
" model=deployment_name,\n",
|
|
" credential=DefaultAzureCredential()\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "f6g7h8i9",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Task Decomposition\n",
|
|
"\n",
|
|
"Task decomposition is the core of the planning design pattern. Instead of asking a single agent to\n",
|
|
"handle a complex request end-to-end, we break the problem into smaller, well-defined **subtasks**.\n",
|
|
"Each subtask is assigned to a specialist agent (e.g., flights, hotels, activities) with clear\n",
|
|
"priorities and dependency ordering.\n",
|
|
"\n",
|
|
"This approach provides several benefits:\n",
|
|
"- **Clarity**: each subtask has a single responsibility.\n",
|
|
"- **Parallelism**: independent subtasks can run concurrently.\n",
|
|
"- **Reliability**: failures are isolated to individual subtasks.\n",
|
|
"- **Budget tracking**: costs are estimated per subtask and rolled up."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "g7h8i9j0",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class TravelSubTask(BaseModel):\n",
|
|
" task_id: int\n",
|
|
" description: str\n",
|
|
" assigned_agent: str # \"flight_agent\", \"hotel_agent\", \"activity_agent\"\n",
|
|
" priority: str # \"high\", \"medium\", \"low\"\n",
|
|
" dependencies: list[int] = []\n",
|
|
"\n",
|
|
"\n",
|
|
"class TravelPlan(BaseModel):\n",
|
|
" destination: str\n",
|
|
" trip_duration_days: int\n",
|
|
" subtasks: list[TravelSubTask]\n",
|
|
" total_estimated_budget_usd: int\n",
|
|
" notes: str"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "h8i9j0k1",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Creating a Planning Agent with Structured Output\n",
|
|
"\n",
|
|
"The planning agent acts as a **front desk coordinator**. Given a high-level travel request it\n",
|
|
"produces a structured `TravelPlan` — decomposing the request into subtasks, setting priorities,\n",
|
|
"and identifying dependencies so that a concierge or execution layer can carry out the work."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "i9j0k1l2",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"planning_agent = client.as_agent(\n",
|
|
" name=\"TravelPlanner\",\n",
|
|
" instructions=\"\"\"You are a travel planning agent. When given a travel request:\n",
|
|
"1. Break it into specific subtasks (flights, hotels, activities, logistics)\n",
|
|
"2. Assign each subtask to the appropriate specialist agent\n",
|
|
"3. Set priorities and identify dependencies between tasks\n",
|
|
"4. Estimate the total budget\"\"\",\n",
|
|
")\n",
|
|
"\n",
|
|
"result = await planning_agent.run(\n",
|
|
" \"Plan a 7-day trip to Paris for a couple interested in art, cuisine, and history. Budget around $5000.\",\n",
|
|
" options={\"response_format\": TravelPlan}\n",
|
|
")\n",
|
|
"if result:\n",
|
|
" plan = result.value\n",
|
|
" print(f\"Destination: {plan.destination}\")\n",
|
|
" print(f\"Duration: {plan.trip_duration_days} days\")\n",
|
|
" print(f\"Budget: ${plan.total_estimated_budget_usd}\")\n",
|
|
" print(f\"\\nSubtasks:\")\n",
|
|
" for task in plan.subtasks:\n",
|
|
" print(f\" [{task.priority}] {task.task_id}. {task.description} → {task.assigned_agent}\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "j0k1l2m3",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Executing a Plan with Specialist Tools\n",
|
|
"\n",
|
|
"Once the front desk agent has produced a structured plan, the **concierge agent** executes it.\n",
|
|
"Each specialist tool handles one category of subtask (flights, hotels, activities). The concierge\n",
|
|
"iterates through the plan's subtasks in dependency order and dispatches each one to the\n",
|
|
"appropriate tool."
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": null,
|
|
"id": "k1l2m3n4",
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"@tool\n",
|
|
"def book_flight(\n",
|
|
" destination: Annotated[str, \"The destination city\"],\n",
|
|
" departure_date: Annotated[str, \"Departure date (YYYY-MM-DD)\"],\n",
|
|
" return_date: Annotated[str, \"Return date (YYYY-MM-DD)\"],\n",
|
|
") -> str:\n",
|
|
" \"\"\"Search and book flights for the trip.\"\"\"\n",
|
|
" return f\"Flight booked to {destination}: {departure_date} → {return_date}, confirmation #FLT-{hash(destination) % 10000:04d}\"\n",
|
|
"\n",
|
|
"\n",
|
|
"@tool\n",
|
|
"def reserve_hotel(\n",
|
|
" city: Annotated[str, \"The city for the hotel\"],\n",
|
|
" check_in: Annotated[str, \"Check-in date (YYYY-MM-DD)\"],\n",
|
|
" check_out: Annotated[str, \"Check-out date (YYYY-MM-DD)\"],\n",
|
|
" guests: Annotated[int, \"Number of guests\"],\n",
|
|
") -> str:\n",
|
|
" \"\"\"Reserve a hotel room in the destination city.\"\"\"\n",
|
|
" return f\"Hotel reserved in {city}: {check_in} to {check_out} for {guests} guests, confirmation #HTL-{hash(city) % 10000:04d}\"\n",
|
|
"\n",
|
|
"\n",
|
|
"@tool\n",
|
|
"def book_activity(\n",
|
|
" activity_name: Annotated[str, \"Name of the activity or tour\"],\n",
|
|
" date: Annotated[str, \"Date of the activity (YYYY-MM-DD)\"],\n",
|
|
" participants: Annotated[int, \"Number of participants\"],\n",
|
|
") -> str:\n",
|
|
" \"\"\"Book a tour, museum visit, or other activity.\"\"\"\n",
|
|
" return f\"Activity booked: {activity_name} on {date} for {participants} people, confirmation #ACT-{hash(activity_name) % 10000:04d}\"\n",
|
|
"\n",
|
|
"\n",
|
|
"# Concierge agent that executes the plan using specialist tools\n",
|
|
"concierge_agent = client.as_agent(\n",
|
|
" name=\"Concierge\",\n",
|
|
" instructions=\"\"\"You are a travel concierge executing a structured travel plan.\n",
|
|
"Use the available tools to fulfil each subtask. Work through the subtasks in order,\n",
|
|
"respecting dependencies. Summarise the results when finished.\"\"\",\n",
|
|
" tools=[book_flight, reserve_hotel, book_activity],\n",
|
|
")\n",
|
|
"\n",
|
|
"# Build a prompt from the plan produced above\n",
|
|
"if result.value:\n",
|
|
" subtask_lines = \"\\n\".join(\n",
|
|
" f\"- [{t.priority}] {t.task_id}. {t.description} (agent: {t.assigned_agent}, deps: {t.dependencies})\"\n",
|
|
" for t in plan.subtasks\n",
|
|
" )\n",
|
|
" execution_prompt = (\n",
|
|
" f\"Execute the following travel plan for {plan.destination} \"\n",
|
|
" f\"({plan.trip_duration_days} days, ${plan.total_estimated_budget_usd} budget):\\n\"\n",
|
|
" f\"{subtask_lines}\"\n",
|
|
" )\n",
|
|
"\n",
|
|
" exec_response = await concierge_agent.run(execution_prompt)\n",
|
|
" print(exec_response)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"id": "l2m3n4o5",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Summary\n",
|
|
"\n",
|
|
"In this lesson you learned the **Planning Design Pattern** for AI agents:\n",
|
|
"\n",
|
|
"1. **Task Decomposition** — A front desk planning agent breaks a complex travel request into\n",
|
|
" structured subtasks using Pydantic models, assigning each to a specialist agent with priorities\n",
|
|
" and dependencies.\n",
|
|
"2. **Structured Output** — By passing a `response_format` the agent returns a validated\n",
|
|
" `TravelPlan` object instead of free-form text, making downstream processing reliable.\n",
|
|
"3. **Plan Execution** — A concierge agent iterates through the subtasks using specialist tools\n",
|
|
" (`book_flight`, `reserve_hotel`, `book_activity`) to carry out the plan and report results.\n",
|
|
"\n",
|
|
"This pattern separates *what to do* (planning) from *how to do it* (execution), making agents\n",
|
|
"more modular, testable, and easier to extend."
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "Python 3",
|
|
"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.12.13"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|