<?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>Mon, 05 Oct 2026 21:26:55 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[System Design and Behavioral Intelligence - Book Review]]></title><description><![CDATA[Most system design books stop at diagrams and trade-offs. Most behavioral interview guides stop at STAR stories. This one argues — convincingly — that the two are inseparable, because interviewers (an]]></description><link>https://dimitaronai.com/system-design-and-behavioral-intelligence-book-review</link><guid isPermaLink="true">https://dimitaronai.com/system-design-and-behavioral-intelligence-book-review</guid><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Mon, 28 Sep 2026 08:51:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/053e3011-5bf5-4414-8db1-d4279334889e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most system design books stop at diagrams and trade-offs. Most behavioral interview guides stop at STAR stories. This one argues — convincingly — that the two are inseparable, because interviewers (and careers) evaluate both your architecture <em>and</em> how you reason and communicate your way to it.</p>
<img src="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/f93d95d9-3bdb-4fd2-92e9-9dbfac087089.png" alt="" style="display:block;margin:0 auto" />

<h2><strong>Part 1: What You Can Build</strong></h2>
<p>The book opens by framing system design not as memorized solutions but as a trail of decisions and trade-offs, built on domain knowledge, design skill, and behavioral intelligence — the three things it argues AI hasn't displaced. From there it methodically builds a shared vocabulary: network models and protocols, DNS resolution, symmetric vs. asymmetric encryption, hashing vs. checksums vs. tokenization, and the throughput/latency/bandwidth distinctions interviewers love to probe.</p>
<p>It then moves into the algorithms and patterns that make distributed systems actually work — Snowflake IDs for globally unique ordering, consistent hashing for even data distribution, Raft for consensus, Bloom filters for cheap membership testing, and resilience patterns like circuit breakers, Merkle trees, and scatter-gather.</p>
<p>The building-blocks chapter is the densest: stateful vs. stateless services, sticky sessions, load balancing, sync vs. async communication, SQL vs. NoSQL decision-making, API gateway design, and a clear breakdown of when to reach for WebSockets (full-duplex, chat apps), SSE (one-way server push), or webhooks (server-to-server events).</p>
<p>A full chapter on security and observability covers encryption at rest/in transit, JWT-based stateless auth, OAuth2/OIDC, RBAC vs. ABAC, rate limiting vs. throttling, and the logs/metrics/traces triad — plus a pointed reminder never to log PII.</p>
<p>Where the book earns its keep is the case study chapters: a Slack-like messaging system (with per-channel append-only logs for ordering), a distributed key-value datastore built around write-ahead logs and partitioning, a millisecond-latency real-time bidding platform, an Instagram-like system covering fan-out and presigned URLs for media uploads, and — the standout — an agentic AI system for investment research, complete with tool-access guardrails, a forward-proxy allow-list, golden-dataset regression testing, and negative/adversarial testing for prompt injection.</p>
<hr />
<h2><strong>Part 2: How You Behave While Building It</strong></h2>
<p>The second half redefines behavioral intelligence as a learnable, observable set of choices — distinct from IQ and from emotional intelligence — that predicts performance and gets tested deliberately in interviews. It walks through real workplace friction (conflict resolution via RACI, risk mitigation, stakeholder alignment via Mendelow's Matrix and the Salience Model, DORA metrics for engineering performance) before turning to interview preparation itself: building a story bank, calibrating stories to seniority using STAR, STAR+R, CAR, CARL, SOAR, and PAR, and understanding that a JD is a signal, not a checklist.</p>
<p>The closing chapters pull back the curtain on how Big Tech interviews are actually scored — core values as the real evaluation criteria, live scorecards, level calibration — and then look past the offer: negotiating, onboarding in the first 90 days, and eventually becoming the interviewer yourself.</p>
<hr />
<h2><strong>Takeaway</strong></h2>
<p>The book's real argument isn't "learn system design, then learn to interview." It's that clear technical thinking and clear communication are the same underlying discipline — and that discipline, not any single technology, is what actually gets evaluated.</p>
]]></content:encoded></item><item><title><![CDATA[The Generative AI Career Masterplan - Book Review]]></title><description><![CDATA[The Generative AI Career Masterplan: A Systems-Thinking Guide to an AI-Driven Career
There's no shortage of books promising to help you "future-proof" your career against AI. Most either drown you in ]]></description><link>https://dimitaronai.com/the-generative-ai-career-masterplan-book-review</link><guid isPermaLink="true">https://dimitaronai.com/the-generative-ai-career-masterplan-book-review</guid><category><![CDATA[AI]]></category><category><![CDATA[generative ai]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Fri, 14 Aug 2026 07:13:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/456bb895-1c6e-432f-a418-76e06764a0d4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/5adf889a-8ed2-4ddf-8897-793be5870883.jpg" alt="" style="display:block;margin:0 auto" />

<h3>The Generative AI Career Masterplan: A Systems-Thinking Guide to an AI-Driven Career</h3>
<p>There's no shortage of books promising to help you "future-proof" your career against AI. Most either drown you in hype or oversimplify into a list of prompts to memorize. <em>The Generative AI Career Masterplan</em> takes a different, more durable approach: it treats a GenAI career the way you'd treat building a reliable AI system — through clear objectives, disciplined iteration, and evidence that things actually work.</p>
<p><strong>From tools to agentic enterprises</strong></p>
<p>The book opens by framing generative AI as a genuinely transformative force, not just a productivity add-on. It traces the shift from AI as a static tool to AI as an autonomous agent capable of planning, reasoning, and executing multi-step work — and the organizational consequence of that shift: the "agentic enterprise," where humans move from supervising every step to setting goals and auditing outcomes from above. The discussion of AI agents as emerging "corporate citizens," complete with identities, permissions, and accountability, is a nice preview of governance conversations that are only going to get louder.</p>
<p><strong>Concepts without the jargon overload</strong></p>
<p>The middle chapters do solid work demystifying the technical core — generative versus discriminative models, transformers and attention, diffusion models, in-context learning — without turning into a textbook. It's practical enough for a business reader but has real technical substance for practitioners, covering architecture evolution (MHA to GQA, Mixture of Experts, hybrid and sliding-window attention), RAG and multimodal RAG, and the increasingly important distinction between <em>workflows</em> (code-orchestrated) and true <em>agents</em> (model-driven decision loops).</p>
<p><strong>A genuinely useful career map</strong></p>
<p>Where the book earns its keep is Chapter 3's role taxonomy: Builders, Shapers, Guardians, Enablers, and Adopters. Rather than chasing whatever job title is trending, the book argues for anchoring your trajectory in systems and evidence — pick a role family, build something real, evaluate it rigorously, and govern it responsibly. That advice holds up regardless of which model or framework is popular next year, which is exactly the point.</p>
<p><strong>Tools, hard skills, and the agentic frontier</strong></p>
<p>Later chapters survey the practical toolkit (LangGraph, LlamaIndex, vLLM, Hugging Face, fine-tuning frameworks like Axolotl and Unsloth) and the hard skills that underpin it all — Python, SQL, evaluation methodology. Chapter 7's treatment of agentic AI is one of the strongest sections: the sense–reason–plan–coordinate–act–reflect loop, two-tiered agent memory, harness engineering, and security concerns like indirect prompt injection are all explained clearly and tied back to what it means for human oversight (the "Human-Above-the-Loop" model in particular is a useful mental model).</p>
<p><strong>The human layer</strong></p>
<p>What keeps this from reading like a pure engineering manual is the consistent return to human factors — personal branding as "distribution and proof of work" rather than vanity, ATS-aware but human-honest job searching, STAR-based interview prep, and a closing chapter that's refreshingly unglamorous: show up, be reliable, find mentors, protect your capacity to last. Career compounding, the book argues, isn't about chasing virality — it's about integrity and consistency over years.</p>
<p><strong>Bottom line:</strong> A well-organized, evidence-oriented guide that treats career-building with the same rigor the book recommends for AI systems themselves — measure, iterate, govern, repeat.</p>
]]></content:encoded></item><item><title><![CDATA[Classify and route your data using Content Understanding]]></title><description><![CDATA[Classification and splitting
The analyzer concept has been expanded to include contentCategories and enableSegment, which let you classify and split the input data processed by your application. This ]]></description><link>https://dimitaronai.com/classify-and-route-your-data-using-content-understanding</link><guid isPermaLink="true">https://dimitaronai.com/classify-and-route-your-data-using-content-understanding</guid><category><![CDATA[Azure]]></category><category><![CDATA[Document Processing]]></category><category><![CDATA[search]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Sun, 19 Jul 2026 08:26:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/fbe2e3aa-3afb-4e7f-bb1d-bfe9149cae5b.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>Classification and splitting</h2>
<p>The <code>analyzer</code> concept has been expanded to include <code>contentCategories</code> and <code>enableSegment</code>, which let you classify and split the input data processed by your application. This analyzer capability can classify an entire input file at once, and it can also detect multiple documents—or multiple copies of the same document—contained within a single input file.</p>
<p>Beyond this, Content Understanding lets you build custom classification workflows that direct your content to the appropriate analyzer. Through routing, you can pass several data streams through one shared pipeline while making sure each is handled by the analyzer best suited to its type.</p>
<hr />
<h2>Architecture Overview</h2>
<img src="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/407ca730-99f7-4ed8-aa31-9f55606398b5.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Input file</strong> enters the pipeline as the raw document you want to process.</p>
<p><strong>Classifier analyzer</strong> sits at the center. This is the analyzer configured with <code>contentCategories</code> and <code>enableSegment</code> — it looks at the input file and both classifies it and splits it into segments.</p>
<p>That classifier fans out into three example categories, each representing a type of content the classifier can detect:</p>
<ul>
<li><p><strong>Category A</strong> and <strong>Category B</strong> are each routed to their own <strong>custom analyzer</strong>, which extracts the relevant fields from that piece of content and produces <strong>structured output</strong>.</p>
</li>
<li><p><strong>Category C</strong> (e.g. a cover page) has no analyzer linked to it. Not every category needs one — if a segment just needs to be identified and removed rather than further processed, you can leave it unrouted and you can simply discard it from the pipeline.</p>
</li>
</ul>
<hr />
<h2>Creating a <strong>base classifier</strong></h2>
<p>To create the base classifier, first you need to define the custom categories with names and descriptions. These definitions will be used by the analyzer to classify the input file.</p>
<p>Here is an example <code>definition of a list of categories</code>. For simplicity, I define only one category in the list. To <code>improve classification and splitting quality</code>, use a good category name and description so that the model can understand the categories with some context.</p>
<pre><code class="language-csharp">public sealed record ContentUnderstandingCategory(string Name, string Description);

public static readonly IReadOnlyList&lt;ContentUnderstandingCategory&gt; All =
[
    new("Walmart_Invoice",
        "Invoice containing Walmart as the title on the top right of each page.") 
];
</code></pre>
<p>Then, create the <code>classification analyzer</code> for your input file:</p>
<pre><code class="language-csharp"> var config = new ContentAnalyzerConfig
 {
     ShouldReturnDetails = false,
     EnableOcr = true,
     EnableLayout = true,
     EnableFormula = false,
     EnableFigureDescription = false,
     EnableFigureAnalysis = false,
     ChartFormat = ChartFormat.ChartJs,
     TableFormat = TableFormat.Html,
     AnnotationFormat = AnnotationFormat.Markdown,
     EnableSegment = true,
     SegmentPerPage = false,
     ShouldOmitContent = false
 };

 foreach (var category in ContentUnderstandingCategories.All)
 {
     var definition = new ContentCategoryDefinition { Description = category.Description };
 
// If you have more than one category, each will have a different analyzer linked
definition.AnalyzerId = _options.ExtractAnalyzerId;

config.ContentCategories.Add(category.Name, definition);
 }

 var analyzer = new ContentAnalyzer
 {
     BaseAnalyzerId = "prebuilt-document",
     ProcessingLocation = ProcessingLocation.Geography,
     Config = config,
 };

 analyzer.Models["completion"] = _options.CompletionModelName;

 await CreateOrReplaceAsync(_options.ClassifierAnalyzerId, analyzer, ct).ConfigureAwait(false);
</code></pre>
<p>To route to a custom analyzer, we need to create one. Below is the code for the analyzer that will extract information from the <code>Walmart invoice</code>:</p>
<pre><code class="language-csharp">public async Task CreateOrUpdateExtractAnalyzerAsync(CancellationToken ct = default)
 {
     var analyzer = new ContentAnalyzer
     {
         BaseAnalyzerId = "prebuilt-document",
         Config = new ContentAnalyzerConfig
         {
             EnableOcr = true,
             EnableLayout = true,
             EstimateFieldSourceAndConfidence = true,
         }
         FieldSchema = new ContentFieldSchema(
             WalmartFields.All.ToDictionary(
                 f =&gt; f.Name,
                 f =&gt; ExtractField(f.Type, f.Description))),
     };

     analyzer.Models["completion"] = _options.CompletionModelName;

// You will create one analyzer per category needed to be processed
     await CreateOrReplaceAsync(_options.ExtractAnalyzerId, analyzer, ct).ConfigureAwait(false);
 }
</code></pre>
<p>As mentioned above, you <strong>don't need to link an analyzer to a category</strong> if that category doesn't require further processing.</p>
<hr />
]]></content:encoded></item><item><title><![CDATA[The Ultimate AI Guide for Linux Engineers - Book Review]]></title><description><![CDATA[If you're a Linux engineer trying to figure out where AI actually fits into your day-to-day work - not the hype, the actual workflow - this book delivers a solid, practical roadmap.
It opens by ground]]></description><link>https://dimitaronai.com/the-ultimate-ai-guide-for-linux-engineers-book-review</link><guid isPermaLink="true">https://dimitaronai.com/the-ultimate-ai-guide-for-linux-engineers-book-review</guid><category><![CDATA[AI]]></category><category><![CDATA[agentic AI]]></category><category><![CDATA[generative ai]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Wed, 08 Jul 2026 10:15:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/4f2f7c33-3e99-4931-abaa-04a294517145.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/6117e428-3997-4d87-85b2-df98d5735d5b.png" alt="" style="display:block;margin:0 auto" />

<p>If you're a Linux engineer trying to figure out where AI actually fits into your day-to-day work - not the hype, the actual workflow - this book delivers a solid, practical roadmap.</p>
<p>It opens by grounding AI in something engineers already understand: logs. Instead of treating AI as a black box, the book frames it as a context-aware layer on top of the logging and monitoring systems Linux already gives us, while being upfront about the tradeoffs - data privacy, redaction of sensitive values, and the need for approval gates before any AI-suggested action touches production.</p>
<p>From there it walks through the concepts in a logical order: demystifying AI/ML/LLMs and where each layer (ML, deep learning, generative AI) maps to real Linux tasks like anomaly detection and log summarization; setting up an AI-ready environment with Python, virtual environments, containers, and a clear-eyed comparison of CPUs vs GPUs vs specialized accelerators; and a tour of the open-source ecosystem that any engineer building AI tooling will run into.</p>
<p>Where the book really earns its "for Linux engineers" title is in the back half. It gets into automating operations with AI assistants (with an emphasis on safety validation, dry-runs, and auditability rather than just "let the AI run commands"), building bounded, least-privilege autonomous agents, using LLMs for monitoring and troubleshooting via a dialog-style interface over your telemetry, and applying RAG so systems can reason over your actual logs and runbooks instead of hallucinating. It closes with deployment on Kubernetes, security/guardrails (STRIDE, prompt injection), and a forward-looking chapter on how much autonomy is actually appropriate in infrastructure.</p>
<p>The throughline that stuck with me most: <strong>not every problem needs the same level of intelligence</strong>. The book keeps circling back to governance, human approval for high-impact changes, and progressive rollout - starting with read-only operations before expanding scope. It's a refreshingly sober take in a space full of overpromising.</p>
<p><strong>Bottom line:</strong> a practical, well-structured guide that treats AI as a serious operational tool rather than magic - recommended for any Linux engineer or SRE looking to move from reactive firefighting to more proactive, AI-assisted system management.</p>
]]></content:encoded></item><item><title><![CDATA[Building Agent-Powered Applications - Book Review]]></title><description><![CDATA[Building Agent-Powered Applications is the kind of book you wish existed when you first started trying to make sense of the LLM ecosystem. It opens with a grounded overview of ML and NLP fundamentals ]]></description><link>https://dimitaronai.com/building-agent-powered-applications-book-review</link><guid isPermaLink="true">https://dimitaronai.com/building-agent-powered-applications-book-review</guid><category><![CDATA[agents]]></category><category><![CDATA[agentic AI]]></category><category><![CDATA[AI]]></category><category><![CDATA[app development]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Mon, 25 May 2026 07:24:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/072ee2e1-769e-4b8b-9fc6-3cb0ab56db3a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<img src="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/b5c63b0a-febf-424d-9ba9-96fde0398312.png" alt="" style="display:block;margin:0 auto" />

<p><code>Building Agent-Powered Applications</code> is the kind of book you wish existed when you first started trying to make sense of the LLM ecosystem. It opens with a grounded overview of ML and NLP fundamentals - not exhaustive, but just enough to build shared vocabulary before diving into the real substance.</p>
<p>The middle chapters are where the book earns its keep. The coverage of prompt engineering goes well beyond "write clearer prompts" - it walks through chain-of-thought, tree-of-thought, few-shot strategies, and multi-prompt chaining with enough context to understand the trade-offs. The RAG chapter is similarly practical: it distinguishes between lexical, semantic, and hybrid retrieval, explains TF-IDF and vector databases without oversimplifying, and keeps the focus on engineering decisions rather than just theory. Fine-tuning gets honest treatment too - the book is clear about when it's the right tool versus when RAG or prompting will serve you better, and it covers the real costs of full versus partial fine-tuning.</p>
<p>The final stretch, covering agent architecture and evaluation, is the strongest part. The agent chapter pulls everything together - memory design, tool orchestration, MCP, multi-agent coordination - and frames it as a software engineering problem, not just an AI one. That framing matters. It treats reliability, error handling, and human-in-the-loop design as first-class concerns rather than footnotes.</p>
<p>The evaluation chapter deserves special mention because it's often the first thing cut from books like this. Here it gets proper attention: offline vs. online evaluation, lexical and semantic metrics, LLM-as-judge approaches, and safety/adversarial testing all feature. The core message - that evaluation should be defined at the start of a project, not bolted on at the end - is exactly right.</p>
<p>If you're building <code>AI-powered applications</code> and want a single resource that takes you from foundational concepts all the way through to production-ready agents, this is the one to read.</p>
]]></content:encoded></item><item><title><![CDATA[The Secret Behind Better RAG Results: Hybrid Search in Azure AI Search]]></title><description><![CDATA[What is hybrid search?
Hybrid search combines full-text and vector queries into a single request, running against an index that holds both plain-text content and generated embeddings.
Vector search ex]]></description><link>https://dimitaronai.com/the-secret-behind-better-rag-results</link><guid isPermaLink="true">https://dimitaronai.com/the-secret-behind-better-rag-results</guid><category><![CDATA[Azure]]></category><category><![CDATA[AI]]></category><category><![CDATA[generative ai]]></category><dc:creator><![CDATA[Dimitar Iliev]]></dc:creator><pubDate>Wed, 06 May 2026 07:42:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/3d69fcde-9dd3-448a-9d41-f914af281f5a.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>What is hybrid search?</h3>
<p>Hybrid search combines full-text and vector queries into a single request, running against an index that holds both plain-text content and generated embeddings.</p>
<p>Vector search excels at surfacing information that is conceptually related to your query, even when no exact keywords match in the inverted index. Full-text search, on the other hand, offers precision, and can be paired with optional semantic ranking to further improve the quality of results.</p>
<hr />
<h3>Prerequisites</h3>
<ul>
<li><p>A Cosmos database with sample documents to index</p>
</li>
<li><p>An Azure AI Search service</p>
</li>
<li><p>A search index containing searchable vector and nonvector fields.</p>
</li>
<li><p>Search service must have the semantic ranker enabled</p>
</li>
</ul>
<hr />
<h3>Overview of the services</h3>
<p>The Cosmos database has five documents with the following structure:</p>
<pre><code class="language-json">{
    "id": "doc-005",
    "title": "Azure OpenAI Integration Patterns",
    "category": "AI Architecture",
    "tags": [
        "OpenAI",
        "Azure",
        "Integration"
    ],
    "content": "Azure OpenAI can be integrated with backend systems using APIs, event-driven architectures, or orchestration frameworks like Semantic Kernel. It enables intelligent applications with natural language capabilities.",
    "summary": "Patterns for integrating Azure OpenAI into applications.",
    "author": "Michael Green",
    "createdDate": "2025-05-01T16:20:00Z",
    "region": "EU",
    "accessLevel": "public"
}
</code></pre>
<p>The Azure AI Search contains an index with field configuration:</p>
<img src="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/9adee294-81ff-4bbf-bc9e-a087f8c492c4.png" alt="" style="display:block;margin:0 auto" />

<p>Notice the <code>text_vecor</code> field used for the vector query.</p>
<p>Finally, the semantic configuration defined:</p>
<img src="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/b29fb8cd-2040-40c3-8064-e8cafb952063.png" alt="" style="display:block;margin:0 auto" />

<hr />
<h3>Rise of the working example</h3>
<p>Let’s walk through how to build this in C#. Here’s the code:</p>
<pre><code class="language-csharp">using Azure;
using Azure.Identity;
using Azure.Search.Documents;
using Azure.Search.Documents.Models;
using Azure.AI.OpenAI;

string searchEndpoint = Environment.GetEnvironmentVariable("AZURE_SEARCH_ENDPOINT")!;
string indexName = "rag-hybrid";

string openAiEndpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!;
string deploymentName = "text-embedding-ada-itt";

var azureOpenAIClient = new AzureOpenAIClient(
    new Uri(openAiEndpoint),
    new DefaultAzureCredential());

// Create embedding client
var embeddingClient = azureOpenAIClient.GetEmbeddingClient(deploymentName);

string query = "how to integrate RAG";

// Generate embedding
var embeddingResponse = await embeddingClient.GenerateEmbeddingAsync(query);

// Convert to float[]
var queryVector = embeddingResponse.Value.ToFloats();

string searchKey = Environment.GetEnvironmentVariable("AZURE_SEARCH_KEY")!;

SearchClient client = new(
    new Uri(searchEndpoint),
    indexName,
    new AzureKeyCredential(searchKey));

SearchResults&lt;SearchDocument&gt; results = await client.SearchAsync&lt;SearchDocument&gt;(
    query,
    new SearchOptions
    {
        VectorSearch = new()
        {
            Queries =
            {
                new VectorizedQuery(queryVector)
                {
                    Fields = { "text_vector" },
                    Exhaustive = true,
                    KNearestNeighborsCount = 5
                },
            }
        },
        QueryType = SearchQueryType.Semantic,
        SemanticSearch = new SemanticSearchOptions
        {
            SemanticConfigurationName = "rag-hybrid-semantic-configuration"
        },
        Select = { "summary", "chunk", "category", "author" },
        Size = 5
    });

await foreach (SearchResult&lt;SearchDocument&gt; result in results.GetResultsAsync())
{
    Console.WriteLine($"{result.Document["category"]} -&gt; {result.Document["summary"]} \n" +
        $"{result.Document["chunk"]}");
    Console.WriteLine();
}
</code></pre>
<p>The core of the example is the <code>SearchAsync</code> call, which sends a query that does four things simultaneously.</p>
<p>First, the query string is passed as the first argument to <code>SearchAsync</code>, which triggers traditional keyword search.</p>
<p>Second, the <code>VectorizedQuery</code> uses the float vector generated earlier to search the <code>text_vector</code> field in the index. It also finds the 5 nearest neighbors.</p>
<p>Third, the results from keyword search and vector search are merged, producing a single ranked list.</p>
<p>Fourth, the <code>QueryType</code> is set to <code>Semantic</code>, which activates Azure's <code>semantic re-ranker.</code> This re-ranks the fused results using a language model that understands context, not just keywords. The <code>semantic configuration</code> defines how the re-ranker interprets fields like title and content, guiding how relevance is computed.</p>
<p>Let's observe the results returned by running two different queries. The first query is <code>string query = "how to integrate RAG";</code> while the second is <code>string query = "written by Michael Green";</code></p>
<img src="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/3f07f256-bb98-4f7e-93d8-7bb5848b0c2a.png" alt="" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/uploads/covers/68f7b959ef9f07b510b2b032/4c776155-a6c1-4cc2-8eed-be3a6482b760.png" alt="" style="display:block;margin:0 auto" />

<hr />
]]></content:encoded></item><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></channel></rss>