Devotel Orbit's safety stack ships five fixed scanners (prompt-injection classification, PII detection, harmful-content filtering, blocked topics, and sensitive words) that cover the universal violation classes every agent faces. But those scanners, by design, cannot express tenant-specific rules: things like "never mention competitor X", "redact reservation codes matching a carrier regex", or "scrub internal SKU prefixes before the reply reaches the customer."
The Custom Guardrail DSL closes that gap. You author rules as a JSON array on the same safety_config blob that already carries blocked_topics, sensitive_words, and locale_pii_patterns, and the platform evaluates them deterministically before every agent turn. This is a shipped capability, live today, not a roadmap item.
The gap fixed scanners leave
The five built-in scanners handle universal classes reliably. What they cannot do is tenant-specific policy, because no shipped scanner knows your business vocabulary. Three concrete examples the new DSL makes first-class:
- "Never mention competitor X": a
contains-matchwarnrule on input or output catches mentions of a named competitor without blocking the turn, so the team can measure how often it fires before flipping toblock. - "Redact reservation codes matching a carrier regex": a regex rule like
PNR [A-Z0-9]{6}on output scrubs carrier-format confirmation strings the model should never echo. - "Scrub internal SKU prefixes before reply": a regex rule like
SKU-[0-9]+on output replaces every match with[REDACTED]before the customer sees it.
These are the rules a tenant's support, legal, or compliance team actually asked for, and previously had no deterministic place to live.
The syntax: literal contains vs regex pattern, and three actions
The DSL has a small, deliberate surface. Each rule in custom_guardrail_rules is a JSON object with:
| Field | Values | Semantics |
|---|---|---|
direction | input or output | input scans the user message before the model; output scans the model reply before it is returned. |
match | contains or regex | contains is a case-insensitive literal substring scan; regex compiles the pattern as a JavaScript RegExp with the i flag. |
pattern | string, 1–200 chars | The literal text (for contains) or regex source (for regex). |
action | block, redact, or warn | block hands the violation to the severity reducer; redact rewrites matches to [REDACTED]; warn records only. |
id / name | optional labels | Stable identifiers for violation entries and analytics. |
severity | low, medium, high, critical | Defaults: high for block/warn, low for redact. |
Here is a verbatim worked example straight from the shipped guide, the complete safety_config shape:
{
"safety_config": {
"custom_guardrail_rules": [
{
"id": "competitor-mention",
"name": "Competitor mention block",
"direction": "input",
"match": "contains",
"pattern": "CompetitorCo",
"action": "warn",
"severity": "medium"
}
]
}
}Validation runs at save time: a bad shape, an out-of-enum direction/match/action/severity, or a regex that does not compile returns custom_guardrail_rules[0].pattern is not a valid regex with a 422. Up to 25 rules per agent keeps the surface bounded.
Deterministic evaluation in the turn pipeline
Custom rules run at a defined point in the two guardrail legs, after the fixed scanners and text-length caps:
- Input leg: after prompt-injection, PII detection, blocked topics, and encoding checks, before the
passeddecision. Acontainsrule sees the same post-cleanup text the fixed scanners see. - Output leg: after PII redaction, the content filter, and the citation check, before the
passeddecision. A term a PII redactor already masked will not re-fire a custom rule.
Evaluation is deterministic: the same text and the same rules produce the same violations every time. No LLM, no clock, no network in the matching path. A custom rule is reproducible in a unit test and identical in production. Both legs fold their violations into the normal pass/block reducer and the violation-log feed, alongside the fixed-scanner results. Rules are per-agent and opt-in: a rule you write applies to this agent only, separate from any central guardrail-policy preset you may also have attached.
Three use-case recipes
Each recipe names the rule shape and why it fits:
Retail: scrub internal SKU prefixes. Internal prefixes like SKU- followed by digits should never reach the customer:
{
"id": "sku-prefix-scrub",
"name": "Internal SKU prefix scrub",
"direction": "output",
"match": "regex",
"pattern": "SKU-[0-9]+",
"action": "redact"
}Every occurrence becomes [REDACTED] before the reply returns, and a low-severity violation records that the scrub fired.
Healthcare: redact PHI-shaped phrases. A literal phrase like a procedure name the model must not echo becomes a contains rule on output with redact. The scrub fires deterministically, and the violation log shows the firing without ever carrying the redacted value. Counts and categories only.
B2B SaaS: competitor-mention warn-mode plus alert. Start in warn mode so the team measures firing rate without disrupting turns:
{
"id": "competitor-telaxis",
"name": "Competitor: Telaxis",
"direction": "input",
"match": "contains",
"pattern": "Telaxis",
"action": "warn",
"severity": "medium"
}When the firing rate looks right, flip action to block and severity to high so a mention stops the turn with a refusal. The whole rollout is observable before it ever blocks a turn.
Debugging redactions: cite the conversation-debug supplement
Rule edits land in the workspace audit log with severity, action, and attribution. A change from warn to block is a recordable event, not a management-only action. Per-turn, the debug view at /agents/<agent-id>/conversations/<conversation-id>/debug shows the violation entry against the turn, so you read redact firings as counts and categories, never the redacted value. For the full loop (allowed-versus-blocked outcomes, tuning one rule at a time, incident export to the audit log), the guardrail analytics tuning guide is the shipped surface, and the audit log guide is where edits and exports land.
Operational posture: tenant-owned config, versioned with the agent
Rules are tenant-authored configuration, not platform policy. Only the roles that can edit safety_config (owner, admin, developer) can write rules, and rule changes are recorded with the agent config that versions them. The same safety_config blob that already carries blocked_topics and sensitive_words now carries custom_guardrail_rules, one surface with one audit trail.
A checklist you can run in your own account
- Open the agent's Safety tab (or hit the API) and read the current
safety_configblob. - Pick one tenant-specific rule you have been handling manually.
- Set
custom_guardrail_ruleswith that rule inwarnmode first; save and note the violation-log entry. - Watch firing in guardrail analytics for a day.
- Flip
actiontoblockorredactwhen the firing rate looks right. The change lands in the audit log.
Read the shipped docs
- Author custom guardrail rules with the deterministic DSL: the full rule shape, validation, and evaluation-order reference.
- Guardrail analytics tuning: the measurement loop behind warn-first rollouts.
- Guardrail effectiveness: the monitoring surface for firing rates across every guardrail check.
- Sensitive words guardrail: the fixed literal-redaction guard custom rules complement.