ML Behavioral Baselining
Pavri Phase 2 introduces per-agent behavioral baseline models that reduce false-positive detections by at least 30% compared to rule-only detection, without regressing critical recall.
How It Works
Pavri ML baselining is intentionally scoped as anomaly scoring only. The ML layer:
- Extracts feature vectors from agent telemetry (tool call rates, session duration, model invocation frequency, token usage, policy eval counts)
- Trains a per-agent statistical profile (mean and standard deviation per feature)
- Scores incoming events using z-score based anomaly detection
- Feeds scores into the existing rule/threshold engine — it does NOT make autonomous enforcement decisions
The ML layer never blocks execution, overrides policy, or auto-resolves correlation. Scores are advisory inputs to the threat detection pipeline.
Feature Vectors
Each agent's behavioral profile is built from these features:
| Feature | Description |
|---|---|
tool_call_rate | Tool calls per unit time in a session |
avg_session_duration_s | Average session duration in seconds |
model_invoke_frequency | Model invocations per unit time |
token_usage_ratio | Tokens used as a fraction of context window |
policy_eval_count | Number of policy evaluations per session |
Training a Baseline
Baselines are trained from historical telemetry. A minimum of 3 samples is required for a meaningful profile. Below 3 samples, the detector falls back to rule-only detection.
from pavri.core.behavioral_baseline import train_baseline, FeatureVector
samples = [
FeatureVector(tool_call_rate=2.0, avg_session_duration_s=45.0,
model_invoke_frequency=1.8, token_usage_ratio=0.4, policy_eval_count=3.0),
# ... more samples from production telemetry ...
]
profile = train_baseline(
agent_id="my-agent-prod",
samples=samples,
trained_at="2026-03-25T00:00:00Z",
)
Anomaly Scoring
from pavri.core.behavioral_baseline import BaselineScorer, extract_features
scorer = BaselineScorer(threshold=0.65)
features = extract_features(event_payload)
score = scorer.score(features, profile)
print(f"Score: {score.score:.4f}")
print(f"Explanation: {score.explanation_stub}")
print(f"Model version: {score.model_version}")
The score output always includes:
AnomalyScore(
score=0.82, # 0.0 (normal) to 1.0 (highly anomalous)
features_used=[...], # feature names that contributed
explanation_stub="top anomalous features: tool_call_rate(z=4.20)",
model_version="v1.0.0",
agent_id="my-agent-prod",
baseline_available=True,
)
AnomalyDetector Integration
The AnomalyDetector class integrates the ML scorer with the rule-only fallback path:
from pavri.detectors.anomaly import AnomalyDetector
from pavri.core.behavioral_baseline import BaselineScorer
detector = AnomalyDetector(scorer=BaselineScorer(threshold=0.65))
detector.register_profile(profile)
finding = detector.detect(event_payload, agent_id="my-agent-prod")
if finding:
print(f"Anomaly detected: {finding.message}")
print(f"ML score: {finding.extensions['ml_score']}")
Fallback Behavior
When no trained baseline is available for an agent, the detector falls back to rule-only detection using pattern markers. This ensures no gaps in coverage during the bootstrapping period.
event received
├── profile exists and is_trained() → ML baseline scoring
│ ├── score >= threshold → ThreatFinding (behavioral_anomaly)
│ └── score < threshold → None (no finding)
└── no profile or insufficient samples → rule-only fallback
├── rule marker matched → ThreatFinding (baseline_available=False)
└── no marker → None
False-Positive Reduction
The P2-S6 exit criterion requires the ML baseline to achieve ≥30% false-positive reduction versus rule-only detection on the frozen evaluation set.
The evaluation set is at tests/fixtures/ml/evaluation_set.json and contains:
- 60 labeled events across 3 reference agents
- 10 genuine anomalies (true positives)
- 50 normal events, of which ~60% are over-flagged by rule-only detection
To run the evaluation:
cd sdk/python
python -m pytest tests/unit/test_behavioral_baseline.py::BaselineEvaluatorTests::test_fp_reduction_meets_30_percent_target -v
Model Versioning
Every AnomalyScore and ThreatFinding from the ML path includes model_version. The current model version is v1.0.0. When the feature extraction logic or scoring algorithm changes, the version must be bumped in BASELINE_MODEL_VERSION in core/behavioral_baseline.py.
Scope Constraints
The Phase 2 ML baseline is intentionally limited:
- No external ML libraries — pure Python statistics (mean, stddev, z-score)
- No autonomous decisions — scores feed the rule engine, not enforcement
- No ML for correlation — correlation uses confidence scores, not ML
- No model training server — training runs locally from telemetry fixtures or batched telemetry exports
Advanced ML capabilities (online learning, model serving, GPU inference) are Phase 3 scope.