项目文件夹

文件
2026-07-13 12:59:43 +08:00

474 行
21 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "2b91961c",
"metadata": {},
"source": [
"## Két egymást követő ügynök:\n",
"\n",
"1. **Recepciós ügynök**: Kezdeti városi látnivaló ajánlásokat tesz\n",
"2. **Concierge ügynök**: Áttekinti és értékeli a recepciós ajánlását a népszerűség alapján\n",
"\n",
"## Az egymás utáni összehangolás fő előnyei:\n",
"\n",
"- **Ismétlődő finomítás**: A második ügynök javítja az első ügynök munkáját\n",
"- **Specializáció**: Minden ügynöknek speciális szerepe van a folyamatban\n",
"- **Minőségellenőrzés**: Beépített átnézési és érvényesítési lépés\n",
"- **Világos információáramlás**: Strukturált átadás az ügynökök között\n",
"\n",
"## Előfeltételek:\n",
"- Telepítve a Microsoft Agent Framework\n",
"- Azure AI Foundry projekt végpont és modell telepítés konfigurálva (`AZURE_AI_PROJECT_ENDPOINT`, `AZURE_AI_MODEL_DEPLOYMENT_NAME`)\n",
"- Azure CLI-val hitelesítve (`az login`)\n",
"- Alapvető ügynökfogalmak megértése\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0981c0bb",
"metadata": {},
"outputs": [],
"source": [
"import asyncio\n",
"import json\n",
"import os\n",
"from typing import Any, cast\n",
"\n",
"from agent_framework import Message, WorkflowBuilder\n",
"from agent_framework.azure import AzureAIProjectAgentProvider\n",
"from azure.identity import AzureCliCredential\n",
"from dotenv import load_dotenv\n",
"from IPython.display import HTML, display\n",
"from pydantic import BaseModel\n",
"\n",
"print(\"All imports successful!\")"
]
},
{
"cell_type": "markdown",
"id": "790b74bd",
"metadata": {},
"source": [
"## 1. lépés: Pydantic modellek definiálása strukturált kimenetekhez\n",
"\n",
"Ezek a modellek határozzák meg a sémát, amelyet minden ügynök vissza fog adni. A recepciós ügynök egy ajánlást nyújt, a concierge ügynök pedig egy értékelést és minősítést ad.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3436dc8d",
"metadata": {},
"outputs": [],
"source": [
"class AttractionRecommendation(BaseModel):\n",
" \"\"\"Attraction recommendation from the front desk agent.\"\"\"\n",
"\n",
" city: str\n",
" attraction_name: str\n",
" description: str\n",
" category: str # e.g., \"museum\", \"landmark\", \"park\", \"entertainment\"\n",
" recommended_duration: str # e.g., \"2-3 hours\", \"half day\"\n",
" why_recommended: str\n",
" best_time_to_visit: str\n",
"\n",
"\n",
"class AttractionReview(BaseModel):\n",
" \"\"\"Expert review and rating from the concierge agent.\"\"\"\n",
"\n",
" attraction_name: str\n",
" city: str\n",
" popularity_score: int # 1-10 scale\n",
" popularity_reasoning: str\n",
" visitor_rating: float # 1.0-5.0 scale\n",
" pros: list[str]\n",
" cons: list[str]\n",
" concierge_recommendation: str\n",
" alternative_suggestions: list[str]"
]
},
{
"cell_type": "markdown",
"id": "f4269b29",
"metadata": {},
"source": [
"## 2. lépés: Környezeti változók betöltése és a Foundry szolgáltató konfigurálása\n",
"\n",
"Használja az `AzureAIProjectAgentProvider`-t kulcs nélküli `AzureCliCredential` hitelesítéssel, az 01–13. leckékben alkalmazott mintának megfelelően.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2152c7d3",
"metadata": {},
"outputs": [],
"source": [
"# Load environment variables\n",
"load_dotenv()\n",
"\n",
"# Configure the Azure AI Foundry provider with keyless authentication\n",
"provider = AzureAIProjectAgentProvider(credential=AzureCliCredential())\n",
"\n",
"print(\"Azure AI Foundry provider configured successfully!\")"
]
},
{
"cell_type": "markdown",
"id": "e63c63dd",
"metadata": {},
"source": [
"## 3. lépés: Két szekvenciális ügynök létrehozása\n",
"\n",
"Minden ügynöknek meghatározott szerepe van a szekvenciális munkafolyamatban. A recepciós ügynök javaslatokat tesz, a concierge ügynök pedig felülvizsgálja és értékeli azokat.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3f862eee",
"metadata": {},
"outputs": [],
"source": [
"# Agent 1: Front Desk Agent (Makes initial recommendations)\n",
"front_desk_agent = await provider.create_agent(\n",
" name=\"front-desk-agent\",\n",
" instructions=(\n",
" \"You are a knowledgeable hotel front desk agent who specializes in local attractions. \"\n",
" \"When a guest asks about attractions in a city, provide a single, well-researched recommendation \"\n",
" \"for a popular tourist attraction. Focus on giving practical information including what makes \"\n",
" \"this attraction special, how long to spend there, and the best time to visit. \"\n",
" \"Be helpful and enthusiastic about your recommendation. \"\n",
" \"Return structured JSON matching the AttractionRecommendation schema.\"\n",
" ),\n",
")\n",
"\n",
"# Agent 2: Concierge Agent (Reviews and rates recommendations)\n",
"concierge_agent = await provider.create_agent(\n",
" name=\"concierge-agent\",\n",
" instructions=(\n",
" \"You are an expert concierge with extensive knowledge of tourist attractions worldwide. \"\n",
" \"You will receive an attraction recommendation and must provide an expert review and rating. \"\n",
" \"Evaluate the recommendation based on the attraction's popularity, visitor satisfaction, \"\n",
" \"and overall quality. Provide a popularity score (1-10), visitor rating (1.0-5.0), \"\n",
" \"list pros and cons, and give your professional assessment. \"\n",
" \"Also suggest alternative attractions if appropriate. \"\n",
" \"Return structured JSON matching the AttractionReview schema.\"\n",
" ),\n",
")\n"
]
},
{
"cell_type": "markdown",
"id": "0afa99f5",
"metadata": {},
"source": [
"## 4. lépés: Az egymás utáni munkafolyamat felépítése\n",
"\n",
"A `WorkflowBuilder` létrehoz egy munkafolyamatot, ahol:\n",
"1. A **Recepciós ügynök** fogadja a felhasználói bemenetet és ajánlást tesz\n",
"2. A **Konzierzs ügynök** megkapja a recepciós ajánlását, és szakértői véleményt ad\n",
"3. A **Kimenet** tartalmazza mind az eredeti ajánlást, mind a szakértői véleményt\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d76c5b11",
"metadata": {},
"outputs": [],
"source": [
"# Build the sequential workflow with WorkflowBuilder\n",
"workflow = (\n",
" WorkflowBuilder(\n",
" start_executor=front_desk_agent,\n",
" output_executors=[front_desk_agent, concierge_agent],\n",
" )\n",
" .add_edge(front_desk_agent, concierge_agent)\n",
" .build()\n",
")\n",
"\n",
"display(HTML(\"\"\"\n",
"<div style='padding: 20px; background: linear-gradient(135deg, #ff7043 0%, #ff5722 100%); color: white; border-radius: 8px; margin: 10px 0;'>\n",
" <h3 style='margin: 0 0 15px 0;'>Sequential Workflow Built Successfully!</h3>\n",
" <p style='margin: 0; line-height: 1.6;'>\n",
" <strong>Flow:</strong><br>\n",
" • User Input → <strong>Front Desk Agent</strong> (recommendation)<br>\n",
" • Front Desk Output → <strong>Concierge Agent</strong> (review & rating)<br>\n",
" • Final Output → Combined recommendation + expert review\n",
" </p>\n",
"</div>\n",
"\"\"\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "27a52679",
"metadata": {},
"outputs": [],
"source": [
"async def display_attraction_recommendation(city: str):\n",
" \"\"\"Run the sequential workflow and display formatted results.\"\"\"\n",
"\n",
" display(HTML(f\"\"\"\n",
" <div style='padding: 20px; background: #fff3e0; border-left: 4px solid #ff9800; border-radius: 8px; margin: 20px 0;'>\n",
" <h3 style='margin: 0 0 10px 0; color: #e65100;'>Processing Attraction Recommendation for {city}</h3>\n",
" <p style='margin: 0;'><strong>Status:</strong> Running sequential workflow...</p>\n",
" </div>\n",
" \"\"\"))\n",
"\n",
" # Run the workflow. With WorkflowBuilder(output_executors=[a1, a2]),\n",
" # outputs is a list of AgentResponse objects, one per output executor.\n",
" events = await workflow.run(f\"I want to visit an attraction in {city}\")\n",
" outputs = events.get_outputs()\n",
"\n",
" front_desk_response = outputs[0].text if len(outputs) > 0 else None\n",
" concierge_response = outputs[1].text if len(outputs) > 1 else None\n",
"\n",
" # Display results\n",
" display(HTML(f\"\"\"\n",
" <div style='padding: 25px; background: linear-gradient(135deg, #4caf50 0%, #8bc34a 100%); color: white; border-radius: 12px;\n",
" box-shadow: 0 4px 12px rgba(76,175,80,0.3); margin: 20px 0;'>\n",
" <h2 style='margin: 0 0 20px 0;'>Attraction Recommendation for {city}</h2>\n",
" <p style='margin: 0; font-size: 14px; opacity: 0.9;'>Generated by sequential agent workflow</p>\n",
" </div>\n",
" \"\"\"))\n",
"\n",
" # Process and display responses\n",
" if front_desk_response:\n",
" try:\n",
" recommendation_data = AttractionRecommendation.model_validate_json(front_desk_response)\n",
" display_front_desk_section(recommendation_data)\n",
" except Exception as e:\n",
" display(HTML(f\"\"\"\n",
" <div style='padding: 15px; background: #ffcdd2; border-left: 4px solid #f44336; border-radius: 4px; margin: 10px 0;'>\n",
" <strong>Error parsing front desk response:</strong> {str(e)}\n",
" <details><summary>Raw response</summary>{front_desk_response}</details>\n",
" </div>\n",
" \"\"\"))\n",
"\n",
" if concierge_response:\n",
" try:\n",
" review_data = AttractionReview.model_validate_json(concierge_response)\n",
" display_concierge_section(review_data)\n",
" except Exception as e:\n",
" display(HTML(f\"\"\"\n",
" <div style='padding: 15px; background: #ffcdd2; border-left: 4px solid #f44336; border-radius: 4px; margin: 10px 0;'>\n",
" <strong>Error parsing concierge response:</strong> {str(e)}\n",
" <details><summary>Raw response</summary>{concierge_response}</details>\n",
" </div>\n",
" \"\"\"))\n",
"\n",
"\n",
"def display_front_desk_section(data: AttractionRecommendation):\n",
" \"\"\"Display front desk recommendation in a formatted section.\"\"\"\n",
"\n",
" display(HTML(f\"\"\"\n",
" <div style='padding: 20px; background: #e3f2fd; border-radius: 8px; margin: 15px 0; border-left: 4px solid #2196f3;'>\n",
" <h3 style='margin: 0 0 15px 0; color: #1976d2;'>🏨 Front Desk Recommendation</h3>\n",
" <div style='margin-bottom: 15px;'>\n",
" <h4 style='margin: 0 0 8px 0; color: #333;'>{data.attraction_name}</h4>\n",
" <span style='background: #2196f3; color: white; padding: 4px 8px; border-radius: 12px; font-size: 12px;'>{data.category}</span>\n",
" </div>\n",
" <div style='margin-bottom: 15px;'>\n",
" <strong style='color: #333;'>Description:</strong> {data.description}\n",
" </div>\n",
" <div style='margin-bottom: 10px;'>\n",
" <strong style='color: #333;'>Why Recommended:</strong> {data.why_recommended}\n",
" </div>\n",
" <div style='margin-bottom: 10px;'>\n",
" <strong style='color: #333;'>Recommended Duration:</strong> {data.recommended_duration}\n",
" </div>\n",
" <div>\n",
" <strong style='color: #333;'>Best Time to Visit:</strong> {data.best_time_to_visit}\n",
" </div>\n",
" </div>\n",
" \"\"\"))\n",
"\n",
"\n",
"def display_concierge_section(data: AttractionReview):\n",
" \"\"\"Display concierge review in a formatted section.\"\"\"\n",
"\n",
" # Create star rating display\n",
" star_rating = \"⭐\" * int(data.visitor_rating) + \"☆\" * (5 - int(data.visitor_rating))\n",
"\n",
" # Create popularity bar\n",
" popularity_bar = \"🟩\" * data.popularity_score + \"⬜\" * (10 - data.popularity_score)\n",
"\n",
" pros_list = \"\".join([f\"<li style='color: #4caf50;'>✓ {pro}</li>\" for pro in data.pros])\n",
" cons_list = \"\".join([f\"<li style='color: #f44336;'>✗ {con}</li>\" for con in data.cons])\n",
" alternatives_list = \"\".join([f\"<li>{alt}</li>\" for alt in data.alternative_suggestions])\n",
"\n",
" display(HTML(f\"\"\"\n",
" <div style='padding: 20px; background: #fff3e0; border-radius: 8px; margin: 15px 0; border-left: 4px solid #ff9800;'>\n",
" <h3 style='margin: 0 0 15px 0; color: #f57c00;'>🎩 Concierge Expert Review</h3>\n",
"\n",
" <div style='display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px;'>\n",
" <div style='background: rgba(255,152,0,0.1); padding: 15px; border-radius: 8px;'>\n",
" <h4 style='margin: 0 0 8px 0; color: #333;'>Popularity Score</h4>\n",
" <div style='font-size: 24px; font-weight: bold; color: #f57c00;'>{data.popularity_score}/10</div>\n",
" <div style='font-size: 12px; margin-top: 5px;'>{popularity_bar}</div>\n",
" </div>\n",
" <div style='background: rgba(255,152,0,0.1); padding: 15px; border-radius: 8px;'>\n",
" <h4 style='margin: 0 0 8px 0; color: #333;'>Visitor Rating</h4>\n",
" <div style='font-size: 20px; font-weight: bold; color: #f57c00;'>{data.visitor_rating}/5.0</div>\n",
" <div style='font-size: 16px; margin-top: 5px;'>{star_rating}</div>\n",
" </div>\n",
" </div>\n",
"\n",
" <div style='margin-bottom: 15px;'>\n",
" <strong style='color: #333;'>Popularity Reasoning:</strong> {data.popularity_reasoning}\n",
" </div>\n",
"\n",
" <div style='display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 15px;'>\n",
" <div>\n",
" <h4 style='margin: 0 0 8px 0; color: #333;'>Pros:</h4>\n",
" <ul style='margin: 0; padding-left: 20px;'>{pros_list}</ul>\n",
" </div>\n",
" <div>\n",
" <h4 style='margin: 0 0 8px 0; color: #333;'>Cons:</h4>\n",
" <ul style='margin: 0; padding-left: 20px;'>{cons_list}</ul>\n",
" </div>\n",
" </div>\n",
" <div style='margin-bottom: 15px;'>\n",
" <strong style='color: #333;'>Concierge Recommendation:</strong> {data.concierge_recommendation}\n",
" </div>\n",
"\n",
" <div>\n",
" <h4 style='margin: 0 0 8px 0; color: #333;'>Alternative Suggestions:</h4>\n",
" <ul style='margin: 0; padding-left: 20px; color: #555;'>{alternatives_list}</ul>\n",
" </div>\n",
" </div>\n",
" \"\"\"))\n",
"\n",
"\n",
"# Test with Stockholm\n",
"await display_attraction_recommendation(\"Stockholm\")\n"
]
},
{
"cell_type": "markdown",
"id": "1840d000",
"metadata": {},
"source": [
"## 8. lépés: Munkafolyamat elemzése - A szekvenciális folyamat megértése\n",
"\n",
"Vizsgáljuk meg, hogyan áramlik az információ az ügynökök között, és elemezzük a beszélgetés előzményeit.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "86e1bcc3",
"metadata": {},
"outputs": [],
"source": [
"async def analyze_sequential_flow(city: str):\n",
" \"\"\"Analyze the sequential flow between agents.\"\"\"\n",
"\n",
" display(HTML(f\"\"\"\n",
" <div style='padding: 20px; background: #f3e5f5; border-left: 4px solid #9c27b0; border-radius: 8px; margin: 20px 0;'>\n",
" <h3 style='margin: 0 0 10px 0; color: #7b1fa2;'>Sequential Flow Analysis for {city}</h3>\n",
" <p style='margin: 0;'>Examining agent interactions and information handoff...</p>\n",
" </div>\n",
" \"\"\"))\n",
"\n",
" # Run the workflow\n",
" user_input = f\"I want to visit an attraction in {city}\"\n",
" events = await workflow.run(user_input)\n",
" outputs = events.get_outputs()\n",
"\n",
" # Reconstruct the conversation flow as a list of (author, text) steps.\n",
" # outputs is a list of AgentResponse objects, ordered to match output_executors.\n",
" steps = [(\"user\", user_input)]\n",
" if len(outputs) > 0:\n",
" steps.append((\"front-desk-agent\", outputs[0].text))\n",
" if len(outputs) > 1:\n",
" steps.append((\"concierge-agent\", outputs[1].text))\n",
"\n",
" display(HTML(f\"\"\"\n",
" <div style='padding: 25px; background: #f3e5f5; border-radius: 12px; margin: 20px 0;'>\n",
" <h2 style='margin: 0 0 20px 0; color: #7b1fa2;'>Conversation Flow Analysis</h2>\n",
" </div>\n",
" \"\"\"))\n",
"\n",
" # Display each step in the sequence\n",
" for i, (author, text) in enumerate(steps, 1):\n",
" role_color = {\n",
" \"user\": \"#2196f3\",\n",
" \"front-desk-agent\": \"#4caf50\",\n",
" \"concierge-agent\": \"#ff9800\"\n",
" }.get(author, \"#666666\")\n",
"\n",
" role_name = {\n",
" \"user\": \"👤 User\",\n",
" \"front-desk-agent\": \"🏨 Front Desk Agent\",\n",
" \"concierge-agent\": \"🎩 Concierge Agent\"\n",
" }.get(author, \"Unknown\")\n",
"\n",
" # Truncate long messages for flow analysis\n",
" content_preview = text[:200] + \"...\" if len(text) > 200 else text\n",
" display(HTML(f\"\"\"\n",
" <div style='padding: 15px; background: white; border-left: 4px solid {role_color}; border-radius: 4px; margin: 10px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1);'>\n",
" <div style='display: flex; align-items: center; margin-bottom: 10px;'>\n",
" <span style='font-weight: bold; color: {role_color}; margin-right: 10px;'>Step {i}:</span>\n",
" <span style='font-weight: bold; color: {role_color};'>{role_name}</span>\n",
" </div>\n",
" <div style='color: #555; font-size: 14px; line-height: 1.4;'>\n",
" {content_preview}\n",
" </div>\n",
" </div>\n",
" \"\"\"))\n",
"\n",
" # Analyze the flow\n",
" display(HTML(f\"\"\"\n",
" <div style='padding: 20px; background: linear-gradient(135deg, #9c27b0 0%, #673ab7 100%); color: white; border-radius: 8px; margin: 20px 0;'>\n",
" <h3 style='margin: 0 0 15px 0;'>Flow Analysis Summary</h3>\n",
" <ul style='margin: 0; padding-left: 20px; line-height: 1.6;'>\n",
" <li><strong>Total Steps:</strong> {len(steps)}</li>\n",
" <li><strong>Agents Involved:</strong> 2 (Front Desk + Concierge)</li>\n",
" <li><strong>Flow Pattern:</strong> Linear sequential (User → Agent 1 → Agent 2)</li>\n",
" <li><strong>Information Handoff:</strong> Front desk recommendation becomes concierge input</li>\n",
" <li><strong>Output Quality:</strong> Enhanced through expert review and rating</li>\n",
" </ul>\n",
" </div>\n",
" \"\"\"))\n",
"\n",
"\n",
"# Analyze the flow for Barcelona\n",
"await analyze_sequential_flow(\"Barcelona\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n\n<!-- CO-OP TRANSLATOR DISCLAIMER START -->\n**Jogi nyilatkozat**:\nEz a dokumentum az AI fordítási szolgáltatás, a [Co-op Translator](https://github.com/Azure/co-op-translator) segítségével készült. Bár az pontosságra törekszünk, kérjük, vegye figyelembe, hogy az automatikus fordítások hibákat vagy pontatlanságokat tartalmazhatnak. Az eredeti dokumentum az anyanyelvén tekintendő hiteles forrásnak. Fontos információk esetén professzionális emberi fordítást javasolunk. Nem vállalunk felelősséget semmilyen félreértésért vagy téves értelmezésért, amely ebből a fordításból ered.\n<!-- CO-OP TRANSLATOR DISCLAIMER END -->\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv (3.12.12)",
"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.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}