Files
2026-07-13 12:59:43 +08:00

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
}