Skip to content

feat: PII detection pipeline and post-generation audit#2

Merged
adel-saoud merged 1 commit into
mainfrom
feat/pii-detection
Jun 17, 2026
Merged

feat: PII detection pipeline and post-generation audit#2
adel-saoud merged 1 commit into
mainfrom
feat/pii-detection

Conversation

@adel-saoud

@adel-saoud adel-saoud commented Jun 17, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds the two-stage PII detection pipeline: Presidio (regex, no spaCy), GLiNER (NER) via Protocol-injected backends, conditional DeBERTa validation, typed-placeholder redactor
  • Adds the post-generation audit (PostGenerationAuditor) that re-runs the same detector + redactor over generated LLM answers
  • All heavy ML imports are lazy (pii/_real_backends.py, pyright-excluded). Tests substitute deterministic fakes

Test plan

  • uv run ruff check . clean
  • uv run pyright 0 errors / 0 warnings
  • uv run pytest tests/test_pii_*.py all green
  • Full suite: 46 passed, 95.26% coverage
  • CI green on the PR

ADRs referenced

Adds the two-stage detection pipeline at the heart of Guardian RAG.

- Stage 1: Presidio regex recognisers (email, phone, IBAN, credit card,
  IP, SSN, URL). Bypasses spaCy / the NlpEngine so detection stays in
  the ~5ms latency budget.
- Stage 2: GLiNER NER for unstructured PII (names, organisations,
  locations, etc). Backend is injected via Protocol so tests substitute
  deterministic fakes; real `gliner` import lives in `_real_backends.py`.
- Stage 3: Conditional DeBERTa zero-shot validation that fires only on
  GLiNER spans below `GUARDIAN_DEBERTA_THRESHOLD` (default 0.85). Safety
  net without the inference cost of running DeBERTa on every span.

Outputs:
- `PiiDetector` orchestrates the three stages and returns entities in
  source order.
- `Redactor` replaces spans with typed placeholders (`[PERSON_1]`,
  `[EMAIL_1]`, ...) preserving grammar so the chunk remains useful for
  RAG. Same span resolves to the same placeholder within a document.
- `PostGenerationAuditor` re-runs the detector + redactor over an LLM
  answer before it reaches the user. Fail-closed by default: any
  exception inside the audit returns a safe placeholder.

Tests are hermetic. No weights are downloaded; backends are fake.

ADRs:
  001 Presidio + GLiNER orchestration
  002 GLiNER over openai/privacy-filter
  003 Conditional DeBERTa validation
  005 Typed placeholders
  006 Post-generation audit
@adel-saoud adel-saoud self-assigned this Jun 17, 2026
@adel-saoud adel-saoud merged commit 9940b7a into main Jun 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant