Aller au contenu
Tutorial

Supprimez les informations personnelles des journaux de l'agent IA avant qu'elles n'atteignent votre base de données

| 7 min read

Votre agent enregistre chaque invite et appel d'outil. Un SSN manqué dans une transcription se transforme en une divulgation RGPD. Un middleware à trois lignes le corrige avant que la ligne ne soit écrite.

Padlock representing PII redaction and data protection in AI agent logs
Photo by FLY:D on Unsplash

Votre agent IA enregistre l'invite. L'entrée d'appel d'outil. Le résultat de l'outil. La réponse finale. C’était autrefois une mine d’or de données de débogage. Aujourd'hui, il s'agit d'une divulgation RGPD en attente d'un ticket d'assistance pour porter un SSN après votre validation.

Vous ne pouvez pas empêcher les utilisateurs de coller des données sensibles. Vous pouvez empêcher le texte brut d'atteindre votre magasin de journaux, votre fournisseur d'observabilité et votre exportation d'évaluation hebdomadaire. Un petit middleware le fait en un seul saut.

La fuite que la plupart des configurations d’agents ont déjà

// before: every raw prompt, tool call, and tool result lands in the log row
logger.info({
  event: 'agent.turn',
  prompt: userInput,
  tool_calls: toolCalls,
  tool_results: toolResults,
});

// one support ticket later: "My SSN is 123-45-6789 and card 4111 1111 1111 1111"
// sits in the logs, the observability vendor, and the weekly eval export.

Chaque ligne porte désormais un numéro de carte. Le magasin de bûches le conserve pendant 30 jours. Le SDK d'observabilité l'expédie à un tiers. L'exportation d'évaluation récupère la même chaîne deux jours plus tard. Cinq exemplaires d'une carte ; tous sont hors de portée de votre politique de chiffrement au repos.

Détecter les informations personnelles en un seul appel

Le Botoi /v1/pii/detect le point de terminaison analyse le texte à la recherche d'e-mails, de numéros de téléphone, de SSN, de cartes de crédit (validées par Luhn), d'adresses IP et de dates de naissance. Il renvoie chaque résultat avec un décalage de début, un décalage de fin et une valeur masquée que vous pouvez déposer.

Demande

curl -X POST https://api.botoi.com/v1/pii/detect \\
  -H "Content-Type: application/json" \\
  -d '{"text": "Reach me at alice@example.com or 555-123-4567. Card: 4111 1111 1111 1111."}'

Réponse

{
  "found": true,
  "count": 3,
  "findings": [
    { "type": "email",       "value": "alice@example.com",      "start": 12, "end": 29, "masked": "al***@example.com" },
    { "type": "phone",       "value": "555-123-4567",           "start": 33, "end": 45, "masked": "***-***-4567" },
    { "type": "credit_card", "value": "4111 1111 1111 1111",    "start": 53, "end": 72, "masked": "************1111" }
  ]
}

Trois matchs, trois remplacements masqués, des positions que vous pouvez épisser proprement. Pas de bibliothèque d'expressions régulières à maintenir, pas de table de préfixes SSN à tenir à jour, pas de passe Luhn à écrire vous-même.

Un middleware de journalisation qui rédige avant l'écriture

Le bon endroit pour cela est le dernier saut avant que la ligne ne quitte votre processus. Chaque composant en amont voit toujours le texte brut dont il a besoin ; la copie persistante est nettoyée.

// log-redact.ts
import type { LogRecord } from './types';

const PII_FIELDS = ['prompt', 'tool_calls', 'tool_results', 'output'] as const;

export async function redactPii(record: LogRecord): Promise<LogRecord> {
  const clone = structuredClone(record);
  for (const field of PII_FIELDS) {
    const value = clone[field];
    if (!value) continue;
    clone[field] = await scrub(JSON.stringify(value));
  }
  return clone;
}

async function scrub(text: string): Promise<string> {
  const res = await fetch('https://api.botoi.com/v1/pii/detect', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: \`Bearer \${process.env.BOTOI_API_KEY}\`,
    },
    body: JSON.stringify({ text }),
  });
  const data = await res.json();
  if (!data.found) return text;

  // Replace from the end of the string so offsets stay valid.
  const sorted = [...data.findings].sort((a, b) => b.start - a.start);
  let scrubbed = text;
  for (const f of sorted) {
    scrubbed = scrubbed.slice(0, f.start) + f.masked + scrubbed.slice(f.end);
  }
  return scrubbed;
}

Trois détails comptent dans cet extrait. Premièrement, il parcourt les champs connus riches en PII plutôt que l'ensemble de l'enregistrement ; vous n'avez pas besoin de nettoyer l'ID de la demande. Deuxièmement, il sérialise chaque champ en une seule chaîne avant de l'envoyer à l'API, de sorte qu'un appel couvre l'intégralité du résultat de l'outil. Troisièmement, il fusionne les remplacements à partir de la fin de la chaîne afin que les décalages ne se déplacent pas en dessous.

Connectez-le à l'enregistreur

// logger.ts
import { redactPii } from './log-redact';

export async function logTurn(raw: LogRecord) {
  const safe = await redactPii(raw);
  await logStore.write(safe);
}

// anywhere in your agent loop:
await logTurn({
  event: 'agent.turn',
  prompt: userInput,
  tool_calls: toolCalls,
  tool_results: toolResults,
});

Appel logTurn à la place de votre existant logger.info à la limite du virage. Tout en amont reste le même.

Échec fermé, pas échec silencieux

Le point final de détection répond généralement en moins de 20 ms. Lorsqu'il expire, vous avez encore le choix : enregistrer la ligne brute (risque de fuite) ou supprimer les champs sensibles et enregistrer un marqueur. L'abandon est la méthode par défaut la plus sûre pour les charges de travail sensibles à la conformité.

async function redactPiiSafe(record: LogRecord): Promise<LogRecord> {
  try {
    return await Promise.race([
      redactPii(record),
      new Promise<LogRecord>((_, reject) =>
        setTimeout(() => reject(new Error('pii-detect timeout')), 250)
      ),
    ]);
  } catch (err) {
    // Fail closed: drop the sensitive fields rather than logging them raw.
    return { ...record, prompt: '[REDACT_FAILED]', tool_calls: [], tool_results: [] };
  }
}

Réglez le délai d'attente sur quelque chose de petit. 250 ms suffisent pour absorber un ralentissement régional sans bloquer un chemin de requête sain.

Version Python

# log_redact.py
import os, json, httpx

PII_FIELDS = ('prompt', 'tool_calls', 'tool_results', 'output')
API = 'https://api.botoi.com/v1/pii/detect'

async def scrub(text: str) -> str:
    async with httpx.AsyncClient(timeout=0.25) as client:
        r = await client.post(
            API,
            headers={'Authorization': f"Bearer {os.environ['BOTOI_API_KEY']}"},
            json={'text': text},
        )
    data = r.json()
    if not data.get('found'):
        return text
    out = text
    for f in sorted(data['findings'], key=lambda x: x['start'], reverse=True):
        out = out[:f['start']] + f['masked'] + out[f['end']:]
    return out

Baisse scrub dans le hook de journal de votre infrastructure d'agent. Le middleware FastAPI, les rappels LangChain et les exportateurs de span OpenInference acceptent tous les fonctions asynchrones.

Ce que ce middleware ne fait pas

  • Il ne détecte pas les noms, adresses ou numéros de compte qui ne ressemblent à aucun type pris en charge. Ceux-ci ont besoin d'un modèle d'entité nommée et d'une décision politique (masquer ? abandonner ? expurger tout le tour ?).
  • Cela n'empêche pas votre fournisseur de modèles de voir l'invite brute. Pour cela, exécutez le même appel de détection sur le client avant de l'envoyer au modèle.
  • Cela ne remplace pas une politique de conservation des données. Raccourcissez quand même la durée de vie du journal.

Deux endroits auxquels cela appartient

Couche Protège Calendrier des appels
Avant demande de modèle Fournisseur de modèles, données de formation, fuites d'évaluation Latence bloquante et visible par l'utilisateur
Avant l'écriture du journal Magasin de journaux, fournisseur d'observabilité, exportations Hors bande, invisible pour l'utilisateur

Expédiez d’abord le middleware d’écriture de journaux. Il s'étend en dehors du chemin chaud et bloque les fuites les plus courantes. Ajoutez la version pré-modèle une fois que le côté journal est couvert.

Obtenez une clé API et démarrez

L'accès anonyme vous donne 5 requêtes par minute, suffisamment pour tester le point final avec un exemple de journal. Pour le middleware de production, obtenez une clé gratuite sur botoi.com/api/signup. Le niveau gratuit couvre 1 000 appels de nettoyage par jour sans carte de crédit.

Voir la référence complète du point final à l'adresse Page API de détection des informations personnelles ou parcourir api.botoi.com/docs pour les 149 autres points finaux.

FAQ

Pourquoi les journaux de l'agent IA divulguent-ils plus de données personnelles que les journaux du serveur normaux ?
Les agents enregistrent l'intégralité de l'invite, chaque entrée d'appel d'outil et chaque sortie d'outil. Une transcription de support qui se trouvait autrefois derrière un indicateur « Ne pas enregistrer » apparaît désormais à cinq endroits : l'orchestrateur, le serveur d'outils, le fournisseur d'observabilité, le fournisseur de modèles et l'ensemble d'évaluation de formation.
Où doit se dérouler l’étape de rédaction ?
Exécutez-le à la limite du rédacteur de journaux, juste avant que la ligne ne soit envoyée à votre magasin de journaux. De cette façon, chaque composant en amont (orchestre, outil, SDK d'observabilité) voit le texte brut dont il a besoin, et seule la copie persistante est nettoyée.
Un rédacteur d’expressions régulières capture-t-il tout ?
Non. L'expression régulière Roll-your-own manque les numéros de carte de crédit avec un espacement inhabituel, les SSN qui ressemblent à d'autres numéros à 9 chiffres et les noms de personnes. Une API telle que /v1/pii/detect exécute Luhn sur les cartes, filtre les préfixes SSN et renvoie les positions afin que vous puissiez supprimer uniquement la correspondance, pas la ligne entière.
Quelle latence l'API Botoi PII Detect ajoute-t-elle ?
Le point de terminaison s'exécute en périphérie et revient en moins de 20 ms pour une charge utile de 500 jetons. Vous pouvez l'appeler de manière synchrone dans un middleware de journalisation sans affecter les temps de réponse visibles par l'utilisateur ; la journalisation a lieu après l'envoi de la réponse.
Puis-je rédiger sur le client avant de l'envoyer au modèle ?
Oui, et c'est une bonne deuxième couche. La suppression dans le middleware du serveur protège votre magasin de journaux ; la suppression dans le client empêche votre fournisseur de modèles de voir les informations personnelles brutes. Les deux constituent une configuration compatible avec le RGPD.

Commencez a construire avec botoi

150+ endpoints API pour la recherche, le traitement de texte, la generation d'images et les utilitaires pour developpeurs. Offre gratuite, sans carte bancaire.