Agent Middleware
The GuardrailsMiddleware class integrates NeMo Guardrails directly into LangChain agents via the AgentMiddleware protocol. Unlike RunnableRails, which wraps a chain, the middleware hooks into the agent loop itself — running safety checks before and after every model call, including intermediate tool-calling steps.
How It Works
When a LangChain agent runs, it enters a loop:
GuardrailsMiddleware hooks into before_model and after_model to apply NeMo Guardrails at each step. This means:
- Input rails run before every model call, not just the first.
- Output rails run after every model response, including intermediate tool-calling responses.
- If input rails block, the middleware skips the model call (
jump_to: "end"). - If output rails block, the middleware replaces the AIMessage with a policy message (no
tool_calls), terminating the loop naturally.
Prerequisites
Install the required dependencies:
Set up your environment:
Quick Start
The following example creates a tool-calling agent with guardrails applied to every model call.
Configuration
Configure the middleware through constructor parameters and a standard NeMo Guardrails config directory.
Constructor Parameters
The GuardrailsMiddleware constructor accepts the following parameters.
Guardrails Configuration
Create a configuration directory with the standard NeMo Guardrails structure. For example:
config.yml:
prompts.yml:
For the full NeMo Guardrails configuration reference, see the Configuration Guide.
Usage Patterns
The following examples demonstrate common integration patterns with GuardrailsMiddleware.
Basic Agent with Tools
Create an agent with a database search tool and observe how input rails block policy-violating requests.
Expected output:
Exception-Based Error Handling
Set raise_on_violation=True to raise GuardrailViolation exceptions instead of returning blocked messages:
Custom Blocked Messages
Override the default policy messages returned when rails block input or output.
Input-Only or Output-Only Middleware
Use the convenience subclasses when you only need one type of rail:
Or disable specific rails on the main class:
Multi-Turn with Checkpointing
Use LangGraph’s InMemorySaver to maintain conversation state across multiple invocations while guardrails run on every turn.
Known Limitations
Be aware of the following constraints when using GuardrailsMiddleware with tool-calling agents.
Security Considerations for Tool-Calling Agents
Rails evaluate the content field of messages only. This has two implications for tool-calling agents:
Tool call arguments are not inspected. When the LLM generates a tool call, the arguments (e.g., send_email(body="SSN: 123-45-6789")) are in the tool_calls field, not content. Input and output rails do not see or validate these arguments.
Tool results bypass input rails. When a tool returns its result as a ToolMessage, that message is not subject to input rail validation. Malicious or unexpected tool outputs can influence subsequent model responses without being checked.
To mitigate these risks, enable output rails to validate the final LLM response before it reaches the user. This ensures that even if unsafe content enters through tool calls or tool results, the model’s response is still checked. However, note that intermediate tool-calling responses often have empty content (the instructions are in the tool_calls field), and some LLM-based output rails (such as self_check_output) may flag empty content as a false positive. If you encounter this, you can disable output rails as a workaround — but be aware this also removes the safety net for tool result content:
For more details, see Security Considerations in the tools integration guide.
MODIFIED Status Replaces Message Content
When a rail modifies content (returns RailStatus.MODIFIED), the middleware replaces the relevant message with the modified content. For input rails, the last user message is replaced. For output rails, the last AI message is replaced. This enables use cases like PII redaction and content sanitization.
API Reference
Summary of the middleware classes and exception type.
GuardrailsMiddleware
The main middleware class. Implements both async (abefore_model, aafter_model) and sync (before_model, after_model) hooks.
InputRailsMiddleware
Convenience subclass that only runs input rails. The aafter_model hook is a no-op.
OutputRailsMiddleware
Convenience subclass that only runs output rails. The abefore_model hook is a no-op.
GuardrailViolation
Exception raised when raise_on_violation=True and a rail blocks.
Comparison with RunnableRails
Choose between the two integration approaches based on your architecture.
Use GuardrailsMiddleware when building tool-calling agents with create_agent. Use RunnableRails when composing custom LangGraph graphs or wrapping individual chains.
Related Resources
- LangChain Integration - Overview of all LangChain integration approaches
- RunnableRails - Wrap chains with guardrails using the LCEL
|operator - Configuring YAML File - Full NeMo Guardrails configuration reference
- Checking Messages Against Rails - The
check_asyncAPI used internally by the middleware