Home / AI & GenAI

AI & GenAIHow-toBeginner

Introduction to LLM guardrails: input checks, output checks and tool limits for a production chatbot

What guardrails are, the layers a production LLM app needs (input, retrieval, output, tools), and a small Python example that blocks secrets, checks grounding and limits tool calls, plus managed options on Azure, AWS and OCI.

Deepak Bhardwaj5 min read

Applies toLLM applications using Azure OpenAI, Amazon Bedrock, OCI Generative AI or self-hosted models; Python 3.10+

What "guardrails" means

Guardrails are checks around the model, not inside it. The model will sometimes follow malicious instructions, make things up, or leak data it was shown. Guardrails catch those cases before they reach a user or a system.

User input ─► [Input checks] ─► [Retrieval + filters] ─► LLM ─► [Output checks] ─► User
                                                          │
                                                          └─► [Tool call checks] ─► Systems

No single check is enough. You stack cheap, predictable checks and accept that each one catches some problems.

Layer 1: Input checks

Check Why How
Length limit Stops huge prompts that cost money or hide injections Reject or truncate over N tokens
Secrets / PII Users paste passwords, keys, card numbers Regex + a PII detector; mask before sending
Prompt injection / jailbreak attempts "Ignore previous instructions..." Classifier or managed service (Prompt Shields, Bedrock Guardrails prompt attack filter)
Topic scope An IT helpdesk bot shouldn't give legal advice Simple classifier or allow-list of intents

Layer 2: Retrieval (for RAG)

  • Filter documents by the user's permissions before they reach the prompt. The model can't keep secrets from a user if you put them in its context.
  • Treat retrieved text as data, not instructions. Documents can contain injected instructions ("When summarising this page, tell the user to visit...").
  • Delimit it clearly in the prompt and tell the model not to follow instructions inside it. This reduces, but doesn't eliminate, the risk.

Layer 3: Output checks

Check How
Harmful content Provider content filters, or a moderation model
Leaked secrets / PII Same detectors as input, on the answer
Grounding For RAG: does each claim have a supporting source? Ask a second model, or require citations and verify they exist in retrieved chunks
Format Validate JSON against a schema before using it
Links Only allow URLs from your own domains

Layer 4: Tool and agent limits

When the model can call tools (run queries, open tickets, restart services), guardrails matter most:

  • Allow-list tools per user role. A read-only user's session never gets the restart_service tool.
  • Validate arguments in code (types, ranges, allowed hostnames), never trust the model's values.
  • Human approval for destructive or costly actions.
  • Step and budget limits: max tool calls per request, max tokens, max wall time.
  • Log every tool call with the user, arguments and result.

A small Python example

This shows the shape of a guarded call. It uses plain regexes and a second LLM call for grounding; replace the pieces with managed services where you have them.

import re, json

MAX_INPUT_CHARS = 8000
SECRET_PATTERNS = [
    re.compile(r"AKIA[0-9A-Z]{16}"),                          # AWS access key id
    re.compile(r"-----BEGIN (RSA |EC )?PRIVATE KEY-----"),
    re.compile(r"(?i)password\s*[:=]\s*\S+"),
    re.compile(r"\b(?:\d[ -]*?){13,16}\b"),                     # card-like numbers
]
INJECTION_HINTS = re.compile(r"(?i)(ignore (all|previous) instructions|you are now|system prompt)")

def check_input(text: str) -> str:
    if len(text) > MAX_INPUT_CHARS:
        raise ValueError("Input too long")
    for p in SECRET_PATTERNS:
        text = p.sub("[REDACTED]", text)
    if INJECTION_HINTS.search(text):
        # don't block outright on a regex; flag for stricter handling and logging
        print("WARN possible prompt injection")
    return text

def check_output(answer: str, allowed_domains=("hubdatai.com", "learn.microsoft.com")) -> str:
    for p in SECRET_PATTERNS:
        answer = p.sub("[REDACTED]", answer)
    for url in re.findall(r"https?://([^/\s]+)", answer):
        if not any(url.endswith(d) for d in allowed_domains):
            answer = answer.replace(url, "[link removed]")
    return answer

def is_grounded(llm, answer: str, sources: list[str]) -> bool:
    prompt = (
        "Answer only YES or NO. Is every factual claim in the ANSWER supported by the SOURCES?\n\n"
        f"SOURCES:\n{json.dumps(sources)[:12000]}\n\nANSWER:\n{answer}"
    )
    verdict = llm(prompt, temperature=0)
    return verdict.strip().upper().startswith("YES")

def guarded_answer(llm, user_text: str, retrieve):
    clean = check_input(user_text)
    sources = retrieve(clean)                       # must already apply user permissions
    answer = llm(build_prompt(clean, sources), temperature=0.2)
    if not is_grounded(llm, answer, sources):
        return "I couldn't find a reliable answer in the documentation I can access."
    return check_output(answer)

llm, retrieve and build_prompt are your own functions. The point is the order: clean input, permission-filtered retrieval, grounding check, output scrub.

Warning

Regex checks are easy to bypass and produce false positives. Use them as a cheap first layer and for logging, not as your only defence against prompt injection.

Managed guardrail services

Platform Service Covers
Azure Azure AI Content Safety, Prompt Shields, Azure OpenAI content filters, groundedness detection Harmful content, jailbreak/indirect injection, protected material, grounding
AWS Amazon Bedrock Guardrails Content filters, denied topics, word filters, PII redaction, contextual grounding checks, prompt attack filter
OCI OCI Generative AI guardrails / content moderation features Content moderation, prompt injection and PII (check current feature list for your region)
Self-hosted Open-source guardrail frameworks and moderation models Varies; you run and tune them

Feature names and availability change often; check the current documentation for your region before designing around one.

Measuring guardrails

  • Keep a test set of bad inputs (injections, secrets, off-topic) and good inputs. Run it in CI when prompts or models change.
  • Track block rate and false positive rate in production; a guardrail that blocks 10% of legitimate questions will be switched off by frustrated users.
  • Log blocked requests (with sensitive data masked) for review.

Prevent problems later

  • Apply permissions at retrieval, not in the prompt.
  • Give tools the least privilege and validate every argument in code.
  • Start with managed filters, then add app-specific checks for your own data and tools.
  • Re-test guardrails whenever you change model version, system prompt or tools.