Guides

Guardrails

Enforcing security policies, input sanitization, and tool call approval gates.

While type and schema validations check for correctness (e.g. "is this argument a number?"), guardrails enforce policy (e.g. "should the model be allowed to view this specific user's folder?" or "require human approval before modifying an invoice").

Chai AI SDK supports three categories of guardrails with distinct behavior suited for different stages of the execution loop.


The Three Guardrail Types

All guardrails check actions using GuardrailAction, which returns one of three actions: allow, block (with a reason), or require-approval (with a reason, valid only for tools).

Guardrail TypeStageFailure Behavior
InputGuardrailBefore the first model callFails Fast: Throws an AgentError immediately. No model call occurs.
ToolGuardrailRight before a tool executesFails Gracefully: Returns an error string to the model, giving it a chance to apologize or recover.
OutputGuardrailBefore final text is returnedAuto-Repair: Prompts the model with the rejection reason to rewrite the response. Can also mechanically transform output.

Defining Guardrails

1. Input Guardrails

Input guardrails analyze the raw prompt. If the prompt contains forbidden terms (like secrets), the execution is aborted immediately:

import { type InputGuardrail } from "chai-ai";

const noSecretsGuardrail: InputGuardrail = {
  name: "no-secrets",
  check: ({ prompt }) =>
    /api[_-]?key|password/i.test(prompt)
      ? { action: "block", reason: "Prompt contains sensitive search terms." }
      : { action: "allow" },
};

2. Tool Guardrails & Human-in-the-loop Approval

Tool guardrails execute right before a tool runs. They can request human approval via the onApprovalRequired handler:

import { type ToolGuardrail } from "chai-ai";

const sensitiveActionGuardrail: ToolGuardrail = {
  name: "sensitive-action",
  check: ({ toolName }) =>
    toolName === "deleteDatabase"
      ? { action: "require-approval", reason: "Database deletion is irreversible." }
      : { action: "allow" },
};

To handle approval requests, configure onApprovalRequired on the Agent constructor. If omitted, or if it returns false/throws, the request fails closed and the tool call is blocked:

const agent = new Agent({
  name: "admin-agent",
  model,
  tools: { deleteDatabase },
  toolGuardrails: [sensitiveActionGuardrail],
  onApprovalRequired: async ({ toolName, reason }) => {
    // Show UI prompt or ask in terminal
    return await askUserForPermission(reason); 
  },
});

3. Output Guardrails

Output guardrails check the final generated response. They support an optional transform function (e.g. regex email redaction) that runs before verification:

import { type OutputGuardrail } from "chai-ai";

const redactEmailGuardrail: OutputGuardrail = {
  name: "redact-emails",
  transform: ({ text, output }) => ({
    text: text.replace(/[\w.+-]+@[\w-]+\.[\w.-]+/g, "[redacted]"),
    output,
  }),
  check: ({ text }) => 
    text.includes("[redacted]")
      ? { action: "block", reason: "Emails were detected and redacted." }
      : { action: "allow" }
};

Execution Flow Traces

Tool Guardrail: Approved vs. Denied

// If approved
Model Call → Tool Request: deleteDatabase()
  → sensitive-action checks deleteDatabase() → "require-approval"
  → onApprovalRequired() executes → returns true (approved)
  → runTool(deleteDatabase) executes
  ← agent returns success response

// If denied (fails closed)
Model Call → Tool Request: deleteDatabase()
  → sensitive-action checks deleteDatabase() → "require-approval"
  → onApprovalRequired() is undefined → approved = false
  → Intercept tool: runTool() is skipped
  ← Send tool result message: { error: "Database deletion is irreversible." }
  → Model sees error, apologizes: "I cannot delete the database without approval."

Output Guardrail: Redaction & Repair

Model Call → returns "Contact me at bob@example.com"
  → redact-emails.transform() → "Contact me at [redacted]"
  → redact-emails.check() → block ("Emails detected")
  → Yield guardrail_triggered event
  → Push corrective prompt: "Your response was rejected: Emails detected. Respond again."
  → Model Call (Iteration 2) → returns "Please contact me through the portal instead."
  → redact-emails.check() → allow
  ← Final returned text: "Please contact me through the portal instead."

On this page