← q52 Labs

Agentic SOC Architecture

Agentic SOC platform processing alerts factory

Overview

Automation in the SOC is too often pursued for its own sake: faster closures, fewer touches, and a dashboard that rewards volume over judgment. This design starts from the opposite premise. The goal isn’t to remove analysts from the loop. It’s to protect their attention for the work that deserves it. The mundane is machine work: sorting telemetry from signal, confirming that an audit event was expected, gathering the same enrichment for the thousandth time, and drafting the summary nobody enjoys writing. Each of those is delegated to a narrow, single-purpose agent that must show its evidence and say what it doesn’t know. The consequential work stays with people: weighing ambiguity, deciding whether to isolate a host that a clinic or a factory floor depends on, and choosing when to pick up the phone. The agents advise, but they never contain, and every path where confidence is low, data is missing, or the stakes are high ends at a human with the full case already assembled. When the system works as intended, the measure of success isn’t how many cases the machine closed. It’s how much more time a person has to think carefully about the few that
matter. At q52.ai, automation is there to give analyst judgment more room.

Solution Brief

Platform: Google Security Operations (SIEM + SOAR) with Gemini 

An orchestrator playbook runs five single-purpose agents in sequence, routing between them with deterministic branches. Each agent is a nested playbook block that

(1) assembles context from case data,

(2) calls Gemini with a tightly specified prompt,

(3) sanitizes and regex-parses the response,

(4) validates it with fail-safe defaults, and

(5) writes typed values to namespaced case custom fields.

3.1 Triage Agent

Question answeredWhat kind of item is this?
InputsCase title, description, environment name, original alert JSON
Output fieldsclassification (alert, signal, telemetry, audit), confidence (0-1), severity_recommendation (Information, Low, Medium, High, Critical), recommended_path (investigate, enrich_only, observe, close_candidate), evidence_fields_evaluated, reasoning_summary
Case fields writtentriage_classification, triage_confidence, triage_severity, triage_route, triage_requires_human_review, triage_evidence_fields_evaluated, triage_version
Prompt hard rules(1) If the security product reports BLOCK/DROP/DENY, classify as signal or close-candidate, never alert. (2) Known authorized-scanner callback domains in the payload mean signal or close-candidate. (3) Only after (1) and (2), evaluate signature, severity label and volume. (4) Vulnerability or exposure findings without exploitation evidence are signals. (5) Do not claim exploitation unless the alert states it.
Playbook validationEmpty classification: default to signal, confidence 0, human review true, route to investigator. Confidence above 1 clamps to 1; below 0 clamps to 0. Confidence below 0.50 sets human review.
Playbook actionsSets case stage, tags environment type, maps advisory severity to case and alert priority, writes a completion comment.

3.2 Investigator Agent (two-pass, evidence-seeking)

Question answeredWhat happened, who or what is involved, and how sure are we?
Pass 1 prompt“Context engine”: frame a hypothesis, list existing evidence and gaps, choose enrichment categories. Forbidden: attacker intent without evidence, containment advice, final verdict.
Pass 1 outputinvestigation_summary, confidence_in_story, existing_evidence, evidence_gaps, recommended_enrichment (identity, asset, network, authentication, vulnerability, threat_intel, timeline, none), investigation_risk_level
Confidence bandingStrong at 0.70 or above, weak at or below 0.50, normal otherwise; stored as case context.
Tool discoveryReads the tenant’s integration inventory and only calls products that exist for that environment (EDR, directory/IdP, firewall analyzer, vulnerability scanner, threat intel). Environments without tool coverage get an explicit “no tool enrichment” comment.
Enrichment branchesAsset and identity; network and domain; timeline (similar alerts plus similar cases by rule and by entity); threat intel (IOC reputation and sandbox); vulnerability (scanner data plus a Gemini-generated UDM query against ingested scan logs). Each runs only if Pass 1 requested it.
NarratorsOne Gemini call per domain. Each answers 5-6 fixed questions in one paragraph and is told it is not determining a verdict. Output is sanitized and stored in an enrichment_* field.
Pass 2 prompt“Reasoning engine”: reassess using only the supplied evidence; missing data means unknown, not safe.
Pass 2 outputinvestigation_disposition (true_positive, false_positive, benign, inconclusive), investigation_confidence, investigation_evidence_summary, investigation_behavioral_signal_assessment, investigation_asset_identity_risk_context, investigation_enrichment_consensus (agree, conflict, insufficient), investigation_key_uncertainties, investigation_recommended_action (no_action, monitor_only, containment_recommended, containment_required, escalate_for_human_review)
Failure handlingIf Pass 2 returns a blank action, write “Summary Data Blank” and let downstream placeholder logic force escalation.

3.3 Response Advisor Agent

Question answeredWhat should the customer or operator do next?
Prompt A: capability assessmentFrom the integration inventory only, determine capability_endpoint_isolation, capability_network_blocking, capability_identity_control, capability_email_control, plus capability_confidence and notes. Shared case-management integrations do not count. Ambiguous means unavailable.
Prompt B: recommendationInputs: alert, capabilities, triage fields, investigation fields.
Outputresponse_decision (escalate, contain_recommended, contain_required, monitor_only, no_action), response_containment_eligible, response_approval_required, response_containment_targets (type:value list or none), response_immediate_next_steps, response_recommendation_summary, response_rationale, response_risk_notes, response_version
Guardrails (in prompt)Containment only if disposition is true_positive AND confidence is at least 0.7 AND behavior supports it. Targets must be explicitly named in the evidence, otherwise “none”. Weak confidence or material uncertainty: escalate or monitor-only, approval required. If containment could cause significant service impact, never “required,” at most “recommended.” Do not contradict the investigation without new evidence.
Re-entryA stage check detects duplicate assessment runs and closes the duplicate alert.

3.4 Disposition Validator (control gate)

Question answeredGiven everything, what is the case’s final handling?
Mostly deterministicVerifies triage, investigation and response fields all exist. If not: abort, comment, escalate.
Decision rulesEscalate when response_decision = escalate AND human review is flagged AND investigation confidence meets threshold AND the recommended action says escalate. Contain branches key off contain_required and contain_recommended. Monitor-only keys off low confidence with false_positive or benign. No match: escalate for human review.
Priority tableFixed mapping of (disposition x triage severity x classification) to case and alert priority.
Gemini’s only jobClosure note (3-4 sentences) and closure_reason from a fixed enum (Human error, Legit action, Misconfigured system, Rule under construction) for clean reporting.
Containment outcomePosts “no response actions have been taken,” raises an incident, assigns to a human.

3.5 Notification Dispatcher

Question answeredWho needs to know, how, and what do we tell them?
Summary promptOutputs case_summary, text_format (character-limited SMS), phone_format (word-limited TTS script), method (text, voice, email, ticket, messenger). Rules bias method selection by detection source and environment type.
Team picker promptPlain-language role definitions in the prompt; model returns one or more workgroup labels from a closed list based on the entities and actions involved.
SBAR email promptSituation, Background, Assessment, Recommendation; includes containment advice, unknowns and next steps.
Gemini case summaryNative “Create Gemini Case Summary” action attached to the case record.
Human gateAnalyst prompt: create ticket, close case, or stop automation. SLA clock pauses once customer communication is raised.
Known gapVoice is recommended but handled manually; automated TTS is not implemented.

3.6 Non-incident handlers

  • Audit handler: confirm the activity was expected or authorized; if so, close as legitimate and move the case to an improvement stage; otherwise send it on to response assessment.
  • Telemetry handler: record why the item is telemetry, post to a baseline log channel, lower priority, close. Also receives investigator “benign” outcomes.

 Case state contract

triage_*TriageInvestigator, Response Advisor, Disposition, Notifier
enrichment_*Investigator narratorsInvestigator Pass 2
investigation_*Investigator Pass 2Response Advisor, Disposition, Notifier
capability_*Response Advisor (assessment)Response Advisor (recommendation)
response_*Response AdvisorDisposition, Notifier
isEscalated, EscalationMethodNotifierSLA and assignment logic

Standalone-run safety: every agent that reads upstream fields first checks for them and, if absent, writes labeled placeholders (for example triage unknown, investigation inconclusive at 0.30, response DEFER) so it can run ad hoc without the full chain.


5. Routing matrix

Not the first alert on the caseClose as duplicate of the case under investigation
Triage = auditAudit handler; if expected, close; else Response Advisor
Triage = telemetryTelemetry handler
Triage = alert or signalInvestigator
Investigation = true_positiveResponse Advisor
Investigation = benignTelemetry handler
Investigation = false_positiveDisposition Validator
Investigation = inconclusiveDisposition Validator (human review)
Response = monitor_only or no_actionClose as not malicious with AI-written note
Response = escalate, contain_recommended, contain_requiredDisposition Validator, then Notification Dispatcher
Disposition = escalateNotification Dispatcher, human assignment
Disposition = containNotification Dispatcher, containment NOT executed
Required agent output missingAbort, escalate to human

Evolution of the design

The playbook inventory shows these distinct generations. The ordering is inferred from their complexity; confirm the dates against your own history before publishing a timeline.

0Alert explainer: plain-language description of an alert for Tier 1/Tier 2No decision support
1Quick verdict: two 1-100 scores (malicious, misconfiguration), regex-extracted, threshold branches; manual SBAR and auto-close variantsScores carry no evidence; one prompt did everything
2Gemini-generated UDM query, executed in the SIEM, results analyzed by a second callUseful pattern, but ad hoc and single-purpose
3Five-agent chain with typed contracts, conditional enrichment, hard guardrails, deterministic control gateCurrent design

Prompt-engineering patterns

  1. Role line + non-goals. “You are a SOC [role]. You are NOT determining a verdict / executing containment.”
  2. Output contract. Exact labels, closed value sets, one value per line, no markdown/JSON/code blocks.
  3. Evidence discipline. “Use only the data provided. Missing data is unknown, not safe. Placeholders in brackets are missing.”
  4. Ordered evaluation. Force checks in sequence before any judgment (action outcome, then scanner infrastructure, then signature).
  5. Absolute disqualifiers. State which conditions cannot be overridden by severity labels or signature names.
  6. Definitions inline. Define every enum value in the prompt.
  7. Narrators vs deciders. Narrators answer fixed questions; only one prompt may produce a verdict.
  8. Capability grounding. Pass in what the tenant can actually do and require ignoring everything else.
  9. Similar history as awareness only. Never as equivalence.
  10. Closure-reason enums. Free text for humans, enums for reporting.

SOAR plumbing patterns

  • HTML-wrapped responses: sanitize, strip paragraph tags, regex each labeled line into its own variable.
  • Parallel extraction containers for the 5-10 fields per agent.
  • Fail-safe branch after every parse: empty classification, blank action, out-of-range confidence.
  • Case stage as state machine: Triage, Investigation, Assessment/Research, Incident/Escalation, Improvement.
  • First-alert gate at the orchestrator and a re-entry gate in the advisor.
  • Per-agent trigger playbooks to run, test and retire agents independently of the master flow.
  • Notification log channels separated by class (incident, signals and leads, audit and telemetry).

Risks and recommendations

  • LLM confidence is uncalibrated; the thresholds (0.50 human review, 0.70 containment) are routing heuristics. Calibrate against analyst outcomes per detection source.
  • Several guardrails are encoded in prompts only. Mirror the critical ones (blocked events, containment eligibility) as deterministic playbook checks.
  • Keep a regression set of past cases and re-run it after each prompt change.
  • Track: analyst override rate, auto-close precision, false-escalation rate, median time to triage. [INSERT METRIC]
  • Voice notification and human-approved containment execution are the main open items.

Mermaid Diagram

%% q52.ai - Agentic SOC - generalized playbook flow
%% Paste into any Mermaid renderer (mermaid.live, GitHub, Notion, Confluence, WordPress plugin).
%% Legend: rounded = agent (LLM reasoning), rectangle = deterministic playbook logic,
%%         diamond = deterministic router, cylinder = shared case state, hexagon = human gate.
flowchart TD
    classDef agent fill:#e8f0fe,stroke:#1a73e8,stroke-width:2px,color:#0b2a5b
    classDef det fill:#f1f3f4,stroke:#5f6368,color:#202124
    classDef router fill:#fef7e0,stroke:#f9ab00,color:#4a3500
    classDef human fill:#fce8e6,stroke:#d93025,stroke-width:2px,color:#5c0a05
    classDef state fill:#e6f4ea,stroke:#188038,color:#0d3b1a
    classDef terminal fill:#ffffff,stroke:#202124,stroke-dasharray:4 3,color:#202124

    START(["Detection fires<br/>alert ingested into SOAR case"]):::terminal
    DEDUP{"First alert<br/>for this case?"}:::router
    DUP(["Close as duplicate<br/>already under investigation"]):::terminal

    subgraph STATE["Shared case state - typed custom fields are the agent contract"]
        direction LR
        S1[("triage_*")]:::state
        S2[("investigation_*")]:::state
        S3[("response_*")]:::state
        S4[("capability_*")]:::state
    end

    subgraph TRIAGE["Stage 1 - Triage Agent"]
        T1("LLM: classify<br/>alert / signal / telemetry / audit<br/>+ confidence + severity + path"):::agent
        T2["Parse output<br/>sanitize + regex-extract fields"]:::det
        T3{"Output valid?<br/>confidence 0-1?"}:::router
        T4["Fail-safe defaults<br/>class=signal, conf=0<br/>human_review=true"]:::det
        T5{"Confidence<br/>below 0.50?"}:::router
        T6["Flag human review"]:::det
        T7["Map fields to case state<br/>set advisory severity"]:::det
        T1 --> T2 --> T3
        T3 -- "no" --> T4 --> T7
        T3 -- "yes" --> T5
        T5 -- "yes" --> T6 --> T7
        T5 -- "no" --> T7
    end

    ROUTE{"Classification<br/>router"}:::router

    subgraph NONSEC["Non-incident handlers"]
        AUD("Audit handler<br/>is this expected / authorized?"):::agent
        AUDQ{"Expected?"}:::router
        TEL["Telemetry handler<br/>retain for baseline<br/>log + close"]:::det
    end

    subgraph INVEST["Stage 2 - Investigator Agent, two-pass"]
        I1("Pass 1: LLM frames hypothesis<br/>evidence gaps + recommended enrichments"):::agent
        I2{"Pass-1 confidence<br/>strong / normal / weak"}:::router
        I3{"Any enrichment<br/>recommended?"}:::router
        I0["Discover available integrations<br/>per-tenant inventory"]:::det
        subgraph FAN["Conditional enrichment fan-out - only what Pass 1 asked for"]
            direction LR
            E1["Asset + identity<br/>EDR / directory / IdP"]:::det
            E2["Network + domain<br/>WHOIS / reputation / SIEM context"]:::det
            E3["Timeline<br/>similar alerts + similar cases"]:::det
            E4["Threat intel<br/>IOC reputation"]:::det
            E5["Vulnerability<br/>scanner data + generated query"]:::det
        end
        N1("Per-domain narrator LLMs<br/>answer fixed questions<br/>never issue a verdict"):::agent
        I4("Pass 2: reasoning LLM<br/>typed verdict from evidence only<br/>disposition + confidence + uncertainties"):::agent
        I5["Validate + write investigation_*"]:::det
        I1 --> I2 --> I3
        I3 -- "none" --> I4
        I3 -- "some" --> I0 --> FAN --> N1 --> I4
        I4 --> I5
    end

    VERD{"Investigation<br/>disposition"}:::router

    subgraph RESP["Stage 3 - Response Advisor Agent, advisory only"]
        R1("LLM: capability assessment<br/>from integration inventory<br/>isolate / block / identity / email"):::agent
        R2("LLM: response recommendation<br/>under hard guardrails"):::agent
        R3{"Decision"}:::router
        R1 --> R2 --> R3
    end

    subgraph DISP["Stage 4 - Disposition Validator, final control gate"]
        D1{"All upstream outputs<br/>present?"}:::router
        D2["Deterministic rules<br/>triage x investigation x response"]:::det
        D3{"Outcome"}:::router
        D4("LLM: closure note<br/>+ reason from fixed enum"):::agent
        D5["Auto-close case"]:::det
        D6["Escalate to human analyst<br/>raise priority"]:::det
        D7["Containment recommended / required<br/>NOT EXECUTED - human approves"]:::det
        D8["Abort and escalate<br/>missing agent output"]:::det
        D1 -- "no" --> D8
        D1 -- "yes" --> D2 --> D3
        D3 -- "monitor / false positive / benign" --> D4 --> D5
        D3 -- "escalate / unknown" --> D6
        D3 -- "contain" --> D7
    end

    subgraph NOTIFY["Stage 5 - Notification Dispatcher"]
        N2("LLM: summaries<br/>email SBAR, SMS, voice script"):::agent
        N3("LLM: pick responsible team(s)<br/>from role definitions"):::agent
        N4("LLM: pick channel(s)<br/>text / voice / email / ticket / chat"):::agent
        N5{{"Analyst confirms<br/>ticket / close / stop automation"}}:::human
        N6["Post to notification log<br/>pause SLA clock"]:::det
        N7["Create ticket<br/>notify customer / team"]:::det
        N2 --> N3 --> N4 --> N5
        N5 -- "ticket" --> N7
        N5 -- "close" --> D5
        N5 -- "stop" --> N6
        N7 --> N6
    end

    HUMAN(["Human analyst owns the case"]):::human
    CLOSED(["Case closed with audit trail"]):::terminal

    START --> DEDUP
    DEDUP -- "no" --> DUP
    DEDUP -- "yes" --> T1
    T7 --> ROUTE
    ROUTE -- "audit" --> AUD --> AUDQ
    AUDQ -- "yes" --> D5
    AUDQ -- "no" --> R1
    ROUTE -- "telemetry" --> TEL
    ROUTE -- "alert / signal" --> I1
    I5 --> VERD
    VERD -- "true_positive" --> R1
    VERD -- "benign" --> TEL
    VERD -- "false_positive / inconclusive" --> D1
    R3 -- "monitor_only / no_action" --> D5
    R3 -- "escalate / contain_*" --> D1
    D6 --> N2
    D7 --> N2
    D8 --> N2
    N6 --> HUMAN
    D5 --> CLOSED

    T7 -.-> S1
    I5 -.-> S2
    R3 -.-> S3
    R1 -.-> S4
    S1 -.-> I1
    S1 -.-> R2
    S2 -.-> R2
    S2 -.-> D2
    S3 -.-> D2
    S4 -.-> R2

Discover more from q52.ai

Subscribe now to keep reading and get access to the full archive.

Continue reading