<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: shafivullashaik-tech-ops</title>
    <description>The latest articles on DEV Community by shafivullashaik-tech-ops (@shafivullashaiktechops).</description>
    <link>https://dev.to/shafivullashaiktechops</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4027275%2Fef27b5f2-a3b7-423b-8d84-7239c6a3f577.png</url>
      <title>DEV Community: shafivullashaik-tech-ops</title>
      <link>https://dev.to/shafivullashaiktechops</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shafivullashaiktechops"/>
    <language>en</language>
    <item>
      <title>How I Designed an AI SRE Agent to Cut LLM Costs by Up to 100x — With Architecture, Not Prompts</title>
      <dc:creator>shafivullashaik-tech-ops</dc:creator>
      <pubDate>Mon, 13 Jul 2026 13:14:44 +0000</pubDate>
      <link>https://dev.to/shafivullashaiktechops/how-i-designed-an-ai-sre-agent-to-cut-llm-costs-by-up-to-100x-with-architecture-not-prompts-40gm</link>
      <guid>https://dev.to/shafivullashaiktechops/how-i-designed-an-ai-sre-agent-to-cut-llm-costs-by-up-to-100x-with-architecture-not-prompts-40gm</guid>
      <description>&lt;p&gt;The fastest way to kill an AI project isn't a bad model. It's the first cloud bill.&lt;/p&gt;

&lt;p&gt;When I set out to build an AI SRE agent for Kubernetes — an agent that detects incidents, reads live cluster state, and drafts proposed fixes either proactively or when an engineer requests a diagnosis — the scariest number wasn't accuracy. It was this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens when an LLM sits inside a loop that runs every 5 minutes, 24/7, across a whole cluster?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do the math. In a naive architecture where every scan invokes the model, a scan every five minutes results in approximately &lt;strong&gt;8,640 LLM invocations per 30-day month&lt;/strong&gt;. Per cluster. Before a single user asks a single question. That's how a modest system quietly becomes a very expensive one — and how many AI-ops projects die in their first budget review.&lt;/p&gt;

&lt;p&gt;This article is about how I designed the invocation architecture so the model participates only at the steps where reasoning provides meaningful value. Almost none of it involves prompts. All of it involves architecture.&lt;/p&gt;

&lt;p&gt;Here's the shape of the difference, comparing the naive design (LLM inside the scan loop) with the final design::&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Naive design&lt;/th&gt;
&lt;th&gt;Final design&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Scheduled scans / month&lt;/td&gt;
&lt;td&gt;8,640&lt;/td&gt;
&lt;td&gt;8,640&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM calls from the scan loop&lt;/td&gt;
&lt;td&gt;8,640&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM calls per repeated incident&lt;/td&gt;
&lt;td&gt;1 per occurrence&lt;/td&gt;
&lt;td&gt;1 per fingerprint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When the model runs&lt;/td&gt;
&lt;td&gt;every scan&lt;/td&gt;
&lt;td&gt;once per materially distinct incident&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Depending on your incident volume, that's a reduction in LLM invocations of one to two orders of magnitude — the "up to 100x" in the title comes from the invocation count; your exact dollar savings will depend on model pricing and incident rate. The point of this article is the architecture that makes the reduction structural rather than accidental.&lt;/p&gt;

&lt;h2&gt;
  
  
  The system in one paragraph
&lt;/h2&gt;

&lt;p&gt;The agent is an AI SRE for Kubernetes, built on AWS: the agent runtime lives on &lt;strong&gt;Amazon Bedrock AgentCore&lt;/strong&gt; with Claude as the reasoning model, every capability is a governed tool behind an &lt;strong&gt;MCP gateway&lt;/strong&gt; (live K8s API reads, runbook retrieval from a Bedrock Knowledge Base), and detection runs on a serverless pipeline — &lt;strong&gt;EventBridge → Lambda → DynamoDB&lt;/strong&gt;. The agent's runtime identity has read-only Kubernetes permissions, every tool invocation is audited, and remediation is always human-approved: the agent produces the root cause, a YAML diff, and the exact &lt;code&gt;kubectl&lt;/code&gt; commands as text; an authenticated engineer reviews and applies them through the organization's existing access controls.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp4whd13mioj416jphfox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp4whd13mioj416jphfox.png" alt="An agentic AI SRE for Kubernetes — reference architecture" width="800" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that picture in mind, here are the five architectural decisions that shaped the cost model.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Rules detect, the LLM diagnoses
&lt;/h2&gt;

&lt;p&gt;The single most important decision in the whole system: &lt;strong&gt;the model never enters the detection loop.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The 5-minute scan is pure rule-based logic. It reads live cluster state and looks for well-known failure signatures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CrashLoopBackOff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OOMKilled&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ImagePullBackOff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NodeNotReady&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Known Kubernetes states like these can be detected deterministically — there's nothing for a model to interpret at the detection step. Detection is deterministic, auditable, and low-cost compared with invoking an LLM on every scan:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# detection Lambda — no LLM anywhere in this path
&lt;/span&gt;&lt;span class="n"&gt;DETECTION_RULES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CrashLoopBackOff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waiting&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;waiting&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CrashLoopBackOff&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;container_statuses&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OOMKilled&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminated&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;last_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;terminated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OOMKilled&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;container_statuses&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;# ... ImagePullBackOff, NodeNotReady
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cluster_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Incident&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;incidents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pod&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cluster_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pods&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;rule_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rule&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;DETECTION_RULES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;incidents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make_incident&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rule_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;incidents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A note on connectivity, because it's the first question any AWS/K8s architect asks: the detection Lambda authenticates to EKS through an IAM role mapped to a read-only Kubernetes identity (via EKS access entries / RBAC). For clusters with private API endpoints, the function runs in approved VPC subnets with security-group connectivity restricted to the EKS control-plane endpoint. In multi-cluster setups, each cluster gets its own scoped role — credentials are never shared across clusters.&lt;/p&gt;

&lt;p&gt;The LLM enters only where reasoning actually adds value: &lt;strong&gt;diagnosis&lt;/strong&gt;. &lt;em&gt;Why&lt;/em&gt; is this pod OOMKilled — bad memory limit, a leak after the last deploy, a node problem? That's a reasoning task, grounded in logs, events, and runbooks. That's worth a model invocation.&lt;/p&gt;

&lt;p&gt;The rule I'd carry into any agentic system: &lt;strong&gt;an LLM call inside a scheduled loop is a design smell.&lt;/strong&gt; If a rule can handle it, a rule should handle it. Every time I was tempted to add "intelligence" somewhere, the right question was: does this step need reasoning, or just reliability?&lt;/p&gt;

&lt;h2&gt;
  
  
  2. One incident fingerprint = one diagnosis
&lt;/h2&gt;

&lt;p&gt;Even after moving the LLM out of the loop, there's a second cost trap: &lt;strong&gt;the same incident firing over and over.&lt;/strong&gt; A pod crash-looping all night can generate the same incident 50+ times. Diagnosing it 50 times is 49 wasted invocations — the answer didn't change.&lt;/p&gt;

&lt;p&gt;The fix is incident fingerprinting with deduplication. Each incident gets a stable identity built from enough dimensions to distinguish &lt;em&gt;materially different&lt;/em&gt; failures — not just "same workload, same reason," because the same deployment can be OOMKilled today for a completely different cause than yesterday:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;incident_fingerprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cluster&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;workload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deployment_revision&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image_digest&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error_signature&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;unknown&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# normalized from logs/events
&lt;/span&gt;    &lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()[:&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The scan writes incidents to DynamoDB with a conditional write: if the fingerprint already exists, it just bumps the occurrence count and last-seen timestamp — no new diagnosis. If the fingerprint is &lt;strong&gt;new or materially changed&lt;/strong&gt;, that's when a diagnosis is triggered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_incident&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;fp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;incident_fingerprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;incidents_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fingerprint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Item&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;existing&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;is_fresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;existing&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;bump_occurrence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                      &lt;span class="c1"&gt;# 0 LLM calls
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt;

    &lt;span class="c1"&gt;# new or materially changed incident:
&lt;/span&gt;    &lt;span class="c1"&gt;# high-severity -&amp;gt; diagnose immediately (result waits for the engineer)
&lt;/span&gt;    &lt;span class="c1"&gt;# lower-severity -&amp;gt; diagnose on first human open
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;diagnosis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;invoke_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# 1 LLM call
&lt;/span&gt;        &lt;span class="nf"&gt;store_diagnosis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diagnosis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl_hours&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the lower-severity path, triggered when an engineer opens the incident in the UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;diagnose_on_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;incident&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_incident&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;diagnosis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;is_fresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;diagnosis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;             &lt;span class="c1"&gt;# cached — 0 LLM calls
&lt;/span&gt;
    &lt;span class="n"&gt;diagnosis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;invoke_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;incident&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;# first open — 1 LLM call
&lt;/span&gt;    &lt;span class="nf"&gt;store_diagnosis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fingerprint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;diagnosis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl_hours&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;diagnosis&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details that matter here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When the diagnosis runs.&lt;/strong&gt; High-severity incidents trigger one diagnosis on the &lt;em&gt;first occurrence&lt;/em&gt; of a new fingerprint — so by the time an engineer opens the incident, the root cause and proposed fix are already waiting. Lower-severity incidents are diagnosed on first human open. Either way, repeated occurrences reuse the cached result. Same crash looping 50 times with the same revision and error signature? &lt;strong&gt;One LLM call, not 50.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When the cache dies.&lt;/strong&gt; Diagnoses are cached for up to 24 hours, but invalidated earlier when the workload revision, image digest, configuration, or normalized error signature changes. A fixed TTL alone would happily serve yesterday's diagnosis for today's different failure — freshness has to be event-aware, not just time-based.&lt;/p&gt;

&lt;p&gt;What surprised me: this wasn't an optimization I bolted on. Deciding that &lt;em&gt;one fingerprint = one diagnosis&lt;/em&gt; ended up shaping the DynamoDB schema, the cost model, and even the UX (incidents show a "diagnosed" state with cache freshness). If I built this again, I'd design the fingerprinting and invalidation strategy on day one. &lt;strong&gt;Caching is product design, not optimization.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Right-size the model
&lt;/h2&gt;

&lt;p&gt;Most routine diagnoses do not require the largest available model. They need a &lt;strong&gt;fast, cost-efficient model with good tool-calling&lt;/strong&gt;, grounded in live cluster data and runbooks.&lt;/p&gt;

&lt;p&gt;This is the part most cost advice gets backwards. The instinct is "harder problem → bigger model." But for operational diagnosis, the biggest quality lever isn't model size — it's &lt;strong&gt;grounding&lt;/strong&gt;. A smaller, well-grounded model that can see this pod's actual events, the last N log lines, and the matching runbook can outperform a larger model that lacks relevant operational context.&lt;/p&gt;

&lt;p&gt;Garbage context in, confident garbage out — at any model size.&lt;/p&gt;

&lt;p&gt;Save the frontier model for where reasoning depth genuinely pays: complex multi-service correlation, ambiguous novel failures. For the common case — "why is this pod OOMKilled" — a well-grounded mid-tier model is cheaper and often just as good or better.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Serverless orchestration and managed agent runtime
&lt;/h2&gt;

&lt;p&gt;The runtime cost model matters as much as the invocation cost model.&lt;/p&gt;

&lt;p&gt;To be precise about scope: the &lt;em&gt;detection, incident-storage, and diagnosis-orchestration path&lt;/em&gt; is what's serverless — EventBridge, Lambda, DynamoDB, plus a managed agent runtime (Bedrock AgentCore). The Kubernetes cluster itself, networking, and observability stack obviously exist independently; the point is that the &lt;strong&gt;AI control plane adds no always-on compute of its own&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No idle compute&lt;/strong&gt; for the agent between incidents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No GPU sitting warm&lt;/strong&gt; waiting for a question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No dedicated "AI infrastructure" cluster&lt;/strong&gt; to justify, patch, and pay for at 3 a.m.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AI layer's cost scales with &lt;strong&gt;incidents&lt;/strong&gt;, not with &lt;strong&gt;time&lt;/strong&gt;. A healthy cluster incurs only the relatively small cost of scheduled checks, API access, logging, and incident-state storage. That's exactly the cost curve you want for an ops tool: the expensive weeks are the weeks it earned its keep.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Tools return data, not dumps
&lt;/h2&gt;

&lt;p&gt;The quietest cost (and quality) killer: tool responses that dump everything.&lt;/p&gt;

&lt;p&gt;Each tool behind the MCP gateway returns &lt;strong&gt;only what the diagnosis needs&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;this pod's&lt;/em&gt; events — not the namespace's&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;last N&lt;/em&gt; log lines — not the full log stream&lt;/li&gt;
&lt;li&gt;the &lt;em&gt;relevant&lt;/em&gt; runbook section — not the whole knowledge base
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tool: k8s_data_lookup — scoped by design
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_pod_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pod_spec&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;get_pod_spec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;get_events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;        &lt;span class="c1"&gt;# this pod only
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;logs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;get_logs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tail_lines&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;      &lt;span class="c1"&gt;# last 100 lines
&lt;/span&gt;        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;restarts&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;get_restart_summary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pod&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Token discipline at the tool layer is invisible in demos and decisive in production. It cuts input tokens per invocation dramatically — and, as a bonus, it made diagnosis &lt;em&gt;quality&lt;/em&gt; go up, because the model stopped drowning in irrelevant context. I got more quality improvement from making tools return clean, scoped data than from any prompt rewrite I ever did.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trust boundary (because someone will ask)
&lt;/h2&gt;

&lt;p&gt;A cost article isn't the place for the full security design, but two points are inseparable from the architecture above:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The agent's identity is read-only.&lt;/strong&gt; Its runtime identity maps to a read-only ClusterRole. The suggested remediation — YAML diff, &lt;code&gt;kubectl&lt;/code&gt; commands — is rendered as &lt;em&gt;text only&lt;/em&gt;. Any execution happens separately, by an authenticated engineer, through the organization's existing access controls and approval process. (For stronger governance, generated commands can additionally be validated against an allowlist or policy engine before display.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Everything is audited.&lt;/strong&gt; Every tool invocation is recorded with the requesting identity, incident fingerprint, tool name, parameters, timestamp, and result status — searchable through the observability platform and retained in S3 per the audit-retention policy, with secrets and sensitive log content redacted before storage. When someone asks "what did the AI actually do?", the answer is a log, not a shrug.&lt;/p&gt;

&lt;h2&gt;
  
  
  The principle underneath all five
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Put the LLM at the point of maximum value — and nowhere else.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detection? Rules. (Reliability, not reasoning.)&lt;/li&gt;
&lt;li&gt;Repeated incidents? Fingerprint + cache. (The answer didn't change.)&lt;/li&gt;
&lt;li&gt;Routine diagnosis? Smaller grounded model. (Grounding beats size.)&lt;/li&gt;
&lt;li&gt;Idle time? Serverless AI control plane. (Pay for incidents, not hours.)&lt;/li&gt;
&lt;li&gt;Context? Scoped tools. (Tokens are a budget.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a system where the expensive component — the model — runs once per materially distinct incident, sees only the context it needs, and never repeats work it already did.&lt;/p&gt;

&lt;p&gt;Most LLM cost advice is about prompt compression and model discounts. Those are real, but they're 2x levers. In my experience, the largest savings often live one level up — &lt;strong&gt;in the invocation architecture, in deciding when the model is allowed to run at all.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What this looks like in practice
&lt;/h2&gt;

&lt;p&gt;The full flow, end to end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EventBridge scheduled scan (every 5 min)
        ↓
Lambda performs deterministic detection          ← no LLM
        ↓
Normalize + fingerprint incident
        ↓
DynamoDB conditional write
        ↓
New or materially changed incident?
   ├── No  → bump occurrence count + last-seen   ← no LLM
   └── Yes → diagnosis (by severity or on human open)
                    ↓
            Retrieve scoped evidence (tools)
                    ↓
            Retrieve relevant runbook sections
                    ↓
            Invoke appropriately sized model      ← the one LLM call
                    ↓
            Store diagnosis + evidence references
                    ↓
            Engineer reviews proposed remediation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the time an engineer opens a high-severity incident, the root cause and a proposed fix — YAML diff plus exact &lt;code&gt;kubectl&lt;/code&gt; commands — are already waiting, generated from live cluster data and runbooks, on a read-only credential path, fully audited. The agent turns a repetitive 30-minute investigation into a focused human review. And the bill stays boring.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This is part of a series on building a production-grade agentic AI SRE — upcoming posts cover the read-only trust architecture (why my agent can diagnose anything but can't restart a single pod) and the lessons learned building it. Follow me here or on &lt;a href="https://www.linkedin.com/in/shafivulla-shaik-b4b520120/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt; if you want the rest.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How have LLM costs surprised your team — and did the biggest issue come from prompts, excessive context, or the invocation loop?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>kubernetes</category>
      <category>aws</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
