{"id":1515,"date":"2026-03-29T07:30:42","date_gmt":"2026-03-29T07:30:42","guid":{"rendered":"https:\/\/bogdanburuiana.com\/?p=1515"},"modified":"2026-04-30T07:50:05","modified_gmt":"2026-04-30T07:50:05","slug":"the-microsoft-agent-framework-when-you-need-code-level-control","status":"publish","type":"post","link":"https:\/\/bogdanburuiana.com\/index.php\/2026\/03\/29\/the-microsoft-agent-framework-when-you-need-code-level-control\/","title":{"rendered":"The Microsoft Agent Framework: When You Need Code-Level Control"},"content":{"rendered":"\n<p><\/p>\n\n\n\n<p>The Foundry portal and its visual workflow builder are excellent tools. But there comes a point in every serious AI project where you need to step beyond the visual layer and write code. The Microsoft Agent Framework is where you go when that moment arrives.<br>It&#8217;s a Python SDK that gives you programmatic control over everything &#8211; agent creation, conversation management, tool integration, and multi-agent orchestration. If the Foundry portal is the configuration layer, the Agent Framework is the engineering layer.<br>Let me walk you through the core components and how they fit together.<\/p>\n\n\n\n<p class=\"has-medium-font-size\"><strong>The Five Core Components<\/strong><\/p>\n\n\n\n<p>The framework is built around five well-defined abstractions. Understand these and the rest follows naturally.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"684\" height=\"1024\" src=\"\/wp-content\/uploads\/2026\/04\/image-27-684x1024.png\" alt=\"\" class=\"wp-image-1516\" srcset=\"\/wp-content\/uploads\/2026\/04\/image-27-684x1024.png 684w, \/wp-content\/uploads\/2026\/04\/image-27-200x300.png 200w, \/wp-content\/uploads\/2026\/04\/image-27-768x1150.png 768w, \/wp-content\/uploads\/2026\/04\/image-27.png 1025w\" sizes=\"(max-width: 684px) 100vw, 684px\" \/><\/figure>\n\n\n\n<p class=\"has-medium-font-size\"><strong>Setting Up Your First Agent (Code Walkthrough)<\/strong><\/p>\n\n\n\n<p>Here&#8217;s the minimal setup to create a working agent with the framework:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from azure.ai.agents import AzureAIAgentClient, ChatAgent, AgentThread\nfrom azure.identity import AzureCliCredential\n\n# Step 1: Authenticate with Azure\ncredential = AzureCliCredential()\n\n# Step 2: Connect to your Foundry project\nclient = AzureAIAgentClient(\n    endpoint=\"https:\/\/your-project.openai.azure.com\/\",\n    credential=credential\n)\n\n# Step 3: Define your agent\nagent = ChatAgent(\n    client=client,\n    model=\"gpt-4.1\",\n    instructions=\"\"\"You are a cloud cost optimisation advisor \n    for enterprise Azure environments. Analyse resource usage \n    patterns and recommend specific cost-saving actions with \n    estimated monthly savings. Always quantify recommendations \n    when possible.\"\"\",\n    tools=&#91;get_resource_costs, get_usage_metrics]  # custom tools\n)\n\n# Step 4: Create a conversation thread\nthread = AgentThread()\n\n# Step 5: Send a message and get a response\nresponse = await agent.send_message(\n    thread=thread,\n    message=\"Analyse our storage account usage for the last 30 days\"\n)\n\nprint(response.content)<\/code><\/pre>\n\n\n\n<p>Five steps. That&#8217;s the full agent lifecycle &#8211; authentication, connection, configuration, thread creation, and conversation.<br>The <strong>AgentThread <\/strong>is what manages conversation state. Messages accumulate in the thread across turns, so the agent remembers the full context of the conversation. You create one thread per user session and pass it into every subsequent message.<\/p>\n\n\n\n<p class=\"has-medium-font-size\"><strong>Adding Custom Tools: The @tool Decorator Pattern<\/strong><\/p>\n\n\n\n<p>I covered the <strong>@tool <\/strong>decorator in the tools article, but let me show the complete pattern here in the context of the framework:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from azure.ai.agents import tool\nimport json\n\n@tool\ndef get_azure_resource_costs(\n    resource_group: str,\n    time_period_days: int = 30\n) -> dict:\n    \"\"\"\n    Retrieve cost breakdown for an Azure resource group.\n    \n    Use this when the user asks about costs, spend, or billing\n    for a specific resource group or environment.\n    \n    Args:\n        resource_group: The Azure resource group name\n        time_period_days: Number of days to analyse (default 30)\n        \n    Returns:\n        Dictionary with cost by resource type and total\n    \"\"\"\n    # Your Azure Cost Management API call\n    costs = cost_management_client.query_costs(\n        resource_group=resource_group,\n        days=time_period_days\n    )\n    return costs.to_dict()\n\n# Register with the agent\nagent = ChatAgent(\n    client=client,\n    model=\"gpt-4.1\",\n    instructions=\"...\",\n    tools=&#91;get_azure_resource_costs]  # Pass function reference\n)<\/code><\/pre>\n\n\n\n<p>Critical things to get right:<\/p>\n\n\n\n<ul>\n<li><strong>The docstring<\/strong> is the tool&#8217;s self-description. The LLM reads it. Write it clearly. <\/li>\n\n\n\n<li><strong>Type annotations<\/strong> tell the framework what parameters to expect and validate <\/li>\n\n\n\n<li><strong>Return type<\/strong> &#8211; return structured data (dicts, lists) not raw strings <\/li>\n\n\n\n<li><strong>Error handling inside the function<\/strong> &#8211; the agent receives whatever you return, including errors<\/li>\n<\/ul>\n\n\n\n<p class=\"has-medium-font-size\"><strong>AgentThread: Understanding Conversation State<\/strong><\/p>\n\n\n\n<p>This is a concept worth spending time on because it&#8217;s different from how most developers think about API calls.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>STATELESS API CALL (traditional):\nRequest 1: \"What is X?\" \u2192 Response 1\nRequest 2: \"Tell me more\" \u2192 ??? (no memory of Request 1)\n\nAGENT THREAD (stateful):\nThread created\nMessage 1: \"What is X?\" \u2192 Response 1\nMessage 2: \"Tell me more\" \u2192 Response 2 (knows context from M1)\nMessage 3: \"And in comparison to Y?\" \u2192 Response 3 (knows M1 + M2)<\/code><\/pre>\n\n\n\n<p>The thread stores the full message history. Every subsequent message to the same thread includes all previous context. This is how multi-turn conversations work without you manually managing history.<br><\/p>\n\n\n\n<p>For enterprise applications, the implications are:<\/p>\n\n\n\n<ul>\n<li>Each user session should have its own thread<\/li>\n\n\n\n<li>Threads have a token limit &#8211; very long conversations will eventually hit it<\/li>\n\n\n\n<li>Thread persistence (saving thread ID to database, resuming later) lets users continue conversations across sessions<\/li>\n\n\n\n<li>Thread data may contain sensitive information &#8211; apply appropriate access controls<\/li>\n<\/ul>\n\n\n\n<p class=\"has-medium-font-size\"><strong>Chat Providers: Swapping the Underlying Model<\/strong><\/p>\n\n\n\n<p>One of the framework&#8217;s design strengths is the BaseAgent abstraction. All chat providers implement the same interface, which means you can swap the underlying model without changing your agent logic.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Using Azure OpenAI\nfrom azure.ai.agents.providers import AzureOpenAIProvider\n\n# Using another provider\nfrom azure.ai.agents.providers import AzureAIInferenceProvider\n\n# The agent interface is identical regardless\nagent = ChatAgent(\n    client=client,  # Different client, same ChatAgent\n    model=\"claude-sonnet-4-6\",  # Or any supported model\n    instructions=\"...\",\n    tools=&#91;...]\n)<\/code><\/pre>\n\n\n\n<p>In practice, this means you can:<\/p>\n\n\n\n<ul>\n<li>Start development with one model and switch for cost\/performance reasons<\/li>\n\n\n\n<li>A\/B test different models against the same agent configuration<\/li>\n\n\n\n<li>Use different models for different agents in a multi-agent system (a cheaper model for simple routing, a more capable model for complex reasoning)<\/li>\n<\/ul>\n\n\n\n<p class=\"has-medium-font-size\"><strong>Built-In Tools in the Framework<\/strong><\/p>\n\n\n\n<p>Beyond custom tools, the framework exposes the same built-in capabilities as the Foundry portal:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from azure.ai.agents.tools import CodeInterpreterTool, FileSearchTool\n\nagent = ChatAgent(\n    client=client,\n    model=\"gpt-4.1\",\n    instructions=\"...\",\n    tools=&#91;\n        CodeInterpreterTool(),      # Execute Python for analysis\n        FileSearchTool(             # Search your document index\n            vector_store_ids=&#91;\"vs_abc123\"]\n        ),\n        get_crm_data,               # Custom function tool\n        send_slack_notification     # Another custom tool\n    ]\n)<\/code><\/pre>\n\n\n\n<p>Mix and match built-in and custom tools in the same agent. The framework handles the routing.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"579\" src=\"\/wp-content\/uploads\/2026\/04\/image-28-1024x579.png\" alt=\"\" class=\"wp-image-1517\" srcset=\"\/wp-content\/uploads\/2026\/04\/image-28-1024x579.png 1024w, \/wp-content\/uploads\/2026\/04\/image-28-300x170.png 300w, \/wp-content\/uploads\/2026\/04\/image-28-768x434.png 768w, \/wp-content\/uploads\/2026\/04\/image-28-1536x868.png 1536w, \/wp-content\/uploads\/2026\/04\/image-28.png 1668w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>My recommendation: Use the portal for initial agent configuration and testing. Migrate to the framework when you need complex tool logic, multi-agent orchestration, or proper production deployment with version control.<\/p>\n\n\n\n<p class=\"has-medium-font-size\"><strong>What Makes the Framework Production-Ready<\/strong><\/p>\n\n\n\n<p>Beyond the core features, the framework includes:<\/p>\n\n\n\n<ul>\n<li>Streaming responses &#8211; stream tokens to the user as they&#8217;re generated rather than waiting for the full response<\/li>\n\n\n\n<li>Structured outputs &#8211; force the agent to return JSON matching a defined schema (essential for downstream processing)<\/li>\n\n\n\n<li>Retry logic &#8211; built-in exponential backoff for transient failures<\/li>\n\n\n\n<li>Telemetry hooks &#8211; emit custom metrics to your observability platform<\/li>\n\n\n\n<li>Async support &#8211; full async\/await support for high-concurrency applications<\/li>\n<\/ul>\n\n\n\n<p>These aren&#8217;t features you&#8217;ll need for a proof-of-concept. They&#8217;re features you&#8217;ll need before going to production.<\/p>\n\n\n\n<p><em>Next: I&#8217;ll cover the five multi-agent orchestration patterns &#8211; concurrent, sequential, group chat, handoff, and Magentic &#8211; with real use cases for each.<\/em><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The Foundry portal and its visual workflow builder are excellent tools. But there comes a point in every serious AI project where you need to step beyond the visual layer and write code. The Microsoft Agent Framework is where you go when that moment arrives.It&#8217;s a Python SDK that gives you programmatic control over everything [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/bogdanburuiana.com\/index.php\/wp-json\/wp\/v2\/posts\/1515"}],"collection":[{"href":"https:\/\/bogdanburuiana.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bogdanburuiana.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bogdanburuiana.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bogdanburuiana.com\/index.php\/wp-json\/wp\/v2\/comments?post=1515"}],"version-history":[{"count":1,"href":"https:\/\/bogdanburuiana.com\/index.php\/wp-json\/wp\/v2\/posts\/1515\/revisions"}],"predecessor-version":[{"id":1518,"href":"https:\/\/bogdanburuiana.com\/index.php\/wp-json\/wp\/v2\/posts\/1515\/revisions\/1518"}],"wp:attachment":[{"href":"https:\/\/bogdanburuiana.com\/index.php\/wp-json\/wp\/v2\/media?parent=1515"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bogdanburuiana.com\/index.php\/wp-json\/wp\/v2\/categories?post=1515"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bogdanburuiana.com\/index.php\/wp-json\/wp\/v2\/tags?post=1515"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}