<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://autoshiftops.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://autoshiftops.com/" rel="alternate" type="text/html" /><updated>2026-07-17T20:37:22+00:00</updated><id>https://autoshiftops.com/feed.xml</id><title type="html">AutoShiftOps</title><subtitle>Sajja — Application Architect with 13+ years across cloud infrastructure, DevOps, and AI-powered operations. Creator of QueryTuner.</subtitle><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><entry><title type="html">🧠 CPU-Only LLM Inference Explained: Quantization, GGUF, and llama.cpp</title><link href="https://autoshiftops.com/blog/2025/12/28/cpu-only-llm-inference-explained/" rel="alternate" type="text/html" title="🧠 CPU-Only LLM Inference Explained: Quantization, GGUF, and llama.cpp" /><published>2025-12-28T00:00:00+00:00</published><updated>2025-12-28T00:00:00+00:00</updated><id>https://autoshiftops.com/blog/2025/12/28/cpu-only-llm-inference-explained</id><content type="html" xml:base="https://autoshiftops.com/blog/2025/12/28/cpu-only-llm-inference-explained/"><![CDATA[<h1 id="running-large-language-models-on-cpu-a-practical-guide-to-cpu-only-llm-inference">Running Large Language Models on CPU: A Practical Guide to CPU-Only LLM Inference</h1>

<blockquote>
  <p><em>No GPUs. No cloud scaling. Just Linux, CPUs, and solid systems engineering.</em></p>
</blockquote>

<p>Large Language Models (LLMs) are often associated with expensive GPUs and cloud infrastructure. However, for <strong>development, research, privacy-sensitive environments, and cost-controlled setups</strong>, running LLMs <strong>entirely on CPU</strong> is not only possible — it’s practical.</p>

<p>This post is a <strong>complete, end-to-end guide</strong> to running <strong>large models (13B–27B+) on CPU-only hardware</strong>, using modern quantization techniques and efficient runtimes like <code class="language-plaintext highlighter-rouge">llama.cpp</code>.</p>

<p>By the end, you’ll understand:</p>
<ul>
  <li>Why CPU-based LLMs exist</li>
  <li>What <em>quantization</em> and <em>inference</em> really mean</li>
  <li>How to set up a Linux system for CPU inference</li>
  <li>How large models can realistically run without GPUs</li>
  <li>A reproducible workflow you can use immediately</li>
</ul>

<hr />

<h2 id="why-run-llms-on-cpu">Why Run LLMs on CPU?</h2>

<p>Let’s address the obvious question first.</p>

<h3 id="if-gpus-are-faster-why-bother-with-cpu">If GPUs are faster, why bother with CPU?</h3>

<p>Because <strong>speed is not the only constraint</strong>.</p>

<p>CPU-based LLMs are ideal when you need:</p>

<ul>
  <li><strong>Low-cost experimentation</strong> (no $10K GPUs)</li>
  <li><strong>Offline or air-gapped environments</strong></li>
  <li><strong>Privacy &amp; compliance</strong> (healthcare, finance, legal)</li>
  <li><strong>On-prem developer tooling</strong></li>
  <li><strong>Edge or internal R&amp;D systems</strong></li>
  <li><strong>Predictable, reproducible environments</strong></li>
</ul>

<p>For many teams, <strong>“fast enough” beats “fastest possible.”</strong></p>

<hr />

<h2 id="core-concepts-no-ml-background-required">Core Concepts (No ML Background Required)</h2>

<h3 id="1-what-is-inference">1. What Is Inference?</h3>

<p><strong>Inference</strong> is the act of <em>using</em> a trained model to generate text.</p>

<ul>
  <li>Training = learning weights (expensive, GPU-heavy)</li>
  <li>Inference = reading weights + predicting tokens (much cheaper)</li>
</ul>

<p>This guide is <strong>only about inference</strong>.</p>

<hr />

<h3 id="2-why-large-models-dont-fit-on-cpu-by-default">2. Why Large Models Don’t Fit on CPU (By Default)</h3>

<p>A 27B model in FP16 format:</p>
<ul>
  <li>27B parameters × 2 bytes ≈ 54 GB RAM</li>
</ul>

<p>That’s before runtime overhead.</p>

<p>This is why <strong>quantization</strong> exists.</p>

<hr />

<h2 id="quantization-explained-simply">Quantization Explained (Simply)</h2>

<h3 id="what-is-quantization">What Is Quantization?</h3>

<p>Quantization reduces the <strong>precision</strong> of model weights to save memory and speed up inference.</p>

<table>
  <thead>
    <tr>
      <th>Format</th>
      <th>Memory</th>
      <th>Quality</th>
      <th>Use Case</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>FP16</td>
      <td>Very High</td>
      <td>Best</td>
      <td>Training / GPUs</td>
    </tr>
    <tr>
      <td>Q6</td>
      <td>Medium</td>
      <td>Very Good</td>
      <td>CPU, high quality</td>
    </tr>
    <tr>
      <td>Q5</td>
      <td>Lower</td>
      <td>Good</td>
      <td>CPU, balanced</td>
    </tr>
    <tr>
      <td>Q4</td>
      <td>Lowest</td>
      <td>Acceptable</td>
      <td>CPU, fastest</td>
    </tr>
  </tbody>
</table>

<p>Quantization:</p>
<ul>
  <li>Reduces RAM usage by <strong>4–6×</strong></li>
  <li>Improves CPU cache efficiency</li>
  <li>Makes CPU inference viable</li>
</ul>

<hr />

<h2 id="why-gguf-format">Why GGUF Format?</h2>

<p>Modern CPU runtimes use <strong>GGUF</strong>, a binary format that:</p>

<ul>
  <li>Packs weights + tokenizer together</li>
  <li>Is optimized for memory-mapped loading</li>
  <li>Avoids Python overhead</li>
  <li>Works directly with C/C++ inference engines</li>
</ul>

<p>Think of GGUF as:</p>
<blockquote>
  <p><em>“Docker images for LLM weights.”</em></p>
</blockquote>

<hr />

<h2 id="the-cpu-inference-stack">The CPU Inference Stack</h2>

<p>Here’s the <strong>minimal, production-grade stack</strong>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Raw Model Weights (HF / Google)
↓
Conversion → GGUF
↓
Quantization (Q4/Q5/Q6)
↓
CPU Runtime (llama.cpp)
↓
Optimized Linux Execution
</code></pre></div></div>

<p>No PyTorch runtime is needed at inference time.</p>

<hr />

<h2 id="system-requirements-realistic">System Requirements (Realistic)</h2>

<h3 id="minimum-recommended-specs">Minimum Recommended Specs</h3>

<ul>
  <li>Ubuntu 22.04+</li>
  <li>x86_64 CPU with <strong>AVX2</strong> (AVX512 preferred)</li>
  <li><strong>128 GB RAM</strong> for 27B models</li>
  <li>16–32 CPU cores</li>
  <li>SSD storage</li>
</ul>

<blockquote>
  <p>Tip: CPUs with high memory bandwidth matter more than clock speed.</p>
</blockquote>

<hr />

<h2 id="step-by-step-quick-start">Step-by-Step Quick Start</h2>

<h3 id="1-install-system-dependencies">1. Install System Dependencies</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update <span class="o">&amp;&amp;</span> <span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> <span class="se">\</span>
  build-essential cmake git wget <span class="se">\</span>
  python3 python3-venv python3-pip <span class="se">\</span>
  numactl htop perf libopenblas-dev
</code></pre></div></div>

<h3 id="2-build-the-cpu-inference-engine">2. Build the CPU Inference Engine</h3>

<p>llama.cpp is the gold standard for CPU LLM inference.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp
make LLAMA_AVX2=1 LLAMA_AVX512=1 LLAMA_BLAS=1 -j$(nproc)
</code></pre></div></div>

<p>Verify CPU features:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>lscpu | grep AVX
</code></pre></div></div>

<h3 id="3-download-model-weights">3. Download Model Weights</h3>

<p>Download official model weights (example shown):</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>mkdir -p models/raw
cd models/raw
wget &lt;official-model-url&gt;
</code></pre></div></div>

<p>Always respect model licenses.</p>

<h3 id="4-convert-to-gguf">4. Convert to GGUF</h3>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python llama.cpp/tools/convert-hf-to-gguf.py \
  models/raw/model.safetensors \
  models/gguf/model.gguf
</code></pre></div></div>

<p>This step:</p>

<ul>
  <li>Aligns tokenizer</li>
  <li>Normalizes weight layout</li>
  <li>Ensures runtime compatibility</li>
</ul>

<h3 id="5-quantize-the-model">5. Quantize the Model</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./quantize models/gguf/model.gguf models/quantized/model-q4.gguf q4_k_m
./quantize models/gguf/model.gguf models/quantized/model-q5.gguf q5_k_m
./quantize models/gguf/model.gguf models/quantized/model-q6.gguf q6_k
</code></pre></div></div>

<p>Start with Q4. Move up if quality is insufficient.</p>

<h3 id="6-run-inference-optimized">6. Run Inference (Optimized)</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>numactl --cpunodebind=0 --membind=0 \
./main -m models/quantized/model-q4.gguf \
--threads 16 \
-p "Explain CPU-based LLM inference"
</code></pre></div></div>

<p>Tune:</p>

<ul>
  <li>
    <p>–threads</p>
  </li>
  <li>
    <p>NUMA binding</p>
  </li>
  <li>
    <p>Batch size</p>
  </li>
  <li>
    <p>Performance Expectations (Honest)</p>
  </li>
</ul>

<p>For a 27B model on CPU:</p>

<table>
  <thead>
    <tr>
      <th>Quantization</th>
      <th>Tokens/sec</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Q4</td>
      <td>4–7 t/s</td>
    </tr>
    <tr>
      <td>Q5</td>
      <td>3–5 t/s</td>
    </tr>
    <tr>
      <td>Q6</td>
      <td>2–4 t/s</td>
    </tr>
  </tbody>
</table>

<p>This is not chatGPT speed — but it is:</p>

<ul>
  <li>
    <p>Stable</p>
  </li>
  <li>
    <p>Cheap</p>
  </li>
  <li>
    <p>Private</p>
  </li>
  <li>
    <p>Predictable</p>
  </li>
</ul>

<h2 id="common-pitfalls">Common Pitfalls</h2>
<p>❌ Tokenizer mismatch</p>

<ul>
  <li>Always convert with the correct tokenizer.</li>
</ul>

<p>❌ Running out of memory</p>

<ul>
  <li>Use lower quantization or fewer threads.</li>
</ul>

<p>❌ Poor performance</p>

<p>Check:</p>

<ul>
  <li>
    <p>AVX support</p>
  </li>
  <li>
    <p>NUMA locality</p>
  </li>
  <li>
    <p>BLAS enabled</p>
  </li>
</ul>

<h2 id="final-thoughts">Final Thoughts</h2>

<p>CPU-based LLM inference is not a workaround — it’s a legitimate engineering choice.</p>

<p>With the right:</p>

<ul>
  <li>Quantization</li>
  <li>Runtime</li>
  <li>Linux tuning</li>
  <li>Documentation</li>
</ul>

<p>You can run surprisingly large models on commodity hardware.</p>

<p>And most importantly — you understand exactly how it works.</p>

<h2 id="further-reading">Further Reading</h2>

<ul>
  <li>llama.cpp GitHub</li>
  <li>GGUF specification</li>
  <li>CPU vectorization (AVX2 / AVX512)</li>
  <li>NUMA performance tuning</li>
</ul>]]></content><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><category term="DevOps" /><category term="AI" /><category term="LLMs" /><category term="Machine Learning" /><category term="LLMs" /><category term="CPU inference" /><category term="quantization" /><category term="llama.cpp" /><category term="large language models" /><category term="AI" /><category term="machine learning" /><summary type="html"><![CDATA[A comprehensive guide to running large language models (LLMs) entirely on CPU hardware using quantization techniques and efficient runtimes like llama.cpp.]]></summary></entry><entry><title type="html">🤖 Best Practices for Developing AI Agents</title><link href="https://autoshiftops.com/blog/2025/12/27/best-practices-for-developing-ai-agents/" rel="alternate" type="text/html" title="🤖 Best Practices for Developing AI Agents" /><published>2025-12-27T00:00:00+00:00</published><updated>2025-12-27T00:00:00+00:00</updated><id>https://autoshiftops.com/blog/2025/12/27/best-practices-for-developing-ai-agents</id><content type="html" xml:base="https://autoshiftops.com/blog/2025/12/27/best-practices-for-developing-ai-agents/"><![CDATA[<p>Here’s a comprehensive guide for developing robust, reliable AI agents:</p>

<h2 id="1-design--architecture">1. Design &amp; Architecture</h2>
<h3 id="11-clear-agent-purpose">1.1 Clear Agent Purpose</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ✅ Good: Clear, single responsibility
</span><span class="k">class</span> <span class="nc">IncidentAnalysisAgent</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s">
    Agent responsible for:
    - Fetching incidents from API
    - Analyzing incident data
    - Extracting actionable insights
    - Providing recommendations
    </span><span class="sh">"""</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">api_client</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">api_client</span> <span class="o">=</span> <span class="n">api_client</span>
        <span class="n">self</span><span class="p">.</span><span class="n">config</span> <span class="o">=</span> <span class="n">config</span>
        <span class="n">self</span><span class="p">.</span><span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nf">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">analyze_incidents</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s">Analyze single incident</span><span class="sh">"""</span>
        <span class="k">pass</span>
    
    <span class="k">def</span> <span class="nf">batch_analyze</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">incidents</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s">Analyze multiple incidents</span><span class="sh">"""</span>
        <span class="k">pass</span>
</code></pre></div></div>

<h3 id="12-separation-of-concerns">1.2 Separation of Concerns</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ✅ Good: Separate components
</span><span class="k">class</span> <span class="nc">IncidentAgent</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">fetcher</span><span class="p">,</span> <span class="n">parser</span><span class="p">,</span> <span class="n">analyzer</span><span class="p">,</span> <span class="n">recommender</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">fetcher</span> <span class="o">=</span> <span class="n">fetcher</span>          <span class="c1"># Fetch data  
</span>        <span class="n">self</span><span class="p">.</span><span class="n">parser</span> <span class="o">=</span> <span class="n">parser</span>            <span class="c1"># Parse data
</span>        <span class="n">self</span><span class="p">.</span><span class="n">analyzer</span> <span class="o">=</span> <span class="n">analyzer</span>        <span class="c1"># Analyze data
</span>        <span class="n">self</span><span class="p">.</span><span class="n">writer</span> <span class="o">=</span> <span class="n">writer</span>            <span class="c1"># Write results
</span>    
<span class="c1"># ❌ Bad: Everything mixed together
</span><span class="k">class</span> <span class="nc">MonolithicAgent</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">api_client</span><span class="p">):</span>
        <span class="c1"># Fetch, parse, analyze, and write all in one class
</span>        <span class="k">pass</span>
</code></pre></div></div>

<h2 id="2-error-handling--resilience">2. Error Handling &amp; Resilience</h2>
<h3 id="21-comprehensive-error-handling">2.1 Comprehensive Error Handling</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">logging</span>
<span class="kn">from</span> <span class="n">typing</span> <span class="kn">import</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">Any</span>
<span class="kn">from</span> <span class="n">functools</span> <span class="kn">import</span> <span class="n">wraps</span>
<span class="kn">import</span> <span class="n">time</span>

<span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nf">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">AIAgent</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">max_retries</span><span class="o">=</span><span class="mi">3</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">max_retries</span> <span class="o">=</span> <span class="n">max_retries</span>
        <span class="n">self</span><span class="p">.</span><span class="n">timeout</span> <span class="o">=</span> <span class="n">timeout</span>
    
    <span class="k">def</span> <span class="nf">retry_with_backoff</span><span class="p">(</span><span class="n">func</span><span class="p">):</span>
        <span class="c1">### Decorator for retrying with exponential backoff ###
</span>        <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
        <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
            <span class="k">for</span> <span class="n">attempt</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">max_retries</span><span class="p">):</span>
                <span class="k">try</span><span class="p">:</span>
                    <span class="n">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Attempt </span><span class="si">{</span><span class="n">attempt</span> <span class="o">+</span> <span class="mi">1</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">self</span><span class="p">.</span><span class="n">max_retries</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
                    <span class="k">return</span> <span class="nf">func</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
                
                <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                    <span class="k">if</span> <span class="n">attempt</span> <span class="o">==</span> <span class="n">self</span><span class="p">.</span><span class="n">max_retries</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span>
                        <span class="n">logger</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">All </span><span class="si">{</span><span class="n">self</span><span class="p">.</span><span class="n">max_retries</span><span class="si">}</span><span class="s"> attempts failed: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
                        <span class="k">raise</span>
                    
                    <span class="n">wait_time</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">**</span> <span class="n">attempt</span> <span class="c1"># Exponential backoff
</span>                    <span class="n">logger</span><span class="p">.</span><span class="nf">warning</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Attempt </span><span class="si">{</span><span class="n">attempt</span> <span class="o">+</span> <span class="mi">1</span><span class="si">}</span><span class="s"> failed. Retrying in </span><span class="si">{</span><span class="n">wait_time</span><span class="si">}</span><span class="s">s: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
                    <span class="n">time</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="n">wait_time</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">wrapper</span>
    
    <span class="nd">@retry_with_backoff</span>
    <span class="k">def</span> <span class="nf">fetch_data</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">url</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s">Fetch data from a URL with retries and timeout</span><span class="sh">"""</span>
        <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="n">self</span><span class="p">.</span><span class="n">timeout</span><span class="p">)</span>
        <span class="n">response</span><span class="p">.</span><span class="nf">raise_for_status</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">response</span><span class="p">.</span><span class="nf">json</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="22-graceful-degradation">2.2 Graceful Degradation</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># ✅ Good: Provide fallback when API fails
</span><span class="k">class</span> <span class="nc">RobustIncidentAgent</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">get_incidents</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="nf">fetch_from_primary_api</span><span class="p">()</span>
        <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">warning</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Primary API failed: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s">. Falling back to secondary API.</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="nf">fetch_from_cache</span><span class="p">()</span>      <span class="c1"># Fallback to cached data
</span>    
    <span class="k">def</span> <span class="nf">analyze_with_fallback</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">incident</span><span class="p">):</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="nf">advanced_analysis</span><span class="p">(</span><span class="n">incident</span><span class="p">)</span>
        <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">warning</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Advanced analysis failed: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="s">. Using basic analysis.</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="nf">basic_analysis</span><span class="p">(</span><span class="n">incident</span><span class="p">)</span> <span class="c1"># Fallback to basic analysis
</span></code></pre></div></div>

<h2 id="3-input-validation--simulation">3. Input Validation &amp; Simulation</h2>
<h3 id="31-strict-input-validation">3.1 Strict Input Validation</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">pydantic</span> <span class="kn">import</span> <span class="n">BaseModel</span><span class="p">,</span> <span class="n">ValidationError</span><span class="p">,</span> <span class="n">Field</span>
<span class="kn">from</span> <span class="n">typing</span> <span class="kn">import</span> <span class="n">Optional</span>

<span class="k">class</span> <span class="nc">IncidentData</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
    <span class="c1">### Validate incident data structure ###
</span>    <span class="n">incident_id</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">target_name</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">severity</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">timestamp</span><span class="p">:</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]</span> <span class="o">=</span> <span class="bp">None</span>

    <span class="nd">@validator</span><span class="p">(</span><span class="sh">'</span><span class="s">incident_id</span><span class="sh">'</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">incident_id_not_empty</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">v</span><span class="p">):</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">v</span> <span class="ow">and</span> <span class="nf">len</span><span class="p">(</span><span class="n">v</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nc">ValueError</span><span class="p">(</span><span class="sh">'</span><span class="s">incident_id must not be empty</span><span class="sh">'</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">v</span>
    
    <span class="nd">@validator</span><span class="p">(</span><span class="sh">'</span><span class="s">severity</span><span class="sh">'</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">severity_valid</span><span class="p">(</span><span class="n">cls</span><span class="p">,</span> <span class="n">v</span><span class="p">):</span>
        <span class="n">valid_severities</span> <span class="o">=</span> <span class="p">{</span><span class="sh">'</span><span class="s">low</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">medium</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">high</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">critical</span><span class="sh">'</span><span class="p">}</span>
        <span class="k">if</span> <span class="n">v</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">valid_severities</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nc">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="sh">'</span><span class="s">severity must be one of </span><span class="si">{</span><span class="n">valid_severities</span><span class="si">}</span><span class="sh">'</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">v</span>
    
<span class="k">class</span> <span class="nc">IncidentAgent</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">process_incident</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="nb">dict</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">bool</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="c1"># Validate data structure
</span>            <span class="n">incident</span> <span class="o">=</span> <span class="nc">IncidentData</span><span class="p">(</span><span class="o">**</span><span class="n">data</span><span class="p">)</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Processing incident: </span><span class="si">{</span><span class="n">incident</span><span class="p">.</span><span class="n">incident_id</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">return</span> <span class="bp">True</span>
        <span class="k">except</span> <span class="n">ValidationError</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Invalid incident data: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">return</span> <span class="bp">False</span>
</code></pre></div></div>

<h3 id="32-sanitize-external-inputs">3.2 Sanitize External Inputs</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">html</span>
<span class="kn">import</span> <span class="n">re</span>
<span class="kn">from</span> <span class="n">urllib.parse</span> <span class="kn">import</span> <span class="n">quote</span>

<span class="k">def</span> <span class="nf">sanitize_input</span><span class="p">(</span><span class="n">user_input</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s">Sanitize user input to prevent injection attacks</span><span class="sh">"""</span>
    <span class="c1"># Remove potentially dangerous characters
</span>    <span class="n">sanitized</span> <span class="o">=</span> <span class="n">html</span><span class="p">.</span><span class="nf">escape</span><span class="p">(</span><span class="n">user_input</span><span class="p">)</span>
    <span class="n">sanitized</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nf">sub</span><span class="p">(</span><span class="sa">r</span><span class="sh">'</span><span class="s">[&lt;&gt;\"\'%;()&amp;+]</span><span class="sh">'</span><span class="p">,</span> <span class="sh">''</span><span class="p">,</span> <span class="n">sanitized</span><span class="p">)</span>
    <span class="n">sanitized</span> <span class="o">=</span> <span class="n">sanitized</span><span class="p">.</span><span class="nf">strip</span><span class="p">()</span>
    <span class="k">return</span> <span class="n">sanitized</span>

<span class="k">def</span> <span class="nf">sanitize_file_path</span><span class="p">(</span><span class="n">file_path</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="c1">### Prevent directory traversal attacks ###
</span>    <span class="n">path</span> <span class="o">=</span> <span class="n">file_path</span><span class="p">.</span><span class="nf">replace</span><span class="p">(</span><span class="sh">"</span><span class="s">..</span><span class="sh">"</span><span class="p">,</span> <span class="sh">""</span><span class="p">)</span>
    <span class="n">path</span> <span class="o">=</span> <span class="n">path</span><span class="p">.</span><span class="nf">replace</span><span class="p">(</span><span class="sh">"</span><span class="s">//</span><span class="sh">"</span><span class="p">,</span> <span class="sh">"</span><span class="s">/</span><span class="sh">"</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">path</span>
</code></pre></div></div>

<h2 id="4-logging--monitoring">4. Logging &amp; Monitoring</h2>
<h3 id="41-comprehensive-logging">4.1 Comprehensive Logging</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">logging</span>
<span class="kn">import</span> <span class="n">json</span>
<span class="kn">from</span> <span class="n">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>

<span class="k">class</span> <span class="nc">StructuredLogger</span><span class="p">:</span>
    <span class="c1">### Structured logging for AI agents ###
</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nf">getLogger</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
        <span class="n">self</span><span class="p">.</span><span class="nf">setup_handlers</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">setup_handlers</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="c1"># Console handler
</span>        <span class="n">console_handler</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nc">StreamHandler</span><span class="p">()</span>
        <span class="n">console_handler</span><span class="p">.</span><span class="nf">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="p">.</span><span class="n">INFO</span><span class="p">)</span>

        <span class="c1"># File handler
</span>        <span class="n">file_handler</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nc">FileHandler</span><span class="p">(</span><span class="sh">'</span><span class="s">agent.log</span><span class="sh">'</span><span class="p">)</span>
        <span class="n">file_handler</span><span class="p">.</span><span class="nf">setLevel</span><span class="p">(</span><span class="n">logging</span><span class="p">.</span><span class="n">DEBUG</span><span class="p">)</span>

        <span class="c1"># Formatter
</span>        <span class="n">log_formatter</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nc">Formatter</span><span class="p">(</span><span class="sh">'</span><span class="s">%(asctime)s - %(name)s - %(levelname)s - %(message)s</span><span class="sh">'</span><span class="p">)</span>

        <span class="n">console_handler</span><span class="p">.</span><span class="nf">setFormatter</span><span class="p">(</span><span class="n">log_formatter</span><span class="p">)</span>
        <span class="n">file_handler</span><span class="p">.</span><span class="nf">setFormatter</span><span class="p">(</span><span class="n">log_formatter</span><span class="p">)</span>

        <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">addHandler</span><span class="p">(</span><span class="n">console_handler</span><span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">log_action</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">action</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">details</span><span class="p">:</span> <span class="nb">dict</span><span class="p">):</span>
        <span class="c1">### Log structured action data ###
</span>        <span class="n">log_entry</span> <span class="o">=</span> <span class="p">{</span>
            <span class="sh">"</span><span class="s">timestamp</span><span class="sh">"</span><span class="p">:</span> <span class="n">datetime</span><span class="p">.</span><span class="nf">utcnow</span><span class="p">().</span><span class="nf">isoformat</span><span class="p">(),</span>
            <span class="sh">"</span><span class="s">action</span><span class="sh">"</span><span class="p">:</span> <span class="n">action</span><span class="p">,</span>
            <span class="sh">"</span><span class="s">details</span><span class="sh">"</span><span class="p">:</span> <span class="n">details</span>
        <span class="p">}</span>
        <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="n">json</span><span class="p">.</span><span class="nf">dumps</span><span class="p">(</span><span class="n">log_entry</span><span class="p">))</span>

<span class="c1"># Usage details
</span><span class="n">logger</span> <span class="o">=</span> <span class="nc">StructuredLogger</span><span class="p">(</span><span class="n">__name_</span><span class="p">)</span>
<span class="n">logger</span><span class="p">.</span><span class="nf">log_action</span><span class="p">(</span><span class="sh">'</span><span class="s">fetch_incidents</span><span class="sh">'</span><span class="p">,</span> <span class="p">{</span>
    <span class="sh">'</span><span class="s">status</span><span class="sh">'</span><span class="p">:</span> <span class="sh">'</span><span class="s">success</span><span class="sh">'</span><span class="p">,</span>
    <span class="sh">'</span><span class="s">count</span><span class="sh">'</span><span class="p">:</span> <span class="mi">10</span><span class="p">,</span>
    <span class="sh">'</span><span class="s">duration_ms</span><span class="sh">'</span><span class="p">:</span> <span class="mi">1234</span>
<span class="p">})</span>
</code></pre></div></div>

<h3 id="42-monitoring--metrics">4.2 Monitoring &amp; Metrics</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">prometheus_client</span> <span class="kn">import</span> <span class="n">Counter</span><span class="p">,</span> <span class="n">Histogram</span><span class="p">,</span> <span class="n">Guage</span>
<span class="kn">import</span> <span class="n">time</span>

<span class="k">class</span> <span class="nc">MonitoredAgent</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s"> Agent with monitoring and metrics </span><span class="sh">"""</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="c1"># Metrics
</span>        <span class="n">self</span><span class="p">.</span><span class="n">request_count</span> <span class="o">=</span> <span class="nc">Counter</span><span class="p">(</span>
            <span class="sh">'</span><span class="s">agent_requests_total</span><span class="sh">'</span><span class="p">,</span>
            <span class="sh">'</span><span class="s">Total requests</span><span class="sh">'</span><span class="p">,</span>
            <span class="p">[</span><span class="sh">'</span><span class="s">method</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">status</span><span class="sh">'</span><span class="p">]</span>
        <span class="p">)</span>

        <span class="n">self</span><span class="p">.</span><span class="n">request_duration</span> <span class="o">=</span> <span class="nc">Histogram</span><span class="p">(</span>
            <span class="sh">'</span><span class="s">agent_request_duration_seconds</span><span class="sh">'</span><span class="p">,</span>
            <span class="sh">'</span><span class="s">Request duration</span><span class="sh">'</span><span class="p">,</span>
            <span class="p">[</span><span class="sh">'</span><span class="s">method</span><span class="sh">'</span><span class="p">]</span>
        <span class="p">)</span>

        <span class="n">self</span><span class="p">.</span><span class="n">active_requests</span> <span class="o">=</span> <span class="nc">Guage</span><span class="p">(</span>
            <span class="sh">'</span><span class="s">agent_active_requests</span><span class="sh">'</span><span class="p">,</span>
            <span class="sh">'</span><span class="s">Active requests</span><span class="sh">'</span>
        <span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">fetch_data</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">url</span><span class="p">):</span>
        <span class="c1">### Fetch with monitoring ###
</span>        <span class="n">start_time</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="nf">time</span><span class="p">()</span>
        <span class="n">self</span><span class="p">.</span><span class="n">active_requests</span><span class="p">.</span><span class="nf">inc</span><span class="p">()</span>

        <span class="k">try</span><span class="p">:</span>
            <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
            <span class="n">response</span><span class="p">.</span><span class="nf">raise_for_status</span><span class="p">()</span>

            <span class="n">self</span><span class="p">.</span><span class="n">request_count</span><span class="p">.</span><span class="nf">labels</span><span class="p">(</span>
                <span class="n">method</span><span class="o">=</span><span class="sh">'</span><span class="s">fetch</span><span class="sh">'</span><span class="p">,</span>
                <span class="n">status</span><span class="o">=</span><span class="sh">'</span><span class="s">success</span><span class="sh">'</span>
            <span class="p">).</span><span class="nf">inc</span><span class="p">()</span>

            <span class="k">return</span> <span class="n">response</span><span class="p">.</span><span class="nf">json</span><span class="p">()</span>
        
        <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
            <span class="n">self</span><span class="p">.</span><span class="n">request_count</span><span class="p">.</span><span class="nf">labels</span><span class="p">(</span>
                <span class="n">method</span><span class="o">=</span><span class="sh">'</span><span class="s">fetch</span><span class="sh">'</span><span class="p">,</span>
                <span class="n">status</span><span class="o">=</span><span class="sh">'</span><span class="s">raise</span><span class="sh">'</span>
            <span class="p">).</span><span class="nf">inc</span><span class="p">()</span>
            <span class="k">raise</span>
        
        <span class="k">finally</span><span class="p">:</span>
            <span class="n">duration</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="nf">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start_time</span>
            <span class="n">self</span><span class="p">.</span><span class="n">request_duration</span><span class="p">.</span><span class="nf">labels</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="sh">'</span><span class="s">fetch</span><span class="sh">'</span><span class="p">).</span><span class="nf">observe</span><span class="p">(</span><span class="n">duration</span><span class="p">)</span>
            <span class="n">self</span><span class="p">.</span><span class="n">active_requests</span><span class="p">.</span><span class="nf">dec</span><span class="p">()</span>
</code></pre></div></div>

<h2 id="5-testing--quality-assurance">5. Testing &amp; Quality Assurance</h2>
<h3 id="51-unit-testing">5.1 Unit Testing</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">pytest</span>
<span class="kn">from</span> <span class="n">unittest.mock</span> <span class="kn">import</span> <span class="n">Mock</span><span class="p">,</span> <span class="n">patch</span>

<span class="k">class</span> <span class="nc">TestIncidentAgent</span><span class="p">:</span>
    <span class="nd">@pytest.fixture</span>
    <span class="k">def</span> <span class="nf">agent</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="c1">### Create test agent ###
</span>        <span class="n">mock_api</span> <span class="o">=</span> <span class="nc">Mock</span><span class="p">()</span>
        <span class="k">return</span> <span class="nc">IncidentAgent</span><span class="p">(</span><span class="n">api</span><span class="o">=</span><span class="n">mock_api</span><span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">test_extract_target_name</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">agent</span><span class="p">):</span>
        <span class="c1">### Test target name extraction ###
</span>        <span class="n">description</span> <span class="o">=</span> <span class="sh">"</span><span class="s">Target Name: xyz.example.com</span><span class="sh">"</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="nf">extract_target_name</span><span class="p">(</span><span class="n">description</span><span class="p">)</span>
        <span class="k">assert</span> <span class="n">result</span> <span class="o">==</span> <span class="sh">"</span><span class="s">xyz.example.com</span><span class="sh">"</span>
    
    <span class="k">def</span> <span class="nf">test_invalid_incident_data</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">agent</span><span class="p">):</span>
        <span class="c1">### Test invalid incident data handling ###
</span>        <span class="n">invalid_data</span> <span class="o">=</span> <span class="p">{</span><span class="sh">'</span><span class="s">incident_id</span><span class="sh">'</span><span class="p">:</span> <span class="sh">''</span><span class="p">}</span> <span class="c1"># Empty ID
</span>        <span class="k">with</span> <span class="n">pytest</span><span class="p">.</span><span class="nf">raises</span><span class="p">(</span><span class="nb">ValueError</span><span class="p">):</span>
            <span class="n">agent</span><span class="p">.</span><span class="nf">process_incident</span><span class="p">(</span><span class="n">invalid_data</span><span class="p">)</span>
    
    <span class="nd">@patch</span><span class="p">(</span><span class="sh">'</span><span class="s">requests.get</span><span class="sh">'</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">test_fetch_with_proxy</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">mock_get</span><span class="p">,</span> <span class="n">agent</span><span class="p">):</span>
        <span class="c1">### Test fetch with proxy ###
</span>        <span class="n">mock_get</span><span class="p">.</span><span class="n">return_value</span><span class="p">.</span><span class="n">json</span><span class="p">.</span><span class="n">return_value</span> <span class="o">=</span> <span class="p">{</span><span class="sh">'</span><span class="s">result</span><span class="sh">'</span><span class="p">:</span> <span class="p">[]}</span>

        <span class="n">result</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="nf">fetch_incidents</span><span class="p">()</span>

        <span class="k">assert</span> <span class="n">mock_get</span><span class="p">.</span><span class="n">called</span>
        <span class="n">mock_get</span><span class="p">.</span><span class="nf">assert_called_with</span><span class="p">(</span>
            <span class="n">timeout</span><span class="o">=</span><span class="mi">30</span><span class="p">,</span>
            <span class="n">proxies</span><span class="o">=</span><span class="p">{</span><span class="sh">'</span><span class="s">http</span><span class="sh">'</span><span class="p">:</span> <span class="sh">'</span><span class="s">http://proxy:8080</span><span class="sh">'</span><span class="p">}</span>
        <span class="p">)</span>
</code></pre></div></div>
<h3 id="52-integration-testing">5.2 Integration Testing</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">pytest</span>
<span class="kn">import</span> <span class="n">responses</span>

<span class="k">class</span> <span class="nc">TestAgentIntegration</span><span class="p">:</span>
    <span class="nd">@responses.activate</span>
    <span class="k">def</span> <span class="nf">test_full_incidents_processing</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="c1">### Test complete incidents processing flow ###
</span>        
        <span class="c1"># Mock API response
</span>        <span class="n">responses</span><span class="p">.</span><span class="nf">add</span><span class="p">(</span>
            <span class="n">responses</span><span class="p">.</span><span class="n">GET</span><span class="p">,</span>
            <span class="sh">'</span><span class="s">https://api.example.com/incidents</span><span class="sh">'</span><span class="p">,</span>
            <span class="n">json</span><span class="o">=</span><span class="p">[{</span>
                <span class="sh">'</span><span class="s">id</span><span class="sh">'</span><span class="p">:</span> <span class="sh">'</span><span class="s">INC123</span><span class="sh">'</span><span class="p">,</span>
                <span class="sh">'</span><span class="s">description</span><span class="sh">'</span><span class="p">:</span> <span class="sh">'</span><span class="s">Target Name: xyz.example.com</span><span class="se">\n</span><span class="s">Target Type: server</span><span class="sh">'</span><span class="p">,</span>
            <span class="p">}]</span>
            <span class="n">status</span><span class="o">=</span><span class="mi">200</span>
        <span class="p">)</span>

        <span class="c1"># Create agent and process
</span>        <span class="n">agent</span> <span class="o">=</span> <span class="nc">IncidentAgent</span><span class="p">()</span>
        <span class="n">incidents</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="nf">fetch_incidents</span><span class="p">()</span>

        <span class="c1"># Assertions
</span>        <span class="k">assert</span> <span class="nf">len</span><span class="p">(</span><span class="n">incidents</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span>
        <span class="k">assert</span> <span class="n">inciedents</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="sh">'</span><span class="s">incident_id</span><span class="sh">'</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">INC123</span><span class="sh">'</span>
        <span class="k">assert</span> <span class="n">incidents</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="sh">'</span><span class="s">target_name</span><span class="sh">'</span><span class="p">]</span> <span class="o">==</span> <span class="sh">'</span><span class="s">xyz.example.com</span><span class="sh">'</span>
</code></pre></div></div>

<h2 id="6-configuration-management">6. Configuration Management</h2>
<h3 id="61-environment-based-configuration">6.1 Environment-Based Configuration</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">os</span>
<span class="kn">from</span> <span class="n">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
<span class="kn">from</span> <span class="n">dotenv</span> <span class="kn">import</span> <span class="n">load_dotenv</span>

<span class="nf">load_dotenv</span><span class="p">()</span>  <span class="c1"># Load .env file
</span>
<span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">Config</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s"> Agent configuration </span><span class="sh">"""</span>

    <span class="c1"># API settings
</span>    <span class="n">api_url</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">API_URL</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">https://api.example.com</span><span class="sh">'</span><span class="p">)</span>
    <span class="n">api_timeout</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">API_TIMEOUT</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">30</span><span class="sh">'</span><span class="p">))</span>
    <span class="n">max_retries</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">MAX_RETRIES</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">3</span><span class="sh">'</span><span class="p">))</span>

    <span class="c1"># Proxy settings
</span>    <span class="n">proxy_enabled</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">PROXY_ENABLED</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">false</span><span class="sh">'</span><span class="p">).</span><span class="nf">lower</span><span class="p">()</span> <span class="o">==</span> <span class="sh">'</span><span class="s">true</span><span class="sh">'</span>
    <span class="n">proxy_url</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">PROXY_URL</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">https://proxy:8080</span><span class="sh">'</span><span class="p">)</span>

    <span class="c1"># Logging
</span>    <span class="n">log_level</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">LOG_LEVEL</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">INFO</span><span class="sh">'</span><span class="p">)</span>
    <span class="n">log_file</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">LOG_FILE</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">agent.log</span><span class="sh">'</span><span class="p">)</span>

    <span class="c1"># Security
</span>    <span class="n">api_token</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">API_TOKEN</span><span class="sh">'</span><span class="p">,</span> <span class="sh">''</span><span class="p">)</span>
    <span class="n">verify_ssl</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">VERIFY_SSL</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">true</span><span class="sh">'</span><span class="p">).</span><span class="nf">lower</span><span class="p">()</span> <span class="o">==</span> <span class="sh">'</span><span class="s">true</span><span class="sh">'</span>

    <span class="k">def</span> <span class="nf">validate</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s"> validate configuration </span><span class="sh">"""</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">self</span><span class="p">.</span><span class="n">api_url</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nc">ValueError</span><span class="p">(</span><span class="sh">"</span><span class="s">API_URL must be set</span><span class="sh">"</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">self</span><span class="p">.</span><span class="n">api_token</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nc">ValueError</span><span class="p">(</span><span class="sh">"</span><span class="s">API_TOKEN must be set</span><span class="sh">"</span><span class="p">)</span>
<span class="c1"># Usage
</span><span class="n">config</span> <span class="o">=</span> <span class="nc">Config</span><span class="p">()</span>
<span class="n">config</span><span class="p">.</span><span class="nf">validate</span><span class="p">()</span>
</code></pre></div></div>

<h3 id="62-env-file">6.2 .env file</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># API Configuration
</span><span class="n">API_URL</span><span class="o">=</span><span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">api</span><span class="p">.</span><span class="n">example</span><span class="p">.</span><span class="n">com</span>
<span class="n">API_TIMEOUT</span><span class="o">=</span><span class="mi">30</span>
<span class="n">MAX_RETRIES</span><span class="o">=</span><span class="mi">3</span>

<span class="c1"># Proxy Configuration
</span><span class="n">PROXY_ENABLED</span><span class="o">=</span><span class="n">true</span>
<span class="n">PROXY_URL</span><span class="o">=</span><span class="n">http</span><span class="p">:</span><span class="o">//</span><span class="n">proxy</span><span class="p">:</span><span class="mi">8080</span>

<span class="c1"># Logging
</span><span class="n">LOG_LEVEL</span><span class="o">=</span><span class="n">DEBUG</span>
<span class="n">LOG_FILE</span><span class="o">=</span><span class="n">agent</span><span class="p">.</span><span class="n">log</span>

<span class="c1"># Security
</span><span class="n">API_TOKEN</span><span class="o">=</span><span class="n">your_api_token_here</span>
<span class="n">VERIFY_SSL</span><span class="o">=</span><span class="n">true</span>
</code></pre></div></div>

<h2 id="7-security-best-practices">7. Security Best Practices</h2>
<h3 id="71-secure-credential-management">7.1 Secure Credential Management</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">cryptography.fernet</span> <span class="kn">import</span> <span class="n">Fernet</span>
<span class="kn">import</span> <span class="n">os</span>

<span class="k">class</span> <span class="nc">SecureCredentials</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s"> Manage credentials securely </span><span class="sh">"""</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="c1"># Never hardcode key - use environment variable
</span>        <span class="n">key</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">ENCRYPTION_KEY</span><span class="sh">'</span><span class="p">)</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">key</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nc">ValueError</span><span class="p">(</span><span class="sh">"</span><span class="s">ENCRYPTION_KEY must be set in environment variables</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="n">self</span><span class="p">.</span><span class="n">cipher</span> <span class="o">=</span> <span class="nc">Fernet</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">encrypt</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">token</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
        <span class="sh">"""</span><span class="s"> Encrypt sensitive token </span><span class="sh">"""</span>
        <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">cipher</span><span class="p">.</span><span class="nf">encrypt</span><span class="p">(</span><span class="n">token</span><span class="p">.</span><span class="nf">encode</span><span class="p">()).</span><span class="nf">decode</span><span class="p">()</span>
    
    <span class="k">def</span> <span class="nf">decrypt</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">encrypted_token</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
        <span class="sh">"""</span><span class="s"> Decrypt sensitive token </span><span class="sh">"""</span>
        <span class="k">return</span> <span class="n">self</span><span class="p">.</span><span class="n">cipher</span><span class="p">.</span><span class="nf">decrypt</span><span class="p">(</span><span class="n">encrypted_token</span><span class="p">.</span><span class="nf">encode</span><span class="p">()).</span><span class="nf">decode</span><span class="p">()</span>

<span class="c1"># ❌ BAD: Hardcoding credentials
</span><span class="n">api_token</span> <span class="o">=</span> <span class="sh">"</span><span class="s">secret_token_123</span><span class="sh">"</span>

<span class="c1"># ✅ GOOD: Using secure credential management
</span><span class="n">api_token</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">API_TOKEN_ENCRYPTED</span><span class="sh">'</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="72-rate-limiting">7.2 Rate Limiting</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">requests.adapters</span> <span class="kn">import</span> <span class="n">HTTPAdapter</span>
<span class="kn">from</span> <span class="n">urllib3.util.retry</span> <span class="kn">import</span> <span class="n">Retry</span>
<span class="kn">import</span> <span class="n">time</span>

<span class="k">class</span> <span class="nc">RateLimitedAgent</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s"> Agent with rate limiting </span><span class="sh">"""</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">requests_per_second</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">requests_per_second</span> <span class="o">=</span> <span class="n">requests_per_second</span>
        <span class="n">self</span><span class="p">.</span><span class="n">min_interval</span> <span class="o">=</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="n">requests_per_second</span>
        <span class="n">self</span><span class="p">.</span><span class="n">last_request_time</span> <span class="o">=</span> <span class="mi">0</span>
    
    <span class="k">def</span> <span class="nf">_wait_if_needed</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s"> Wait to maintain rate limit </span><span class="sh">"""</span>
        <span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="nf">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">self</span><span class="p">.</span><span class="n">last_request_time</span>
        <span class="k">if</span> <span class="n">elapsed</span> <span class="o">&lt;</span> <span class="n">self</span><span class="p">.</span><span class="n">min_interval</span><span class="p">:</span>
            <span class="n">time</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">min_interval</span> <span class="o">-</span> <span class="n">elapsed</span><span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">fetch</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">url</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s"> Fetch with rate limiting </span><span class="sh">"""</span>
        <span class="n">self</span><span class="p">.</span><span class="nf">_wait_if_needed</span><span class="p">()</span>
        <span class="n">self</span><span class="p">.</span><span class="n">last_request_time</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="nf">time</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="8-performance-optimization">8. Performance Optimization</h2>
<h3 id="81-caching">8.1 Caching</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">funtools</span> <span class="kn">import</span> <span class="n">lru_cache</span>
<span class="kn">from</span> <span class="n">datetime</span> <span class="kn">import</span> <span class="n">datetime</span><span class="p">,</span> <span class="n">timedelta</span>

<span class="k">class</span> <span class="nc">CachedAgent</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s"> Agent with intelligent caching </span><span class="sh">"""</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">cache</span> <span class="o">=</span> <span class="p">{}</span>
        <span class="n">self</span><span class="p">.</span><span class="n">cache_ttl</span> <span class="o">=</span> <span class="mi">300</span> <span class="c1"># 5 minutes
</span>    
    <span class="k">def</span> <span class="nf">get_incidents</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">force_refresh</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s"> Get incidents with caching </span><span class="sh">"""</span>

        <span class="n">cache_key</span> <span class="o">=</span> <span class="sh">'</span><span class="s">incidents</span><span class="sh">'</span>

        <span class="c1"># Check cache
</span>        <span class="k">if</span> <span class="n">cache_key</span> <span class="ow">in</span> <span class="n">self</span><span class="p">.</span><span class="n">cache</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">force_refresh</span><span class="p">:</span>
            <span class="n">cached_data</span><span class="p">,</span> <span class="n">timestamp</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="n">cache</span><span class="p">[</span><span class="n">cache_key</span><span class="p">]</span>
            <span class="k">if</span> <span class="n">datetime</span><span class="p">.</span><span class="nf">now</span><span class="p">()</span> <span class="o">-</span> <span class="n">timestamp</span> <span class="o">&lt;</span> <span class="nf">timedelta</span><span class="p">(</span><span class="n">seconds</span><span class="o">=</span><span class="n">self</span><span class="p">.</span><span class="n">cache_ttl</span><span class="p">):</span>
                <span class="k">return</span> <span class="n">cached_data</span>
        
        <span class="c1"># Fetch fresh data
</span>        <span class="n">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sh">"</span><span class="s">Fetching fresh incidents</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">incidents</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="nf">_fetch_from_api</span><span class="p">()</span>

        <span class="c1"># Update cache
</span>        <span class="n">self</span><span class="p">.</span><span class="n">cache</span><span class="p">[</span><span class="n">cache_key</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">incidents</span><span class="p">,</span> <span class="n">datetime</span><span class="p">.</span><span class="nf">now</span><span class="p">())</span>

        <span class="k">return</span> <span class="n">incidents</span>
    
    <span class="k">def</span> <span class="nf">_fetch_from_api</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s"> Fetch from API </span><span class="sh">"""</span>
        <span class="k">pass</span>
</code></pre></div></div>

<h3 id="82-batch-processing">8.2 Batch Processing</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Iterator</span>

<span class="k">class</span> <span class="nc">BatchedAgent</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s"> Process incidents in batches </span><span class="sh">"""</span>

    <span class="k">def</span> <span class="nf">process_in_batches</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">incidents</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">dict</span><span class="p">],</span> <span class="n">batch_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Iterator</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="nb">dict</span><span class="p">]]:</span>
        <span class="sh">"""</span><span class="s"> process incidents in batches to reduce memory </span><span class="sh">"""</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nf">len</span><span class="p">(</span><span class="n">incidents</span><span class="p">),</span> <span class="n">batch_size</span><span class="p">):</span>
            <span class="n">batch</span> <span class="o">=</span> <span class="n">incidents</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span> <span class="o">+</span> <span class="n">batch_size</span><span class="p">]</span>
            <span class="n">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Processing batch </span><span class="si">{</span><span class="n">i</span> <span class="o">//</span> <span class="n">batch_size</span> <span class="o">+</span> <span class="mi">1</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
           
            <span class="k">for</span> <span class="n">incident</span> <span class="ow">in</span> <span class="n">batch</span><span class="p">:</span>
                <span class="k">yield</span> <span class="n">self</span><span class="p">.</span><span class="nf">process_incident</span><span class="p">(</span><span class="n">incident</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="9-documentation--code-quality">9. Documentation &amp; Code Quality</h2>
<h3 id="91-type-hints">9.1 Type Hints</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="n">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">Optional</span><span class="p">,</span> <span class="n">Tuple</span>

<span class="k">class</span> <span class="nc">DocumentedAgent</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s"> Well-documented agent </span><span class="sh">"""</span>

    <span class="k">def</span> <span class="nf">extract_target_name</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">description</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Optional</span><span class="p">[</span><span class="nb">str</span><span class="p">]:</span>
        <span class="sh">"""</span><span class="s"> Extract target name from incident description

        Args:
            description (str): Incident description text

        Returns:
            The Target name if found, None Otherwise

        Raises:
            ValueError: If description is empty
        
        Example:
</span><span class="gp">            &gt;&gt;&gt;</span> <span class="n">agent</span> <span class="o">=</span> <span class="nc">DocumentedAgent</span><span class="p">()</span>
            <span class="o">&gt;&gt;&gt;</span> <span class="n">agent</span><span class="p">.</span><span class="nf">extract_target_name</span><span class="p">(</span><span class="sh">"</span><span class="s">Target Name: xyz.example.com</span><span class="sh">"</span><span class="p">)</span>
            <span class="sh">'</span><span class="s">xyz.example.com</span><span class="sh">'</span>
        <span class="sh">"""</span>
        <span class="k">if</span> <span class="ow">not</span> <span class="n">description</span><span class="p">:</span>
            <span class="k">raise</span> <span class="nc">ValueError</span><span class="p">(</span><span class="sh">"</span><span class="s">Description must not be empty</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="n">match</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nf">search</span><span class="p">(</span><span class="sa">r</span><span class="sh">'</span><span class="s">Target Name:\s*(\S+)</span><span class="sh">'</span><span class="p">,</span> <span class="n">description</span><span class="p">)</span>
        <span class="k">return</span> <span class="k">match</span><span class="p">.</span><span class="nf">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="k">if</span> <span class="n">match</span> <span class="k">else</span> <span class="bp">None</span>
    
    <span class="k">def</span> <span class="nf">batch_process</span><span class="p">(</span>
        <span class="n">self</span><span class="p">,</span>
        <span class="n">incidents</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="n">Any</span><span class="p">]],</span>
        <span class="n">parallel</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Tuple</span><span class="p">[</span><span class="n">List</span><span class="p">[</span><span class="n">Dict</span><span class="p">],</span> <span class="n">List</span><span class="p">[</span><span class="nb">str</span><span class="p">]]:</span>
        <span class="sh">"""</span><span class="s"> 
        Process multiple incidents

        Args:
            incidents: List of incident dictionaries
            parallel: Whether to process in parallel

        Returns:
            Tuple of (processed_incidents, error_ids)
        </span><span class="sh">"""</span>
        <span class="k">pass</span>
</code></pre></div></div>

<h2 id="10-observability--debugging">10. Observability &amp; Debugging</h2>
<h3 id="101-debugging-mode">10.1 Debugging Mode</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">logging</span>

<span class="k">class</span> <span class="nc">DebuggableAgent</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s"> Agent with debug mode </span><span class="sh">"""</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">debug</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">debug</span> <span class="o">=</span> <span class="n">debug</span>
        <span class="n">self</span><span class="p">.</span><span class="nf">setup_logging</span><span class="p">()</span>
    
    <span class="k">def</span> <span class="nf">setup_logging</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s"> Setup logging based on debug mode </span><span class="sh">"""</span>
        <span class="n">level</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="n">DEBUG</span> <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">debug</span> <span class="k">else</span> <span class="n">logging</span><span class="p">.</span><span class="n">INFO</span>
        <span class="n">logging</span><span class="p">.</span><span class="nf">basicConfig</span><span class="p">(</span><span class="n">level</span><span class="o">=</span><span class="n">level</span><span class="p">)</span>
        <span class="n">self</span><span class="p">.</span><span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nf">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">fetch_data</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">url</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s"> Fetch with debug output </span><span class="sh">"""</span>
        <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">debug</span><span class="p">:</span>
            <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">debug</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Fetching data from URL: </span><span class="si">{</span><span class="n">url</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">debug</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Using proxies: </span><span class="si">{</span><span class="n">self</span><span class="p">.</span><span class="n">proxies</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="n">debug</span><span class="p">:</span>
            <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">debug</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Response status: </span><span class="si">{</span><span class="n">response</span><span class="p">.</span><span class="n">status_code</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">debug</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Response size: </span><span class="si">{</span><span class="nf">len</span><span class="p">(</span><span class="n">response</span><span class="p">.</span><span class="n">content</span><span class="p">)</span><span class="si">}</span><span class="s"> bytes</span><span class="sh">"</span><span class="p">)</span>
        
        <span class="k">return</span> <span class="n">response</span>
</code></pre></div></div>
<h3 id="102-health-checks">10.2 Health Checks</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">HealthCheckAgent</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s"> Agent with health checks </span><span class="sh">"""</span>

    <span class="k">def</span> <span class="nf">health_check</span><span class="p">(</span><span class="n">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Dict</span><span class="p">[</span><span class="nb">str</span><span class="p">,</span> <span class="nb">str</span><span class="p">]:</span>
        <span class="sh">"""</span><span class="s"> check agent health </span><span class="sh">"""</span>
        <span class="k">return</span> <span class="p">{</span>
            <span class="sh">'</span><span class="s">api_connectivity</span><span class="sh">'</span><span class="p">:</span> <span class="n">self</span><span class="p">.</span><span class="nf">_check_api</span><span class="p">(),</span>
            <span class="sh">'</span><span class="s">proxy_connectivity</span><span class="sh">'</span><span class="p">:</span> <span class="n">self</span><span class="p">.</span><span class="nf">_check_proxy</span><span class="p">(),</span>
            <span class="sh">'</span><span class="s">database_connection</span><span class="sh">'</span><span class="p">:</span> <span class="n">self</span><span class="p">.</span><span class="nf">_check_database</span><span class="p">(),</span>
            <span class="sh">'</span><span class="s">cache_health</span><span class="sh">'</span><span class="p">:</span> <span class="n">self</span><span class="p">.</span><span class="nf">_check_cache</span><span class="p">()</span>
        <span class="p">}</span>
    
    <span class="k">def</span> <span class="nf">_check_api</span><span class="p">(</span><span class="n">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
        <span class="sh">"""</span><span class="s"> Check API connectivity </span><span class="sh">"""</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="si">{</span><span class="n">self</span><span class="p">.</span><span class="n">api_url</span><span class="si">}</span><span class="s">/health</span><span class="sh">"</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">response</span><span class="p">.</span><span class="n">status_code</span> <span class="o">==</span> <span class="mi">200</span>
        <span class="k">except</span> <span class="nb">Exception</span><span class="p">:</span>
            <span class="k">return</span> <span class="bp">False</span>
</code></pre></div></div>

<h2 id="-quick-refence-checklist">✅ Quick Refence Checklist</h2>
<ul>
  <li>✅ Single responsibility principle</li>
  <li>✅ Comprehensive error handling with retries</li>
  <li>✅ Input validation and sanitization</li>
  <li>✅ Structured logging</li>
  <li>✅ Unit &amp; Integration testing</li>
  <li>✅ Configuration management</li>
  <li>✅ Security best practices</li>
  <li>✅ Rate limiting and Caching</li>
  <li>✅ Type hints and documentation</li>
  <li>✅ Health checks and monitoring</li>
  <li>✅ Gradeful degradation</li>
  <li>✅ Retry logic with backoff</li>
  <li>✅ Environment-based configuration</li>
  <li>✅ Secrets management</li>
  <li>✅ Performance optimization</li>
</ul>

<h2 id="-complete-production-ready-agent-template">🚀 Complete Production-Ready Agent Template</h2>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">logging</span>
<span class="kn">import</span> <span class="n">os</span>
<span class="kn">import</span> <span class="n">time</span>
<span class="kn">from</span> <span class="n">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
<span class="kn">from</span> <span class="n">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Dict</span><span class="p">,</span> <span class="n">Optional</span>
<span class="kn">from</span> <span class="n">functools</span> <span class="kn">import</span> <span class="n">wraps</span>

<span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">Config</span><span class="p">:</span>
    <span class="n">api_url</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">API_URL</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">https://api.example.com</span><span class="sh">'</span><span class="p">)</span>
    <span class="n">api_timeout</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">API_TIMEOUT</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">30</span><span class="sh">'</span><span class="p">))</span>
    <span class="n">max_retries</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="nf">int</span><span class="p">(</span><span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">MAX_RETRIES</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">3</span><span class="sh">'</span><span class="p">))</span>
    <span class="n">log_level</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="nf">getenv</span><span class="p">(</span><span class="sh">'</span><span class="s">LOG_LEVEL</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">INFO</span><span class="sh">'</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">ProductionAgent</span><span class="p">:</span>
    <span class="sh">"""</span><span class="s"> Production-ready AI Agent </span><span class="sh">"""</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">config</span><span class="p">:</span> <span class="n">Config</span><span class="p">):</span>
        <span class="n">self</span><span class="p">.</span><span class="n">config</span> <span class="o">=</span> <span class="n">config</span> <span class="ow">or</span> <span class="nc">Config</span><span class="p">()</span>
        <span class="n">self</span><span class="p">.</span><span class="n">logger</span> <span class="o">=</span> <span class="n">self</span><span class="p">.</span><span class="nf">_setup_logging</span><span class="p">()</span>
        <span class="n">self</span><span class="p">.</span><span class="n">cache</span> <span class="o">=</span> <span class="p">{}</span>
    
    <span class="k">def</span> <span class="nf">_setup_logging</span><span class="p">(</span><span class="n">self</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s"> Setup structured logging </span><span class="sh">"""</span>
        <span class="n">logger</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nf">getLogger</span><span class="p">(</span><span class="n">__name__</span><span class="p">)</span>
        <span class="n">logger</span><span class="p">.</span><span class="nf">setLevel</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">config</span><span class="p">.</span><span class="n">log_level</span><span class="p">)</span>

        <span class="n">handler</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nc">StreamHandler</span><span class="p">()</span>
        <span class="n">formatter</span> <span class="o">=</span> <span class="n">logging</span><span class="p">.</span><span class="nc">Formatter</span><span class="p">(</span><span class="sh">'</span><span class="s">%(asctime)s - %(name)s - %(levelname)s - %(message)s</span><span class="sh">'</span><span class="p">)</span>

        <span class="n">handler</span><span class="p">.</span><span class="nf">setFormatter</span><span class="p">(</span><span class="n">formatter</span><span class="p">)</span>
        <span class="n">logger</span><span class="p">.</span><span class="nf">addHandler</span><span class="p">(</span><span class="n">handler</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">logger</span>

    <span class="k">def</span> <span class="nf">retry</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">func</span><span class="p">):</span>
        <span class="sh">"""</span><span class="s"> Retry decorator with backoff </span><span class="sh">"""</span>
        <span class="nd">@wraps</span><span class="p">(</span><span class="n">func</span><span class="p">)</span>
        <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
            <span class="k">for</span> <span class="n">attempt</span> <span class="ow">in</span> <span class="nf">range</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">config</span><span class="p">.</span><span class="n">max_retries</span><span class="p">):</span>
                <span class="k">try</span><span class="p">:</span>
                    <span class="k">return</span> <span class="nf">func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
                <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                    <span class="k">if</span> <span class="n">attempt</span> <span class="o">==</span> <span class="n">self</span><span class="p">.</span><span class="n">config</span><span class="p">.</span><span class="n">max_retries</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span>
                        <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">All retries failed: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
                        <span class="k">raise</span>
                    <span class="n">wait_time</span> <span class="o">=</span> <span class="mi">2</span> <span class="o">**</span> <span class="n">attempt</span>
                    <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">warning</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Attempt </span><span class="si">{</span><span class="n">attempt</span> <span class="o">+</span> <span class="mi">1</span><span class="si">}</span><span class="s"> failed. Retrying in </span><span class="si">{</span><span class="n">wait_time</span><span class="si">}</span><span class="s">s: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
                    <span class="n">time</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="n">wait_time</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">wrapper</span>
    
    <span class="nd">@retry</span>
    <span class="k">def</span> <span class="nf">fetch_data</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">url</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Dict</span><span class="p">:</span>
        <span class="sh">"""</span><span class="s"> Fetch data with retry logic </span><span class="sh">"""</span>
        <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Fetching from: </span><span class="si">{</span><span class="n">url</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
        <span class="n">response</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="n">self</span><span class="p">.</span><span class="n">config</span><span class="p">.</span><span class="n">api_timeout</span><span class="p">)</span>
        <span class="n">response</span><span class="p">.</span><span class="nf">raise_for_status</span><span class="p">()</span>
        <span class="k">return</span> <span class="n">response</span><span class="p">.</span><span class="nf">json</span><span class="p">()</span>
    
    <span class="k">def</span> <span class="nf">process</span><span class="p">(</span><span class="n">self</span><span class="p">,</span> <span class="n">data</span><span class="p">:</span> <span class="n">Dict</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Optional</span><span class="p">[</span><span class="n">Dict</span><span class="p">]:</span>
        <span class="sh">"""</span><span class="s"> Process data safely </span><span class="sh">"""</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sh">"</span><span class="s">Processing data</span><span class="sh">"</span><span class="p">)</span>
            <span class="c1"># Your processing logic here
</span>            <span class="k">return</span> <span class="n">data</span>
        <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
            <span class="n">self</span><span class="p">.</span><span class="n">logger</span><span class="p">.</span><span class="nf">error</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Processing failed: </span><span class="si">{</span><span class="n">e</span><span class="si">}</span><span class="sh">"</span><span class="p">)</span>
            <span class="k">return</span> <span class="bp">None</span>

<span class="c1"># Usage
</span><span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="sh">"</span><span class="s">__main__</span><span class="sh">"</span><span class="p">:</span>
    <span class="n">agent</span> <span class="o">=</span> <span class="nc">ProductionAgent</span><span class="p">(</span><span class="n">config</span><span class="p">)</span>
    <span class="n">data</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="nf">fetch_data</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">https://api.example.com/incidents</span><span class="sh">"</span><span class="p">)</span>
    <span class="n">processed</span> <span class="o">=</span> <span class="n">agent</span><span class="p">.</span><span class="nf">process</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
</code></pre></div></div>

<p>This comprehensive guide covers all aspects of developing professional AI agents! 🎯</p>]]></content><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><category term="DevOps" /><category term="AI" /><category term="LLMs" /><category term="Machine Learning" /><category term="LLMs" /><category term="AI agents" /><category term="best practices" /><category term="software engineering" /><category term="machine learning" /><summary type="html"><![CDATA[A comprehensive guide to best practices for developing robust, reliable AI agents with code examples and patterns.]]></summary></entry><entry><title type="html">↔️New Relic to ServiceNow Incident Automation(Lab Guide)</title><link href="https://autoshiftops.com/blog/2025/12/22/new-relic-to-service-now-incident-automation/" rel="alternate" type="text/html" title="↔️New Relic to ServiceNow Incident Automation(Lab Guide)" /><published>2025-12-22T00:00:00+00:00</published><updated>2025-12-22T00:00:00+00:00</updated><id>https://autoshiftops.com/blog/2025/12/22/new-relic-to-service-now-incident-automation</id><content type="html" xml:base="https://autoshiftops.com/blog/2025/12/22/new-relic-to-service-now-incident-automation/"><![CDATA[<h2 id="objective">Objective</h2>
<p>Create a closed-loop incident management system where New Relic automatically creates ServiceNow incidents when CPU usage is high and resolves them when usage returns to normal.</p>

<h2 id="architecture">Architecture:</h2>
<ol>
  <li><b>Monitor:</b> New Relic Infrastructure Agent monitors a local machine.</li>
  <li><b>Trigger:</b> NRQL Alert Policy detects CPU &gt; 5%.</li>
  <li><b>Transport:</b> New Relic Workflow sends a Webhook <code class="language-plaintext highlighter-rouge">POST</code> request.</li>
  <li><b>Logic:</b> ServiceNow Scripted REST API receives the request, checks for a <code class="language-plaintext highlighter-rouge">correlation_id</code>, and decides whether to <code class="language-plaintext highlighter-rouge">insert()</code> (Create) or <code class="language-plaintext highlighter-rouge">update()</code> (Close).</li>
</ol>

<h2 id="part-i-environment-setup">Part I: Environment Setup</h2>
<p>Before configuring the integration, you need to establish your “Sandbox” environment.</p>

<h3 id="1-get-a-servicenow-personal-developer-instance-pdi">1. Get a ServiceNow Personal Developer Instance (PDI)</h3>
<p><i>ServiceNow does not run locally; you must use their cloud sandbox.</i></p>

<ol>
  <li>
    <p>Go to the <a href="https://developer.servicenow.com/">ServiceNow Developer Site</a>.</p>
  </li>
  <li>
    <p>Sign up for a free account.</p>
  </li>
  <li>
    <p>Click Request Instance.</p>
  </li>
  <li>
    <p>Wait a few minutes. You will receive:</p>

    <ul>
      <li>URL: e.g., <code class="language-plaintext highlighter-rouge">https://devXXXXX.service-now.com</code></li>
      <li>Username: e.g., <code class="language-plaintext highlighter-rouge">admin</code></li>
      <li>Password: e.g., <code class="language-plaintext highlighter-rouge">XXXXXX</code>(Save this securely)</li>
    </ul>
  </li>
  <li>
    <p>Log in to your instance using the provided URL, username, and password.</p>
  </li>
</ol>

<h3 id="2-get-new-relic--install-agent">2. Get New Relic &amp; Install Agent</h3>
<ol>
  <li>Sign up for a free New Relic account at <a href="https://newrelic.com/signup">New Relic Sign Up</a>.</li>
  <li>Once logged in, go to <b> Add Data &gt; Linux </b> (or MacOS/Windows depending on your local machine).</li>
  <li>Follow the instructions to run the installation command in your terminal.
    <ul>
      <li>Example (linux):
        <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-Ls</span> https://download.newrelic.com/install/newrelic-cli/scripts/install.sh | bash <span class="o">&amp;&amp;</span> <span class="nb">sudo </span><span class="nv">NEW_RELIC_API_KEY</span><span class="o">=</span>YOUR_API_KEY <span class="nv">NEW_RELIC_ACCOUNT_ID</span><span class="o">=</span>YOUR_ACCOUNT_ID newrelic <span class="nb">install</span>
</code></pre></div>        </div>
      </li>
    </ul>
  </li>
  <li>Verify data is flowing by going to <b> Infrastructure &gt; Hosts </b> in New Relic.</li>
</ol>

<h2 id="part-ii-servicenow-configurationthe-listener">Part II: ServiceNow Configuration(The Listener)</h2>
<p>We will build a “Smart Endpoint” that accepts the alert data.</p>

<h3 id="1-create-the-api-container">1. Create the API Container</h3>
<ol>
  <li>In your ServiceNow PDI, use the Filter Navigator (top left) to search for “Scripted REST APIs”.</li>
  <li>Click <b> New </b>.
    <ul>
      <li><b>Name:</b> <code class="language-plaintext highlighter-rouge">NewReliWebhook</code></li>
      <li><b>API ID:</b> <code class="language-plaintext highlighter-rouge">newrelic_handler</code></li>
    </ul>
  </li>
  <li>Click <b> Submit </b>.</li>
</ol>

<h3 id="2-create-the-resourcethe-endpoint">2. Create the Resource(The Endpoint)</h3>
<ol>
  <li>Open the <code class="language-plaintext highlighter-rouge">NewRelicWebhook</code> record you just created.</li>
  <li>Scroll down to the Resources tab and click <b> New </b>.
    <ul>
      <li><b>Name:</b> <code class="language-plaintext highlighter-rouge">HandleAlert</code></li>
      <li><b>HTTP Method:</b> <code class="language-plaintext highlighter-rouge">POST</code></li>
      <li><b>Relative Path:</b> <code class="language-plaintext highlighter-rouge">/incident</code></li>
    </ul>
  </li>
  <li>
    <p><b>The Script:</b> Delete the default code in the <b> Script </b> field and paste the following “Traffic Cop” logic:</p>

    <div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="p">(</span><span class="kd">function</span> <span class="nf">process</span><span class="p">(</span><span class="cm">/*RESTAPIRequest*/</span> <span class="nx">request</span><span class="p">,</span> <span class="cm">/*RESTAPIResponse*/</span> <span class="nx">response</span><span class="p">)</span> <span class="p">{</span>
     <span class="c1">// 1. Parse the incoming JSON from New Relic</span>
     <span class="kd">var</span> <span class="nx">requestBody</span> <span class="o">=</span> <span class="nx">request</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
     <span class="kd">var</span> <span class="nx">nrIssueId</span> <span class="o">=</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">issue_id</span><span class="p">;</span>
     <span class="kd">var</span> <span class="nx">nrState</span> <span class="o">=</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">current_state</span><span class="p">;</span>
     <span class="kd">var</span> <span class="nx">nrTitle</span> <span class="o">=</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">title</span><span class="p">;</span>
     <span class="kd">var</span> <span class="nx">nrCpu</span> <span class="o">=</span> <span class="nx">requestBody</span><span class="p">.</span><span class="nx">cpu_value</span><span class="p">;</span>

     <span class="c1">// 2. Look for an existing Incident with this New Relic Issue ID</span>
     <span class="kd">var</span> <span class="nx">gr</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">GlideRecord</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">);</span>
     <span class="nx">gr</span><span class="p">.</span><span class="nf">addQuery</span><span class="p">(</span><span class="dl">'</span><span class="s1">correlation_id</span><span class="dl">'</span><span class="p">,</span> <span class="nx">nrIssueId</span><span class="p">);</span>
     <span class="nx">gr</span><span class="p">.</span><span class="nf">query</span><span class="p">();</span>

     <span class="k">if </span><span class="p">(</span><span class="nx">gr</span><span class="p">.</span><span class="nf">next</span><span class="p">())</span> <span class="p">{</span>
         <span class="c1">// --- SCENARIO: UPDATE EXISTING INCIDENT ---</span>
         <span class="k">if </span><span class="p">(</span><span class="nx">nrState</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">CLOSED</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
             <span class="nx">gr</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="mi">6</span><span class="p">;</span> <span class="c1">// Set to 'Resolved' (Standard ID)</span>
             <span class="nx">gr</span><span class="p">.</span><span class="nx">close_code</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">Solved (Permanently)</span><span class="dl">'</span><span class="p">;</span>
             <span class="nx">gr</span><span class="p">.</span><span class="nx">close_notes</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">Cloased automatically by New Relic. Alert cleared.</span><span class="dl">'</span><span class="p">;</span>
             <span class="nx">gr</span><span class="p">.</span><span class="nx">work_notes</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">New Relic reports issue is closed.</span><span class="dl">"</span><span class="p">;</span>
             <span class="nx">gr</span><span class="p">.</span><span class="nf">update</span><span class="p">();</span>

             <span class="k">return</span> <span class="p">{</span> <span class="na">status</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Incident Resolved</span><span class="dl">"</span><span class="p">,</span> <span class="na">number</span><span class="p">:</span> <span class="nx">gr</span><span class="p">.</span><span class="nx">number</span> <span class="p">};</span>
         <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
             <span class="c1">// Optional: Update work notes if it's just an update</span>
             <span class="nx">gr</span><span class="p">.</span><span class="nx">work_notes</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Received update from New Relic. Current CPU: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">nrCpu</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">%</span><span class="dl">"</span><span class="p">;</span>
             <span class="nx">gr</span><span class="p">.</span><span class="nf">update</span><span class="p">();</span>
             <span class="k">return</span> <span class="p">{</span> <span class="na">status</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Incident Updated</span><span class="dl">"</span><span class="p">,</span> <span class="na">number</span><span class="p">:</span> <span class="nx">gr</span><span class="p">.</span><span class="nx">number</span> <span class="p">};</span>
         <span class="p">}</span>
     <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
         <span class="c1">// --- SCENARIO: CREATE NEW INCIDENT ---</span>
         <span class="k">if </span><span class="p">(</span><span class="nx">nrState</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">ACTIVATED</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
             <span class="kd">var</span> <span class="nx">newInc</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">GlideRecord</span><span class="p">(</span><span class="dl">'</span><span class="s1">incident</span><span class="dl">'</span><span class="p">);</span>
             <span class="nx">newInc</span><span class="p">.</span><span class="nf">initialize</span><span class="p">();</span>
             <span class="nx">newInc</span><span class="p">.</span><span class="nx">short_description</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">New Relic Alert: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">nrTitle</span><span class="p">;</span>
             <span class="nx">newInc</span><span class="p">.</span><span class="nx">description</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">Automatic alert from New Relic.</span><span class="se">\n</span><span class="s2">Issue ID: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">nrIssueId</span> <span class="o">+</span> <span class="dl">"</span><span class="se">\n</span><span class="s2">CPU Usage: </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">nrCpu</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">%</span><span class="dl">"</span><span class="p">;</span>
             <span class="nx">newInc</span><span class="p">.</span><span class="nx">correlation_id</span> <span class="o">=</span> <span class="nx">nrIssueId</span><span class="p">;</span> <span class="c1">// THIS IS CRITICAL</span>
             <span class="nx">newInc</span><span class="p">.</span><span class="nx">caller_id</span> <span class="o">=</span> <span class="nx">gs</span><span class="p">.</span><span class="nf">getUserID</span><span class="p">();</span> <span class="c1">// Assign to API user</span>
             <span class="nx">newInc</span><span class="p">.</span><span class="nx">urgency</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span> <span class="c1">// High</span>
             <span class="nx">newInc</span><span class="p">.</span><span class="nf">insert</span><span class="p">();</span>

             <span class="k">return</span> <span class="p">{</span> <span class="na">status</span><span class="p">:</span> <span class="dl">"</span><span class="s2">Incident Created</span><span class="dl">"</span><span class="p">,</span> <span class="na">number</span><span class="p">:</span> <span class="nx">newInc</span><span class="p">.</span><span class="nx">number</span> <span class="p">};</span>
         <span class="p">}</span>
     <span class="p">}</span>
 <span class="p">})(</span><span class="nx">request</span><span class="p">,</span> <span class="nx">response</span><span class="p">);</span>
</code></pre></div>    </div>
  </li>
  <li>Click <b>Submit.</b></li>
  <li><b>Important:</b> Note your full Endpoint URL. It will be: <code class="language-plaintext highlighter-rouge">https://devXXXXX.service-now.com/api/x_scope_id/newrelic_handler/incident</code></li>
</ol>

<h2 id="part-iii-new-relic-configurationthe-trigger">Part III: New Relic Configuration(The Trigger)</h2>
<h3 id="1-configure-the-destination">1. Configure the Destination</h3>
<ol>
  <li>In New Relic, go to <b>Alerts &amp; AI &gt; Destinations</b>.</li>
  <li>Select <b> Webhook </b>.
    <ul>
      <li><b>Endpoint URL:</b> Your ServiceNow Scripted REST API URL from above.</li>
      <li><b>Authorization Type:</b> Select <b>Basic Authentication</b>. Use your PDI <code class="language-plaintext highlighter-rouge">admin</code> username and password.</li>
    </ul>
  </li>
  <li>Name it “ServiceNow PDI”.
    <h3 id="2-configure-the-workflow-payload">2. Configure the Workflow (Payload)</h3>
  </li>
  <li>Go to <b> Alerts &amp; AI &gt; Workflows </b>.</li>
  <li>Click <b> Add Workflow </b>.
    <ul>
      <li><b>Filter:</b> <code class="language-plaintext highlighter-rouge">Policy Name = 'Low CPU Test'</code> ( we will create policy in the next step)</li>
    </ul>
  </li>
  <li><b>Notify:</b> Select your “ServiceNow PDI” destination.</li>
  <li><b>Payload Template:</b> This defines exactly what data is sent to script. Paste this JSON:
    <div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w"> </span><span class="p">{</span><span class="w">
     </span><span class="nl">"issue_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
     </span><span class="nl">"title"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
     </span><span class="nl">"current_state"</span><span class="p">:</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
     </span><span class="nl">"cpu_value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"N/A"</span><span class="w">
 </span><span class="p">}</span><span class="w">
</span></code></pre></div>    </div>
    <h3 id="3-create-the-alert-policy">3. Create the Alert Policy</h3>
  </li>
  <li>Go to <b>Alerts &amp; AI &gt; Conditions (Policies) </b>.</li>
  <li>Create a New Policy named <code class="language-plaintext highlighter-rouge">Local CPU Test</code>.</li>
  <li><b>Create a Condition:</b>
    <ul>
      <li><b>Product:</b> NRQL</li>
      <li><b>Query:</b>
        <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">SELECT</span> <span class="n">average</span><span class="p">(</span><span class="n">cpuPercent</span><span class="p">)</span> <span class="k">FROM</span> <span class="n">SystemSample</span> <span class="k">WHERE</span> <span class="nv">`hostname = 'YOUR_HOSTNAME'
</span></code></pre></div>        </div>
      </li>
      <li><b>Threshold:</b> Critical if query returns value <code class="language-plaintext highlighter-rouge">above 5</code> (Low threshold for easy testing) for at least <code class="language-plaintext highlighter-rouge">1 minute</code>.</li>
      <li><b>Advanced Signal Settings:</b> “Close open incidents on signal loss” -&gt; set to <code class="language-plaintext highlighter-rouge">5 minutes</code>.</li>
    </ul>
  </li>
</ol>

<h2 id="part-iv-execution--testing">Part IV: Execution &amp; Testing</h2>
<h3 id="test-a-unit-test-via-curl-command-line">Test A: Unit Test via cURL (Command Line)</h3>
<p>Verify your serviceNow script works without waiting for New Relic.</p>
<h4 id="1-create-incidentsimulate-alert">1. Create Incident(Simulate Alert)</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="s2">"https://&lt;YOUR_INSTANCE&gt;.service-now.com/api/&lt;YOUR_SCOPE_ID&gt;/newrelic_handler/incident"</span> <span class="se">\</span>
<span class="nt">--request</span> POST <span class="se">\</span>
<span class="nt">--header</span> <span class="s2">"Accept: application/json"</span> <span class="se">\</span>
<span class="nt">--header</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
<span class="nt">--user</span> <span class="s1">'admin:&lt;YOUR_PASSWORD&gt;'</span> <span class="se">\</span>
<span class="nt">--data</span> <span class="s1">'{
  "issue_id": "TEST12345",
  "title": "Test High CPU Alert",
  "current_state": "ACTIVATED",
  "cpu_value": "95"
}'</span>
</code></pre></div></div>
<p><b><i>Result&lt;/b&gt;: Check ServiceNow incidents table. You should see a new ticket.</i></b></p>

<h4 id="2-resolve-incidentsimulate-recovery">2. Resolve Incident(Simulate Recovery)</h4>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="s2">"https://&lt;YOUR_INSTANCE&gt;.service-now.com/api/&lt;YOUR_SCOPE_ID&gt;/newrelic_handler/incident"</span> <span class="se">\</span>
<span class="nt">--request</span> POST <span class="se">\</span>
<span class="nt">--header</span> <span class="s2">"Accept: application/json"</span> <span class="se">\</span>
<span class="nt">--header</span> <span class="s2">"Content-Type: application/json"</span> <span class="se">\</span>
<span class="nt">--user</span> <span class="s1">'admin:&lt;YOUR_PASSWORD&gt;'</span> <span class="se">\</span>
<span class="nt">--data</span> <span class="s1">'{
  "issue_id": "TEST12345",
  "title": "Test High CPU Alert",
  "current_state": "CLOSED",
  "cpu_value": "20"
}'</span>
</code></pre></div></div>
<p><b><i>Result&lt;/b&gt;: The ticket created in step 1 should now be marked as “Resolved”.</i></b></p>

<h3 id="test-b-end-to-end-integration-smoke-test">Test B: End-to-End Integration (“Smoke Test”)</h3>
<p>Now, use the <code class="language-plaintext highlighter-rouge">strees</code> tool to forced a real CPU spike on your local machine.</p>
<ol>
  <li>Install Stress Tool:
    <ul>
      <li>Linux: <code class="language-plaintext highlighter-rouge">sudo apt-get install stress</code></li>
      <li>MacOS: <code class="language-plaintext highlighter-rouge">brew install stress</code></li>
    </ul>
  </li>
  <li>Run Stress:
    <ul>
      <li><code class="language-plaintext highlighter-rouge">stress --cpu 8 --timeout 180</code> (Runs for 3 minutes).</li>
    </ul>
  </li>
  <li>Monitor:
    <ul>
      <li><b>T+0 to T+1 min:</b> New Relic detects high CPU.</li>
      <li><b>T+1 min:</b> New Relic triggers Alert -&gt; Webhook -&gt; <b>ServiceNow creates Incident</b>.</li>
      <li><b>T+3 min:</b> Stress command ends, CPU drops.</li>
      <li><b>T+5 to T+8 min:</b> New Relic closes Alert -&gt; Webhook -&gt; <b>ServiceNow resolves Incident</b>.</li>
    </ul>
  </li>
</ol>

<h2 id="appendix-troubleshooting">Appendix: Troubleshooting</h2>

<table>
  <thead>
    <tr>
      <th>Error</th>
      <th>Cause</th>
      <th>Fix</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>401 Unauthorized</td>
      <td>Bad credentials</td>
      <td>Check your PDI admin password. Update the Destination in New Relic.</td>
    </tr>
    <tr>
      <td>403 Forbidden</td>
      <td>Scope/ACL</td>
      <td>Ensure the user has the <code class="language-plaintext highlighter-rouge">admin</code> or <code class="language-plaintext highlighter-rouge">rest_service</code> role.</td>
    </tr>
    <tr>
      <td>404 Not Found</td>
      <td>Bad URL</td>
      <td>Check your API ID and Resource Path. Ensure the Resource is set to <code class="language-plaintext highlighter-rouge">POST</code>.</td>
    </tr>
    <tr>
      <td>Duplicate Incidents</td>
      <td>Logic Error</td>
      <td>Verify the <code class="language-plaintext highlighter-rouge">gr.addQuery</code> in the script matches the <code class="language-plaintext highlighter-rouge">issue_id</code> being sent.</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><category term="New Relic" /><category term="SeriveNow" /><category term="New Relic" /><category term="ServiceNow" /><category term="Integration" /><category term="Incident" /><category term="SRE" /><summary type="html"><![CDATA[A comprehensive guide to Integrate New Relic Incidents into ServiceNow.]]></summary></entry><entry><title type="html">🚀 Building an AI Incident Copilot: How I Automated the First 15 Minutes of Every Production Incident</title><link href="https://autoshiftops.com/blog/2025/11/29/ai-incident-copilot/" rel="alternate" type="text/html" title="🚀 Building an AI Incident Copilot: How I Automated the First 15 Minutes of Every Production Incident" /><published>2025-11-29T00:00:00+00:00</published><updated>2025-11-29T00:00:00+00:00</updated><id>https://autoshiftops.com/blog/2025/11/29/ai-incident-copilot</id><content type="html" xml:base="https://autoshiftops.com/blog/2025/11/29/ai-incident-copilot/"><![CDATA[<p>Every production incident follows the same painful ritual.</p>

<p>An alert fires at 2am. An engineer wakes up, SSH’s into a server, and begins the manual loop — pulling logs, scanning for errors, guessing what to check next. This loop can take 15 to 45 minutes before the real diagnosis even begins. Multiply that by every incident across every team in your organisation, and you have thousands of engineering hours lost every year to work that is repetitive, stressful, and largely automatable.</p>

<p>I’ve been on that on-call rotation. I know what it costs — not just in time, but in cognitive load, in missed context, and in the compounding pressure of an active incident. So I built incopilot: a CLI tool that automates the entire first-pass triage so engineers can skip straight to actual problem-solving.</p>

<p>This post walks through the architecture, the design decisions, and exactly how to build it yourself. Everything is open source at https://github.com/AutoShiftOps/incopilot.</p>

<h2 id="architecture">Architecture</h2>
<p><a href="/assets/images/posts/2025-11-29-ai-incident-copilot.png"><img src="/assets/images/posts/2025-11-29-ai-incident-copilot.png" alt="Architecture diagram" /></a></p>

<h2 id="project-structure">Project structure</h2>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>incopilot/
  __init__.py
  cli.py          # argument parsing + console output
  collectors.py   # journalctl, docker logs, file, bundle
  analyzer.py     # pattern detection + line normalization
  reporter.py     # report.md / report.json generation
  config.py       # patterns, golden-signal map, safe-command list
scripts/
  demo_generate_sample_logs.py
posts/
requirements.txt
pyproject.toml
README.md
</code></pre></div></div>

<h2 id="setup">Setup</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/AutoShiftOps/incopilot.git
<span class="nb">cd </span>incopilot
python <span class="nt">-m</span> venv .venv
<span class="nb">source</span> .venv/bin/activate
pip <span class="nb">install</span> <span class="nt">-r</span> requirements.txt
</code></pre></div></div>

<h2 id="quick-test-no-real-services-needed">Quick test (no real services needed)</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python scripts/demo_generate_sample_logs.py
python <span class="nt">-m</span> incopilot file <span class="nt">--path</span> sample.log
<span class="nb">ls </span>out/
</code></pre></div></div>

<h2 id="systemd-journal-triage">Systemd journal triage</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> incopilot journal <span class="nt">--unit</span> nginx <span class="nt">--since</span> <span class="s2">"30 min ago"</span>
</code></pre></div></div>

<h2 id="docker-triage">Docker triage</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> incopilot docker <span class="nt">--container</span> my-api <span class="nt">--since</span> 1h
</code></pre></div></div>

<h2 id="both-sources-bundle">Both sources (bundle)</h2>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> incopilot bundle <span class="se">\</span>
  <span class="nt">--unit</span> nginx <span class="se">\</span>
  <span class="nt">--container</span> my-api <span class="se">\</span>
  <span class="nt">--since-journal</span> <span class="s2">"30 min ago"</span> <span class="se">\</span>
  <span class="nt">--since-docker</span> 1h
</code></pre></div></div>

<h2 id="what-you-get">What you get</h2>
<p><code class="language-plaintext highlighter-rouge">out/report.md</code> — paste into your incident doc<br />
<code class="language-plaintext highlighter-rouge">out/report.json</code> — attach to a ticket or POST to a webhook</p>

<h2 id="what-to-improve-next">What to improve next</h2>
<ul>
  <li>Per-service pattern packs (nginx, postgres, java, node)</li>
  <li>Slack/Teams webhook posting (<code class="language-plaintext highlighter-rouge">--webhook &lt;url&gt;</code>)</li>
  <li>Unit tests + GitHub Actions CI</li>
  <li>Scheduled timer (systemd timer unit) for proactive reports</li>
</ul>

<p>Sudhakar Sajja is an Application Architect at TechMahindra with 13 years of experience across protocol testing, SDET, DevOps, and cloud architecture. He specialises in AI-powered DevOps operations — building tools that use LLMs to replace manual incident response and query diagnostics. He writes weekly at AutoShiftOps (autoshiftops.com) and built QueryTuner (querytuner.com), an AI-driven SQL query analysis tool. Based in Mississauga, Canada.</p>]]></content><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><category term="DevOps" /><category term="AI" /><category term="Incident Management" /><category term="Machine Learning" /><category term="AI agents" /><category term="incident management" /><category term="Python" /><category term="log analysis" /><category term="command suggestions" /><category term="machine learning" /><category term="AutoShiftOps" /><summary type="html"><![CDATA[A practical DevOps tool you can run over SSH in minutes (journalctl, docker logs, or plain files)]]></summary></entry><entry><title type="html">🚀 Building an AI-Powered Stock Trading Bot in Python</title><link href="https://autoshiftops.com/blog/2025/11/20/Building-ai-agents-using-python-for-stocktrade/" rel="alternate" type="text/html" title="🚀 Building an AI-Powered Stock Trading Bot in Python" /><published>2025-11-20T00:00:00+00:00</published><updated>2025-11-20T00:00:00+00:00</updated><id>https://autoshiftops.com/blog/2025/11/20/Building-ai-agents-using-python-for-stocktrade</id><content type="html" xml:base="https://autoshiftops.com/blog/2025/11/20/Building-ai-agents-using-python-for-stocktrade/"><![CDATA[<h3 id="an-autoshiftops-guide-to-ai-agents-backtesting-and-real-world-automation">An AutoShiftOps guide to AI agents, backtesting, and real-world automation</h3>

<hr />

<h2 id="introduction">Introduction</h2>

<p>At <strong>AutoShiftOps</strong>, we don’t build demos — we build systems that survive real-world uncertainty.</p>

<p>AI trading is often presented as a shortcut to profits:</p>

<blockquote>
  <p>“Train a model, predict prices, make money.”</p>
</blockquote>

<p>That’s not how markets work.</p>

<p>This post walks through how to <strong>design an AI-powered stock trading bot</strong> using Python, focusing on <strong>engineering discipline, backtesting, and system thinking</strong>, rather than hype.</p>

<p>You’ll also find links to other published versions of this post on <strong>Medium, Substack, Patreon, and DEV.to</strong> at the bottom.</p>

<hr />

<h2 id="what-is-an-ai-trading-agent">What Is an AI Trading Agent?</h2>

<p>An AI trading agent is <strong>not just a model</strong>.</p>

<p>It’s a system that:</p>

<ul>
  <li>Observes market data</li>
  <li>Generates signals</li>
  <li>Applies trading rules</li>
  <li>Manages risk</li>
  <li>Evaluates performance</li>
</ul>

<p>Remove any one of these pieces, and the system breaks.</p>

<hr />

<h2 id="autoshiftops-trading-agent-architecture">AutoShiftOps Trading Agent Architecture</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Market Data
↓
AI Prediction Engine
↓
Strategy &amp; Risk Rules
↓
Backtesting Engine
↓
Broker Execution Layer
</code></pre></div></div>

<p>This separation keeps the system testable, explainable, and production-ready.</p>

<hr />

<h2 id="step-1-market-data">Step 1: Market Data</h2>

<p>We start with <strong>historical stock prices</strong> (for example, from Yahoo Finance).</p>

<p>Clean, consistent data matters more than fancy indicators at this stage.</p>

<p>Goals:</p>
<ul>
  <li>Stable inputs</li>
  <li>Reproducible experiments</li>
  <li>Minimal noise</li>
</ul>

<hr />

<h2 id="step-2-ai-model-why-lstm">Step 2: AI Model (Why LSTM?)</h2>

<p>Markets are <strong>time-series data</strong>.</p>

<p>LSTM (Long Short-Term Memory) models work well because they:</p>
<ul>
  <li>Capture temporal patterns</li>
  <li>Handle noisy signals better than linear models</li>
</ul>

<p>⚠️ Reminder: The model provides <strong>signals</strong>, not decisions.</p>

<hr />

<h2 id="step-3-prediction-is-not-trading">Step 3: Prediction Is Not Trading</h2>

<p>Prediction alone does not generate profits.</p>

<p>Trading requires:</p>
<ul>
  <li>Rules</li>
  <li>Constraints</li>
  <li>Risk limits</li>
</ul>

<p>At AutoShiftOps, AI output is treated as <strong>input</strong>, not authority.</p>

<hr />

<h2 id="step-4-strategy-rules">Step 4: Strategy Rules</h2>

<p>Example simple rule layer:</p>

<table>
  <thead>
    <tr>
      <th>Condition</th>
      <th>Action</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Predicted price &gt; current + 2%</td>
      <td>BUY</td>
    </tr>
    <tr>
      <td>Predicted price &lt; current - 2%</td>
      <td>SELL</td>
    </tr>
    <tr>
      <td>Otherwise</td>
      <td>HOLD</td>
    </tr>
  </tbody>
</table>

<p>Doing nothing is a valid action.<br />
Over-trading kills strategies faster than bad models.</p>

<hr />

<h2 id="step-5-backtesting">Step 5: Backtesting</h2>

<p>Backtesting is <strong>non-negotiable</strong>.</p>

<p>If you skip it:</p>
<ul>
  <li>You are guessing</li>
  <li>You are curve-fitting</li>
  <li>You are gambling</li>
</ul>

<p>Backtesting reveals:</p>
<ul>
  <li>Drawdowns</li>
  <li>Trade frequency</li>
  <li>Capital erosion</li>
  <li>Market regime sensitivity</li>
</ul>

<p>Many “great” models fail here — and that’s a good thing.</p>

<hr />

<h2 id="lessons-learned">Lessons Learned</h2>

<ul>
  <li>AI didn’t make me profitable — it made me <strong>disciplined</strong></li>
  <li>Consistency comes from <strong>rules</strong>, not intelligence</li>
  <li>Engineers have an advantage because they think in <strong>systems, failure modes, and feedback loops</strong></li>
  <li>Trading is a <strong>software system problem first</strong>, finance second</li>
</ul>

<hr />

<h2 id="cross-published-versions">Cross-Published Versions</h2>

<p>This post is also available on:</p>

<ul>
  <li><strong>Medium:</strong> <a href="https://medium.com/@sajjasudhakarrao/building-an-ai-powered-stock-trading-bot-in-python-with-backtesting-779ac13cfd9f">Read on Medium</a></li>
  <li><strong>Substack:</strong> <a href="https://sajjas.substack.com/p/building-an-ai-powered-stock-trading">Read on Substack</a></li>
  <li><strong>Patreon:</strong> <a href="https://www.patreon.com/posts/building-ai-bot-147412738?collection=1863952">Read on Patreon</a></li>
  <li><strong>DEV.to:</strong> <a href="https://dev.to/sajjasudhakararao/building-an-ai-powered-stock-trading-bot-in-python-with-backtesting-19f6">Read on DEV</a></li>
</ul>

<hr />

<h2 id="disclaimer">Disclaimer</h2>

<p>This content is for <strong>educational purposes only</strong>.<br />
It is <strong>not financial advice</strong>.</p>

<hr />

<h2 id="about-autoshiftops">About AutoShiftOps</h2>

<p><strong>AutoShiftOps</strong> explores the intersection of:</p>
<ul>
  <li>AI agents</li>
  <li>Automation</li>
  <li>DevOps</li>
  <li>Real-world engineering systems</li>
</ul>

<p>We focus on <strong>practical AI</strong> — systems that survive production, not demos.</p>

<hr />]]></content><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><category term="DevOps" /><category term="AI" /><category term="Trading" /><category term="Machine Learning" /><category term="AI agents" /><category term="stock trading" /><category term="Python" /><category term="LSTM" /><category term="backtesting" /><category term="machine learning" /><category term="AutoShiftOps" /><summary type="html"><![CDATA[A comprehensive guide to building AI agents for stock trading using Python, LSTM models, and backtesting.]]></summary></entry><entry><title type="html">🔧 Designing an Alert Decision Layer (with a Working CLI in Python)</title><link href="https://autoshiftops.com/blog/2025/07/26/alert-decision-layer-cli-python/" rel="alternate" type="text/html" title="🔧 Designing an Alert Decision Layer (with a Working CLI in Python)" /><published>2025-07-26T00:00:00+00:00</published><updated>2025-07-26T00:00:00+00:00</updated><id>https://autoshiftops.com/blog/2025/07/26/alert-decision-layer-cli-python</id><content type="html" xml:base="https://autoshiftops.com/blog/2025/07/26/alert-decision-layer-cli-python/"><![CDATA[<p>Most teams don’t have an alerting problem. They have a <strong>decision problem</strong>.</p>

<p>Over time, SRE and DevOps teams add more checks, more monitors, more “smart” tools. The result is often alert fatigue: hundreds of alerts, dozens of dashboards, and still no clear answer to the question “Do we wake someone up for this?”</p>

<p>In this post we’ll do two things:</p>

<ul>
  <li>Design a simple <strong>Alert Decision Layer</strong> – an explicit step between alerts and humans.</li>
  <li>Build a small, working CLI in Python called <code class="language-plaintext highlighter-rouge">alertdecider</code> that you can run today.</li>
</ul>

<p>By the end you’ll have:</p>

<ul>
  <li>A clear architecture for routing alerts to <code class="language-plaintext highlighter-rouge">page</code>, <code class="language-plaintext highlighter-rouge">ticket</code>, <code class="language-plaintext highlighter-rouge">aggregate</code>, or <code class="language-plaintext highlighter-rouge">suppress</code>.</li>
  <li>A CLI that reads alerts from JSON, applies transparent rules, and outputs a Markdown + JSON report.</li>
  <li>A foundation you can later extend with AI without turning your incident pipeline into a black box.</li>
</ul>

<hr />

<h2 id="the-problem-alerts-without-decisions">The problem: alerts without decisions</h2>

<p>Alert fatigue has been written about a lot, but the symptoms are consistent:</p>

<ul>
  <li>On-call engineers get spammed by low-value alerts, become desensitized, and miss the important ones.</li>
  <li>Teams add more tools and even AI summaries, but humans still have to manually decide “page vs ticket vs ignore” for each signal.</li>
  <li>Runbooks and SLOs exist, but the mental model that turns raw alerts into decisions lives in people’s heads.</li>
</ul>

<p>What’s missing is a <strong>small decision layer</strong> that sits between your alert sources and your incident response.</p>

<p>That layer should be:</p>

<ul>
  <li><strong>Transparent</strong> – decisions come from rules everyone can understand and modify.</li>
  <li><strong>Context-aware</strong> – severity alone isn’t enough; you need service tier, environment, and history.</li>
  <li><strong>Composable</strong> – easy to extend later with AI-based explanations instead of replacing human judgment.</li>
</ul>

<p>That’s what we’ll build.</p>

<hr />

<h2 id="design-principles">Design principles</h2>

<p>Before touching code, let’s describe the design as an architect:</p>

<ol>
  <li>
    <p><strong>Normalize first, then decide</strong><br />
Every alert source has its own JSON schema. We’ll normalize into a simple <code class="language-plaintext highlighter-rouge">Alert</code> model (id, name, service, severity, environment, fingerprint, timestamps) before any rules.</p>
  </li>
  <li>
    <p><strong>Service profiles matter</strong><br />
A <code class="language-plaintext highlighter-rouge">critical</code> alert on a tier1, SLO-critical service is not the same as a <code class="language-plaintext highlighter-rouge">critical</code> on a best-effort internal tool. We encode this in a <code class="language-plaintext highlighter-rouge">ServiceProfile</code> model loaded from <code class="language-plaintext highlighter-rouge">services.yml</code>.</p>
  </li>
  <li>
    <p><strong>History influences action</strong><br />
A fingerprint fired once is different from a fingerprint that fired 40 times today. We’ll use a small <code class="language-plaintext highlighter-rouge">History</code> model to treat flapping/noisy alerts differently.</p>
  </li>
  <li>
    <p><strong>Rules are code, not magic</strong><br />
The <code class="language-plaintext highlighter-rouge">AlertDecisionEngine</code> is a small, explicit rule engine. The first version has no AI – just clear if/else policies you can review in code.</p>
  </li>
  <li>
    <p><strong>Decisions must be explainable</strong><br />
Every decision carries a <code class="language-plaintext highlighter-rouge">reason</code> string. This is where AI can plug in later to generate richer explanations, but today it’s hand-written and predictable.</p>
  </li>
</ol>

<hr />

<h2 id="architecture">Architecture</h2>

<p>Here’s the high-level architecture of the Alert Decision Layer:</p>

<ol>
  <li><strong>Sources</strong>
    <ul>
      <li>Prometheus Alertmanager, PagerDuty, or any system that can export alerts as JSON.</li>
    </ul>
  </li>
  <li><strong>Normalizer (<code class="language-plaintext highlighter-rouge">loader.py</code>)</strong>
    <ul>
      <li>Reads raw JSON and maps each item into an <code class="language-plaintext highlighter-rouge">Alert</code> dataclass with a consistent shape.</li>
    </ul>
  </li>
  <li><strong>Context loaders</strong>
    <ul>
      <li><code class="language-plaintext highlighter-rouge">services.yml</code> → <code class="language-plaintext highlighter-rouge">ServiceProfile</code> per service (tier, SLO criticality, owner).</li>
      <li><code class="language-plaintext highlighter-rouge">history.json</code> → <code class="language-plaintext highlighter-rouge">History</code> per fingerprint (how often we’ve seen this alert recently).</li>
    </ul>
  </li>
  <li><strong>Decision engine (<code class="language-plaintext highlighter-rouge">engine.py</code>)</strong>
    <ul>
      <li>For each <code class="language-plaintext highlighter-rouge">Alert</code>, looks up the service profile and history and decides one of: <code class="language-plaintext highlighter-rouge">page</code>, <code class="language-plaintext highlighter-rouge">ticket</code>, <code class="language-plaintext highlighter-rouge">aggregate</code>, <code class="language-plaintext highlighter-rouge">suppress</code>.</li>
    </ul>
  </li>
  <li><strong>Reporters (<code class="language-plaintext highlighter-rouge">reporter.py</code>)</strong>
    <ul>
      <li>Writes a human-friendly <code class="language-plaintext highlighter-rouge">decision_report.md</code> and a machine-friendly <code class="language-plaintext highlighter-rouge">decision_report.json</code>.</li>
      <li>Prints a table to the terminal so you can sanity-check the decisions quickly.</li>
    </ul>
  </li>
</ol>

<p>This is intentionally simple: a single CLI binary that can be run locally, in CI, or as a cron/systemd job.</p>

<hr />

<h2 id="project-structure">Project structure</h2>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>alertdecider-agent/
  __init__.py
  __main__.py
  cli.py          # CLI entry point
  models.py       # Alert, ServiceProfile, History dataclasses
  loader.py       # Load alerts/services/history from JSON/YAML
  engine.py       # Decision rules (AlertDecisionEngine)
  reporter.py     # Console + Markdown/JSON output
examples/
  alerts.json     # Example alerts
  services.yml    # Service risk profiles
  history.json    # Simple alert history
</code></pre></div></div>

<p>You can clone the repo and run it against the example data to see how everything works before wiring it into your own tooling.</p>

<hr />

<h2 id="setup">Setup</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/AutoShiftOps/alertdecider.git
<span class="nb">cd </span>alertdecider
python <span class="nt">-m</span> venv .venv
<span class="nb">source</span> .venv/bin/activate
pip <span class="nb">install</span> <span class="nt">-r</span> requirements.txt
</code></pre></div></div>

<p>The dependencies are minimal:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">rich</code> for nice CLI tables.</li>
  <li><code class="language-plaintext highlighter-rouge">PyYAML</code> for <code class="language-plaintext highlighter-rouge">services.yml</code>.</li>
</ul>

<hr />

<h2 id="modelling-alerts-services-and-history">Modelling alerts, services, and history</h2>

<p>We start by defining three small dataclasses in <code class="language-plaintext highlighter-rouge">models.py</code>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Alert</code> – normalized alert coming from any source (Prometheus, PagerDuty, etc.).</li>
  <li><code class="language-plaintext highlighter-rouge">ServiceProfile</code> – tier and SLO information for a service.</li>
  <li><code class="language-plaintext highlighter-rouge">History</code> – how often an alert fingerprint has fired in the last 24 hours.</li>
</ul>

<p>This gives us a clean domain model to talk about decisions.</p>

<hr />

<h2 id="implementing-the-decision-engine">Implementing the decision engine</h2>

<p><code class="language-plaintext highlighter-rouge">engine.py</code> contains the <code class="language-plaintext highlighter-rouge">AlertDecisionEngine</code> with a handful of clear rules.</p>

<p>Examples of policies encoded:</p>

<ul>
  <li>
    <p><strong>Low-severity non-prod noise</strong><br />
<code class="language-plaintext highlighter-rouge">info</code> / <code class="language-plaintext highlighter-rouge">debug</code> in non-prod → <code class="language-plaintext highlighter-rouge">suppress</code>.</p>
  </li>
  <li>
    <p><strong>Critical on tier1, SLO-critical services</strong><br />
<code class="language-plaintext highlighter-rouge">critical</code> + <code class="language-plaintext highlighter-rouge">tier1</code> + <code class="language-plaintext highlighter-rouge">slo_critical=true</code> → <code class="language-plaintext highlighter-rouge">page</code>.</p>
  </li>
  <li>
    <p><strong>Flapping alerts</strong><br />
<code class="language-plaintext highlighter-rouge">count_24h &gt;= 20</code> → <code class="language-plaintext highlighter-rouge">aggregate</code> (don’t page for every occurrence; treat as noisy/frequent).</p>
  </li>
  <li>
    <p><strong>Warnings on tier1</strong><br />
<code class="language-plaintext highlighter-rouge">warning</code> on <code class="language-plaintext highlighter-rouge">tier1</code> → <code class="language-plaintext highlighter-rouge">ticket</code> (not a page, but still important to track).</p>
  </li>
  <li>
    <p><strong>Default behavior</strong><br />
Prod alerts without a specific rule → <code class="language-plaintext highlighter-rouge">ticket</code>.<br />
Non-prod alerts without a specific rule → <code class="language-plaintext highlighter-rouge">suppress</code>.</p>
  </li>
</ul>

<p>The whole point is that you can read this engine like a policy document.</p>

<hr />

<h2 id="running-the-cli-with-example-data">Running the CLI with example data</h2>

<p>With the repo set up, try the example:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python <span class="nt">-m</span> alertdecider-agent   <span class="nt">--alerts</span> examples/alerts.json   <span class="nt">--services</span> examples/services.yml   <span class="nt">--history</span> examples/history.json   <span class="nt">--out-dir</span> out

<span class="nb">cat </span>out/decision_report.md
</code></pre></div></div>

<p>You’ll see a table in your terminal and a Markdown report with each alert’s decision and reason.</p>

<p>With the provided examples, you should observe:</p>

<ul>
  <li>A <strong>critical</strong> alert on a tier1, SLO-critical service in prod → <strong>page</strong>.</li>
  <li>A <strong>warning</strong> on a tier1 service → <strong>ticket</strong>.</li>
  <li>A flapping alert with high <code class="language-plaintext highlighter-rouge">count_24h</code> → <strong>aggregate</strong>.</li>
  <li>A low-severity <code class="language-plaintext highlighter-rouge">info</code> alert in staging → <strong>suppress</strong>.</li>
</ul>

<p>From here, you can plug in your own alerts JSON and adjust <code class="language-plaintext highlighter-rouge">services.yml</code> and <code class="language-plaintext highlighter-rouge">engine.py</code> to match your reality.</p>

<hr />

<h2 id="where-ai-fits-in-later">Where AI fits in (later)</h2>

<p>Right now, <code class="language-plaintext highlighter-rouge">alertdecider-agent</code> is deliberately rule-based and transparent.</p>

<p>Once you’re happy with the structure, you can start experimenting with AI in safe, incremental ways:</p>

<ul>
  <li>
    <p><strong>Richer explanations</strong><br />
Feed the normalized alert + decision into an LLM to generate a human-friendly explanation and suggested next steps.</p>
  </li>
  <li>
    <p><strong>Runbook suggestions</strong><br />
Use alert name, service name, and history to suggest a runbook link or dashboard.</p>
  </li>
  <li>
    <p><strong>Rule tuning hints</strong><br />
Analyze real decision logs to recommend new rules (e.g., “this pattern is always suppressed but often escalated manually”).</p>
  </li>
</ul>

<p>The important part is: the control plane (what gets paged vs ticketed vs suppressed) remains in code you own.</p>

<hr />

<h2 id="next-steps-for-your-team">Next steps for your team</h2>

<p>If you try this out, here are some directions to evolve it:</p>

<ul>
  <li>
    <p><strong>Add time-of-day and on-call load</strong><br />
Don’t page at 03:00 for something that can safely be a ticket until business hours.</p>
  </li>
  <li>
    <p><strong>Persist history more robustly</strong><br />
Replace <code class="language-plaintext highlighter-rouge">history.json</code> with a small SQLite table or a lightweight time-series store.</p>
  </li>
  <li>
    <p><strong>Integrate with your actual alert pipeline</strong><br />
Wire <code class="language-plaintext highlighter-rouge">alertdecider</code> into Alertmanager/PagerDuty via webhooks, or run it as part of your incident ingestion path.</p>
  </li>
  <li>
    <p><strong>Measure impact</strong><br />
Track how many alerts are suppressed or aggregated, and how many pages you avoided without missing true incidents.</p>
  </li>
</ul>

<p>This is the kind of small, opinionated tool that can pay off quickly for SRE/DevOps teams drowning in alerts – and it’s a great foundation for more advanced AI-assisted incident management.</p>]]></content><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><category term="Monitoring" /><category term="Alerting" /><category term="Python" /><category term="Python" /><category term="alerting" /><category term="decision-making" /><summary type="html"><![CDATA[Build a Python CLI for designing and managing an alert decision layer that intelligently routes and handles alerts.]]></summary></entry><entry><title type="html">🔧 Self-Healing Containers: Bash Script for Docker Auto-Recovery</title><link href="https://autoshiftops.com/blog/2025/07/20/self-healing-containers-docker-bash/" rel="alternate" type="text/html" title="🔧 Self-Healing Containers: Bash Script for Docker Auto-Recovery" /><published>2025-07-20T00:00:00+00:00</published><updated>2025-07-20T00:00:00+00:00</updated><id>https://autoshiftops.com/blog/2025/07/20/self-healing-containers-docker-bash</id><content type="html" xml:base="https://autoshiftops.com/blog/2025/07/20/self-healing-containers-docker-bash/"><![CDATA[<p>Manual restarts during incidents are reactive. Self-healing means your containers recover themselves between alerts.</p>

<p>This post shows how to build a lightweight bash watchdog that:</p>

<ol>
  <li>Monitors container health via Docker health checks</li>
  <li>Restarts unhealthy containers</li>
  <li>Integrates with systemd for daemon-like behavior</li>
  <li>Logs everything for incident review</li>
</ol>

<h2 id="the-self-healing-architecture">The Self-Healing Architecture</h2>
<p>Docker has built-in restart policies (<code class="language-plaintext highlighter-rouge">--restart unless-stopped</code>), but they don’t respect health checks. A container can be “running” but unhealthy (app crashed, dependencies down, etc.).</p>

<p>Our script loops every 30 seconds:</p>

<ol>
  <li>Query Docker API for container health</li>
  <li>Restart unhealthy containers</li>
  <li>Log actions to systemd journal</li>
  <li>Repeat</li>
</ol>

<h3 id="step-1-docker-health-checks-the-foundation">Step 1: Docker Health Checks (the foundation)</h3>
<p>First, ensure your containers have proper health checks in <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> or <code class="language-plaintext highlighter-rouge">docker run</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">services</span><span class="pi">:</span>
  <span class="na">web</span><span class="pi">:</span>
    <span class="na">image</span><span class="pi">:</span> <span class="s">nginx</span>
    <span class="na">healthcheck</span><span class="pi">:</span>
      <span class="na">test</span><span class="pi">:</span> <span class="pi">[</span><span class="s2">"</span><span class="s">CMD"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">curl"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">-f"</span><span class="pi">,</span> <span class="s2">"</span><span class="s">http://localhost/health"</span><span class="pi">]</span>
      <span class="na">interval</span><span class="pi">:</span> <span class="s">30s</span>
      <span class="na">timeout</span><span class="pi">:</span> <span class="s">10s</span>
      <span class="na">retries</span><span class="pi">:</span> <span class="m">3</span>
      <span class="na">start_period</span><span class="pi">:</span> <span class="s">40s</span>
</code></pre></div></div>

<p>This marks containers <code class="language-plaintext highlighter-rouge">healthy</code>, <code class="language-plaintext highlighter-rouge">unhealthy</code>, or <code class="language-plaintext highlighter-rouge">starting</code>.</p>

<h3 id="step-2-the-self-healing-watchdog-script">Step 2: The Self-Healing Watchdog Script</h3>
<p>Save this as <code class="language-plaintext highlighter-rouge">/usr/local/bin/docker-autoheal.sh</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/usr/bin/env bash</span>
<span class="nb">set</span> <span class="nt">-euo</span> pipefail

<span class="c"># Config</span>
<span class="nv">CHECK_INTERVAL</span><span class="o">=</span>30
<span class="nv">LOG_FILE</span><span class="o">=</span><span class="s2">"/var/log/docker-autoheal.log"</span>
<span class="nv">CONTAINER_LABEL</span><span class="o">=</span><span class="s2">"autoheal=true"</span>  <span class="c"># Label your containers</span>

log<span class="o">()</span> <span class="o">{</span>
  <span class="nb">echo</span> <span class="s2">"</span><span class="si">$(</span><span class="nb">date</span> <span class="s1">'+%Y-%m-%d %H:%M:%S'</span><span class="si">)</span><span class="s2"> - </span><span class="nv">$*</span><span class="s2">"</span> | <span class="nb">tee</span> <span class="nt">-a</span> <span class="s2">"</span><span class="nv">$LOG_FILE</span><span class="s2">"</span>
<span class="o">}</span>

heal_containers<span class="o">()</span> <span class="o">{</span>
  <span class="nb">local </span><span class="nv">unhealthy</span><span class="o">=(</span><span class="si">$(</span>docker ps <span class="nt">--filter</span> <span class="s2">"health=unhealthy"</span> <span class="nt">--filter</span> <span class="s2">"label=</span><span class="nv">$CONTAINER_LABEL</span><span class="s2">"</span> <span class="nt">--format</span> <span class="s2">""</span><span class="si">)</span><span class="o">)</span>
  
  <span class="k">for </span>container <span class="k">in</span> <span class="s2">"</span><span class="k">${</span><span class="nv">unhealthy</span><span class="p">[@]</span><span class="k">}</span><span class="s2">"</span><span class="p">;</span> <span class="k">do
    </span>log <span class="s2">"RESTARTING UNHEALTHY CONTAINER: </span><span class="nv">$container</span><span class="s2">"</span>
    
    <span class="c"># Graceful stop first</span>
    docker stop <span class="s2">"</span><span class="nv">$container</span><span class="s2">"</span> <span class="o">||</span> <span class="nb">true</span>
    
    <span class="c"># Hard kill after timeout</span>
    docker <span class="nb">kill</span> <span class="s2">"</span><span class="nv">$container</span><span class="s2">"</span> <span class="o">||</span> <span class="nb">true</span>
    
    <span class="c"># Restart with original command</span>
    docker start <span class="s2">"</span><span class="nv">$container</span><span class="s2">"</span>
    
    log <span class="s2">"RESTARTED: </span><span class="nv">$container</span><span class="s2">"</span>
  <span class="k">done</span>
<span class="o">}</span>

<span class="c"># Main loop</span>
log <span class="s2">"Docker Auto-Heal started (check interval: </span><span class="k">${</span><span class="nv">CHECK_INTERVAL</span><span class="k">}</span><span class="s2">s)"</span>
<span class="k">while </span><span class="nb">true</span><span class="p">;</span> <span class="k">do
  </span>heal_containers
  <span class="nb">sleep</span> <span class="s2">"</span><span class="nv">$CHECK_INTERVAL</span><span class="s2">"</span>
<span class="k">done</span>
</code></pre></div></div>

<h3 id="step-3-run-as-a-systemd-service-production-ready">Step 3: Run as a Systemd Service (production-ready)</h3>
<p>Create <code class="language-plaintext highlighter-rouge">/etc/systemd/system/docker-autoheal.service</code>:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Unit]
Description=Docker Container Auto-Healer
After=docker.service
Requires=docker.service

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/docker-autoheal.sh
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
</code></pre></div></div>

<p><strong>Enable and start:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl daemon-reload
<span class="nb">sudo </span>systemctl <span class="nb">enable </span>docker-autoheal.service
<span class="nb">sudo </span>systemctl start docker-autoheal.service
</code></pre></div></div>

<p><strong>Check status:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl status docker-autoheal.service
<span class="nb">sudo </span>journalctl <span class="nt">-u</span> docker-autoheal.service <span class="nt">-f</span>
</code></pre></div></div>

<h3 id="step-4-label-your-containers">Step 4: Label Your Containers</h3>
<p>Tag containers you want to auto-heal:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">-d</span> <span class="se">\</span>
  <span class="nt">--label</span> <span class="s2">"autoheal=true"</span> <span class="se">\</span>
  <span class="nt">--restart</span> unless-stopped <span class="se">\</span>
  <span class="nt">--health-cmd</span><span class="o">=</span><span class="s2">"curl -f http://localhost:8080/health || exit 1"</span> <span class="se">\</span>
  nginx
</code></pre></div></div>

<h3 id="step-5-advanced-features">Step 5: Advanced Features</h3>
<p>Grace Period (avoid restart loops)</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Add to script before restart</span>
<span class="k">if </span>docker inspect <span class="s2">"</span><span class="nv">$container</span><span class="s2">"</span> <span class="nt">--format</span> <span class="s1">''</span> | <span class="nb">grep</span> <span class="nt">-q</span> <span class="s2">"10"</span><span class="p">;</span> <span class="k">then
  </span>log <span class="s2">"SKIPPING: </span><span class="nv">$container</span><span class="s2"> restart count too high (restart loop?)"</span>
  <span class="k">continue
fi</span>
</code></pre></div></div>

<p><strong>Webhook Alerts</strong></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> POST <span class="nt">-d</span> <span class="s2">"{</span><span class="se">\"</span><span class="s2">container</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="nv">$container</span><span class="se">\"</span><span class="s2">,</span><span class="se">\"</span><span class="s2">action</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">restart</span><span class="se">\"</span><span class="s2">}"</span> <span class="s2">"</span><span class="nv">$WEBHOOK_URL</span><span class="s2">"</span>
</code></pre></div></div>

<h3 id="multi-host-docker-swarm">Multi-Host (Docker Swarm)</h3>
<p>Use labels + a central orchestrator or run the script on each host.
​</p>
<h3 id="testing-it-safely">Testing It (safely)</h3>
<p>Spin up a container with a failing health check:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker run <span class="nt">-d</span> <span class="se">\</span>
  <span class="nt">--name</span> test-fail <span class="se">\</span>
  <span class="nt">--label</span> <span class="s2">"autoheal=true"</span> <span class="se">\</span>
  <span class="nt">--health-cmd</span><span class="o">=</span><span class="s2">"exit 1"</span> <span class="se">\</span>
  alpine <span class="nb">sleep </span>infinity
</code></pre></div></div>

<p><strong>Watch it get restarted:</strong></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>journalctl <span class="nt">-u</span> docker-autoheal.service <span class="nt">-f</span>
</code></pre></div></div>

<h2 id="limitations--when-to-upgrade">Limitations + When to Upgrade</h2>
<p><strong>This works great for:</strong></p>

<ul>
  <li>Small deployments / homelabs</li>
  <li>Edge services / single-host apps</li>
  <li>Dev/staging environments</li>
</ul>

<p><strong>Upgrade to:</strong></p>

<ul>
  <li>Kubernetes: liveness/readiness probes + pod disruption budgets</li>
  <li>Docker Swarm: service replicas + constraints</li>
  <li>Nomad: health checks + restart stanzas</li>
</ul>

<h3 id="summary-table-copypaste">Summary Table (Copy/Paste)</h3>

<table>
  <thead>
    <tr>
      <th>Component</th>
      <th>Command</th>
      <th>Purpose</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Health Check</td>
      <td><code class="language-plaintext highlighter-rouge">docker ps --filter health=unhealthy</code></td>
      <td>Find broken containers</td>
    </tr>
    <tr>
      <td>Watchdog</td>
      <td><code class="language-plaintext highlighter-rouge">systemctl status docker-autoheal</code></td>
      <td>Confirm service running</td>
    </tr>
    <tr>
      <td>Logs</td>
      <td><code class="language-plaintext highlighter-rouge">journalctl -u docker-autoheal</code></td>
      <td>Review restart history</td>
    </tr>
    <tr>
      <td>Container Labels</td>
      <td><code class="language-plaintext highlighter-rouge">--label autoheal=true</code></td>
      <td>Target specific services</td>
    </tr>
  </tbody>
</table>]]></content><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><category term="Docker" /><category term="Linux" /><category term="Troubleshooting" /><category term="Docker" /><category term="cgroups" /><category term="namespaces" /><category term="bash scripting" /><category term="monitoring" /><category term="self-healing" /><summary type="html"><![CDATA[Build a bash watchdog that monitors Docker container health, auto-restarts unhealthy ones, and integrates with systemd and cgroups for production reliability.]]></summary></entry><entry><title type="html">🔧 The Container Troubleshooting Playbook: OOMs, CPU Spikes, and Network Timeouts</title><link href="https://autoshiftops.com/blog/2025/07/19/container-troubleshooting-playbook-docker-linux/" rel="alternate" type="text/html" title="🔧 The Container Troubleshooting Playbook: OOMs, CPU Spikes, and Network Timeouts" /><published>2025-07-19T00:00:00+00:00</published><updated>2025-07-19T00:00:00+00:00</updated><id>https://autoshiftops.com/blog/2025/07/19/container-troubleshooting-playbook-docker-linux</id><content type="html" xml:base="https://autoshiftops.com/blog/2025/07/19/container-troubleshooting-playbook-docker-linux/"><![CDATA[<p>When a container fails in production, you don’t always have time to browse StackOverflow. You need a checklist.</p>

<p>This post is a field guide for the three most common container “murders”: Memory (OOMKilled), CPU Throttling, and I/O Saturation. We’ll diagnose each using the <code class="language-plaintext highlighter-rouge">docker stats</code> + Linux host tools workflow we established last week.</p>

<h2 id="scenario-1-the-silent-death-oomkilled">Scenario 1: The “Silent” Death (OOMKilled)</h2>
<p>Symptom: The container restarts randomly. No error logs in the application output because it was killed instantly by the kernel.</p>

<h3 id="1-confirm-it-was-an-oom-kill">1. Confirm it was an OOM Kill</h3>
<p>Docker knows why the container died. Ask it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker inspect &lt;container&gt; <span class="nt">--format</span> <span class="s1">''</span>
<span class="c"># Output: true</span>
</code></pre></div></div>

<p>Or check the specific exit code (137 = 128 + 9 SIGKILL):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker inspect &lt;container&gt; <span class="nt">--format</span> <span class="s1">''</span>
<span class="c"># Output: 137</span>
</code></pre></div></div>

<h3 id="2-find-the-smoking-gun-in-kernel-logs">2. Find the “Smoking Gun” in Kernel Logs</h3>
<p>If Docker confirms it, see exactly when the kernel snapped. Run this on the host:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dmesg <span class="nt">-T</span> | <span class="nb">grep</span> <span class="nt">-i</span> <span class="s2">"killed process"</span>
</code></pre></div></div>

<p>You’ll see a line like: <code class="language-plaintext highlighter-rouge">Out of memory: Killed process 1234 (node) total-vm:2048kB, anon-rss:1024kB</code>.</p>

<h3 id="3-the-fix">3. The Fix</h3>
<ul>
  <li>
    <p><strong>Immediate:</strong> Bump the memory limit if the host has capacity.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  docker update <span class="nt">--memory</span> 2g &lt;container&gt;
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Root Cause:</strong> Check your application for memory leaks. If it’s Java, check the heap settings (<code class="language-plaintext highlighter-rouge">-Xmx</code>). If it’s Node, check the GC behavior.</p>
  </li>
</ul>

<h2 id="scenario-2-the-slow-death-cpu-throttling">Scenario 2: The “Slow” Death (CPU Throttling)</h2>
<p>Symptom: App is running but incredibly slow. Latency spikes. Health checks time out.
​</p>
<h3 id="1-check-if-its-throttling">1. Check if it’s throttling</h3>
<p>Linux cgroups enforce CPU limits by “pausing” your process when it uses its quota. It doesn’t kill the app; it just freezes it for milliseconds at a time.</p>

<p>Check <code class="language-plaintext highlighter-rouge">docker stats</code> first:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker stats <span class="nt">--no-stream</span>
</code></pre></div></div>

<p>If <code class="language-plaintext highlighter-rouge">CPU %</code> is consistently near 100% of your configured limit (e.g., if you gave it 0.5 CPUs and it’s at 50%), you are being throttled.</p>

<h3 id="2-verify-throttling-in-cgroups">2. Verify Throttling in cgroups</h3>
<p>Look at the raw cgroup metrics (works on cgroup v1/v2):</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Find the container ID</span>
docker inspect &lt;container&gt; <span class="nt">--format</span> <span class="s1">''</span>

<span class="c"># Check throttle stats (path varies by distro, commonly:)</span>
<span class="nb">cat</span> /sys/fs/cgroup/cpu/docker/&lt;long-id&gt;/cpu.stat
</code></pre></div></div>

<p>Look for <code class="language-plaintext highlighter-rouge">nr_throttled</code> and <code class="language-plaintext highlighter-rouge">throttled_time</code>. If these numbers are rising, your app is gasping for air.
​</p>
<h3 id="3-the-fix-1">3. The Fix</h3>
<ul>
  <li>
    <p><strong>Remove the limit</strong> temporarily to prove it’s the bottleneck.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  docker update <span class="nt">--cpus</span> 0 &lt;container&gt;
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Tune requests:</strong> If the app needs that CPU, increase the limit. If it’s a bug (infinite loop), profile the app.
​</p>
    <h2 id="scenario-3-the-gridlock-disk-io-saturation">Scenario 3: The “Gridlock” (Disk I/O Saturation)</h2>
    <p>Symptom: The container becomes unresponsive, <code class="language-plaintext highlighter-rouge">docker ps</code> hangs, or logs stop writing.
​</p>
    <h3 id="1-identify-the-io-hog">1. Identify the I/O Hog</h3>
    <p>Is it the container or the neighbor?</p>
  </li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Check host I/O</span>
iostat <span class="nt">-x</span> 1 5
</code></pre></div></div>

<p>If <code class="language-plaintext highlighter-rouge">%util</code> is &gt;80%, the disk is saturated.</p>

<h3 id="2-blame-the-container">2. Blame the Container</h3>
<p>Use <code class="language-plaintext highlighter-rouge">pidstat</code> (part of <code class="language-plaintext highlighter-rouge">sysstat</code>) to find which process is thrashing the disk:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pidstat <span class="nt">-d</span> 1
</code></pre></div></div>

<p>Look for the PID with high <code class="language-plaintext highlighter-rouge">kB_rd/s</code> or <code class="language-plaintext highlighter-rouge">kB_wr/s</code>. Match that PID back to a container:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker inspect <span class="nt">--format</span> <span class="s1">''</span> &lt;container&gt;
</code></pre></div></div>

<h3 id="3-the-fix-2">3. The Fix</h3>
<ul>
  <li>
    <p><strong>Limit the blast radius:</strong> Set a Block I/O limit on the greedy container so it doesn’t kill the host.</p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  docker update <span class="nt">--blkio-weight</span> 100 &lt;container&gt;  <span class="c"># Low priority (default 500)</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><strong>Move logs:</strong> Ensure your app isn’t logging debug data to the container’s JSON log driver (which writes to disk). Use a log shipper or write to <code class="language-plaintext highlighter-rouge">stdout</code> sparingly.
​</p>
  </li>
</ul>

<h2 id="bonus-network-connectivity-issues">Bonus: Network Connectivity Issues</h2>
<p><strong>Symptom:</strong> “Connection refused” or timeouts between containers.</p>

<h3 id="1-the-can-i-reach-it-check">1. The “Can I reach it?” Check</h3>
<p>Don’t guess. Enter the container’s namespace:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker <span class="nb">exec</span> <span class="nt">-it</span> &lt;source-container&gt; sh
<span class="c"># Inside:</span>
ping &lt;target-container-name&gt;
nc <span class="nt">-zv</span> &lt;target-container-name&gt; &lt;port&gt;
</code></pre></div></div>

<h3 id="2-if-dns-fails">2. If DNS fails</h3>
<p>Docker has its own internal DNS. Check <code class="language-plaintext highlighter-rouge">/etc/resolv.conf</code> inside the container:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> /etc/resolv.conf
</code></pre></div></div>

<p>It should point to Docker’s embedded DNS server (usually <code class="language-plaintext highlighter-rouge">127.0.0.11</code>). If it’s missing or wrong, check your daemon config.
​</p>
<h2 id="summary-checklist-copypaste">Summary Checklist (Copy/Paste)</h2>

<table>
  <thead>
    <tr>
      <th>Symptom</th>
      <th>Check Command</th>
      <th>Fix Action</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Random Restarts</td>
      <td><code class="language-plaintext highlighter-rouge">docker inspect &lt;container&gt; --format ''</code></td>
      <td>Increase RAM limit / Fix memory leak</td>
    </tr>
    <tr>
      <td>Sluggish App</td>
      <td><code class="language-plaintext highlighter-rouge">cat /sys/fs/cgroup/cpu/docker/&lt;id&gt;/cpu.stat</code> (check <code class="language-plaintext highlighter-rouge">nr_throttled</code>)</td>
      <td>Increase CPU limit / Profile app</td>
    </tr>
    <tr>
      <td>Host Unresponsive</td>
      <td><code class="language-plaintext highlighter-rouge">iostat -x 1 5</code> &amp; <code class="language-plaintext highlighter-rouge">pidstat -d 1</code></td>
      <td>Limit Block I/O weight / Reduce logging</td>
    </tr>
    <tr>
      <td>Network Timeout</td>
      <td><code class="language-plaintext highlighter-rouge">docker exec &lt;container&gt; nc -zv &lt;target&gt; &lt;port&gt;</code></td>
      <td>Check Docker DNS / Verify network aliases</td>
    </tr>
  </tbody>
</table>

<hr />

<h2 id="next-steps">Next Steps</h2>
<p>Now that you can debug containers manually, how do you automate this? Next week, we’ll build a “Self-Healing” Bash Script that detects these states and alerts you automatically.</p>]]></content><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><category term="Docker" /><category term="Linux" /><category term="Troubleshooting" /><category term="Docker" /><category term="cgroups" /><category term="namespaces" /><category term="troubleshooting" /><category term="monitoring" /><summary type="html"><![CDATA[When a containerized service is misbehaving, you need fast answers. This playbook walks through practical commands and mental models to quickly identify and mitigate OOMs, CPU spikes, and network timeouts using Docker and Linux tools.]]></summary></entry><entry><title type="html">📊 Docker Monitoring Without a Platform: docker stats + cgroups (DevOps Field Guide)</title><link href="https://autoshiftops.com/blog/2025/07/13/docker-monitoring-without-a-platform-docker-stats-cgroups/" rel="alternate" type="text/html" title="📊 Docker Monitoring Without a Platform: docker stats + cgroups (DevOps Field Guide)" /><published>2025-07-13T00:00:00+00:00</published><updated>2025-07-13T00:00:00+00:00</updated><id>https://autoshiftops.com/blog/2025/07/13/docker-monitoring-without-a-platform-docker-stats-cgroups</id><content type="html" xml:base="https://autoshiftops.com/blog/2025/07/13/docker-monitoring-without-a-platform-docker-stats-cgroups/"><![CDATA[<p>When an incident hits a containerized service, you often don’t need a full observability stack to get traction. You need fast answers: Which container is hot? What resource is saturating? Is it an app problem or a limit problem?</p>

<p>This guide shows a practical monitoring stack you can run from any Docker host:</p>

<ol>
  <li>Docker-level commands (docker stats, docker inspect, docker logs)</li>
  <li>Host Linux tools (ps/top/free/df/iostat/ss/journalctl)</li>
  <li>Kernel primitives: cgroups (resource limits/accounting) and namespaces (isolation)</li>
</ol>

<h2 id="1-start-with-docker-stats-the-fastest-signal">1) Start with docker stats (the fastest signal)</h2>
<p>docker stats streams runtime metrics for containers, including CPU%, memory usage/limit, network I/O, and block I/O.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker stats
</code></pre></div></div>

<p>Common workflows:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker stats <span class="nt">--no-stream</span>          <span class="c"># Snapshot (good for scripts)</span>
docker stats &lt;container_name&gt;     <span class="c"># Focus on one container</span>
</code></pre></div></div>

<p>How to interpret it (in plain language)</p>
<ul>
  <li><strong>CPU%:</strong> who’s burning compute right now.</li>
  <li><strong>MEM USAGE / LIMIT:</strong> how close you are to the memory ceiling.</li>
  <li><strong>NET I/O:</strong> traffic spikes, retries, or unusual egress.</li>
  <li><strong>BLOCK I/O:</strong> slow disks, chatty logging, or heavy read/write workloads.</li>
</ul>

<h2 id="2-jump-from-container-name--what-is-it">2) Jump from “container name” → “what is it?”</h2>
<p>Once you identify a hot container, immediately gather identity + configuration.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker ps
docker inspect &lt;container&gt; | less
</code></pre></div></div>

<p>Useful inspect questions:</p>

<ul>
  <li>What image/tag is running?</li>
  <li>What env vars/config are set?</li>
  <li>What ports and volumes are attached?</li>
  <li>Are there memory/CPU limits configured?</li>
</ul>

<h2 id="3-logs-confirm-symptoms-fast">3) Logs: confirm symptoms fast</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker logs <span class="nt">--tail</span> 200 &lt;container&gt;
docker logs <span class="nt">-f</span> &lt;container&gt;
</code></pre></div></div>

<p>This is often enough to spot:</p>

<ul>
  <li>crash loops</li>
  <li>OOM errors / memory pressure</li>
  <li>upstream timeouts</li>
  <li>DB connection exhaustion</li>
</ul>

<h2 id="4-understand-why-its-happening-cgroups--namespaces-the-mental-model">4) Understand why it’s happening: cgroups + namespaces (the mental model)</h2>
<p>Docker relies on Linux kernel features:</p>

<ul>
  <li>Namespaces isolate views of processes, networking, mounts, etc.</li>
  <li>cgroups control and account for resources like CPU, memory, and I/O.</li>
</ul>

<p>Why this matters during incidents:</p>

<ul>
  <li>A container can be “slow” because it’s CPU-throttled, not because the app code suddenly got worse.</li>
  <li>A container can restart because it hit its memory limit and the kernel’s OOM behavior targeted its processes.</li>
</ul>

<h2 id="5-host-level-confirmation-tie-back-to-your-linux-monitoring-toolkit">5) Host-level confirmation (tie back to your Linux monitoring toolkit)</h2>
<p>When docker stats shows a spike, verify on the host to avoid false conclusions.</p>

<h3 id="cpu-hogs">CPU hogs</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ps aux <span class="nt">--sort</span><span class="o">=</span>-%cpu | <span class="nb">head</span> <span class="nt">-15</span>
</code></pre></div></div>

<h3 id="memory-pressure">Memory pressure</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>free <span class="nt">-h</span>
</code></pre></div></div>

<h3 id="disk-full--log-explosions">Disk full / log explosions</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">df</span> <span class="nt">-h</span>
<span class="nb">du</span> <span class="nt">-sh</span> /var/lib/docker/<span class="k">*</span> 2&gt;/dev/null | <span class="nb">sort</span> <span class="nt">-h</span> | <span class="nb">tail</span> <span class="nt">-10</span>
</code></pre></div></div>

<h3 id="disk-io-saturation">Disk I/O saturation</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>iostat <span class="nt">-x</span> 1 3
</code></pre></div></div>

<h3 id="unexpected-listeners--traffic-patterns">Unexpected listeners / traffic patterns</h3>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ss <span class="nt">-tuln</span>
</code></pre></div></div>

<p>These host checks help you decide whether you’re dealing with a single container or a node-wide saturation problem.
​</p>
<h2 id="6-what-to-do-with-the-data-action-mapping">6) What to do with the data (action mapping)</h2>
<p>Use the shortest safe path to stability:</p>

<ul>
  <li>
    <p>CPU high + latency rising</p>

    <ul>
      <li>If CPU is legitimately needed: scale out / add capacity.</li>
      <li>If CPU is throttled: revisit limits/requests (or container CPU shares).</li>
    </ul>
  </li>
  <li>
    <p>Memory near limit</p>

    <ul>
      <li>If memory leak suspected: restart as mitigation + open an issue with heap profiling.</li>
      <li>If limit too low for normal peaks: adjust limit carefully and monitor.</li>
    </ul>
  </li>
  <li>
    <p>Block I/O high</p>

    <ul>
      <li>Check log volume and disk saturation; reduce noisy logs or move logs off disk.</li>
      <li>Consider storage performance constraints and workload patterns.</li>
    </ul>
  </li>
  <li>
    <p>Network I/O abnormal</p>

    <ul>
      <li>Look for retries, timeouts, DDoS/abuse patterns, or upstream issues.</li>
    </ul>
  </li>
</ul>

<h2 id="7-copypaste-triage-sequence-5-minutes">7) Copy/paste triage sequence (5 minutes)</h2>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 1) Find the hot container</span>
docker stats <span class="nt">--no-stream</span>

<span class="c"># 2) Identify it</span>
docker ps
docker inspect &lt;container&gt; | less

<span class="c"># 3) Check symptoms</span>
docker logs <span class="nt">--tail</span> 200 &lt;container&gt;

<span class="c"># 4) Confirm on host (avoid guessing)</span>
ps aux <span class="nt">--sort</span><span class="o">=</span>-%cpu | <span class="nb">head</span> <span class="nt">-10</span>
free <span class="nt">-h</span>
<span class="nb">df</span> <span class="nt">-h</span>
iostat <span class="nt">-x</span> 1 3
ss <span class="nt">-tuln</span>
</code></pre></div></div>

<h2 id="next-in-the-series">Next in the series</h2>
<p><b>Next post:</b> Container Troubleshooting Playbook (common symptoms → commands → safe mitigations → root cause follow-ups).</p>]]></content><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><category term="Docker" /><category term="Linux" /><category term="SRE" /><category term="Observability" /><category term="Automation" /><category term="SRE" /><category term="Bash" /><category term="Linux" /><category term="Docker" /><category term="AutoShiftOps" /><summary type="html"><![CDATA[Learn container monitoring from the terminal: docker stats, docker inspect, logs, and how cgroups/namespaces explain CPU/memory/I/O behavior.]]></summary></entry><entry><title type="html">🔧 Incident Response Runbook Template for DevOps (Alert → Mitigation → Comms)</title><link href="https://autoshiftops.com/blog/2025/07/12/incident-response-runbook-template-devops/" rel="alternate" type="text/html" title="🔧 Incident Response Runbook Template for DevOps (Alert → Mitigation → Comms)" /><published>2025-07-12T00:00:00+00:00</published><updated>2025-07-12T00:00:00+00:00</updated><id>https://autoshiftops.com/blog/2025/07/12/incident-response-runbook-template-devops</id><content type="html" xml:base="https://autoshiftops.com/blog/2025/07/12/incident-response-runbook-template-devops/"><![CDATA[<h2 id="incident-response-runbook-template-for-devops">Incident Response Runbook Template for DevOps</h2>
<p>Incidents are stressful when the team is improvising. A simple runbook reduces MTTR by making response repeatable, not heroic.
​
This post provides a ready to use incident response runbook template plus a practical Linux triage checklist you can run from any box.</p>

<h2 id="what-this-runbook-optimizes-for">What this runbook optimizes for</h2>
<ul>
  <li>Fast acknowledgement and clear ownership (Incident Commander + roles).</li>
  <li>Early impact assessment and severity assignment to avoid under/over‑reacting.</li>
  <li>Communication cadence and “known/unknown/next update” structure that builds trust.</li>
  <li>Evidence capture (commands + logs) to support post‑incident review.</li>
</ul>

<h2 id="the-incident-runbook-template">The incident runbook template</h2>
<p>Copy this into your internal wiki, README, Notion, or ops repo.
​</p>
<ol>
  <li>
    <p>Trigger</p>

    <p><b>Triggers:</b></p>
    <ul>
      <li>Monitoring alert / SLO breach</li>
      <li>Customer report escalated</li>
      <li>Internal detection (logs, latency spikes, error spikes)</li>
    </ul>
  </li>
  <li>Acknowledge (0–5 minutes)
    <ul>
      <li>Acknowledge page/alert in your paging system.</li>
      <li>Create an incident channel: #inc-YYYYMMDD-service-shortdesc.</li>
      <li>Assign Incident Commander (IC) and Comms Lead.</li>
      <li>Start an incident document: timeline + links + decisions.
​</li>
    </ul>
  </li>
  <li>
    <p>Assess severity (5–10 minutes)</p>

    <p><b>Answer quickly:</b></p>
    <ul>
      <li>What’s impacted (service, region, feature)?</li>
      <li>How many users / revenue / compliance impact?</li>
      <li>Is impact ongoing and spreading?</li>
    </ul>

    <p><b>Suggested severity:</b></p>
    <ul>
      <li>SEV1: Major outage / severe user impact; immediate coordination.</li>
      <li>SEV2: Partial outage / significant degradation; urgent but controlled.</li>
      <li>SEV3: Minor impact; can be handled async.</li>
    </ul>
  </li>
  <li>
    <p>Stabilize first (10–30 minutes)</p>

    <p><b>Goal:</b> stop the bleeding before chasing root cause.</p>

    <p><b>Typical mitigations:</b></p>

    <ul>
      <li>Roll back the last deploy/config change.</li>
      <li>Disable a feature flag.</li>
      <li>Scale up/out temporarily.</li>
      <li>Fail over if safe.</li>
      <li>Rate-limit or block abusive traffic.</li>
    </ul>
  </li>
  <li>
    <p>Triage checklist (host-level)
 Run these to establish the baseline quickly (copy/paste friendly).</p>

    <p><b>CPU</b></p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ps aux <span class="nt">--sort</span><span class="o">=</span>-%cpu | <span class="nb">head</span> <span class="nt">-15</span>
</code></pre></div>    </div>
    <p><b>Alert cue:</b> any process &gt;50% sustained.</p>

    <p><b>Memory</b></p>
    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> free <span class="nt">-h</span>
</code></pre></div>    </div>

    <p><b>Alert cue:</b> available &lt;20% total RAM.</p>

    <p><b>Disk</b></p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nb">df</span> <span class="nt">-h</span>
 <span class="nb">du</span> <span class="nt">-sh</span> /var/log/<span class="k">*</span> 2&gt;/dev/null | <span class="nb">sort</span> <span class="nt">-h</span> | <span class="nb">tail</span> <span class="nt">-10</span>
</code></pre></div>    </div>
    <p><b>Alert cue:</b> any filesystem &gt;90%.
 ​</p>

    <p><b>Disk I/O</b></p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> iostat <span class="nt">-x</span> 1 3
</code></pre></div>    </div>
    <p><b>Alert cue:</b> %util &gt;80%, await &gt;20ms.</p>

    <p><b>Network listeners</b></p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> ss <span class="nt">-tuln</span>
</code></pre></div>    </div>
    <p><b>Alert cue:</b> unexpected listeners/ports.
 ​
 <b>Logs (example: nginx)</b></p>

    <div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code> journalctl <span class="nt">-u</span> nginx <span class="nt">-f</span>
</code></pre></div>    </div>
    <p><b>Alert cue:</b> 5xx errors spiking.</p>
  </li>
  <li>
    <p>Comms cadence (keep it boring)<br /></p>

    <p>SEV1: updates every 10–15 minutes.​<br />
 SEV2: updates every 30 minutes.<br />
 SEV3: async updates acceptable.<br />
 <br />​
 Use this structure:</p>
    <ul>
      <li>What we know</li>
      <li>What we don’t know</li>
      <li>What we’re doing now</li>
      <li>Next update at: TIME
​</li>
    </ul>
  </li>
  <li>Verify resolution
    <ul>
      <li>Confirm user impact is gone (synthetic checks + error rate + latency).</li>
      <li>Confirm saturation is back to normal (CPU/memory/disk/I/O).</li>
      <li>Watch for 30–60 minutes for regression.</li>
    </ul>
  </li>
  <li>Close and learn (post-incident)
    <ul>
      <li>Write a brief timeline (detection → mitigation → resolution).</li>
      <li>Capture what worked, what didn’t, and what to automate.</li>
      <li>Create follow-ups: alerts tuning, runbook updates, tests, guardrails.</li>
    </ul>
  </li>
</ol>

<h2 id="bonus-golden-signals-lens-for-incidents">Bonus: “Golden signals” lens for incidents</h2>
<p>When you’re lost, anchor on the four golden signals:</p>

<ul>
  <li>Latency (are requests slower?)</li>
  <li>Traffic (is demand abnormal?)</li>
  <li>Errors (is failure rate rising?)</li>
  <li>Saturation (are resources hitting limits?)</li>
</ul>

<p>This keeps triage focused on user impact and system limits, not vanity metrics.
​</p>

<h2 id="download--reuse">Download / reuse</h2>
<p>If you reuse this template internally, make one improvement immediately: add links to dashboards, logs, deploy history, and owners for each service. Your future self will thank you.</p>]]></content><author><name>Sajja</name><email>sajja.sudhakararao@gmail.com</email></author><category term="Automation" /><category term="Linux" /><category term="Bash" /><category term="Incident" /><category term="Automation" /><category term="Scripting" /><category term="Bash" /><category term="Linux" /><category term="Incident" /><category term="AutoShiftOps" /><summary type="html"><![CDATA[A ready to use incident response runbook template with severity levels, comms cadence, and Linux triage commands to reduce MTTR.]]></summary></entry></feed>