SentriScope LLM Architecture
Natural language to SQL pipeline, semantic registry, query modes, schema grounding, guardrails, and platform-level LLM configuration.
1. Purpose
The LLM layer enables security operators to query canonical data using natural language. It translates user questions into validated SQL, executes against the canonical database, and returns formatted, evidence-backed answers — without exposing the database schema directly or allowing unrestricted SQL execution.
Key design decision: LLM is never in the critical risk-scoring path. Risk calculation is deterministic. LLM only queries pre-existing canonical data.
2. NL→SQL Pipeline (10-Step Execution Model)
[1] User submits natural language question
│
[2] Intent classification → IntentResult
│ question_class, query_mode, is_count_intent, requires_correlation
│
[3] Contract lookup from Semantic Registry
│ ContractKey → QueryContract (table, join rules, evidence fields)
│
[4] Scalar path check
│ If query_mode=SCALAR_AGGREGATE and scalar compiler returns SQL:
│ → Skip to [8] with deterministic SQL (no LLM)
│
[5] LLM Query Planner (non-scalar path)
│ Generates structured query_plan: table, filters, joins, evidence_fields
│
[6] Query Plan Validation (authoritative — cannot be bypassed)
│ validate_query_plan(plan, intent_result, contract)
│ Reject on any failure — no auto-correct
│
[7] SQL Generation from validated plan
│ LLM or deterministic compiler generates SQL
│
[8] SQL Validation
│ validate_sql(sql, contract)
│ Check allowed tables, no DDL/DML, injection patterns, column existence
│
[9] Execute SQL (read-only, tenant-scoped)
│ assert_tenant_context_set()
│ Execute within tenant context
│
[10] Format and return response with evidence
Rule: Validation is authoritative. If validation fails, execution is rejected. The LLM output is never trusted without validation.
3. Query Mode Model
The intent router classifies every question into a query mode in addition to a question class:
| Mode | Description | Evidence Required | LLM Planner Required |
|---|---|---|---|
scalar_aggregate |
Single count ("How many exposures?") | No | No — deterministic compiler |
grouped_aggregate |
Counts by dimension ("by severity") | Per contract | Yes |
entity_listing |
List/show/top N entities | Per contract | Yes |
investigation |
Correlate entities (IOC + asset + incident) | Yes (strict) | Yes |
Scalar Aggregate Path
classify_intent(question)→IntentResult(query_mode="scalar_aggregate", is_count_intent=True)- If
is_scalar_count_supported(intent_result, contract)→ scalar compiler generates deterministic SQL - Validate with
query_mode=SCALAR_AGGREGATE(evidence not required) - Execute and return formatted count answer
- No LLM SQL generation — no planner
4. Semantic Registry and Contract Model
The Semantic Registry maps intent → contract. A contract defines:
| Contract Field | Description |
|---|---|
table |
Primary canonical table for this question class |
allowed_joins |
Tables that may be joined |
required_evidence_fields |
Columns that must appear in SELECT |
forbidden_columns |
Columns never permitted (PII, credentials) |
masking_rules |
Column-level masking specifications |
is_scalar_count_supported |
Whether scalar compiler can handle this class |
Contracts are stored in apps/llm/registry/contracts.py. Every question class maps to exactly one contract.
5. SQL Guardrails
The SQL validator enforces these rules before any execution:
| Rule | Enforcement |
|---|---|
| Read-only | No INSERT, UPDATE, DELETE, DROP, TRUNCATE, CREATE |
| Allowed tables only | SQL may only reference tables defined in contract |
| Column existence | All referenced columns must exist in canonical schema |
| No subquery injection | Nested subqueries validated against allowed tables |
| Tenant context | assert_tenant_context_set() before execution |
| Injection patterns | Pattern-based detection of prompt injection attempts |
| Row limits | All listing queries have MAX_ROWS cap enforced |
6. LLM Schema Grounding
Before any LLM call, the canonical JSON schema bundle is loaded as context:
foundations/schema/canonical_bundle.json
This bundle contains all canonical model schemas in LLM-optimized format. Grounding prevents the LLM from hallucinating column names or relationships.
Schema loading strategy:
1. Full bundle loaded on LLM service initialization
2. Per-question, a relevant subset is injected into the prompt (context window optimization)
3. Bundle version hash verified in CI to prevent stale schema
7. LLM Configuration (Platform Level)
Platform administrators configure LLM providers via PLATFORM_LLM_CONFIGURATION:
| Setting | Description |
|---|---|
llm_provider |
openai, anthropic, azure_openai, bedrock |
model_name |
Provider-specific model identifier |
api_key_reference |
Encrypted credentials reference — never plaintext |
max_tokens |
Per-query token budget |
temperature |
0.0 for deterministic SQL generation |
timeout_seconds |
Query timeout |
Tenant operators may select from platform-approved LLM profiles. No tenant configures their own API key.
8. Risk Snapshot Listing Constraints
Listing risk snapshots and risk snapshot items requires specific contract constraints:
RiskSnapshotqueries scope totenant_idautomatically.RiskSnapshotItemqueries must always filter by an explicitsnapshot_id.- GROUP BY on snapshot items is always secondary to tenant + snapshot scope.
- Top-N item queries use
ORDER BY residual_risk_score DESC LIMIT N. - The scalar compiler handles
count(snapshots)andlatest snapshotdeterministically.
9. LLM Security and Privacy Controls
| Control | Implementation |
|---|---|
| Prompt injection protection | Input sanitization + injection pattern detection |
| Data masking | CONFIDENTIAL fields masked; RESTRICTED fields never sent to LLM |
| LLM training isolation | Queries never used to train models (opt-out enforced in provider config) |
| Query logging | Stored without raw PII; query intent and question class stored only |
| Tenant isolation | LLM queries always execute within tenant_context(tenant) |
| No cross-tenant data | LLM never receives data from another tenant |
10. Suggest Fix vs Automated Query
Design rationale: "Suggest Fix" mode (LLM provides remediation recommendation as text) is safer and more reliable than "Automated Query" (LLM generates and executes arbitrary SQL) because:
- Remediation recommendations require understanding context, not schema traversal.
- SQL generation errors in a fix context produce wrong results silently; in a query context they produce wrong data.
- The LLM's strength is language reasoning, not schema navigation.
- Suggest Fix can be validated by the operator before action; automated query executes immediately.
For investigation queries (IOC correlation, attack path), the investigation deterministic compiler is preferred over free-form LLM SQL.
11. Feedback and Quality System
| Component | Purpose |
|---|---|
LLMQueryLog |
Every query logged: question, intent, SQL, execution time, row count |
LLMQueryFeedback |
Operator feedback: thumbs up/down + optional comment |
LLMQueryBenchmark |
Regression test suite: reference questions + expected SQL patterns |
| QA scoring | Benchmark run scores: intent accuracy, SQL validity, evidence correctness |
Feedback feeds continuous improvement of contracts and intent classification.
12. Code Locations
| Module | Purpose |
|---|---|
apps/llm/intent_router.py |
classify_intent(), classify_question() |
apps/llm/query_modes.py |
Mode constants, IntentResult dataclass |
apps/llm/registry/contracts.py |
Semantic registry, contract definitions |
apps/llm/scalar_compiler.py |
Deterministic SQL for scalar aggregates |
apps/llm/contract_validator.py |
Query plan validation |
apps/llm/planner_validator.py |
Planner output validation |
apps/llm/masking.py |
Column-level data masking |
apps/llm/services/chat_service.py |
End-to-end pipeline orchestration |
13. Attack Path Intelligence Integration (planned)
Attack path questions (e.g. “How could this identity reach SAP?”) must not use free-form LLM SQL or graph traversal.
Planned integration:
- Intent router classifies as
investigation+ sub-intentattack_path. - LLM tool layer calls deterministic Attack Path Intelligence APIs (
API_ATTACK_PATH_INTELLIGENCE.md). - Response narrative is bound to
AttackPathIntelligenceDTOevidence — no invented hops.
See ARCHITECTURE_ATTACK_PATH_INTELLIGENCE.md §11 for tool mapping.
Rule: Graph BFS remains authoritative in apps/attack_graph; LLM is narrative-only for path questions.
| apps/llm/models.py | LLMQueryLog, LLMQueryFeedback, LLMQueryBenchmark |