TechByteByByte

Guardrails: Practical Protection via Middleware

Real safety measures for a deployed agent — input validation, output validation, tool permissions, and human approval — built directly on the middleware system from Module 20.

#LangChain#Guardrails#Middleware#Safety

Module 27 protected against technical failure — a timeout, an outage. This module protects against a different, equally real risk: an agent doing something it genuinely shouldn’t, even when every technical piece is working exactly as designed. Recall Module 20’s middleware system — guardrails are, concretely, middleware built specifically for safety.

Example 1: input validation

from langchain.chat_models import init_chat_model
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware

class InputLengthGuardrail(AgentMiddleware):
    def before_agent(self, state):
        last_message = state["messages"][-1].content
        if len(last_message) > 2000:
            raise ValueError("Message too long — please shorten your request.")

agent = create_agent(
    model=init_chat_model("openai:gpt-4o-mini"),
    tools=[],
    middleware=[InputLengthGuardrail()],
)

before_agent runs once, before anything else — a genuine, early checkpoint for rejecting obviously problematic input before it ever reaches the model at all, saving both real cost and real risk.

Example 2: output validation

from langchain.chat_models import init_chat_model
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware

BLOCKED_PHRASES = ["I cannot verify", "as an AI language model"]

class OutputQualityGuardrail(AgentMiddleware):
    def after_model(self, state):
        last_message = state["messages"][-1].content
        if any(phrase in last_message for phrase in BLOCKED_PHRASES):
            state["messages"][-1].content = "Let me look into that and get back to you with a clear answer."
        return state

agent = create_agent(
    model=init_chat_model("openai:gpt-4o-mini"),
    tools=[],
    middleware=[OutputQualityGuardrail()],
)

after_model runs after every single model reply, giving you a genuine, real chance to inspect and, if needed, rewrite output before it ever reaches a user — a real, practical mechanism for enforcing tone or content standards your application actually needs.

Example 3: tool permission guardrails

Recall Module 20’s RoleBasedToolMiddleware — this is that same real pattern, framed explicitly as a safety measure.

from langchain.chat_models import init_chat_model
from langchain.tools import tool
from langchain.agents import create_agent
from langchain.agents.middleware import AgentMiddleware

@tool
def send_email(to: str, body: str) -> str:
    """Send an email to a given address."""
    return f"Email sent to {to}."

class RestrictedDomainGuardrail(AgentMiddleware):
    ALLOWED_DOMAIN = "@company.com"

    def before_model(self, state):
        for msg in state["messages"]:
            if hasattr(msg, "tool_calls"):
                for call in msg.tool_calls or []:
                    if call["name"] == "send_email" and not call["args"].get("to", "").endswith(self.ALLOWED_DOMAIN):
                        raise ValueError("Emails can only be sent to internal company addresses.")
        return state

agent = create_agent(
    model=init_chat_model("openai:gpt-4o-mini"),
    tools=[send_email],
    middleware=[RestrictedDomainGuardrail()],
)

This is a real, meaningful constraint — an agent with genuine power to send real emails is deliberately restricted from sending them anywhere outside a known, trusted domain, regardless of what a user might try to convince it to do.

Example 4: human-in-the-loop approval for sensitive actions

The most conservative, and often most appropriate, guardrail for a genuinely consequential action: don’t let the agent act at all without a real human confirming first.

from langchain.chat_models import init_chat_model
from langchain.tools import tool
from langchain.agents import create_agent
from langchain.agents.middleware import HumanInTheLoopMiddleware

@tool
def issue_refund(order_id: str, amount: float) -> str:
    """Issue a monetary refund for a given order."""
    return f"Refunded ${amount} for order {order_id}."

agent = create_agent(
    model=init_chat_model("openai:gpt-4o-mini"),
    tools=[issue_refund],
    middleware=[HumanInTheLoopMiddleware(interrupt_on=["issue_refund"])],
)

HumanInTheLoopMiddleware genuinely pauses the agent’s loop before executing issue_refund, waiting for real human confirmation before real money actually moves — the single most direct, reliable guardrail available for an agent’s highest-consequence actions.

Common mistakes worth avoiding

Relying on prompt instructions alone for genuine safety-critical behavior. Telling a model “never send emails outside the company domain” in a system prompt is a real, useful hint, but it’s not a guarantee — recall how easily role-play framing bypassed early, weaker safety training in your earlier AI safety coursework. Recall Example 3 — an actual code-level guardrail, checked outside the model’s own reasoning, is the real, reliable enforcement mechanism.

Adding guardrails only after something has already gone wrong. Guardrails are cheapest and most effective when designed in from the start, exactly like the max_iterations safety net in Module 14 — retrofitting them after a real incident is a genuinely harder, more stressful position to build from.

Treating HumanInTheLoopMiddleware as overkill for every sensitive action. It’s a real trade-off — genuine safety, at the cost of speed and full autonomy. For truly high-stakes, hard-to-reverse actions, like Example 4’s refund, that trade-off is usually the right one to make deliberately.

What you should take away from this module

  • Guardrails are middleware, specifically applied to safety — input validation, output validation, tool restrictions, and human approval.
  • before_agent and before_model let you reject or restrict input and tool calls before they take effect; after_model lets you inspect and adjust output before a user sees it.
  • HumanInTheLoopMiddleware is the most direct, reliable guardrail for genuinely high-stakes, hard-to-reverse actions.
  • Real, code-level enforcement is genuinely more reliable than instructions given only through a system prompt.

Where this goes next

The next module covers Observability — why print(response) genuinely isn’t enough for a real, deployed application, and how to inspect a full execution properly, introducing just enough LangSmith to support real development.

Author
TechByteByByte Editorial Team
Reviewed by
TechByteByByte Admin
Published
Last reviewed