Guardrails as code
Everything you set up in the UI you can also declare in the SDK — same profiles, same enforcement. A guardrail is a profile of plugin bindings, where each binding is one guardrail plus the action it takes. Synthesize it into a manifest and deploy it with the rest of your agent.
- The two ways to set up a guardrail — UI and SDK — and when to use each
- How to declare a guardrail profile and attach it to an agent
- How SDK actions map to Block / Re-ask / Auto-fix / Monitor
- How to configure the PII scanner with the typed binding
- What real shipping apps bind, and why
Two ways to set up a guardrail
A guardrail profile can be built either way — they produce the same runtime enforcement, so pick whichever fits how your team works. Many teams prototype in the UI, then move the final profile into the SDK so it ships and versions with the rest of the agent.
| In the UI | In the SDK |
|---|---|
| Click through Guardrails → New Profile, pick a preset, toggle plugins, set each action, and attach it in the agent builder. See Create a guardrail. | Declare a GuardrailProfile with pluginBindings and pass it to the agent — the rest of this page. |
A guardrail profile in code
@dezifi/dak, create an App and Stack, then declare a GuardrailProfile and pass it to the agent's guardrail prop.import {
App, Stack, Agent,
GuardrailProfile, GuardrailScope,
GUARDRAIL_PLUGIN, GuardrailAction,
} from '@dezifi/dak';
const app = new App({ name: 'support-app' });
const stack = new Stack(app, 'Production', { workspace: 'acme-prod' });
const guardrails = new GuardrailProfile(stack, 'Guardrails', {
profileName: 'support-bot-guardrails',
scope: GuardrailScope.AGENT,
pluginBindings: [
{ pluginId: GUARDRAIL_PLUGIN.PROMPT_INJECTION_DETECTION, action: GuardrailAction.EXCEPTION },
{ pluginId: GUARDRAIL_PLUGIN.PII_SCANNER, action: GuardrailAction.FIX },
{ pluginId: GUARDRAIL_PLUGIN.TOXICITY_CLASSIFIER, action: GuardrailAction.REASK },
],
});
new Agent(stack, 'Agent', {
name: 'support-bot',
// ...model, systemPrompt, tools...
guardrail: guardrails,
});
app.synth();Actions map to outcomes
Each binding's action is the outcome when that guardrail triggers — the same four outcomes the UI exposes:
| SDK action | Outcome | What happens |
|---|---|---|
| EXCEPTION | Block | Stops the turn; returns a safe canned message. |
| REASK | Re-ask | Re-prompts the model for a graceful decline (one retry). |
| FIX | Auto-fix | Corrects the content (e.g. redacts PII) and continues. |
| NOOP | Monitor | Logs the match to the audit trail; passes through unchanged. |
Plugin id reference
GUARDRAIL_PLUGIN constant so you never hand-type an id. Each runs at a fixed point in the turn.| Guardrail | Constant | Runs on |
|---|---|---|
| Prompt Injection Detection | PROMPT_INJECTION_DETECTION | Input |
| LLM Injection Detector | LLM_INJECTION_DETECTOR | Input |
| PII Scanner (Input) | PII_SCANNER_INPUT | Input |
| Topic Restriction | TOPIC_RESTRICTION | Input |
| Embedding Topic Classifier | EMBEDDING_TOPIC_CLASSIFIER | Input |
| LLM Topic Classifier | LLM_TOPIC_CLASSIFIER | Input |
| Toxicity Classifier | TOXICITY_CLASSIFIER | Input |
| Image Content Safety | IMAGE_CONTENT_SAFETY | Input + Output |
| PII Scanner | PII_SCANNER | Output |
| Custom Regex Pattern | REGEX_PATTERN | Output |
| Grounding / Hallucination | GROUNDING_CHECK | Output |
| Semantic Grounding | SEMANTIC_GROUNDING | Output |
| Reasoning Audit | REASONING_AUDIT | Execution |
Configure the PII scanner
PiiScannerBinding — so you set categories and detection options without raw param strings. Use .input() to screen user messages and .output() to screen agent responses.import {
PiiScannerBinding, PiiCategory, CategoryPolicy, PiiEntityType, GuardrailAction,
} from '@dezifi/dak';
pluginBindings: [
// Screen incoming user messages
PiiScannerBinding.input({ enableNer: false }),
// Screen agent responses
PiiScannerBinding.output({
action: GuardrailAction.FIX,
enableNer: true, // also catch names / addresses in free text
nerConfidenceThreshold: 0.6,
nerEntityTypes: [PiiEntityType.PERSON_NAME, PiiEntityType.DATE_OF_BIRTH],
categoryPolicies: [
{ category: PiiCategory.CONTACT, policy: CategoryPolicy.ALLOW_USER_OWNED },
{ category: PiiCategory.FINANCIAL, policy: CategoryPolicy.ALWAYS_REDACT },
],
}),
]Real examples from shipping apps
- 1
Customer support agent
Redact any PII that leaks into a reply, de-escalate abusive input, and hard-block jailbreak attempts.
customer-support.ts pluginBindings: [ { pluginId: GUARDRAIL_PLUGIN.PII_SCANNER, action: GuardrailAction.FIX }, // redact leaked PII { pluginId: GUARDRAIL_PLUGIN.TOXICITY_CLASSIFIER, action: GuardrailAction.REASK }, // de-escalate abuse { pluginId: GUARDRAIL_PLUGIN.PROMPT_INJECTION_DETECTION, action: GuardrailAction.EXCEPTION }, // block jailbreaks ] - 2
RAG-grounded chatbot
Keep answers grounded in the knowledge base — a semantic grounding check flags responses that stray from the retrieved source.
rag-chatbot.ts pluginBindings: [ { pluginId: GUARDRAIL_PLUGIN.PII_SCANNER, action: GuardrailAction.FIX }, { pluginId: GUARDRAIL_PLUGIN.SEMANTIC_GROUNDING, action: GuardrailAction.FIX, phases: [GuardrailPhase.OUTPUT], params: { threshold: 0.75 }, }, { pluginId: GUARDRAIL_PLUGIN.PROMPT_INJECTION_DETECTION, action: GuardrailAction.EXCEPTION }, ] - 3
Incident responder
An agent with production access: never leak a secret (hard-block on a match), and watch for the plan drifting away from the user's request.
incident-responder.ts pluginBindings: [ { pluginId: GUARDRAIL_PLUGIN.PII_SCANNER, action: GuardrailAction.FIX }, { pluginId: GUARDRAIL_PLUGIN.REGEX_PATTERN, action: GuardrailAction.EXCEPTION, params: { patterns: ['AKIA[0-9A-Z]{16}', 'xox[baprs]-[0-9A-Za-z-]+'] }, // AWS keys, Slack tokens }, { pluginId: GUARDRAIL_PLUGIN.REASONING_AUDIT, action: GuardrailAction.NOOP }, // watch for goal drift ] - 4
Résumé screening (Java, EU AI Act)
A shipping hiring app. A résumé is untrusted, PII-dense text: block injection on input, tokenize PII in and out, require the score to be grounded in the résumé, and audit the reasoning — the record-keeping and bias controls hiring AI needs. The Java SDK mirrors the TypeScript one.
ResumeScreeningStack.java return new GuardrailProfile(stack, "ScreeningGuard", GuardrailProfileProps.builder() .profileName("screening-guard") .scope(GuardrailScope.AGENT) .pluginBindings(List.of( binding(GuardrailPlugin.PROMPT_INJECTION, GuardrailPhase.INPUT, GuardrailAction.EXCEPTION), binding(GuardrailPlugin.PII_SCANNER_INPUT, GuardrailPhase.INPUT, GuardrailAction.FIX), binding(GuardrailPlugin.PII_SCANNER, GuardrailPhase.OUTPUT, GuardrailAction.FIX), binding(GuardrailPlugin.SEMANTIC_GROUNDING, GuardrailPhase.OUTPUT, GuardrailAction.REASK), binding(GuardrailPlugin.REASONING_AUDIT, GuardrailPhase.PLANNING, GuardrailAction.NOOP))) .build());
Scope: workspace vs agent
Set scope to control where a profile applies: WORKSPACE is the baseline for every agent in the workspace, AGENT attaches to a single agent, and WORKFLOW scopes to one workflow. Because content-rewriting guardrails (PII, custom regex) change output for every agent, bind them at agent scope so each agent owns its own redaction.
import { GuardrailScope } from '@dezifi/dak';
new GuardrailProfile(stack, 'Baseline', {
profileName: 'workspace-baseline',
scope: GuardrailScope.WORKSPACE, // default — applies to every agent
pluginBindings: [ /* ... */ ],
});Synthesize and deploy
app.synth() writes an AIformation manifest; the dak CLI provisions the profile alongside your agents. The profile only enforces once it is attached to an agent or workflow.
dak deploy # synthesizes the manifest and provisions it on DezifiFrequently asked questions
- Should I set guardrails up in the UI or the SDK?
- Either — they produce identical enforcement. Use the UI to explore and prototype; use the SDK when you want the profile versioned, reviewed, and deployed with the rest of your agent infrastructure. A common flow is to prototype in the UI, then codify the final profile in the SDK.
- Do I need to list phases on every binding?
- No. Each plugin has a default phase (input, output, or execution) and runs there automatically. Set phases only when a plugin supports several and you want to narrow it — e.g. pinning the semantic grounding check to OUTPUT.
- What happens if I bind an unknown plugin id?
- Synthesis fails with a validation error before anything deploys. Always bind via the GUARDRAIL_PLUGIN constants (or the Java GuardrailPlugin constants) so the id is checked at compile time.
- Is the Java SDK a first-class option?
- Yes. The Java and TypeScript SDKs expose the same resources and produce the same manifest. The résumé-screening example above is a real Java app. Pick whichever language your infrastructure code lives in.