First pipeline¶
You will build a pipeline that detects arbitrary names and locations, not only values known in advance, and watch it run at each step. Two detectors fit this, an NER model (GLiNER2) or a catalog of regex patterns. You start from a detector, add the three remaining components one at a time, then run the pipeline on a sentence.
Prerequisites
piighost installed, see Installation. The regex path uses only the core, no extra. The GLiNER2 path needs the gliner2 extra and downloads a model on first load.
1. Pick a detector¶
The detector reads the text and returns detections, one per PII found. The rest of the pipeline is the same whatever the detector, so pick the one that matches your text.
A RegexDetector recognizes patterns, that is strings of characters following a fixed structure. For arbitrary names and locations, you pass it a dictionary mapping a label to a pattern. Here two patterns, one for first names, one for the city.
from piighost.components.detector import RegexDetector
patterns = {
"PERSON": r"\b(?:Patrick|Marie)\b",
"LOCATION": r"\bParis\b",
}
detector = RegexDetector(patterns)
piighost also ships ready-made catalogs for formats that are not language-specific, such as email and URL.
An NER is an AI model that, over a text, classifies words according to a classification decided in advance (name, first name, location, organization). Unlike the regex, it does not need to know the values in advance, it detects a first name it has never seen.
from piighost.components.detector.ner import Gliner2Detector
detector = Gliner2Detector(
model="fastino/gliner2-multi-v1",
labels=["PERSON", "LOCATION"],
threshold=0.5,
)
The first argument is a model name loaded by GLiNER2, or an already loaded instance. labels sets the queried categories. threshold is the minimum confidence above which a detection is kept.
2. Group detections into entities¶
One first name can appear several times. The linker groups the detections of the same value and the same label into a single entity, so every occurrence later receives the same token.
3. Assign a token to each entity¶
The anonymizer replaces each entity with a placeholder, that is the token that takes its place in the text. The token depends on the chosen factory. LabelCounterPlaceholderFactory numbers per label, so <<PERSON:1>>, <<PERSON:2>>, <<LOCATION:1>>.
from piighost.components.anonymizer import Anonymizer
from piighost.components.placeholder import LabelCounterPlaceholderFactory
factory = LabelCounterPlaceholderFactory()
anonymizer = Anonymizer(factory)
4. Assemble and run¶
AnonymizationPipeline chains the three components in order, detect, group, replace. Its anonymize call is asynchronous and returns a result whose text carries the de-identified sentence.
import asyncio
from piighost.pipeline import AnonymizationPipeline
pipeline = AnonymizationPipeline(detector, linker, anonymizer)
async def main() -> None:
text = "Patrick habite à Paris. Patrick aime Paris. Marie aussi."
result = await pipeline.anonymize(text)
print(result.text)
asyncio.run(main())
The output should be:
Each occurrence of Patrick receives the same <<PERSON:1>>, Paris keeps <<LOCATION:1>> at both appearances, and Marie receives the next number <<PERSON:2>>. The linker from step 2 is what makes this consistency possible.
How it works¶
AnonymizationPipeline runs three mandatory stages. The detector finds the PII, the linker groups the occurrences of the same value into one entity, the anonymizer replaces each entity with the token from its factory. Optional stages exist (missed-occurrence expansion, entity merging), disabled by default, while overlap resolution runs by default. Only the detector is strictly required to construct, which is enough for a first pipeline.
What's next¶
- To describe this pipeline in a file rather than in Python, see the TOML reference. A regex detector takes its catalogs there with
catalogs = ["generic"]. - To de-identify across a conversation with tokens stable between messages, see the Conversational pipeline.