<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Elastic Security Labs - Articles by Maggie Musquez</title>
        <link>https://www.elastic.co/security-labs</link>
        <description>Trusted security news &amp; research from the team at Elastic.</description>
        <lastBuildDate>Tue, 25 Aug 2026 15:53:09 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Elastic Security Labs - Articles by Maggie Musquez</title>
            <url>https://www.elastic.co/security-labs/assets/security-labs-thumbnail.png</url>
            <link>https://www.elastic.co/security-labs</link>
        </image>
        <copyright>© 2026. elasticsearch B.V. All Rights Reserved</copyright>
        <item>
            <title><![CDATA[Inside Elastic's agentic SOC: How we took AI alert triage from 60% to 92% accuracy]]></title>
            <link>https://www.elastic.co/security-labs/alert-triage-agentic-soc-self-correcting-agents</link>
            <guid>alert-triage-agentic-soc-self-correcting-agents</guid>
            <pubDate>Tue, 25 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic's InfoSec team runs three agents that read the detection rule's investigation guide and the closure reasons on 30 days of past cases. Analysts now clear most alerts with a single click in Slack.]]></description>
            <content:encoded><![CDATA[<p>AI verdict correctness in our security operations center (SOC) is 92%, up from 60%, but we didn't switch models to get there. What we changed is the context the agents get before they decide anything, including the detection rule's investigation guide and user risk data from Workday, along with the closure reasons from 30 days of past cases on that same rule.</p>
<p>This post covers how the agentic SOC pipeline is built in Elastic Workflows and Elastic Agent Builder, down to the prompts and the feedback loop that lets an agent see where it got the same rule wrong last time.</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image2.png" alt="" /></p>
<h2>Customer Zero: Running Agent Builder in our own SOC</h2>
<p>At Elastic, our internal SOC operates as Customer Zero, meaning that we’re the first and most demanding user of every feature we ship. We run the newest versions of <a href="https://www.elastic.co/guide/en/security/current/">Elastic Security</a> and <a href="https://www.elastic.co/docs/solutions/security/ai/agent-builder/agent-builder">Agent Builder</a> in our production environment, often before they reach general availability (GA), across a globally distributed fleet of laptops, servers, and cloud workloads. The workflows and agent configurations shown in this post reflect our setup as of version 9.5.1.</p>
<h2>When your AI SOC analyst is wrong 40% of the time</h2>
<p>Our team dove in headfirst with AI agents and fully integrated our alerts with AI triage. When our agents were looking at only the current alert context and investigation indexes, they weren’t always correct. Actually, our logs showed accuracy hovering around 60%. It’s great to have this data, but not if the analysts can’t trust it.</p>
<p>We were adding long AI summaries to each case, what we would consider <em>AI slop</em>, as it was inaccurate 40% of the time. The feedback we got from the analysts was that they weren’t reading them. The analysts started ignoring the AI summaries completely since they couldn't trust that they were helpful or accurate. It took more time to read a paragraph of incorrect information than to just triage the case manually. The summaries were slowing analysts down without providing any benefit worth the additional token cost.</p>
<h3>Leading with the data</h3>
<p>Before getting too in the weeds, here’s the data. Our AI verdict correctness (based on comparing the AI verdict and the analyst close reason) went from 60% to 92% after implementing the changes we discuss in this blog. We’re tracking these metrics using <a href="https://www.elastic.co/docs/explore-analyze/dashboards/managing">Elastic dashboards</a> by comparing the case custom fields that are discussed more below. This increase in accuracy meant that the analysts could start double-checking the summary and closing the case right away. This changed our AI summaries from being a time sink to allowing our analysts to close the case in one step.</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image5.png" alt="Elastic dashboard showing AI verdict accuracy rising from 60% to 92% after the Brainstorm agents launched." /></p>
<h3>What context AI alert triage actually needs</h3>
<p>We significantly increased agent accuracy by feeding them more context. Here's what we pull in from each source before an agent makes a verdict:</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image9.png" alt="Context enrichment sources for AI alert triage: Kibana API and ES|QL lookups with index and endpoint detail." /></p>
<h3>When should you use an AI agent instead of a query?</h3>
<p>It's important to know when to use AI and when not to. If the answer requires a predictable query with only a variable or two changing each time, don't use an agent. Instead, use an <a href="https://www.elastic.co/docs/explore-analyze/workflows">Elastic workflow</a> that runs an Elasticsearch Query Language (ES|QL) query, a Kibana API call, or a GET request. They're faster and cheaper, and we keep them modular and reusable across many different orchestrators, so a UserDetailsLookup or PastCasesByRulenameLookup can be called from any workflow that needs it. Agents are more suited for tasks that require reading and reasoning that cannot be completed with a simple query; for example, analyzing past case comments for patterns.</p>
<p>We named our workflows to reflect the three types of activities in the main orchestrator:</p>
<ol>
<li><strong>Lookup:</strong> ES|QL queries, Kibana API calls, and GET requests to external services.</li>
<li><strong>Agent:</strong> AI agents built in <a href="https://www.elastic.co/docs/solutions/security/ai/agent-builder/agent-builder">Kibana's Agent Builder</a>.</li>
<li><strong>Action:</strong> POST requests to Kibana case comments and to Slack and other external services.</li>
</ol>
<h3>Investigation guide lookup: Per-rule triage instructions</h3>
<p>Using the <code>kibana.alert.rule.uuid</code>, we can do a GET request to <code>/api/alerting/rule/\&lt;kibana.alert.rule.uuid\&gt;</code> to grab the investigation guide attached to the detection rule that triggered the alert. Here, we can provide context to <em>both</em> the analyst and our AI agents. Our detection engineers fill out these investigation guides when creating the detection, sometimes separating the analyst sections and AI sections with notes to follow. This gives us specific, per-rule instructions so the AI triages each alert type the same way every time. Here's where to find the investigation guide on a rule:</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image14.png" alt="Navigating to the alert’s associated investigation guide." /></p>
<h3>The user details lookup: Finding high-risk users</h3>
<p>We look up user details in Workday using the <code>user.email</code> field from the alert. This gives us context on high-risk users, for example:</p>
<ul>
<li>Users who have joined the company in the last 90 days.</li>
<li>Users who are leaving the company in the next 90 days.</li>
<li>Cost centers that carry elevated risk (for example, help desk, security researchers).</li>
</ul>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image10.png" alt="Kibana workflow on UserDetailsLookup." /></p>
<h3>Past cases by rule name: What verdict did the analyst choose?</h3>
<p>We look up alerts with the same <code>kibana.alert.rule.uuid</code> from the last 30 days that have been tied to cases and closed. This gives our agents a historical baseline. If this rule has fired 50 times and 48 were false positives, that's critical context. An agent that knows the history of a rule makes significantly better verdicts than one that doesn't.</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image7.png" alt="Kibana workflow on PastCasesByRulenameLookup." /></p>
<p>Using the cases we've seen in the last 30 days, we pull the fields that tell us how analysts actually closed them, the verdict they chose, their summary of the activity, and whether the AI got it right. These are the signals that close the loop:</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/side.png" alt="Analyst-selected close reason and custom fields displayed on Kibana Cases." /></p>
<h2>The AI feedback loop: How agents learn from wrong verdicts</h2>
<p>Using the data returned from the PastCasesbyRulenameLookup, if the analyst selects <strong>False Positive</strong> but the AI verdict was <strong>True Positive - Suspicious</strong> in previous cases, our agents can reference the analyst closure notes and case comments to understand why the previous verdict was incorrect. This context is fed to the agent before it makes a decision on the new case.</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image1.png" alt="Case fields table showing which alert triage fields are filled by the analyst and which by the AI agent." /></p>
<p>The <strong>AI Incorrect?</strong> custom field toggle is optional. An analyst can select it and fill out why they feel the AI verdict was wrong in the 'Detail on AI Incorrectness' custom field. If the AI verdict itself was correct but the “AI Generated Summary” case comment went in the wrong direction, the analyst can also explain here where the comment was incorrect. Even without this field, we can determine AI verdict correctness because the two AI agent–generated fields (AI Confidence and AI Verdict) are always on the alert via the workflows shown here, and the analyst is required to pick a closure reason. Our automation team pulls these custom fields from Kibana cases into a dashboard so we can track incorrect verdicts over time by comparing the analyst closure reason and AI verdict to tune our agent prompts or provide additional context where needed.</p>
<p>Here's the feedback loop in action, where our Pattern Finder Agent catches its own overclassification pattern in previous cases and surfaces tuning requests already filed against this rule:</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image13.png" alt="Real example of mistake auto-correction under AI Generated Summary case comment." /></p>
<h2>How the agentic SOC pipeline runs in Elastic Workflows</h2>
<p>We have one overarching Elastic workflow, Agent Brainstorm, that orchestrates 12 child workflows (and growing). The name reflects what's happening inside: one parent workflow (Agent Brainstorm), many context lookups, and three agents working together to reach a combined AI-generated case summary.</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image11.png" alt="Kibana Agent Builder setup with three agents." /></p>
<table>
<thead>
<tr>
<th>Agent</th>
<th>Tools assigned</th>
<th>Inputs</th>
<th>Output</th>
<th>External queries</th>
</tr>
</thead>
<tbody>
<tr>
<td>Pattern Finder</td>
<td>None</td>
<td>User details, investigation guide, past cases, current event</td>
<td>JSON</td>
<td>No</td>
</tr>
<tr>
<td>L1 Investigation</td>
<td>Yes</td>
<td>Pattern JSON, investigation guide, current alert</td>
<td>Structured report</td>
<td>Yes</td>
</tr>
<tr>
<td>Summarizer</td>
<td>None</td>
<td>Investigator report, pattern JSON, investigation guide, ES|QL queries</td>
<td>Markdown for Slack and Cases</td>
<td>No</td>
</tr>
</tbody>
</table>
<h3>Pattern agent: Finding patterns across closed cases</h3>
<p>Our Pattern Finder Agent takes in many inputs (user details, investigation guide, past case records, details about the current event) and makes pattern comparisons across historical cases. <em>This agent has no tools assigned to it in Agent Builder and doesn’t make any external queries. (It only uses the data given to it.)</em> That makes this agent much faster and ensures that the behavior cannot be overridden by injected content. Here’s a snippet of the prompt where we talk about our feedback loop:</p>
<pre><code>    ## AI Notes on Patterns
        Look for patterns across these fields in past_case_records:
        - workflow_reason (benign_positive, true_positive, etc.) This is the ANALYST PICKED result and should be seen as the truth.
        - custom_field_ai_verdict. This is picked by AI - Is AI usually correct on this type of case?
        - custom_field_ai_confidence. This is picked by AI - Is AI usually correct on this type of case?
        - custom_field_ai_incorrect_analyst_bool - This is a boolean the analyst can choose to say that AI was incorrect here.
        - custom_field_summary - This is written by an analyst on why they closed the case. This should be seen as the truth.
        - comments_text (analyst comments and AI summaries)
        - user_id, user_name, user_email (repeat offenders?)
        - source_ip, host_name (recurring infrastructure?)
        - If there is another case with the exact same type of activity, ALWAYS add the case # and a link to the case so we can reference it.
        - Also consider inputs.user_job_family and inputs.user_job_title when assessing whether the alert pattern makes sense for this user type.
</code></pre>
<p>At the end of the prompt, we have it return its findings in JSON format:</p>
<pre><code> ## Required Output Format
        Respond with ONLY a valid JSON object, no markdown, no explanation:
        {
          &quot;percentage_false_positive_or_benign_last_50_cases&quot;: &quot;&lt;X&gt;%&quot;,
          &quot;number_of_total_alerts&quot;: &lt;inputs.total_alerts_for_rule&gt;,
          &quot;number_of_alerts_worked_in_cases&quot;: &lt;inputs.total_closed_with_cases&gt;,
          &quot;uniqueness&quot;: &quot;RARE or COMMON&quot;,
          &quot;high_risk_employee&quot;: &quot;NEW HIRE or LEAVING SOON or NO&quot;,
          &quot;job_title&quot;: &quot;&lt;inputs.user_job_title or unknown&gt;&quot;,
          &quot;cost_center&quot;: &quot;&lt;from user details or unknown&gt;&quot;,
          &quot;ai_notes_on_patterns&quot;: &quot;&lt;your pattern analysis here&gt;&quot;,
          &quot;ai_pattern_TLDR&quot;:&quot;&lt;shortened_version_if_notes_long&gt;&quot;,
          &quot;recommendation_for_severity&quot;: &quot;medium or high or critical&quot;
          &quot;explanation_on_severity_change_or_no&quot;: &quot;&lt;explanation_here&gt;&quot;
        }
</code></pre>
<h3>L1 Investigation Agent: Gathering evidence and reaching a verdict</h3>
<p>The Investigation Agent takes in the JSON from the Pattern Finder Agent, the investigation guide, and information about the current case and alert. Most importantly, it first looks at the investigation guide we grabbed above, which our detection engineers fill out with per-detection rule instructions that are the most correct for the agent to follow. If there’s no investigation guide, the agent falls back to its generic alert triage instructions:</p>
<pre><code># Triage Protocol
&gt; **📋 Investigation Priority:** If an Investigation Guide is provided in the input, follow its triage steps as your primary instructions. The steps below are **fallback guidance only** — use them when no Investigation Guide is available or to fill gaps the guide does not cover.
</code></pre>
<p>Using the investigation guide and its own judgment, this agent makes external queries to gather additional evidence and reach a verdict on the case. At the end of the prompt, we have the investigator agent return a report:</p>
<pre><code>INVESTIGATOR_REPORT_START

&lt;emoji&gt; Verdict: &lt;verdict&gt; | 🎯 Confidence: &lt;0–100%&gt; | 📋 Reason: &lt;max 50 words&gt; |

INPUT_TYPE: Alert or Case
RULE_NAME_OR_CASE_TITLE: &lt;&gt;
SEVERITY: &lt;&gt;
STATUS: &lt;&gt;
TIMESTAMP: &lt;&gt;
ALERT_QUERY: &lt;raw detection query from kibana.alert.rule.parameters — omit entire field if not retrievable&gt;
WHY_FIRED: &lt;1 sentence — specific technical condition that matched&gt;
WHEN_MALICIOUS: &lt;1 sentence — malicious pattern and whether this alert DOES or DOES NOT match it&gt;

ACTOR: &lt;user.name&gt; | ASSET_INFO: &lt;&gt;
HOST: &lt;host.name&gt; | ASSET_INFO: &lt;&gt;
KEY_ACTION: &lt;event.action&gt; via &lt;process.name&gt;
COMMAND_CONTEXT: &lt;process.command_line or case description snippet&gt;
OBSERVABLES: &lt;list key IPs, hashes, users, projects&gt;

EXECUTIVE_SUMMARY: &lt;2–3 sentence summary&gt;

INVESTIGATION_FINDINGS: &lt;detailed findings from queries, enrichment, pivoting&gt;

CORRELATED_ALERT_CLUSTER: &lt;If the concurrent pivot returned results, render the full table here with columns: Time | Rule | Severity | Detail. Include the time window
(e.g. &quot;±5 min around 18:16 UTC&quot;). If the pivot returned 0 results, write
&quot;No correlated alerts found in ±5 min window.&quot;&gt;

PATTERN_FINDER_ALIGNMENT: &lt;does the current alert match or contradict the PatternFinder analysis? explain&gt;

ESQL_QUERIES_RUN: &lt;comma-separated list of raw ES|QL query strings run during investigation&gt;

RECOMMENDED_ACTIONS:
- CONFIRM_ACTIVITY: &lt;what to look at or who to ask&gt;
- REMEDIATION: &lt;containment, tuning, or additional data needed&gt;
- RULE_TUNING: &lt;specific improvement or &quot;Rule performed as intended — no tuning needed&quot;&gt;

INVESTIGATOR_REPORT_END
</code></pre>
<h3>Summarizer Agent: Writing the case comment that analysts read</h3>
<p>The Summarizer Agent is responsible for formatting the final AI summary that analysts see in the case comment. Like the Pattern Finder Agent, it has no tools assigned to it in Agent Builder and works only from data passed to it. This keeps it fast. Each time a new alert comes in for the same case, we rerun the Agent Brainstorm workflow and replace the case comment only if it provides a new verdict or new important evidence; otherwise, we skip it. We allow the summarizer to keep replacing itself up until the case is assigned to an analyst, at which point we stop.</p>
<p>Here’s a snippet of the summarizer prompt:</p>
<pre><code># Role &amp; Objective
You are an expert InfoSec report formatter at Elastic. You receive a structured investigation report from the Investigator agent and a pattern analysis from the PatternFinder agent. Your sole job is to combine these into a high-fidelity Markdown summary formatted for Slack. You run NO queries and make NO investigative decisions — all findings and verdicts come from the Investigator and PatternFinder.

You will also receive:
- `INVESTIGATOR_REPORT` — the full structured output from the Investigator agent
- `PATTERN_FINDER_OUTPUT` — the JSON analysis from the PatternFinder agent
- `INVESTIGATION_GUIDE` - the investigation guide attached to the alert that gives suggestions to analyst and AI on how to handle this case
- `ESQL_QUERIES` — comma-separated ES|QL query strings run during investigation, to be formatted as reference links
</code></pre>
<p>Then we ask it to build an output template to post to the case comment:</p>
<pre><code># Final Output Template

🤖 **AI Generated Summary** 🤖

`&lt;emoji&gt; Verdict: &lt;verdict&gt; | 🎯 Confidence: &lt;0–100%&gt; | 📋 Reason: &lt;max 50 words&gt; |`

**⚡ TLDR**
&gt; {1-2 sentences max. Who did what, on what system, and what the verdict is. Written for an analyst who has 5 seconds. No jargon, no hedging.}

** Investigation Guide Suggestions** (only include if it is present)
- Quick summary of what the analyst should do next based on the investigation guide or how to confirm this activity. Use bullet points if possible

....shortened for purposes of this blog...
</code></pre>
<p>Now that we’ve reviewed each agent's role, here's how they're configured in workflow YAML. Each agent is executed one after the other in a synchronous way, waiting for the one above it to finish:</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image8.png" alt="YAML setup of our three agents in the Agent Brainstorm workflow." /></p>
<p>We use two workflows to make POST requests at the end of the Agent Brainstorm to attach the generated final AI summary to both the Kibana case and Slack. Below are some real examples from our environment:</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image4.png" alt="Comment on Kibana case" /></p>
<p>We send alert details to our team Slack channel so that we can monitor the alerts from our phone. Our Slack bot in this channel allows us to perform activities, like <strong>Acknowledge Case</strong> or <strong>Merge Case</strong>, all from Slack, allowing us to quickly take action, even directly from a mobile device. After implementing the work in this blog, we added the <strong>Close Case: AI Correct</strong> button, which you can click to trigger a workflow that assigns you to the case, fills out the closure notes, tags the case, and closes it.</p>
<p><img src="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image6.png" alt="Slack message with AI Verdict added." /></p>
<p>Our AI triager that started off as AI slop is now a core part of how our SOC operates. We’ve kept our analysts in the loop and enabled them to close cases in seconds with our one-button close after AI triages the case. If you're building something similar, we'd love to hear about it in the <a href="https://discuss.elastic.co/c/security">Elastic Security community forum</a>.</p>
<h2>Frequently asked questions</h2>
<p><strong>Why three agents instead of one agent with a lot of skills?</strong><br />
A member of our team did a <a href="https://www.elastic.co/security-labs/agentic-soc-token-budget-architecture">cost analysis</a> of using one agent with a lot of skills versus multiple agents and the token usage came back significantly lower with the latter.</p>
<p><strong>Why don’t we just have the agent run the query rather than providing it with data we’ve already received from ES|QL?</strong><br />
AI agents process pre-fetched data significantly faster than querying indexes directly, and it's cheaper.</p>
<h2>What to build after L1 triage automation</h2>
<p>After the L1 agent runs, a great next step is to dispatch specialized agents based on the alert type or data source; for example, a Windows forensics agent or an Azure agent, or possibly a Linux forensics agent. Read our team's writeup on <a href="https://www.elastic.co/security-labs/alert-triage-agentic-soc-elastic-workflows">L2 and L3 agent escalation</a>.</p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/security-labs/assets/images/alert-triage-agentic-soc-self-correcting-agents/image2.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>