Skip to content

Latest commit

 

History

History
194 lines (135 loc) · 9.99 KB

File metadata and controls

194 lines (135 loc) · 9.99 KB

Rewrite

Instead of replacing individual entities, rewrite mode transforms the entire text to produce a privacy-safe version that reduces both explicit identifiers and implicit, inferable signals that could lead to re-identification. While overall meaning is preserved where possible, rewrite mode intentionally modifies or removes details when needed to eliminate latent entities and break inference pathways.


How it works

Detection runs first (same as Replace mode, plus latent entity detection for context-inferable information). This includes identifying signals that may not be explicitly tagged but can be deduced from combinations of details (e.g., location inferred from contextual cues). The text is then classified by domain, and each entity or attribute is assigned a sensitivity disposition based on contextual risk, recognizing that quasi-identifiers can emerge from any aspect of the text.

The text is then rewritten to reduce identifiability, applying targeted transformations that disrupt inference (e.g., weakening or removing linking details) rather than simply rewording content. The rewritten output is evaluated for both quality and privacy leakage using adversarial testing. If thresholds are exceeded, the system automatically refines the rewrite. Any records that failed to meet standards are tagged for human review.

!!! info "Long records" A record too large to rewrite in a single LLM call is rewritten in sequential windows, with a continuity summary carried across each so the narrative and pseudonyms stay consistent. See Long-context handling.


Key concepts

Sensitivity measures the intrinsic re-identification damage an entity causes if it appears in the output — independently of what else is retained. It is not the protection decision itself; rather, it feeds the downstream leakage scoring system.

Level Meaning Examples Leakage weight
high Exposure alone can identify a person Names, ID numbers, contact details 1.0
medium Meaningfully narrows the identity space Location, occupation, age 0.6
low Minimal standalone identifying power Generic attributes, widely shared traits 0.3

Protection method describes how a sensitive entity is transformed. The choice reflects a holistic view of the document — what other entities are being protected and how, then shapes what each individual entity needs.

Method What it does Typical use
replace Substitutes the entity with a plausible synthetic alternative Direct identifiers (names, IDs, contact details)
generalize Replaces the entity with a broader form Quasi-identifiers (exact date → quarter, city → region)
suppress_inference Rewrites the surrounding text to remove cues that enable the inference Latent entities that are implied rather than stated
remove Deletes the entity entirely Cases where neither replacement nor generalization can preserve meaning without retaining the identifying detail
leave_as_is Leaves the entity unchanged Entities judged not to require protection in context

Basic usage

from anonymizer import Anonymizer, AnonymizerConfig, AnonymizerInput, Rewrite

anonymizer = Anonymizer()

config = AnonymizerConfig(rewrite=Rewrite())
data = AnonymizerInput(source="data.csv", text_column="text")

preview = anonymizer.preview(config=config, data=data, num_records=3)
preview.display_record()

Configuration

Field Default Description
privacy_goal Auto-populated What to protect and what to preserve.
instructions None Additional instructions for the rewrite LLM.
risk_tolerance low Preset controlling repair and review thresholds: minimal, low, moderate, high.
max_repair_iterations 3 Maximum repair rounds. Set to 0 to disable repair.
strict_entity_protection False If True, forces every detected entity to be protected regardless of risk. No entity can be left unchanged.

Privacy goal

PrivacyGoal tells the rewriter what matters. Defaults work for general-purpose data. Override for domain-specific needs:

from anonymizer.config.rewrite import PrivacyGoal

config = AnonymizerConfig(
    rewrite=Rewrite(
        privacy_goal=PrivacyGoal(
            protect="All patient identifiers and clinical facility names",
            preserve="Clinical findings, treatment plans, and medical terminology",
        )
    )
)

!!! tip "Be specific"

The more precise the `protect` and `preserve` fields, the better the rewriter targets sensitive content while retaining what matters.

Risk tolerance

Controls the automated repair loop and human review flagging. Each preset bundles a coherent set of behaviors:

Preset Repair threshold Repair on any high leak Flag utility below Flag leakage above
minimal 0.6 Yes 0.6 1.0
low 1.0 Yes 0.5 2.0
moderate 1.5 Yes 0.4 2.5
high 2.0 No 0.3 3.0

The repair threshold is the leakage mass above which a record is sent for repair.

Leakage mass is a confidence-weighted sum of leaked entities, where each entity's weight reflects its sensitivity (high=1.0, medium=0.6, low=0.3). A leakage mass of 1.0 roughly equals one high-sensitivity entity leaked at full confidence.

config = AnonymizerConfig(
    rewrite=Rewrite(
        risk_tolerance="minimal",
        max_repair_iterations=3,
    )
)

Strict entity protection

By default, some entities — particularly quasi-identifiers judged to be low-risk in context — may be left unchanged. Setting strict_entity_protection=True overrides this: every detected entity is forced into an active protection method, and no entity can be left as-is.

config = AnonymizerConfig(
    rewrite=Rewrite(
        strict_entity_protection=True,
    )
)

!!! tip "When to use this"

Use `strict_entity_protection` when you need a blanket policy that protects all entities regardless of contextual risk — for example, in medical, legal, or financial data where even seemingly benign attributes (gender, marital status, occupation) must be modified. For finer control over which entity types are always protected, combine this with a specific [`privacy_goal`](#privacy-goal).

Output columns

Column When available Description
{text_col}_rewritten Always The privacy-safe rewritten text.
utility_score Always Quality preservation (0.0--1.0). Higher is better.
leakage_mass Always Weighted privacy leakage. Lower is better.
weighted_leakage_rate Always Normalized leakage (0.0--1.0) relative to the maximum possible leakage mass.
any_high_leaked Always Whether any high-sensitivity entity leaked through.
needs_human_review Always Flag for records that may need manual review.
detection_valid After evaluate() Fraction of detected entities that passed the detection judge (0.0--1.0); None if judge unavailable.
detection_invalid_entities After evaluate() Flagged detections with value, label, and one-sentence reasoning.
judge_evaluation After evaluate() Dict with privacy, quality, and style rubric scores and reasoning.

Use preview.trace_dataframe for the full pipeline trace (domain, disposition, QA pairs, repair iterations).

!!! note "No entities? No rewrite."

Records with no detected entities pass through unchanged with `utility_score=1.0` and `leakage_mass=0.0`.

Working with flagged records

Records with needs_human_review=True exceeded automated thresholds for leakage or utility. To investigate and resolve:

Diagnose: Use trace_dataframe to inspect the flagged record's intermediate columns — disposition, leakage breakdown, repair iterations, and judge evaluation.

flagged = result.trace_dataframe[result.trace_dataframe["needs_human_review"] == True]
flagged[["utility_score", "leakage_mass", "any_high_leaked"]].head()

Tune and re-run: Adjust settings and re-run on flagged records:

  • Increase max_repair_iterations to give the rewriter more attempts.
  • Refine privacy_goal with more specific protect / preserve instructions for the domain.
  • Lower risk_tolerance (e.g. minimal) to trigger more aggressive repair.
  • Enable strict_entity_protection=True to ensure no detected entity is left unchanged in the output regardless of risk.

Last resort: Manually edit or exclude records that resist automated repair — some text is inherently difficult to rewrite without losing utility or leaking identifiers, and requires your judgement as the expert.


Model roles

Rewrite uses multiple LLM roles. All default to models in the default config:

Role Default Purpose
domain_classifier gpt-oss-120b Classifies text domain.
disposition_analyzer gpt-oss-120b Assigns sensitivity levels.
meaning_extractor gpt-oss-120b Extracts meaning units.
qa_generator gpt-oss-120b Generates QA pairs for evaluation.
rewriter gpt-oss-120b Generates the rewritten text.
evaluator nemotron-30b-thinking Evaluates quality and leakage.
repairer gpt-oss-120b Repairs high-leakage rewrites.

Evaluating rewrite output

After running rewrite, you can score detection quality and the holistic rewrite quality using LLM-as-judge evaluation. See Evaluation for details on the detection judge and the three rewrite quality rubrics (privacy, quality, style), and how to call Anonymizer.evaluate().