Brancher la protection sur un agent et ses outils
En bref
- Branché sur un agent,
piighostmasque les messages avant le modèle et remet les vraies valeurs dans la réponse. - Par défaut, les outils de l'agent (recherche, envoi de mail, lecture de fichier) reçoivent les vraies valeurs, et ce qu'ils renvoient est masqué avant le modèle.
- Une conversation sans identifiant est refusée, avec LangChain comme avec Claude Code. Sinon, toutes les conversations partageraient leurs jetons.
- Avec Claude Code, la réponse affichée garde les jetons, parce qu'aucun point d'accroche ne permet de la réécrire.
- L'historique gardé par l'agent contient le texte des messages avec les vraies valeurs. Seuls les appels d'outil y restent en jetons. Protégez cet historique comme une donnée personnelle.
Les termes sont définis dans le glossaire. Le mécanisme de la conversation est décrit dans Suivre une conversation et restaurer la réponse.
Pour le métier
piighost n'a pas d'écran. Les réglages se font dans le code de l'agent ou dans sa configuration. Cette partie décrit ce que chaque acteur voit et les choix à arbitrer.
Qui voit quoi
| Acteur | LangChain, Pydantic AI | LlamaIndex | Claude Code |
|---|---|---|---|
| Le modèle | des jetons | des jetons (question et documents indexés) | des jetons (demande et résultats d'outils listés) |
| Les outils | les vraies valeurs (réglage par défaut) | sans objet | les vraies valeurs |
| L'utilisateur final | la réponse avec les vraies valeurs | la réponse avec les vraies valeurs | la réponse avec des jetons |
| Le service d'indexation | sans objet | des jetons | sans objet |
Choisir le traitement des outils
Quatre réglages décident de ce que reçoit l'outil et de ce que lit le modèle. Le choix se fait pour tout l'agent. Le tableau des réglages, l'exemple suivi et les règles sont dans Laisser un outil agir sur les vraies valeurs.
Règles à connaître
BR-AGT-01 . Quand un agent LangChain est appelé sans identifiant de conversation, alors il s'arrête sur No thread_id in the LangGraph config; pass config={'configurable': {'thread_id': ...}} on the agent call, or 'default' if your conversations need no separation. La raison est que sans identifiant, toutes les conversations n'en feraient qu'une et partageraient leurs jetons. Une application qui n'a pas besoin de séparer ses conversations passe default elle-même. 1 emplacement
BR-AGT-02 . Quand le modèle écrit un jeton que piighost n'a jamais émis, alors la réponse est refusée par défaut, avec Deanonymized text holds tokens the pipeline never issued. Les deux autres choix sont de garder le jeton tel quel ou de le retirer du texte. 2 emplacements
BR-AGT-03 . Quand l'assistant cite le premier une valeur, alors elle reste en clair par défaut. Par exemple, l'assistant répond « Le siège est à Lyon ». « Lyon » n'est pas masqué au tour suivant, parce qu'il vient de l'assistant. Les deux autres choix sont de la masquer comme une donnée de l'utilisateur ou de ne pas analyser du tout les messages de l'assistant. 2 emplacements · 11 tests directs
BR-AGT-04 . Quand le réglage d'outil est « Complet » ou « Sortie seule », alors le texte renvoyé par l'outil passe par le repérage complet et est masqué avant le modèle. Avec LangChain, seul le texte du message de l'outil est masqué. Avec Pydantic AI, un résultat structuré (liste, dictionnaire) est parcouru en entier. 2 emplacements · 4 tests directs
BR-AGT-05 . Quand un événement de Claude Code n'a pas d'identifiant de session, alors il est refusé avec The hook event carries no session_id, the thread its values belong to. Aucune conversation commune n'est utilisée. 1 emplacement · 9 tests directs
BR-AGT-06 . Quand un outil de Claude Code n'est pas dans la liste des outils traités, alors son résultat passe en clair. Les outils traités sont Bash, Read, Write, Edit, Agent, WebFetch, WebSearch, ToolSearch. Grep, notamment, n'y est pas. 1 emplacement
BR-AGT-07 . Quand la réponse du modèle est diffusée au fil de l'eau, alors l'affichage montre des jetons jusqu'à la fin du message, sauf si l'application branche le décodeur de flux prévu. Voir Afficher une réponse au fil de l'eau. 2 emplacements · 4 tests directs
Ce que voit l'utilisateur final
- Avec LangChain, Pydantic AI et LlamaIndex : une réponse lisible, avec les vraies valeurs.
- Avec Claude Code : une réponse qui contient des jetons comme
<<PERSON:1>>. Les fichiers modifiés et les commandes lancées, eux, portent les vraies valeurs.
Questions fréquentes
La réponse affichée contient <<PERSON:1>>. Trois causes possibles. Vous utilisez Claude Code, qui ne restaure pas la réponse affichée. Ou l'application diffuse la réponse sans décodeur de flux (BR-AGT-07 ). Ou la restauration n'a pas lieu dans la même conversation que le masquage. Vérifiez que le même identifiant de conversation est passé aux deux.
L'agent s'arrête avec No thread_id in the LangGraph config. L'appel ne transmet pas d'identifiant de conversation. Demandez à l'équipe de développement de le passer à chaque appel, ou default si les conversations n'ont pas besoin d'être séparées (BR-AGT-01 ).
L'agent s'arrête avec Deanonymized text holds tokens the pipeline never issued. Le modèle a écrit un jeton inconnu, souvent en recopiant un jeton d'une autre conversation ou d'un document. Gardez le refus si vous préférez une erreur visible à un texte douteux (BR-AGT-02 ).
Un outil a reçu <<EMAIL:1>> au lieu de l'adresse. Le réglage d'outil est « Sortie seule » ou « Aucun ». Passez-le à « Complet » si l'outil doit agir sur la vraie adresse. Voir Laisser un outil agir.
Le résultat d'une recherche Grep est parti en clair dans Claude Code. Grep n'est pas dans la liste des outils traités (BR-AGT-06 ). Retirez Grep de la session, ou faites ajouter l'outil à la liste.
Pour les développeurs
Où vivent les règles
| Règle | Emplacement |
|---|---|
| BR-AGT-01 | src/piighost/integrations/langchain/middleware.py:47-66 (_thread_id) |
| BR-AGT-02 | src/piighost/integrations/_deidentify.py:133-155, défaut RAISE ligne 57 |
| BR-AGT-03 | src/piighost/integrations/langchain/middleware.py:370 (_message_role), pipeline/thread.py:322-329 |
| BR-AGT-04 | middleware.py:220-272 (LangChain), pydantic_ai/hooks.py:138-154 (Pydantic AI) |
| BR-AGT-05 | src/piighost/integrations/claude_code/hooks.py:101-105 |
| BR-AGT-06 | src/piighost/integrations/claude_code/hooks.py:22-38 |
| BR-AGT-07 | middleware.py:206-218, _deidentify.py:83-107 |
Composants liés :
TextDeidentifier(integrations/_deidentify.py) : logique commune de masquage, restauration et jetons inventés, partagée par LangChain, Pydantic AI et LlamaIndex. Il refuse à la construction un pipeline sansrecognizer(UnrecognizableFactoryError).PIIAnonymizationMiddleware:abefore_model,aafter_model,awrap_tool_call.pii_hooks(pipeline, thread_id, ...): capacité Pydantic AI,thread_idfixe ou fonction duRunContext.PIINodeAnonymizeretPIIQueryEngine(LlamaIndex) : masquage des nœuds avant embedding, puis masquage de la question et restauration de la réponse dans la même conversation de corpus.handle_hook(event, pipeline)etrun()(Claude Code) :runlit l'événement sur stdin et appellepiighost-apiàPIIGHOST_API_URL(défauthttp://localhost:8000).PIIGhostClient: implémenteAnyThreadPipelineen HTTP (/v1/anonymize,/v1/deanonymize,/v1/detect,/v1/labels,/v1/threads/{id}/tokens). Il lèveRemoteErrorsur une réponse non 2xx.
Brancher le middleware LangChain
- Partez de
examples/langchain_middleware.py. - Construisez un
ThreadAnonymizationPipelinedont la fabrique est délimitée (par défautLabelCounterPlaceholderFactory). - Passez-le à
PIIAnonymizationMiddleware(pipeline). Ajouteztool_strategy,invented_strategyouassistant_strategyseulement pour changer un défaut. - Passez
config={"configurable": {"thread_id": "..."}}à chaque appel de l'agent. - Pour un affichage en flux, enveloppez la boucle
agent.astream(..., stream_mode="messages")dansmiddleware.deanonymize_stream(source, thread_id).
Vérifier
uv run pytest tests/integrations/langchainDans une trace de l'agent, le message reçu par le modèle doit contenir <<PERSON:1>> et l'argument reçu par l'outil la vraie valeur.
Pièges
- L'état LangGraph garde le contenu des messages en clair.
aafter_modelrestaure le contenu dans l'état, et le checkpointer l'enregistre ainsi. Seuls lestool_callsrestent en jetons (middleware.py:199-200). Il en va de même pour l'historique Pydantic AI aprèsafter_model_request. - LangChain re-masque les arguments des
tool_callsde l'historique, par précaution, sauf sousIGNORE. Pydantic AI ne le fait pas. - Les hooks Claude Code tolèrent une forme de sortie inconnue : ils la laissent passer. Mettez
PIIGHOST_HOOK_LOGpour voir les formes réelles. Ce journal contient des valeurs restaurées en clair. Gardez-le en local et supprimez-le ensuite. - Si
piighost-apiest injoignable, le hook échoue fermé (claude_code/runner.py:68-88). Un prompt ou un appel d'outil sort en code 2, que Claude Code lit comme un blocage. Une sortie d'outil, déjà produite, est remplacée par un avis.PIIGHOST_HOOK_FAIL_OPEN=1laisse passer le texte en clair (DPO-9 ). PIIQueryEnginerefuse un moteur en flux (NotImplementedError). Ses chemins synchrones passent parasyncio.run, donc appelezaquerydepuis du code asynchrone.PIIGhostClient.anonymizerenvoie un dictionnaire de jetons vide. La correspondance vit sur le serveur. Restaurez avecdeanonymize.
Tests
| Test | Couvre |
|---|---|
tests/integrations/langchain/test_middleware.py | Identifiant de conversation, contenu en blocs, chaque stratégie d'outil, Command, jetons inventés, provenance assistant, refus d'une fabrique non délimitée |
tests/integrations/langchain/test_middleware_stream.py | Restauration en flux |
tests/integrations/test_pydantic_ai_hooks.py | Capacité Pydantic AI |
tests/integrations/llama_index/ | Transformation des nœuds, moteur de requête |
tests/integrations/test_claude_code_hooks.py | Les trois événements, liste des champs, outil inconnu laissé passer, blocage quand piighost-api est injoignable, échec ouvert |
tests/integrations/client/test_client.py | Client HTTP |
Voir aussi Configurer un pipeline.