Bedrock Guardrails

Bedrock Guardrails is the managed safety layer that sits between user input and model output. A guardrail is a versioned, IAM-controlled resource that combines six configurable filter types — content filters, denied topics, word filters, sensitive-info filters (PII), contextual grounding checks, and automated reasoning checks — and runs them on both the prompt and the completion. Apply it to any Bedrock model via the guardrailConfig field on Converse / InvokeModel, attach it to a Bedrock Agent, or use it standalone via the ApplyGuardrail API to evaluate text that hasn’t been through Bedrock at all (useful for filtering third-party model output).

See also

1. The six filter types

Per docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html.

FilterWhat it doesConfigurable severity
Content filtersDetect and block six harm categories: Hate, Insults, Sexual, Violence, Misconduct, Prompt AttackNONE / LOW / MEDIUM / HIGH per category, per input/output
Denied topicsUser-defined topics that should be off-limits (“legal advice”, “financial advice”, “competitor X”)Per-topic on/off, with name + definition + example phrases
Word filtersExact-match blocklist of words and phrases. Includes built-in profanity list.Per-word on/off
Sensitive info filters (PII)Detect and block or mask PII (SSN, credit card, address, phone, email, IP, custom regex). 30+ entity types.BLOCK / ANONYMIZE per entity type
Contextual grounding checkDetect hallucinations — measures grounding (response uses source) and relevance (response addresses query)Threshold 0.0–0.99 per metric
Automated Reasoning checksFormal logical validation against rules expressed in a custom policy domainPolicy + ARN

2. Tiers — Classic vs Standard

Per re:Invent 2024: Bedrock Guardrails now has two tiers.

  • Classic (legacy, cheaper) — text-only filtering, English-only for content filters, no code-aware filtering.
  • Standard — adds image content filtering, code-aware filtering (detects harmful intent inside code comments + string literals + variable names), expanded language coverage.

Pricing:

  • Classic: $0.15 per 1,000 text units (1 text unit = 1k chars input).
  • Standard: $0.20 per 1,000 text units for content + denied topics, $0.10 / 1k for PII detection, $0.50 / 1k for PII detection + redaction, $0.10 / 1k for contextual grounding checks.

3. Creating a guardrail — Python walkthrough

import boto3
 
ctl = boto3.client("bedrock", region_name="us-east-1")
 
resp = ctl.create_guardrail(
    name="prod-customer-chat-guard",
    description="Block harmful content + competitor mentions + financial advice",
    blockedInputMessaging="I can't help with that. Try rephrasing.",
    blockedOutputsMessaging="I'm not able to provide that information.",
    
    # 1. Content filters
    contentPolicyConfig={
        "filtersConfig": [
            {"type": "SEXUAL", "inputStrength": "HIGH", "outputStrength": "HIGH"},
            {"type": "VIOLENCE", "inputStrength": "HIGH", "outputStrength": "HIGH"},
            {"type": "HATE", "inputStrength": "HIGH", "outputStrength": "HIGH"},
            {"type": "INSULTS", "inputStrength": "MEDIUM", "outputStrength": "MEDIUM"},
            {"type": "MISCONDUCT", "inputStrength": "HIGH", "outputStrength": "HIGH"},
            {"type": "PROMPT_ATTACK", "inputStrength": "HIGH", "outputStrength": "NONE"},
        ]
    },
    
    # 2. Denied topics
    topicPolicyConfig={
        "topicsConfig": [
            {
                "name": "FinancialAdvice",
                "definition": (
                    "Investment advice, stock recommendations, or specific "
                    "guidance on buying/selling financial instruments."
                ),
                "examples": [
                    "Should I buy AAPL?",
                    "What's a good ETF to invest in?",
                ],
                "type": "DENY",
            },
            {
                "name": "Competitors",
                "definition": "Discussion of competing products by name",
                "examples": ["What about Acme Corp's offering?"],
                "type": "DENY",
            },
        ]
    },
    
    # 3. Word filters
    wordPolicyConfig={
        "wordsConfig": [{"text": "internal-codename-x"}],
        "managedWordListsConfig": [{"type": "PROFANITY"}],
    },
    
    # 4. Sensitive info / PII
    sensitiveInformationPolicyConfig={
        "piiEntitiesConfig": [
            {"type": "EMAIL", "action": "ANONYMIZE"},
            {"type": "PHONE", "action": "ANONYMIZE"},
            {"type": "CREDIT_DEBIT_CARD_NUMBER", "action": "BLOCK"},
            {"type": "US_SOCIAL_SECURITY_NUMBER", "action": "BLOCK"},
            {"type": "IP_ADDRESS", "action": "ANONYMIZE"},
        ],
        "regexesConfig": [
            {
                "name": "InternalEmployeeID",
                "pattern": r"EMP-\d{6}",
                "action": "ANONYMIZE",
            }
        ],
    },
    
    # 5. Contextual grounding (for RAG)
    contextualGroundingPolicyConfig={
        "filtersConfig": [
            {"type": "GROUNDING", "threshold": 0.7},
            {"type": "RELEVANCE", "threshold": 0.5},
        ]
    },
    
    tags=[{"key": "Environment", "value": "production"}],
)
guardrail_id = resp["guardrailId"]
 
# Create a version (immutable snapshot)
v = ctl.create_guardrail_version(
    guardrailIdentifier=guardrail_id,
    description="Initial production version",
)
print(f"Version: {v['version']}")

4. Applying a guardrail at inference time

Via Converse

runtime = boto3.client("bedrock-runtime", region_name="us-east-1")
 
resp = runtime.converse(
    modelId="anthropic.claude-sonnet-4-6-20260119-v1:0",
    messages=[{"role": "user", "content": [{"text": "Should I buy Tesla stock?"}]}],
    guardrailConfig={
        "guardrailIdentifier": guardrail_id,
        "guardrailVersion": "1",
        "trace": "enabled",   # include guardrail trace in response
    },
)
 
if resp["stopReason"] == "guardrail_intervened":
    print("Guardrail blocked. Filters that fired:")
    for filt in resp["trace"]["guardrail"]["inputAssessment"][guardrail_id]["topicPolicy"]["topics"]:
        if filt["action"] == "BLOCKED":
            print(f"  - Topic: {filt['name']}")

Selective content evaluation

By default the guardrail evaluates every message. If you have safe RAG context that shouldn’t be re-evaluated as user input, mark only the actual user query:

"messages": [{
    "role": "user",
    "content": [
        {"text": "Context: <KB results>"},
        {
            "guardContent": {
                "text": {"text": "User question here"}
            }
        },
    ],
}]

Only the guardContent-wrapped block is evaluated.

Standalone ApplyGuardrail

Apply a guardrail to text that didn’t go through Bedrock — useful for filtering output from a third-party model:

resp = runtime.apply_guardrail(
    guardrailIdentifier=guardrail_id,
    guardrailVersion="1",
    source="OUTPUT",   # or "INPUT"
    content=[{"text": {"text": "The text to evaluate"}}],
)
 
# Inspect action: NONE / GUARDRAIL_INTERVENED
print(resp["action"])
print(resp["outputs"])  # potentially modified text
print(resp["assessments"])  # detailed filter results

5. Contextual grounding — the RAG hallucination filter

Per docs.aws.amazon.com/bedrock/latest/userguide/guardrails-contextual-grounding-check.html.

Two metrics, each scored 0.0–1.0:

  • Grounding — does the response only use information from the provided source?
  • Relevance — does the response address the user’s query?

Configured with thresholds. If the score is below threshold, the response is blocked (or annotated, depending on action). Typically used in RetrieveAndGenerate flows:

# At inference time, pass the source text via guardContent qualifier
"messages": [{
    "role": "user",
    "content": [
        {
            "guardContent": {
                "text": {
                    "text": "[long KB context here]",
                    "qualifiers": ["grounding_source"],   # ← tells guardrail this is source-of-truth
                }
            }
        },
        {
            "guardContent": {
                "text": {
                    "text": "What is the parental leave policy?",
                    "qualifiers": ["query"],
                }
            }
        },
    ],
}]

The guardrail then evaluates the model’s response against the marked source. Grounding score < 0.7 = likely hallucination.

6. Automated Reasoning checks (re:Invent 2024)

Per docs.aws.amazon.com/bedrock/latest/userguide/automated-reasoning-checks.html. This is a formal verification layer — you define logical rules in a custom “policy domain” (e.g. “patients on warfarin should not take ibuprofen”), and Bedrock uses an automated reasoning engine to verify model output against those rules.

Status: GA in 2025, but takes meaningful setup — you author the policy in Bedrock’s policy authoring console, train it against examples, then attach to the guardrail. Useful in regulated domains (healthcare, finance, legal) where you can express ground truth as logical rules.

# Once the policy is authored and trained:
ctl.update_guardrail(
    guardrailIdentifier=guardrail_id,
    automatedReasoningPolicyConfig={
        "policies": ["arn:aws:bedrock:us-east-1:...:automated-reasoning-policy/abc"],
        "confidenceThreshold": 0.85,
    },
    # ... other config remains
)

7. Sensitive info / PII — entity list

Per docs.aws.amazon.com/bedrock/latest/userguide/guardrails-sensitive-information-filters.html. 30+ entity types organized by category:

General: ADDRESS, AGE, NAME, EMAIL, PHONE, USERNAME, PASSWORD, DRIVER_ID, LICENSE_PLATE, VEHICLE_IDENTIFICATION_NUMBER

Financial: CREDIT_DEBIT_CARD_NUMBER, CREDIT_DEBIT_CARD_CVV, CREDIT_DEBIT_CARD_EXPIRY, PIN, INTERNATIONAL_BANK_ACCOUNT_NUMBER, SWIFT_CODE

Identifiers (US): US_BANK_ACCOUNT_NUMBER, US_BANK_ROUTING_NUMBER, US_INDIVIDUAL_TAX_IDENTIFICATION_NUMBER, US_PASSPORT_NUMBER, US_SOCIAL_SECURITY_NUMBER

Identifiers (other): UK_NATIONAL_HEALTH_SERVICE_NUMBER, UK_NATIONAL_INSURANCE_NUMBER, UK_UNIQUE_TAXPAYER_REFERENCE_NUMBER, CA_HEALTH_NUMBER, CA_SOCIAL_INSURANCE_NUMBER

Tech: URL, IP_ADDRESS, MAC_ADDRESS, AWS_ACCESS_KEY, AWS_SECRET_KEY

Per-entity action options:

  • BLOCK — entire request/response is blocked.
  • ANONYMIZE — entity is replaced with [EMAIL], [SSN], etc.

Custom regex patterns also supported (see config example above).

8. Cross-region distribution

Per docs.aws.amazon.com/bedrock/latest/userguide/guardrails-cross-region.html. A guardrail in us-east-1 can be invoked from other regions via a system-defined cross-region inference profile. Useful when your application traffic is multi-region but you want a single guardrail policy.

9. Inspecting traces

When trace: "enabled" is set on guardrailConfig, the response includes a detailed assessment showing which filters fired:

trace = resp["trace"]["guardrail"]
# trace.inputAssessment.<guardrail_id>.{contentPolicy, topicPolicy, wordPolicy, sensitiveInformationPolicy, contextualGroundingPolicy}
# Each filter has confidence scores, matched values, and actions taken

For production, write traces to CloudWatch — they’re invaluable for tuning thresholds and explaining blocks to users.

10. Limits and quirks

LimitValue
Max guardrails per account per region100
Max versions per guardrail20
Max denied topics per guardrail30
Max words in custom blocklist10,000
Max length per word/phrase200 chars
Max custom regex patterns10
Max input size per call25 KB
Latency overhead~80-200ms per evaluation (input + output)

Quirks:

  • Guardrail latency is serial with model inference — the guardrail evaluates input before the model runs and output after it completes. For streaming responses, the guardrail buffers the stream and evaluates it in chunks, so streamed output is slightly delayed vs an un-guardrailed stream.
  • Reasoning content blocks are NOT evaluated (per the docs page). If you use Claude extended thinking, the model’s reasoning can contain harmful content that’s not caught. Strip reasoning content from anything you log or expose.
  • Built-in PII detection is probabilistic — works well on common formats (US-style SSN, common credit card patterns) but misses unusual formats. Add custom regex for org-specific identifiers.
  • Cross-account guardrails (re:Invent 2024) — a central security team can publish a guardrail in account A, and accounts B/C/D can use it via cross-account permissions. Useful for compliance-driven shared safety policies.

11. When to use Guardrails vs DIY

Use Bedrock Guardrails when:

  • You’re already on Bedrock and want a managed, IAM-controlled safety layer.
  • You need PII detection + redaction without writing your own detectors.
  • You want versioned policies that auditors can inspect.
  • Contextual grounding for RAG hallucination detection is appealing.

DIY (open-source like Llama Guard or NVIDIA NeMo Guardrails) when:

  • You’re calling third-party models that aren’t on Bedrock (and don’t want to use ApplyGuardrail overhead).
  • You need filters Bedrock doesn’t offer (e.g. medical-advice-specific filters).
  • Latency budget is < 100ms per turn and the guardrail overhead is unacceptable.
  • You want filters that run during generation (token-level filtering), not just before/after.

Further reading