Skip to content

[Multi-Agent Privacy] Add privacy agents: Analyzer, Detector, Sanitizer#337

Closed
JCHAVEROT wants to merge 0 commit into
feat/detection-toolkit-cleanfrom
feat/privacy-agents-clean
Closed

[Multi-Agent Privacy] Add privacy agents: Analyzer, Detector, Sanitizer#337
JCHAVEROT wants to merge 0 commit into
feat/detection-toolkit-cleanfrom
feat/privacy-agents-clean

Conversation

@JCHAVEROT

@JCHAVEROT JCHAVEROT commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Please refer to the new PR in #344


Summary

Related issue: #293
Depending on: #335
Important rebases were needed to solve conflicts, that's why this PR squashed the commits from its original PR in a fork (link)

This PR adds the first stage for the sanitized context production: pick the domain, build a per-request privacy policy, detect what is sensitive, and transform it

What this adds

  • DomainProfile: a dataclass with three presets that set the pipeline defaults per domain (sensitive entities, agent prompts, detection and sanitization settings):

    • global: generic PII baseline
    • healthcare: clinical entities (patient name, MRN, dates, insurance ID, ...) plus custom clinical recognizers
    • humanitarian: affected-population data (names, GPS/location, ethnicity, displacement/legal status, household ids)
  • Sanitization toolkit (mmore.privacy.sanitization) with four strategies, each a registered agent tool:

    • Token masking: John Doe to [PERSON_1]
    • Entity replacement: Faker-based fake values, same subject maps to the same fake identity within a call
    • Presidio: uses presidio_anonymizer.AnonymizerEngine (replace, redact, mask, hash, encrypt, keep, custom)
    • Synthetic rewrite: an LLM rewrites each chunk, keeping the meaning
    • Strategy and per-entity consistency are set in YAML, defaults from the active DomainProfile
  • Three agent nodes over a shared PrivacyState LangGraph state (analyzer > detector > sanitizer):

    • Analyzer: picks the domain (config override, else inferred from the query and chunks) and emits a PrivacyPolicy (entities, detection engine and thresholds, sanitization strategy)
    • Detector: runs the policy's detection engine on each chunk, dedups spans, and emits a RiskAssessment (counts, density, level). Fails clearly on an unknown engine
    • Sanitizer: takes the Detector's spans and the policy and applies the chosen strategy (it does not detect itself)
  • Shared PrivacyPolicy: the Analyzer emits it and the Detector and Sanitizer read it

    @dataclass
    class PrivacyPolicy:
        """Resolved privacy rules for a single retrieval request."""
    
        domain: str
        sensitive_entities: List[str]
        detection_engine: str
        sanitization_strategy: str
        consistency: bool
        domain_prompt: str
        detection_params: dict = field(default_factory=dict)
        sanitization_params: dict = field(default_factory=dict)
        sanitizer_system_prompt: str = ""
        flagged_fields: List[str] = field(default_factory=list)

Config

domain: healthcare            # global | healthcare | humanitarian (omit to let the analyzer pick)
context_analyzer:
  llm:
    llm_name: Qwen/Qwen2.5-3B-Instruct
    max_new_tokens: 1200
    temperature: 0.7
detection:
  engine: presidio            # resolved as a tool per policy (omit to let the analyzer pick)
  confidence_threshold: 0.7
sanitization:
  strategy: token_masking     # token_masking | entity_replacement | presidio | synthetic_rewrite
  consistency: true

Dependencies / CI

  • the extra privacy now has the faker dependency (for the entity-replacement strategy)

Demo

Each strategy runs on the same chunks and hand-annotated spans, so the output isolates strategy behavior from detector noise.

Input chunks

[0] Patient John Doe (john.doe@example.com) was admitted on 2024-03-15 for review by Dr. Alice Smith.
[1] Follow-up call to John Doe at 555-123-4567, MRN AB12345678 confirmed.
chunk label score text
0 PERSON 0.95 John Doe
0 EMAIL 0.95 john.doe@example.com
0 DATE 0.90 2024-03-15
0 PERSON 0.90 Alice Smith
1 PERSON 0.95 John Doe
1 PHONE 0.90 555-123-4567
1 MRN 0.95 AB12345678

token_masking

[0] Patient [PERSON_1] ([EMAIL_1]) was admitted on [DATE_1] for review by Dr. [PERSON_2].
[1] Follow-up call to [PERSON_1] at [PHONE_1]; MRN [MRN_1] confirmed.

entity_replacement (Faker, consistency=true, John Doe maps to the same fake in both chunks)

[0] Patient David Pacheco (hlawrence@example.com) was admitted on 2016-02-12 for review by Dr. Karen Taylor.
[1] Follow-up call to David Pacheco at 775-592-9555x238; MRN MRN76684261 confirmed.

presidio (AnonymizerEngine, per operator)

operator=replace
[0] Patient <PERSON> (<EMAIL>) was admitted on <DATE> for review by Dr. <PERSON>.
[1] Follow-up call to <PERSON> at <PHONE>; MRN <MRN> confirmed.

operator=redact
[0] Patient  () was admitted on  for review by Dr. .
[1] Follow-up call to  at ; MRN  confirmed.

operator=mask  (masking_char='*', chars_to_mask=4)
[0] Patient **** Doe (****.doe@example.com) was admitted on ****-03-15 for review by Dr. ****e Smith.
[1] Follow-up call to **** Doe at ****123-4567; MRN ****345678 confirmed.

operator=hash
[0] Patient 243c714c...294a36db (a5c155fb...a69ee105) was admitted on bd203e8f...e4bb8098 for review by Dr. 8d28ccce...2f6c2f07.
[1] Follow-up call to a21921b8...52c683cba at 861e89f1...b0986720; MRN 64c4cab9...458acf27 confirmed.

synthetic_rewrite (Qwen/Qwen2.5-3B-Instruct)

[0] Patient (id: JOHNDOE) was admitted for review by Dr. (id: ALICESMITH).
[1] Follow-up call to PERSON at PHONE confirmed. MRN confirmed.

@JCHAVEROT JCHAVEROT self-assigned this Jun 29, 2026
@JCHAVEROT JCHAVEROT added documentation Improvements or additions to documentation enhancement New feature or request dependencies Pull requests that update a dependency file labels Jun 29, 2026
@fabnemEPFL
fabnemEPFL requested a review from Copilot June 30, 2026 15:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces the first end-to-end stage of the privacy “sanitized context” pipeline in mmore.privacy, adding domain-aware policy construction, per-chunk PII detection with risk scoring, and multiple sanitization strategies wired as LangGraph agent nodes/tools.

Changes:

  • Added domain profiles and a request-scoped PrivacyPolicy/RiskAssessment model to drive privacy decisions per request.
  • Implemented analyzer → detector → sanitizer agent nodes over a shared PrivacyState, with detector tool-dispatch and sanitizer strategy-dispatch.
  • Added sanitization strategy toolkit (token masking, entity replacement via Faker, Presidio anonymizer, synthetic rewrite) and updated detection config/constants + tests.

Reviewed changes

Copilot reviewed 33 out of 34 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/test_privacy.py Updates BaseAgent LLM patching and adds tests for LLM-less subclasses / node naming behavior.
tests/test_detection.py Updates imports/config expectations and adapts tests to renamed params and constants.
tests/conftest.py Switches test sample metadata to plain dicts (leveraging MultimodalSample.__post_init__).
src/mmore/privacy/sanitization/base.py Defines sanitization strategy interface and shared replacement helpers.
src/mmore/privacy/sanitization/constants.py Defines YAML strategy names → tool name mapping for sanitizer dispatch.
src/mmore/privacy/sanitization/token_masking_strategy.py Adds [LABEL_N] token masking strategy and tool registration.
src/mmore/privacy/sanitization/entity_replacement_strategy.py Adds Faker-based realistic replacement strategy and tool registration.
src/mmore/privacy/sanitization/presidio_strategy.py Adds Presidio anonymizer strategy with operator normalization and caching.
src/mmore/privacy/sanitization/synthetic_rewrite_strategy.py Adds DSPy-based synthetic rewrite sanitization strategy.
src/mmore/privacy/sanitization/init.py Exposes sanitization strategies and tool names as package API.
src/mmore/privacy/risk.py Introduces RiskAssessment produced by the detector.
src/mmore/privacy/policy.py Introduces request-scoped PrivacyPolicy shared across agents.
src/mmore/privacy/dspy_llm.py Adds shared cached HF pipeline getter for reuse across agents/engines.
src/mmore/privacy/domains/profile.py Adds DomainProfile presets and clinical Presidio patterns + resolver.
src/mmore/privacy/domains/init.py Exposes domain profile API.
src/mmore/privacy/detection/presidio_engine.py Updates detection tool signature to accept PrivacyPolicy and forwards policy params.
src/mmore/privacy/detection/openai_filter_engine.py Updates tool signature to accept PrivacyPolicy and forwards policy params.
src/mmore/privacy/detection/llm_engine.py Updates constants usage and tool signature to accept PrivacyPolicy.
src/mmore/privacy/detection/gliner_engine.py Renames entity_typessensitive_entities and forwards policy params.
src/mmore/privacy/detection/defaults.py Removes old detection defaults module (superseded by constants.py).
src/mmore/privacy/detection/constants.py Adds centralized detection constants, guidance, default params, and tool-name mapping.
src/mmore/privacy/detection/config.py Removes old detection config dataclass (superseded by privacy/config.py).
src/mmore/privacy/detection/base.py Simplifies base types and removes embedded engine enum.
src/mmore/privacy/detection/init.py Updates exports/imports to new config/enum location.
src/mmore/privacy/config.py Adds top-level privacy config dataclasses + enums (detection/sanitization).
src/mmore/privacy/agents/state.py Adds shared PrivacyState for analyzer/detector/sanitizer graph.
src/mmore/privacy/agents/analyzer.py Adds analyzer node that infers domain and emits per-request PrivacyPolicy.
src/mmore/privacy/agents/detector.py Adds detector node that runs configured engine tools, dedupes spans, and computes risk.
src/mmore/privacy/agents/sanitizer.py Adds sanitizer node that dispatches to strategy tools and optionally manages DSPy LM context.
src/mmore/privacy/agents/config.py Clarifies AgentConfig role as a template vs the new PrivacyConfig.
src/mmore/privacy/agents/base.py Generalizes BaseAgent to support LLM-less nodes, shared model caching, and node composition.
src/mmore/privacy/_cache.py Switches to RLock for cache registry locking.
pyproject.toml Adds faker>=30 to the privacy extra for entity replacement strategy.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/mmore/privacy/detection/constants.py Outdated
Comment thread src/mmore/privacy/agents/analyzer.py Outdated
Comment thread src/mmore/privacy/detection/openai_filter_engine.py Outdated
Comment thread src/mmore/privacy/agents/config.py Outdated
Comment thread src/mmore/privacy/agents/base.py Outdated
@JCHAVEROT
JCHAVEROT force-pushed the feat/privacy-agents-clean branch from 939127a to 72e379f Compare June 30, 2026 17:03
@JCHAVEROT JCHAVEROT linked an issue Jul 2, 2026 that may be closed by this pull request
@fabnemEPFL
fabnemEPFL requested a review from Copilot July 8, 2026 08:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 33 out of 34 changed files in this pull request and generated 4 comments.

Comment thread src/mmore/privacy/agents/analyzer.py Outdated
Comment thread src/mmore/privacy/agents/analyzer.py Outdated
Comment thread src/mmore/privacy/agents/analyzer.py Outdated
Comment thread src/mmore/privacy/detection/openai_filter_engine.py Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Analyzer, Detector and Analyzer agents implementation

2 participants