Guardrails¶
Guardrails inspect inputs and outputs for policy violations — PII, harmful content, prompt injection, intellectual property, and custom rules — and respond by logging, blocking, or modifying the data.
The UiPath LangChain SDK exposes two complementary patterns:
- Middleware — a LangChain-native approach. Guardrail classes are passed as a list to
create_agent(..., middleware=[...]). Each class registers hooks (before_agent,after_agent,before_model,after_model,wrap_tool_call) automatically for the configured scope. - Decorator — the same
@guardrailAPI from the core SDK, extended to understand LangChain/LangGraph objects. Scope is inferred automatically from the decorated target type.
For the full list of validators, actions, execution stages, and the low-level API, see the core guardrails documentation.
Middleware pattern¶
Middleware is the preferred approach when all guardrail configuration should live in one place, next to the create_agent() call.
How it works¶
Each built-in middleware class is iterable — unpacking it with * yields one or more AgentMiddleware hook objects that LangChain registers internally. Pass them all inside the middleware=[] list:
from langchain.agents import create_agent
agent = create_agent(
model=llm,
tools=[my_tool],
system_prompt=SYSTEM_PROMPT,
middleware=[
*UiPathPIIDetectionMiddleware(...),
*UiPathHarmfulContentMiddleware(...),
# ... more middleware
],
)
Imports¶
from uipath_langchain.guardrails import (
BlockAction,
LogAction,
LoggingSeverityLevel,
UiPathDeterministicGuardrailMiddleware,
UiPathHarmfulContentMiddleware,
UiPathIntellectualPropertyMiddleware,
UiPathPIIDetectionMiddleware,
UiPathUserPromptAttacksMiddleware,
PIIDetectionEntity,
HarmfulContentEntity,
GuardrailExecutionStage,
)
from uipath_langchain.guardrails.enums import (
HarmfulContentEntityType,
IntellectualPropertyEntityType,
PIIDetectionEntityType,
)
from uipath.core.guardrails import GuardrailScope
Built-in middleware classes¶
| Class | Supported scopes | Stage support | Extra parameters |
|---|---|---|---|
UiPathPIIDetectionMiddleware |
AGENT, LLM, TOOL | PRE / POST / PRE_AND_POST | entities, tools, stage |
UiPathHarmfulContentMiddleware |
AGENT, LLM, TOOL | PRE / POST / PRE_AND_POST | entities, tools, stage |
UiPathUserPromptAttacksMiddleware |
LLM only | PRE only | — |
UiPathIntellectualPropertyMiddleware |
AGENT, LLM only | POST only | entities |
UiPathDeterministicGuardrailMiddleware |
TOOL only | PRE / POST / PRE_AND_POST | tools, rules, stage |
TOOL scope for UiPathPIIDetectionMiddleware and UiPathHarmfulContentMiddleware requires passing tools=[...] to restrict wrap_tool_call hooks to specific tools.
All classes share these common parameters:
name(str) — display name for this guardrail instance.action— what to do on violation:LogAction(...)orBlockAction(...).scopes(list[GuardrailScope]) — restrict which hooks are registered. Defaults shown in the table above. UseGuardrailScope.AGENT,GuardrailScope.LLM,GuardrailScope.TOOL.enabled_for_evals(bool, defaultTrue) — setFalseto skip this guardrail when the agent runs in evaluation mode.
Additional parameters per class:
UiPathPIIDetectionMiddleware/UiPathHarmfulContentMiddleware:entities(list of entity configs),tools(restrict TOOL-scope hooks to specific tools),stage.UiPathUserPromptAttacksMiddleware: no extra parameters.UiPathIntellectualPropertyMiddleware:entities(list ofIntellectualPropertyEntityTypevalues).UiPathDeterministicGuardrailMiddleware:tools(required — list of tools to guard),rules(list of lambda functions),stage.
Full example¶
from langchain.agents import create_agent
from langchain_core.tools import tool
from uipath_langchain.chat import UiPathChat
from uipath_langchain.guardrails import (
BlockAction,
LogAction,
LoggingSeverityLevel,
UiPathDeterministicGuardrailMiddleware,
UiPathHarmfulContentMiddleware,
UiPathIntellectualPropertyMiddleware,
UiPathPIIDetectionMiddleware,
UiPathUserPromptAttacksMiddleware,
PIIDetectionEntity,
HarmfulContentEntity,
GuardrailExecutionStage,
)
from uipath_langchain.guardrails.enums import (
HarmfulContentEntityType,
IntellectualPropertyEntityType,
PIIDetectionEntityType,
)
from uipath.core.guardrails import GuardrailScope
@tool
def analyze_text(text: str) -> str:
"""Count words in the provided text."""
return f"Word count: {len(text.split())}"
llm = UiPathChat(model="gpt-4o-2024-08-06")
agent = create_agent(
model=llm,
tools=[analyze_text],
system_prompt="You are a helpful assistant.",
middleware=[
# PII detection on agent input/output and LLM messages
*UiPathPIIDetectionMiddleware(
name="PII detector",
scopes=[GuardrailScope.AGENT, GuardrailScope.LLM],
action=LogAction(severity_level=LoggingSeverityLevel.WARNING),
entities=[
PIIDetectionEntity(PIIDetectionEntityType.EMAIL, threshold=0.5),
PIIDetectionEntity(PIIDetectionEntityType.CREDIT_CARD_NUMBER, threshold=0.5),
],
),
# PII detection restricted to TOOL scope for specific tools
*UiPathPIIDetectionMiddleware(
name="Tool PII detector",
scopes=[GuardrailScope.TOOL],
action=LogAction(severity_level=LoggingSeverityLevel.WARNING),
entities=[
PIIDetectionEntity(PIIDetectionEntityType.PHONE_NUMBER, threshold=0.5),
],
tools=[analyze_text],
enabled_for_evals=False,
),
# Block adversarial user prompts at the LLM level
*UiPathUserPromptAttacksMiddleware(
name="User prompt attacks",
action=BlockAction(),
enabled_for_evals=False,
),
# Block harmful content in agent + LLM messages
*UiPathHarmfulContentMiddleware(
name="Harmful content",
scopes=[GuardrailScope.AGENT, GuardrailScope.LLM],
action=BlockAction(),
entities=[
HarmfulContentEntity(HarmfulContentEntityType.VIOLENCE, threshold=2),
],
),
# Log IP violations in LLM output (POST only)
*UiPathIntellectualPropertyMiddleware(
name="IP detection",
scopes=[GuardrailScope.LLM],
action=LogAction(severity_level=LoggingSeverityLevel.WARNING),
entities=[IntellectualPropertyEntityType.TEXT],
),
# Deterministic: block tool input longer than 1000 chars
*UiPathDeterministicGuardrailMiddleware(
tools=[analyze_text],
rules=[lambda data: len(data.get("text", "")) > 1000],
action=BlockAction(detail="Input too long"),
stage=GuardrailExecutionStage.PRE,
name="Length limiter",
),
],
)
Deterministic middleware¶
UiPathDeterministicGuardrailMiddleware applies in-process rule functions without any API call.
rules— list of callables. ForPREstage:(input_dict: dict) -> bool. ForPOSTstage:(input_dict: dict, output_dict: dict) -> bool. ReturnTrueto signal a violation.rules=[](empty list) — always triggers the action, useful for unconditional transforms.stage—GuardrailExecutionStage.PRE,POST, orPRE_AND_POST.
# Always replace a word in tool output (unconditional transform, empty rules)
*UiPathDeterministicGuardrailMiddleware(
tools=[analyze_text],
rules=[],
action=CustomFilterAction(word_to_filter="count", replacement="total"),
stage=GuardrailExecutionStage.POST,
name="Output transformer",
),
Custom middleware hooks¶
For logic that doesn't fit a built-in class, implement raw middleware hooks using the langchain.agents.middleware decorators and pass them directly to the middleware list.
Available hook decorators: before_agent, after_agent, before_model, after_model, wrap_tool_call.
from langchain.agents.middleware import AgentState, before_agent, after_agent
from langchain_core.messages import AIMessage, HumanMessage
from langgraph.runtime import Runtime
@before_agent
async def log_agent_input(state: AgentState, runtime: Runtime) -> None:
for msg in reversed(state.get("messages", [])):
if isinstance(msg, HumanMessage):
print(f"[INPUT] {msg.content}")
break
@after_agent
async def log_agent_output(state: AgentState, runtime: Runtime) -> None:
for msg in reversed(state.get("messages", [])):
if isinstance(msg, AIMessage):
print(f"[OUTPUT] {msg.content}")
break
LoggingMiddleware = [log_agent_input, log_agent_output]
agent = create_agent(
model=llm,
tools=[analyze_text],
middleware=[
*LoggingMiddleware,
*UiPathPIIDetectionMiddleware(...),
],
)
Custom actions¶
Both the built-in middleware and UiPathDeterministicGuardrailMiddleware accept any GuardrailAction subclass as the action parameter. This lets you implement content sanitisation, redaction, or any other custom response to a violation:
import re
from dataclasses import dataclass
from typing import Any
from uipath.core.guardrails import GuardrailValidationResult, GuardrailValidationResultType
from uipath_langchain.guardrails import GuardrailAction
@dataclass
class RedactAction(GuardrailAction):
pattern: str
replacement: str = "[REDACTED]"
def handle_validation_result(
self,
result: GuardrailValidationResult,
data: str | dict[str, Any],
guardrail_name: str,
) -> str | dict[str, Any] | None:
if result.result != GuardrailValidationResultType.VALIDATION_FAILED:
return None
if isinstance(data, str):
return re.sub(self.pattern, self.replacement, data, flags=re.IGNORECASE)
return None
Decorator pattern¶
Importing uipath_langchain.guardrails auto-registers a LangChain adapter that extends @guardrail to wrap LangChain/LangGraph objects in addition to plain Python callables. All validators, actions, execution stages, stacking, and GuardrailExclude work exactly as described in the core guardrails documentation — use uipath_langchain.guardrails as the import path, which re-exports everything from the core SDK.
LangChain target types¶
On LangChain objects, scope is inferred automatically from the target — no stage inference, but no explicit scopes= parameter is needed either:
| Decorated target | Inferred scope | Notes |
|---|---|---|
@tool function |
TOOL | @guardrail must be placed above @tool |
BaseChatModel factory |
LLM | Decorate the factory function, not the model instance |
create_agent() factory |
AGENT | Decorate the factory function, not the returned agent |
| LangGraph node (async fn) | AGENT | — |
LLM factory¶
from uipath_langchain.chat import UiPathChat
from uipath_langchain.guardrails import guardrail, PIIValidator, BlockAction, GuardrailExecutionStage, PIIDetectionEntity
from uipath_langchain.guardrails.enums import PIIDetectionEntityType
@guardrail(
validator=PIIValidator(
entities=[PIIDetectionEntity(PIIDetectionEntityType.EMAIL, threshold=0.5)],
),
action=BlockAction(),
name="LLM PII check",
stage=GuardrailExecutionStage.PRE,
)
def create_llm():
return UiPathChat(model="gpt-4o-2024-08-06")
llm = create_llm()
Tool¶
@guardrail must be placed above @tool:
from langchain_core.tools import tool
from uipath_langchain.guardrails import guardrail, CustomValidator, BlockAction, GuardrailExecutionStage
@guardrail(
validator=CustomValidator(lambda args: len(args.get("text", "")) > 1000),
action=BlockAction(detail="Text exceeds 1000 characters"),
stage=GuardrailExecutionStage.PRE,
name="Length limiter",
)
@tool
def analyze_text(text: str) -> str:
"""Count words in the provided text."""
return f"Word count: {len(text.split())}"
Agent factory¶
from langchain.agents import create_agent
from uipath_langchain.guardrails import guardrail, HarmfulContentValidator, BlockAction, GuardrailExecutionStage, HarmfulContentEntity
from uipath_langchain.guardrails.enums import HarmfulContentEntityType
@guardrail(
validator=HarmfulContentValidator(
entities=[HarmfulContentEntity(HarmfulContentEntityType.VIOLENCE, threshold=2)],
),
action=BlockAction(),
name="Block harmful content",
stage=GuardrailExecutionStage.PRE,
)
def create_my_agent():
return create_agent(model=llm, tools=[analyze_text], system_prompt="...")
agent = create_my_agent()
LangGraph node¶
from uipath_langchain.guardrails import guardrail, PIIValidator, BlockAction, GuardrailExecutionStage, PIIDetectionEntity
from uipath_langchain.guardrails.enums import PIIDetectionEntityType
@guardrail(
validator=PIIValidator(
entities=[PIIDetectionEntity(PIIDetectionEntityType.PERSON, threshold=0.5)],
),
action=BlockAction(title="Person name in input"),
name="Node PII check",
stage=GuardrailExecutionStage.PRE,
)
async def my_node(state: Input) -> Output:
...
Choosing between middleware and decorator¶
| Middleware | Decorator | |
|---|---|---|
| Configuration location | Centralised in create_agent() |
Per-target, co-located with the object |
| Scope specification | Explicit scopes= parameter |
Auto-inferred from the decorated type |
Works outside create_agent |
No | Yes — any LangGraph graph or plain function |
| Reusable validator objects | No | Yes — declare once, use in multiple decorators |
| Parameter exclusion | No | GuardrailExclude() annotation |
| Custom deterministic rules | UiPathDeterministicGuardrailMiddleware(rules=[...]) |
CustomValidator(lambda ...) |
Use middleware when you want all guardrail policy in one place alongside a single create_agent() call. Use decorators when building custom LangGraph graphs, reusing validators across multiple agents, or guarding code outside the agent context.
Sample agents¶
samples/joke-agent— middleware patternsamples/joke-agent-decorator— decorator pattern
Reference¶
GuardrailScope¶
Imported from uipath.core.guardrails.
| Value | Description |
|---|---|
AGENT |
Hooks run at agent input/output boundary (before_agent / after_agent) |
LLM |
Hooks run around every LLM call (before_model / after_model) |
TOOL |
Hooks run around every tool call (wrap_tool_call) |
GuardrailExecutionStage¶
Imported from uipath_langchain.guardrails.
| Value | When it fires |
|---|---|
PRE |
Before the call (inspect / block inputs) |
POST |
After the call (inspect / transform outputs) |
PRE_AND_POST |
Both — used only by UiPathDeterministicGuardrailMiddleware |
LoggingSeverityLevel¶
Used in LogAction(severity_level=...). Imported from uipath_langchain.guardrails.
| Value |
|---|
DEBUG |
INFO |
WARNING |
ERROR |
PIIDetectionEntityType¶
Imported from uipath_langchain.guardrails.enums. Wrap each value in PIIDetectionEntity(entity_type, threshold=0.5) — threshold is a float from 0.0 to 1.0.
PII detection entity types supported by UiPath guardrails.
| Value |
|---|
PERSON |
ADDRESS |
DATE |
PHONE_NUMBER |
EUGPS_COORDINATES |
EMAIL |
CREDIT_CARD_NUMBER |
INTERNATIONAL_BANKING_ACCOUNT_NUMBER |
SWIFT_CODE |
ABA_ROUTING_NUMBER |
US_DRIVERS_LICENSE_NUMBER |
UK_DRIVERS_LICENSE_NUMBER |
US_INDIVIDUAL_TAXPAYER_IDENTIFICATION |
UK_UNIQUE_TAXPAYER_NUMBER |
US_BANK_ACCOUNT_NUMBER |
US_SOCIAL_SECURITY_NUMBER |
USUK_PASSPORT_NUMBER |
URL |
IP_ADDRESS |
HarmfulContentEntityType¶
Imported from uipath_langchain.guardrails.enums. Wrap each value in HarmfulContentEntity(entity_type, threshold=2) — threshold must be one of 0, 2, 4, or 6 (higher = less sensitive).
Harmful content entity types supported by UiPath guardrails.
| Value |
|---|
HATE |
SELF_HARM |
SEXUAL |
VIOLENCE |
IntellectualPropertyEntityType¶
Imported from uipath_langchain.guardrails.enums. Pass values directly in entities=[...] — no wrapper model class.
Intellectual property entity types supported by UiPath guardrails.
| Value |
|---|
TEXT |
CODE |