microsoft--ai-agents-for-beginners
314 行
10 KiB
Plaintext
314 行
10 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "8744544f",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Lesson 04 - 工具使用設計模式\n",
|
||
"\n",
|
||
"在本課程中,您將學習使用 Microsoft Agent Framework(Python)為 AI 代理設計的 <strong>工具使用</strong> 設計模式。我們涵蓋:\n",
|
||
"\n",
|
||
"- 使用 `@tool` 裝飾器和類型參數定義函式工具\n",
|
||
"- 提供工具結構,使模型了解每個工具的功能\n",
|
||
"- 使用 `approval_mode` 控制工具執行\n",
|
||
"- 透過 Pydantic 模型和 `response_format` 返回<strong>結構化輸出</strong>\n",
|
||
"\n",
|
||
"情境是一個 <strong>旅行預訂代理</strong>,可以查詢目的地、檢查可用性並檢索航班資訊。\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "b1a2c3d4",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 設定\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "59c0feeb",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"%pip install agent-framework azure-ai-projects azure-identity python-dotenv -U -q"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "c0df8a52",
|
||
"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 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(dotenv.find_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": "a6141584",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Create the Azure AI Foundry client\n",
|
||
"client = FoundryChatClient(\n",
|
||
" project_endpoint=endpoint,\n",
|
||
" model=deployment_name,\n",
|
||
" credential=DefaultAzureCredential()\n",
|
||
")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "d5e6f7a8",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 使用 @tool 裝飾器定義工具\n",
|
||
"\n",
|
||
"`@tool` 裝飾器將普通的 Python 函數轉換為代理可以調用的工具。 \n",
|
||
"重點如下:\n",
|
||
"\n",
|
||
"- <strong>文件字串</strong> 會成為模型看到的工具描述。 \n",
|
||
"- <strong>類型註解</strong>(包括帶描述的 `Annotated`)定義工具的結構。 \n",
|
||
"- `approval_mode` 控制是否須要使用者在每次呼叫前批准執行。\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "a6507f83",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"@tool(approval_mode=\"never_require\")\n",
|
||
"def get_destinations() -> list[str]:\n",
|
||
" \"\"\"Get available vacation destinations.\"\"\"\n",
|
||
" return [\"Barcelona\", \"Paris\", \"Berlin\", \"Tokyo\", \"Sydney\", \"New York City\"]\n",
|
||
"\n",
|
||
"\n",
|
||
"@tool(approval_mode=\"never_require\")\n",
|
||
"def check_availability(\n",
|
||
" destination: Annotated[str, \"The destination to check\"],\n",
|
||
") -> str:\n",
|
||
" \"\"\"Check booking availability for a destination.\"\"\"\n",
|
||
" availability = {\n",
|
||
" \"Barcelona\": \"Available - 3 spots left\",\n",
|
||
" \"Paris\": \"Available\",\n",
|
||
" \"Berlin\": \"Sold out\",\n",
|
||
" \"Tokyo\": \"Available - 1 spot left\",\n",
|
||
" \"Sydney\": \"Available\",\n",
|
||
" \"New York City\": \"Available\",\n",
|
||
" }\n",
|
||
" return availability.get(destination, \"Unknown destination\")\n",
|
||
"\n",
|
||
"\n",
|
||
"@tool(approval_mode=\"never_require\")\n",
|
||
"def get_flight_info(\n",
|
||
" origin: Annotated[str, \"Origin airport code\"],\n",
|
||
" destination: Annotated[str, \"Destination airport code\"],\n",
|
||
") -> str:\n",
|
||
" \"\"\"Get flight information between two cities.\"\"\"\n",
|
||
" flights = {\n",
|
||
" \"LHR-BCN\": \"BA 2042, Departs 08:30, Arrives 11:45, $350\",\n",
|
||
" \"LHR-CDG\": \"AF 1081, Departs 09:15, Arrives 11:30, $280\",\n",
|
||
" \"LHR-NRT\": \"JL 044, Departs 11:00, Arrives 07:00+1, $890\",\n",
|
||
" }\n",
|
||
" return flights.get(\n",
|
||
" f\"{origin}-{destination}\",\n",
|
||
" f\"No direct flights from {origin} to {destination}\",\n",
|
||
" )"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "e9f0a1b2",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 使用多個工具建立代理人\n",
|
||
"\n",
|
||
"將所有三個工具傳遞給用戶端,讓模型可以調用其所需的任何工具來回答使用者的問題。\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "be18ac4f",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"travel_tools = [get_destinations, check_availability, get_flight_info]\n",
|
||
"\n",
|
||
"agent = client.as_agent(\n",
|
||
" name=\"TravelToolAgent\",\n",
|
||
" instructions=\"You are a travel agent. Use the available tools to answer questions about destinations, availability, and flights.\",\n",
|
||
" tools=travel_tools,\n",
|
||
")\n",
|
||
"\n",
|
||
"response = await agent.run(\n",
|
||
" \"What destinations do you have? Which ones are still available?\"\n",
|
||
")\n",
|
||
"print(response)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "c3d4e5f6",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 使用工具的結構化輸出\n",
|
||
"\n",
|
||
"透過將 `response_format` 設定為 Pydantic 模型,代理程式將被強制回傳一個型別明確的 JSON 物件,而非自由格式的文字。當下游程式碼需要以程式方式消耗結果時,這非常有用。\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "772e9481",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class BookingRecommendation(BaseModel):\n",
|
||
" destination: str\n",
|
||
" available: bool\n",
|
||
" flight_details: str\n",
|
||
" estimated_cost: int\n",
|
||
"\n",
|
||
"\n",
|
||
"class TravelPlan(BaseModel):\n",
|
||
" recommendations: list[BookingRecommendation]\n",
|
||
"\n",
|
||
"\n",
|
||
"structured_agent = client.as_agent(\n",
|
||
" name=\"StructuredTravelAgent\",\n",
|
||
" instructions=(\n",
|
||
" \"You are a travel agent. Use the available tools to find destinations, \"\n",
|
||
" \"check availability, and get flight info. Return structured results.\"\n",
|
||
" ),\n",
|
||
" tools=[get_destinations, check_availability, get_flight_info],\n",
|
||
")\n",
|
||
"\n",
|
||
"response = await structured_agent.run(\n",
|
||
" \"I want to fly from London Heathrow to somewhere warm in Europe. \"\n",
|
||
" \"Check what's available.\"\n",
|
||
")\n",
|
||
"if response:\n",
|
||
" print(response)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "a7b8c9d0",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 工具批准模式\n",
|
||
"\n",
|
||
"`@tool` 上的 `approval_mode` 參數控制工具呼叫是否在執行前需要人工批准:\n",
|
||
"\n",
|
||
"| 模式 | 行為 |\n",
|
||
"|---|---|\n",
|
||
"| `\"never_require\"` | 工具自動執行 — 不需要用戶確認。 |\n",
|
||
"| `\"always_require\"` | 每次呼叫必須經用戶批准後才執行。 |\n",
|
||
"\n",
|
||
"對於具有副作用的工具(例如預訂航班、信用卡收費)請使用 `\"always_require\"`,以便保持有人介入。\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "a731b547",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"@tool(approval_mode=\"always_require\")\n",
|
||
"def book_flight(\n",
|
||
" origin: Annotated[str, \"Origin airport code\"],\n",
|
||
" destination: Annotated[str, \"Destination airport code\"],\n",
|
||
" passenger_name: Annotated[str, \"Full name of the passenger\"],\n",
|
||
") -> str:\n",
|
||
" \"\"\"Book a flight for a passenger. Requires approval before executing.\"\"\"\n",
|
||
" return (\n",
|
||
" f\"Flight booked from {origin} to {destination} \"\n",
|
||
" f\"for {passenger_name}. Confirmation #TRV-2024-{hash(passenger_name) % 10000:04d}\"\n",
|
||
" )\n",
|
||
"\n",
|
||
"\n",
|
||
"print(\"Tool name:\", book_flight.name)\n",
|
||
"print(\"Approval mode:\", book_flight.approval_mode)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "f1e2d3c4",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Summary\n",
|
||
"\n",
|
||
"在本課程中,您學到了如何:\n",
|
||
"\n",
|
||
"1. 使用帶有型別參數和作為工具結構的docstring的 `@tool` 裝飾器<strong>定義工具</strong>。\n",
|
||
"2. <strong>組合多個工具</strong>,讓代理能夠按順序調用它們來回答複雜的問題。\n",
|
||
"3. 通過傳遞 Pydantic 模型作為 `response_format`,<strong>回傳結構化輸出</strong>。\n",
|
||
"4. 使用 `approval_mode` <strong>控制工具批准</strong>,以保持在敏感操作中有人類參與。\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",
|
||
"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
|
||
} |