Guardrails System
When to Use
Use guardrails when you need to intercept AI requests before they reach the provider (pre-processing) or after receiving a response (post-processing). Required for user-facing AI features.
Decision
| Situation |
Choose |
Why |
| Content moderation |
Pre + post guardrail |
Block unsafe input and output |
| PII filtering |
Pre guardrail |
Scrub before sending to provider |
| Prompt injection detection |
Pre guardrail |
Catch injection attempts before processing |
| AI-based moderation |
NonDeterministicGuardrailInterface |
Guardrail itself uses AI; receives AiProviderPluginManager |
| Streaming response |
Avoid NonStreamableGuardrailInterface |
Skipped for streaming calls automatically |
Pattern
use Drupal\ai\Attribute\AiGuardrail;
#[AiGuardrail(
id: 'safety:pii_filter', // ID must match or be prefixed by group ("safety")
label: new TranslatableMarkup('PII Filter'),
description: new TranslatableMarkup('Removes PII before sending to AI'),
)]
class PiiFilter extends AiGuardrailPluginBase {
public function processInput(InputInterface $input): GuardrailResultInterface {
// Return PassResult, StopResult, or RewriteInputResult.
return new RewriteInputResult('Input scrubbed', $this, []);
}
public function processOutput(OutputInterface $output): GuardrailResultInterface {
return new PassResult('Output passed', $this, []);
}
}
Guardrail Result Types
| Result Class |
stop() |
Purpose |
PassResult |
false |
Input/output passes without changes |
StopResult |
true |
Block the request; includes a $score |
RewriteInputResult |
false |
Rewrite the input before sending |
RewriteOutputResult |
false |
Rewrite the output before returning |
Request Lifecycle
PreGenerateResponseEvent fires
GuardrailsEventSubscriber loads matching guardrails
- Each guardrail's
processInput() runs
- Provider processes the request
PostGenerateResponseEvent fires
- Each guardrail's
processOutput() runs
Config Entities
| Entity Type |
Purpose |
ai_guardrail |
Individual guardrail config entity |
ai_guardrail_set |
Groups guardrails with pre/post lists and a stop threshold |
AiGuardrailRepository
$repo = \Drupal::service('Drupal\ai\Guardrail\AiGuardrailRepository');
$guardrail = $repo->getGuardrailById('safety:pii_filter');
$set = $repo->getGuardrailSetById('my_guardrail_set');
Common Mistakes
- Wrong: ID doesn't match its group prefix → Right: ID must match or be prefixed by group (e.g., group "safety" → ID
safety or safety:pii_filter)
- Wrong: Applying streaming-incompatible guardrails without
NonStreamableGuardrailInterface → Right: Mark as NonStreamableGuardrailInterface and it will be skipped for streaming calls
- Wrong: Not enabling guardrails on user-facing features → Right: Prompt injection can bypass agent instructions without guardrails
See Also