Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

Reçus d’outils

Les reçus d’outils sont des preuves cryptographiques qu’un résultat d’outil réussi provient du runtime. Lorsque les reçus sont activés, les exécutions d’outils réussies reçoivent un digest HMAC-SHA256 sur l’appel et son résultat. Le digest est ajouté au texte du tool-result et renvoyé au modèle dans le cadre de la conversation.

Le résultat pratique : le modèle ne peut pas prétendre de manière convaincante avoir exécuté un outil qu’il n’a pas exécuté, et il ne peut pas fabriquer un résultat d’outil. Ces affirmations produisent des reçus manquants ou invalides.

Le modèle de menace

Un LLM est un générateur de chaînes de caractères. Par défaut, rien ne l’empêche de raconter un appel d’outil qu’il n’a jamais effectué (« J’ai exécuté git log et le dernier commit est… »), ou d’inventer un résultat pour un appel d’outil (« L’API météo indique 72 °F », alors que l’appel a expiré). Pour un agent doté d’autonomie, c’est plus qu’un problème d’exactitude : c’est un problème de déni de responsabilité.

Les reçus d’outils comblent cette lacune avec la construction la moins chère possible : un MAC symétrique avec une clé éphémère en mémoire.

Ce que “éphemère” signifie ici. Les chemins canal-serveur conservent une clé HMAC en mémoire pendant toute la durée de vie du contexte d’exécution du canal. Les chemins de tour d’agent direct créent une nouvelle portée de reçu pour le tour. Dans les deux cas, la clé reste en mémoire, n’est jamais envoyée au modèle et ne constitue pas un vérificateur durable inter-sessions.

Based: Basu, A. (2026). « Tool Receipts, Not Zero-Knowledge Proofs: Practical Hallucination Detection for AI Agents ». arXiv:2603.10060.

Comment ça fonctionne

  1. Lorsque les reçus sont activés, le runtime crée une clé de 256 bits en mémoire pour la portée de reçu active. Les chemins Channel-server conservent cette clé dans ChannelRuntimeContext ; les chemins de tour directs créent une nouvelle portée pour le tour. La clé n’est jamais écrite sur le disque, jamais envoyée au modèle et jamais journalisée.

  2. Après chaque exécution d’outil réussie, le runtime calcule :

    receipt = HMAC-SHA256(key, tool_name || args || result || timestamp)
    
  3. Le reçu est ajouté au texte du résultat de l’outil comme suit :

    [receipt: zc-receipt-<timestamp>-<base64url-digest>]
    
  4. Le résultat de l’outil (avec le reçu) est renvoyé au modèle.

Le modèle voit tous les reçus dans son historique de conversation. Il peut les reproduire dans le texte qu’il génère pour l’utilisateur. Mais il ne peut pas produire un nouveau reçu valide : le HMAC nécessite la clé de session, que le modèle ne possède pas.

Forme de reçu

zc-receipt-1774608496-gzpEBuUIRYX1vd4fQl4oYkqhq4-GnoJDStmlYzvQiWA
          ^ epoch seconds     ^ base64url(HMAC-SHA256 digest)

Le préfixe zc-receipt- existe afin que le détecteur de fuites ne les masque pas (les reçus sont sûrs à afficher ; ils ne contiennent aucun matériel secret).

Quels reçus détecter

ScénarioSans reçusAvec les reçus
Le modèle prétend avoir exécuté un outil, mais ne l’a pas fait.IndétectableAucun reçu : fabrication visible
Le modèle fabrique un résultat pour un appel réel.IndétectableÉchec de la vérification HMAC
Le modèle nie avoir effectué un appel réussiNon vérifiableLe reçu dans la conversation prouve qu’un résultat signé a été émis
Le modèle fabrique une chaîne de reçu plausible.PlausibleLa vérification HMAC a échoué.

Ce que les reçus ne font pas

  • Ne contraignez pas la sortie de texte. Le modèle peut toujours dire des choses non liées à un appel d’outil.
  • Ne forcez pas l’utilisation d’outils. Les reçus sont générés uniquement lorsqu’un outil est appelé ; ils n’aident pas à résoudre le problème du « modèle répondant à partir de connaissances antérieures alors qu’il aurait dû effectuer une recherche ».
  • Ne masquez pas les appels bloqués ou échoués. Les refus d’approbation, les timeouts, les appels bloqués et les échecs de retours d’outil sont des événements d’observabilité ou d’audit, et non des résultats d’outil accompagnés d’un accusé de réception.
  • Ne pas utiliser les reçus d’une portée à une autre. Les clés de reçu sont éphémères, donc un reçu généré dans une portée ne peut pas être vérifié une fois celle-ci révolue.
  • Ne isolez pas les canaux ou les conversations les uns des autres dans un contexte d’exécution de canal. Les conversations canal-serveur dans ce contexte partagent la clé. Le modèle de menace cible la falsification par un LLM au sein de l’environnement d’exécution, et non la falsification inter-canaux.
  • N’étendez pas aux spawns d’arrière-plan ou de délégués détachés. Les spawns de délégués en arrière-plan et en parallèle qui se détachent du tour de l’utilisateur (background: true) n’affichent pas les reçus dans le bloc visible par l’utilisateur, car le collecteur par tour est rendu avant la fin de ces spawns. Les reçus à l’intérieur des sous-agents délégués synchrones sont capturés.

Afficher les reçus

Dans les journaux de débogage

sh

RUST_LOG=zeroclaw_runtime::agent=debug zeroclaw daemon

Génère :

DEBUG Tool receipt generated tool=shell receipt=zc-receipt-1774604899-fVRG...

Dans les réponses visibles par l’utilisateur

Si [agent.tool_receipts] show_in_response = true, la réponse inclut un bloc de fin :

Here's the weather in Istanbul: 16°C, sunny.

---
Tool receipts:
  weather: zc-receipt-1774608496-gzpEBuUIRYX1vd4fQl4oYkqhq4-GnoJDStmlYzvQiWA

Dans la sortie du LLM

Étant donné que le modèle voit les reçus dans son contexte, il peut les répéter lorsqu’il décrit les résultats des outils. Le détecteur de fuites est configuré pour laisser passer les jetons zc-receipt-* sans modification afin que cette répétition fonctionne. Si le runtime et le modèle incluent tous deux un bloc de reçus, l’utilisateur en voit deux : supprimez-en un via des règles de formatage spécifiques au canal.

Configuration

Propriétés de sécurité

  • Clé éphémère en mémoire. Conservée uniquement dans la portée de réception active : les chemins serveur-canal utilisent le contexte d’exécution du canal, et les chemins de tour directs utilisent une portée par tour. Jamais persistée, jamais journalisée, jamais dans le contexte du modèle. La compromission du stockage à long terme n’apporte aucun avantage.
  • Primitives MAC standard. hmac + sha2 de l’écosystème Rust.
  • Surcharge négligeable. <1 ms par appel d’outil.
  • Aucune nouvelle dépendance externe.

Quels reçus ne sont pas

  • Pas des preuves ZK. L’exécution peut vérifier les reçus car elle détient la clé. Un tiers ne peut pas le faire.
  • Non signé croisé avec le hachage de la conversation. La falsification de la conversation antérieure n’annule pas les reçus suivants (le reçu ne couvre que l’appel pour lequel il a été calculé).
  • Pas un remplacement des portes de validation. Un reçu prouve qu’un appel a eu lieu ; il ne décide pas s’il aurait dû se produire.

État actuel

FonctionnalitéStatut
Génération HMAC pour les résultats des outils réussisExpédié
Reçu ajouté au résultat d’outil réussiExpédié
Journal de débogage des reçusExpédié
show_in_responseExpédié
Instruction du système pour écho les reçusExpédié
Base de données d’audit persistante des reçusPrévu
Vérification de reçus inter-étenduesNon prévu (voir la conception des clés éphémères)

Voir aussi