Exam Room · Advanced GenAI

Choosing a Guardrail Strategy: Managed, Custom, or Both

August 05, 2026 · 31 min read

Generative AI Development · part of The Exam Room

The situation

A team is putting a generative-AI assistant into production. It answers questions, summarises documents, and drafts responses, all on Amazon Bedrock. Legal, security, and the product owner each hand over a list of things the assistant must never do, and the lists do not look alike.

Some entries are the usual suspects: no hate speech, no sexual content, do not leak a customer’s email address or card number, do not get talked into ignoring its instructions. Others are specific to this business: never quote a price outside the published rate card, never recommend a competitor’s product by name, never emit a response that fails the internal disclosure template, never mention the unreleased product code-named internally until launch day. A few are structural: the drafting feature must always return valid JSON with a fixed set of fields, or the downstream system rejects it.

Bedrock Guardrails is right there, managed and quick to switch on. The question is whether it covers the whole list, and if not, what fills the gap and where the two meet.

What actually matters

The first cut is whether a rule is a standard category or a bespoke one. Standard categories, hate, violence, sexual content, self-harm, common PII types, prompt-injection shapes, generic profanity, are the same for every customer, so a managed service can be trained and tuned on them once and applied everywhere. Bespoke rules encode something only this business knows: its rate card, its competitor list, its disclosure template, its unreleased code name. No off-the-shelf policy has ever seen those, and no amount of configuration teaches a content filter a rule that lives in a spreadsheet the model was never shown. That split, generic versus proprietary, decides more than any other property which layer a given rule belongs to.

The second property is where the check runs and what it can reach. A managed guardrail sits between the application and the model and inspects text: the prompt going in, the completion coming out. It reasons over language. A custom check can do anything code can do, which includes things text inspection cannot: call an authoritative service, look a value up in a database, parse output against a schema and reject it deterministically, compare a quoted figure to the live rate card. If a rule needs ground truth the model does not carry, a language filter is the wrong shape for it and a validator is the right one.

Then there is coverage on both sides of the model. Input filtering stops a bad request before it burns tokens and before the model can be steered by it. Output filtering catches what the model actually produced, which is the only place a hallucinated price or a leaked code name can be seen. Most real rules need both passes, and a control that only runs on one side leaves the other open.

The last two are the running costs of each choice. Every check adds latency and money: a managed guardrail call, a Comprehend call, a database lookup, each has its own price and its own delay, and they stack on the critical path of every request. And custom logic is code somebody owns forever, a competitor list that goes stale, a regex that rots, a schema that drifts from the downstream contract. Managed policies move maintenance to AWS at the cost of control; custom checks keep control at the cost of a maintenance burden that never goes away. A defensible design spends the managed layer where the categories are standard and reserves custom code for the rules that genuinely need it, rather than rebuilding hate-speech detection by hand or trying to bend a content filter into a rate-card validator.

What we’ll filter on

  1. Standard or bespoke: is the rule a common category any customer would share, or does it encode this business’s own proprietary knowledge?
  2. Ground truth: does enforcing it need a value the model does not carry, a live price, a current competitor list, a schema, so that a deterministic check outside the model is the only reliable enforcer?
  3. Coverage: does the control run on the input, the output, or both, and does the rule need both sides?
  4. Latency and cost: what does each check add to the per-request budget in milliseconds and dollars?
  5. Maintenance burden: who owns the logic over time, and how fast does it go stale if nobody tends it?

The guardrail landscape

Amazon Bedrock Guardrails (managed). A configurable safety layer that sits between the application and the model and is applied to both the input and the output. It is model-independent: the same guardrail works across the foundation models Bedrock hosts, and it is versioned so you can promote a tested configuration. The policies it offers cover the standard categories:

  • Denied topics, defined in natural language, so the assistant refuses whole subjects (a competitor comparison, a category of advice) without you enumerating every phrasing.
  • Content filters across harmful categories such as hate, insults, sexual content, violence, and misconduct, each with a configurable strength, plus a prompt-attack filter aimed at prompt-injection and jailbreak attempts.
  • Word filters: block lists for specific terms and a managed profanity filter.
  • Sensitive-information filters that detect PII and either block the request or redact the value, using both built-in PII types and custom regex patterns you supply, applied to input or output.
  • Contextual grounding and relevance checks that score a response against the source passages it was meant to draw on and against the user’s query, blocking answers that are unsupported or off-topic.

Crucially, Guardrails is reachable through the standalone ApplyGuardrail API, which evaluates arbitrary text against a guardrail without invoking a model at all. That means you can screen content that never goes near Bedrock, or run a guardrail on output produced by a model hosted elsewhere, and still get the managed policy layer. The custom-regex hook on the PII filter and the custom word lists let the managed layer absorb a slice of the bespoke work too, as long as the rule can be expressed as a pattern or a list.

Custom checks (your own code). Everything the managed policies do not know about. This is where business-specific rules live: comparing a quoted price against the live rate card, checking a drafted response against the current competitor list, enforcing the disclosure template, refusing the unreleased code name. It is where deterministic validators belong: parsing tool arguments or a drafting response against a strict JSON schema and rejecting anything that does not conform or falls outside allowed values, the kind of hard, repeatable check a language model should never be trusted to do by feel. Custom code is also how you reach a purpose-built service when detection needs more than a filter: Amazon Comprehend for entity recognition, language detection, or its own PII detection; a domain classifier you have trained for a category no generic filter covers; an allow or deny list maintained against an authoritative source. Custom checks run wherever you put them, on the input, on the output, or both, and they can act on ground truth the model was never given.

Both, layered as defence in depth. The strong pattern is not managed instead of custom, or custom instead of managed. It is managed guardrails carrying the common categories, hate, PII, prompt attacks, denied topics, grounding, and custom checks carrying the rules that are specific to the business or that need deterministic enforcement, with the two stacked so a gap in one is covered by the other. Managed handles the breadth cheaply; custom handles the depth the managed layer cannot reach. The same defence-in-depth reasoning runs through the prompt-injection design, where no single control is sufficient and the layers only work because they are independent.

Side by side

Property Managed Bedrock Guardrails Custom checks Both, layered
Standard categories (hate, PII, prompt attacks)
Bespoke business rules (rate card, competitor list)
Deterministic output-schema validation
Grounding and relevance scoring
Input and output coverage
Usable without a model call (ApplyGuardrail)
Model-independent, managed by AWS ✓ (partly)
Low ongoing maintenance burden ✓ (partly)
Reaches external ground truth (Comprehend, a DB)

Read the first two rows together: neither column alone covers both. Managed guardrails own the standard categories and cannot learn the bespoke ones; custom checks own the bespoke rules but rebuilding hate-speech or prompt-attack detection by hand is wasted effort. The “both” column is not a compromise, it is the only column that ticks the rules from every list the team was handed.

The picks in depth

Start by sorting each rule into standard or bespoke, because the sort does most of the work. Hate speech, sexual content, violence, self-harm, common PII, generic profanity, and prompt-injection shapes are standard: they go to managed content filters, the sensitive-information filter, and the prompt-attack filter, tuned by strength rather than reimplemented. The rate card, the competitor list, the disclosure template, the unreleased code name are bespoke: they go to custom code that can consult the authoritative source. A few rules sit on the line and the managed layer can absorb them cheaply: a fixed code name is a word-filter block-list entry, and a structured internal identifier is a custom-regex PII pattern. Push a rule into the managed layer whenever it fits a block list or a regex, and keep the truly dynamic ones, anything that changes as a price or a product list changes, in code where they can be refreshed without re-tuning a guardrail.

For the bespoke rules, decide what ground truth each one needs. A rule that only needs pattern matching stays a simple validator. A rule that needs to know the current price or the live competitor list needs a lookup against the system of record, run as an output check after the model has produced its draft, because the violation only exists in the generated text. A rule that needs specialised detection the managed filters do not offer, entity extraction, language detection, a trained domain classifier, calls out to Comprehend or to your own model. Deterministic structure, the JSON contract the downstream system depends on, is never the language model’s job: validate the output against a schema in code and reject non-conforming responses outright, the same way you would validate any untrusted input.

Then place the checks on the right side of the model and mind the budget. Input-side: run the guardrail on the prompt to block prompt attacks and disallowed topics before the model is invoked, which also saves the token cost of a request that was going to be refused anyway. Output-side: run the guardrail again on the completion for PII redaction, content violations, and grounding, then run the custom checks that need the generated text, the rate-card comparison, the competitor scan, the schema validation. Each check is latency and money on every request, so order them to fail fast: cheap deterministic checks and the input guardrail first, so an expensive Comprehend call or database lookup only runs on requests that have already passed the cheap gates.

Use ApplyGuardrail where the text does not flow through a Bedrock InvokeModel call but still needs screening: content from another source, or output you want to check independently of the generation call. It gives the managed policy layer reach beyond the model invocation itself, which matters when the architecture is not a single call-and-response.

A word on maintenance, because it decides the long-run cost. Every custom check is code the team owns: the competitor list drifts, the disclosure template changes, the schema evolves with the downstream contract. Keep that surface as small as the rules allow. Move anything the managed layer can express, a block list, a regex, a denied topic, into the guardrail, where AWS carries the detection models and you carry only the configuration. Reserve custom code for the rules that genuinely need ground truth or deterministic enforcement, and give each one an owner and a review cadence so a stale competitor list does not quietly become the weakest link.

A worked example: the drafting feature

The assistant’s document-drafting feature has to satisfy three of the handed-over rules at once: never quote a price off the rate card, never name a competitor, and always return valid JSON with a fixed set of fields. It also inherits the standard safety rules every feature carries.

The standard rules go to a managed guardrail associated with the drafting call. The prompt-attack filter and denied topics run on the input; content filters, the PII sensitive-information filter, and grounding run on the output. The unreleased code name, a fixed string, goes into the guardrail’s word-filter block list, and the internal reference-number format goes in as a custom-regex PII pattern that redacts on output. That is the whole slice of the list the managed layer can carry, switched on by configuration, maintained by AWS.

The three feature-specific rules need code the guardrail cannot supply. After the model returns a draft, an output validator parses it against the JSON schema; a response missing a field or malformed is rejected before it ever reaches the downstream system, no model judgement involved. A rate-card check pulls every figure out of the draft and compares it against the live rate-card service, failing the response if a quoted price is not on the current card, because the guardrail has no idea what the prices are. A competitor scan checks the draft against the current competitor list, maintained in a table an owner updates, and blocks a draft that names one. The checks are ordered cheap-first: schema validation runs before the rate-card lookup, so a malformed draft never triggers a call to the rate-card service.

The result is that no rule is enforced in the wrong place. Hate speech and PII are AWS’s trained models, not a regex someone wrote on a Friday. The rate card is a live lookup, not a list baked into a prompt that goes stale the next time pricing changes. The JSON contract is a deterministic parser, not a hope that the model formats correctly. Each rule sits where its shape and its ground truth put it, and the managed and custom layers together cover a list that neither could cover alone.

What’s worth remembering

  1. Sort every rule into standard or bespoke first: standard categories go to managed Guardrails, business-specific rules go to custom code, and the sort decides most of the design.
  2. Amazon Bedrock Guardrails covers the common categories out of the box: denied topics, content filters, word filters, PII detection and redaction, contextual grounding and relevance, and prompt-attack filtering, applied to input and output, model-independent.
  3. Guardrails is reachable through the ApplyGuardrail API without a model call, so you can screen text that never goes through Bedrock or check output from a model hosted elsewhere.
  4. Custom checks own what the managed layer cannot know: bespoke business rules, deterministic output-schema validation, allow and deny lists against an authoritative source, and calls to a purpose-built service like Comprehend.
  5. A rule that needs ground truth the model does not carry, a live price, a current competitor list, a valid schema, needs a deterministic check outside the model, not a language filter.
  6. The strong pattern is both, layered as defence in depth: managed guardrails for breadth across standard categories, custom checks for the depth they cannot reach.
  7. Most rules need coverage on both sides of the model: input filtering to stop bad requests early, output filtering to catch what the model actually produced.
  8. Push a rule into the managed layer whenever it fits a block list or a custom regex; keep only the genuinely dynamic rules in code, so the maintenance surface stays small.
  9. Every check is latency and cost on every request, so order them to fail fast: cheap deterministic checks and the input guardrail before an expensive lookup or classifier call.
  10. Custom logic is code you own forever, a competitor list rots, a schema drifts, so give each custom check an owner and a review cadence, and let AWS carry the detection models it can.

These posts are LLM-aided. Backbone, original writing, and structure by Craig. Research and editing by Craig + LLM. Proof-reading by Craig.