Guardian is Regent’s ML layer. It watches every agent’s audit stream in real time and produces a risk score between 0 (normal) and 1 (highly anomalous). When the score crosses a threshold, an alert is created.
Why ML, not rules
Mandate enforcement (per-tx limits, daily caps) is rule-based and deterministic — it catches known violations. Guardian catches the unknown ones: agents whose individual actions are all within limits but whose pattern is suspicious.
Examples Guardian catches that mandates miss:
- An agent suddenly making many tiny trades after weeks of normal pacing
- An agent triggering many rejections in a short window (probing limits)
- An agent active at an unusual time of day for its profile
- An agent whose action types shift dramatically (e.g., started buying, now only selling)
How scoring works
Every audit event triggers a fresh score for that agent:
The model is an Isolation Forest — an unsupervised algorithm that scores how easily a point can be isolated from the rest of a learned distribution. Easily-isolated → anomalous.
The six features
| Feature | What it measures | What an anomaly looks like |
|---|---|---|
amount_zscore | How far this event’s amount is from the agent’s historical mean | A $1,000 trade for an agent that usually does $50 |
error_rate_1h | Fraction of recent events that were rejections | Many rejections in a short window = probing or runaway |
event_rate_1h | Number of events per second over the last hour | Burst activity vs steady cadence |
event_rate_24h | Same, scaled to 24 hours | Detects level shifts |
hour_of_day_sin | Time of day (cyclical encoding) | Activity at unusual hours for this agent |
unique_event_types | Diversity of action types | Agent suddenly emitting types it never has before |
These are computed on Redis rolling windows so scoring stays sub-millisecond.
SHAP explanations
For every score, Guardian computes SHAP factors — per-feature contributions to the risk score. This makes the model auditable and regulator-friendly:
{
"agent_id": "agent_b1c59d23...",
"score": 0.587,
"features": {
"amount_zscore": 0.0,
"error_rate_1h": 0.45,
"event_rate_1h": 0.008,
"event_rate_24h": 0.001,
"hour_of_day_sin": 0.71,
"unique_event_types": 0.18
},
"shap_factors": [
{"feature": "error_rate_1h", "value": -3.10},
{"feature": "event_rate_1h", "value": -0.65},
{"feature": "event_rate_24h", "value": -0.44},
{"feature": "unique_event_types","value": 0.28},
{"feature": "amount_zscore", "value": -0.18},
{"feature": "hour_of_day_sin", "value": -0.07}
]
}Negative SHAP values indicate features that pushed the score toward anomalous. In the example above, error_rate_1h is the dominant signal — the agent has been rejected unusually often.
This satisfies the EU AI Act’s explainability requirements: every alert can be defended with a per-feature attribution, not “the model said so.”
Alert types
| Type | Triggered when | Default threshold |
|---|---|---|
threshold | Score >= alert threshold | 0.5 (configurable per-environment) |
drift | Score delta from previous score >= drift threshold | 0.2 |
Alerts are stored with status open and shown on the dashboard. Operators acknowledge or resolve them via the API.
Soft dependency from authorize
When api-payment processes an /authorize call, it asks Guardian for the latest score as a soft dependency: if Guardian is unavailable, the authorize still proceeds. If Guardian returns a score above the hard reject threshold, the authorize is denied with RISK_SCORE_TOO_HIGH.
This means Guardian can act as a second layer of denial beyond mandate limits — agents whose ML risk has spiked are blocked even if their individual action is within the rulebook.