<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Dimitar on AI]]></title><description><![CDATA[Expert insights on Azure AI architecture and implementation. Real-world solutions for building intelligent enterprise systems.]]></description><link>https://dimitaronai.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1761386302158/20d0eccb-a1ae-44d0-a32c-98de9a20299c.jpeg</url><title>Dimitar on AI</title><link>https://dimitaronai.com</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 16 Apr 2026 20:50:07 GMT</lastBuildDate><atom:link href="https://dimitaronai.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Book Review: Design Multi-Agent AI Systems Using MCP and A2A]]></title><description><![CDATA[There's no shortage of AI content right now, but most of it sits at one of two extremes: breezy conceptual overviews that leave you with nothing to build, or narrow tutorials that solve one specific p]]></description><link>https://dimitaronai.com/book-review-design-multi-agent-ai-systems-using-mcp-and-a2a</link><guid isPermaLink="true">https://dimitaronai.com/book-review-design-multi-agent-ai-systems-using-mcp-and-a2a</guid><category><![CDATA[agentic AI]]></category><category><![CDATA[General Programming]]></category><category><![CDATA[generative ai]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Fri, 27 Mar 2026 08:31:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/8a8e5699-955a-4368-b0b4-ecf43b7ff3fc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>There's no shortage of AI content right now, but most of it sits at one of two extremes: breezy conceptual overviews that leave you with nothing to build, or narrow tutorials that solve one specific problem and generalize to nothing. <em>Design Multi-Agent AI Systems Using MCP and A2A</em> manages to occupy the useful middle ground - it's a book with genuine architectural depth that never loses sight of the fact that you're here to build real systems.</p>
<img src="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/7ace1cd7-4212-4988-8ea1-79ccd686812a.png" alt="" style="display:block;margin:0 auto" />

<hr />
<p><strong>The Arc of the Book</strong></p>
<p>The structure is well thought out. The first few chapters establish foundational concepts - what an AI agent actually is (autonomy, perception, reasoning, action, adaptation), how the agent loop works, and how memory, tools, and orchestration fit together. This isn't padding; the definitions are precise enough to be useful later when the complexity ramps up.</p>
<p>The standout early chapter is the hands-on walkthrough of a Kubernetes diagnostic agent. It's a deliberate provocation: <em>look how much you can do with how little</em>. The agent inspects cluster state, diagnoses issues using an LLM, proposes fixes, and requests human confirmation before making changes. It's a clean demonstration of the sense-think-act loop in the real world, and it sets a useful benchmark for what "simple but capable" looks like.</p>
<hr />
<p><strong>The AI-6 Framework</strong></p>
<p>Much of the book is structured around AI-6, a custom Python framework. You get to see how session management, context compression, LLM provider abstraction, and tool execution actually work - not as magic, but as explicit, readable code.</p>
<p>The tool system chapters are particularly strong. The book covers custom tools, MCP tools, provider-agnostic tool definitions, and the mechanics of how an LLM selects and invokes a tool. The discussion of tool safety - controlling what tools can access, sandboxing execution, and using human-in-the-loop confirmation for high-risk operations - is more thorough than most resources on the subject.</p>
<hr />
<p><strong>MCP: Why It Matters</strong></p>
<p>The Model Context Protocol chapter is one of the best concise explanations of MCP I've read. The problem it solves is stated clearly: before MCP, every agentic system had to build its own bespoke connectors to every external tool or service, creating tight coupling and constant maintenance burden. MCP introduces a shared protocol so that a tool built once can be consumed by any compliant host - "write once, run everywhere" for AI tooling.</p>
<p>The chapter covers the client-server architecture, the two protocol layers (data and transport), local versus remote servers, and how tool discovery works at runtime. Crucially, it also shows how MCP maps onto AI-6's existing tool abstractions, making integration feel natural rather than bolted on. For anyone evaluating whether to build MCP-native tooling, this chapter gives you the mental model you need.</p>
<hr />
<p><strong>A2A and Multi-Agent Orchestration</strong></p>
<p>The second half of the book moves into multi-agent territory, and this is where it becomes genuinely distinctive. The orchestration patterns chapter - covering sequential, parallel, hierarchical, event-driven, and collaborative patterns - is the kind of clean taxonomy that the field has needed. Each pattern is explained with its tradeoffs: sequential is predictable but slow, parallel maximizes throughput but requires result merging, collaborative enables emergent problem-solving but demands sophisticated coordination protocols.</p>
<p>The A2A (Agent-to-Agent) protocol coverage is timely. The breakdown of the five core primitives - agent cards, tasks, messages, parts, and artifacts - gives you a concrete vocabulary for designing inter-agent communication. The three interaction patterns (request/response polling, push notifications, and streaming via SSE) map cleanly to real use cases.</p>
<hr />
<p><strong>Testing, Debugging, and the Honest Chapter</strong></p>
<p>Chapter 10 on testing and debugging is the one most books skip or treat superficially, and its thoroughness here is appreciated. The catalog of failure modes is comprehensive: hallucinations embedded in tool calls, agents claiming to have executed operations they haven't, infinite retry loops, context drift across long sessions, tool selection errors, instruction following failures, and cross-agent interference in concurrent workflows. These aren't theoretical - they're the things that actually go wrong in production agentic systems.</p>
<p>The logging and observability guidance is practical: structured hierarchical traces, complete LLM prompt/response logging including token counts and latency, tool invocation capture with input/output, and contextual metadata tagging. The redundancy and resilience section addresses multi-provider LLM strategies and graceful degradation, which are often afterthoughts in agentic system design.</p>
<hr />
<p><strong>Who Should Read This</strong></p>
<p>This book is aimed squarely at software engineers and AI practitioners who want to move beyond building simple LLM wrappers and understand how production-grade multi-agent systems actually work. It assumes Python familiarity and some exposure to LLM APIs. If you're already deep in the weeds of agentic frameworks, some of the foundational chapters will feel familiar - but the MCP, A2A, orchestration patterns, and debugging chapters will likely contain material worth your time regardless of experience level.</p>
<p>It's a strong, practically-oriented book on a topic that genuinely matters right now. Recommended.</p>
]]></content:encoded></item><item><title><![CDATA[Agentic Architectural Patterns for Building Multi-Agent Systems - Book Review]]></title><description><![CDATA[Blog Review
Book Review: Agentic Architectural Patterns for Building Multi-Agent Systems
If you're serious about building production-grade AI agents - not just experimenting with ChatGPT wrappers - this book is essential reading. It bridges the gap b...]]></description><link>https://dimitaronai.com/agentic-architectural-patterns-for-building-multi-agent-systems-book-review</link><guid isPermaLink="true">https://dimitaronai.com/agentic-architectural-patterns-for-building-multi-agent-systems-book-review</guid><category><![CDATA[MultiAgentSystems]]></category><category><![CDATA[AI]]></category><category><![CDATA[agentic AI]]></category><category><![CDATA[generative ai]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Mon, 16 Feb 2026 10:07:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1771108434071/e90836c7-3b42-4211-a5df-29d71a6cca77.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-blog-review"><strong>Blog Review</strong></h2>
<h3 id="heading-book-review-agentic-architectural-patterns-for-building-multi-agent-systems"><strong>Book Review: Agentic Architectural Patterns for Building Multi-Agent Systems</strong></h3>
<p>If you're serious about building production-grade AI agents - not just experimenting with ChatGPT wrappers - this book is essential reading. It bridges the gap between AI research and software engineering with remarkable clarity.</p>
<hr />
<h1 id="heading-what-this-book-covers"><strong>What This Book Covers</strong></h1>
<p>The book is structured around a <strong>GenAI Maturity Model</strong> with six levels, progressing from basic GenAI applications to fully autonomous multi-agent systems. This framework alone is worth the read, as it provides a clear strategic roadmap for organizations navigating the agentic AI landscape.</p>
<p>The core of the book lies in its <strong>comprehensive pattern library</strong>:</p>
<ul>
<li><p><strong>Multi-Agent Coordination Patterns</strong>: From Supervisor Architecture and Swarm patterns to Consensus and Negotiation protocols</p>
</li>
<li><p><strong>Explainability &amp; Compliance</strong>: Instruction Fidelity Auditing, Fractal Chain-of-Thought, and Shared Epistemic Memory</p>
</li>
<li><p><strong>Robustness &amp; Fault Tolerance</strong> : Parallel Execution Consensus, Watchdog Timeouts, Agent Self-Defense against prompt injection</p>
</li>
<li><p><strong>Human-Agent Interaction</strong>: Clear patterns for delegation, escalation, and collaborative workflows</p>
</li>
</ul>
<hr />
<h1 id="heading-what-makes-it-stand-out"><strong>What Makes It Stand Out</strong></h1>
<p>Unlike typical AI books that focus on model training or prompt engineering, this treats <strong>agentic AI as a distributed systems problem</strong>. The authors understand that production systems require fault isolation, state management, observability, and security - not just clever prompts.</p>
<p>The <strong>practical implementations</strong> using Google ADK, CrewAI, and LangGraph demonstrate these patterns in action with a loan processing use case. Seeing the same problem solved with different frameworks helps you understand the trade-offs between abstraction levels.</p>
<p>The emphasis on <strong>AgentOps</strong> and the <strong>R⁵ model</strong> (Relax, Reflect, Reference, Retry, Report) shows mature thinking about production operations. Chapter 11 on self-improving agents through coevolved training is particularly forward-looking.</p>
<hr />
<h1 id="heading-final-verdict"><strong>Final Verdict</strong></h1>
<p>This is the most comprehensive guide available for building multi-agent systems that can survive production. It transforms agentic AI from an experimental curiosity into an engineering discipline with proven patterns, clear trade-offs, and practical implementations.</p>
<p><strong>The future belongs to those who can build systems where AI agents collaborate, recover from failures, and continuously improve.</strong> This book shows you how.</p>
]]></content:encoded></item><item><title><![CDATA[Using Logic Apps with Foundry Agents]]></title><description><![CDATA[Creating a Foundry agent
If you don’t have an existing Foundry agent, go ahead and create a foundry agent now. To continue with this example, you should have at least one agent as below:


Adding an action
Let’s add an action to the agent, which will...]]></description><link>https://dimitaronai.com/using-logic-apps-with-foundry-agents</link><guid isPermaLink="true">https://dimitaronai.com/using-logic-apps-with-foundry-agents</guid><category><![CDATA[Azure]]></category><category><![CDATA[AI]]></category><category><![CDATA[agentic AI]]></category><category><![CDATA[agents]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Thu, 29 Jan 2026 08:02:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635988027/9d8d4f7d-3f5e-4274-bd28-b2d565ad9275.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-creating-a-foundry-agent">Creating a Foundry agent</h1>
<p>If you don’t have an existing Foundry agent, go ahead and <a target="_blank" href="https://dimitaronai.com/microsoft-foundry-agents">create a foundry agent</a> now. To continue with this example, you should have at least one agent as below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635417305/b8639919-df56-46eb-84b6-ff012cade22f.png" alt class="image--center mx-auto" /></p>
<hr />
<h1 id="heading-adding-an-action">Adding an action</h1>
<p>Let’s add an action to the agent, which will use an Azure Logic App as a tool. Click on the agent, and on the right side select add for the actions:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635478023/4faa2e69-fb49-47b4-8170-e5883dcb8887.png" alt class="image--center mx-auto" /></p>
<p>Then, from the options choose Azure Logic Apps:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635512590/c05d5cbf-54de-4084-9087-f5d5764c6dac.png" alt class="image--center mx-auto" /></p>
<p>You can choose from your own authored actions or Microsoft authored. Let’s use a Microsoft authored action for the weather forecast:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635551055/b3f81865-68ee-4fa0-b594-2b86ffcd58eb.png" alt class="image--center mx-auto" /></p>
<p>Add the action name and description and choose the resource group, region and subscription needed to create the Logic App:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635588628/0f4dfbc5-5a1e-41a6-b501-5ce9d7af4aaa.png" alt class="image--center mx-auto" /></p>
<p>Finally, review the <code>schema</code> for the logic app workflow:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635620691/326c53da-5d34-4f79-ace7-7c70a85f82b9.png" alt class="image--center mx-auto" /></p>
<p>If you open the resource group in Azure, you can see that a connector and a Logic App were created automatically.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635670328/d4f75031-2e47-47a9-9897-a2553b3ed7ec.png" alt class="image--center mx-auto" /></p>
<p>You can also check the workflow in the Logic App designer. What’s important to note is that the trigger is an <code>HTTP request</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635701795/f00b6015-d628-4d85-9ce5-b7958bcd4ba9.png" alt class="image--center mx-auto" /></p>
<p>Looking the actions of the agent again, note that the <code>weather forecast tool</code> has been added.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635828958/8df223af-989f-4328-b0ba-babcd2d15e93.png" alt class="image--center mx-auto" /></p>
<p>To see if this works, open the playground and ask a weather-related question.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635730098/8890f16b-fda5-4d4f-9784-31f1856d633c.png" alt class="image--center mx-auto" /></p>
<p>Notice that the agent used one tool, which is the Logic App. Checking the execution of the Logic App also shows that it has been invoked.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769635766302/eaaeb2ba-39f9-4cdc-9d23-ab2da686241a.png" alt class="image--center mx-auto" /></p>
<hr />
]]></content:encoded></item><item><title><![CDATA[Microsoft Foundry Agents]]></title><description><![CDATA[Foundry Agent Service
Think of Foundry as a production line for intelligent agents. The Foundry Agent Service brings together the core components of Foundry - models, tools, and frameworks - into a unified runtime. It handles conversations, coordinat...]]></description><link>https://dimitaronai.com/microsoft-foundry-agents</link><guid isPermaLink="true">https://dimitaronai.com/microsoft-foundry-agents</guid><category><![CDATA[agentic AI]]></category><category><![CDATA[AI]]></category><category><![CDATA[Azure]]></category><category><![CDATA[ai agents]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Mon, 19 Jan 2026 09:18:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768765592202/7d3e76a6-f81d-4d32-aa17-0c98abc130b7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-foundry-agent-service"><strong>Foundry Agent Service</strong></h1>
<p>Think of Foundry as a production line for intelligent agents. The Foundry Agent Service brings together the core components of Foundry - models, tools, and frameworks - into a unified runtime. It handles conversations, coordinates tool execution, applies content safety controls, and integrates with identity, networking, and observability systems. Together, these capabilities ensure that agents are secure, scalable, and ready for production use.</p>
<hr />
<h1 id="heading-building-your-foundry-ai-agent">Building your Foundry AI Agent</h1>
<p>Start by installing the following NuGet packages:</p>
<pre><code class="lang-plaintext">dotnet add package Azure.Identity
dotnet add package Microsoft.Agents.AI.AzureAI.Persistent --prereleased
</code></pre>
<p>Next, add your Foundry endpoint and model deployment name, as well as the agent's name and instructions:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> endpoint = <span class="hljs-string">""</span>;
<span class="hljs-keyword">var</span> model = <span class="hljs-string">"gpt-4.1-mini-itt"</span>;
<span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> AgentName = <span class="hljs-string">"ArchitectAgent"</span>;
<span class="hljs-keyword">const</span> <span class="hljs-keyword">string</span> AgentInstructions = <span class="hljs-string">"You are an Azure Solutions Architect that uses knowledge from the official Microsoft docs to answer questions."</span>;
</code></pre>
<p>For MCP tools, we will use the official <strong>Microsoft Learn MCP Server</strong>, which you can find on the following GitHub <a target="_blank" href="https://github.com/microsoftdocs/mcp">repository</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768764926221/c13ae393-c84a-425f-b821-0aa2ff04b0fe.png" alt class="image--center mx-auto" /></p>
<p>Create an MCP tool definition by specifying the server label and URL:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> mcpTool = <span class="hljs-keyword">new</span> MCPToolDefinition(
    serverLabel: <span class="hljs-string">"msdocs_mcp"</span>,
    serverUrl: <span class="hljs-string">"https://learn.microsoft.com/api/mcp"</span>);
</code></pre>
<p>For the tools, add the three available tools from the documentation:</p>
<pre><code class="lang-csharp"><span class="hljs-comment">// Currently, only three tools are available</span>
mcpTool.AllowedTools.Add(<span class="hljs-string">"microsoft_docs_search"</span>);
mcpTool.AllowedTools.Add(<span class="hljs-string">"microsoft_docs_fetch"</span>);
mcpTool.AllowedTools.Add(<span class="hljs-string">"microsoft_code_sample_search"</span>);
</code></pre>
<p>To create the agent in Microsoft Foundry, use the following code which creates a <code>PersistentAgentsClient</code> and calls the <code>CreateAgentAsync</code>.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> persistentAgentsClient = <span class="hljs-keyword">new</span> PersistentAgentsClient(endpoint, <span class="hljs-keyword">new</span> DefaultAzureCredential());

<span class="hljs-keyword">var</span> agentMetadata = <span class="hljs-keyword">await</span> persistentAgentsClient.Administration.CreateAgentAsync(
    model: model,
    name: AgentName,
    instructions: AgentInstructions,
    tools: [mcpTool]);
</code></pre>
<p>The initial state of my Foundry is with zero agents:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768765038883/887e61e9-5650-4459-a40f-86a744e576f7.png" alt class="image--center mx-auto" /></p>
<p>After running the code, we can see the agent was successfully created.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768765064602/6c1a0361-e72b-47eb-9382-0f91b5b4be1f.png" alt class="image--center mx-auto" /></p>
<p>Agents created through Foundry aren't monoliths. Each agent has a specific role, is powered by the right model, and is equipped with the right tools.</p>
<p>To get the agent and execute it:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> agent = <span class="hljs-keyword">await</span> persistentAgentsClient.GetAIAgentAsync(agentMetadata.Value.Id);

<span class="hljs-keyword">var</span> runOptions = <span class="hljs-keyword">new</span> ChatClientAgentRunOptions()
{
    ChatOptions = <span class="hljs-keyword">new</span>()
    {
        RawRepresentationFactory = (_) =&gt; <span class="hljs-keyword">new</span> ThreadAndRunOptions()
        {
            ToolResources = <span class="hljs-keyword">new</span> MCPToolResource(serverLabel: <span class="hljs-string">"msdocs_mcp"</span>)
            {
                RequireApproval = <span class="hljs-keyword">new</span> MCPApproval(<span class="hljs-string">"never"</span>),
            }.ToToolResources()
        }
    }
};

AgentThread thread = agent.GetNewThread();
<span class="hljs-keyword">var</span> response = <span class="hljs-keyword">await</span> agent.RunAsync(<span class="hljs-string">"What is Azure Key Vault?"</span>, thread, runOptions);
Console.WriteLine(response);
</code></pre>
<p>Notice that we can configure the tool resources with approval settings. The <code>RequireApproval</code> controls when user approval is needed for tool invocations:</p>
<ul>
<li><p><code>"never"</code>: Tools invocations don’t require user approval</p>
</li>
<li><p><code>"always"</code>: All tool invocations require user approval</p>
</li>
<li><p>Custom approval rules can also be configured</p>
</li>
</ul>
<p>The response returned by the agent is the following:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768765252839/77eda616-ba7e-4327-86e6-efb6a45d5785.png" alt class="image--center mx-auto" /></p>
<p>Finally, to delete an agent we can run the following code:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">await</span> persistentAgentsClient.Administration.DeleteAgentAsync(agent.Id);
</code></pre>
<hr />
]]></content:encoded></item><item><title><![CDATA[Semantic Cache for LLM responses]]></title><description><![CDATA[Prerequisites

API Management instance with an Azure OpenAI model deployment as an API

Deployment for the following APIs:

Chat Completion API

Embeddings API





Configured API Management instance to use managed identity authentication to the Azur...]]></description><link>https://dimitaronai.com/semantic-cache-for-llm-responses</link><guid isPermaLink="true">https://dimitaronai.com/semantic-cache-for-llm-responses</guid><category><![CDATA[Azure]]></category><category><![CDATA[AI]]></category><category><![CDATA[agentic AI]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Mon, 05 Jan 2026 08:10:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767563281966/f80bedff-a36e-494f-8544-fc49c5fcdd45.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-prerequisites"><strong>Prerequisites</strong></h1>
<ul>
<li><p>API Management instance with an Azure OpenAI model deployment as an API</p>
</li>
<li><p>Deployment for the following APIs:</p>
<ul>
<li><p>Chat Completion API</p>
</li>
<li><p>Embeddings API</p>
</li>
</ul>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767557003875/ca43291f-5117-413c-b8d7-e2d61141074a.png" alt class="image--center mx-auto" /></p>
</li>
<li><p>Configured API Management instance to use managed identity authentication to the Azure OpenAI service</p>
</li>
<li><p>An Azure Managed Redis instance with the <code>RediSearch</code> module enabled</p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767557449758/e780876e-9063-48a5-b0a5-16edff317bfc.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<hr />
<h1 id="heading-authenticate-with-managed-identity"><strong>Authenticate with managed identity</strong></h1>
<p>Let’s quickly explain how to use Managed Identity to authenticate to Azure OpenAI. Make sure that the managed identity is enabled on your API Management instance.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767556855571/586023c6-b4d7-47fe-a2f0-a47357a447df.png" alt class="image--center mx-auto" /></p>
<p>Assign the <code>Cognitive Services OpenAI User</code> role to the managed identity. Add the following inbound policy section to authenticate requests to the API by using the managed identity.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767558274371/855717c0-6821-40ce-93d1-995b07475fdf.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">authentication-managed-identity</span> <span class="hljs-attr">resource</span>=<span class="hljs-string">"https://cognitiveservices.azure.com"</span> <span class="hljs-attr">output-token-variable-name</span>=<span class="hljs-string">"managed-id-access-token"</span> <span class="hljs-attr">ignore-error</span>=<span class="hljs-string">"false"</span> /&gt;</span> 
<span class="hljs-tag">&lt;<span class="hljs-name">set-header</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"Authorization"</span> <span class="hljs-attr">exists-action</span>=<span class="hljs-string">"override"</span>&gt;</span> 
    <span class="hljs-tag">&lt;<span class="hljs-name">value</span>&gt;</span>@("Bearer " + (string)context.Variables["managed-id-access-token"])<span class="hljs-tag">&lt;/<span class="hljs-name">value</span>&gt;</span> 
<span class="hljs-tag">&lt;/<span class="hljs-name">set-header</span>&gt;</span>
</code></pre>
<hr />
<h1 id="heading-import-an-azure-openai-api"><strong>Import an Azure OpenAI API</strong></h1>
<p>If you have imported an Azure OpenAI instance as an API into APIM you should see something like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767558312983/b1fd529b-d1ee-4651-a392-3585330623c9.png" alt class="image--center mx-auto" /></p>
<p>If not, you can either import an Azure OpenAI API directly from a deployment in Microsoft Foundry or download and edit the OpenAPI specification.</p>
<hr />
<h1 id="heading-azure-managed-redis-setup">Azure Managed Redis Setup</h1>
<p>We will setup an external cache using the Azure Managed Redis instance.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767560600011/b2ba07b4-038a-4516-b093-730fb3e1eff8.png" alt class="image--center mx-auto" /></p>
<p><mark>Azure API Management uses a Redis connection string to connect to the cache. Because we are using Azure Managed Redis, we need to </mark> <strong><mark>enable </mark></strong> <mark>access key authentication for the cache and use the key as password in the connection string. Currently, we can't use Microsoft Entra authentication to connect Azure API Management to Azure Managed Redis.</mark></p>
<p>The connection string will look like:</p>
<pre><code class="lang-plaintext">&lt;cache-name&gt;:10000,password=&lt;cache-access-key&gt;,ssl=True,abortConnect=False
</code></pre>
<hr />
<h1 id="heading-semantic-cache-configuration">Semantic Cache Configuration</h1>
<p>Start by creating a backend for the embeddings API.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767558683062/db63ee10-6bec-451d-98fc-a39c16cac3e5.png" alt class="image--center mx-auto" /></p>
<p>Next, let’s configure the semantic caching policies. In the Inbound section, add the <code>azure-openai-semantic-cache-lookup</code> policy. In the embeddings-backend-id attribute, specify the Embeddings API backend you created. In my case, the value is <code>Embedding</code>.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">azure-openai-semantic-cache-lookup</span>
    <span class="hljs-attr">score-threshold</span>=<span class="hljs-string">"0.15"</span>
    <span class="hljs-attr">embeddings-backend-id</span>=<span class="hljs-string">"Embedding"</span>
    <span class="hljs-attr">embeddings-backend-auth</span>=<span class="hljs-string">"system-assigned"</span>
    <span class="hljs-attr">ignore-system-messages</span>=<span class="hljs-string">"true"</span>
    <span class="hljs-attr">max-message-count</span>=<span class="hljs-string">"10"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">vary-by</span>&gt;</span>@(context.Subscription.Id)<span class="hljs-tag">&lt;/<span class="hljs-name">vary-by</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">azure-openai-semantic-cache-lookup</span>&gt;</span>
</code></pre>
<p>In the Outbound processing section for the API, add the <code>azure-openai-semantic-cache-store</code> policy.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">azure-openai-semantic-cache-store</span> <span class="hljs-attr">duration</span>=<span class="hljs-string">"60"</span> /&gt;</span>
</code></pre>
<p>To confirm the semantic cache is working, we can trace a test Completion or Chat Completion operation by using the test console in the portal.</p>
<p>Example from my trace operation:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1767560288292/7c3c2e90-7c53-40cd-83c9-e733134ae8dd.png" alt class="image--center mx-auto" /></p>
<hr />
]]></content:encoded></item><item><title><![CDATA[Automated secret expiration alerts with Azure Logic Apps]]></title><description><![CDATA[In this article, I will walk you through on how to create an automated solution using Azure Logic Apps to monitor App Registration secrets in Entra ID and send email notifications before they expire.

Prerequisites

Azure subscription with a Logic Ap...]]></description><link>https://dimitaronai.com/automated-secret-expiration-alerts-with-azure-logic-apps</link><guid isPermaLink="true">https://dimitaronai.com/automated-secret-expiration-alerts-with-azure-logic-apps</guid><category><![CDATA[Azure]]></category><category><![CDATA[Entra ID]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Sun, 21 Dec 2025 15:23:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766307729464/ea180099-3edc-4745-aaa2-f92581f191c4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this article, I will walk you through on how to create an automated solution using Azure Logic Apps to monitor App Registration secrets in Entra ID and send email notifications before they expire.</p>
<hr />
<h1 id="heading-prerequisites">Prerequisites</h1>
<ul>
<li><p>Azure subscription with a Logic App already created</p>
</li>
<li><p>A managed identity with appropriate permissions to read App Registrations</p>
</li>
</ul>
<hr />
<h1 id="heading-create-a-managed-identity-for-the-logic-app">Create a Managed Identity for the Logic App</h1>
<p>First, you'll need to grant your Logic App's managed identity the necessary permissions. Assign the Microsoft Graph API permission <a target="_blank" href="http://Application.Read"><code>Application.Read</code></a><code>.All</code> to the managed identity.</p>
<p>To be easier to follow, I split the design of the workflow in three parts. Let’s continue with the first.</p>
<hr />
<h1 id="heading-logic-app-workflow-foundations"><strong>Logic App Workflow -</strong> Foundations</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766244411644/e56335f2-64ae-481b-b98f-9672ba7fcf2f.png" alt class="image--center mx-auto" /></p>
<p>Let’s look at the entry point of our Logic App. For the trigger, I use <strong>Recurrence</strong> that I set to run daily at 9 AM.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246260649/1f416165-96db-4659-b7ec-7454fc29d849.png" alt class="image--center mx-auto" /></p>
<p>The next step is to initialize a few variables. I set a variable named <code>DaysThreshold</code> of type <code>Integer</code> with an initial value of 30. This will allow me to alert for secrets expiring within 30 days. The second variable is <code>ExpiringSecrets</code> which I initialize as an empty array (<code>[]</code>).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246274446/617c7b6d-0fec-4638-92a8-ab1b8e313b8c.png" alt class="image--center mx-auto" /></p>
<p>Add an HTTP action to fetch all app registrations. I do that by calling the HTTP GET <code>https://graph.microsoft.com/v1.0/applications</code> endpoint. For the authentication type use Managed Identity and the audience <code>https://graph.microsoft.com</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246288164/7995abc5-b85b-4ebe-be57-057e54265f76.png" alt class="image--center mx-auto" /></p>
<p>Finally, use a parse JSON action to parse the response from the API. You can use the following sample schema for it:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
  <span class="hljs-attr">"properties"</span>: {
    <span class="hljs-attr">"value"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"array"</span>,
      <span class="hljs-attr">"items"</span>: {
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
        <span class="hljs-attr">"properties"</span>: {
          <span class="hljs-attr">"id"</span>: {
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
          },
          <span class="hljs-attr">"appId"</span>: {
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
          },
          <span class="hljs-attr">"displayName"</span>: {
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
          }
        },
        <span class="hljs-attr">"required"</span>: [
          <span class="hljs-string">"id"</span>,
          <span class="hljs-string">"appId"</span>,
          <span class="hljs-string">"displayName"</span>
        ]
      }
    }
  }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246301874/8bba3ea6-7659-4a50-8c8d-239b6476aea1.png" alt class="image--center mx-auto" /></p>
<p>That’s all the steps we need to get started with the workflow. In the second part, I will show you how to get the secrets for each app registration and check their expiry.</p>
<hr />
<h1 id="heading-logic-app-workflow-core-logic"><strong>Logic App Workflow -</strong> Core Logic</h1>
<p>Congratulations for making it this far. Let’s now complicate the workflow a little.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766244806630/997e7eb2-e036-4bbf-ad99-b80ec66a135b.png" alt class="image--center mx-auto" /></p>
<p>As we can see, there is a lot to unpack here.</p>
<p>Start off by adding a for each loop. This allows us to loop through each app registration. For the configuration, use the output from the previous step. From dynamic content, select <strong>value</strong> from the Parse JSON step.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246316754/ff2a021e-c118-49bf-84eb-326d8d1b7733.png" alt class="image--center mx-auto" /></p>
<p>Inside the for each loop, add another HTTP action. Inside, call the following HTTP GET endpoint <a target="_blank" href="https://graph.microsoft.com/v1.0/applications/items\('For_each'\)?%5B'id'%5D?$select=id,appId,displayName,passwordCredentials"><code>https://graph.microsoft.com/v1.0/applications/@{items('For_each')?['id']}</code></a>. Use the same authentication type as before, which is Managed Identity, and the same audience.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246335646/d38e0c92-d751-4e30-8b13-d053daeb22f6.png" alt class="image--center mx-auto" /></p>
<p>Again, use another Parse JSON action to parse the body of the response. You can use this sample JSON schema:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
  <span class="hljs-attr">"properties"</span>: {
    <span class="hljs-attr">"id"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
    },
    <span class="hljs-attr">"appId"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
    },
    <span class="hljs-attr">"displayName"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
    },
    <span class="hljs-attr">"passwordCredentials"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"array"</span>,
      <span class="hljs-attr">"items"</span>: {
        <span class="hljs-attr">"type"</span>: <span class="hljs-string">"object"</span>,
        <span class="hljs-attr">"properties"</span>: {
          <span class="hljs-attr">"customKeyIdentifier"</span>: {
            <span class="hljs-attr">"type"</span>: [
              <span class="hljs-string">"string"</span>,
              <span class="hljs-string">"null"</span>
            ]
          },
          <span class="hljs-attr">"displayName"</span>: {
            <span class="hljs-attr">"type"</span>: [
              <span class="hljs-string">"string"</span>,
              <span class="hljs-string">"null"</span>
            ]
          },
          <span class="hljs-attr">"endDateTime"</span>: {
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
          },
          <span class="hljs-attr">"hint"</span>: {
            <span class="hljs-attr">"type"</span>: [
              <span class="hljs-string">"string"</span>,
              <span class="hljs-string">"null"</span>
            ]
          },
          <span class="hljs-attr">"keyId"</span>: {
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
          },
          <span class="hljs-attr">"startDateTime"</span>: {
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"string"</span>
          },
          <span class="hljs-attr">"secretText"</span>: {
            <span class="hljs-attr">"type"</span>: [
              <span class="hljs-string">"string"</span>,
              <span class="hljs-string">"null"</span>
            ]
          }
        }
      }
    },
    <span class="hljs-attr">"keyCredentials"</span>: {
      <span class="hljs-attr">"type"</span>: <span class="hljs-string">"array"</span>
    }
  }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246346347/cab10fd1-6cc5-4911-ac7b-a25a664fd7ec.png" alt class="image--center mx-auto" /></p>
<p>With a condition, check if the length of the <code>passwordCredentials</code> array is greater than zero. If it is, then we do another for each loop to go through each credential separately.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246148385/1b473deb-606c-41ac-bec1-9701091bac7c.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246457208/eade8319-a026-4324-b1ac-fd634d9c9695.png" alt class="image--center mx-auto" /></p>
<p>In the compose action, switch to expression tab and paste:</p>
<pre><code class="lang-typescript">div(sub(ticks(items(<span class="hljs-string">'Apply_to_each'</span>)?[<span class="hljs-string">'endDateTime'</span>]), ticks(utcNow())), <span class="hljs-number">864000000000</span>)
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246170066/4c5a1f86-a5c6-49f9-9831-c29e9820a9ec.png" alt class="image--center mx-auto" /></p>
<p>Add another condition and check if the <code>Outputs</code> from the compose action is less than or equal to <code>DaysThreshold</code> we defined at the start.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246212112/b8dba967-0ac5-4323-83b1-be7be3c52829.png" alt class="image--center mx-auto" /></p>
<p>If it’s true, add the expiring secret to the array we defined.</p>
<pre><code class="lang-typescript">
json(concat(<span class="hljs-string">'{\"appName\":\"'</span>, body(<span class="hljs-string">'Parse_JSON_1'</span>)?[<span class="hljs-string">'displayName'</span>], <span class="hljs-string">'\",\"appId\":\"'</span>, body(<span class="hljs-string">'Parse_JSON_1'</span>)?[<span class="hljs-string">'appId'</span>], <span class="hljs-string">'\",\"secretName\":\"'</span>, coalesce(items(<span class="hljs-string">'For_each_1'</span>)?[<span class="hljs-string">'displayName'</span>], <span class="hljs-string">'No Name'</span>), <span class="hljs-string">'\",\"secretHint\":\"'</span>, items(<span class="hljs-string">'For_each_1'</span>)?[<span class="hljs-string">'hint'</span>], <span class="hljs-string">'\",\"expirationDate\":\"'</span>, items(<span class="hljs-string">'For_each_1'</span>)?[<span class="hljs-string">'endDateTime'</span>], <span class="hljs-string">'\",\"daysRemaining\":\"'</span>, <span class="hljs-built_in">string</span>(outputs(<span class="hljs-string">'Compose'</span>)), <span class="hljs-string">'\"}'</span>))
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246228825/bd814b11-ebf7-48e8-9f15-dad9655fe0db.png" alt class="image--center mx-auto" /></p>
<p>The most complicated part of the workflow is now complete. In the last part, we will take a look at how to send an email notification with a list of expiring secrets.</p>
<hr />
<h1 id="heading-logic-app-workflow-completion"><strong>Logic App Workflow -</strong> Completion</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766245477212/1d3086ab-cc36-46c8-8b10-be7b1976e5cc.png" alt class="image--center mx-auto" /></p>
<p>The condition here just checks if the length of <code>ExpiringSecrets</code> we defined at the start is greater than 0. If it is, that means we have secrets that will expire soon!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246362396/6eabf91a-ddb5-49d2-8f4c-e34cd1b09dfa.png" alt class="image--center mx-auto" /></p>
<p>We use the create HTML table with <code>ExpiringSecrets</code> to construct a simple table we will use in the email.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246373756/91f9003d-6858-425e-b8d3-537fea46a7e1.png" alt class="image--center mx-auto" /></p>
<p>Final step is to add the <code>Send an email (V2)</code> action and send the email notification. I made the email body to be easy to read and understand:</p>
<pre><code class="lang-typescript">The following App Registration secrets will expire within the next <span class="hljs-number">30</span> days:

body(<span class="hljs-string">'Create_HTML_table'</span>)

Please renew these secrets <span class="hljs-keyword">as</span> soon <span class="hljs-keyword">as</span> possible to avoid service disruption.
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766246406588/7b3aeb3c-f7bc-4100-97bd-692762ca1779.png" alt class="image--center mx-auto" /></p>
<p>And that’s it. Lastly, save and test the workflow. Following is a sample email I received from my workflow (I edited out the appName, appId, secretName and secretHint).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766245801390/6cd4113e-7d19-4ff8-a904-97bf0e88df9e.png" alt class="image--center mx-auto" /></p>
<hr />
]]></content:encoded></item><item><title><![CDATA[Mastering Docker on Windows - Book Review]]></title><description><![CDATA[A Comprehensive Guide for Windows-Based Container Workflows
"Mastering Docker on Windows" fills a critical gap in the Docker literature landscape. While countless resources exist for Docker on Linux, this book tackles the unique challenges and archit...]]></description><link>https://dimitaronai.com/mastering-docker-on-windows-book-review</link><guid isPermaLink="true">https://dimitaronai.com/mastering-docker-on-windows-book-review</guid><category><![CDATA[Docker]]></category><category><![CDATA[containers]]></category><category><![CDATA[Windows]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Tue, 16 Dec 2025 11:00:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1765882512891/b189ad8c-d7cc-4fc3-845b-d117e9a35d1b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-a-comprehensive-guide-for-windows-based-container-workflows">A Comprehensive Guide for Windows-Based Container Workflows</h2>
<p>"Mastering Docker on Windows" fills a critical gap in the Docker literature landscape. While countless resources exist for Docker on Linux, this book tackles the unique challenges and architectural differences that arise when running Docker on Windows 11 with WSL 2.</p>
<h4 id="heading-what-makes-this-book-different">What Makes This Book Different</h4>
<p>The author approaches Docker on Windows with refreshing honesty - acknowledging that Windows can't run Linux containers natively and that everything flows through WSL 2 under the hood. This fundamental difference impacts installation, troubleshooting, performance tuning, file paths, networking, and image caching. Rather than glossing over these complexities, the book uses them as teaching opportunities.</p>
<h4 id="heading-coverage-and-structure">Coverage and Structure</h4>
<p>The book systematically builds from fundamentals to advanced topics across five comprehensive chapters:</p>
<p><strong>Installation and Management</strong> - The opening chapter establishes essential concepts while addressing Windows-specific setup requirements. The discussion of resource management through .wslconfig files and the explanation of how Docker Desktop abstracts the WSL 2 complexity are particularly valuable. The author also provides practical guidance on image management, including handling dangling images and understanding the critical difference between images as blueprints and containers as running instances.</p>
<p><strong>Networking Implementation</strong> - This chapter excels at demystifying Docker networking on Windows. The explanation of why <a target="_blank" href="http://localhost">localhost</a> behaves differently in WSL 2 environments is crucial knowledge that many developers discover through frustrating trial and error. The book clearly distinguishes between bridge, host, and overlay networks, with particular attention to user-defined networks and their automatic DNS resolution capabilities. The security guidance here is practical rather than paranoid, focusing on minimal port exposure and proper network isolation.</p>
<p><strong>Data Persistence and Volumes</strong> - The treatment of volumes goes beyond basic usage to address enterprise concerns. The author covers backup strategies, disaster recovery workflows, and cross-container data sharing patterns. The discussion of volume performance optimization and the appropriate use cases for named volumes versus bind mounts versus tmpfs volumes demonstrates real-world experience. The security section on encrypting volumes and restricting access shows attention to production requirements.</p>
<p><strong>Multi-Container Orchestration</strong> - The Docker Compose chapter teaches patterns that scale beyond toy examples. The layered configuration approach using base files and environment-specific overrides reflects how actual development teams structure their projects. The honest discussion of depends_on limitations and the need for health checks addresses a common source of frustration. The author also provides practical advice on service naming, version pinning, and when to split large stacks into multiple files.</p>
<p><strong>Security and Best Practices</strong> - This final chapter confronts the "it's containerized so it's secure" fallacy directly. The explanation of container isolation through namespaces and the risks of privileged containers or Docker socket mounting is clear and actionable. The guidance on running containers as non-root users, scanning images for vulnerabilities, and maintaining minimal base images represents security thinking that belongs in every Docker workflow.</p>
<h4 id="heading-final-verdict">Final Verdict</h4>
<p>"Mastering Docker on Windows" succeeds in its mission to provide practical, production-ready guidance for Docker on Windows.</p>
<p>For anyone working with Docker on Windows, this book transforms Docker from a mysterious black box into an understandable, controllable tool. It's the resource the Windows Docker community has needed - practical, honest, and comprehensive.</p>
]]></content:encoded></item><item><title><![CDATA[Building Agents with OpenAI Agents SDK - Book Review]]></title><description><![CDATA[A Comprehensive Guide to the Future of Autonomous AI Systems
If you've been watching the AI space evolve from simple chatbots to sophisticated autonomous systems, this book offers a masterclass in making that transition practical and achievable.

Wha...]]></description><link>https://dimitaronai.com/building-agents-with-openai-agents-sdk-book-review</link><guid isPermaLink="true">https://dimitaronai.com/building-agents-with-openai-agents-sdk-book-review</guid><category><![CDATA[agentic AI]]></category><category><![CDATA[books]]></category><category><![CDATA[openai]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Thu, 20 Nov 2025 10:38:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763585669423/f212d7ef-5f22-4f28-9bb3-ab731d8ba672.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-a-comprehensive-guide-to-the-future-of-autonomous-ai-systems">A Comprehensive Guide to the Future of Autonomous AI Systems</h2>
<p>If you've been watching the AI space evolve from simple chatbots to sophisticated autonomous systems, this book offers a masterclass in making that transition practical and achievable.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763585684145/b389cdc7-2be0-4173-8463-0c82daee42d6.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-what-this-book-covers">What This Book Covers</h3>
<p>"Building Agents with OpenAI Agents SDK" takes readers on a journey from foundational concepts to production-ready implementations. The author systematically builds your understanding across nine comprehensive chapters, moving from theory to hands-on development.</p>
<p>The book opens by establishing what truly differentiates AI agents from conventional software. Unlike deterministic systems that follow rigid instructions, AI agents can reason from ambiguous goals, create plans autonomously, and adapt when situations change. This philosophical foundation proves crucial for understanding the architectural decisions that follow.</p>
<h3 id="heading-the-technical-deep-dive">The Technical Deep Dive</h3>
<p>What sets this book apart is its focus on OpenAI Agents SDK as a practical framework. The author demonstrates how this SDK eliminates thousands of lines of boilerplate code that would otherwise be needed for orchestration, tracing, and logging. The framework's minimalist abstraction philosophy means developers work with familiar Python constructs - agents are simply Python objects, tools are decorated functions, and orchestration uses standard language patterns.</p>
<p>The coverage of core primitives is particularly strong. Readers learn how agents serve as configurable wrappers around language models, how the Runner class manages the iterative reasoning loop, and how tools enable agents to interact with the external world. The book excels at explaining these concepts through practical examples.</p>
<h3 id="heading-memory-knowledge-and-multi-agent-orchestration">Memory, Knowledge, and Multi-Agent Orchestration</h3>
<p>The middle chapters tackle crucial challenges in building intelligent systems. The discussion of memory management distinguishes between short-term working memory and long-term persistent memory, offering practical patterns like sliding message windows and structured memory recall. The treatment of knowledge - both training knowledge baked into models and retrieved knowledge accessed dynamically - provides clear guidance on implementing retrieval-augmented generation patterns.</p>
<p>The multi-agent systems chapter is a standout, exploring both deterministic and dynamic orchestration strategies. The comparison between handoff patterns and agent-as-tool patterns gives developers the framework to make informed architectural decisions. The coverage extends to centralized, hierarchical, decentralized, and swarm architectures, each with clear use cases and trade-offs.</p>
<h3 id="heading-production-readiness">Production Readiness</h3>
<p>Later chapters address the often-overlooked aspects of deploying agent systems to production. The book covers visualization tools for understanding system architecture, guardrails for validating inputs and outputs, comprehensive tracing for debugging, and testing strategies for non-deterministic systems. These operational concerns are treated with the same rigor as development topics.</p>
<h3 id="heading-who-should-read-this">Who Should Read This</h3>
<p>This book is ideal for developers and technical leaders who want to move beyond experimentation with AI to building production-grade autonomous systems. While it assumes basic Python knowledge, the explanations are clear enough for intermediate developers while providing depth that experienced practitioners will appreciate.</p>
<h3 id="heading-the-bottom-line">The Bottom Line</h3>
<p>"Building Agents with OpenAI Agents SDK" succeeds as both a comprehensive reference and a practical guide. It doesn't just explain concepts - it shows you how to implement and manage them. The author's systematic approach, from foundations through advanced multi-agent systems to production management, provides a complete toolkit for building the next generation of intelligent applications.</p>
<p>Whether you're automating business workflows, creating specialized assistants, or innovating new AI-powered products, this book equips you with the knowledge and practical skills to build agents that handle meaningful, real-world tasks. It's a valuable addition to any AI developer's library and a solid foundation for anyone serious about building autonomous AI systems.</p>
]]></content:encoded></item><item><title><![CDATA[AI at Ignite 2025: My Personal Top 5 Session Picks]]></title><description><![CDATA[Microsoft Ignite is just around the corner, and I know many attendees are still deciding where to begin with AI. To help with that, I’ve put together my top five recommended AI sessions you won’t want to miss at this year’s event.
Below are the sessi...]]></description><link>https://dimitaronai.com/ai-at-ignite-2025-my-personal-top-5-session-picks</link><guid isPermaLink="true">https://dimitaronai.com/ai-at-ignite-2025-my-personal-top-5-session-picks</guid><category><![CDATA[Azure]]></category><category><![CDATA[AI]]></category><category><![CDATA[generative ai]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Mon, 17 Nov 2025 13:50:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763387350545/4c759927-8b88-440a-872b-dcb29e1c8a61.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Microsoft Ignite is just around the corner, and I know many attendees are still deciding where to begin with AI. To help with that, I’ve put together my top five recommended AI sessions you won’t want to miss at this year’s event.</p>
<p>Below are the sessions, presented in no specific order:</p>
<ol>
<li><p><strong><mark>The future of RAG with agentic knowledge retrieval and AI Search</mark></strong> - a session exploring how to modernize RAG for agents, showcasing new Azure AI Search and Foundry capabilities - including multi-source RAG orchestration, retrieval steering, dynamic security controls, and agentic grounding.</p>
</li>
<li><p><strong><mark>Apps, agents, and MCP is the AI innovation recipe</mark></strong> - a session on how Azure App Platform enables secure, scalable, and well-governed deployment of AI agents, covering MCP tool integration, observability, access controls, and policies for managing agents in production environments.</p>
</li>
<li><p><strong><mark>Don't let your AI agents go rogue, govern with Azure API Management</mark></strong> - a session showing how Azure API Management serves as a unified control plane for securing, governing, and monitoring AI workloads - using AI Gateway and MCP support to protect data, enforce policies, control costs, and ensure safe, scalable enterprise AI adoption.</p>
</li>
<li><p><strong><mark>Building responsible AI agents with Azure AI Foundry</mark></strong> - a session on how Azure AI Foundry enables responsible, compliant, and scalable AI agent development with built-in safety evaluations, observability, and governance - addressing risks like task adherence, explainability, and data leakage through real-world examples.</p>
</li>
<li><p><strong><mark>From DEV to PROD: How to build agentic memory with Azure Cosmos DB</mark></strong> - a session on using Azure Cosmos DB’s advanced retrieval capabilities to manage operational data - like agent memories - and enhance agent intelligence, featuring real-world patterns and lessons from large-scale multi-agent systems.</p>
</li>
</ol>
<p>This year’s Ignite offers an incredible opportunity to deepen your AI expertise. These five sessions stand out for their practical value, forward-looking perspectives, and relevance to real-world enterprise challenges. I hope my recommendations help you navigate the event and get the most out of everything Microsoft is bringing to the AI space.</p>
<p>If you're interested in my full session catalog, you can find it on my LinkedIn <a target="_blank" href="https://www.linkedin.com/posts/dimitar-iliev96_ignite-2025-schedule-activity-7395850105029017600-ySBv?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAADQKFTkB1C5O6yGsHH8kD4gOkZrysb8V4z8">profile</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1763387209799/f89f3560-c9ba-47e1-9ed5-9567ec5fa06c.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Manually Run a Non-HTTP Triggered Azure Function]]></title><description><![CDATA[Introduction
Sometimes, we need to trigger our Azure Functions indirectly. What that means is that we want to trigger a function that is on a schedule or a function that runs as a result of an action from another resource. That action can be from a B...]]></description><link>https://dimitaronai.com/manually-run-a-non-http-triggered-azure-function</link><guid isPermaLink="true">https://dimitaronai.com/manually-run-a-non-http-triggered-azure-function</guid><category><![CDATA[Azure]]></category><category><![CDATA[Cloud Computing]]></category><category><![CDATA[Microsoft]]></category><category><![CDATA[serverless]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Tue, 11 Nov 2025 10:59:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762858720720/43335bb1-7a31-401a-bdf0-d8c8ec6d9116.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>Sometimes, we need to trigger our Azure Functions indirectly. What that means is that we want to trigger a function that is on a schedule or a function that runs as a result of an action from another resource. That action can be from a Blob storage, Service Bus Queue or Topic and other.</p>
<p>In this example, we will take a look at how we can manually run an Azure function that responds to messages from a Service Bus queue.</p>
<p>We will learn how to trigger the function without actually sending a message to the queue.</p>
<hr />
<h1 id="heading-examining-the-azure-function">Examining the Azure function</h1>
<p>For this example, we have a very simple Azure function. The code for the function implementation is the following:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">namespace</span> <span class="hljs-title">FunctionAppSbTrigger</span>
{
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Function1</span>
    {
        [<span class="hljs-meta">FunctionName(<span class="hljs-meta-string">"MyAwesomeFunction"</span>)</span>]
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">Run</span>(<span class="hljs-params">[ServiceBusTrigger(<span class="hljs-string">"myqueue"</span>, Connection = <span class="hljs-string">"ServiceBusConnection"</span></span>)]<span class="hljs-keyword">string</span> myQueueItem, ILogger log)</span>
        {
            log.LogInformation(<span class="hljs-string">$"C# ServiceBus queue trigger function processed message: <span class="hljs-subst">{myQueueItem}</span>"</span>);
        }
    }
}
</code></pre>
<p>We can see that we have the Service Bus trigger set up that responds to messages from the "myqueue" queue.</p>
<p>Now if we try sending a message to the queue, we can observe that our function executes successfully.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762858245558/0b6895f3-b441-4ed9-a994-78b6ed547e0f.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762858230480/fbec269f-bcb4-4884-8170-effcc6e49397.png" alt class="image--center mx-auto" /></p>
<p>But what if we have a situation where we don't want to or just simply can't send a message to the queue. How can we test our function then?</p>
<hr />
<h1 id="heading-manually-running-the-function">Manually running the function</h1>
<p>To run a non HTTP-triggered function, we can send a simple <code>HTTP POST</code> request.</p>
<p>Now you might be thinking "But Dimitar, this is not even an HTTP triggered function, how can we do that?" and you're right.</p>
<p>We will need to construct a special type of URL to make this request to.</p>
<p>The code of our <code>launchSettings.json</code> file is:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"profiles"</span>: {
    <span class="hljs-attr">"FunctionAppSbTrigger"</span>: {
      <span class="hljs-attr">"commandName"</span>: <span class="hljs-string">"Project"</span>,
      <span class="hljs-attr">"commandLineArgs"</span>: <span class="hljs-string">"--port 7289"</span>,
      <span class="hljs-attr">"launchBrowser"</span>: <span class="hljs-literal">false</span>
    }
  }
}
</code></pre>
<p>We will construct the new URL using the following information:</p>
<ul>
<li><p><strong>Host name:</strong> The function app's public location</p>
</li>
<li><p><strong>Folder path:</strong> we have to send the request through the folders <em>admin/functions</em></p>
</li>
<li><p><strong>Function name:</strong> name of the function we want to run</p>
</li>
</ul>
<p>The special URL will have the form:</p>
<pre><code class="lang-yaml">{<span class="hljs-string">hostName</span>}<span class="hljs-string">/{folderPath}/{functionName}</span>
</code></pre>
<p>Replacing these values gives us the final state of the URL:</p>
<pre><code class="lang-yaml"><span class="hljs-string">http://localhost:7289/admin/functions/MyAwesomeFunction</span>
</code></pre>
<hr />
<h1 id="heading-testing-it-all-out">Testing it all out</h1>
<p>Next, let's open Postman and do a simple test.</p>
<p>We will set the HTTP method to <code>POST</code> and use the <code>URL</code> from the previous step. We can also specify a body for our request.</p>
<blockquote>
<p>if we don't want to pass any data to our function, we still need to pass {} as the body of the request.</p>
</blockquote>
<p>In the <code>Headers</code> section set the <code>'Content-Type'</code> to <code>'application/json'</code>.</p>
<p>You should have a similar setup as the image below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762858455404/b8cad63f-f1d2-4047-963d-eae5465eeb8e.png" alt class="image--center mx-auto" /></p>
<p>Now click on the 'Send' button. Observe that we have successfully triggered our Azure Function without sending any messages to the queue.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762858479958/34d4df3c-d067-45c7-8ff7-8699233e844e.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1762858493279/bb7da855-bd26-4f71-9664-1123caace1dd.png" alt class="image--center mx-auto" /></p>
<hr />
]]></content:encoded></item><item><title><![CDATA[AI Agent as a function tool]]></title><description><![CDATA[Agent Tools
Tools refer to capabilities or external functions that an agent can invoke to accomplish tasks it can’t do on its own (like accessing data, running code, or integrating with APIs). Tools are functions, APIs, or skills that the agent can c...]]></description><link>https://dimitaronai.com/ai-agent-as-a-function-tool</link><guid isPermaLink="true">https://dimitaronai.com/ai-agent-as-a-function-tool</guid><category><![CDATA[aiagents]]></category><category><![CDATA[agentic AI]]></category><category><![CDATA[Azure]]></category><category><![CDATA[Microsoft]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Tue, 04 Nov 2025 10:37:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762207046972/0d8e3227-9732-45c6-ab07-2cd8fe9df7f7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-agent-tools">Agent Tools</h1>
<p>Tools refer to capabilities or external functions that an agent can invoke to accomplish tasks it can’t do on its own (like accessing data, running code, or integrating with APIs). Tools are functions, APIs, or skills that the agent can call when reasoning through a task.</p>
<p>Let’s take a look at how we can use an existing AI agent as a function tool.</p>
<hr />
<h1 id="heading-ai-agent-as-a-tool">AI Agent as a tool</h1>
<p>Initially, what we need to have is an agent defined. In the example below, I use an agent that generates a random base salary.</p>
<pre><code class="lang-csharp">[<span class="hljs-meta">Description(<span class="hljs-meta-string">"Generates a base salary"</span>)</span>]
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">GetBaseSalary</span>(<span class="hljs-params"></span>)</span>
{
    <span class="hljs-keyword">var</span> start = <span class="hljs-number">1000</span>;
    <span class="hljs-keyword">var</span> end = <span class="hljs-number">10000</span>;
    <span class="hljs-keyword">var</span> rand = <span class="hljs-keyword">new</span> Random();
    <span class="hljs-keyword">var</span> salary = rand.Next(start, end);
    Console.WriteLine(<span class="hljs-string">"Generated Base Salary: "</span> + salary);
    <span class="hljs-keyword">return</span> salary;
}

AIAgent salaryAgent = <span class="hljs-keyword">new</span> AzureOpenAIClient(
    endpoint,
    <span class="hljs-keyword">new</span> AzureKeyCredential(apiKey))
     .GetChatClient(deploymentName)
     .CreateAIAgent(
        instructions: <span class="hljs-string">"You are an agent that generates salary."</span>,
        name: <span class="hljs-string">"SalaryAgent"</span>,
        description: <span class="hljs-string">"An agent that generates a base salary."</span>,
        tools: [AIFunctionFactory.Create(GetBaseSalary)]);
</code></pre>
<p>Next, we create a new agent and specify the existing agent as a tool by using <code>AsAIFunction()</code>:</p>
<pre><code class="lang-csharp">AIAgent salaryBonusAgent = <span class="hljs-keyword">new</span> AzureOpenAIClient(
     endpoint,
     <span class="hljs-keyword">new</span> AzureKeyCredential(apiKey))
     .GetChatClient(deploymentName)
     .CreateAIAgent(instructions: <span class="hljs-string">"You are a helpful assistant that always adds $10 bonus amount to the generated base salary."</span>, tools: [salaryAgent.AsAIFunction()]);
</code></pre>
<p>Lastly, let’s run the agent with a simple prompt:</p>
<pre><code class="lang-csharp">Console.WriteLine(<span class="hljs-keyword">await</span> salaryBonusAgent.RunAsync(<span class="hljs-string">"How much money am I getting today?"</span>));
</code></pre>
<p>As we can see, we get the correct output, which added $10 to the base salary.</p>
<blockquote>
<p>Generated Base Salary: 7099</p>
<p>Today, you are getting a total of <strong>$7,109</strong> (which includes a $10 bonus).</p>
</blockquote>
<p>This approach allows us to compose agents and build more complex workflows.</p>
]]></content:encoded></item><item><title><![CDATA[Agent Background Responses - Microsoft Agent Framework]]></title><description><![CDATA[Background processing
Sometimes an agent requires a long time to complete a request. This can happen with complex reasoning tasks, but interruptions may also occur due to network or client issues.
Background processing uses a continuation token, whic...]]></description><link>https://dimitaronai.com/agent-background-responses-microsoft-agent-framework</link><guid isPermaLink="true">https://dimitaronai.com/agent-background-responses-microsoft-agent-framework</guid><category><![CDATA[Azure]]></category><category><![CDATA[aiagents]]></category><category><![CDATA[agentic AI]]></category><category><![CDATA[#agent]]></category><category><![CDATA[AI]]></category><category><![CDATA[Microsoft]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Wed, 29 Oct 2025 09:00:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761730910995/08d4c97b-daed-46ae-8d86-0ca8229898be.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-background-processing">Background processing</h1>
<p>Sometimes an agent requires a long time to complete a request. This can happen with complex reasoning tasks, but interruptions may also occur due to network or client issues.</p>
<p>Background processing uses a continuation token, which can be used to:</p>
<ul>
<li><p>Poll for completion using the non-streaming agent</p>
</li>
<li><p>Resume an interrupted stream with streaming agent</p>
</li>
</ul>
<p>Only when the continuation token is <code>null</code>, we know the operation is complete.</p>
<blockquote>
<p>Important note: currently, only agents that use the OpenAI Responses API support background responses</p>
</blockquote>
<hr />
<h1 id="heading-using-background-responses">Using background responses</h1>
<p>Let’s look at a simple example of how to start using background responses. The first step, of course, is to enable this option:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> agent = <span class="hljs-keyword">new</span> AzureOpenAIClient(endpoint, <span class="hljs-keyword">new</span> AzureKeyCredential(apiKey))
          .GetOpenAIResponseClient(deploymentName)
          .CreateAIAgent();

AgentRunOptions options = <span class="hljs-keyword">new</span>()
{
    AllowBackgroundResponses = <span class="hljs-literal">true</span>
};
</code></pre>
<p>Next, we will start streaming a response and then intentionally break it, so we can simulate interruption.</p>
<pre><code class="lang-csharp">AgentThread thread = agent.GetNewThread();
AgentRunResponseUpdate? latestReceivedUpdate = <span class="hljs-literal">null</span>;
<span class="hljs-keyword">int</span> textCount = <span class="hljs-number">0</span>;

Console.WriteLine(<span class="hljs-string">"===Generating Monthly Sales Performance Report...\n"</span>);

<span class="hljs-keyword">await</span> <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> update <span class="hljs-keyword">in</span> agent.RunStreamingAsync(
    <span class="hljs-string">"Generate a detailed business report analyzing last month's sales performance. "</span>
    + <span class="hljs-string">"Include executive summary, regional breakdown, and recommendations for next month."</span>,
    thread,
    options))
{
    <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">string</span>.IsNullOrEmpty(update.Text))
    {
        Console.Write(update.Text);
        textCount++;

        <span class="hljs-keyword">if</span> (textCount == <span class="hljs-number">10</span>)
        {
            Console.WriteLine(<span class="hljs-string">"\n\n===Pausing report generation...\n"</span>);
            <span class="hljs-keyword">break</span>;
        }
    }

    latestReceivedUpdate = update;
}
</code></pre>
<p>From the code, we can see that the prompt simply asks to generate a complex report, and when the text output count reaches 10, we simulate an interruption.</p>
<p>The output so far in my example is the following:</p>
<blockquote>
<p>\===Generating Monthly Sales Performance Report...</p>
<h1 id="heading-business-report-on-last-months-sales-performance">Business Report on Last Month's Sales Performance</h1>
<p>\===Pausing report generation...</p>
</blockquote>
<p>To resume the streaming, we will use the continuation token we’ve received.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">if</span> (latestReceivedUpdate?.ContinuationToken <span class="hljs-keyword">is</span> not <span class="hljs-literal">null</span>)
{
    options.ContinuationToken = latestReceivedUpdate.ContinuationToken;

    Console.WriteLine(<span class="hljs-string">"===Resuming report generation from state...\n"</span>);

    <span class="hljs-keyword">await</span> <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> update <span class="hljs-keyword">in</span> agent.RunStreamingAsync(thread, options))
    {
        <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">string</span>.IsNullOrEmpty(update.Text))
            Console.Write(update.Text);
    }

    Console.WriteLine(<span class="hljs-string">"\n\nReport generation complete!"</span>);
}
<span class="hljs-keyword">else</span>
{
    Console.WriteLine(<span class="hljs-string">"No continuation token available to resume."</span>);
}
</code></pre>
<p>The final output in my example is the following:</p>
<blockquote>
<p>\===Resuming report generation from state...</p>
<h2 id="heading-executive-summary">Executive Summary</h2>
<p>Last month's sales performance reflects a mixed but overall positive trend, with total revenue reaching $1.5 million, an increase of 10% compared to the previous month. Key drivers of this growth included successful marketing campaigns and increased demand for our flagship products. However, certain regions underperformed, which necessitates a closer examination and tailored strategies moving forward.</p>
<h3 id="heading-key-highlights">Key Highlights:</h3>
<ul>
<li><p><strong>Total Sales:</strong> $1.5 million</p>
</li>
<li><p><strong>Percentage Growth:</strong> 10% MoM</p>
</li>
<li><p><strong>Top Performer:</strong> Product Line A, accounting for 40% of total sales.</p>
</li>
<li><p><strong>Underperforming Regions:</strong> Northeast and Southwest.</p>
</li>
</ul>
<h2 id="heading-regional-breakdown">Regional Breakdown</h2>
<h3 id="heading-1-northeast-region">1. <strong>Northeast Region</strong></h3>
<ul>
<li><p><strong>Sales Performance:</strong> $400,000</p>
</li>
<li><p><strong>Change from Previous Month:</strong> -5%</p>
</li>
<li><p><strong>Analysis:</strong> Decreased sales attributed to increased competition and stock shortages. Target demographics reported dissatisfaction regarding product availability.</p>
</li>
</ul>
<h3 id="heading-2-southeast-region">2. <strong>Southeast Region</strong></h3>
<ul>
<li><p><strong>Sales Performance:</strong> $500,000</p>
</li>
<li><p><strong>Change from Previous Month:</strong> +15%</p>
</li>
<li><p><strong>Analysis:</strong> Successful promotional strategies led to higher engagement and conversion rates, particularly in urban markets.</p>
</li>
</ul>
<h3 id="heading-3-midwest-region">3. <strong>Midwest Region</strong></h3>
<ul>
<li><p><strong>Sales Performance:</strong> $350,000</p>
</li>
<li><p><strong>Change from Previous Month:</strong> +8%</p>
</li>
<li><p><strong>Analysis:</strong> Steady growth sustained by strong community ties and local marketing efforts. Product Line A performed exceptionally well.</p>
</li>
</ul>
<h3 id="heading-4-west-region">4. <strong>West Region</strong></h3>
<ul>
<li><p><strong>Sales Performance:</strong> $250,000</p>
</li>
<li><p><strong>Change from Previous Month:</strong> +5%</p>
</li>
<li><p><strong>Analysis:</strong> Gradual increase attributed to new distribution channels. Potential for further growth exists with enhanced local initiatives.</p>
</li>
</ul>
<h3 id="heading-5-southwest-region">5. <strong>Southwest Region</strong></h3>
<ul>
<li><p><strong>Sales Performance:</strong> $300,000</p>
</li>
<li><p><strong>Change from Previous Month:</strong> -10%</p>
</li>
<li><p><strong>Analysis:</strong> Economic factors and reduced promotional activities contributed to lower sales. Customer feedback indicated a lack of awareness regarding recent offerings.</p>
</li>
</ul>
<h2 id="heading-recommendations-for-next-month">Recommendations for Next Month</h2>
<h3 id="heading-1-northeast-region-1">1. <strong>Northeast Region</strong></h3>
<ul>
<li><p><strong>Action:</strong> Conduct a stock audit to ensure product availability. Implement targeted marketing campaigns highlighting new arrivals to regain lost customers.</p>
</li>
<li><p><strong>Goal:</strong> Increase sales by 10% through outreach and stock replenishment.</p>
</li>
</ul>
<h3 id="heading-2-southeast-region-1">2. <strong>Southeast Region</strong></h3>
<ul>
<li><p><strong>Action:</strong> Continue leveraging successful marketing strategies while exploring partnership opportunities with local influencers to maintain momentum.</p>
</li>
<li><p><strong>Goal:</strong> Target an additional 10% growth by enhancing community engagement.</p>
</li>
</ul>
<h3 id="heading-3-midwest-region-1">3. <strong>Midwest Region</strong></h3>
<ul>
<li><p><strong>Action:</strong> Expand community events and workshops focusing on Product Line A to increase brand loyalty and cross-sell other products.</p>
</li>
<li><p><strong>Goal:</strong> Achieve a 12% increase in sales through direct customer interaction.</p>
</li>
</ul>
<h3 id="heading-4-west-region-1">4. <strong>West Region</strong></h3>
<ul>
<li><p><strong>Action:</strong> Strengthen online marketing efforts and social media presence to attract a broader audience. Collaborate with local businesses for cross-promotion.</p>
</li>
<li><p><strong>Goal:</strong> Aim for a 10% increase in sales via enhanced branding efforts.</p>
</li>
</ul>
<h3 id="heading-5-southwest-region-1">5. <strong>Southwest Region</strong></h3>
<ul>
<li><p><strong>Action:</strong> Reassess marketing strategies and implement a targeted advertisement campaign focusing on product benefits and customer testimonials.</p>
</li>
<li><p><strong>Goal:</strong> Reverse the negative trend by aiming for at least a 15% increase in sales.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The sales performance over the last month demonstrates both opportunities for growth and areas needing attention. By honing in on regional strengths and weaknesses, the company can optimize its strategies to ensure sustained growth. Implementing the recommendations outlined above will be critical for the upcoming month, with particular emphasis on addressing the underperforming regions to maximize overall profitability.</p>
<h3 id="heading-next-steps">Next Steps:</h3>
<ul>
<li><p>Regular monitoring of sales data will be essential to track the effectiveness of implemented strategies.</p>
</li>
<li><p>Prepare a follow-up report to analyze the outcomes of these recommendations at the end of next month.</p>
</li>
</ul>
<p>Report generation complete!</p>
</blockquote>
<p>What we did here was use the continuation token to resume the stream from the interruption point. Keep in mind that in some situations <strong>you will need to store continuation tokens persistently</strong>.</p>
<p>Finally, if you are doing non-streaming, using the token will be similar to:</p>
<pre><code class="lang-csharp">AgentRunOptions options = <span class="hljs-keyword">new</span>()
{
    AllowBackgroundResponses = <span class="hljs-literal">true</span>
};

AgentThread thread = agent.GetNewThread();

AgentRunResponse response = <span class="hljs-keyword">await</span> agent.RunAsync(<span class="hljs-string">"Generate a detailed business report analyzing last month's sales performance. "</span>
    + <span class="hljs-string">"Include executive summary, regional breakdown, and recommendations for next month."</span>, thread, options);

<span class="hljs-keyword">while</span> (response.ContinuationToken <span class="hljs-keyword">is</span> not <span class="hljs-literal">null</span>)
{
    <span class="hljs-keyword">await</span> Task.Delay(TimeSpan.FromSeconds(<span class="hljs-number">5</span>));

    options.ContinuationToken = response.ContinuationToken;
    response = <span class="hljs-keyword">await</span> agent.RunAsync(thread, options);
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Workflows as Agents - Microsoft Agent Framework]]></title><description><![CDATA[Workflows in Agent Framework
A workflow, simply put, represents a predefined set of operations. Workflows are built to manage complex business processes that can include multiple agents and integrations with external systems. Their flow is explicitly...]]></description><link>https://dimitaronai.com/workflows-as-agents-microsoft-agent-framework</link><guid isPermaLink="true">https://dimitaronai.com/workflows-as-agents-microsoft-agent-framework</guid><category><![CDATA[Azure]]></category><category><![CDATA[ai-agent]]></category><category><![CDATA[agentic AI]]></category><category><![CDATA[Microsoft]]></category><category><![CDATA[framework]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Mon, 27 Oct 2025 09:09:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761516728519/dabf4799-dbcc-485c-af63-7ad9b113a594.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-workflows-in-agent-framework">Workflows in Agent Framework</h1>
<p>A workflow, simply put, represents a predefined set of operations. Workflows are built to manage complex business processes that can include multiple agents and integrations with external systems. Their flow is explicitly defined, providing greater control over the execution.</p>
<p>There is also the concept of orchestrations which are pre-built workflow patterns. Currently supported orchestrations are:</p>
<ul>
<li><p>Concurrent</p>
</li>
<li><p>Sequential</p>
</li>
<li><p>Handoff</p>
</li>
<li><p>Magentic</p>
</li>
</ul>
<hr />
<h1 id="heading-workflows-as-agents">Workflows as Agents</h1>
<p>You can easily convert a workflow into an agent. Let’s take a look at a simple example.</p>
<p>I have defined two agents, one is a horror story writer agent, the other is a comedy story writer. The agent definition is the following:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> chatClient = <span class="hljs-keyword">new</span> AzureOpenAIClient(endpoint, <span class="hljs-keyword">new</span> AzureKeyCredential(apiKey))
    .GetChatClient(deploymentName)
    .AsIChatClient();

<span class="hljs-keyword">var</span> horrorInstructions = <span class="hljs-string">@"You are a horror writer. Your task is to write EXACTLY one two-sentence horror story using the theme provided. 
After writing your two sentences, your job is complete. Do not write anything else."</span>;
<span class="hljs-keyword">var</span> horrorAgent = <span class="hljs-keyword">new</span> ChatClientAgent(chatClient, horrorInstructions, <span class="hljs-string">"horror-writer-agent"</span>);

<span class="hljs-keyword">var</span> comedyInstructions = <span class="hljs-string">@"You are a comedy writer. Your task is to write EXACTLY one two-sentence comedy story using the theme provided. 
After writing your two sentences, your job is complete. Do not write anything else."</span>;
<span class="hljs-keyword">var</span> comedyAgent = <span class="hljs-keyword">new</span> ChatClientAgent(chatClient, comedyInstructions, <span class="hljs-string">"comedy-writer-agent"</span>);
</code></pre>
<p>Now, to use these agents in a workflow, I will use the sequential orchestration.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> horrorComedyOrchestration = AgentWorkflowBuilder.BuildSequential([horrorAgent, comedyAgent]);
</code></pre>
<p>Next, we can easily convert this workflow into an agent, and use it as it were an agent:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> horrorComedyAgent = <span class="hljs-keyword">await</span> horrorComedyOrchestration.AsAgentAsync(
    id: <span class="hljs-string">"horror-comedy-agent"</span>,
    name: <span class="hljs-string">"HorrorComedyAgent"</span>
);
</code></pre>
<p>Finally, let’s run the new agent:</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> thread = horrorComedyAgent.GetNewThread();
<span class="hljs-keyword">var</span> input = <span class="hljs-string">"Write a story about a boy and his dog."</span>;

Dictionary&lt;<span class="hljs-keyword">string</span>, List&lt;AgentRunResponseUpdate&gt;&gt; buffer = [];
<span class="hljs-keyword">await</span> <span class="hljs-keyword">foreach</span> (AgentRunResponseUpdate update <span class="hljs-keyword">in</span> horrorComedyAgent.RunStreamingAsync(input, thread))
{
    <span class="hljs-keyword">if</span> (update.MessageId <span class="hljs-keyword">is</span> <span class="hljs-literal">null</span>)
    {
        <span class="hljs-keyword">continue</span>;
    }
    Console.Clear();

    <span class="hljs-keyword">if</span> (!buffer.TryGetValue(update.MessageId, <span class="hljs-keyword">out</span> List&lt;AgentRunResponseUpdate&gt;? <span class="hljs-keyword">value</span>))
    {
        <span class="hljs-keyword">value</span> = [];
        buffer[update.MessageId] = <span class="hljs-keyword">value</span>;
    }
    <span class="hljs-keyword">value</span>.Add(update);

    <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> (messageId, segments) <span class="hljs-keyword">in</span> buffer)
    {
        <span class="hljs-keyword">string</span> combinedText = <span class="hljs-keyword">string</span>.Concat(segments);
        <span class="hljs-keyword">if</span> (!<span class="hljs-keyword">string</span>.IsNullOrEmpty(combinedText))
        {
            Console.WriteLine(<span class="hljs-string">$"<span class="hljs-subst">{segments[<span class="hljs-number">0</span>].AuthorName}</span>: <span class="hljs-subst">{combinedText}</span>"</span>);
            Console.WriteLine();
        }        
    }
}
</code></pre>
<p>The output of the agents is the following:</p>
<blockquote>
<p>horror-writer-agent: As the boy played fetch with his beloved dog in the fading light, he was blissfully unaware that the playful barks were growing fainter, echoing from the depths of an empty, darkened forest. When he turned to call his dog back, he found not his furry friend, but the twisted figure of something wearing its skin, grinning wide with his dog's last memories trapped inside.</p>
<p>comedy-writer-agent: Timmy was excited to finally teach his dog, Rex, how to fetch the newspaper; he figured if a dog could grab a stick, fetching a rolled-up paper should be a piece of cake. After a week of training, Rex proudly returned with a pile of newspapers, but unfortunately, they all belonged to the neighbors-who were now very confused as to why they suddenly had the Daily Bark.</p>
</blockquote>
<p>Notice how easy it was to create an agent based on a defined workflow and use it as part of further processing.</p>
]]></content:encoded></item><item><title><![CDATA[Azure AI Search - Data Deletion and Change Detection Policies]]></title><description><![CDATA[The story so far...
In my previous article, I discussed how we can index data from multiple data sources into a single consolidated search index.
https://dimitaronai.com/azure-ai-search-data-deletion-and-change-detection-policies
 
And this is all gr...]]></description><link>https://dimitaronai.com/azure-ai-search-data-deletion-and-change-detection-policies-1</link><guid isPermaLink="true">https://dimitaronai.com/azure-ai-search-data-deletion-and-change-detection-policies-1</guid><category><![CDATA[Azure]]></category><category><![CDATA[AI]]></category><category><![CDATA[search]]></category><category><![CDATA[azure ai services]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Sat, 25 Oct 2025 09:36:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384955147/42028c08-292e-40b3-95e2-4f3592c3cd33.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-story-so-far"><strong>The story so far...</strong></h2>
<p>In my previous article, I discussed how we can index data from multiple data sources into a single consolidated search index.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://dimitaronai.com/azure-ai-search-data-deletion-and-change-detection-policies">https://dimitaronai.com/azure-ai-search-data-deletion-and-change-detection-policies</a></div>
<p> </p>
<p>And this is all great when we are dealing with new data.</p>
<p>But what happens if some item gets deleted from the database? We might want to remove it from the index without the need to drop and rebuild the index.</p>
<p>That is why I will walk you through on how we can define a data deletion detection policy that implements a soft-deletion strategy. It will determine whether an item should be deleted based on the value of a designated 'soft delete' column.</p>
<p>Additionally, we will also add a data change detection policy which will help us identify changed data items. This way, the indexer will update only them.</p>
<p>For this example, we will create a new index using only the Cosmos database products data.</p>
<hr />
<h2 id="heading-adding-the-soft-delete-column"><strong>Adding the soft delete column</strong></h2>
<p>Go to the Azure portal and open the Cosmos database that you will use to create the index.</p>
<p>In my scenario, it's the 'eshop' database with the two documents that we saw in the previous article.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384220565/4b4aa035-400e-42ff-98b2-fce914f1bd95.png" alt class="image--center mx-auto" /></p>
<p>Next, we will extend our document structure to include a new column for the soft delete. We can name this column 'isDeleted'. The value will either be 'true' or 'false'. Keep in mind that these values are important, and we will see why later.</p>
<blockquote>
<p>Only columns with string, integer, or boolean values are supported. The value used as softDeleteMarkerValue must be a string, even if the corresponding column holds integers or booleans. For example, if the value that appears in your data source is 1, use "1" as the softDeleteMarkerValue.</p>
</blockquote>
<hr />
<h2 id="heading-defining-the-change-detection-policy"><strong>Defining the change detection policy</strong></h2>
<p>For the change detection policy, we need to define it when creating the data source. We can do this by using the following code:</p>
<pre><code class="lang-csharp">cosmosDbDataSource.DataChangeDetectionPolicy = <span class="hljs-keyword">new</span> HighWaterMarkChangeDetectionPolicy(<span class="hljs-string">"_ts"</span>);
</code></pre>
<hr />
<h2 id="heading-defining-the-data-deletion-detection-policy"><strong>Defining the data deletion detection policy</strong></h2>
<p>Creating the data deletion detection policy is simple as well, we just need to set the <strong>SoftDeleteColumnName</strong> and the <strong>SoftDeleteMarkerValue</strong>.</p>
<p>For our solution, the code should be the following:</p>
<pre><code class="lang-csharp">cosmosDbDataSource.DataDeletionDetectionPolicy = <span class="hljs-keyword">new</span> SoftDeleteColumnDeletionDetectionPolicy
            {
                SoftDeleteColumnName = <span class="hljs-string">"isDeleted"</span>,
                SoftDeleteMarkerValue = <span class="hljs-string">"true"</span>
            };
</code></pre>
<p>Additionally, we need to add the property to our documents in the Cosmos database. Currently we will set the value to 'false' for both documents.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384320587/5257d913-1720-4cf7-8bbe-22cc2af1156a.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-creating-the-index-indexer-and-data-source"><strong>Creating the index, indexer and data source</strong></h2>
<p>For the creation of the index, indexer and data source, we will use the code from the previous example and just modify it only with the new lines shown previously.</p>
<p>I won't go through the code here, as I explained it in my previous article I linked above. You can also find the updated code on the GitHub repository:</p>
<p>👉 <a target="_blank" href="https://github.com/DimitarIliev/aisearch-multi-src-index"><strong>DimitarIliev/aisearch-multi-src-index (</strong></a><a target="_blank" href="http://github.com/"><strong>github.com</strong></a><a target="_blank" href="https://github.com/DimitarIliev/aisearch-multi-src-index"><strong>)</strong></a></p>
<hr />
<h2 id="heading-testing-the-data-deletion-detection-policy"><strong>Testing the data deletion detection policy</strong></h2>
<p>After creating the index, indexer and updating the data source, we can observe that in the 'Indexes' tab we have the new index created.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384352652/4611f3f6-bf0c-4fa4-a8a1-89b552347fed.png" alt class="image--center mx-auto" /></p>
<p>In the 'Indexers' tab we got the new indexer created.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384366585/550c4b19-3c97-45e0-9133-42e6d2a080d7.png" alt class="image--center mx-auto" /></p>
<p>And finally, checking our data source, we can observe the updated configuration.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384384630/dbeb3087-f0f8-4a71-a29e-69b8c1cdd9fa.png" alt class="image--center mx-auto" /></p>
<p>Initiating a search to the index returns the two documents we have in the Cosmos database.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384399758/a69dc2c1-63b6-4fa4-9396-7fd7e4549ecd.png" alt class="image--center mx-auto" /></p>
<p>Now, let's update the second document to be deleted. We will do that by changing the value of 'isDeleted' from 'false' to 'true'.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384410644/ee8968cb-7f7d-4027-bb88-5e54febaaed8.png" alt class="image--center mx-auto" /></p>
<p>Now, we can just run the indexer. The expected result should be that 1 document was successfully changed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384435332/7643b265-ff45-4538-b835-871486f16915.png" alt class="image--center mx-auto" /></p>
<p>Now, initiating a search to the index again, we can observe that we receive only the first document, which was not deleted.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384444985/afdd7d61-4174-4d4a-990a-b6c278a8aadf.png" alt class="image--center mx-auto" /></p>
<p>Great! We have successfully removed a document from our search index without needing to drop it and rebuild.</p>
<p>Returning the document to the index is as simple as just setting the 'isDeleted' property back to 'false' and running the indexer.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384455392/5ffefb6c-0106-4310-ae46-fdb1aaa92d90.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384467527/a70f0451-6c12-42fd-a42a-31a7cdea6fb0.png" alt class="image--center mx-auto" /></p>
<hr />
<h2 id="heading-testing-the-change-detection-policy"><strong>Testing the change detection policy</strong></h2>
<p>The final thing we have to test is the change detection policy. Let's update the name property of a product document.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384481042/194f009e-cac2-4b54-b272-6b478cdbcb0c.png" alt class="image--center mx-auto" /></p>
<p>Next, run the indexer. We should see that only 1 document was actually updated.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384489618/4d4bb4fb-f0db-41a1-bf43-8ed0b9bf8888.png" alt class="image--center mx-auto" /></p>
<p>Finally, initiating a search to our index will give us the updated data for the document.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761384496899/88edd39a-5e4f-4545-9914-a0791b6bc268.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Mastering Azure Virtual Desktop - Book Review]]></title><description><![CDATA[Whether you're preparing for the Microsoft Certified: Azure Virtual Desktop Specialty certification or simply have an interest in the topic, "Mastering Azure Virtual Desktop" is an essential resource.  
This book provides an in-depth exploration of A...]]></description><link>https://dimitaronai.com/mastering-azure-virtual-desktop-book-review</link><guid isPermaLink="true">https://dimitaronai.com/mastering-azure-virtual-desktop-book-review</guid><category><![CDATA[Azure]]></category><category><![CDATA[Cloud Computing]]></category><category><![CDATA[Certification]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Fri, 24 Oct 2025 21:47:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761342405409/cae8fbfa-540f-44ba-9292-72ae58af505b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761342401273/f91d7966-e0d4-47d6-b265-f3224f085358.png" alt class="image--center mx-auto" /></p>
<p>Whether you're preparing for the Microsoft Certified: Azure Virtual Desktop Specialty certification or simply have an interest in the topic, "Mastering Azure Virtual Desktop" is an essential resource.  </p>
<p>This book provides an in-depth exploration of Azure Virtual Desktop (AVD) and its key benefits.</p>
<p>It begins with a thorough assessment of your current desktop environment, helping you establish baselines for performance, data management, and user experience.  </p>
<p>The book then delves into the critical requirements for designing user identities and profiles, including considerations around licensing and storage solutions.  </p>
<p>Security is a central theme throughout, with detailed guidance on implementing Azure VNet connectivity, managing both on-premises and internet connectivity, and enforcing network security. The integration of Microsoft Defender for Cloud, with a specific focus on AVD, is also covered in detail.  </p>
<p>Access management is another crucial area addressed in the book, with a dedicated chapter on Azure roles and the specific RBAC configurations for AVD resources. The discussion extends to the creation and management of host pools, a fundamental component of AVD.  </p>
<p>One of the standout sections for me was the practical guide to implementing FSLogix profile containers and Cloud Cache, offering actionable insights for real-world scenarios.  </p>
<p>Business Continuity and Disaster Recovery planning is another key aspect of the book, which examines the five critical components of an Azure Virtual Desktop environment: virtual networks, virtual machines, user identities, user and application data configuration, and application dependencies.  </p>
<p>Finally, the book offers comprehensive coverage on monitoring and managing the performance and health of AVD environments, along with automation strategies for routine management tasks.  </p>
<p>"Mastering Azure Virtual Desktop" is rich with both theoretical insights and practical examples, complemented by end-of-chapter questions to reinforce your understanding.</p>
]]></content:encoded></item><item><title><![CDATA[Microsoft Copilot in Azure - Book Review]]></title><description><![CDATA[Just finished reading "Microsoft Copilot in Azure" by Steve Miles and Dave Rendon, and I'm impressed by how comprehensively it covers AI-assisted cloud management.
This book excels at demystifying how Copilot in Azure transforms cloud operations thro...]]></description><link>https://dimitaronai.com/microsoft-copilot-in-azure-book-review</link><guid isPermaLink="true">https://dimitaronai.com/microsoft-copilot-in-azure-book-review</guid><category><![CDATA[Azure]]></category><category><![CDATA[copilot]]></category><category><![CDATA[AI]]></category><category><![CDATA[generative ai]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Fri, 24 Oct 2025 12:53:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761310393816/6f6a54c6-551b-4f42-9641-ec4cf3032006.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761310353764/2599c1dd-cb55-4cb3-b176-64488a9a4081.jpeg" alt class="image--center mx-auto" /></p>
<p>Just finished reading "Microsoft Copilot in Azure" by Steve Miles and Dave Rendon, and I'm impressed by how comprehensively it covers AI-assisted cloud management.</p>
<p>This book excels at demystifying how Copilot in Azure transforms cloud operations through natural language interactions.</p>
<p>The authors do an excellent job explaining the three-layer architecture (frontend, orchestration, and AI infrastructure) and how it all works within your existing security context - a critical point for enterprise adoption.</p>
<h2 id="heading-what-i-found-most-valuable">What I found most valuable</h2>
<p>🔹 Practical approach: Each chapter tackles real scenarios - from deploying VMs and AKS clusters to managing databases and optimizing costs</p>
<p>🔹 Security-first mindset: Strong emphasis on RBAC, compliance frameworks (PCI DSS, GDPR, HIPAA), and how Copilot respects existing access controls</p>
<p>🔹 End-to-end coverage: Goes beyond basics to include AI Shell integration, predictive scaling, cost management, and security posture improvement</p>
<p>The book aligns Copilot capabilities with Azure's Well-Architected Framework pillars (reliability, security, cost optimization, operational excellence, and performance efficiency), making it easy to see where AI assistance adds the most value.</p>
<h2 id="heading-bottom-line">Bottom line</h2>
<p>If you're managing Azure infrastructure and want to understand how AI can make you more efficient while maintaining security and governance, this is a solid resource. The focus on RAG, context-aware responses, and multimodal outputs shows where cloud management is heading.</p>
]]></content:encoded></item><item><title><![CDATA[Microsoft Learn Interview]]></title><description><![CDATA[About the Interview
Microsoft Learn reached out to discuss my journey with Applied Skills credentials and how they are shaping the way professionals validate their hands-on expertise in cloud technolo]]></description><link>https://dimitaronai.com/microsoft-learn-interview</link><guid isPermaLink="true">https://dimitaronai.com/microsoft-learn-interview</guid><category><![CDATA[interview]]></category><category><![CDATA[Azure]]></category><category><![CDATA[Microsoft]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Thu, 23 Oct 2025 18:13:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761243149040/f0e07851-a25d-4491-a2d4-23d9492f985f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761243074482/77749e81-21dc-4f76-86d9-68b139d0975f.png" alt="" style="display:block;margin:0 auto" />

<h2><strong>About the Interview</strong></h2>
<p>Microsoft Learn reached out to discuss my journey with Applied Skills credentials and how they are shaping the way professionals validate their hands-on expertise in cloud technologies and AI.</p>
<h3><strong>Key Topics Covered</strong></h3>
<ul>
<li><p>My experience with Microsoft certifications and Applied Skills</p>
</li>
<li><p>The value of hands-on, scenario-based learning</p>
</li>
<li><p>How Applied Skills credentials differ from traditional certifications</p>
</li>
<li><p>Practical applications of Azure and AI skills in real-world projects</p>
</li>
<li><p>Advice for professionals looking to upskill in cloud and AI</p>
</li>
</ul>
<h2><strong>Why Applied Skills Matter</strong></h2>
<p>Applied Skills credentials represent a shift toward validating practical, job-ready skills through hands-on assessments. Unlike traditional exams, these credentials focus on your ability to solve real-world scenarios, making them incredibly relevant for today's cloud and AI landscape.</p>
<h2><strong>Watch/Read the Interview</strong></h2>
<p><a class="embed-card" href="https://www.linkedin.com/embed/feed/update/urn:li:ugcPost:7328841908934336514?collapsed=1">https://www.linkedin.com/embed/feed/update/urn:li:ugcPost:7328841908934336514?collapsed=1</a></p>

<p>Check it out <a href="https://www.linkedin.com/posts/microsoftlearn_learn-by-doing-with-microsoft-applied-skills-activity-7328841909819334656-cFA-?utm_source=share&amp;utm_medium=member_android&amp;rcm=ACoAADQKFTkB1C5O6yGsHH8kD4gOkZrysb8V4z8"><strong>here</strong></a>.</p>
]]></content:encoded></item><item><title><![CDATA[AI Agent Middleware]]></title><description><![CDATA[Agent Middleware
Agent middleware can be used to handle cross-cutting concerns like logging, security, error handling, and transforming results.
There are three main types of middleware that can be defined:

Agent Run middleware – intercepts all agen...]]></description><link>https://dimitaronai.com/ai-agent-middleware</link><guid isPermaLink="true">https://dimitaronai.com/ai-agent-middleware</guid><category><![CDATA[Azure]]></category><category><![CDATA[agentic AI]]></category><category><![CDATA[AI]]></category><category><![CDATA[agents]]></category><category><![CDATA[Microsoft]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Thu, 23 Oct 2025 11:10:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761217723900/6171946e-e9bd-4fd8-b128-2b2e41dc7f13.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-agent-middleware"><strong>Agent Middleware</strong></h1>
<p>Agent middleware can be used to handle cross-cutting concerns like logging, security, error handling, and transforming results.</p>
<p>There are three main types of middleware that can be defined:</p>
<ul>
<li><p><strong>Agent Run middleware</strong> – intercepts all agent executions</p>
</li>
<li><p><strong>Function calling middleware</strong> – intercepts all function calls made by the agent</p>
</li>
<li><p><strong>IChatClient middleware</strong> – intercepts calls to an <code>IChatClient</code> implementation</p>
</li>
</ul>
<p>Let’s explore an example by creating and using a simple agent run middleware.</p>
<hr />
<h1 id="heading-defining-the-author-agent"><strong>Defining the author agent</strong></h1>
<p>To begin, let’s define a simple author agent that will be responsible for writing horror stories.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> azureOpenAIClient = <span class="hljs-keyword">new</span> AzureOpenAIClient(endpoint, <span class="hljs-keyword">new</span> AzureKeyCredential(apiKey))
    .GetChatClient(deploymentName);

<span class="hljs-keyword">var</span> authorAgent = azureOpenAIClient.AsIChatClient()
    .AsBuilder()
    .BuildAIAgent(
        instructions: <span class="hljs-string">"You are an author that writes horror stories."</span>,
        name: <span class="hljs-string">"Author"</span>);
</code></pre>
<p>Next, let’s add our middleware, which will format the agent’s response with a stylish horror-themed header and footer.</p>
<pre><code class="lang-csharp"><span class="hljs-function"><span class="hljs-keyword">async</span> Task&lt;AgentRunResponse&gt; <span class="hljs-title">HorrorAtmosphereMiddleware</span>(<span class="hljs-params">
        IEnumerable&lt;ChatMessage&gt; messages, AgentThread? thread, AgentRunOptions? options, AIAgent innerAgent, CancellationToken cancellationToken</span>)</span>
{
    Console.Write(<span class="hljs-string">"The author retreats into the shadows"</span>);
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">3</span>; i++)
    {
        <span class="hljs-keyword">await</span> Task.Delay(<span class="hljs-number">800</span>, cancellationToken).ConfigureAwait(<span class="hljs-literal">false</span>);
        Console.Write(<span class="hljs-string">"."</span>);
    }
    Console.WriteLine(<span class="hljs-string">"\n"</span>);

    <span class="hljs-keyword">var</span> response = <span class="hljs-keyword">await</span> innerAgent.RunAsync(messages, thread, options, cancellationToken).ConfigureAwait(<span class="hljs-literal">false</span>);

    <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> message <span class="hljs-keyword">in</span> response.Messages)
    {
        <span class="hljs-keyword">if</span> (message.Contents != <span class="hljs-literal">null</span>)
        {
            <span class="hljs-keyword">foreach</span> (<span class="hljs-keyword">var</span> content <span class="hljs-keyword">in</span> message.Contents)
            {
                <span class="hljs-keyword">if</span> (content <span class="hljs-keyword">is</span> TextContent textContent)
                {
                    textContent.Text = <span class="hljs-string">$"================================================\n"</span> +
                                     <span class="hljs-string">$"           HORROR STORY ARCHIVE\n"</span> +
                                     <span class="hljs-string">$"================================================\n\n"</span> +
                                     <span class="hljs-string">$"<span class="hljs-subst">{textContent.Text}</span>\n\n"</span> +
                                     <span class="hljs-string">$"================================================\n"</span> +
                                     <span class="hljs-string">$"Author: <span class="hljs-subst">{innerAgent.Name}</span>\n"</span> +
                                     <span class="hljs-string">$"Date: <span class="hljs-subst">{DateTime.Now:MMMM dd, yyyy <span class="hljs-string">'at'</span> HH:mm}</span>\n"</span> +
                                     <span class="hljs-string">$"================================================"</span>;
                }
            }
        }
    }

    <span class="hljs-keyword">return</span> response;
}
</code></pre>
<p>Agent run and function calling middleware types can be registered on an agent by using the agent builder along with an existing agent instance.</p>
<pre><code class="lang-csharp"> <span class="hljs-keyword">var</span> updatedAgent = authorAgent
     .AsBuilder()
         .Use(HorrorAtmosphereMiddleware, <span class="hljs-literal">null</span>)
     .Build();

 <span class="hljs-keyword">var</span> authorResponse = <span class="hljs-keyword">await</span> updatedAgent.RunAsync(<span class="hljs-string">"Tell me a short horror story about vampires."</span>);
 Console.WriteLine(<span class="hljs-string">$"<span class="hljs-subst">{authorResponse}</span>"</span>);
</code></pre>
<p>Executing the agent yields the following output:</p>
<blockquote>
<p>The author retreats into the shadows...</p>
<h1 id="heading-horror-story-archive">\================================================ HORROR STORY ARCHIVE</h1>
<p>The moon hung high over the abandoned village of Eldersblood, casting eerie shadows that danced among the crumbling stone houses. Legend had it, the village had been cursed by a dark Hemlock witch centuries ago, drawing the attention of a clan of vampires who sought refuge from the sun's scalding rays. Though no one had set foot in Eldersblood for decades, whispers of its sinister past lingered in the air like a promise of dread.</p>
<p>One night, a curious traveler named Elara, drawn by the thrill of the unknown, ventured into the ghostly village. With each cautious step, the wooden boards beneath her feet creaked and moaned as if warning her of an impending doom. The chilling wind wrapped around her like a cold finger, and the distant howl of wolves sent shivers racing down her spine.</p>
<p>As Elara explored, she stumbled upon an ancient tavern, its door slightly ajar, as if inviting her in. Inside, the air thickened with an unsettling chill, and cobwebs adorned the corners like tattered curtains. The flicker of her lantern revealed a table set for a feast, the plates gleaming, untouched, as shadows began to stretch across the room.</p>
<p>Suddenly, the door slammed shut, plunging her into darkness. Panic coursed through her veins as she fumbled for the handle, but it was locked tight. A raspy voice echoed from the shadows, "Welcome, dear traveler. You've found your way to our humble domain."</p>
<p>A figure emerged from the darkness, cloaked in a tattered black robe. Its face was pale as death, eyes gleaming like two obsidian stones. Behind it, more figures began to materialize, their features hidden, but their hunger palpable.</p>
<p>"Stay for dinner," the figure hissed, revealing elongated fangs glistening under the dim flicker of light. "We rarely have guests in Eldersblood."</p>
<p>Elara's heart raced. She backed away, her instincts screaming for her to flee, but the ground seemed to grow roots beneath her feet. The vampires circled closer, their predatory gazes locking onto her.</p>
<p>With a sudden rush of adrenaline, Elara lunged for the window, clawing at the grime-covered glass, but it was too late. The air crackled with dark energy as the vampires closed in, filling the air with a deep, rumbling laughter.</p>
<p>"Dinner is served!" they declared in unison, their fangs bared, eyes sparkling with wicked delight.</p>
<p>The moon outside bore witness as Elara was swallowed by shadows, her screams swallowed whole by the night. Eldersblood remained silent, the village locked in its cursed slumber, waiting patiently for the next curious soul to wander into its grasp.</p>
<h1 id="heading-author-author-date-october-23-2025-at-1305">\================================================ Author: Author Date: October 23, 2025 at 13:05</h1>
</blockquote>
<p>I hope you enjoyed this chilling tale - and that it didn’t send too many shivers down your spine. More importantly, I hope you learned something along the way. Until the next story… sleep well, if you can.</p>
]]></content:encoded></item><item><title><![CDATA[Azure Costs Out of Control? Here’s How to Take Back Control]]></title><description><![CDATA[Working with Azure as a Cloud Provider
Microsoft Azure is a powerful cloud platform that provides scalability, security, and a vast array of services. However, navigating its complexities can be challenging, and many organizations unknowingly accumul...]]></description><link>https://dimitaronai.com/azure-costs-out-of-control-heres-how-to-take-back-control</link><guid isPermaLink="true">https://dimitaronai.com/azure-costs-out-of-control-heres-how-to-take-back-control</guid><category><![CDATA[Azure]]></category><category><![CDATA[Cloud Computing]]></category><category><![CDATA[cost-optimisation]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Wed, 22 Oct 2025 19:55:59 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761162916925/747c739a-30a0-4f0a-80b5-8944c53dae0f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-working-with-azure-as-a-cloud-provider"><strong>Working with Azure as a Cloud Provider</strong></h2>
<p>Microsoft Azure is a powerful cloud platform that provides scalability, security, and a vast array of services. However, navigating its complexities can be challenging, and many organizations unknowingly accumulate unnecessary costs.</p>
<p>This often occurs because tech leads and architects lack a deep understanding of the applications they are building. Without this clarity, resource planning and cost management suffer, leading to inefficient spending.</p>
<p>In this article, I will highlight some of the most common pitfalls I’ve observed when working with Azure and how to avoid them.</p>
<hr />
<h2 id="heading-misunderstood-applications"><strong>Misunderstood Applications</strong></h2>
<p>One of the biggest issues I’ve encountered is a lack of Azure expertise among tech leads and architects. Without a solid grasp of how to design and build applications in Azure, teams often choose suboptimal or even incorrect services. This results in paying for resources that are either unnecessary or misconfigured.</p>
<p>Additionally, understanding when and how an application is used is crucial. Some applications require 24/7 availability, while others may only be active during specific hours. By analyzing usage patterns, organizations can optimize costs by resizing resources, implementing auto-scaling, or even shutting down certain services during off-peak hours.</p>
<p>Without a comprehensive understanding of an application’s behavior and workload patterns, selecting the right services and pricing models becomes a challenge - leading to our next common pitfall.</p>
<hr />
<h2 id="heading-choosing-the-wrong-skus"><strong>Choosing the Wrong SKUs</strong></h2>
<p>Selecting the wrong service tier (SKU) often results in paying for capacity or features that exceed actual needs. To prevent this, organizations should align SKUs with real application requirements.</p>
<p>Understanding the optimal SKU for a service comes from continuous monitoring and analysis of usage patterns. By assessing how resources are utilized, teams can determine whether to scale up or down, ensuring they only pay for what they actually need.</p>
<hr />
<h2 id="heading-misunderstood-pricing-models"><strong>Misunderstood pricing models</strong></h2>
<p>Azure services follow different pricing structures, and without a clear understanding of these models, it’s easy to overspend.</p>
<p>Choosing the right pricing model requires first understanding what options are available for the services in use. For instance, organizations can opt for a <strong>Pay-as-you-go</strong> model, an <strong>Azure Savings Plan for Compute</strong>, or <strong>Reserved Instances</strong>, among others.</p>
<p>Selecting the best pricing model should be a strategic decision based on application demand, expected usage, and long-term cost efficiency.</p>
<hr />
<h2 id="heading-forgotten-resources"><strong>Forgotten resources</strong></h2>
<p>One of the most common yet overlooked cost drivers in Azure is <strong>unused and forgotten resources</strong>. Throughout my career, I’ve come across countless instances where resources were left running, silently draining budgets.</p>
<p>To mitigate this, organizations should conduct <strong>regular audits</strong> of their Azure environment, identifying and decommissioning resources that are no longer in use. Implementing automated policies for resource cleanup can also help prevent unnecessary costs.</p>
<hr />
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Optimizing Azure costs is not just about reducing expenses—it’s about making informed decisions that align with your application’s actual needs. By understanding your workloads, selecting the right services and SKUs, leveraging the correct pricing models, and maintaining resource hygiene, organizations can maximize the efficiency of their Azure investment.</p>
<p>With a proactive approach to cost management, businesses can harness the full power of Azure without falling into common financial pitfalls.</p>
]]></content:encoded></item><item><title><![CDATA[Select the best LLM to respond to a given prompt in real time!]]></title><description><![CDATA[What is Model Router in Azure AI Foundry?
Model router for Azure AI Foundry is a deployable AI chat model that is trained to select the best large language model to respond to a given prompt in real time. By evaluating factors like query complexity, ...]]></description><link>https://dimitaronai.com/ai-model-router</link><guid isPermaLink="true">https://dimitaronai.com/ai-model-router</guid><category><![CDATA[Azure]]></category><category><![CDATA[AI]]></category><category><![CDATA[generative ai]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Wed, 22 Oct 2025 10:58:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761152182789/b9987405-7bc3-437e-b76e-b610b5a66a62.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-what-is-model-router-in-azure-ai-foundry"><strong>What is Model Router in Azure AI Foundry?</strong></h2>
<p>Model router for Azure AI Foundry is a deployable AI chat model that is trained to select the best large language model to respond to a given prompt in real time. By evaluating factors like query complexity, cost, and performance, it intelligently routes requests to the most suitable model. With that, it delivers high performance while saving on compute costs where possible, all packaged as a single model deployment.</p>
<p>Smaller and cheaper models are used when they're sufficient for the task, but larger and more expensive models are available for more complex tasks.</p>
<hr />
<h2 id="heading-deploying-the-model-router"><strong>Deploying the Model Router</strong></h2>
<p>To use the model router, we need to initially deploy it. To do that go to Azure AI Foundry and open the model deployments. Choose the '+ Deploy model' option.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761130488457/1346b52b-3ddb-45e2-b6b4-2f47c4adf379.png" alt class="image--center mx-auto" /></p>
<p>Next, select the model-router model and click on 'Confirm'.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761130509326/bb4b7cbd-df08-413f-b83b-3c2105d35bf1.png" alt class="image--center mx-auto" /></p>
<p>Finally, specify the deployment name and click on 'Deploy'.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761130530813/81b4bee3-5d8b-4342-9cf7-356fb783f9df.png" alt class="image--center mx-auto" /></p>
<p>After the deployment is completed, you can use the model router in your applications.</p>
<hr />
<h2 id="heading-using-the-model-router"><strong>Using the Model Router</strong></h2>
<p>I have created a simple Console application to demonstrate how to use the model router.</p>
<p>We can use model router in the same way we'd use other OpenAI chat models. Let's set the deployment name parameter to the name of our model router deployment.</p>
<pre><code class="lang-csharp">builder.Services.AddAzureOpenAIChatClient(
   deploymentName: <span class="hljs-string">"model-router-itt"</span>,
   endpoint: Environment.GetEnvironmentVariable(<span class="hljs-string">"AZURE_OPENAI_ENDPOINT"</span>)!,
   apiKey: Environment.GetEnvironmentVariable(<span class="hljs-string">"OPENAI_API_KEY"</span>)!);
</code></pre>
<p>Then let's define two prompts and see what models our router will choose for the responses.</p>
<pre><code class="lang-csharp"><span class="hljs-keyword">var</span> prompt = <span class="hljs-string">"What is the capital city of France?"</span>;
<span class="hljs-keyword">var</span> result = <span class="hljs-keyword">await</span> kernel.InvokePromptAsync(prompt, <span class="hljs-keyword">new</span>(executionSettings)).ConfigureAwait(<span class="hljs-literal">false</span>);
Console.WriteLine(<span class="hljs-string">$"\n\n<span class="hljs-subst">{prompt}</span>\n<span class="hljs-subst">{result}</span>"</span>);

prompt = <span class="hljs-string">"Write a detailed blog post comparing the benefits and trade-offs of using vector search versus keyword-based search in enterprise AI applications, including practical Azure AI Search configuration examples."</span>;
result = <span class="hljs-keyword">await</span> kernel.InvokePromptAsync(prompt, <span class="hljs-keyword">new</span>(executionSettings)).ConfigureAwait(<span class="hljs-literal">false</span>);
Console.WriteLine(<span class="hljs-string">$"\n\n<span class="hljs-subst">{prompt}</span>\n<span class="hljs-subst">{result}</span>"</span>);
</code></pre>
<p>The result for the first prompt is:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761130593180/8d831ee9-0e84-497f-bac6-8c654c7f941b.png" alt class="image--center mx-auto" /></p>
<p>We can see that the chosen model here was <strong>GPT-4.1-nano-</strong>2025-04-14. This seems reasonable as the prompt was very simple.</p>
<p>Let's see the result for the second prompt:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761130615987/98728f89-eec0-4bde-a714-87ea1c76e0b5.png" alt class="image--center mx-auto" /></p>
<p>Because the second prompt was more complex than the first, we can see that the router chose the <strong>o4-mini-</strong>2025-04-16 model.</p>
<p>And that's it. This is how simple it is to use the model router in your applications.</p>
<p>One important limitation to note is that the context window limit is the limit of the smallest underlying model. Other underlying models are compatible with larger context windows, which means an API call with a larger context will succeed only if the prompt happens to be routed to the right model, otherwise the call will fail. To shorten the context window, you can do one of the following:</p>
<ul>
<li><p>Summarize the prompt before passing it to the model</p>
</li>
<li><p>Truncate the prompt into more relevant parts</p>
</li>
<li><p>Use document embeddings and have the chat model retrieve relevant sections</p>
</li>
</ul>
]]></content:encoded></item></channel></rss>