{"id":4960,"date":"2026-04-06T17:33:16","date_gmt":"2026-04-06T15:33:16","guid":{"rendered":"https:\/\/gokhan-gokalp.com\/?p=4960"},"modified":"2026-04-06T17:45:28","modified_gmt":"2026-04-06T15:45:28","slug":"runtime-governance-for-ai-agents-policy-as-code-with-opa","status":"publish","type":"post","link":"https:\/\/gokhan-gokalp.com\/tr\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/","title":{"rendered":"Runtime Governance for AI Agents: Policy-as-Code with OPA"},"content":{"rendered":"\n<p>In my&nbsp;<em><a href=\"https:\/\/gokhan-gokalp.com\/building-an-ai-agent-in-net-deterministic-routing-and-intelligent-search-with-microsoft-agent-framework\/\" target=\"_blank\" rel=\"noreferrer noopener\">previous article<\/a><\/em>, I tried to show how we can build a simple but practical&nbsp;<em><strong>AI Agent<\/strong><\/em>&nbsp;using&nbsp;<em><strong>Microsoft Agent Framework<\/strong><\/em>&nbsp;(<em>MAF<\/em>) with deterministic routing and intelligent search.<\/p>\n\n\n\n<p>I also mentioned that I kept things simple on purpose and that in a production scenario there are other concerns that need to be addressed. Building the agent itself is really just the beginning. Once we start thinking about putting an agentic system in front of real users, one of these concerns is&nbsp;<strong>governance.<\/strong><\/p>\n\n\n\n<p>By governance I mean things like controlling which tools an agent is allowed to call, enforcing access policies based on user roles and having a&nbsp;<strong>centralized<\/strong>&nbsp;and&nbsp;<strong>auditable<\/strong>&nbsp;way to manage these rules. With growing regulatory attention around&nbsp;<em>AI<\/em>, frameworks like the&nbsp;<em><strong>EU AI Act<\/strong><\/em>, I think having a predictable and auditable way to enforce these policies is going to become more and more important, especially when agents access data or perform actions on behalf of users.<\/p>\n\n\n\n<p>One approach to enforce these rules is embedding them in the system prompt, telling the agent what it should or should not do. But this relies on the <em>LLM <\/em>interpreting those instructions correctly every time and prompts on their own are too fragile to rely on in production systems. Prompts can suggest behavior but they cannot enforce governance. <\/p>\n\n\n\n<p>So in scope of this article, I wanted to explore a more deterministic approach using <strong><em>Open Policy Agent<\/em><\/strong> (<em>OPA<\/em>), where rules are <strong>enforced <\/strong>outside the <em>LLM <\/em>and evaluated reliably every time.<\/p>\n\n\n\n<p>In one of my previous articles on <a href=\"https:\/\/gokhan-gokalp.com\/securing-the-supply-chain-of-containerized-applications-to-reduce-security-risks-policy-enforcement-automated-governance-with-opa-gatekeeper-and-ratify-part-2\/\" target=\"_blank\" rel=\"noreferrer noopener\"><em>supply chain security<\/em><\/a>, I had used <strong><em>OPA Gatekeeper<\/em><\/strong> to enforce policies at the infrastructure level in <em>Kubernetes<\/em>. The idea here is very similar. Instead of governing what gets deployed, we focus on what an agent is allowed to do at runtime. <\/p>\n\n\n\n<p>There are several approaches to governance depending on how the agent is built. Enterprise platforms like <em>Microsoft Foundry<\/em> already provide features such as <em>RBAC <\/em>and content filters. Developer first frameworks like <em>MAF <\/em>or <em>LangChain <\/em>give us full control over the execution flow, which makes it straightforward to integrate <em>OPA <\/em>as a centralized policy enforcement point. <\/p>\n\n\n\n<p>The nice thing about <em>OPA <\/em>is that it is <strong>platform agnostic<\/strong> by design, so the same policies could potentially apply regardless of how the agent is built. <em>OPA <\/em>can sit at an <em>API <\/em>gateway or <em>MCP <\/em>gateway which makes it a natural fit for enterprises looking to adopt <em><strong>Agentic AI<\/strong><\/em> without skipping their existing <em>API <\/em>security and governance layers.<\/p>\n\n\n\n<p>In this article, I will try to show how we can extend the product search agent from the previous article by integrating <em>OPA <\/em>as a <strong>policy-as-code<\/strong> layer to enforce tool level access control.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Where Does OPA Fit In<\/h1>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image size-large wp-duotone-unset-1\"><a href=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/03\/opa-logo2.png\"><img decoding=\"async\" width=\"1024\" height=\"339\" data-src=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/03\/opa-logo2-1024x339.png\" alt=\"\" class=\"wp-image-4977 lazyload\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/03\/opa-logo2-1024x339.png 1024w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/03\/opa-logo2-300x99.png 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/03\/opa-logo2-768x255.png 768w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/03\/opa-logo2-1536x509.png 1536w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/03\/opa-logo2.png 2000w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/339;\" \/><\/a><\/figure>\n\n\n\n<p>As I mentioned in my supply chain security article, <em>OPA <\/em>is a general purpose <strong>policy engine<\/strong> that uses a declarative language called <em>Rego <\/em>to define policies as code. In that article, I used it at the infrastructure level through <em>OPA Gatekeeper<\/em> to intercept admission requests in <em>Kubernetes <\/em>and enforce policies before containerized applications could run.<\/p>\n\n\n\n<p>Here, the idea is also very similar, but the layer is different. Instead of intercepting deployments, we intercept agent actions. When our agent decides to call a tool, we evaluate that decision against an <em>OPA <\/em>policy before the tool actually executes. <\/p>\n\n\n\n<p>The flow looks something like this:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>User request \u2192 Agent reasons \u2192 Agent decides to call a tool \u2192 OPA evaluates policy \u2192 Allow or Deny \u2192 Tool executes or request is rejected<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9\">User<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">request<\/span><span style=\"color: #D8DEE9FF\"> \u2192 <\/span><span style=\"color: #D8DEE9\">Agent<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">reasons<\/span><span style=\"color: #D8DEE9FF\"> \u2192 <\/span><span style=\"color: #D8DEE9\">Agent<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">decides<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">to<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">call<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">a<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">tool<\/span><span style=\"color: #D8DEE9FF\"> \u2192 <\/span><span style=\"color: #D8DEE9\">OPA<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">evaluates<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">policy<\/span><span style=\"color: #D8DEE9FF\"> \u2192 <\/span><span style=\"color: #D8DEE9\">Allow<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">or<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Deny<\/span><span style=\"color: #D8DEE9FF\"> \u2192 <\/span><span style=\"color: #D8DEE9\">Tool<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">executes<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">or<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">request<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">is<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">rejected<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The point here is that the <strong>LLM has no say in the policy decision<\/strong>. It can reason, it can pick a tool, but whether that tool actually runs is determined by a policy that lives outside the agent, written in <em>Rego<\/em>, versioned in source control and testable like any other code.<\/p>\n\n\n\n<p>This is what makes it <strong>deterministic<\/strong>. The same input will always produce the same policy decision, regardless of what the <em>LLM <\/em>thinks.<\/p>\n\n\n\n<p>Before we get started with <em>OPA<\/em>, it helps to quickly recall what we built in the first article. We created a simple but practical product search agent using the <em>MAF<\/em>. It used predictable routing and a small reasoning loop. <em>Azure AI Search<\/em> handled the retrieval side. The agent could interpret a user request, decide whether it needed to call the search tool and return structured results in a predictable way. That setup gives us a clear execution flow to build on. Now we can focus on adding a governance layer with <em>OPA<\/em>.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Let&#8217;s Implement!<\/h1>\n\n\n\n<p>Now it\u2019s time to bring <em>OPA <\/em>into our sample <em>AI <\/em>workflow. The scenario we are going to build is simple. Our current agent already supports product search and now we want to add a second capability that lets users retrieve full product details. But that feature is going to be reserved for <strong>premium <\/strong>users. Standard users can search as much as they want but only premium users can ask follow up questions like \u201c<em>tell me more about this product<\/em>.\u201d<\/p>\n\n\n\n<p>To support that behavior, we will introduce a new tool that returns detailed product information and <em>OPA <\/em>will sit in front of it since it can govern tool access, guide actions or control and decide who is allowed to call. For this example, we will focus purely on access control.<\/p>\n\n\n\n<p>The plan is straightforward. We will simply add OPA to the project, write a small policy in <em>Rego<\/em> and connect everything so the agent checks user permissions before calling the tool.<\/p>\n\n\n\n<p>There are a few ways to bring <em>OPA <\/em>into a system and the choice usually depends on the <strong>deployment architecture<\/strong>. In a <em>Kubernetes <\/em>setup, for example, we might run a central <em>OPA <\/em>instance that intercepts requests or exposes an <em>API <\/em>for policy checks at platform level. That approach works well when multiple agents or services share the same governance layer. <\/p>\n\n\n\n<p>For this example, we are going to include the <em>OPA SDK<\/em> directly in our .<em>NET <\/em>project and evaluate policies inside the agent\u2019s workflow. This keeps the setup simple and self contained.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Adding the GetProductDetails Tool<\/h3>\n\n\n\n<p>Let&#8217;s create a &#8220;<em>ProductDetailsTool<\/em>&#8221; class and add the following.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>using System.ComponentModel;\nusing Azure.Search.Documents;\nusing Azure.Search.Documents.Models;\n\nnamespace ProductSearchAgent.Tools;\n\npublic class ProductDetailsTool\n{\n    private readonly SearchClient _searchClient;\n\n    public ProductDetailsTool(SearchClient searchClient)\n    {\n        _searchClient = searchClient;\n    }\n\n    &#91;Description(\"Get detailed information about a specific product by name. Returns full specifications, pricing, and description. Use this when the user asks for more details about a specific product.\")&#93;\n    public async Task&lt;string> GetProductDetails(\n        &#91;Description(\"The exact or partial name of the product to look up, e.g. 'Dell XPS 15', 'MacBook Pro'.\")&#93; string productName)\n    {\n        var options = new SearchOptions\n        {\n            Size = 1,\n            Select = { \"id\", \"name\", \"description\", \"price\", \"category\", \"brand\", \"specifications\" }\n        };\n\n        Console.WriteLine($\"ProductDetailsTool: GetProductDetails called for \\\"{productName}\\\"\");\n        var results = await _searchClient.SearchAsync&lt;SearchDocument>(productName, options);\n\n        await foreach (var result in results.Value.GetResultsAsync())\n        {\n            var doc = result.Document;\n            string Get(string key) => doc.TryGetValue(key, out var v) ? v?.ToString() ?? \"\" : \"\";\n\n            return $\"\"\"\n                Product: {Get(\"name\")}\n                Brand: {Get(\"brand\")}\n                Category: {Get(\"category\")}\n                Price: ${Get(\"price\")}\n                Description: {Get(\"description\")}\n                Specifications: {Get(\"specifications\")}\n                \"\"\";\n        }\n\n        return $\"No product found matching '{productName}'.\";\n    }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">using<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">System<\/span><span style=\"color: #D8DEE9FF\">.ComponentModel<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">using<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Azure<\/span><span style=\"color: #D8DEE9FF\">.Search.Documents<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">using<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Azure<\/span><span style=\"color: #D8DEE9FF\">.Search.Documents.Models<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">namespace<\/span><span style=\"color: #D8DEE9FF\"> ProductSearchAgent<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">Tools<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">public<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">class<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">ProductDetailsTool<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">private<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">readonly<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">SearchClient<\/span><span style=\"color: #D8DEE9FF\"> _searchClient<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">public<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">ProductDetailsTool<\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9\">SearchClient<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">searchClient<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">_searchClient<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">searchClient<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    &#91;<\/span><span style=\"color: #88C0D0\">Description<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Get detailed information about a specific product by name. Returns full specifications, pricing, and description. Use this when the user asks for more details about a specific product.<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">public<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">async<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Task<\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9\">string<\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">GetProductDetails<\/span><span style=\"color: #ECEFF4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">&#91;<\/span><span style=\"color: #D8DEE9\">Description<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">The exact or partial name of the product to look up, e.g. &#39;Dell XPS 15&#39;, &#39;MacBook Pro&#39;.<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">string<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">productName<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">options<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">SearchOptions<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #D8DEE9\">Size<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #D8DEE9\">Select<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">id<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">name<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">description<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">price<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">category<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">brand<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">specifications<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">}<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">Console<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">WriteLine<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">$<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">ProductDetailsTool: GetProductDetails called for <\/span><span style=\"color: #EBCB8B\">\\&quot;<\/span><span style=\"color: #A3BE8C\">{productName}<\/span><span style=\"color: #EBCB8B\">\\&quot;<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">results<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">await<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">_searchClient<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">SearchAsync<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">SearchDocument<\/span><span style=\"color: #ECEFF4\">&gt;<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">productName<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">options<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">await<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">foreach<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">result<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">in<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">results<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Value<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetResultsAsync<\/span><span style=\"color: #D8DEE9FF\">())<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">doc<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">result<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Document<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #D8DEE9\">string<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Get<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">string<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">key<\/span><span style=\"color: #D8DEE9FF\">) <\/span><span style=\"color: #81A1C1\">=&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">doc<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">TryGetValue<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">key<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">out<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">v<\/span><span style=\"color: #D8DEE9FF\">) <\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">v<\/span><span style=\"color: #ECEFF4\">?.<\/span><span style=\"color: #88C0D0\">ToString<\/span><span style=\"color: #D8DEE9FF\">() <\/span><span style=\"color: #81A1C1\">??<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;&quot;<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">$<\/span><span style=\"color: #ECEFF4\">&quot;&quot;&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">                Product: {Get(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9\">name<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">)<\/span><span style=\"color: #D8DEE9\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                Brand<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #88C0D0\">Get<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">brand<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                Category<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #88C0D0\">Get<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">category<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                Price<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">$<\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #88C0D0\">Get<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">price<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                Description<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #88C0D0\">Get<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">description<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                Specifications<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #88C0D0\">Get<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">specifications<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #ECEFF4\">&quot;&quot;&quot;<\/span><span style=\"color: #D8DEE9\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">$<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">No product found matching &#39;{productName}&#39;.<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Here we have a simple tool that looks up a product by name and returns full details of it. So, nothing fancy. The tool stays focused on retrieving data and we will let <em>OPA <\/em>decide who is permitted to invoke it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Bringing OPA into the Agent Pipeline<\/h3>\n\n\n\n<p>Now that the tool is in place, the next step is to plug <em>OPA <\/em>into the agent pipeline. This is where we add our &#8220;<em>OpaAuthorizationMiddleware<\/em>&#8221; which will act as the gatekeeper and decide whether a tool call is allowed before the agent actually executes it.<\/p>\n\n\n\n<p>In the previous article, we used the same pattern to add a logging middleware that captured every tool call the agent made. That\u2019s exactly why <em>OPA <\/em>fits so well here. We don\u2019t have to change how the agent works or touch any tool implementation. We simply add one more layer to the pipeline.<\/p>\n\n\n\n<p>First, let&#8217;s add the <em>OPA SDK<\/em> to the project and create the middleware class called &#8220;<em>OpaAuthorizationMiddleware<\/em>&#8221; under &#8220;<em>Tools<\/em>&#8221; folder.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>dotnet add package OpenPolicyAgent.Opa<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9\">dotnet<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">add<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">package<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">OpenPolicyAgent<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Opa<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>using System.Security.Claims;\nusing Microsoft.Extensions.AI;\nusing OpenPolicyAgent.Opa;\n\nnamespace ProductSearchAgent.Tools;\n\npublic class OpaAuthorizationMiddleware(\n    IChatClient inner,\n    OpaClient opa,\n    IHttpContextAccessor httpContextAccessor,\n    string policyPath) : DelegatingChatClient(inner)\n{\n    public override async Task&lt;ChatResponse> GetResponseAsync(\n        IEnumerable&lt;ChatMessage> messages,\n        ChatOptions? options = null,\n        CancellationToken cancellationToken = default)\n    {\n        var response = await base.GetResponseAsync(messages, options, cancellationToken);\n\n        var httpContext = httpContextAccessor.HttpContext;\n        var userRole = httpContext?.User.FindFirst(ClaimTypes.Role)?.Value!;\n        var userId   = httpContext?.User.FindFirst(ClaimTypes.NameIdentifier)?.Value!;\n\n        foreach (var msg in response.Messages)\n        {\n            foreach (var call in msg.Contents.OfType&lt;FunctionCallContent>())\n            {\n                var input = new Dictionary&lt;string, object?>\n                {\n                    &#91;\"user\"&#93; = new Dictionary&lt;string, object?> { &#91;\"role\"&#93; = userRole, &#91;\"id\"&#93; = userId },\n                    &#91;\"tool\"&#93; = call.Name,\n                    &#91;\"arguments\"&#93; = call.Arguments?.ToDictionary(x => x.Key, x => x.Value) ?? []\n                };\n\n                try\n                {\n                    var result = await opa.Evaluate&lt;Dictionary&lt;string, object?>>(policyPath, input);\n                    var allowed = result&#91;\"allowed\"&#93; is true;\n                    var message = result&#91;\"message\"&#93;?.ToString();\n\n                    if (!allowed)\n                    {\n                        var denial = message ?? $\"Access denied: {call.Name} is not allowed.\";\n                        Console.WriteLine($\"OpaAuthorizationMiddleware: denied \u2014 {denial}\");\n                        return new ChatResponse(new ChatMessage(ChatRole.Assistant, denial));\n                    }\n\n                    Console.WriteLine($\"OpaAuthorizationMiddleware: allowed \u2014 {call.Name}\");\n                }\n                catch (Exception ex)\n                {\n                    Console.WriteLine($\"OpaAuthorizationMiddleware: policy evaluation failed \u2014 {ex.Message}\");\n                    return new ChatResponse(new ChatMessage(ChatRole.Assistant,\n                        $\"Access denied: policy evaluation unavailable for {call.Name}.\"));\n                }\n            }\n        }\n\n        return response;\n    }\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">using<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">System<\/span><span style=\"color: #D8DEE9FF\">.Security.Claims<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">using<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Microsoft<\/span><span style=\"color: #D8DEE9FF\">.Extensions.AI<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">using<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">OpenPolicyAgent<\/span><span style=\"color: #D8DEE9FF\">.Opa<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">namespace<\/span><span style=\"color: #D8DEE9FF\"> ProductSearchAgent<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">Tools<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">public<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">class<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">OpaAuthorizationMiddleware<\/span><span style=\"color: #D8DEE9FF\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #8FBCBB\">IChatClient<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">inner<\/span><span style=\"color: #D8DEE9FF\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #8FBCBB\">OpaClient<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">opa<\/span><span style=\"color: #D8DEE9FF\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #8FBCBB\">IHttpContextAccessor<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">httpContextAccessor<\/span><span style=\"color: #D8DEE9FF\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #8FBCBB\">string<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">policyPath<\/span><span style=\"color: #D8DEE9FF\">) : <\/span><span style=\"color: #8FBCBB\">DelegatingChatClient<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #8FBCBB\">inner<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">public<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">override<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">async<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Task<\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9\">ChatResponse<\/span><span style=\"color: #81A1C1\">&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">GetResponseAsync<\/span><span style=\"color: #ECEFF4\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">IEnumerable<\/span><span style=\"color: #D8DEE9FF\">&lt;<\/span><span style=\"color: #D8DEE9\">ChatMessage<\/span><span style=\"color: #D8DEE9FF\">&gt; <\/span><span style=\"color: #D8DEE9\">messages<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">ChatOptions<\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">options<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">null<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">CancellationToken<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">cancellationToken<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">default<\/span><span style=\"color: #ECEFF4\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">response<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">await<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">base<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetResponseAsync<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">messages<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">options<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">cancellationToken<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">httpContext<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">httpContextAccessor<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">HttpContext<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">userRole<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">httpContext<\/span><span style=\"color: #ECEFF4\">?.<\/span><span style=\"color: #D8DEE9\">User<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">FindFirst<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">ClaimTypes<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Role<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #ECEFF4\">?.<\/span><span style=\"color: #D8DEE9\">Value<\/span><span style=\"color: #81A1C1\">!;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">userId<\/span><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">httpContext<\/span><span style=\"color: #ECEFF4\">?.<\/span><span style=\"color: #D8DEE9\">User<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">FindFirst<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">ClaimTypes<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">NameIdentifier<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #ECEFF4\">?.<\/span><span style=\"color: #D8DEE9\">Value<\/span><span style=\"color: #81A1C1\">!;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #88C0D0\">foreach<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">msg<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">in<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">response<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Messages<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #88C0D0\">foreach<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">call<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">in<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">msg<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Contents<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">OfType<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">FunctionCallContent<\/span><span style=\"color: #ECEFF4\">&gt;<\/span><span style=\"color: #D8DEE9FF\">())<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">            <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">input<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Dictionary<\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9\">string<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">object<\/span><span style=\"color: #D8DEE9FF\">?&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    &#91;<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">user<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">&#93; <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Dictionary<\/span><span style=\"color: #81A1C1\">&lt;<\/span><span style=\"color: #D8DEE9\">string<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">object<\/span><span style=\"color: #81A1C1\">?&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">&#91;<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">role<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #88C0D0\">&#93; = userRole, &#91;<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">id<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #88C0D0\">&#93; = userId },<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                    &#91;<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">tool<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #88C0D0\">&#93; = call.Name,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                    &#91;<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">arguments<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #88C0D0\">&#93; = call.Arguments?.ToDictionary(x =&gt; x.Key, x =&gt; x.Value) ?? []<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                };<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                try<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                    var result = await opa.Evaluate&lt;Dictionary&lt;string, object?&gt;&gt;(policyPath, input);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                    var allowed = result&#91;<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">allowed<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #88C0D0\">&#93; is true;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                    var message = result&#91;<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">message<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #88C0D0\">&#93;?.ToString();<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                    if (!allowed)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                    {<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                        var denial = message ?? $&quot;Access denied<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9FF\">call.<\/span><span style=\"color: #D8DEE9\">Name<\/span><span style=\"color: #ECEFF4\">}<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">is<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">not<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">allowed<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                        <\/span><span style=\"color: #D8DEE9\">Console<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">WriteLine<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">$<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">OpaAuthorizationMiddleware: denied \u2014 {denial}<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                        <\/span><span style=\"color: #D8DEE9\">return<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">ChatResponse<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">ChatMessage<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">ChatRole<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Assistant<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">denial<\/span><span style=\"color: #D8DEE9FF\">));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    <\/span><span style=\"color: #D8DEE9\">Console<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">WriteLine<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">$<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">OpaAuthorizationMiddleware: allowed \u2014 {call.Name}<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #88C0D0\">catch<\/span><span style=\"color: #D8DEE9FF\"> (<\/span><span style=\"color: #D8DEE9\">Exception<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">ex<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">                    Console.WriteLine($<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">OpaAuthorizationMiddleware: policy evaluation failed \u2014 {ex.Message}<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #88C0D0\">);<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                    return new ChatResponse(new ChatMessage(ChatRole.Assistant,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                        $<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Access denied: policy evaluation unavailable for {call.Name}.<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #88C0D0\">));<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">                }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">            }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">        }<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">        return response;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">    }<\/span><\/span>\n<span class=\"line\"><span style=\"color: #88C0D0\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The important point here is what gets sent to <em>OPA <\/em>as input. We build a simple input object that includes the tool the agent wants to call, the user&#8217;s identity and role and any arguments passed to the tool. This is what makes <em>OPA <\/em><strong>context <\/strong>and <strong>user-aware<\/strong>. The policy receives this input and decides what to do with it.<\/p>\n\n\n\n<p>For this example we kept the input minimal, but the structure is open. For example we could include the user&#8217;s subscription tier, the request origin, a data classification label and anything our policy needs to make a more<strong> informed decision<\/strong>.<\/p>\n\n\n\n<p>The middleware doesn&#8217;t care what is in that input.  It simply calls <em>OPA <\/em>with it:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>var result = await opa.Evaluate&lt;Dictionary&lt;string, object?>>(policyPath, input);<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">result<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">await<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">opa<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Evaluate<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">Dictionary<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #81A1C1\">string<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">object<\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #ECEFF4\">&gt;&gt;<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">policyPath<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">input<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>And acts on what <em>OPA <\/em>says back. If <em>OPA <\/em>denies the call, we short circuit and return the denial message directly. The tool never runs. If it allows, execution continues as normal. And if <em>OPA <\/em>is unreachable for any reason, we fail closed and deny the action.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Writing the Policy<\/h3>\n\n\n\n<p>So how does <em>OPA <\/em>decide whether a tool call is allowed? That is where <em>Rego <\/em>comes in. <\/p>\n\n\n\n<p><em>Rego <\/em>is <em>OPA&#8217;s <\/em>policy language and it is declarative. Instead of writing step by step logic, we describe what is allowed and <em>OPA <\/em>evaluates it against the input we send.<\/p>\n\n\n\n<p>Policies in <em>Rego <\/em>are grouped by <strong>package <\/strong>declarations. This is not just a naming convention. The package name becomes part of the<strong> evaluation path<\/strong>. In our middleware we are going to pass &#8220;<em>product\/authorize<\/em>&#8221; as the policy path and that maps directly to a package &#8220;<em>product<\/em>&#8221; with an &#8220;<em>authorize<\/em>&#8221; rule. <\/p>\n\n\n\n<p>This is how different teams can own their <strong>domain specific policies<\/strong> independently. A product team manages package product, an orders team manages package orders and so on. In short, each package is <strong>isolated<\/strong>.<\/p>\n\n\n\n<p>It&#8217;s also worth mentioning that <em>OPA <\/em>treats policies as <strong>versioned <\/strong>artifacts that can be published to a shared <em>OCI <\/em>registry and updated <strong>independently <\/strong>of the application. The agent doesn&#8217;t need to be redeployed every time a rule changes. <em>OPA <\/em>can simply pull the latest policy bundle on a schedule and enforce whatever the current version says. This makes authorization <strong>predictable<\/strong>, <strong>centralized <\/strong>and easy to <strong>evolve <\/strong>over time.<\/p>\n\n\n\n<p>For this demo, we are going to load the policy from a local folder. So let&#8217;s create a &#8220;<em>product.rego<\/em>&#8221; file under a folder called &#8220;<em>Policies<\/em>&#8221; and add the following.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>package product\n\ndefault authorize := {\"allowed\": false, \"message\": \"upgrade required: this tool is available for premium users only\"}\n\nauthorize := {\"allowed\": true, \"message\": \"allowed\"} if {\n  input.tool == \"GetProductDetails\"\n  input.user.role == \"premiumUser\"\n}\n\nauthorize := {\"allowed\": true, \"message\": \"allowed\"} if {\n  input.tool == \"SearchProducts\"\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">package<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">product<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">default<\/span><span style=\"color: #D8DEE9FF\"> authorize <\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">allowed<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">false<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">message<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">upgrade required: this tool is available for premium users only<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">authorize <\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">allowed<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">true<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">message<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">allowed<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">}<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #D8DEE9\">input<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">tool<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">GetProductDetails<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #D8DEE9\">input<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">user<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">role<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">premiumUser<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">authorize <\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">allowed<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">true<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">message<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">allowed<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">}<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">if<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">  <\/span><span style=\"color: #D8DEE9\">input<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">tool<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">==<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">SearchProducts<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The &#8220;<em>package product<\/em>&#8221; declaration at the top scopes this policy to the &#8220;<em>product<\/em>&#8221; domain and &#8220;<em>authorize<\/em>&#8221; is the decision rule that the middleware evaluates.<\/p>\n\n\n\n<p>We start with a default rule that denies access and returns a message telling the user to upgrade. In <em>Rego<\/em>, defaults act as the fallback when no other rule matches, so everything is denied unless a more specific rule overrides it.<\/p>\n\n\n\n<p>The two rules below handle the allow cases. &#8220;<em>SearchProducts<\/em>&#8221; is open to everyone, no conditions on the user. &#8220;<em>GetProductDetails<\/em>&#8221; requires the user&#8217;s role to be &#8220;<em>premiumUser<\/em>&#8220;. If that condition is not met, the default kicks in and the denial message flows all the way back to the user.<\/p>\n\n\n\n<p>The important point here is that the denial message is defined in the policy, not in the middleware code. Whatever <em>OPA <\/em>returns, the middleware surfaces it directly. No extra logic is needed on the middleware side.<\/p>\n\n\n\n<p>I would say, this is what makes <em>OPA <\/em>such a practical fit for <strong><em>AI <\/em>governance<\/strong>. The policy is context-aware because it sees the tool and its arguments. It is user-aware because it gets the role and identity. And all of it lives <em>outside the application code<\/em>, in a place that can be <em>reviewed<\/em>, <em>tested <\/em>and <em>changed <\/em>without touching the agent itself.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Updating the Agent Prompt<\/h3>\n\n\n\n<p>Now that we have the tool and the policy, we also need to update the agent&#8217;s prompt. The agent needs to know that it has two tools available and how to choose between them. It also needs to know what to do when a tool call gets denied. <\/p>\n\n\n\n<p>Let&#8217;s edit the &#8220;<em>AgentPrompts<\/em>&#8221; class under &#8220;<em>Prompts<\/em>&#8221; folder and update the &#8220;<em>ProductSearch<\/em>&#8221; prompt as follows.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>namespace ProductSearchAgent.Prompts;\n\npublic static class AgentPrompts\n{\n    \/\/...\n\n    public const string ProductSearch = \"\"\"\n        You are a product search assistant for an electronics store.\n        Your job is to help users find products from our catalog.\n\n        You have two tools:\n        - SearchProducts: for general product searches and comparisons\n        - GetProductDetails: for detailed information about a specific product\n\n        When you receive a query:\n        1. If the user wants to browse or compare products, call SearchProducts with the user's search keywords (e.g. \"laptop\", \"gaming laptop\", \"dell xps\"). Use the words from the query. Do not replace them with generic category names like \"electronics\".\n        2. If the user asks for details about a specific product (e.g. \"tell me more about Dell XPS 15\"), call GetProductDetails with the product name\n        3. Summarize the results focusing on what the user asked for, including price and key specs\n        4. If the user mentioned a price constraint, filter results by that price range when summarizing\n\n        Rules:\n        - Be concise and focus on facts from the search results\n        - Do not make up products that were not returned by the search tool\n        - If no results are found, let the user know and suggest broadening their search\n        - Always mention price and key specs when presenting products\n        - If a tool call is denied by policy, relay the denial message to the user\n        \"\"\";\n}<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">namespace<\/span><span style=\"color: #D8DEE9FF\"> ProductSearchAgent<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9FF\">Prompts<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">public<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">static<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">class<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #8FBCBB\">AgentPrompts<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">    <\/span><span style=\"color: #616E88\">\/\/...<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #81A1C1\">public<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">const<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">string<\/span><span style=\"color: #D8DEE9FF\"> ProductSearch <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;&quot;&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A3BE8C\">        You are a product search assistant for an electronics store<\/span><span style=\"color: #D8DEE9\">.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">Your<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">job<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">is<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">to<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">help<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">users<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">find<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">products<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">from<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">our<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">catalog<\/span><span style=\"color: #ECEFF4\">.<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">You<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">have<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">two<\/span><span style=\"color: #D8DEE9FF\"> tools<\/span><span style=\"color: #81A1C1\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        - SearchProducts<\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> for general product searches and comparisons<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        - GetProductDetails<\/span><span style=\"color: #81A1C1\">:<\/span><span style=\"color: #D8DEE9FF\"> for detailed information about a specific product<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        When you receive a query<\/span><span style=\"color: #81A1C1\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">1.<\/span><span style=\"color: #D8DEE9FF\"> If the user wants to browse or compare products<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">call<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">SearchProducts<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">with<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">the<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">user<\/span><span style=\"color: #ECEFF4\">&#39;<\/span><span style=\"color: #A3BE8C\">s search keywords (e.g. &quot;laptop&quot;, &quot;gaming laptop&quot;, &quot;dell xps&quot;). Use the words from the query. Do not replace them with generic category names like &quot;electronics&quot;<\/span><span style=\"color: #D8DEE9\">.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">2.<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">If<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">the<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">user<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">asks<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">for<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">details<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">about<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">a<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">specific<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">product<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(<\/span><span style=\"color: #D8DEE9FF\">e.g. <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">tell me more about Dell XPS 15<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">),<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">call<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">GetProductDetails<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">with<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">the<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">product<\/span><span style=\"color: #D8DEE9FF\"> name<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">3.<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Summarize<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">the<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">results<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">focusing<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">on<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">what<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">the<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">user<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">asked<\/span><span style=\"color: #D8DEE9FF\"> for<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">including<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">price<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">and<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">key<\/span><span style=\"color: #D8DEE9FF\"> specs<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #B48EAD\">4.<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">If<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">the<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">user<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">mentioned<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">a<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">price<\/span><span style=\"color: #D8DEE9FF\"> constraint<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">filter<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">results<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">by<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">that<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">price<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">range<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">when<\/span><span style=\"color: #D8DEE9FF\"> summarizing<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        Rules<\/span><span style=\"color: #81A1C1\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        - Be concise and focus on facts from the search results<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        - Do not make up products that were not returned by the search tool<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        - If no results are found<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">let<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">the<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">user<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">know<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">and<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">suggest<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">broadening<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">their<\/span><span style=\"color: #D8DEE9FF\"> search<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Always<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">mention<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">price<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">and<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">key<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">specs<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">when<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">presenting<\/span><span style=\"color: #D8DEE9FF\"> products<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">If<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">a<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">tool<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">call<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">is<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">denied<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">by<\/span><span style=\"color: #D8DEE9FF\"> policy<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">relay<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">the<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">denial<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">message<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">to<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">the<\/span><span style=\"color: #D8DEE9FF\"> user<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #ECEFF4\">&quot;&quot;&quot;<\/span><span style=\"color: #D8DEE9\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Here, we simply added &#8220;<em>GetProductDetails<\/em>&#8221; as a second tool and described when the agent should use it. If the user asks for details about a specific product, the agent calls &#8220;<em>GetProductDetails<\/em>&#8221; with the product name to retrieve more information.<\/p>\n\n\n\n<p>The last rule is the one that matters for <em>OPA<\/em>. We tell the agent that if a tool call is denied by policy, it should relay the denial message back to the user. Without this, the agent might try to work around the denial or generate its own explanation. We want the message coming from the <em>Rego <\/em>policy to reach the user unchanged.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wiring Everything Together<\/h3>\n\n\n\n<p>At this point, we have the tool, the policy and the middleware. Now we need to connect them in &#8220;<em>Program.cs<\/em>&#8220;. <\/p>\n\n\n\n<p>Let&#8217;s start with the <em>OPA <\/em>client and the middleware pipeline.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>builder.Services.AddHttpContextAccessor();\n\nvar opaUrl = builder.Configuration&#91;\"OPA:Url\"&#93; ?? \"http:\/\/localhost:8181\";\nvar opaClient = new OpaClient(opaUrl);\n\nvar app = builder.Build();\n\nvar httpContextAccessor = app.Services.GetRequiredService&lt;IHttpContextAccessor>();\n\nvar chatClient = openAiClient.GetChatClient(chatDeployment)\n    .AsIChatClient()\n    .AsBuilder()\n    .Use(inner => new AgentLoggingMiddleware(inner))\n    .Use(inner => new OpaAuthorizationMiddleware(inner, opaClient, httpContextAccessor, \"product\/authorize\"))\n    .Build();<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9\">builder<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Services<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">AddHttpContextAccessor<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">opaUrl<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">builder<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Configuration<\/span><span style=\"color: #D8DEE9FF\">&#91;<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">OPA:Url<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">&#93; <\/span><span style=\"color: #81A1C1\">??<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">http:\/\/localhost:8181<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">opaClient<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">OpaClient<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">opaUrl<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">app<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">builder<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Build<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">httpContextAccessor<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">app<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Services<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetRequiredService<\/span><span style=\"color: #ECEFF4\">&lt;<\/span><span style=\"color: #D8DEE9FF\">IHttpContextAccessor<\/span><span style=\"color: #ECEFF4\">&gt;<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">chatClient<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">openAiClient<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">GetChatClient<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">chatDeployment<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">AsIChatClient<\/span><span style=\"color: #D8DEE9FF\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">AsBuilder<\/span><span style=\"color: #D8DEE9FF\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Use<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">inner<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">AgentLoggingMiddleware<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">inner<\/span><span style=\"color: #D8DEE9FF\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Use<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">inner<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=&gt;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">OpaAuthorizationMiddleware<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">inner<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">opaClient<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">httpContextAccessor<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">product\/authorize<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">))<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Build<\/span><span style=\"color: #D8DEE9FF\">()<\/span><span style=\"color: #81A1C1\">;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Here, we simply create an &#8220;<em>OpaClient<\/em>&#8221; pointing to an <em>OPA <\/em>server and pass it into the &#8220;<em>OpaAuthorizationMiddleware<\/em>&#8221; along with the &#8220;<em>IHttpContextAccessor<\/em>&#8221; and the policy path we defined earlier.<\/p>\n\n\n\n<p>The middleware is added to the same &#8220;<em>IChatClient<\/em>&#8221; pipeline we already had from the previous article. So nothing else in the pipeline changes.<\/p>\n\n\n\n<p>We also need to register &#8220;<em>GetProductDetails<\/em>&#8221; as a second tool on the search agent.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>var detailsTool = new ProductDetailsTool(searchClient);\n\nAIAgent searchAgent = new ChatClientAgent(chatClient, new ChatClientAgentOptions\n{\n    ChatOptions = new()\n    {\n        Instructions = AgentPrompts.ProductSearch,\n        Tools = &#91;AIFunctionFactory.Create(searchTool.SearchProducts), AIFunctionFactory.Create(detailsTool.GetProductDetails)&#93;\n    }\n});<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #81A1C1\">var<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">detailsTool<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">ProductDetailsTool<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">searchClient<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">AIAgent<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">searchAgent<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">ChatClientAgent<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">chatClient<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">ChatClientAgentOptions<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">ChatOptions<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\">()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">Instructions<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">AgentPrompts<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">ProductSearch<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #D8DEE9\">Tools<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> &#91;<\/span><span style=\"color: #D8DEE9\">AIFunctionFactory<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Create<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">searchTool<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">SearchProducts<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">AIFunctionFactory<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">Create<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">detailsTool<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">GetProductDetails<\/span><span style=\"color: #D8DEE9FF\">)&#93;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The last piece is getting the user&#8217;s role into the request. For the sake of simplicity, I&#8217;m going to simulate this with a query parameter instead of setting up a full authentication flow.<\/p>\n\n\n\n<p>Let&#8217;s add a &#8220;<em>Role<\/em>&#8221; field to our &#8220;<em>UserQuery<\/em>&#8221; model and set it as a claim on the &#8220;<em>HttpContext<\/em>&#8221; in the endpoint.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>public record UserQuery(string Query, string? Role = \"user\");\n\napp.MapGet(\"\/api\/search\", async (&#91;AsParameters&#93; UserQuery request, HttpContext httpContext) =>\n{\n    httpContext.User = new ClaimsPrincipal(new ClaimsIdentity(\n    &#91;\n        new Claim(ClaimTypes.NameIdentifier, \"user1234\"),\n        new Claim(ClaimTypes.Role, request.Role ?? \"user\")\n    &#93;));\n\n    \/\/ ... rest of the endpoint stays the same\n});<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9\">public<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">record<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">UserQuery<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">string<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Query<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">string<\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Role<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">user<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">);<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9\">app<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #88C0D0\">MapGet<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">\/api\/search<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">async<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">(&#91;<\/span><span style=\"color: #D8DEE9\">AsParameters<\/span><span style=\"color: #ECEFF4\">&#93;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">UserQuery<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">request<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">HttpContext<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">httpContext<\/span><span style=\"color: #ECEFF4\">)<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=&gt;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    <\/span><span style=\"color: #D8DEE9\">httpContext<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">User<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">ClaimsPrincipal<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">ClaimsIdentity<\/span><span style=\"color: #D8DEE9FF\">(<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    &#91;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Claim<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">ClaimTypes<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">NameIdentifier<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">user1234<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">        <\/span><span style=\"color: #81A1C1\">new<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">Claim<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">ClaimTypes<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Role<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">request<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">Role<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">??<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">user<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">    &#93;))<\/span><span style=\"color: #81A1C1\">;<\/span><\/span>\n<span class=\"line\"><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">    <\/span><span style=\"color: #616E88\">\/\/ ... rest of the endpoint stays the same<\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\">}<\/span><span style=\"color: #D8DEE9FF\">);<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Let&#8217;s See It In Action<\/h2>\n\n\n\n<p>Before we run the application, we need to spin up an <em>OPA <\/em>server. The easiest way is to use <em>Docker <\/em>and mount our policies folder directly.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>docker run -p 8181:8181 -v $(pwd)\/Policies:\/policies openpolicyagent\/opa:latest run --server --addr 0.0.0.0:8181 --set decision_logs.console=true \/policies<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9\">docker<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">run<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9\">p<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">8181<\/span><span style=\"color: #D8DEE9FF\">:<\/span><span style=\"color: #B48EAD\">8181<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">-<\/span><span style=\"color: #D8DEE9\">v<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #88C0D0\">$<\/span><span style=\"color: #D8DEE9FF\">(<\/span><span style=\"color: #D8DEE9\">pwd<\/span><span style=\"color: #D8DEE9FF\">)<\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #D8DEE9FF\">Policies<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #D8DEE9\">policies<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">openpolicyagent<\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #D8DEE9FF\">opa<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9\">latest<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">run<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">--<\/span><span style=\"color: #D8DEE9\">server<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">--<\/span><span style=\"color: #D8DEE9\">addr<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">0.0<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #B48EAD\">0.0<\/span><span style=\"color: #D8DEE9FF\">:<\/span><span style=\"color: #B48EAD\">8181<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">--<\/span><span style=\"color: #D8DEE9\">set<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">decision_logs<\/span><span style=\"color: #ECEFF4\">.<\/span><span style=\"color: #D8DEE9\">console<\/span><span style=\"color: #81A1C1\">=true<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #D8DEE9\">policies<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>This starts <em>OPA<\/em>, loads our &#8220;<em>product.rego<\/em>&#8221; from the mounted folder and enables decision logging so we can see every evaluation in the console. <\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Decision logs are also useful if you want to capture them for <strong>auditing <\/strong>or <strong>compliance <\/strong>later on. <\/p>\n<\/blockquote>\n\n\n\n<p>Now let&#8217;s start the application and try a couple of different scenarios.<\/p>\n\n\n\n<p>The first request is a regular product search. No role specified, so the user defaults to a standard user.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>GET \/api\/search?query=laptop under 1500 with at least 16gb ram<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9\">GET<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #D8DEE9\">api<\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #D8DEE9\">search<\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9\">query<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9\">laptop<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">under<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">1500<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">with<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">at<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">least<\/span><span style=\"color: #D8DEE9FF\"> 16<\/span><span style=\"color: #D8DEE9\">gb<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">ram<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The second request asks for details about a specific product, still as a standard user. This is the one <em>OPA <\/em>should <strong>deny<\/strong>.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>GET \/api\/search?query=\ufefftell me more about Dell XPS 15<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9\">GET<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #D8DEE9\">api<\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #D8DEE9\">search<\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9\">query<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\">\ufeff<\/span><span style=\"color: #D8DEE9\">tell<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">me<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">more<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">about<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Dell<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">XPS<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">15<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The third request is the same query but with &#8220;<em>role=premiumUser<\/em>&#8220;. This time <em>OPA <\/em>should <strong>allow <\/strong>it.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>GET \/api\/search?query=\ufefftell me more about Dell XPS 15&amp;role=premiumUser<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9\">GET<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #D8DEE9\">api<\/span><span style=\"color: #81A1C1\">\/<\/span><span style=\"color: #D8DEE9\">search<\/span><span style=\"color: #81A1C1\">?<\/span><span style=\"color: #D8DEE9\">query<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9FF\">\ufeff<\/span><span style=\"color: #D8DEE9\">tell<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">me<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">more<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">about<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">Dell<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #D8DEE9\">XPS<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #B48EAD\">15<\/span><span style=\"color: #81A1C1\">&amp;<\/span><span style=\"color: #D8DEE9\">role<\/span><span style=\"color: #81A1C1\">=<\/span><span style=\"color: #D8DEE9\">premiumUser<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/curl-logs-gokhan-gokalp.jpg\"><img decoding=\"async\" width=\"1024\" height=\"140\" data-src=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/curl-logs-gokhan-gokalp-1024x140.jpg\" alt=\"\" class=\"wp-image-5011 lazyload\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/curl-logs-gokhan-gokalp-1024x140.jpg 1024w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/curl-logs-gokhan-gokalp-300x41.jpg 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/curl-logs-gokhan-gokalp-768x105.jpg 768w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/curl-logs-gokhan-gokalp-1536x211.jpg 1536w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/curl-logs-gokhan-gokalp.jpg 1677w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/140;\" \/><\/a><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/product-search-agent-api-logs-gokhan-gokalp.jpg\"><img decoding=\"async\" width=\"1024\" height=\"573\" data-src=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/product-search-agent-api-logs-gokhan-gokalp-1024x573.jpg\" alt=\"\" class=\"wp-image-5012 lazyload\" data-srcset=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/product-search-agent-api-logs-gokhan-gokalp-1024x573.jpg 1024w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/product-search-agent-api-logs-gokhan-gokalp-300x168.jpg 300w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/product-search-agent-api-logs-gokhan-gokalp-768x430.jpg 768w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/product-search-agent-api-logs-gokhan-gokalp-1536x860.jpg 1536w, https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/product-search-agent-api-logs-gokhan-gokalp.jpg 1667w\" data-sizes=\"(max-width: 1024px) 100vw, 1024px\" src=\"data:image\/svg+xml;base64,PHN2ZyB3aWR0aD0iMSIgaGVpZ2h0PSIxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjwvc3ZnPg==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/573;\" \/><\/a><\/figure>\n\n\n\n<p>Now if we look at the <em>API <\/em>logs, the behavior is exactly what we expect. <\/p>\n\n\n\n<p>On the first request, the middleware logs allowed and the tool executes. On the second request, the middleware logs denied and the tool never runs. The agent simply returns the denial message directly to the user. On the third request, the middleware logs allowed again and the agent returned full product details.<\/p>\n\n\n\n<p>As we can see, there is no tool execution log between the denial and the final answer. The middleware stopped it before the tool ran. The tool was never invoked, that is the whole point.<\/p>\n\n\n\n<p><em>OPA <\/em>also logs every decision it makes. With decision logging enabled, we can see exactly what input was evaluated and what the result was.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#d8dee9ff;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\ufeff {\n   \"decision_id\": \"390533b0-cf03-4ded-9bc8-29201d77c871\",\n   \"timestamp\": \"2026-04-01T20:41:11.613282329Z\",\n   \"path\": \"product\/authorize\",\n   \"input\": {\n     \"tool\": \"SearchProducts\",\n     \"user\": { \"id\": \"user1234\", \"role\": \"user\" },\n     \"arguments\": { \"query\": \"laptop 16GB RAM under 1500\" }\n   },\n   \"result\": {\n     \"allowed\": true,\n     \"message\": \"allowed\"\n   }\n },\n \n \/\/ ...\n \n {\n   \"decision_id\": \"7965bd67-a4b8-4e09-93fb-776b64c016b8\",\n   \"timestamp\": \"2026-04-01T20:41:36.512711273Z\",\n   \"path\": \"product\/authorize\",\n   \"input\": {\n     \"tool\": \"GetProductDetails\",\n     \"user\": { \"id\": \"user1234\", \"role\": \"user\" },\n     \"arguments\": { \"productName\": \"Dell XPS 15\" }\n   },\n   \"result\": {\n     \"allowed\": false,\n     \"message\": \"upgrade required: this tool is available for premium users only\"\n   }\n }<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki nord\" style=\"background-color: #2e3440ff\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #D8DEE9FF\">\ufeff <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">decision_id<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">: <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">390533b0-cf03-4ded-9bc8-29201d77c871<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">timestamp<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">: <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">2026-04-01T20:41:11.613282329Z<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">path<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">: <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">product\/authorize<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">input<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">: <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">tool<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">SearchProducts<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">user<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">id<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">user1234<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">role<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">user<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">},<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">arguments<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">query<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">laptop 16GB RAM under 1500<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">},<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">result<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">: <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">allowed<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">true<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">message<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">allowed<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">},<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #ECEFF4\"> <\/span><span style=\"color: #616E88\">\/\/ ...<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">decision_id<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">: <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">7965bd67-a4b8-4e09-93fb-776b64c016b8<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">timestamp<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">: <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">2026-04-01T20:41:36.512711273Z<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">path<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">: <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">product\/authorize<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">input<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">: <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">tool<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">GetProductDetails<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">user<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">id<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">user1234<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">,<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">role<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">user<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">},<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">arguments<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">{<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">productName<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">Dell XPS 15<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">},<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">result<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #D8DEE9FF\">: <\/span><span style=\"color: #ECEFF4\">{<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">allowed<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #81A1C1\">false<\/span><span style=\"color: #ECEFF4\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">     <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">message<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #ECEFF4\">:<\/span><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">&quot;<\/span><span style=\"color: #A3BE8C\">upgrade required: this tool is available for premium users only<\/span><span style=\"color: #ECEFF4\">&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\">   <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span>\n<span class=\"line\"><span style=\"color: #D8DEE9FF\"> <\/span><span style=\"color: #ECEFF4\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Every decision here has a unique &#8220;<em>decision_id<\/em>&#8220;, a full record of the input and the result. In production, these logs can be shipped to any log aggregator. If something goes wrong, we can <em>reconstruct <\/em>the full sequence of agent decisions without touching application logs.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Wrapping Up<\/h1>\n\n\n\n<p>In this article, we added <em>OPA <\/em>to an existing <em>AI <\/em>agent workflow and used it to enforce <strong>business specific<\/strong> policies before any tool runs. <\/p>\n\n\n\n<p>By placing <em>OPA <\/em>between the agent and its tools, we get a clear enforcement point that is both <strong>context-aware<\/strong> and <strong>user-aware<\/strong> without coupling any of that logic to the application. The middleware simply forwards the input, <em>OPA <\/em>evaluates the policy and the agent behaves accordingly.<\/p>\n\n\n\n<p>Of course, role-based tool access is just one use case. The same pattern can apply to data classification in <em>RAG <\/em>where a policy decides which documents the agent is allowed to retrieve based on the user&#8217;s role. Or argument-level guardrails where the policy inspects the tool&#8217;s arguments and denies the call if they exceed certain limits. The middleware stays the same. Only the <em>Rego <\/em>policy changes.<\/p>\n\n\n\n<p>The real strength of this approach is that the rules live<strong> outside the code<\/strong>. Policies can evolve <strong>independently<\/strong>, teams can <strong>own <\/strong>their own domains and updates can be published as <strong>versioned <\/strong>artifacts without redeploying the agent.<\/p>\n\n\n\n<p>I found this approach much cleaner than embedding rules in prompts and it&#8217;s a solid starting point for thinking about governance in agentic systems. As far as I have seen, a lot of governance tooling today focuses on model behavior rather than business-specific enforcement. With <em>OPA<\/em>, those rules become <strong>explicit<\/strong>, <strong>reviewable <\/strong>and <strong>deterministic<\/strong>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em><strong>PS<\/strong><\/em>: This article was written by me and reviewed with the help of&nbsp;<em>AI<\/em>&nbsp;tools for grammar and clarity.<\/p>\n<\/blockquote>\n\n\n\n<h1 class=\"wp-block-heading\">References<\/h1>\n\n\n\n<p><em>https:\/\/open-policy-agent.github.io\/opa-aspnetcore\/index.html<\/em><br \/><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my&nbsp;previous article, I tried to show how we can build a simple but practical&nbsp;AI Agent&nbsp;using&nbsp;Microsoft Agent Framework&nbsp;(MAF) with deterministic routing and intelligent search. I also mentioned that I kept things simple on purpose and that in a production scenario there are other concerns that&#8230;<\/p>\n<div class=\"more-link-wrapper\"><a class=\"more-link\" href=\"https:\/\/gokhan-gokalp.com\/tr\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/\">Devam\u0131n\u0131 okuyun<span class=\"screen-reader-text\">Runtime Governance for AI Agents: Policy-as-Code with OPA<\/span><\/a><\/div>\n","protected":false},"author":1,"featured_media":5027,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,732],"tags":[753,749,733,750,551,739,666,659,726,734,688,748,752,751],"class_list":["post-4960","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net","category-ai-agents","tag-agentic-system","tag-ai","tag-ai-agent","tag-ai-governance","tag-azure","tag-azure-ai-search","tag-c","tag-dotnet","tag-llm","tag-microsoft-agent-framework","tag-opa","tag-open-policy-agent","tag-openai","tag-rego","entry"],"translation":{"provider":"WPGlobus","version":"3.0.2","language":"tr","enabled_languages":["en","tr"],"languages":{"en":{"title":true,"content":true,"excerpt":false},"tr":{"title":false,"content":false,"excerpt":false}}},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Runtime Governance for AI Agents: Policy-as-Code with OPA - G\u00f6khan G\u00f6kalp<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/\" \/>\n<meta property=\"og:locale\" content=\"tr_TR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Runtime Governance for AI Agents: Policy-as-Code with OPA - G\u00f6khan G\u00f6kalp\" \/>\n<meta property=\"og:url\" content=\"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/\" \/>\n<meta property=\"og:site_name\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-06T15:33:16+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-06T15:45:28+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/gokhan-gokalp-ai-agent-governance-opa.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"675\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Yazan:\" \/>\n\t<meta name=\"twitter:data1\" content=\"G\u00f6khan G\u00f6kalp\" \/>\n\t<meta name=\"twitter:label2\" content=\"Tahmini okuma s\u00fcresi\" \/>\n\t<meta name=\"twitter:data2\" content=\"13 dakika\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/\"},\"author\":{\"name\":\"G\u00f6khan G\u00f6kalp\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"headline\":\"Runtime Governance for AI Agents: Policy-as-Code with OPA\",\"datePublished\":\"2026-04-06T15:33:16+00:00\",\"dateModified\":\"2026-04-06T15:45:28+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/\"},\"wordCount\":2683,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"image\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/gokhan-gokalp-ai-agent-governance-opa.jpg\",\"keywords\":[\"agentic system\",\"ai\",\"ai agent\",\"ai governance\",\"azure\",\"azure ai search\",\"C#\",\"dotnet\",\"llm\",\"microsoft agent framework\",\"OPA\",\"open policy agent\",\"openai\",\"rego\"],\"articleSection\":[\".NET\",\"AI Agents\"],\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/\",\"name\":\"Runtime Governance for AI Agents: Policy-as-Code with OPA - G\u00f6khan G\u00f6kalp\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/gokhan-gokalp-ai-agent-governance-opa.jpg\",\"datePublished\":\"2026-04-06T15:33:16+00:00\",\"dateModified\":\"2026-04-06T15:45:28+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/#breadcrumb\"},\"inLanguage\":\"tr\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/#primaryimage\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/gokhan-gokalp-ai-agent-governance-opa.jpg\",\"contentUrl\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/uploads\\\/2026\\\/04\\\/gokhan-gokalp-ai-agent-governance-opa.jpg\",\"width\":1200,\"height\":675},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/runtime-governance-for-ai-agents-policy-as-code-with-opa\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/gokhan-gokalp.com\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Runtime Governance for AI Agents: Policy-as-Code with OPA\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#website\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/\",\"name\":\"G\u00f6khan G\u00f6kalp\",\"description\":\"C# &amp; Python lover\",\"publisher\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/gokhan-gokalp.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"tr\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/#\\\/schema\\\/person\\\/7e2a7fa98babd22a5fdae563c4b8cdbe\",\"name\":\"G\u00f6khan G\u00f6kalp\",\"pronouns\":\"he\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"tr\",\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1774960998\",\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1774960998\",\"contentUrl\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1774960998\",\"caption\":\"G\u00f6khan G\u00f6kalp\"},\"logo\":{\"@id\":\"https:\\\/\\\/gokhan-gokalp.com\\\/wp-content\\\/litespeed\\\/avatar\\\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1774960998\"},\"sameAs\":[\"https:\\\/\\\/gokhan-gokalp.com\"],\"url\":\"https:\\\/\\\/gokhan-gokalp.com\\\/tr\\\/author\\\/gok-gokalp\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Runtime Governance for AI Agents: Policy-as-Code with OPA - G\u00f6khan G\u00f6kalp","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/","og_locale":"tr_TR","og_type":"article","og_title":"Runtime Governance for AI Agents: Policy-as-Code with OPA - G\u00f6khan G\u00f6kalp","og_url":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/","og_site_name":"G\u00f6khan G\u00f6kalp","article_published_time":"2026-04-06T15:33:16+00:00","article_modified_time":"2026-04-06T15:45:28+00:00","og_image":[{"width":1200,"height":675,"url":"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/gokhan-gokalp-ai-agent-governance-opa.jpg","type":"image\/jpeg"}],"author":"G\u00f6khan G\u00f6kalp","twitter_card":"summary_large_image","twitter_misc":{"Yazan:":"G\u00f6khan G\u00f6kalp","Tahmini okuma s\u00fcresi":"13 dakika"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/#article","isPartOf":{"@id":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/"},"author":{"name":"G\u00f6khan G\u00f6kalp","@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"headline":"Runtime Governance for AI Agents: Policy-as-Code with OPA","datePublished":"2026-04-06T15:33:16+00:00","dateModified":"2026-04-06T15:45:28+00:00","mainEntityOfPage":{"@id":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/"},"wordCount":2683,"commentCount":0,"publisher":{"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"image":{"@id":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/#primaryimage"},"thumbnailUrl":"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/gokhan-gokalp-ai-agent-governance-opa.jpg","keywords":["agentic system","ai","ai agent","ai governance","azure","azure ai search","C#","dotnet","llm","microsoft agent framework","OPA","open policy agent","openai","rego"],"articleSection":[".NET","AI Agents"],"inLanguage":"tr","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/","url":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/","name":"Runtime Governance for AI Agents: Policy-as-Code with OPA - G\u00f6khan G\u00f6kalp","isPartOf":{"@id":"https:\/\/gokhan-gokalp.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/#primaryimage"},"image":{"@id":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/#primaryimage"},"thumbnailUrl":"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/gokhan-gokalp-ai-agent-governance-opa.jpg","datePublished":"2026-04-06T15:33:16+00:00","dateModified":"2026-04-06T15:45:28+00:00","breadcrumb":{"@id":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/#breadcrumb"},"inLanguage":"tr","potentialAction":[{"@type":"ReadAction","target":["https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/"]}]},{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/#primaryimage","url":"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/gokhan-gokalp-ai-agent-governance-opa.jpg","contentUrl":"https:\/\/gokhan-gokalp.com\/wp-content\/uploads\/2026\/04\/gokhan-gokalp-ai-agent-governance-opa.jpg","width":1200,"height":675},{"@type":"BreadcrumbList","@id":"https:\/\/gokhan-gokalp.com\/runtime-governance-for-ai-agents-policy-as-code-with-opa\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/gokhan-gokalp.com\/"},{"@type":"ListItem","position":2,"name":"Runtime Governance for AI Agents: Policy-as-Code with OPA"}]},{"@type":"WebSite","@id":"https:\/\/gokhan-gokalp.com\/#website","url":"https:\/\/gokhan-gokalp.com\/","name":"G\u00f6khan G\u00f6kalp","description":"C# &amp; Python lover","publisher":{"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/gokhan-gokalp.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"tr"},{"@type":["Person","Organization"],"@id":"https:\/\/gokhan-gokalp.com\/#\/schema\/person\/7e2a7fa98babd22a5fdae563c4b8cdbe","name":"G\u00f6khan G\u00f6kalp","pronouns":"he","image":{"@type":"ImageObject","inLanguage":"tr","@id":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1774960998","url":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1774960998","contentUrl":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1774960998","caption":"G\u00f6khan G\u00f6kalp"},"logo":{"@id":"https:\/\/gokhan-gokalp.com\/wp-content\/litespeed\/avatar\/e645f66b6264ced10d7b6d8b1f85509b.jpg?ver=1774960998"},"sameAs":["https:\/\/gokhan-gokalp.com"],"url":"https:\/\/gokhan-gokalp.com\/tr\/author\/gok-gokalp\/"}]}},"_links":{"self":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/4960","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/comments?post=4960"}],"version-history":[{"count":97,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/4960\/revisions"}],"predecessor-version":[{"id":5064,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/posts\/4960\/revisions\/5064"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/media\/5027"}],"wp:attachment":[{"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/media?parent=4960"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/categories?post=4960"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gokhan-gokalp.com\/tr\/wp-json\/wp\/v2\/tags?post=4960"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}