{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a1b2c3d4",
   "metadata": {},
   "source": [
    "# 第07课 - 规划设计模式\n",
    "\n",
    "本笔记本演示了使用Microsoft Agent Framework的<strong>规划设计模式</strong>，适用于AI代理。\n",
    "您将学习如何将复杂的旅行请求拆分为结构化的子任务，分配给专业代理，\n",
    "并执行生成的计划——所有这些都通过Pydantic模型驱动的结构化输出实现。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2c3d4e5",
   "metadata": {},
   "source": [
    "## 设置\n"
   ]
  },
  {
   "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": [
    "## 任务分解\n",
    "\n",
    "任务分解是规划设计模式的核心。我们不是让单个代理端到端处理复杂请求，\n",
    "而是将问题拆解为更小的、定义明确的<strong>子任务</strong>。\n",
    "每个子任务被分配给一个专业代理（例如，航班、酒店、活动），并有清晰的\n",
    "优先级和依赖顺序。\n",
    "\n",
    "这种方法带来若干好处：\n",
    "- <strong>清晰性</strong>：每个子任务都有单一职责。\n",
    "- <strong>并行性</strong>：独立子任务可并发运行。\n",
    "- <strong>可靠性</strong>：失败被限制在单个子任务内。\n",
    "- <strong>预算追踪</strong>：成本按子任务估算并汇总。\n"
   ]
  },
  {
   "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": [
    "## 使用结构化输出创建规划代理\n",
    "\n",
    "规划代理充当前台协调员。根据高级别的旅行请求，它\n",
    "生成一个结构化的 `TravelPlan` —— 将请求分解为子任务，设定优先级，\n",
    "并识别依赖关系，以便礼宾或执行层能够完成工作。\n"
   ]
  },
  {
   "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": [
    "## 使用专业工具执行计划\n",
    "\n",
    "一旦前台代理生成了结构化计划，<strong>管家代理</strong>就会执行该计划。\n",
    "每个专业工具处理一类子任务（航班、酒店、活动）。管家\n",
    "按依赖顺序遍历计划的子任务，并将每个子任务分派给\n",
    "适当的工具。\n"
   ]
  },
  {
   "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.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n\n<!-- CO-OP TRANSLATOR DISCLAIMER START -->\n**免责声明**：\n本文件由 AI 翻译服务 [Co-op Translator](https://github.com/Azure/co-op-translator) 翻译完成。尽管我们力求准确，但请注意，自动翻译可能包含错误或不准确之处。原始语言版文件应视为权威来源。对于重要信息，建议使用专业人工翻译。我们对因使用本翻译而产生的任何误解或误释不承担责任。\n<!-- CO-OP TRANSLATOR DISCLAIMER END -->\n"
   ]
  }
 ],
 "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
}