AI-Driven Incident Response: Can Machine Learning Beat Human Intuition?

Balancing automation with analyst expertise

Contents
<p>At 03:14 one night my monitoring stack lit up: a burst of failed SSH logins against a box that has no business accepting SSH from outside, followed by one success. A correlation rule had already fired, opened a ticket, and — because I&rsquo;d wired it that way — pulled the host&rsquo;s recent auth log into the alert. The &ldquo;AI&rdquo; part flagged it as a credential-stuffing pattern with 0.91 confidence. The <em>human</em> part (me, bleary, on the sofa) took one look and realised it was my own laptop on a flaky VPN reconnecting forty times before the password manager filled in correctly. The model was right about the pattern and wrong about the world. That gap is the whole subject of this post: machine learning is genuinely good at parts of incident response and confidently useless at others, and the engineering question is where you draw the line.</p> <h2 id="what-the-machine-is-actually-good-at">What the machine is actually good at</h2><div class="ad-unit ad-in-article" aria-label="Advertisement"> <span class="ad-label">Advertisement</span> <ins class="adsbygoogle" style="display:block;text-align:center" data-ad-client="ca-pub-3726833845844946" data-ad-slot="3291553914" data-ad-format="auto" data-full-width-responsive="true"></ins> <script>(adsbygoogle = window.adsbygoogle || []).push({});</script> </div> <p>Strip away the marketing and &ldquo;AI-driven incident response&rdquo; is two unglamorous things that work. The first is <strong>anomaly detection</strong> — a model trained on what normal looks like for a given host or user flags the statistically weird: a service account that suddenly reads ten thousand files, a login from a geography the user has never touched, an outbound connection to an address nothing else talks to. The second is <strong>correlation and triage</strong> — collapsing a thousand raw events into one ranked incident, deduplicating, and enriching it with context so a human sees a story instead of a firehose.</p> <p>Both are speed plays. A machine reads every line of every log every second; a human reads the ones the machine surfaces. On volume alone that&rsquo;s not a contest, and the value is real: the slow, mechanical first pass over the haystack is exactly the work humans are worst at and burn out doing.</p> <p>What makes it tractable at all is that you don&rsquo;t need a giant model. A modest scoring function over enriched events — the kind of thing you can run on your own hardware, the same way I run <a href="/story/running-gemma-3-locally-googles-small-model-on-consumer-hardware/">Gemma 3 locally for other tasks</a> — gets you most of the triage win without shipping your security telemetry to someone else&rsquo;s cloud.</p> <h2 id="the-unglamorous-prerequisite-your-data">The unglamorous prerequisite: your data</h2> <p>Before any of the clever modelling matters, the boring part decides whether it works at all: the model is only as good as the telemetry feeding it, and most &ldquo;the AI doesn&rsquo;t catch anything&rdquo; complaints are really &ldquo;we never gave it the right logs.&rdquo; A useful pipeline needs a few things in place, and none of them are AI.</p> <p>You need <strong>normalised events from enough sources</strong> — auth logs, network flows, DNS queries, process execution, cloud audit trails — landed somewhere queryable, with consistent timestamps and a shared notion of &ldquo;which host&rdquo; and &ldquo;which user.&rdquo; If your SSH logs call a machine by its short hostname and your firewall logs call it by IP, the correlation engine can&rsquo;t join them, and no model fixes a join it can&rsquo;t make. You need <strong>enrichment at ingest</strong>: GeoIP, ASN, asset criticality, and whether a user is privileged, attached to each event as it arrives, because a human analyst reasons with that context instantly and the model can only use what&rsquo;s in the record. And you need <strong>enough history of normal</strong> to define abnormal — a model trained on a week of data hasn&rsquo;t seen your monthly batch jobs and will scream every time payroll runs.</p> <p>Get this plumbing right and even a simple scoring function performs well. Get it wrong and the fanciest model in the world is pattern-matching noise. I&rsquo;d spend the first month on the pipeline and the boring normalisation, not on model selection — the leverage is overwhelmingly there.</p> <h2 id="a-concrete-playbook">A concrete playbook</h2><div class="ad-unit ad-in-article" aria-label="Advertisement"> <span class="ad-label">Advertisement</span> <ins class="adsbygoogle" style="display:block;text-align:center" data-ad-client="ca-pub-3726833845844946" data-ad-slot="3291553914" data-ad-format="auto" data-full-width-responsive="true"></ins> <script>(adsbygoogle = window.adsbygoogle || []).push({});</script> </div> <p>The unit of automation worth building is the <strong>playbook</strong>: a deterministic response that fires when a detection crosses a confidence threshold, does the cheap safe things automatically, and hands the expensive irreversible things to a human. Here&rsquo;s the shape of one, trimmed to the essentials, for a suspected compromised credential:</p> <div class="highlight"><div class="chroma"> <table class="lntable"><tr><td class="lntd"> <pre tabindex="0" class="chroma"><code><span class="lnt"> 1 </span><span class="lnt"> 2 </span><span class="lnt"> 3 </span><span class="lnt"> 4 </span><span class="lnt"> 5 </span><span class="lnt"> 6 </span><span class="lnt"> 7 </span><span class="lnt"> 8 </span><span class="lnt"> 9 </span><span class="lnt">10 </span><span class="lnt">11 </span><span class="lnt">12 </span><span class="lnt">13 </span><span class="lnt">14 </span><span class="lnt">15 </span><span class="lnt">16 </span><span class="lnt">17 </span><span class="lnt">18 </span><span class="lnt">19 </span><span class="lnt">20 </span></code></pre></td> <td class="lntd"> <pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># soar-playbook: suspected-credential-compromise.yml</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nt">trigger</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">detection</span><span class="p">:</span><span class="w"> </span><span class="l">impossible-travel OR credential-stuffing</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">min_confidence</span><span class="p">:</span><span class="w"> </span><span class="m">0.85</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="c"># things the machine may do unattended — cheap and reversible</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nt">auto_actions</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">enrich</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="nt">add</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">geoip, asn, recent_auth_log, user_risk_score] }</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">notify</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="nt">channel: secops-alerts, include</span><span class="p">:</span><span class="w"> </span><span class="l">enrichment }</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">rate_limit</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="nt">source_ip: $event.src_ip, duration</span><span class="p">:</span><span class="w"> </span><span class="l">15m }</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="c"># things that require a human to click — expensive or irreversible</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nt">gated_actions</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">disable_account</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="nt">user</span><span class="p">:</span><span class="w"> </span><span class="l">$event.user }</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">revoke_sessions</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="nt">user</span><span class="p">:</span><span class="w"> </span><span class="l">$event.user }</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">isolate_host</span><span class="p">:</span><span class="w"> </span>{<span class="w"> </span><span class="nt">host</span><span class="p">:</span><span class="w"> </span><span class="l">$event.host }</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="nt">escalate_if</span><span class="p">:</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">confidence</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;&gt;= 0.95&#34;</span><span class="w"> </span></span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="nt">asset_tier</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;crown-jewel&#34;</span><span class="w"> </span></span></span></code></pre></td></tr></table> </div> </div><p>The dividing line in that file — <code>auto_actions</code> versus <code>gated_actions</code> — <em>is</em> the design. Rate-limiting an IP for fifteen minutes is reversible and low-blast-radius, so let the machine do it at 03:14. Disabling the CFO&rsquo;s account during a board meeting because of a 0.86-confidence anomaly is not something you want a model doing unattended, no matter how clean the maths looks. Every action you automate, you should be able to answer: &ldquo;what&rsquo;s the worst case if the model is wrong, and how fast can I undo it?&rdquo;</p> <h2 id="where-the-model-confidently-misleads-you">Where the model confidently misleads you</h2> <p>The failure mode that bites is not the model missing things — it&rsquo;s the model being <em>plausibly wrong</em>. My 03:14 VPN incident is the benign version. The dangerous version is the inverse: a real intrusion that looks normal because the attacker moved slowly, used valid credentials, and stayed inside the statistical envelope the model considers fine. Anomaly detection is, by construction, blind to attacks that don&rsquo;t look anomalous, and a patient adversary&rsquo;s entire job is to not look anomalous.</p> <p>The other trap is <strong>alert fatigue working in reverse</strong>. Tune the model loose and you drown analysts in false positives until they reflexively dismiss everything, including the true one. Tune it tight and you miss the subtle real attack. There is no setting that escapes the trade-off; there is only choosing where on the curve you sit and revisiting it as the environment changes. A model is also only as honest as its training data — feed it six months of &ldquo;normal&rdquo; that quietly included an undetected breach, and it will learn to consider that breach normal.</p> <p>Context is the thing the machine simply does not have. It does not know that the database the alert touches holds invoices, that the maintenance window started an hour ago, or that the &ldquo;suspicious&rdquo; admin is the on-call engineer doing exactly what the runbook says. That business context lives in human heads and is exactly what turns a ranked list of anomalies into a prioritised response.</p> <p>There&rsquo;s a second-order risk too: automation breeds complacency. Once a team trusts the machine to surface what matters, they stop reading the things it filters out — which is fine until the day the model is wrong about what matters. The healthiest teams I&rsquo;ve seen treat the model&rsquo;s output as a <em>prioritised reading list</em>, not a <em>complete one</em>, and deliberately spot-check the low-priority bucket so the muscle of human judgement doesn&rsquo;t atrophy. The point of automating the haystack search is to free humans for the needles, not to convince them there are no needles the machine missed.</p> <p>My 03:14 VPN false positive is the cheap, funny version of this. The expensive version is a security team that&rsquo;s been lulled by months of clean dashboards into assuming green means safe, and a patient attacker who has spent those same months learning to stay green.</p> <h2 id="a-short-history-because-it-explains-the-hype-cycle">A short history, because it explains the hype cycle</h2> <p>It helps to know that none of this is new, only newly capable. Signature-based intrusion-detection systems date to the early 1990s — Snort, the open-source workhorse most people met first, arrived in 1998 — and they worked by pattern-matching known-bad traffic against a rule set. They caught what they had a signature for and were blind to everything else, and they were noisy: a busy network could generate more alerts than any team could read, which is the original alert-fatigue problem we&rsquo;re still fighting.</p> <p>The 2000s brought correlation engines and the first &ldquo;SIEM&rdquo; products that stitched logs together; the 2010s brought the machine-learning anomaly-detection wave, fuelled less by algorithmic breakthroughs than by cloud providers suddenly having enormous labelled data sets to train on. The current generation layers large language models on top for summarising incidents and drafting response steps in plain language. Each wave was sold as the one that would finally replace the analyst, and each turned out to be a better assistant rather than a replacement. That pattern — capability climbs, the &ldquo;human is obsolete&rdquo; pitch returns, reality settles on &ldquo;force multiplier&rdquo; — is worth remembering when you read the latest vendor deck. The honest read of three decades is that the machine keeps getting better at the volume problem and no better at the judgement problem.</p> <h2 id="how-id-actually-wire-it">How I&rsquo;d actually wire it</h2> <p>Treat the model as a force multiplier on the boring half, never as the decision-maker on the consequential half. Concretely:</p> <ol> <li><strong>Automate enrichment and triage; gate remediation.</strong> Let the machine collapse, rank, and annotate. Let a human pull the trigger on anything that disables, isolates, or deletes.</li> <li><strong>Pilot on low-stakes assets first.</strong> Point the automation at non-critical systems, watch its false-positive and false-negative rates for a few weeks, and only widen scope once you trust the numbers — the same caution as introducing any tool that can change state in your infrastructure.</li> <li><strong>Measure the boring metrics.</strong> Track mean time to detect and mean time to respond before and after. If the automation isn&rsquo;t moving those numbers, it&rsquo;s theatre.</li> <li><strong>Feed corrections back.</strong> Every &ldquo;this was a false positive&rdquo; verdict from an analyst is a training signal. Capture it deliberately rather than letting it evaporate in a chat thread.</li> <li><strong>Keep it auditable.</strong> You want a Git-style record of what fired, what the model scored, and who approved the gated actions — the same &ldquo;who changed this and why&rdquo; discipline that makes <a href="/story/gitops-with-flux-letting-git-be-your-clusters-source-of-truth/">GitOps with Flux</a> worth the effort applies to security automation too.</li> </ol> <h2 id="verdict-who-should-bother">Verdict: who should bother</h2> <p>If you&rsquo;re triaging more alerts than humans can read — which is almost anyone running real infrastructure — then yes, ML-driven triage and enrichment earn their place, and the payoff shows up fast in mean-time-to-respond. The honest caveat is that it&rsquo;s an assistant, not an analyst: it wins the volume game and loses the judgement game, and wiring it as though it could win both is how organisations automate themselves into a confidently-wrong incident. For a solo homelab, a handful of correlation rules and a local model doing triage is plenty and genuinely useful. For a team defending crown-jewel assets, the model belongs firmly on the enrichment side of the line, with a human&rsquo;s finger on every irreversible button. Can machine learning beat human intuition? On speed, easily. On judgement, not yet — and the engineering win is in knowing which question you&rsquo;re asking.</p>
Advertisement
Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.