LangChainLangChain 1.4 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
43 small wins to finish your pathNext lesson

Guardrails

Guardrails check what goes into and out of an agent. PIIMiddleware finds card numbers and similar data, and a custom hook can stop a request early.

Customers paste card numbers into support chats. The number should not reach the model, the logs or the saved conversation.

Exampleagent.py
from langchain.agents import create_agent
from langchain.agents.middleware import PIIMiddleware
from shop_model import ShopModel

agent = create_agent(ShopModel(), tools=[], middleware=[PIIMiddleware("credit_card")])
Example
result = agent.invoke({"messages": [{"role": "user", "content": "My card 4111 1111 1111 1111 was charged twice for A17"}]})

for message in result["messages"]:
    print(f"{message.type:<5} {message.text}")

The card number was replaced before the model was called, in the conversation itself, so the saved state never holds it. By default PIIMiddleware checks the input only; apply_to_output and apply_to_tool_results turn on checks of the model's replies and of tool results.

Other strategies

Example
for strategy in ["mask", "hash"]:
    guard = PIIMiddleware("credit_card", strategy=strategy)
    agent = create_agent(ShopModel(), tools=[], middleware=[guard])
    print(agent.invoke({"messages": [{"role": "user", "content": "My card 4111 1111 1111 1111 was charged twice for A17"}]})["messages"][0].text)

redact, the default, replaces the whole number. mask keeps the last four digits, which support staff often need. hash replaces it with a short hash, so two messages with the same card can be matched without storing it. block raises an error instead.

A check of your own

Exampleguard.py
from langchain.agents.middleware import before_agent
from langchain.messages import AIMessage


@before_agent(can_jump_to=["end"])
def no_passwords(state, runtime):
    if "password" in state["messages"][-1].text.lower():
        answer = AIMessage("I cannot help with passwords. Please use the reset link.")
        return {"messages": [answer], "jump_to": "end"}

A before_agent hook runs once, before anything else. Returning jump_to ends the run, and the answer the hook adds becomes the reply. can_jump_to declares which jumps the hook may make; without it, the jump is ignored and the model is called anyway.

Exampleagent.py
from langchain.agents import create_agent
from guard import no_passwords
from shop_model import ShopModel

agent = create_agent(ShopModel(), tools=[], middleware=[no_passwords])
Example
result = agent.invoke({"messages": [{"role": "user", "content": "What is my password?"}]})

for message in result["messages"]:
    print(f"{message.type:<5} {message.text}")

Two messages and no model call. A check like this costs nothing to run and cannot be talked out of its rule, which is its advantage over asking the model to refuse.

Try it yourself
  • Use strategy="block" and read the error.
  • Add PIIMiddleware("email") to the list and include an email address in the message.
  • Remove can_jump_to from no_passwords and count the AI messages.

Every expert started right here.