Five RAG Architectures in Real Azure Code

Over the past few months I kept running into the similar looking infographics, in one form or another: five or six boxes, each a named RAG architecture, arrows showing how a query flows through it. Hybrid RAG. GraphRAG. Agentic RAG. Corrective RAG. Multimodal RAG. They’re useful as vocabulary. They are not implementation guides. None of them show you the part that actually takes the time.

So I built all five RAG architectures, on Azure, against one shared corpus and one shared set of test questions, and measured what came out. This post is the result: what each diagram leaves out, what the equivalent Azure code actually looks like, where the real deployment pitfalls were, and a comparison table built from real runs, not from argument.

The corpus is a fictional Dutch health insurer, Zorgverzekeraar Meridiaan, the same one I’ve used in a couple of other posts in this series. Nine documents: dental and physiotherapy policies, a provider network, an authorization process, a member complaint and the quarterly report that restates it, a stale FAQ sitting next to the current policy, a reimbursement table, and a scanned claim form. Fifteen questions, tagged by which pattern they were designed to stress. All five patterns answer all fifteen questions, so the comparison is apples to apples.

The repo is at github.com/steefjan1/five-rag-patterns if you want to run it yourself.

What the diagram shows vs. what the Azure code does

Hybrid RAG

The diagram draws dense and sparse retrieval as two separate paths that merge into a box labeled Reciprocal Rank Fusion. That box is mostly a non-event on Azure. Azure AI Search’s hybrid query type takes a vector query and a text query together and fuses them server-side. There is no RRF code to write.

What actually takes engineering effort is the index schema: chunk granularity (I chunk by document section, not by a fixed token window, so a retrieval unit is a coherent answer, not an arbitrary slice), which fields are filterable versus searchable versus vector, and whether semantic ranking is worth its cost on top of the fusion you already get for free.

Measured: recall 1.00 across all fifteen questions, the best of any pattern on pure coverage. Precision sits at 0.38, diluted by a fixed top-5 retrieval regardless of how many documents a question actually needs. Cheapest sane baseline in the set: $0.0026 and 2.00 seconds per query.

GraphRAG

The diagram draws one static graph: entities, edges, a subgraph retrieval step, a box for community summaries. What it doesn’t draw is that the graph has a maintenance cost. Community detection (Louvain, via networkx, which runs fine at this corpus’s scale without a dedicated graph database) and community summarization are real compute and real Azure OpenAI spend, paid once at setup and again every time the graph changes enough to shift community boundaries. Nothing about that shows up in the box-and-arrow version.

Entity linking here is a cheap substring match against entity names, not an embedding call, which is part of why this pattern is the cheapest per query in the whole set. Retrieval is a graph walk: two hops, both directions, so a question like “which hospital did this referral come from, and which GP group refers into that hospital” resolves correctly even though no single document states the answer. It’s two separate edges, walked in sequence.

Measured: recall 1.00 on its own three relational questions, the two-hop case included, and 0.21 on the other twelve. No other pattern swings that hard between its own territory and everything else. $0.0013 per query, the cheapest pattern here, in the narrowest lane.

Agentic RAG

The diagram shows a planner routing to tools and a reasoner that loops “until confident.” There is no upper bound drawn anywhere on that loop. Left alone, that is a cost leak, not a reliability feature, so the actual implementation caps it at five iterations and reports hitting the cap as its own outcome rather than quietly forcing an answer and calling it clean.

The other thing worth knowing if you’re building this on Azure: the AI Foundry Agent Service SDK bypasses API Management for its own LLM calls. I found this the hard way on an earlier project in this series. If your governance model depends on APIM, that means routing tool-calling agents through the standard OpenAI SDK pointed at the gateway, not through the framework’s own agent runtime, or every rate limit and kill switch you built stops applying the moment the agent framework makes the call instead of your code.

Two of this pattern’s four tools aren’t retrieval at all. The dental waiting-period and annual-maximum arithmetic is transcribed from the policy documents as plain code, not left for a language model to compute from prose. Insurance eligibility math is exactly the kind of thing an LLM gets subtly wrong under pressure, and exactly the kind of thing code gets right every time.

Measured: precision 1.00, recall 1.00 on its own two questions, and it’s the only pattern that doesn’t collapse elsewhere: recall 0.85 on the other thirteen, because it always has a general search tool as a fallback when nothing more specific fits. That’s the real finding here. It’s not that Agentic RAG is “better,” it’s that it hedges.

Corrective RAG

The diagram shows retrieve, grade, then three branches: answer, rewrite the query and loop back, or fall back to a web search. The rewrite loop has an arrow pointing backward and no stated exit condition. A closed corpus also has no web to fall back to, so “incorrect” here means declining to answer rather than guessing.

The corpus has a document built specifically to test the grading step: an archived FAQ with a plausible, wrong number sitting right next to the current policy with the right one. A pattern with no grading step retrieves both and may cite either. This one grades the retrieval, asks the model to identify which passage is authoritative using published dates and explicit supersession language, and only feeds the authoritative passages to the final answer. What got fetched and what got used are tracked separately on purpose, so a working grader shows zero distractor citations even though the distractor was retrieved.

Measured: precision 1.00, recall 1.00 on the three distractor questions, confirmed live, not just in the design. The more interesting number is that its other twelve questions score better (precision 0.79) than its own target slice (0.67). The grading discipline isn’t just catching the one distractor it was built to catch, it generalizes. That comes at a real cost: 3.97 seconds average latency, roughly double every other pattern, and the highest cost per query in the set, because a full run can mean three model calls instead of one.

Multimodal RAG

The diagram’s box says “shared multimodal embedding model (e.g. CLIP or ColPali),” which means self-hosting an embedding model. That’s a heavier operational commitment than anything else in this comparison needs, and it’s avoidable. This uses caption-then-embed instead: Document Intelligence extracts the actual structure of the reimbursement table (tables are exactly where a vision model hallucinates a plausible-looking row that isn’t in the source, so that step doesn’t get skipped), the vision-capable chat deployment captions the scanned claim form directly, and both captions get embedded with the same text-embedding-3-large deployment every other pattern uses. Same index Hybrid RAG built, two more documents in it, no new index and no new field.

One implementation note that cost real iteration: a first version of the captioning prompt asked for verbatim transcription, which correctly produced the form’s Dutch date format and Dutch status text. A validation step checking the caption against a hand-written ground truth flagged that as a mismatch, because the ground truth expected ISO dates and English. That’s not a captioning bug, it’s a prompt that needed to ask for normalization, not transcription. Worth deciding on purpose, since a shared index with mixed date formats and mixed languages retrieves worse than a normalized one.

Measured: recall 1.00 across the board and groundedness 1.00 on its own two questions, with no degradation on the other thirteen. That composability is the finding: this pattern is Hybrid RAG’s exact retrieve-and-answer loop plus two documents, and the numbers confirm that composition was free.

The comparison table

Same corpus, same fifteen questions, one pass, all five RAG architectures.

PatternAvg latencyCost/queryOverall precisionOverall recallOwn-target recall
Hybrid2.00s$0.00260.381.001.00 (n=5)
GraphRAG2.32s$0.00130.200.371.00 (n=3)
Agentic2.09s$0.00450.450.871.00 (n=2)
Corrective3.97s$0.00510.770.971.00 (n=3)
Multimodal2.18s$0.00270.381.001.00 (n=2)

“Own-target” means the small subset of the fifteen questions each pattern was actually designed to answer (GraphRAG’s two-hop provider questions, Corrective RAG’s stale-document case, and so on). Every pattern hits recall 1.00 in its own lane. What separates them is what happens outside it: GraphRAG falls to 0.21 recall on the other twelve questions, Agentic RAG only falls to 0.85, and Hybrid, Corrective, and Multimodal don’t fall at all, because their retrieval isn’t scoped to a narrow entity set in the first place.

Two honest caveats on this table. Precision across every pattern is capped low by a fixed top-5 retrieval regardless of how many documents a question actually needs, so precision here measures retrieval breadth more than answer quality, read recall and the own-target column as the more meaningful columns. And the cost figures come from a placeholder price table, not a live Azure billing export, useful for comparing patterns against each other, not for a procurement conversation.

Deployment pitfalls

Every one of these was a real failure against a live Azure subscription, not a hypothetical.

Pinned model versions rot. A deployment written against gpt-4o-mini version 2024-07-18 failed eight months later with ServiceModelDeprecated. The fix wasn’t a newer pin, it was to stop pinning: leave the deployment’s model version empty and let Azure resolve the current default, and check az cognitiveservices model list -l <region> -o table before assuming a model name is still offered at all.

The account kind changed. Azure OpenAI is now provisioned through Foundry as kind: 'AIServices', not the older kind: 'OpenAI'. Same deployment mechanism underneath, different account kind and a newer API version. A template written against the old kind fails Cognitive Services preflight validation, not at compile time.

A malformed policy XML fails at ARM validation, not at Bicep build time. An APIM policy embedded as a Bicep string had a raw double-quoted path literal sitting inside an already double-quoted XML attribute. bicep build compiled it clean, because Bicep has no way to know a string is meant to be well-formed XML. The actual break only showed up against the live ARM validation API, after Azure AI Search, Cosmos DB, and the Foundry account had already finished provisioning. A small script that compiles the template and separately parses every embedded policy string as XML catches this before the next azd up, not during one.

RBAC role assignments alone don’t turn on Azure AD authentication. Azure AI Search kept returning a flat 403 on every data-plane call despite two correctly scoped role assignments, because the service still only accepted API-key authentication. Nothing had told it to accept AAD tokens at all. The fix is a separate property, disableLocalAuth: true, on the search service itself. If a resource with roles that look correct still refuses an authenticated caller, check the resource’s own auth settings before re-checking the role assignment.

Where none of this is the answer

None of these five patterns is the right first move for a small, stable knowledge base. Plain vector search, no fusion, no graph, no grading, no agent loop, is the correct answer until you can name the specific failure mode you’re buying insurance against. Every pattern here is a bet against one kind of failure, and every bet has a cost attached whether or not you ever collect on it.

Don’t build all five for one real system either. Pick based on the failure mode your domain actually has. GraphRAG only pays for itself if your questions are genuinely relational, multi-hop, the kind no single document answers. If they’re not, you’re paying setup cost and getting a narrower Hybrid RAG. Agentic RAG’s flexibility costs a planning call before any retrieval happens at all, worth it if your questions genuinely vary in shape, wasted overhead if they don’t. Corrective RAG’s discipline costs roughly double the latency of everything else in this comparison. That’s a fine trade when a wrong answer is expensive and a two-second wait isn’t. It’s a bad trade for a chat widget where speed is the product.

What this actually proves

The infographic’s taxonomy is real. These five RAG architectures are genuinely different, with genuinely different failure modes, and that part of the diagram holds up. What doesn’t hold up is the implication that the hard part is choosing between them. The hard part, in every case, was the piece the diagram didn’t draw: RRF turned out to be free because Azure AI Search already does it, but community detection is not free and has to be redone as the graph changes. An agent loop needs a hard cap or it’s an open-ended bill. A query rewrite loop needs the same cap for the same reason. A self-hosted multimodal embedding model turned out to be avoidable entirely, caption-then-embed onto infrastructure you already have gets you most of the way there.

The single most useful number in this whole exercise might be the smallest one: Corrective RAG’s grading step scored better on questions it wasn’t built for than on the one it was. That’s a pattern worth paying attention to. The things that make a RAG system more disciplined in one specific place often make it more disciplined everywhere, not just in the place you were testing for.

If you want to see the actual failure modes up close rather than the aggregate table, the earlier posts in this series go deeper on two of them: what naive RAG diagrams leave out covers the hybrid retrieval and groundedness gaps in more detail, and choosing between RAG, GraphRAG, and Agentic RAG when auditability is the constraint makes the conceptual case this post backs with numbers.

The full repo, including the corpus, the eval harness, and every pattern’s implementation, is at github.com/steefjan1/five-rag-patterns.

The Microsoft AI Stack in 2026 and the Certification Trail That Runs Through It

I spent some time observing what’s inside the Microsoft AI stack. Foundry, Agent Framework, Logic Apps, AI Search, Purview, Entra. After a while, I had a picture of how the pieces fit and decided to draw one myself to share.

Then recently Microsoft published AI-500, an expert certification for multi-agent systems. That made me curious whether Microsoft’s view of the platform matches my own. So I plotted the certification trail against my diagram. This post shows the result.

The Microsoft AI stack as I see it

Six layers. Five of them stack vertically. The sixth runs down the side, through all the others.

Models sit at the bottom: GPT-5, Claude, Mistral, Grok, Microsoft’s own MAI and Phi, Llama, DeepSeek, and the open catalog. This is the least differentiated layer. In Foundry, swapping one model for another is a configuration change. It gets the most attention and deserves the least.

Infrastructure comes next. Foundry is the hub, alongside Azure OpenAI, Azure ML, AKS, Container Apps, App Service, and Foundry Local for the edge. This is hosting, serving, and compute. Solid and well understood.

Data and context: the real moat

I split the data layer in two, because it hides the most important part of the platform. Layer 3a holds the sources of truth: Microsoft Graph, SharePoint, Exchange, Fabric and OneLake, Dataverse, and the vector stores in Cosmos DB, Azure SQL, and PostgreSQL.

Layer 3b is context. Work IQ, Fabric IQ, Foundry IQ, and Azure AI Search turn enterprise data into grounding that respects who is asking. Permission-trimmed retrieval must honor Entra ACLs at query time, not filter after ranking. AI Search supports this through document-level access control. Get it wrong and answers leak, or recall collapses.

This is where the hard engineering hours go. Swapping a model is a config change. Graph-grounded context is months of work.

Agents, Copilots, and the layer vendors leave out

The agentic platform sits above the data. Microsoft Agent Framework merged Semantic Kernel and AutoGen. Next to it sit Foundry Agent Service, Copilot Studio, Logic Apps, Azure Functions, Service Bus, and Event Grid. MCP and A2A handle tools and agent-to-agent communication.

The Copilot layer is on top: Microsoft 365, GitHub, Security, Dynamics 365, Power Platform, and Teams. This is distribution. These are the surfaces people already live in. As a result, enterprise AI adoption is easier when data, permissions, infrastructure, and applications already exist in one ecosystem.

Then comes the sixth layer of the Microsoft AI stack, the one vendor diagrams leave out: governance and evidence. Entra ID and Entra Agent ID handle identity. Purview covers labels, DLP, and audit. Content Safety and API Management provide guardrails and the AI gateway. Defender, Sentinel, Azure Monitor, Log Analytics, and Azure Policy deliver traces, evaluations, and control evidence.

I work for a regulated organization. Before anything goes live, Legal and Internal Audit ask four questions. Who approved the agent? What data did it access? Which controls applied? What happened when it made a wrong decision? Distribution makes deployment easier. However, without traceable evidence it does not make the system production ready. That is why this layer runs vertically through my drawing.

The Microsoft AI certification trail

I knew AI-900. I had not followed what replaced it. This week I learned that Microsoft now offers a full set of AI certifications, from fundamentals to an expert exam. The expert exam, AI-500, is in beta. I read the study guide. Its scope says a lot: orchestration patterns, agent-to-agent protocols, observability, guardrails, and cost control. Architecture work, end to end.

What made it click was laying the whole trail side by side. Each step has its own verb.

  1. AI-901, Azure AI Fundamentals. Understand the concepts and services.
  2. AI-103, AI Apps and Agents Developer. Build applications and agentic solutions on Foundry.
  3. AI-200, Azure AI Cloud Developer. Engineer cloud-native AI properly.
  4. AI-300, ML Operations Engineer. Operate models in production.
  5. GH-300 and GH-600, Copilot and Agentic AI Developer. Ship with agents working beside you.
  6. AI-500, Multi-Agent AI Solutions Expert. Orchestrate systems of agents at scale.

Plotting the trail on the Microsoft AI stack

Six exams, six layers of the Microsoft AI stack. I mapped each exam to its layers, in its study guide exercises, and its center of gravity.

The fundamentals exam touches every layer at concept depth. AI-103 lives in models, infrastructure, context, and the agentic platform. AI-200 moves down into infrastructure and data. AI-300 sits on infrastructure and the evidence plane, because operating models in production is mostly monitoring and evaluation. Meanwhile, the GitHub exams live at the top, where developers meet agents in the editor.

AI-500 is the interesting one. It spans context, the agentic platform, and governance. It does not test models at all. In fact, three of its five headline topics belong to the plane most stack diagrams leave out.

That was the moment the two pictures agreed. My diagram says the hard part of the Microsoft AI stack is context and evidence, not model choice. Microsoft’s expert exam tests context and evidence, not model choice. I did not expect a certification roadmap to confirm an architecture opinion, but here we are.

Where this is the wrong answer

Do not read the trail as a ladder you must climb in order. If you already run agents in production, AI-500 reflects your work, and AI-901 will teach you nothing. Platform engineers should look at AI-200 and AI-300 first. Developers should start with the GitHub exams.

Also, do not read my layer mapping as Microsoft’s. It is my reading of the study guides, and beta study guides move.

Finally, do not confuse the certificate with the evidence. Passing AI-500 shows you know what a control looks like. It does not produce the audit trail for your agent. That still takes Purview configured, Entra Agent ID issued, traces flowing to Log Analytics, and someone signing off. The exam is a map of the work. The work is still the work!

Credits

The Azure icons come from the official Azure architecture icon set. Product marks belong to their owners. Microsoft’s announcements are on the Skills Hub blog: Multi-Agent AI Solutions Expert, AI Apps and Agents Developer Associate, and GitHub Agentic AI Developer. The stack diagram builds on a five-layer picture that circulated on LinkedIn; the split data layer and the governance plane are my additions.

Agentic AI Design Patterns on Azure

Like a lot of people on LinkedIn right now, I have seen no shortage of infographics laying out agentic AI design patterns: tidy boxes and arrows for Tool Use, ReAct, Reflection, Planning, Orchestrator, Sequential Chain, Parallel Fan-out/Fan-in, Hierarchical, and P2P Mesh. These diagrams are a fine way to get the shape of an idea across. However, a diagram cannot tell you whether the thing actually works, or what breaks when you try to stand it up for real. So I spent the last stretch actually building agentic AI on Azure: all nine patterns, for real, on Azure’s PaaS offerings. That meant writing Bicep IaC, running azd up, making actual HTTP calls against the deployed endpoints, and running actual Application Insights queries when things went wrong. In the end I had nine independently deployable reference samples, not nine slides.

Writing the Bicep was the easy part. Getting all nine actually to stand up and respond correctly was where the real lessons were. This is a retrospective on what broke, why, and what I’d tell someone else to do to avoid it.

Lesson 1: model availability is regional, and the error message won’t always tell you clearly

The first failure mode hit me repeatedly, across multiple patterns. azd up would warn that gpt-4.1 “was not found” in the target region, then fail validation outright with InvalidResourceProperties: The specified SKU 'Standard' for model 'gpt-4.1' is not supported in this region.

This is not a bug in the Bicep. It is genuinely true that not every Azure OpenAI model and SKU combination is available in every region, and availability also varies by subscription. centralus and westeurope both rejected gpt-4.1 for me at different points, while swedencentral consistently worked across every pattern in the repo. So if you are scripting or templating Azure OpenAI deployments, do not hardcode a region and assume it will work everywhere. Instead, treat azd env set AZURE_LOCATION swedencentral as a reflexive first move whenever a fresh pattern’s deploy attempt fails.

Lesson 2: Logic Apps Standard is powerful, and standing it up correctly is its own skill

The Sequential Chain pattern (input → extract → draft → validate → output, a fixed pipeline) is a textbook fit for a low-code workflow engine. So I built it on Logic Apps Standard first, and it ended up costing me, by a wide margin, the most debugging time of the whole project. Here is what went wrong, roughly in order:

  • ServiceProvider-type triggers cannot have a recurrence property. If you copy a recurrence block over from an ApiConnection-style trigger example, you get a WorkflowProcessingFailed error, and the message does not obviously point at the fix.
  • Connector parameter names are not always what you would guess. Azure literally names the built-in OpenAI connector’s connection parameter openAIEndpoint, not endpoint. I only discovered this from Azure’s own runtime error message, not by guessing at the schema.
  • Windows versus Linux hosting changes what app settings you need, and not symmetrically. Windows-hosted Premium plans require WEBSITE_CONTENTAZUREFILECONNECTIONSTRING and WEBSITE_CONTENTSHARE, but carrying those same settings over to a Linux plan actively breaks content sync.
  • WorkflowStandard (WS1) is a hard requirement, not a suggestion. Azure explicitly rejects an ElasticPremium (EP1) substitution with “Logic Apps can be deployed only to ‘WorkflowStandard’ Sku or App Service Environments,” even though the two SKUs share the same underlying compute family.
  • Regional SKU quota is real, and it will not show up until you deploy. SubscriptionIsOverQuotaForSku for “WS1 VMs” hit me in two different regions. I eventually fixed it by decoupling the Logic App’s region from the rest of the pattern’s resources through a separate Bicep parameter, which let it land somewhere with quota headroom.
  • Deterministic role-assignment names collide across resource recreation. If you name a role assignment with guid(scope, resourceId, roleName) and then delete and recreate the resource whose managed identity receives that role, Azure refuses to update the existing assignment to point at the new principal ID. So you first have to find and delete the stale assignment by its exact resource ID.
  • Finally, after fixing all of the above, I ran into a Logic App instance that had simply wedged itself: Sequence contains no elements on startup, Kudu unreachable or crawling, and no combination of RBAC or config fixes helping. The only real fix was deleting and recreating the whole site resource.

Durable Functions

After all that, I rebuilt the same pattern on Durable Functions instead. An orchestrator calls three sequential activities, each a plain Azure OpenAI chat completion call, and the built-in RetryOptions does the retry work that Logic Apps’ declarative retry policies would otherwise have done. It deployed in under two minutes and worked on the first real test. The conceptual case for a low-code workflow engine still holds here, since a fixed sequence of stages is exactly what that kind of tool is for. However, if you want to move fast while building agentic AI on Azure, and you already have a working Durable Functions convention elsewhere in your stack, do not underestimate how much extra surface area Logic Apps Standard adds compared with staying in code you already know how to debug.

Lesson 3: RBAC is not one system, and Cosmos DB has its own

The P2P Mesh pattern (three Azure Functions reacting to each other’s events through Event Grid, with Cosmos DB tracking completion state) threw a 403 Forbidden: "Request blocked by Auth... does not have required RBAC permissions to perform action Microsoft.DocumentDB/databaseAccounts/readMetadata". This happened even though the function app’s managed identity already had every role I expected under Microsoft.Authorization/roleAssignments.

Here is the catch: Cosmos DB runs its own, separate, native RBAC system. A normal Azure role assignment against a Cosmos account grants control-plane access, but it does nothing for data-plane operations like reading or writing items. Instead, you need a Microsoft.DocumentDB/databaseAccounts/sqlRoleAssignments resource, scoped specifically to one of Cosmos’s own built-in role definitions. (The “Cosmos DB Built-in Data Contributor” role has a fixed, well-known GUID: 00000000-0000-0000-0000-000000000002.) So if your Bicep only includes the generic role-assignment resource type pointed at a Cosmos account, it silently grants nothing useful.

(Once I fixed that, the very next error was a 404 reading “Owner resource does not exist” on the database and container the app code expected. Provisioning the Cosmos account is not the same as provisioning the database and container inside it. Remember that if your Bicep stops at the account level.)

Lesson 4: pick the “native” endpoint type when Azure offers one

Still on P2P Mesh: wiring Event Grid subscriptions to Azure Functions endpoints through a raw webhook URL (https://<app>.azurewebsites.net/runtime/webhooks/eventgrid?functionName=X&code=<key>) produced a persistent 401 Unauthorized, Webhook endpoint validation failed. It survived two fixes I tried first: swapping the master key for the eventgrid_extension system key, and then directly inspecting the deployed function, which confirmed its eventGridTrigger binding matched what Event Grid expected. Neither fix mattered, because a plain authenticated GET against that same URL came back 400 Bad Request. That proved the key itself was fine, so the actual problem had to be something specific to Event Grid’s own validation handshake against a webhook-typed endpoint.

The actual fix was switching az eventgrid event-subscription create from a raw webhook URL to --endpoint-type azurefunction --endpoint <function-resource-id>. That is the native destination type Event Grid offers for Azure Functions, and it authenticates through the ARM resource ID instead of a function key embedded in a URL. It worked immediately. So the broader lesson: if a platform offers a first-class integration type for a specific target, prefer it over hand-rolling the equivalent with a generic webhook. That way, you inherit the platform’s own handling of edge cases you would not think of yourself.

Lesson 5: “Azure AI Foundry” isn’t one resource type

The Orchestrator pattern, a central agent hosted on Azure AI Foundry Agent Service that delegates to specialist tools, failed with a raw DNS error: Name or service not known, against a hostname the app was trying to reach. This was not an auth failure or a permissions failure. The hostname genuinely did not exist.

The root cause: the Bicep provisioned the older Azure AI Foundry resource model, a Microsoft.MachineLearningServices/workspaces hub plus a project workspace, which is the original Azure AI Studio approach. Meanwhile, the app code used Azure.AI.Projects.AIProjectClient, which only understands the newer, unified Foundry resource model: a Microsoft.CognitiveServices/accounts resource with kind: 'AIServices' and allowProjectManagement: true, plus a projects child resource. That newer model is reachable at a https://<account>.services.ai.azure.com/api/projects/<project> URL that only exists for that resource type. So two things share the “Azure AI Foundry” name, yet neither is a drop-in substitute for the other from an SDK’s point of view. As a result, it is worth explicitly checking which resource model your SDK version actually expects before wiring up IaC for it, because the error you get when you guess wrong will not obviously point at “you deployed the wrong kind of resource.”

The smaller stuff that added up

A few recurring, less dramatic gotchas worth a line each:

  • Double-check the resource group you think you are querying. More than once I pulled exceptions from the wrong pattern’s Application Insights instance, either because I was still in the wrong working directory, or because I grabbed the first App Insights component Azure CLI happened to return rather than the one for the resource group I actually cared about. So always sanity-check the resource group name before trusting a KQL result.
  • A previous session’s PowerShell variable can look like a fresh result. If a command throws partway through an assignment, the variable keeps its old value. Then, if that old value happens to look plausible (in my case, a valid-looking Durable Functions status payload from an entirely different pattern), it becomes easy to mistake it for real output from the command you just ran.
  • “No results” and “the backing store does not exist yet” are different failure modes. Code that gracefully handles an empty index or table does not automatically handle a missing one. For example, one specialist service in the Orchestrator pattern caught SqlException broadly and degraded gracefully when its sample table lacked seed data. By contrast, the AI Search-backed specialist had no equivalent guard for a 404 on a not-yet-created index, so it took the whole request down with it. Therefore, if you document “unseeded stores degrade gracefully,” actually test the unseeded case for every specialist, not just the one you happened to build first.

The takeaway: what building agentic AI on Azure actually requires

None of these were exotic failures. In fact, every one of them turned out to be a documented, well-understood Azure behavior once I found the right doc page. What made them expensive was that the error messages, on their own, rarely pointed straight at the fix: a DNS failure that was actually a resource-model mismatch, a 401 that was actually about endpoint type rather than credentials, and a 403 that was actually a second, unrelated RBAC system. So the practical lesson is not “Azure is fragile.” Instead, building agentic AI on Azure means dealing with more independently moving parts than the getting-started docs suggest, and the fastest way through is treating every unexplained error as worth one layer deeper of investigation before you assume the obvious fix is the real one.

I have now deployed and tested all nine patterns end to end. If you want the full breakdown of each one, including what it is for, the Azure architecture, and when to reach for it, start with the design patterns overview and go from there. The repo itself is public, so you can see exactly what each pattern provisions.