Ansh Saxena

SOC Triage Agent An open-source LangGraph service for agentic SOC alert triage

SOC Triage Agent is an open-source service by Ansh Saxena. It ingests alerts from Splunk, CrowdStrike, AWS GuardDuty and other sources, normalises them, and enriches them through MITRE-routed tools. It then produces a structured TriageVerdict using LangGraph and LLM reasoning.

github.com/AnshSaxena05/cyberSecurity_alert_triage

Problem

Agentic AI systems fail in production when they hand the LLM decisions that could be made deterministically. Choosing the query window, the source system, the time range and the API parameters are lookup tasks, not reasoning tasks. When an LLM makes them, results become unreliable and it can hallucinate.

Design: determinism first

The system inverts that pattern. The LLM only reasons about meaning: what the data suggests, whether an alert is a false positive, and what the attacker's objective is. Plain Python code makes every structural decision.

The pipeline is split into two phases:

How it works

  1. Ingest. POST /ingest/{source} accepts splunk, crowdstrike, aws_guardduty, sentinel or generic. POST /ingest/auto detects the source from the payload shape. Triage runs asynchronously: the POST returns 202 with an alert_id, and the client polls GET /verdict/{alert_id} until the status is complete.
  2. Normalise. Every source maps to one OCSF-aligned NormalizedAlert. The alert_id is a SHA-256 hash of the source and its alert ID, so duplicate webhook deliveries get the same ID and can be deduplicated.
  3. Extract entities. Pydantic enforces the ExtractedEntities schema (hosts, users, processes, techniques, IOCs), so malformed LLM output cannot reach the routing layer.
  4. Route by MITRE technique. A technique-to-tools routing table gives O(1) lookup, falling back from a sub-technique to its parent technique and then to a default route. Look-back windows are fixed per signal type (for example 72h for process execution and 14d for authentication).
  5. Set a tool budget by severity. The budget is 3 for LOW, 5 for MEDIUM, 8 for HIGH and 99 (effectively unlimited) for CRITICAL. The budget check runs in Python, so the LLM never controls it.
  6. Burst enrichment. asyncio.gather fires all routed tools in parallel, each with its own timeout. A tool that fails degrades gracefully to source_available: False and never raises. Each result is compressed to at most 200 tokens before the LLM sees it.
  7. ReAct follow-up. The LangGraph agent_think node can plan more tool calls. The loop continues only while the model asks for more tools and the budget is still positive.
  8. Generate verdict. The verdict has 6 sections: severity with justification, MITRE assessments, a triage summary, confirmed IOCs (only those corroborated by enrichment), prioritised immediate actions, and an escalation decision (ESCALATE_IR, FALSE_POSITIVE, MONITOR or CLOSE) with rationale.

Safety and reliability

Stack

Python 3.14+ · FastAPI · LangGraph · Pydantic · Ollama or OpenAI (optional) · Redis (optional) · Langfuse (optional) · structlog · Docker Compose (app + Redis + Ollama) · pytest · uv.

Event-driven runtime (optional, behind a config flag): NATS JetStream for alert ingest, with a write-ahead log and a reconciler that republishes anything the broker missed · PostgreSQL via asyncpg (secrets and audit) · JWT service auth · OpenTelemetry with the OTLP exporter · ClickHouse in the dev stack · a Cloudflare Workers + Durable Objects gateway in TypeScript that holds analyst WebSockets at the edge · a Next.js frontend. The API is documented with OpenAPI, Swagger UI and ReDoc. The default test suite needs no external APIs.

Links