Guardrails AI Library

So far, you have written every guardrail by hand. That is the best way to learn, but in a real project, you do not have to start from zero. Guardrails AI is an open-source Python library that gives you a ready-made structure for guardrails, plus a collection of pre-built checks that you can plug into your application.

In this chapter, you will learn the main ideas of the library, set it up correctly, and run six examples: a first guard, several checks in one guard, the different ways to react to a failed check, your own custom check, structured output with Pydantic, and a complete chat function.

What is Guardrails AI?

Guardrails AI is a Python framework that does two main jobs:

  • It runs checks on the input and output of an AI application to detect and handle specific risks, such as private data, rude language, or wrong formats.
  • It helps you get structured data, such as JSON that follows a Pydantic model, from an AI model.

The Key Concepts

  • Validator: A single check. For example, a validator can check the length of a text, match it against a pattern, or look for private data.
  • Guard: A container that holds one or more validators and runs them on a piece of text. You create a guard and give it the validators that you want.
  • on_fail action: What the guard does when a validator finds a problem.
  • Guardrails Hub: A collection of ready-made validators that other people have built, such as validators for private data, toxic language, and competitor names.

The on_fail Actions

Every validator has an on_fail setting. It decides what happens when the check fails. These are the main options, which you import as OnFailAction:

  • EXCEPTION: Raise an error, so that your program can catch it and react.
  • FIX: Replace the text with a corrected version, if the validator knows how to fix it. For example, a length validator can shorten the text.
  • NOOP: Do nothing to the text, but record that the check failed. You can read the result and decide yourself.
  • FILTER: Remove the failing value from the output.
  • REFRAIN: Return nothing, instead of returning an output that failed.
  • REASK: Ask the AI model again and include the error in the request. This one needs a call to a real AI model, so we do not use it in this chapter.

These are the same actions that you built by hand in earlier chapters: block, edit, retry, and fall back.

Setting Up

Use Python 3.12 or 3.13 for This Chapter

At the time of writing (October 2026), the current Guardrails AI release, version 0.11.0, supports Python 3.10 up to 3.13. It does not install on Python 3.14 yet. Libraries often need some time to support a new Python release. Always check the project page on PyPI for the current requirement.

If your computer has Python 3.14, you can install Python 3.12 or 3.13 next to it from python.org. The versions do not interfere with each other. Then create a separate virtual environment for this chapter, as you learned in Chapter 4.

On Windows:

py -3.12 -m venv venv-guardrails
venv-guardrails\Scripts\activate
python --version

On macOS and Linux:

python3.12 -m venv venv-guardrails
source venv-guardrails/bin/activate
python --version

The last command should show Python 3.12.x. If you see 3.14, your environment was created with the wrong Python.

Install the Library and Two Validators

pip install guardrails-ai
pip install guardrails-ai-regex-match guardrails-ai-valid-length

The first command installs the core library. The second command installs two validators. In 2026, Guardrails AI moved its validators to regular packages on PyPI. They are named guardrails-ai- followed by the validator name, and you import them from the guardrails_ai package. For example, the validator regex-match is imported with from guardrails_ai.regex_match import RegexMatch.

If you read older tutorials, you may see commands such as guardrails hub install hub://guardrails/regex_match and imports from guardrails.hub. That was the previous way of installing validators. Use the pip method shown here.

Optional: Configure the Command-Line Tool

guardrails configure

This command is optional. It asks whether you want to enable anonymous usage reporting. If you run the examples while offline, you may see warning lines about sending usage data. They do not change the results.

Example 1: Your First Guard

Our first guard checks that a text looks like a phone number in the form 123-456-7890. We use the RegexMatch validator, and we tell it to raise an error when the check fails.

from guardrails import Guard, OnFailAction
from guardrails_ai.regex_match import RegexMatch

guard = Guard().use(
    RegexMatch(regex=r"^\d{3}-\d{3}-\d{4}$", on_fail=OnFailAction.EXCEPTION)
)

result = guard.validate("123-456-7890")
print("Valid phone number:", result.validation_passed)

try:
    guard.validate("1234-789-0000")
except Exception as error:
    print("Error:", error)

Output

Valid phone number: True
Error: Validation failed for field with errors: Result must match ^\d{3}-\d{3}-\d{4}$

Understanding the Code

  • Guard().use(…) creates a guard and adds a validator to it.
  • RegexMatch(regex=…) creates the validator. The pattern ^\d{3}-\d{3}-\d{4}$ means: three digits, a dash, three digits, a dash, and four digits, and nothing else before or after.
  • on_fail=OnFailAction.EXCEPTION means that a failed check raises an error.
  • guard.validate(text) runs the check and returns a result object. Its validation_passed value is True or False.
  • The first text matches the pattern, so it passes. The second text has four digits in the first group, so the guard raises an error, which we catch and print.

Some older examples pass the validator arguments directly to use(). The style in this chapter, where you create the validator object first and then give it to use(), is the one that worked in our tests.

Example 2: Several Validators in One Guard

A guard can hold many validators. Here, a text must start with a capital letter followed by small letters only, and it must be 1 to 12 characters long. We use NOOP, so the guard never raises an error, and we read the result ourselves. The method parse checks a piece of text that you already have.

from guardrails import Guard, OnFailAction
from guardrails_ai.regex_match import RegexMatch
from guardrails_ai.valid_length import ValidLength

guard = Guard().use(
    RegexMatch(regex=r"^[A-Z][a-z]*$", on_fail=OnFailAction.NOOP),
    ValidLength(min=1, max=12, on_fail=OnFailAction.NOOP),
)

for text in ["Caesar", "Caesar Salad", "Caesarisagreatleader"]:
    result = guard.parse(text)
    print(text, "|", result.validation_passed)

Output

Caesar | True
Caesar Salad | False
Caesarisagreatleader | False

Understanding the Code

  • Guard().use(first, second) adds both validators. The text must pass all of them.
  • ValidLength(min=1, max=12) checks the number of characters.
  • “Caesar” passes both checks. “Caesar Salad” fails the pattern, because it contains a space and a second capital letter. “Caesarisagreatleader” matches the pattern but is 20 characters long, so it fails the length check.
  • The result is a single True or False, so you do not have to run each check yourself.

Example 3: Comparing the on_fail Actions

Let us see how each action treats the same failing text. The text “abcdefghij” is 10 characters long, but our length validator allows at most 5.

from guardrails import Guard, OnFailAction
from guardrails_ai.valid_length import ValidLength

text = "abcdefghij"

for action in [OnFailAction.NOOP, OnFailAction.FIX, OnFailAction.FILTER, OnFailAction.REFRAIN]:
    guard = Guard().use(ValidLength(min=1, max=5, on_fail=action))
    result = guard.validate(text)
    print(action.name, "|", result.validation_passed, "|", result.validated_output)

guard = Guard().use(ValidLength(min=1, max=5, on_fail=OnFailAction.EXCEPTION))
try:
    guard.validate(text)
except Exception as error:
    print("EXCEPTION |", error)

Output

NOOP | False | abcdefghij
FIX | True | abcde
FILTER | False | None
REFRAIN | False | None
EXCEPTION | Validation failed for field with errors: Value has length greater than 5. Please return a shorter output, that is shorter than 5 characters.

Understanding the Code

  • validated_output is the text that the guard gives back after applying the action.
  • NOOP reports the failure but leaves the text unchanged.
  • FIX shortens the text to 5 characters, and the check counts as passed. This validator knows how to fix a text that is too long.
  • FILTER and REFRAIN both return None for this simple text, which means that there is no output to use. They behave differently with more complex data, such as a list of items, where FILTER can remove only the failing parts.
  • EXCEPTION raises an error with a readable message.
  • Choose the action by the situation. FIX is convenient for small problems, and EXCEPTION or REFRAIN is safer for serious ones.

Example 4: Writing Your Own Validator

You can also create your own validator. The one below looks for email addresses, as in Chapter 9. If it finds one, it reports a failure and also supplies a fixed version of the text with the email hidden. A validator is a class with a validate method that returns either PassResult or FailResult.

import re
from typing import Any, Dict

from guardrails import Guard, OnFailAction
from guardrails.validators import (
    FailResult,
    PassResult,
    ValidationResult,
    Validator,
    register_validator,
)

@register_validator(name="no_email", data_type="string")
class NoEmail(Validator):
    def validate(self, value: Any, metadata: Dict[str, Any]) -> ValidationResult:
        pattern = r"[\w\.-]+@[\w\.-]+\.\w+"
        if re.search(pattern, value):
            fixed = re.sub(pattern, "[EMAIL HIDDEN]", value)
            return FailResult(
                error_message="The text contains an email address.",
                fix_value=fixed,
            )
        return PassResult()

text = "Contact us at help@example.com for support."

for action in [OnFailAction.FIX, OnFailAction.NOOP]:
    guard = Guard().use(NoEmail(on_fail=action))
    result = guard.validate(text)
    print(action.name, "|", result.validation_passed, "|", result.validated_output)

guard = Guard().use(NoEmail(on_fail=OnFailAction.EXCEPTION))
try:
    guard.validate(text)
except Exception as error:
    print("EXCEPTION |", error)

guard = Guard().use(NoEmail(on_fail=OnFailAction.FIX))
clean = guard.validate("Nothing private here.")
print("Clean text |", clean.validation_passed, "|", clean.validated_output)

Output

FIX | True | Contact us at [EMAIL HIDDEN] for support.
NOOP | False | Contact us at help@example.com for support.
EXCEPTION | Validation failed for field with errors: The text contains an email address.
Clean text | True | Nothing private here.

Understanding the Code

  • @register_validator(name=”no_email”, data_type=”string”) registers the class as a validator that works on text.
  • The class inherits from Validator. Its validate method receives the text in the value parameter.
  • If the text contains an email address, the method returns FailResult with an error message and a fix_value, which is the text with the email replaced. Otherwise, it returns PassResult.
  • The same validator behaves differently depending on on_fail. With FIX, the guard uses the fix_value. With NOOP, the text stays unchanged, and the check is recorded as failed. With EXCEPTION, an error is raised.
  • The last test has no email address, so the text passes unchanged.

Example 5: Structured Output with Pydantic

In Chapter 8, you validated JSON replies with Pydantic. Guardrails AI can do the same by creating a guard directly from a Pydantic model with Guard.for_pydantic.

from pydantic import BaseModel, Field
from guardrails import Guard

class Recipe(BaseModel):
    name: str = Field(description="Name of the dish")
    cooking_time_minutes: int = Field(gt=0, description="Cooking time in minutes")

guard = Guard.for_pydantic(output_class=Recipe)

replies = [
    ("correct JSON", '{"name": "Pasta", "cooking_time_minutes": 10}'),
    ("wrong type", '{"name": "Pasta", "cooking_time_minutes": "a long time"}'),
    ("impossible time", '{"name": "Pasta", "cooking_time_minutes": 0}'),
    ("plain text", "Sure! Here is a recipe for pasta."),
]

for label, reply in replies:
    result = guard.parse(reply)
    print(label, "|", result.validation_passed, "|", result.validated_output)

Output

correct JSON | True | {'name': 'Pasta', 'cooking_time_minutes': 10}
wrong type | False | None
impossible time | False | None
plain text | False | None

Understanding the Code

  • The Recipe class describes the structure. The rule gt=0 means that the cooking time must be greater than 0.
  • Guard.for_pydantic(output_class=Recipe) creates a guard that checks replies against this structure.
  • guard.parse(reply) checks a reply that you already have, such as the text returned by your AI model.
  • The first reply is correct, so validated_output holds the data as a dictionary. The other three fail, because of a wrong type, a value that breaks the rule, and a reply that is not JSON at all. In each of those cases, validated_output is None.
  • The library can also call an AI model for you and ask it to produce output in this structure. That requires an account and an API key with an AI provider, so we do not cover it here. Check the official documentation if you want to use it.

Example 6: A Complete Chat Function

Now let us combine an input guard and an output guard in one chat function. The input guard checks the message length. The output guard hides email addresses in the reply, using the validator from Example 4. The AI model is a fake function, so no API key is needed. In a real application, you would call your AI provider at that point.

import re
from typing import Any, Dict

from guardrails import Guard, OnFailAction
from guardrails.validators import (
    FailResult,
    PassResult,
    ValidationResult,
    Validator,
    register_validator,
)
from guardrails_ai.valid_length import ValidLength

@register_validator(name="no_email", data_type="string")
class NoEmail(Validator):
    def validate(self, value: Any, metadata: Dict[str, Any]) -> ValidationResult:
        pattern = r"[\w\.-]+@[\w\.-]+\.\w+"
        if re.search(pattern, value):
            fixed = re.sub(pattern, "[EMAIL HIDDEN]", value)
            return FailResult(
                error_message="The text contains an email address.",
                fix_value=fixed,
            )
        return PassResult()

input_guard = Guard().use(
    ValidLength(min=1, max=100, on_fail=OnFailAction.EXCEPTION)
)
output_guard = Guard().use(NoEmail(on_fail=OnFailAction.FIX))

def fake_llm(prompt):
    return "Thanks for asking! Email chef@example.com for the full recipe."

def safe_chat(message):
    try:
        input_guard.validate(message)
    except Exception:
        return "Sorry, your message must be between 1 and 100 characters."
    reply = fake_llm(message)
    result = output_guard.validate(reply)
    return result.validated_output

print(safe_chat("How do I cook pasta?"))
print(safe_chat(""))
print(safe_chat("a" * 150))

Output

Thanks for asking! Email [EMAIL HIDDEN] for the full recipe.
Sorry, your message must be between 1 and 100 characters.
Sorry, your message must be between 1 and 100 characters.

Understanding the Code

  • input_guard raises an error if the message is empty or longer than 100 characters.
  • output_guard uses FIX, so an email address in the reply is replaced by [EMAIL HIDDEN], and the rest of the reply is kept.
  • safe_chat first validates the message. If an error is raised, it returns a polite refusal and never calls the model. Otherwise, it gets the reply from the model, runs the output guard, and returns validated_output.
  • The first message is fine, and the email address in the model’s reply is hidden. The second message is empty, and the third is 150 characters long, so both are refused.

Other Validators in the Hub

The Guardrails Hub has many more validators than the two that we installed. Some examples are:

  • DetectPII: Finds personal data such as emails and phone numbers.
  • ToxicLanguage: Detects rude or harmful language.
  • CompetitorCheck: Finds mentions of competitor names that you provide.
  • SecretsPresent: Looks for secrets such as API keys in a text.

Each validator has its own page with the install name, the options, and the requirements. Check that page before you use it. Be aware that some validators use machine learning models. They may download large files, and they are slower than simple pattern checks.

How the Library Relates to What You Built

  • The length checks from Chapter 6 correspond to ValidLength.
  • The regex rules from Chapter 5 correspond to RegexMatch.
  • The PII masking from Chapter 9 corresponds to DetectPII, or to a custom validator like NoEmail.
  • The toxicity checks from Chapter 10 correspond to ToxicLanguage.
  • The competitor filter from Part 3 of Chapter 13 corresponds to CompetitorCheck.
  • The Pydantic validation from Chapter 8 corresponds to Guard.for_pydantic.
  • The block, edit, retry, and fallback actions from Chapter 7 correspond to the on_fail actions.

Should You Use the Library or Write Your Own?

  • Reasons to use the library: A consistent way to build guards, many ready-made validators, built-in actions for failures, and structured output support.
  • Reasons to write your own: No extra dependency, full control, and simple checks stay fast and easy to understand.
  • Things to watch: The library may limit the Python versions you can use, some validators are heavy, and the library changes over time. Pin the version in your requirements.txt file, and test again before you upgrade.

Many projects use both. They write small custom checks for their own rules and use library validators for harder problems.

Key Takeaways

  • Guardrails AI gives you guards, validators, and on_fail actions as building blocks.
  • Install the library with pip, and install validators as separate pip packages named guardrails-ai- followed by the validator name.
  • The current release needs Python 3.10 to 3.13, so use a separate environment with Python 3.12 or 3.13 for this chapter.
  • EXCEPTION, FIX, NOOP, FILTER, REFRAIN, and REASK decide what happens when a check fails.
  • You can write your own validators, and Guard.for_pydantic validates structured output.
  • The library works with any AI model, because you can run a guard on any text that you already have.

What is Next?

In the next chapter, you will learn about NeMo Guardrails, another popular toolkit, which controls the conversation itself by defining rules for what the AI may talk about and how it should respond.


If you liked the tutorial, spread the word and share the link and our website, Studyopedia, with others.


For Videos, Join Our YouTube Channel: Join Now


Read More:

Top 25 LLM Guardrails With Examples
NeMo Guardrails
Studyopedia Editorial Staff
contact@studyopedia.com

We work to create programming tutorials for all.

No Comments

Post A Comment