# Advanced Patterns Reference Advanced patterns including structured outputs, OpenAPI tools, file handling, and more. ## Structured Outputs with Pydantic ### Basic Response Format ```python from pydantic import BaseModel, ConfigDict from agent_framework.azure import AzureAIAgentsProvider from azure.identity.aio import AzureCliCredential class MovieRecommendation(BaseModel): model_config = ConfigDict(extra="forbid") # Strict validation title: str year: int genre: str rating: float summary: str async with ( AzureCliCredential() as credential, AzureAIAgentsProvider(credential=credential) as provider, ): agent = await provider.create_agent( name="MovieAgent", instructions="Recommend movies based on user preferences.", response_format=MovieRecommendation, # Set at creation ) result = await agent.run("Recommend a sci-fi movie") movie = MovieRecommendation.model_validate_json(result.text) print(f"{movie.title} ({movie.year}) - {movie.rating}/10") ``` ### Complex Nested Structures ```python from pydantic import BaseModel, ConfigDict, Field from typing import Optional class Address(BaseModel): model_config = ConfigDict(extra="forbid") street: str city: str country: str postal_code: Optional[str] = None class Person(BaseModel): model_config = ConfigDict(extra="forbid") name: str age: int email: str address: Address hobbies: list[str] = Field(default_factory=list) class TeamResponse(BaseModel): model_config = ConfigDict(extra="forbid") team_name: str members: list[Person] total_members: int agent = await provider.create_agent( name="TeamGenerator", instructions="Generate fictional team data.", response_format=TeamResponse, ) result = await agent.run("Create a team of 3 software developers") team = TeamResponse.model_validate_json(result.text) for member in team.members: print(f"- {member.name}, {member.age}, {member.address.city}") ``` ### Runtime Response Format Override ```python class QuickAnswer(BaseModel): answer: str confidence: float class DetailedAnalysis(BaseModel): summary: str key_points: list[str] recommendations: list[str] sources: list[str] # Agent created without default response format agent = await provider.create_agent( name="FlexibleAgent", instructions="Provide information in the requested format.", ) # Quick answer format quick_result = await agent.run( "What is Python?", response_format=QuickAnswer, ) # Detailed analysis format (same agent) detailed_result = await agent.run( "Analyze the benefits of microservices architecture", response_format=DetailedAnalysis, ) ``` --- ## OpenAPI Tools Integrate external APIs using OpenAPI specifications. ### Basic OpenAPI Integration ```python from agent_framework import OpenAPITool from agent_framework.azure import AzureAIAgentsProvider from azure.identity.aio import AzureCliCredential # OpenAPI spec can be URL or inline dict openapi_spec = { "openapi": "3.0.0", "info": {"title": "Weather API", "version": "1.0.0"}, "paths": { "/weather/{city}": { "get": { "operationId": "getWeather", "summary": "Get weather for a city", "parameters": [ { "name": "city", "in": "path", "required": True, "schema": {"type": "string"} } ], "responses": { "200": { "description": "Weather data", "content": { "application/json": { "schema": { "type": "object", "properties": { "temperature": {"type": "number"}, "conditions": {"type": "string"} } } } } } } } } } } async with ( AzureCliCredential() as credential, AzureAIAgentsProvider(credential=credential) as provider, ): agent = await provider.create_agent( name="WeatherAPIAgent", instructions="Use the weather API to answer weather questions.", tools=OpenAPITool( name="WeatherAPI", spec=openapi_spec, base_url="https://api.weather.example.com", ), ) ``` ### OpenAPI with Authentication ```python from agent_framework import OpenAPITool openapi_tool = OpenAPITool( name="SecureAPI", spec="https://api.example.com/openapi.json", base_url="https://api.example.com", headers={ "Authorization": "Bearer your-api-key", "X-API-Version": "2024-01", }, ) ``` --- ## File Generation and Handling ### Code Interpreter File Output ```python from agent_framework import HostedCodeInterpreterTool from agent_framework.azure import AzureAIAgentsProvider from azure.identity.aio import AzureCliCredential async with ( AzureCliCredential() as credential, AzureAIAgentsProvider(credential=credential) as provider, ): agent = await provider.create_agent( name="DataAnalyst", instructions="Analyze data and create visualizations.", tools=HostedCodeInterpreterTool(), ) result = await agent.run( "Create a bar chart of sales data: Q1=100, Q2=150, Q3=120, Q4=200. Save as PNG." ) # Check for generated files in the response print(result.text) # Files generated by code interpreter are typically referenced in the response # and can be downloaded via the files API ``` ### Working with File IDs ```python from azure.ai.agents.aio import AgentsClient async with ( AzureCliCredential() as credential, AgentsClient(endpoint=endpoint, credential=credential) as agents_client, AzureAIAgentsProvider(agents_client=agents_client) as provider, ): # Upload a file from pathlib import Path file = await agents_client.files.upload( file_path=Path("data/sales.csv"), purpose="agents" ) print(f"Uploaded file ID: {file.id}") # Use file with code interpreter from agent_framework import HostedCodeInterpreterTool, HostedFileContent agent = await provider.create_agent( name="CSVAnalyst", instructions="Analyze the provided CSV file.", tools=HostedCodeInterpreterTool( inputs=[HostedFileContent(file_id=file.id)] ), ) result = await agent.run("Summarize the data in the uploaded file") ``` --- ## Citations and Source Attribution ### Enabling Citations ```python agent = await provider.create_agent( name="ResearchAgent", instructions="""Answer questions using the knowledge base. IMPORTANT: Always cite your sources using this format: 【message_idx:search_idx†source_name】 Example: "Azure Functions supports Python【1:0†azure-docs】" """, tools=[ HostedFileSearchTool(inputs=[...]), ], ) ``` ### Parsing Citations ```python import re def parse_citations(text: str) -> list[dict]: """Extract citations from agent response.""" pattern = r'【(\d+):(\d+)†([^】]+)】' citations = [] for match in re.finditer(pattern, text): citations.append({ "message_idx": int(match.group(1)), "search_idx": int(match.group(2)), "source": match.group(3), }) return citations result = await agent.run("What is Azure Functions?") citations = parse_citations(result.text) for cite in citations: print(f"Source: {cite['source']}") ``` --- ## Provider Configuration Options ### Custom Model and Endpoint ```python from agent_framework.azure import AzureAIAgentsProvider provider = AzureAIAgentsProvider( credential=credential, project_endpoint="https://my-project.services.ai.azure.com/api/projects/my-project-id", model_deployment_name="gpt-4o", # Override default model ) ``` ### Using Existing AgentsClient ```python from azure.ai.agents.aio import AgentsClient from agent_framework.azure import AzureAIAgentsProvider # Create and configure client separately agents_client = AgentsClient( endpoint=endpoint, credential=credential, ) # Pass to provider provider = AzureAIAgentsProvider(agents_client=agents_client) ``` --- ## Agent Lifecycle Management ### Retrieving Existing Agents ```python async with ( AzureCliCredential() as credential, AzureAIAgentsProvider(credential=credential) as provider, ): # Create agent and save ID agent = await provider.create_agent( name="PersistentAgent", instructions="You remember everything.", ) agent_id = agent.id # Save this # Later: retrieve the same agent same_agent = await provider.get_agent(agent_id=agent_id) ``` ### Wrapping SDK Agents ```python from azure.ai.agents.aio import AgentsClient async with ( AzureCliCredential() as credential, AgentsClient(endpoint=endpoint, credential=credential) as agents_client, AzureAIAgentsProvider(agents_client=agents_client) as provider, ): # Get agent via SDK sdk_agent = await agents_client.get_agent("agent-id") # Wrap as ChatAgent (no HTTP call) agent = provider.as_agent(sdk_agent) # Now use with agent framework result = await agent.run("Hello!") ``` --- ## Error Handling Patterns ### Graceful Degradation ```python from agent_framework import HostedWebSearchTool, HostedCodeInterpreterTool async def run_with_fallback(agent, query: str, thread=None): """Run query with fallback on tool failures.""" try: result = await agent.run(query, thread=thread) return result.text except Exception as e: # Log the error print(f"Tool execution error: {e}") # Create fallback agent without tools fallback_agent = await provider.create_agent( name="FallbackAgent", instructions="Answer based on your knowledge only.", ) result = await fallback_agent.run(query) return f"[Fallback response] {result.text}" ``` ### Retry Logic ```python import asyncio from typing import Optional async def run_with_retry( agent, query: str, thread=None, max_retries: int = 3, delay: float = 1.0, ) -> Optional[str]: """Run query with exponential backoff retry.""" for attempt in range(max_retries): try: result = await agent.run(query, thread=thread) return result.text except Exception as e: if attempt == max_retries - 1: raise wait_time = delay * (2 ** attempt) print(f"Attempt {attempt + 1} failed, retrying in {wait_time}s: {e}") await asyncio.sleep(wait_time) return None ``` --- ## Performance Optimization ### Connection Reuse ```python # ✅ Good: Reuse provider and client async with ( AzureCliCredential() as credential, AzureAIAgentsProvider(credential=credential) as provider, ): # Create multiple agents with same provider agent1 = await provider.create_agent(name="Agent1", instructions="...") agent2 = await provider.create_agent(name="Agent2", instructions="...") # Process multiple requests for query in queries: await agent1.run(query) # ❌ Bad: Creating new provider for each request for query in queries: async with AzureAIAgentsProvider(credential=credential) as provider: agent = await provider.create_agent(...) await agent.run(query) ``` ### Concurrent Requests ```python import asyncio async def process_queries(provider, queries: list[str]) -> list[str]: """Process multiple queries concurrently.""" agent = await provider.create_agent( name="BatchAgent", instructions="Answer questions concisely.", ) # Each query gets its own thread async def process_one(query: str) -> str: thread = agent.get_new_thread() result = await agent.run(query, thread=thread) return result.text results = await asyncio.gather(*[process_one(q) for q in queries]) return results ``` --- ## Debugging and Logging ### Enable Verbose Logging ```python import logging # Enable Azure SDK logging logging.basicConfig(level=logging.DEBUG) azure_logger = logging.getLogger("azure") azure_logger.setLevel(logging.DEBUG) # Enable agent framework logging af_logger = logging.getLogger("agent_framework") af_logger.setLevel(logging.DEBUG) ``` ### Inspecting Tool Calls in Streaming ```python from agent_framework import AgentResponseUpdate async for chunk in agent.run_stream("Calculate something"): if isinstance(chunk, AgentResponseUpdate): if chunk.tool_calls: for tool_call in chunk.tool_calls: print(f"[DEBUG] Tool: {tool_call.name}") print(f"[DEBUG] Args: {tool_call.arguments}") if chunk.text: print(chunk.text, end="", flush=True) ```