Decision Engine
The Decision Engine (IDecisionEngine) is the central orchestrator of Vectra's governance logic. It combines policy evaluation, risk scoring, and semantic analysis into a single, ordered pipeline that produces an Allow, Deny, or Hitl decision for every proxied request.
Evaluation Pipeline
RequestContext
│
▼
┌─────────────────────────────────┐
│ 1. Policy Evaluation │ ← Checks assigned policy rules
│ → Deny / Hitl = short-circuit│
└────────────────┬────────────────┘
│ (no match or Allow)
▼
┌─────────────────────────────────┐
│ 2. Risk Scoring │ ← Computes weighted risk score
│ → score > threshold = Hitl │
└────────────────┬────────────────┘
│ (score ≤ threshold)
▼
┌─────────────────────────────────┐
│ 3. Semantic Analysis │ ← LLM/ONNX intent classification
│ → negative verdict = Deny/Hitl│
└────────────────┬────────────────┘
│ (no concern)
▼
Allow (with risk score as trust)
Each stage short-circuits — if a stage produces a Deny or Hitl, subsequent stages are skipped.
Stage 1: Policy Evaluation
Runs only if Policy.Enabled = true.
- Builds a
Dictionary<string, object>input from theRequestContext. - Calls
IPolicyProvider.EvaluateAsync(policyName, input). - If the provider returns
Deny→ engine returnsDecisionResult.Deny(reason). - If the provider returns
Hitl→ engine returnsDecisionResult.Hitl(reason). - Otherwise, evaluation continues.
If no policy is assigned to the agent (PolicyName == null), this stage is effectively a no-op and returns null (continue).
Stage 2: Risk Scoring
- Calls
IRiskScoringService.ComputeRiskScoreAsync(context). - Compares score against
HumanInTheLoop.Threshold(default:0.8). - If score exceeds threshold →
DecisionResult.Hitl($"High risk score: {score:F2}", score). - Otherwise, the score is passed to the final Allow result.
Stage 3: Semantic Analysis
Runs only if Semantic.Enabled = true.
- The
ISemanticProviderconverts the request to intent text (viaJsonToIntentText) and classifies it. - If the classification returns a negative / malicious verdict with confidence ≥
ConfidenceThreshold→ Deny or Hitl. - If confidence is below threshold and
AllowLowConfidence = false→ Deny.
Decision Finalisation
After every decision (including Allow), the engine calls the private FinalizeAsync method which:
- Updates the agent's
TrustScorein the repository based on the decision outcome. - Writes an
AuditTrailrecord to the audit repository. - Returns the
DecisionResult.
DecisionResult
public record DecisionResult
{
public DecisionType Type { get; init; } // Allow | Deny | Hitl
public string? Reason { get; init; }
public double TrustScore { get; set; }
public bool IsAllowed => Type == DecisionType.Allow;
public bool IsHitl => Type == DecisionType.Hitl;
public bool IsDenied => Type == DecisionType.Deny;
// Factory methods
public static DecisionResult Allow(double trustScore = 1.0);
public static DecisionResult Deny(string reason, double trustScore = 0.0);
public static DecisionResult Hitl(string reason, double trustScore = 0.5);
}
Audit Trail
Every decision (Allow, Deny, Hitl) is persisted as an AuditTrail record containing:
- Agent ID
- Decision type and reason
- Target URL, method, path
- Risk score
- Timestamp