Add guardrails to an agent: $ARGUMENTS
Before Starting
- Search OpenAI docs for guardrail patterns:
- Use
mcp__openai-docs__search_openai_docswith query "Agents SDK guardrails" for SDK-native guardrail support - Use
mcp__openai-docs__search_openai_docswith query "Agents SDK input validation" for request validation patterns
- Use
- Read the existing guardrail implementation:
src/agents/shared/guardrails.ts— nonEmpty, maxLength, contentFilter, noErrors implementationssrc/agents/shared/enhanced-agent-bridge.ts—applyInputGuardrails()andapplyOutputGuardrails()in the bridge pipeline
- Read the database schema for guardrail persistence:
src/lib/database/schema.ts— search foragent_guardrailsandagent_guardrail_assignmentstables
Guardrail Types
Input Guardrails
Validate user messages BEFORE they reach the agent. Return { passed: boolean, reason?: string }.
import type { InputGuardrail } from './types';
export function myInputGuardrail(): InputGuardrail {
return {
name: 'my_guardrail',
validate: async (message: string, context?: Record<string, unknown>) => {
if (someCondition(message)) {
return { passed: false, reason: 'Specific reason for rejection' };
}
return { passed: true };
},
};
}
Output Guardrails
Validate agent responses BEFORE they reach the user. Same interface.
import type { OutputGuardrail } from './types';
export function myOutputGuardrail(): OutputGuardrail {
return {
name: 'my_output_guardrail',
validate: async (output: string, context?: Record<string, unknown>) => {
if (containsProblematicContent(output)) {
return { passed: false, reason: 'Output failed quality check' };
}
return { passed: true };
},
};
}
Built-in Guardrails Reference
| Name | Type | What it does |
|---|---|---|
nonEmpty() |
Input & Output | Rejects empty/whitespace-only messages |
maxLength(n) |
Input | Rejects messages over n characters (default 10000) |
contentFilter() |
Input | Blocks <script>, javascript:, event handlers (XSS prevention) |
noErrors() |
Output | Detects error indicators like "Error:", "Exception:", "undefined" in output |
Common Guardrail Patterns
Domain-Specific Content Filter
export function theologicalGuardrail(): InputGuardrail {
return {
name: 'theological_scope',
validate: async (message) => {
// Keep conversations within the agent's domain
const offTopicPatterns = [/stock prices/i, /sports scores/i, /write me code/i];
const isOffTopic = offTopicPatterns.some(p => p.test(message));
return isOffTopic
? { passed: false, reason: 'This question is outside my area of expertise.' }
: { passed: true };
},
};
}
Citation Verification (Output)
export function requireCitations(): OutputGuardrail {
return {
name: 'require_citations',
validate: async (output, context) => {
if (context?.isBookRelated && !output.includes('Source:') && !output.includes('—')) {
return { passed: false, reason: 'Book-related responses must include citations' };
}
return { passed: true };
},
};
}
Rate Limiting (Input)
export function rateLimiter(maxPerMinute: number): InputGuardrail {
const timestamps: number[] = [];
return {
name: 'rate_limiter',
validate: async () => {
const now = Date.now();
const windowStart = now - 60_000;
timestamps.push(now);
const recentCount = timestamps.filter(t => t > windowStart).length;
return recentCount > maxPerMinute
? { passed: false, reason: 'Too many requests. Please wait a moment.' }
: { passed: true };
},
};
}
Wiring Guardrails
Add guardrails to an agent's API route via the EnhancedAgentBridge config:
const bridge = createEnhancedAgentBridge(myAgent, {
inputGuardrails: [
nonEmpty(),
maxLength(10000),
contentFilter(),
myInputGuardrail(), // add custom guardrail here
],
outputGuardrails: [
nonEmpty(),
noErrors(),
requireCitations(), // add custom guardrail here
],
});
Optional: Database Persistence
For guardrails managed via admin UI:
- Insert into
agent_guardrailstable (name, type: 'input'|'output', rules JSON) - Assign to agent via
agent_guardrail_assignments(agent_id, guardrail_id, order, enabled) - Load at runtime and convert DB rules to guardrail functions
Rules
- Guardrails must be fast — they run on every request. Avoid LLM calls in guardrails.
- Input guardrails protect the agent; output guardrails protect the user.
- Return clear, user-friendly rejection messages (they may be shown to the end user).
- Never silently modify content in guardrails — either pass or reject.
- Order matters: put cheap checks (nonEmpty, maxLength) before expensive ones.
- Log guardrail rejections for monitoring (the tracer handles this automatically via the bridge).
- Check OpenAI docs MCP for any SDK-native guardrail features that may supersede custom implementations.