LangChain integration¶
This page shows the complete integration of PIIGhost into a LangGraph agent, based on the example available in examples/graph/.
Installation¶
To use the LangChain middleware, install the additional dependencies:
Optional dependency
PIIAnonymizationMiddleware imports langchain when instantiated. If langchain is not installed, an explicit ImportError is raised: "You must install piighost[langchain] for use middleware".
Integration structure¶
flowchart TB
classDef model fill:#A5D6A7,stroke:#2E7D32,color:#000
classDef comp fill:#90CAF9,stroke:#1565C0,color:#000
classDef mw fill:#BBDEFB,stroke:#1565C0,color:#000
classDef agent fill:#FFF9C4,stroke:#F9A825,color:#000
MODEL["`**GLiNER2 model**
_fastino/gliner2-multi-v1_`"]:::model
DET["`**Gliner2Detector**
_wraps the NER model_`"]:::comp
PIPE["`**ThreadAnonymizationPipeline**
_extends AnonymizationPipeline_
_holds ConversationMemory_`"]:::comp
MW["`**PIIAnonymizationMiddleware**
_LangChain hooks_`"]:::mw
AGENT["`**create_agent(middleware=[...])**
_LangGraph entry point_`"]:::agent
MODEL -->|wrapped by| DET
DET -->|injected into| PIPE
PIPE -->|passed to| MW
MW -->|registered with| AGENT
Full example¶
| agent.py | |
|---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | |
How the middleware works¶
PIIAnonymizationMiddleware intercepts each agent turn at three points:
abefore_model before the LLM¶
flowchart LR
classDef user fill:#A5D6A7,stroke:#2E7D32,color:#000
classDef mw fill:#BBDEFB,stroke:#1565C0,color:#000
classDef llm fill:#FFF9C4,stroke:#F9A825,color:#000
U["`**User**
_'Send an email to Patrick in Paris'_`"]:::user
M["`**Middleware**
_NER detection via_
_pipeline.anonymize()_`"]:::mw
L["`**LLM sees**
_'Send an email to <<PERSON_1>>_
_in <<LOCATION_1>>'_`"]:::llm
U --> M --> L
awrap_tool_call around tools¶
flowchart TB
classDef tool fill:#A5D6A7,stroke:#2E7D32,color:#000
classDef mw fill:#BBDEFB,stroke:#1565C0,color:#000
classDef llm fill:#FFF9C4,stroke:#F9A825,color:#000
L1["`**LLM calls**
_send_email(to='<<PERSON_1>>', ...)_`"]:::llm
M1["`**Middleware**
_deanonymize args_`"]:::mw
T1["`**Tool receives**
_to='Patrick' (real value)_`"]:::tool
T2["`**Tool returns**
_'Email successfully sent to Patrick.'_`"]:::tool
M2["`**Middleware**
_reanonymize response_`"]:::mw
L2["`**LLM sees**
_'Email successfully sent to <<PERSON_1>>.'_`"]:::llm
L1 --> M1 --> T1 --> T2 --> M2 --> L2
aafter_model after the LLM¶
flowchart LR
classDef user fill:#A5D6A7,stroke:#2E7D32,color:#000
classDef mw fill:#BBDEFB,stroke:#1565C0,color:#000
classDef llm fill:#FFF9C4,stroke:#F9A825,color:#000
L["`**LLM replies**
_'Done! Email sent to <<PERSON_1>>.'_`"]:::llm
M["`**Middleware**
_deanonymize all messages_`"]:::mw
U["`**User sees**
_'Done! Email sent to Patrick.'_`"]:::user
L --> M --> U
Using the agent¶
main.py
import asyncio
async def main():
response = await graph.ainvoke({
"messages": [{"role": "user", "content": "Send an email to Patrick in Paris"}]
})
print(response["messages"][-1].content)
# Done! Email sent to Patrick.
asyncio.run(main())