Guardrails middleware for deterministic policy enforcement
Some policies can't live in a prompt—they need to be enforced deterministically regardless of what the model does. Guardrails middleware (such as PIIMiddleware) intercepts data as it flows through the agent loop, applying compliance rules or content policies before tool results reach the model's context.
Guardrails prevent PII leakage, prompt injection, and harmful content
Guardrails help build safe, compliant AI applications by validating and filtering content at key points in agent execution. Common use cases include preventing PII leakage, detecting and blocking prompt injection attacks, blocking inappropriate or harmful content, enforcing business rules and compliance requirements, and validating output quality and accuracy.
Guardrails implemented via middleware at strategic execution points
Guardrails can be implemented using middleware to intercept execution before the agent starts, after it completes, or around model and tool calls.
Deterministic guardrails use rule-based logic, model-based use LLMs
Guardrails can be implemented using two complementary approaches: deterministic guardrails use rule-based logic like regex patterns, keyword matching, or explicit checks for fast, predictable, cost-effective filtering but may miss nuanced violations; model-based guardrails use LLMs or classifiers to evaluate content with semantic understanding to catch subtle issues that rules miss but are slower and more expensive.
PIIMiddleware PII types and detection
Built-in PII types for PIIMiddleware include: email (email addresses), credit_card (credit card numbers with Luhn validation), ip (IP addresses), mac_address (MAC addresses), url (URLs).
PIIMiddleware strategies for handling detected PII
PIIMiddleware supports four strategies for handling detected PII: redact replaces with [REDACTED_{PII_TYPE}], mask partially obscures like ****-****-****-1234, hash replaces with deterministic hash, block raises exception when detected.
PIIMiddleware configuration parameters
PIIMiddleware configuration parameters are: pii_type (type of PII to detect, built-in or custom, required), strategy (how to handle detected PII: "block", "redact", "mask", "hash", default "redact"), detector (custom detector function or regex pattern, default None uses built-in), apply_to_input (check user messages before model call, default True), apply_to_output (check AI messages after model call, default False), apply_to_tool_results (check tool result messages after execution, default False).
PIIMiddleware with apply_to_output redacts streamed output
With apply_to_output=True, PIIMiddleware redacts streamed wire output including text deltas, tool-call args, tool outputs, and state snapshots via a registered stream transformer. Requires langchain>=1.3.2.
PIIMiddleware Python example with email redact and credit card mask
Example showing PIIMiddleware in Python: from langchain.agents import create_agent, from langchain.agents.middleware import PIIMiddleware. Create agent with model="gpt-5.5", tools=[customer_service_tool, email_tool], middleware=[PIIMiddleware("email", strategy="redact", apply_to_input=True), PIIMiddleware("credit_card", strategy="mask", apply_to_input=True), PIIMiddleware("api_key", detector=r"sk-[a-zA-Z0-9]{32}", strategy="block", apply_to_input=True)]. When user provides PII, it is handled according to the strategy.
HumanInTheLoopMiddleware for approval workflows
Human-in-the-loop middleware requires human approval before executing sensitive operations. It is helpful for financial transactions and transfers, deleting or modifying production data, sending communications to external parties, and any operation with significant business impact. Uses interrupt_on dictionary to specify which tools require approval (True) and which auto-approve (False). Requires a checkpointer like InMemorySaver and thread_id in config for persistence across interrupts.
HumanInTheLoopMiddleware Python example with approval workflow
Example showing HumanInTheLoopMiddleware in Python: from langchain.agents import create_agent, from langchain.agents.middleware import HumanInTheLoopMiddleware, from langgraph.checkpoint.memory import InMemorySaver, from langgraph.types import Command. Create agent with middleware=[HumanInTheLoopMiddleware(interrupt_on={"send_email": True, "delete_database": True, "search": False})], checkpointer=InMemorySaver(). Use config={"configurable": {"thread_id": "some_id"}} for persistence. Agent pauses before sensitive tools. Resume with Command(resume={"decisions": [{"type": "approve"}]}) using same thread_id.
HumanInTheLoopMiddleware JavaScript interruptOn options
In JavaScript, HumanInTheLoopMiddleware interruptOn parameter uses objects with properties: allowAccept (boolean), allowEdit (boolean), allowRespond (boolean) for tools requiring approval, or false for auto-approve. Example: send_email: { allowAccept: true, allowEdit: true, allowRespond: true } requires approval with those options.
Before agent guardrails validate requests at session start
Before agent hooks validate requests once at the start of each invocation. This is useful for session-level checks like authentication, rate limiting, or blocking inappropriate requests before any processing begins.
Before agent guardrail using AgentMiddleware class with hook_config
Custom before agent guardrail example using AgentMiddleware class: inherit from AgentMiddleware, implement before_agent method decorated with @hook_config(can_jump_to=["end"]). Method receives AgentState and Runtime parameters and returns dict[str, Any] | None. Can return None to continue or return dict with messages and jump_to="end" to block execution. Example: ContentFilterMiddleware checks first user message content for banned keywords, blocks with error message and jump_to="end" if found.
Before agent guardrail using decorator with @before_agent
Custom before agent guardrail example using decorator syntax: apply @before_agent(can_jump_to=["end"]) decorator to function accepting AgentState and Runtime parameters, returning dict[str, Any] | None. Function can check first message content and return None to continue or return dict with messages and jump_to="end" to block. Example checks for banned keywords and blocks matching requests.
After agent guardrails validate final outputs before returning
After agent hooks validate final outputs once before returning to the user. This is useful for model-based safety checks, quality validation, or final compliance scans on the complete agent response.
After agent guardrail using AgentMiddleware class
Custom after agent guardrail example using AgentMiddleware class: inherit from AgentMiddleware, implement after_agent method decorated with @hook_config(can_jump_to=["end"]). Method receives AgentState and Runtime and returns dict[str, Any] | None. Example: SafetyGuardrailMiddleware uses an LLM to evaluate response safety by prompting with last AI message content to evaluate if SAFE or UNSAFE, then replaces message content if unsafe is detected.
After agent guardrail using decorator with @after_agent
Custom after agent guardrail example using decorator syntax: apply @after_agent(can_jump_to=["end"]) decorator to function accepting AgentState and Runtime, returning dict[str, Any] | None. Example evaluates last AI message using a safety model, replaces content if UNSAFE is detected in model response.
Multiple guardrails stack in middleware array for layered protection
Multiple guardrails can be stacked by adding them to the middleware array in create_agent. They execute in order, allowing layered protection: layer 1 deterministic input filter (before agent), layer 2 PII protection (before and after model), layer 3 human approval for sensitive tools, layer 4 model-based safety check (after agent).
ContentFilterMiddleware example blocks banned keywords
ContentFilterMiddleware example: from langchain.agents import create_agent. class ContentFilterMiddleware(AgentMiddleware) with __init__ storing banned_keywords lowercased. before_agent method gets first message, checks if human type, converts content to lowercase, iterates banned_keywords, returns block dict with jump_to="end" if keyword found, returns None to continue. Usage: ContentFilterMiddleware(banned_keywords=["hack", "exploit", "malware"]) in middleware list.
SafetyGuardrailMiddleware example uses LLM for response evaluation
SafetyGuardrailMiddleware example: class inherits AgentMiddleware, __init__ initializes safety_model with init_chat_model("gpt-5.4-mini"). after_agent method gets last message, checks if AIMessage type, creates safety_prompt asking to evaluate if response is SAFE or UNSAFE, invokes safety_model with prompt, checks if UNSAFE in result.content and replaces last_message.content with error message if so.
JavaScript contentFilterMiddleware example
JavaScript contentFilterMiddleware example: const contentFilterMiddleware = (bannedKeywords: string[]) => { const keywords = bannedKeywords.map(kw => kw.toLowerCase()); return createMiddleware({ name: "ContentFilterMiddleware", beforeAgent: { hook: (state) => { checks first message, returns jumpTo: "end" with AIMessage if banned keyword found }, canJumpTo: ['end'] } }); }. Usage: contentFilterMiddleware(["hack", "exploit", "malware"]).
JavaScript safetyGuardrailMiddleware uses async LLM evaluation
JavaScript safetyGuardrailMiddleware example: const safetyGuardrailMiddleware = () => { const safetyModel = initChatModel("gpt-5.4-mini"); return createMiddleware({ name: "SafetyGuardrailMiddleware", afterAgent: { hook: async (state) => { gets last message, creates safetyPrompt for evaluation, awaits safetyModel.invoke(), checks if result includes "UNSAFE", returns jumpTo: "end" with new AIMessage if unsafe }, canJumpTo: ['end'] } }); };