{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "a1b2c3d4",
   "metadata": {},
   "source": [
    "# 课程 02 - 探索 Microsoft Agent 框架\n",
    "\n",
    "**Microsoft Agent 框架（MAF）** 是一个用于构建 AI 代理的统一框架。它提供了一个简洁、可组合的架构，包含四个核心构建模块：\n",
    "\n",
    "- <strong>客户端</strong> – 连接到 AI 模型端点并处理通信\n",
    "- <strong>代理</strong> – 包装客户端，带有指令和工具定义\n",
    "- <strong>工具</strong> – 通过模型可调用的自定义函数扩展代理能力\n",
    "- <strong>会话</strong> – 维护多轮交互的对话历史\n",
    "\n",
    "在本课中，我们将构建一个使用这些概念来检查目的地可用性的<strong>旅行预订代理</strong>。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b2c3d4e5",
   "metadata": {},
   "source": [
    "## 设置\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c3d4e5f6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install the Microsoft Agent Framework package\n",
    "! pip install agent-framework azure-ai-projects -U -q\n",
    "! pip install python-dotenv -q"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d4e5f6a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "import logging\n",
    "logging.getLogger(\"agent_framework.foundry\").setLevel(logging.ERROR)\n",
    "\n",
    "import os\n",
    "import asyncio\n",
    "import dotenv\n",
    "from typing import Annotated\n",
    "\n",
    "from agent_framework import tool\n",
    "from agent_framework.foundry import FoundryChatClient\n",
    "from azure.identity import AzureCliCredential\n",
    "\n",
    "dotenv.load_dotenv(dotenv.find_dotenv())"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e5f6a7b8",
   "metadata": {},
   "source": [
    "## 理解 Agent 框架架构\n",
    "\n",
    "Microsoft Agent 框架遵循分层架构：\n",
    "\n",
    "```\n",
    "Client  →  Agent  →  Tools\n",
    "                  →  Session\n",
    "```\n",
    "\n",
    "1. <strong>客户端</strong> – `FoundryChatClient` 连接到 Azure OpenAI 部署，处理身份验证、请求格式化和响应解析。\n",
    "2. **Agent** – 通过 `provider.create_agent()` 从客户端创建，agent 结合了模型访问、指令（系统提示）和工具。\n",
    "3. <strong>工具</strong> – 用 `@tool` 装饰的 Python 函数，agent 可以调用它们执行操作或获取数据。\n",
    "4. <strong>会话</strong> – `AgentSession` 对象（通过 `agent.create_session()` 创建），存储对话历史，实现多轮对话，agent 记忆之前的上下文。\n",
    "\n",
    "让我们一步步构建每一层。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f6a7b8c9",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Create the client – this is the connection to the AI model\n",
    "endpoint = os.getenv(\"AZURE_AI_PROJECT_ENDPOINT\")\n",
    "model = os.getenv(\"AZURE_AI_MODEL_DEPLOYMENT_NAME\")\n",
    "\n",
    "if not endpoint or not model:\n",
    "    raise ValueError(\n",
    "        \"Missing required environment variables. \"\n",
    "        \"Please set AZURE_AI_PROJECT_ENDPOINT and AZURE_AI_MODEL_DEPLOYMENT_NAME as environment variables (e.g., in your .env file or shell environment).\"\n",
    "    )\n",
    "\n",
    "provider = FoundryChatClient(\n",
    "    project_endpoint=endpoint,\n",
    "    model=model,\n",
    "    credential=AzureCliCredential()\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7b8c9d0",
   "metadata": {},
   "source": [
    "## 使用 @tool 装饰器添加工具\n",
    "\n",
    "工具让代理可以执行生成文本以外的操作。`@tool` 装饰器将普通的 Python 函数转换为代理可以调用的功能。\n",
    "\n",
    "关键点：\n",
    "- 使用 `Annotated[type, \"description\"]`，让模型理解每个参数。\n",
    "- 文档字符串变成模型看到的工具描述。\n",
    "- `approval_mode=\"never_require\"` 表示工具自动运行，无需用户确认。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b8c9d0e1",
   "metadata": {},
   "outputs": [],
   "source": [
    "@tool(approval_mode=\"never_require\")\n",
    "def check_destination_availability(\n",
    "    destination: Annotated[str, \"The destination to check availability for\"]\n",
    ") -> str:\n",
    "    \"\"\"Check if a vacation destination is currently available for booking.\"\"\"\n",
    "    available = {\n",
    "        \"Barcelona\": True,\n",
    "        \"Tokyo\": True,\n",
    "        \"Cape Town\": False,\n",
    "        \"Vancouver\": True,\n",
    "        \"Dubai\": False,\n",
    "    }\n",
    "    is_available = available.get(destination, False)\n",
    "    return f\"{destination} is {'available' if is_available else 'not available'} for booking.\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c9d0e1f2",
   "metadata": {},
   "source": [
    "## 使用工具创建代理\n",
    "\n",
    "现在我们将客户端、指令和工具组合成一个代理。`instructions` 作为系统提示——它们定义了代理的角色和行为。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d0e1f2a3",
   "metadata": {},
   "outputs": [],
   "source": [
    "agent = provider.as_agent(\n",
    "    name=\"TravelAvailabilityAgent\",\n",
    "    instructions=(\n",
    "        \"You are a travel booking agent. Help users check destination availability \"\n",
    "        \"and make recommendations. Always check availability before recommending a destination.\"\n",
    "    ),\n",
    "    tools=[check_destination_availability],\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e1f2a3b4",
   "metadata": {},
   "source": [
    "## 多轮对话会话\n",
    "\n",
    "`AgentSession`（通过 `agent.create_session()` 创建）跟踪对话中的所有消息。通过在每次 `agent.run()` 调用中传递相同的会话，代理可以访问完整的对话历史并引用之前的消息。\n",
    "\n",
    "我们传入 `tools=[check_destination_availability]`，以便代理在每轮中都能调用我们的可用性检查器。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2a3b4c5",
   "metadata": {},
   "outputs": [],
   "source": [
    "session = agent.create_session()\n",
    "\n",
    "# Turn 1: Ask about available destinations\n",
    "response = await agent.run(\n",
    "    \"Which destinations do you have available?\",\n",
    "    session=session,\n",
    ")\n",
    "print(f\"Agent: {response}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a3b4c5d6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Turn 2: Follow-up question — the agent remembers the conversation\n",
    "response = await agent.run(\n",
    "    \"I'd like to go somewhere warm. What's available?\",\n",
    "    session=session,\n",
    ")\n",
    "print(f\"Agent: {response}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4c5d6e7",
   "metadata": {},
   "source": [
    "## 总结\n",
    "\n",
    "在本课中，您探索了微软代理框架的四大支柱：\n",
    "\n",
    "| 概念 | 你学到了什么 |\n",
    "|---------|------------------|\n",
    "| <strong>客户端</strong> | `FoundryChatClient` 使用基于凭证的认证连接到 Azure OpenAI |\n",
    "| <strong>代理</strong> | `provider.create_agent()` 将模型连接与指令和名称绑定在一起 |\n",
    "| <strong>工具</strong> | `@tool` 装饰器暴露 Python 函数供代理调用 |\n",
    "| <strong>会话</strong> | `agent.create_session()` 跨多轮保持对话历史 |\n",
    "\n",
    "这些构建模块组合在一起，创建能够进行自然对话、调用外部函数并保持上下文的代理 —— 这是后续课程中更高级代理模式的基础。\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 (ipykernel)",
   "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.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}