User prompts can include email addresses, payment data, IP addresses, and other values that should not reach a model unchanged. LangChain PIIMiddleware adds a deterministic guardrail to an agent so known sensitive patterns are redacted, masked, hashed, or blocked before the model call.
The middleware is passed to create_agent through the middleware list. A no-key echo chat model can prove what text the model receives, so the privacy check does not need OpenAI, Anthropic, or another provider.
Configure each PII type separately because email, credit_card, ip, mac_address, and url use different detectors and handling choices. Keep production traces and logs sanitized as well, because input redaction protects the model call but does not rewrite older application records.
Steps to redact PII with LangChain middleware:
- Open an activated Python project environment.
- Install or upgrade LangChain.
$ python -m pip install --upgrade langchain
LangChain requires Python 3.10 or newer. The core package is enough for the no-key middleware smoke test; install provider packages separately when using a hosted chat model.
Related: How to install LangChain with pip - Create redact_pii_agent.py with an echo model and email redaction middleware.
- redact_pii_agent.py
from typing import Any from langchain.agents import create_agent from langchain.agents.middleware import PIIMiddleware from langchain_core.language_models.chat_models import SimpleChatModel from langchain_core.messages import BaseMessage class EchoLastUserMessage(SimpleChatModel): @property def _llm_type(self) -> str: return "echo-last-user-message" def bind_tools(self, tools: list[Any], *, tool_choice: Any = None, **kwargs: Any) -> "EchoLastUserMessage": if tools: raise NotImplementedError("This validation model does not exercise tool calls.") return self def _call( self, messages: list[BaseMessage], stop: list[str] | None = None, run_manager: Any = None, **kwargs: Any, ) -> str: user_messages = [message for message in messages if message.type == "human"] content = user_messages[-1].content if user_messages else "" if not isinstance(content, str): content = str(content) return f"model saw: {content}" agent = create_agent( model=EchoLastUserMessage(), tools=[], middleware=[ PIIMiddleware("email", strategy="redact", apply_to_input=True), ], ) prompt = "Send the receipt to priya@example.com." result = agent.invoke({"messages": [{"role": "user", "content": prompt}]}) reply = result["messages"][-1].content print("Input prompt:") print(prompt) print() print("Agent reply:") print(reply) print() if "priya@example.com" in reply: raise SystemExit("PII leaked to the model reply") if "[REDACTED_EMAIL]" not in reply: raise SystemExit("Redaction marker was not present") print("PII leaked to model: no")
apply_to_input=True is shown explicitly even though input checking is the default. Add another PIIMiddleware entry for each built-in or custom PII type that the agent must handle.
- Run the middleware smoke test.
$ python redact_pii_agent.py Input prompt: Send the receipt to priya@example.com. Agent reply: model saw: Send the receipt to [REDACTED_EMAIL]. PII leaked to model: no
The echo model prints the message content it received through create_agent. The output should contain [REDACTED_EMAIL] and should not contain the original address.
- Move the middleware list into the real agent configuration.
from langchain.agents import create_agent from langchain.agents.middleware import PIIMiddleware agent = create_agent( model=model, tools=tools, middleware=[ PIIMiddleware("email", strategy="redact", apply_to_input=True), PIIMiddleware("credit_card", strategy="mask", apply_to_input=True), ], )
Use the chat model and tools that already pass the project agent smoke test. redact replaces matches with markers such as [REDACTED_EMAIL], mask keeps only part of a value, hash replaces it with a deterministic hash, and block raises an error when the detector matches. In LangChain 1.3.2 or newer, apply_to_output=True also redacts streamed wire output.
- Remove the temporary validation script.
$ rm redact_pii_agent.py
Mohd Shakir Zakaria is a cloud architect with deep roots in software development and open-source advocacy. Certified in AWS, Red Hat, VMware, ITIL, and Linux, he specializes in designing and managing robust cloud and on-premises infrastructures.