PIIShade Everything the model can do. Nothing it doesn't need to know.
PIIShade replaces the confidential data in your prompts with placeholders before it reaches the model, then restores the real values in the response. Pluggable detectors (regex, NER or an LLM), and OpenAI- and Anthropic-compatible proxies through the piighost-api server.
Works with
- LangChain
- Pydantic AI
- LlamaIndex
from langchain.agents import create_agentfrom piighost.integrations.langchain import PIIAnonymizationMiddleware middleware = PIIAnonymizationMiddleware(pipeline=pipeline) agent = create_agent( model="openai:gpt-5.6", tools=[send_email], middleware=[middleware],)# the model only sees placeholders; send_email receives the real valuesThe problem
You should not have to choose between good models and data privacy
Hosted model
- Quality
- yes, The best
- Data exposed
- no, All of it
- Constraint
- yes, None
- Gains
- yes, Yes
Local model
- Quality
- no, Degraded
- Data exposed
- yes, None
- Constraint
- no, GPUs and operations
- Gains
- partly, Partial
Ban it
- Quality
- no, None
- Data exposed
- yes, None
- Constraint
- yes, Nothing
- Gains
- no, None, and bypassed
PIIShade
- Quality
- yes, The best
- Data exposed
- yes, None
- Constraint
- partly, CPU infrastructure
- Gains
- yes, Yes
One limit worth knowing. PIIShade does pseudonymization in the GDPR sense, not anonymization: the mapping between values and placeholders is kept on your side for the duration of the conversation, and has to be protected like personal data.
Why PIIShade
More than a PII detector
Regex and NER detectors know how to find confidential data. De-identifying an exchange with an LLM also means replacing that data, tracking it from one message to the next, and restoring it. PIIShade orchestrates all of it for you.
Composable detectors
Regex, NER or LLM, in a single pipeline. You keep the one you trust.
Placeholders that come back on their own
The model reads <<PERSON:1>>, your user and your tools read the real name.
The same placeholder all along
A value keeps its placeholder from one message to the next, so the model never loses the thread.
One file, your infrastructure
The whole pipeline fits in a TOML file, validated from the command line, run on your side.
Supported detectors
- Regex
- GLiNER2
- spaCy
- Transformers
- Presidio
How it works
A layer between your agent and the model
User → piishade
Send case #ACME-9123 to marie.lambert@acme.com
piishade → Model
Send case <<ID:1>> to <<EMAIL:1>>
Model → piishade
send_email(to=<<EMAIL:1>>)
piishade → Tools
send_email(to=marie.lambert@acme.com)
Tools → piishade
Email sent to marie.lambert@acme.com
piishade → Model
Email sent to <<EMAIL:1>>
Model → piishade
Case <<ID:1>> was sent to <<EMAIL:1>>.
piishade → User
Case #ACME-9123 was sent to marie.lambert@acme.com.
The model's column holds nothing but placeholders. Even what the tools return is de-identified before the model reads it.
The ecosystem
One privacy layer, many projects
Start with the library. Move to the server when several services share one pipeline, and see it all at work in the chat demo and the CV proofreader.
piighost
The core library. Build PII de-identification pipelines for AI agents.
Learn morepiighost-api
A REST server that hosts one PIIShade pipeline behind HTTP.
Learn morepiighost-chat
A demo chatbot that de-identifies messages before the LLM sees them.
Learn morepiighost-proofreader
An LLM CV proofreader that de-identifies documents before any LLM call.
Learn more<<PROJECT_NAME:2b1f4a>>
More to come.
<<PROJECT_NAME:7c43e9>>
More to come.
<<PROJECT_NAME:9af0d2>>
More to come.
<<PROJECT_NAME:1e8c75>>
More to come.
<<PROJECT_NAME:f30b86>>
More to come.
Quick start
Drop it into your agent framework
Add PIIShade to the framework you already use. Your agent code stays the same.
uv add 'piighost[langchain,gliner2]' from langchain.agents import create_agent from piighost.components.detector.ner import Gliner2Detectorfrom piighost.pipeline import ThreadAnonymizationPipelinefrom piighost.integrations.langchain import PIIAnonymizationMiddleware # Any detector works: regex, NER, or an LLM.detector = Gliner2Detector("fastino/gliner2-multi-v1", labels=["PERSON", "LOCATION"])pipeline = ThreadAnonymizationPipeline(detector)middleware = PIIAnonymizationMiddleware(pipeline=pipeline) agent = create_agent( model="openai:gpt-5.6-terra", tools=[lookup_city], middleware=[middleware],) # The model only sees <<PERSON:1>>; lookup_city still receives "Patrick".FAQ
Frequently asked questions
A question that is not here? The community and the documentation have answers.
Why de-identify instead of self-hosting the model?
<<PERSON:1>> leave your infrastructure. It is one layer of defense, not a silver bullet. The Philosophy page walks through the tradeoff.What do I need in production?
load_thread_pipeline for a conversation (load_pipeline for a one-off text). pip install 'piighost[config]' is enough for regex; add your detector's extra (gliner2, spacy, transformers or llm), and redis to share memory across processes. Several services? Deploy piighost-api.Can I use PIIShade with Claude Code?
pip install 'piighost[client]'.Which frameworks are supported?
base_url change.Is PIIShade GDPR compliant?
What is the difference between anonymization and de-identification?
What is actually sent to the model?
<<PERSON:1>>. The mapping stays on your side for the length of the conversation, and PIIShade restores the values locally.Everything the model can do.
Nothing it doesn't need to know.
Ship your AI features without handing over your customers' confidential data. Install PIIShade, wire your detector, and keep PII out of the model.
LlamaIndex
GLiNER2
spaCy
Presidio