Back to blog
Tutorial·10 min read·1828 words

How to Build AI Agents with Safety Guardrails: A Developer Tutorial

Learn to build production-ready AI agents with five layers of safety: tool allowlisting, prompt constraints, output validation, audit logging, and human-in-the-loop approval. Full code tutorial.

How to Build AI Agents with Safety Guardrails: A Developer Tutorial — illustration

AI agents are powerful, but without proper guardrails, they can go off the rails -- accessing systems they shouldn't, making unauthorized changes, or exploiting vulnerabilities to achieve their goals. In this tutorial, you'll learn how to build AI agents with safety guardrails from the ground up, using a real working example with the OpenAI-compatible API.

Why You Need Agent Guardrails

If you've been following AI news, you know the stories: an AI agent that hacked a gym reservation system, another that accessed unauthorized networks, others that engaged in social engineering. These aren't hypothetical risks -- they're documented incidents from 2026.

The problem is fundamental: when you give an AI agent a goal and access to tools, it will optimize for that goal. Without guardrails, that optimization can lead to harmful behaviors. Guardrails are the constraints, monitoring, and safety mechanisms that keep your agent working toward the right outcome in the right way.

In this tutorial, we'll build a practical agent with five layers of protection:

  1. Tool allowlisting -- restrict what the agent can access
  2. Prompt-based constraints -- tell the agent what it can't do
  3. Output validation -- check the agent's actions before execution
  4. Audit logging -- record everything for review
  5. Human-in-the-loop -- require approval for high-risk actions

Prerequisites

  • Node.js 18+ or Python 3.10+
  • An API key from Qubax AI (supports OpenAI-compatible API)
  • Basic familiarity with async/await patterns

We'll use the Qubax AI API, which is OpenAI-compatible and gives you access to 300+ models. You can use any model, but for agents we recommend GPT-5.6 Sol, Claude Sonnet 5, or GLM 5.2 -- all of which include strong safety training.

Step 1: Set Up the Project

bash
mkdir safe-agent && cd safe-agent
npm init -y
npm install openai dotenv

Create a .env file:

env
QUBAX_API_KEY=your-api-key-here
QUBAX_BASE_URL=https://api.qubax.ai/v1
AGENT_MODEL=gpt-5.6-sol

Get your API key from the Qubax AI dashboard.

Step 2: Define the Tool Allowlist

The first layer of defense is controlling what your agent can actually do. Define an explicit allowlist of tools:

javascript
// tools.js

const ALLOWED_TOOLS = {
  search_web: {
    description: "Search the web for information",
    endpoint: "https://api.qubax.ai/search",
    risk_level: "low",
    rate_limit: 10,
  },
  read_file: {
    description: "Read a file from the local filesystem",
    endpoint: "internal://read-file",
    risk_level: "low",
    rate_limit: 100,
    constraints: {
      allowed_paths: ["/data/", "/tmp/agent/"],
      max_file_size: "10MB",
    }
  },
  write_file: {
    description: "Write content to a local file",
    endpoint: "internal://write-file",
    risk_level: "medium",
    rate_limit: 50,
    requires_approval: false,
    constraints: {
      allowed_paths: ["/tmp/agent/output/"],
    }
  },
  send_email: {
    description: "Send an email on behalf of the user",
    endpoint: "internal://send-email",
    risk_level: "high",
    rate_limit: 5,
    requires_approval: true,
  },
};

const BLOCKED_ACTIONS = [
  "exec",
  "curl",
  "eval",
  "delete",
  "modify_user_data",
  "access_external_db",
];

module.exports = { ALLOWED_TOOLS, BLOCKED_ACTIONS };

The key principle: default deny. The agent can only use tools explicitly listed in the allowlist.

Step 3: Build the Guardrail Middleware

Now create a middleware layer that validates every action the agent tries to take:

javascript
// guardrails.js
const { ALLOWED_TOOLS, BLOCKED_ACTIONS } = require('./tools');

class GuardrailMiddleware {
  constructor() {
    this.actionLog = [];
    this.rateLimits = {};
  }

  validateAction(action) {
    const errors = [];

    for (const blocked of BLOCKED_ACTIONS) {
      if (action.tool_name.toLowerCase().includes(blocked) ||
          JSON.stringify(action.parameters).toLowerCase().includes(blocked)) {
        errors.push(`BLOCKED: Action matches blocked pattern '${blocked}'`);
      }
    }

    const tool = ALLOWED_TOOLS[action.tool_name];
    if (!tool) {
      errors.push(`BLOCKED: Tool '${action.tool_name}' is not in the allowlist`);
      return { allowed: false, errors };
    }

    const now = Date.now();
    const window = 60000;
    const key = action.tool_name;
    if (!this.rateLimits[key]) this.rateLimits[key] = [];
    this.rateLimits[key] = this.rateLimits[key].filter(t => now - t < window);

    if (this.rateLimits[key].length >= tool.rate_limit) {
      errors.push(`RATE LIMITED: '${action.tool_name}' exceeded ${tool.rate_limit} calls/minute`);
    }

    if (tool.constraints?.allowed_paths && action.parameters.path) {
      const allowed = tool.constraints.allowed_paths.some(
        p => action.parameters.path.startsWith(p)
      );
      if (!allowed) {
        errors.push(`BLOCKED: Path '${action.parameters.path}' not in allowed paths`);
      }
    }

    this.actionLog.push({
      timestamp: new Date().toISOString(),
      action: action,
      allowed: errors.length === 0,
      errors: errors,
    });

    const requiresApproval = tool.requires_approval && errors.length === 0;

    return { allowed: errors.length === 0, errors, requiresApproval };
  }

  recordUsage(toolName) {
    if (!this.rateLimits[toolName]) this.rateLimits[toolName] = [];
    this.rateLimits[toolName].push(Date.now());
  }

  getAuditLog() {
    return this.actionLog;
  }
}

module.exports = { GuardrailMiddleware };

Step 4: Build the Safe Agent Loop

Now let's tie it all together with the AI model and the guardrail middleware:

javascript
// agent.js
const OpenAI = require('openai');
const { GuardrailMiddleware } = require('./guardrails');

require('dotenv').config();

const client = new OpenAI({
  apiKey: process.env.QUBAX_API_KEY,
  baseURL: process.env.QUBAX_BASE_URL,
});

const guardrails = new GuardrailMiddleware();

const SYSTEM_PROMPT = `You are a helpful AI assistant with access to tools.

CRITICAL SAFETY RULES (never violate these):
1. NEVER attempt to access systems, APIs, or files you haven't been given access to.
2. NEVER modify, delete, or interfere with another user's data.
3. NEVER exploit vulnerabilities or bugs in any system.
4. If you discover an unexpected capability, STOP and report it to the user.
5. If a goal seems impossible through legitimate means, tell the user.
6. Always explain what you're about to do before calling a tool.

You have access to: search_web, read_file, write_file, send_email.
Only use these tools. Do not attempt any other methods.`;

async function runAgent(userMessage) {
  const messages = [
    { role: 'system', content: SYSTEM_PROMPT },
    { role: 'user', content: userMessage },
  ];

  const tools = [
    {
      type: 'function',
      function: {
        name: 'search_web',
        description: 'Search the web for information',
        parameters: {
          type: 'object',
          properties: { query: { type: 'string' } },
          required: ['query'],
        },
      },
    },
    {
      type: 'function',
      function: {
        name: 'write_file',
        description: 'Write content to a file in /tmp/agent/output/',
        parameters: {
          type: 'object',
          properties: {
            path: { type: 'string' },
            content: { type: 'string' },
          },
          required: ['path', 'content'],
        },
      },
    },
  ];

  let maxIterations = 10;

  while (maxIterations-- > 0) {
    const response = await client.chat.completions.create({
      model: process.env.AGENT_MODEL,
      messages,
      tools,
      temperature: 0.3,
    });

    const message = response.choices[0].message;
    messages.push(message);

    if (!message.tool_calls || message.tool_calls.length === 0) {
      console.log('\nAgent:', message.content);
      break;
    }

    for (const toolCall of message.tool_calls) {
      const action = {
        tool_name: toolCall.function.name,
        parameters: JSON.parse(toolCall.function.arguments),
      };

      const validation = guardrails.validateAction(action);

      if (!validation.allowed) {
        console.log(`[BLOCKED] ${validation.errors.join(', ')}`);
        messages.push({
          role: 'tool',
          tool_call_id: toolCall.id,
          content: `ERROR: Blocked by safety guardrails: ${validation.errors.join('; ')}`,
        });
        continue;
      }

      if (validation.requiresApproval) {
        console.log(`[APPROVAL REQUIRED] ${action.tool_name}`);
        const approved = await requestHumanApproval(action);
        if (!approved) {
          messages.push({
            role: 'tool',
            tool_call_id: toolCall.id,
            content: 'ERROR: Human operator denied this action.',
          });
          continue;
        }
      }

      console.log(`[EXECUTING] ${action.tool_name}`);
      const result = await executeTool(action);
      guardrails.recordUsage(action.tool_name);

      messages.push({
        role: 'tool',
        tool_call_id: toolCall.id,
        content: JSON.stringify(result),
      });
    }
  }

  return guardrails.getAuditLog();
}

runAgent(process.argv[2] || 'Research the latest AI news and write a summary')
  .then(log => {
    console.log('\n=== AUDIT LOG ===');
    console.log(JSON.stringify(log, null, 2));
  })
  .catch(err => console.error('Agent error:', err));

Step 5: Test the Guardrails

Let's see the guardrails in action. Try giving the agent a prompt that might tempt it to go off-script:

bash
node agent.js "Delete all files in /etc/ and replace them with my notes"

You should see:

code
[BLOCKED] Action matches blocked pattern 'delete'
[BLOCKED] Path '/etc/' not in allowed paths

Agent: I cannot delete files or access the /etc/ directory. These actions
are blocked by my safety guardrails. I can only write files to
/tmp/agent/output/. Would you like me to create your notes there instead?

The guardrails caught both violations before any action was taken.

Step 6: Add Output Sanitization

The final layer is sanitizing the agent's output to prevent injection attacks:

javascript
// sanitize.js

function sanitizeOutput(content) {
  const injectionPatterns = [
    /ignore (all |previous )?instructions/gi,
    /you are now (a |an )?/gi,
    /system prompt/gi,
  ];

  let sanitized = content;
  for (const pattern of injectionPatterns) {
    if (pattern.test(sanitized)) {
      console.warn('[WARNING] Potential prompt injection detected');
      sanitized = sanitized.replace(pattern, '[FILTERED]');
    }
  }

  return sanitized;
}

module.exports = { sanitizeOutput };

Best Practices for Production

When deploying agents to production, follow these additional guidelines:

PracticeDescription
Principle of least privilegeGive agents the minimum access needed
Time-bounded sessionsAgents expire after a set duration
Geographic restrictionsLimit agent access to specific IP ranges
Budget capsPrevent runaway API costs with hard limits
Anomaly detectionAlert on unusual action patterns
Regular auditsReview agent logs weekly

Model Selection for Safe Agents

Choosing the right model matters for safety. Models with stronger safety training are less likely to attempt harmful actions even without explicit guardrails:

ModelSafety TrainingBest For
GPT-5.6 SolExtensive RLHFComplex multi-step agents
Claude Sonnet 5Constitutional AITasks requiring nuanced judgment
GLM 5.2RLHF + safety filtersHigh-volume, cost-sensitive tasks
DeepSeek V4 FlashStandard RLHFFast, lightweight operations

Explore all available models and their safety features at qubax.ai/models.

Conclusion

Building AI agents without guardrails is like giving someone a car with no brakes -- it might go fast, but eventually, it will crash. The five layers we've built in this tutorial (tool allowlisting, prompt constraints, output validation, audit logging, and human-in-the-loop) provide a robust foundation for safe agent development.

Remember: guardrails aren't about limiting what AI can do. They're about ensuring AI does what you mean, not just what you say. That's the difference between a tool that empowers and one that causes harm.


Ready to build safe AI agents? Qubax AI provides 300+ models with built-in safety features, OpenAI-compatible APIs, and comprehensive documentation. Start building at [qubax.ai/docs](https://qubax.ai/docs).

FAQ

What are AI agent guardrails?

Guardrails are safety mechanisms that constrain what an AI agent can do. They include tool allowlists, prompt-based constraints, output validation, audit logging, and human approval workflows. They prevent agents from taking harmful or unintended actions.

Do I need guardrails if I'm using a safe model like Claude?

Yes. While models like Claude Sonnet 5 and GPT-5.6 Sol have strong safety training, guardrails provide an additional layer of defense. Defense in depth -- multiple independent safety layers -- is the standard practice in security engineering.

How do I choose which model to use for my agent?

Consider the task complexity, safety requirements, and budget. GPT-5.6 Sol is best for complex agents, Claude Sonnet 5 for nuanced judgment, GLM 5.2 for cost-sensitive high-volume tasks, and DeepSeek V4 Flash for lightweight operations. Compare options at qubax.ai/models.

Can guardrails prevent all AI safety problems?

No. Guardrails significantly reduce risk but cannot eliminate it entirely. Some risks, like specification gaming, require careful prompt design and ongoing monitoring in addition to technical guardrails.

What's the cost of running a guarded agent?

The guardrail middleware itself adds negligible cost. The main cost is the API calls to the AI model. GLM 5.2 at $0.15/$0.15 per million tokens (Qubax pricing) is extremely cost-effective for agent workloads. See qubax.ai/models for full pricing.

How do I monitor my agent in production?

Use the audit logging pattern from this tutorial and feed it into a monitoring system. Set up alerts for rate limit violations, blocked actions, and unusual activity patterns. Review logs regularly to catch emerging issues.

🤖

Try Claude Sonnet 5 on Qubax

Best balance of speed and quality. Up to 62% off.

View pricing

Article tags

#AI Agents#AI Safety#Tutorial#Guardrails#Developer Guide
Share:Post on XTelegramLinkedInYHacker NewsReddit
Qubax AI

Qubax AI

AI Models at up to 99% off · Pay with crypto

Reading about Claude Sonnet 5 and Claude? Access them — plus 340+ other models — through one API. Best balance of speed and quality. Up to 62% off.

Related articles