Accueil / Articles

Pourquoi les journaux des agents ne suffiront pas à respecter l’article 12 : enregistrements des décisions, causalité et provenance

Les journaux des agents ne répondront pas aux exigences de l’article 12 du règlement UE sur l’IA. Enregistrez les décisions des agents IA en tant qu’objets durables dotés de liens causaux, d’une traçabilité par chaîne de hachage et de règles fixes.

Éditeur : REACTAPP.TOP14 min de lecture3244 mots

Sources: Your AI Agent Will Not Survive Article 12

En août 2026, une règle est entrée en vigueur dont la plupart des équipes d’ingénierie ne ressentent encore les effets. L’article 12 du règlement UE sur l’IA stipule que les systèmes d’IA à haut risque doivent permettre techniquement l’enregistrement automatique des événements tout au long de leur cycle de vie, avec une traçabilité proportionnelle à la finalité du système. L’article 26 impose à leur déployeur une obligation correspondante : conserver ces journaux pendant au moins six mois, ou plus si d’autres lois l’exigent.

Un ingénieur qui lit ce texte remarque un étrange déséquilibre. L’objectif est défini avec précision, tandis que le format requis n’est presque pas mentionné. L’objectif est de pouvoir reconstituer, a posteriori, une décision individuelle. Un produit APM ne le permet pas, car il a été conçu pour une autre finalité. Un stockage vectoriel non plus.

Le moment choisi est maladroit, car les agents ont tout juste cessé de répondre aux questions pour commencer à agir. Ils approuvent les dépenses, modifient la taille de l’infrastructure, gèrent les demandes d’assurance, rejetent les candidats et donnent leur accord pour les libérations. Chacune de ces actions constitue une décision que quelqu’un pourrait finir par demander à l’entreprise de justifier, et dans la plupart des organisations d’aujourd’hui, la réponse honnête serait qu’elle ne peut pas le faire.

Un dossier de petite créance qui a mis en évidence le problème

Avant l’existence de cette réglementation, et avant que les agents ne puissent agir, un tribunal en Colombie-Britannique a testé cette même faiblesse à petite échelle. C’est encore l’exemple le plus clair qui existe.

La grand-mère de Jake Moffatt est décédée en novembre 2022. Le même jour, il a visité le site web d’Air Canada pour réserver un vol pour Toronto et, ne sachant pas comment fonctionnaient les tarifs de deuil, a demandé de l’aide au chatbot de la compagnie aérienne. Le bot lui a indiqué qu’il pouvait acheter un billet au prix plein immédiatement et demander un remboursement du tarif réduit pour deuil à titre rétroactif, dans un délai de 90 jours. Avant de se fier à cela, Moffatt a fait preuve de prudence en prenant une capture d’écran de la réponse. Il a ensuite réservé son vol, a pris l’avion, a assisté aux funérailles et a déposé sa demande.

Air Canada a rejeté sa demande. Aucune telle politique n’existait, et le chatbot l’avait inventée.

La suite du récit est la partie instructive. Au Tribunal de résolution civile, la compagnie aérienne a affirmé ne pas pouvoir être tenue pour responsable des paroles de son chatbot, le décrivant comme une entité distincte responsable de ses propres actions. Le juge Christopher Rivers a qualifié cet argument de remarquable et a souligné que la compagnie aérienne n’avait jamais expliqué pourquoi quiconque devrait le accepter. Un chatbot fait partie d’un site web, et une entreprise est responsable de son site web. Air Canada a dû payer 812,02 $ canadiens.

Cette somme est négligeable. Le chiffre réellement significatif est zéro, c’est-à-dire le nombre de documents qu’Air Canada aurait pu produire pour prouver pourquoi son propre système avait émis de telles affirmations. La seule preuve durable de toute l’audience était l’écran capturé par le client.

Cette même faille a depuis évolué. En avril 2025, un bot d’assistance chez Cursor a inventé une politique limitant les abonnements à un seul appareil. Les clients ont commencé à les résilier, et l’entreprise n’en a pris connaissance que parce qu’un cofondateur a découvert le sujet sur Reddit. Le schéma est identique, la portée des conséquences plus grande et le rythme plus rapide, sans qu’aucun tribunal ne soit nécessaire.

Imaginez maintenant cette même faiblesse en matière de preuves présentée à un régulateur, associée à un agent chargé de gérer de l’argent.

Le débogage et la responsabilisation sont des tâches distinctes

La plupart des architectures ne font jamais la distinction qui importe ici.

L’observabilité est axée sur l’exécution. Elle enregistre ce qu’un processus a fait au cours d’une exécution : les périodes d’activité, les appels d’outils, le nombre de tokens, les tentatives de réessai et la latence. Elle est basée sur des échantillons, expire après une période de conservation et est limitée à une requête. C’est tout à fait adapté pour retrouver une erreur un mardi.

La responsabilisation est axée sur le domaine. Elle indique ce à quoi l’organisation est parvenue, sur quels éléments de preuve, conformément à quelle politique, et ce qui s’est passé par la suite. Elle doit survivre aux migrations de cadres, au retrait de modèles, à la reconstruction d’index, à une réorganisation ainsi qu’au départ de deux collègues.

Ailleurs au sein d’une entreprise, cela fait partie des routines. Un changement en production n’est jamais reconstruit à partir des journaux d’application, car un enregistrement de changement indique l’auteur, le ticket correspondant et l’approbation obtenue. La situation financière n’est jamais reconstruite à partir des traces HTTP, puisque des écritures comptables existent. Les commandes, les paiements, les approbations et les déploiements sont des objets durables, chacun ayant un cycle de vie, un responsable et une règle de conservation.

Les décisions prises par les agents constituent le seul type d’événement significatif qui reste encore sous forme de données non structurées, gérées au hasard.

La version de 3h du matin

Le cadre de conformité fait paraître l’échec comme quelque chose d’exotique, alors qu’il est en réalité tout à fait ordinaire ; un cas concret aide donc à comprendre la situation.

Vingt minutes après la mise en production, les vérifications de paiement commencent à retourner des erreurs 5xx. Un agent de tri compare le taux d’erreurs avec la fenêtre de déploiement, ne détecte aucune alerte liée à l’infrastructure et qualifie l’événement de régression due à la mise en production. Un agent de correction révise la mise en production, ce qui fait chuter les erreurs à des niveaux normaux. Un agent de déploiement rouvre la file d’attente des mises en production. Quelqu’un poste un signe d’approbation dans le canal dédié aux incidents, et l’équipe peut se reposer. Au total, onze minutes se sont écoulées, sans aucune revue de l’incident, car le problème semblait s’être résolu de lui-même.

Mais la mise en production n’était en aucun cas responsable. Un prestataire de paiement intermédiaire connaissait des problèmes de performance qui se sont justement résolus vers le moment du réversement de la mise en production. La corrélation entre les deux événements n’était qu’une coïncidence, et les agents ont inventé une histoire crédible pour expliquer la situation.

La vérité émerge en octobre, lorsque le même schéma se reproduit et que quelqu’un se demande si l’épisode d’avril avait la même cause racine. Il est temps d’aller vérifier.

À ce stade, les traces ont déjà vieilli et ont été supprimées en juin. Le modèle de prompt a été réécrit à deux reprises. Lorsque le modèle d’incorporation a changé, l’index de recherche a été reconstruit, de sorte que les extraits du manuel de procédures que l’agent a lus à l’époque ne sont pas ceux qu’il consulterait aujourd’hui. La version du modèle indiquée dans l’en-tête de la demande a été retirée. Ce qui reste, c’est un fil de discussion se terminant par un emoji de pouce levé.

La question ne peut pas être répondue. Personne n’a été négligent. Rien dans l’architecture n’a jamais été conçu pour conserver la réponse.

Pourquoi une meilleure mémoire ne résoudra pas le problème

L’instinct habituel est de s’emparer de la couche de mémoire, qui est généralement un stockage vectoriel. Cela traite cependant un tout autre problème.

L’incorporation dans des modèles de recherche résout bien une question : qu’est-ce qui ressemble à ceci ? Elle ne peut pas indiquer ce en quoi le système croyait à 14h02 d’une journée donnée, quelle conclusion antérieure a mené à celle-ci, d’où provient un fait particulier, ou encore si une politique visant à bloquer une action a réellement été appliquée.

Il existe également une faille plus subtile. La récupération des résultats n’est pas reproductible dans le temps. En réincorporant le corpus avec un modèle plus récent, une même requête produit un voisinage différent, ce qui signifie que ce qui était considéré comme un enregistrement a silencieusement changé. Un historique des actions qui peut être modifié sans bruit par une mise à jour d’une dépendance n’est plus un véritable historique des actions.

Chaque couche répond à sa propre question :

  • Recherche vectorielle : quels éléments ressemblent à celui-ci ?
  • Graphe de connaissances : à quoi est-il lié ?
  • Provenance : d’où provient-il, et est-il encore valide ?
  • Enregistrements des décisions : qu’a été conclu, et sur quels fondements ?
  • Liens causaux : quel événement a influencé quel autre ?
  • Règles : quelle politique dicte le résultat ?
  • État temporel : qu’est-ce qui était vrai pour le système le 14 mars ?

Une véritable enquête sur le « pourquoi a-t-il fait cela » a besoin de la plupart de ces éléments. Un seul d’entre eux constitue un stockage vectoriel.

Transformer une décision en objet

C’est là que Semantica mérite d’être examinée. Il s’agit d’une couche Python sous licence MIT qui fonctionne en dessous d’un cadre d’agents et traite le contexte comme un graphe plutôt que comme un bloc de texte. La construction de graphes, le raisonnement et l’origine des données sont déterministes, sans implication de modèles de langage grand public, ce qui est important lorsque le résultat doit servir de preuve.

L’idée principale est simple. Une décision devient quelque chose d’écrit délibérément, et non quelque chose qui est simplement enregistré dans un journal d’activités.

from semantica.context import ContextGraph

ctx = ContextGraph(advanced_analytics=True)
decision_id = ctx.record_decision(
    category="incident_triage",
    scenario="Checkout 5xx elevated 20 minutes after release 2026.31",
    reasoning="Error onset correlates with rollout window; no infra alerts; "
              "no dependency alarms visible at time of assessment",
    outcome="classified_as_release_regression",
    confidence=0.83,
)

La mécanique est simple. Ce qui change, c’est ce que l’auteur est contraint d’expliquer en détail. Au lieu de texte libre dans une ligne de journal, il faut indiquer une catégorie, un scénario, les conditions, un résultat et un chiffre de confiance. Tout cela reste consultable dix-huit mois plus tard, ce qui n’est pas le cas du texte narratif.

Considérez la chaîne de reasoning du scénario d’octobre. Elle indique qu’aucun signal d’alerte de dépendance n’était visible au moment de l’évaluation. Cette seule phrase transforme le récit d’un agent qui a commis une erreur à un agent qui a tiré une conclusion juste à partir d’un signal partiel, ce qui souligne un manque de surveillance à combler. Ces deux interprétations conduisent à des réunions très différentes.

Précaution concernant ce champ : il ne s’agit pas d’un espace pour consigner la chaîne de réflexion brute du modèle, qui est instable, parfois inventée, et impossible à défendre clause par clause devant un tiers. Il faut enregistrer les raisons pour lesquelles l’organisation est prête à en assumer la responsabilité : les preuves prises en compte, le seuil franchi, la politique appliquée, la personne qui a pris une décision contraire. Cela sert de toute façon mieux les processus de production.

Un seul enregistrement correspond à une ligne de journal, une chaîne représente l’infrastructure

Les flux de travail réels des agents se développent de manière dispersée, et les dommages se situent rarement dans un seul nœud ; ils se trouvent dans tout ce qui suit, ayant silencieusement adopté une conclusion erronée.

Le cas le plus connu avant l’ère de l’IA est Knight Capital. Une mise en œuvre erronée en 2012 a laissé en fonctionnement un chemin de code obsolète, et en l’espace d’environ 45 minutes, l’entreprise a subi une perte d’environ 440 millions de dollars. Personne ne doutait de ce que faisait le logiciel, et chaque ordre avait été enregistré. Ce que personne ne pouvait faire, c’était suivre manuellement les conséquences au fur et à mesure qu’elles se propageaient.

Semantica permet de déclarer les liens entre les décisions au moment où elles sont écrites :

triage_id = ctx.record_decision(
    category="incident_triage",
    scenario="Checkout 5xx elevated after release 2026.31",
    reasoning="Error onset correlates with rollout window; no infra alerts",
    outcome="classified_as_release_regression",
    confidence=0.83,
)

rollback_id = ctx.record_decision(
    category="remediation",
    scenario="Contain checkout 5xx",
    reasoning="Regression attributed to release; rollback is lowest-risk action",
    outcome="rolled_back_to_2026.30",
    confidence=0.91,
)
unblock_id = ctx.record_decision(
    category="release_gate",
    scenario="Resume deploy queue after containment",
    reasoning="Error rate normal for 30 minutes post-rollback",
    outcome="deploy_queue_reopened",
    confidence=0.86,
)
ctx.add_causal_relationship(triage_id, rollback_id, relationship_type="CAUSED")
ctx.add_causal_relationship(rollback_id, unblock_id, relationship_type="INFLUENCED")
why = ctx.trace_decision_chain(unblock_id)
blast_radius = ctx.analyze_decision_impact(triage_id)

Il existe trois types de relations : CAUSED, INFLUENCED et PRECEDENT_FOR. Le vocabulaire est restreint, ce qui est acceptable. Presque toutes les informations proviennent d’une seule opération.

Lorsqu’on découvre en octobre que le tri était incorrect, la question change. Demander quels enregistrements mentionnent les retours de paiement donne quarante mille lignes. Demander quelles décisions ont repris cette conclusion fournit une liste courte et limitée : le rollback, la réouverture de la file d’attente, les messages envoyés aux clients, ainsi que l’analyse post-mortem qui n’a jamais été rédigée. C’est l’ampleur des conséquences des décisions subjectives, et c’est précisément l’opération nécessaire une fois que les agents cessent de suggérer et commencent à agir.

find_similar_decisions() fonctionne dans la direction opposée et renvoie des précédents. Ce système a-t-il déjà été sollicité de cette manière, et que s’est-il passé ? C’est ainsi qu’un agent qui contredit silencieusement ses propres décisions tous les troisièmes mardis se fait découvrir, ce qui représente une véritable défaillance que personne ne surveille faute de métrique correspondante.

La causalité n’est pas la provenance

Ces deux concepts sont constamment confondus, alors qu’ils répondent à des questions distinctes. Savoir que la décision C s’appuie sur la décision A est peu utile sans connaître les preuves sur lesquelles se fondait la décision A.

Le module de provenance dans Semantica suit les normes W3C PROV-O. Il est dépassé, mais correct.

from semantica.provenance import ProvenanceManager

prov = ProvenanceManager(storage_path="./provenance.db")
prov.track_entity(
    entity_id="release_2026_31",
    source="ci/pipeline_runs/2026-31/manifest.json",
    metadata={"stage": "canary", "extractor": "ManifestParser", "confidence": 0.99},
)
lineage = prov.get_lineage("release_2026_31")

Deux choix de conception ici méritent d’être empruntés, même par des équipes qui n’installent jamais la bibliothèque.

Premièrement, les entrées de provenance forment une chaîne de hachage. Chaque enregistrement contient un numéro de séquence ainsi qu’un checksum qui le lie à celui qui le précède, et verify_chain() permet de déterminer si l’historique a été modifié ou si des entrées ont été supprimées. C’est ce qui distingue une table que n’importe qui peut modifier discrètement avec UPDATE d’un système qui fonctionne comme preuve.

Deuxièmement, les faits sont invalidés plutôt que supprimés. Lorsqu’un élément cesse d’être vrai, il est remplacé en même temps que l’acteur responsable et la raison de ce remplacement :

prov.invalidate(
    "release_2026_31",
    agent_id="human_sre_lead",
    reason="Error attribution corrected; degradation traced to payments provider",
)

Une colonne mutable valid=false indique l’état actuel. Un registre précise que l’état a déjà été différent, qui l’a modifié, quand et pourquoi. Retournez à l’audition d’Air Canada et demandez laquelle des deux informations la compagnie aérienne avait besoin. La réponse est évidente.

Retirer la politique du champ de saisie

Un autre avantage non négligeable est que Semantica intègre de véritables moteurs de raisonnement : forward chaining, Rete, Datalog et SPARQL. La logique est simple : si une organisation connaît déjà une règle, il n’y a aucune raison de demander à un modèle de langage de l’inventer en anglais à chaque demande.

from semantica.reasoning import ReteEngine, Rule, Fact, RuleType

engine = ReteEngine()
engine.build_network([
    Rule(
        rule_id="dual_control_prod",
        name="Human approval required for high blast-radius prod changes",
        conditions=[
            {"field": "env", "operator": "==", "value": "production"},
            {"field": "blast_radius", "operator": "in", "value": ["high", "critical"]},
        ],
        conclusion="require_human_approval",
        rule_type=RuleType.IMPLICATION,
    )
])
engine.add_fact(Fact("chg_014", "change", [{"env": "production", "blast_radius": "high"}]))
matches = engine.match_patterns()

Le modèle traite toujours les entrées complexes et propose une action. La différence réside dans la vérification qui approuve ou bloque cette action : il ne s’agit plus d’un paragraphe optimiste inséré dans un prompt système, que des cas limites formulés de manière étrange pourraient contourner. Étant donné que le rule_id peut être enregistré dans le registre de décision, savoir quelle politique a été appliquée devient une simple recherche, et non un débat entre deux équipes.

Une précaution : le moteur de correspondance des conditions est délibérément basique. Testez-le rigoureusement avec l’ensemble réel des règles avant de l’utiliser entre un agent et toute action réglementée. Un moteur de règles de démonstration ne constitue pas un contrôle de conformité, et découvrir les lacunes au cours d’un audit est un moyen coûteux d’en prendre conscience.

Des normes ennuyeuses survivent aux frameworks

PROV-O gère l’origine des données, la représentation RDF, les requêtes SPARQL, les contraintes SHACL et les ontologies OWL. Rien de tout cela n’est passionnant, et c’est justement l’idée.

Les frameworks d’agents ont une durée de vie d’environ dix-huit mois. Les exigences d’audit, en revanche, ne le sont pas. Un évaluateur en 2030 ne se souciera pas de savoir quelle bibliothèque d’orchestration était à la mode au moment où une décision a été prise, et personne ne souhaite conserver un enregistrement permanent dans le format propriétaire d’exportation d’une startup qui a depuis changé de direction.

Il existe également un serveur MCP, lancé via python -m semantica.mcp_server. Ses outils couvrent l’extraction, l’enregistrement des décisions, la recherche de précédents, les chaînes causales et le raisonnement. Avec cela en place, l’agent enregistre ses propres décisions grâce à une simple appel d’outil, sans avoir besoin d’ajouter de wrapper de journalisation que l’agent pourrait contourner.

Le vrai coût

Les coûts méritent d’être énoncés clairement.

Le premier coût concerne un nouveau service à état, avec ses sauvegardes, migrations, schéma et contrôle d’accès. Le deuxième est l’effort de modélisation, car personne d’autre ne peut déterminer ce qui constitue une décision digne d’être enregistrée. Enregistrer chaque étape génère un autre journal, mais à un coût plus élevé. Enregistrer trop peu laisse des lacunes dans la chaîne juste là où la question se posera.

Il existe également un coût culturel, souvent sous-estimé. Un graphe de décisions permet de voir très facilement qu’un agent effectue mal une certaine action depuis quatre mois. C’est bien là l’objectif, mais la réunion restera néanmoins inconfortable.

Le projet avance également rapidement. Fixez la version qui a été validée. Toute personne ayant besoin d’un comportement non encore publié doit installer depuis une source verrouillée sur un commit spécifique. Traitez cela comme de l’infrastructure, et jamais comme une simple importation pratique.

Commencez par un flux de travail

Grapher tout est une erreur. Graphez uniquement ce qui doit être justifié.

Choisissez un flux de travail dans lequel une mauvaise décision entraînerait des pertes financières réelles ou une perte de confiance. Enregistrez une décision uniquement aux points où une autorité est exercée, comme lors de l’approbation, du rejet, d’une dépense, d’une communication vers la production ou d’une promesse envers un client. Avec chacune d’elles, conservez les preuves sur lesquelles elle s’appuie ainsi que l’ID de la règle lorsque une politique déterministe est impliquée. Enregistrez ces données de manière asynchrone afin qu’aucune écriture ne reste bloquée sur un chemin attendu par un utilisateur.

Après trente jours, essayez de répondre à une vraie question du type « pourquoi a-t-il fait cela ? » en utilisant uniquement le graphique. Des lacunes apparaîtront immédiatement, et ce ne seront pas celles prévues. Cette exercice enseigne plus que n’importe quel travail de conception de schéma effectué à l’avance.

À retenir

Deux ans se sont écoulés à chercher une explicableté, ce qui signifiait principalement essayer de regarder à l’intérieur du modèle. Cet objectif est erroné et nous ne nous en rapprochons pas. Le modèle reste probabiliste, et aucune manipulation astucieuse ne rend ses mécanismes internes acceptables là où cela compte vraiment.

La responsabilité est l’alternative réalisable, et chacun de ses éléments se trouve en dehors du modèle : la décision, ses causes, les preuves, leur origine, la politique appliquée, les effets ultérieurs ainsi que les corrections ultérieures. Tout cela relève de données structurées ordinaires. De tels enregistrements existent depuis la comptabilité en partie double. Cette pratique a simplement disparu lorsque celui qui prend les décisions est devenu un modèle de langage, comme si cela changeait la fonction d’un enregistrement.

En supprimant le jargon juridique, la demande de l’Article 12 devient modeste. Elle ne requiert pas davantage de journaux d’activité. Elle demande simplement un registre dont le sens restera valable trois ans, même après que le cadre de référence aura été abandonné, que l’index aura été reconstruit à deux reprises et que l’auteur de la requête aura changé d’emploi.

Un test plus fiable que celui réglementaire consiste à reconsidérer l’incident de 3 heures du matin. Personne n’y était responsable. Les agents ont tiré des conclusions sensées à partir des informations dont ils disposaient et l’incident a été résolu en onze minutes, pourtant l’explication a disparu en un quart d’heure à peine. Si une journée ordinaire de mardi peut faire disparaître la capacité d’expliquer une décision, un auditeur ne sera pas le premier à s’en apercevoir. L’équipe s’en rendra compte lors d’une réunion, des mois plus tard, alors que tout le monde comprendra progressivement que personne présent ne sait ce qui s’est passé.

Ce qui a vraiment coûté cher à Air Canada, ce n’était pas 812 $ canadiens. L’entreprise est apparue lors d’une audition sans avoir rien à dire sur le fonctionnement de son propre système, se réfugiant plutôt dans l’idée que le logiciel relevait de la responsabilité d’autrui. La défense s’est effondrée, même si seul un billet et un passager endeuillé étaient concernés, un passager qui avait pensé à enregistrer la conversation.

Aujourd’hui, les agents approuvent, dépensent, déployent et rejettent plus rapidement que quiconque ne peut vérifier, et personne ne prend de captures d’écran de ces actions. Il faut d’abord établir un rapport, avant même que quiconque ne le demande. La question arrive toujours au pire moment possible, et à ce moment-là, la réponse est soit stockée dans une base de données, soit perdue définitivement.

Lectures complémentaires

  • Créer un assistant mini-IA de zéro avec des agents, des outils et des compétences — Un petit projet Python qui envoie une requête aux agents, appelle un outil de calcul et de conversion de unités, exécute une compétence de résumé et envoie la requête réorganisée à un LLM.
  • Agent Harnesses : un standard de dossier pour les compétences et les références — Comment un harness étend les compétences des agents grâce à un fichier HARNESS.md, à des dossiers de compétences et de références, à une divulgation progressive et à des layouts imbriqués pour de grands agents.
  • Pourquoi les agents à exécution prolongée ont besoin d’une machine à états : les rôles de leader, d’ouvrier et de vérificateur — Quatre manières dont un seul cycle d’agent échoue sur des tâches longues, et comment une équipe de leader, d’ouvrier et de vérificateur pilotée par un moteur déterministe les résout, ainsi que les cas où il convient de l’éviter.
  • Pourquoi les agents vocaux ont un son robotique : les cascades, la détection des tours de parole et le cadre TEN — Comment les pipelines en cascade STT-LLM-TTS créent des silences gênants et de mauvaises interruptions, les quatre types d’agents vocaux, et comment le cadre TEN gère la détection des tours de parole et les graphes parallèles.
  • Pourquoi les agents IA échouent en production : Explication de l’ingénierie Harness — Les agents s’arrêtent à 70 % lorsque la solution réside dans le système entourant le modèle. Une analyse des prompts, du contexte et de l’ingénierie Harness, des six couches de Harness ainsi que des séparations entre générateur et évaluateur.