The most dangerous AI output in a SOC is the one that is almost right: a confident, fluent verdict that a busy analyst rubber-stamps. This playbook is the counter-move — a structured adversarial audit that takes an AI-generated triage verdict and interrogates whether the evidence actually supports it.
Tier: Core · Workflow area: AI-assisted SOC & detection
You are not asking the model to be right; you are building a reflex for catching when it is wrong.
Do this on every AI verdict that would trigger an escalation or a close-as-benign — those are the two directions a wrong conclusion hurts most.
Separate the verdict from its evidence. Take the AI's output — verdict, rationale, cited fields — and the raw alert it was drawn from. Keep them as two distinct blocks so the audit compares them rather than re-reading the summary.
Run an adversarial self-check prompt. Use a different model instance (or a fresh context) as the auditor, so it does not defend the original reasoning.
You are auditing another AI's triage verdict against the raw evidence.
Do not decide the alert yourself. Instead, for each claim in the rationale:
- Quote the claim.
- State whether the raw evidence SUPPORTS, CONTRADICTS, or does NOT MENTION it.
- If CONTRADICTS or NOT MENTIONED, explain the gap.
Then answer: does the verdict survive if we drop every unsupported claim?
[VERDICT + RATIONALE]
...
[RAW ALERT EVIDENCE]
...
Expected output: a claim-by-claim ledger. The killer line is the last one — if the verdict collapses once unsupported claims are removed, it was resting on invented evidence.
For structured pipelines, enforce the audit in code. When the triage AI is part of an app, parse the audit into a schema so an unsupported verdict cannot auto-close a case.
import anthropic
from pydantic import BaseModel
class ClaimCheck(BaseModel):
claim: str
status: str # SUPPORTED | CONTRADICTS | NOT_MENTIONED
gap: str
class Audit(BaseModel):
claims: list[ClaimCheck]
verdict_survives: bool
reasoning: str
client = anthropic.Anthropic()
response = client.messages.parse(
model="claude-opus-4-8",
max_tokens=2000,
thinking={"type": "adaptive"},
messages=[{"role": "user", "content": audit_prompt}],
output_format=Audit,
)
result = response.parsed_output
if not result.verdict_survives:
route_to_human(result) # never auto-close on unsupported evidence
Escalate every collapse to a human. Any verdict where verdict_survives is false, or where the auditor found a CONTRADICTS, goes to manual review. That is the whole safety property.
Log the catches. Keep a tally of what the audit caught. Recurring failure patterns (the AI always over-trusts svchost.exe) become new rules in your triage prompt.
A rationale that sounds authoritative and a rationale that is grounded are independent properties. The audit measures the second; the first is what fools people. Always test whether the verdict survives dropping unsupported claims.
messages.parse): turns the audit into a machine-checkable gate that blocks auto-close on unsupported verdicts.The audit itself is a safety control, so it belongs inside the client boundary. Run both the original triage model and the auditor on a local Ollama model or the client's approved AI endpoint, never a personal account. Redact the raw alert evidence — hostnames, users, internal IPs to placeholders — before either model sees it; the audit checks structural support ("is this field present?"), which survives redaction intact. Store the claim-by-claim ledger and the catch log in the client's case system so the verification trail is auditable by the client, not held on your machine.