From 2584bd8c95731a10278e38e23595c6b95617f7b3 Mon Sep 17 00:00:00 2001 From: Mobiletic Date: Wed, 1 Jul 2026 09:58:56 +0100 Subject: [PATCH] feat: prompt rules for lossless round-trips (0.3.1) Two coherence rules added to DEFAULT_SYSTEM_PROMPT: - different values of the same type get distinct placeholders (context), e.g. old/new IBAN -> [PER_1.IBAN:Ancien] / [PER_1.IBAN:Nouveau] - never absorb adjacent punctuation/separators into a placeholder Took the live Gemma-4 (Infomaniak) e-learning demo from 4/5 to 5/5 exact round-trips. 36 tests passing. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- src/providers/openai-compatible.ts | 3 ++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1208f2..2ae067a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,16 @@ All notable changes to this project are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.1] - Unreleased + +### Changed + +- Default prompt hardening for lossless round-trips: (1) two **different** values of the same type never + share a placeholder — they must be distinguished by context (e.g. `[PER_1.IBAN:Ancien]` vs + `[PER_1.IBAN:Nouveau]`), preventing a within-message collision (old/new IBAN); (2) the model must not + absorb adjacent **punctuation/separators** (commas, spaces, parentheses) into a placeholder. Together + these took the live Gemma-4 e-learning demo from 4/5 to 5/5 exact round-trips. + ## [0.3.0] - Unreleased ### Changed diff --git a/package.json b/package.json index cee3674..3a1cde2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@mobiletic/anonymizer", - "version": "0.3.0", + "version": "0.3.1", "description": "Framework-agnostic PII anonymization & pseudonymization: pluggable LLM detection for free-form PII (names, addresses) with a deterministic regex fallback, deterministic coreference, and streaming-safe de-anonymization.", "license": "MIT", "author": "Mobiletic", diff --git a/src/providers/openai-compatible.ts b/src/providers/openai-compatible.ts index bb19329..cb2088c 100644 --- a/src/providers/openai-compatible.ts +++ b/src/providers/openai-compatible.ts @@ -27,8 +27,9 @@ export const DEFAULT_SYSTEM_PROMPT = [ 'COHÉRENCE (impératif) :', '- Chaque clé de "mapping" est EXACTEMENT un placeholder présent dans "texte_anonymise" ; chaque placeholder du texte est une clé de "mapping".', '- Les VALEURS de "mapping" sont les données ORIGINALES réelles, JAMAIS un autre placeholder.', + '- Deux valeurs DIFFÉRENTES ne partagent JAMAIS le même placeholder. Pour deux valeurs du même type chez la même personne (ex. ancien et nouvel IBAN), distingue-les par un CONTEXTE différent : [PER_1.IBAN:Ancien] et [PER_1.IBAN:Nouveau].', '- Format OBLIGATOIRE : [TYPE_N] ou [TYPE_N.ATTRIBUT:CONTEXTE] (TYPE en MAJUSCULES, N entier ; ex. [ORG_1.NOM:Entreprise], jamais [ORG:Entreprise]).', - '- Remplace UNIQUEMENT la valeur, pas le libellé voisin (ex. « numéro AVS 756… » → seul « 756… » devient un placeholder ; le mot « AVS » reste).', + '- Remplace UNIQUEMENT la valeur elle-même — jamais le libellé voisin (« numéro AVS 756… » → seul « 756… » ; « AVS » reste) NI la ponctuation/séparateurs voisins (virgules, espaces, parenthèses restent HORS des placeholders, ex. « 14 rue X, 1700 Ville » garde la virgule).', '', 'RÈGLES:', '1. Coréférence: la MÊME personne garde le MÊME identifiant (PER_1) dans tout le texte.',