Sur cette page
Propriété d'architecture
COADF P-1 énonce le minimum : un composant probabiliste renvoie une valeur accompagnée d'une confiance et de la méthode par laquelle la valeur a été obtenue, jamais une valeur nue, et il n'atteint jamais directement une sortie. Ce pattern porte sur la seconde moitié, celle que les mises en œuvre perdent. La réponse d'un modèle peut entrer dans le système. Ce qu'elle ne peut pas faire, c'est devenir, par quelque chemin que ce soit, ce que le système traite comme établi.
Trois conditions doivent tenir à la fois :
- La sortie a un type qui lui est propre. Une proposition et un enregistrement faisant autorité sont des types différents, des tables différentes ou des messages différents. Le code qui détient l'un ne peut pas le passer là où l'autre est attendu sans une étape explicite que quelqu'un peut relire.
- La frontière valide à l'exécution. Le contrat est vérifié à l'arrivée de la réponse, dans le processus qui la reçoit, quoi que l'expéditeur dise avoir vérifié.
- La provenance voyage avec la valeur. La méthode, le modèle et sa révision, la révision du prompt et l'emplacement dans la source restent attachés à travers chaque étape, sérialisation comprise. Sans eux, P-4 ne peut pas reconstruire et P-5 ne peut pas déclarer.
P-1 demande aussi au composant d'indiquer une confiance. La façon dont la confiance est exprimée, attribuée et utilisée relève de P-2, que COADF publie seulement comme principe. Ce Companion ne la décrit pas, et rien de ce qui suit n'en dépend.
Pourquoi elle compte
La sortie d'un modèle provient d'un processus qu'on ne peut pas reproduire à partir de ses entrées comme on reproduit la sortie d'un parseur : l'échantillonnage, les mises à jour du modèle et les changements de prompt la font tous varier. C'est acceptable pour une proposition et inacceptable pour un fait. Le risque n'est pas qu'un modèle se trompe parfois ; tout se trompe parfois. Le risque est de perdre la capacité de dire quelles valeurs viennent d'où, parce que chaque contrôle ultérieur en dépend. Une barrière n'arrête que ce qu'elle voit, une déclaration ne nomme que ce qui est marqué, et une personne qui vérifie ne contrôle que ce qui pointe encore vers sa source.
La perte est rarement une décision. Elle survient sur une ligne de code ordinaire : une entité ORM remplie à partir du JSON du modèle parce que les noms de champs se trouvaient correspondre, un modèle de réponse qui laisse tomber la provenance parce que personne ne l'y a ajoutée, un import par lots qui contourne le formulaire où vivait la validation. Le pattern existe pour qu'on ne puisse pas écrire cette ligne sans s'en apercevoir.
Stratégies de mise en œuvre valables
L'isolation est architecturale, pas nécessairement physique
Les microservices ne sont pas nécessaires. La propriété tient quand aucun chemin de code ne permet au côté probabiliste de produire un objet faisant autorité, et un seul processus peut la tenir aussi bien qu'un réseau. La topologie se choisit pour ses propres raisons (mettre à l'échelle séparément une charge d'inférence, isoler la bibliothèque cliente d'un fournisseur, déployer indépendamment), puis la frontière se construit dans la forme choisie. Les quatre figures ci-dessous tiennent la même propriété sous quatre formes.
Architecture d'exemple · Non normative
Monolithe modulaire
- Flux de données dans cet exemple
- Facultatif : peut être indisponible
Description textuelle. Dans un seul processus : un client de modèle, marqué comme probabiliste, transmet sa réponse brute à un module frontière, qui transmet une proposition (Proposal) au cœur du domaine. Un adaptateur de classification, lui aussi dans le processus, peut transmettre un enrichissement au cœur du domaine, et il interroge un service de classification externe situé hors du processus. Le cœur du domaine écrit des entrées dans la piste d'audit, une base de données distincte. Le seul chemin du client de modèle vers le cœur du domaine passe par la frontière.
Architecture d'exemple · Non normative
Orienté services
- Flux de données dans cet exemple
- Facultatif : peut être indisponible
Description textuelle. Un service d'inférence contient un client de modèle, marqué comme probabiliste, et une frontière qui valide ce qu'elle envoie. Il envoie, via HTTP et selon un contrat JSON, à un service de domaine, dont la propre frontière valide ce qu'elle reçoit et transmet une proposition (Proposal) au cœur du domaine. Le service de domaine contient aussi un adaptateur de classification, qui interroge un service externe et peut transmettre un enrichissement au cœur du domaine, ainsi que la piste d'audit, dans laquelle le cœur du domaine écrit des entrées. Le service d'inférence n'a aucune connexion à la piste d'audit.
Architecture d'exemple · Non normative
Piloté par les événements
- Flux de données dans cet exemple
Description textuelle. Un worker d'inférence contient un client de modèle, marqué comme probabiliste, et une frontière. Il publie un message dans un topic de propositions. Le corps du message porte le trace_id d'audit ; ses en-têtes portent le contexte d'exécution sous la forme d'un traceparent W3C. Un consommateur de domaine lit le topic, valide chaque message à la réception et transmet une proposition (Proposal) au cœur du domaine, qui écrit des entrées dans la piste d'audit.
Architecture d'exemple · Non normative
Déploiement cloud-native
- Flux de données dans cet exemple
Description textuelle. Dans le namespace d'inférence, un pod d'inférence exécute le client de modèle et sa frontière. Il peut envoyer vers l'endpoint du modèle, dans le namespace de service du modèle, et vers le point d'entrée des propositions, dans le namespace des enregistrements, uniquement sur son port d'API. Le point d'entrée des propositions valide ce qu'il reçoit et transmet une proposition (Proposal) au cœur du domaine, qui écrit dans la piste d'audit et la base des enregistrements. Une note indique que le namespace d'inférence n'a ni route ni identifiant d'accès vers cette base de données.
Les quatre mêmes éléments dans chaque topologie
- Des types distincts. Le côté probabiliste peut construire une proposition et rien d'autre. L'enregistrement faisant autorité n'est construit que par le code du domaine, à partir d'une proposition et de quelque chose que le côté probabiliste ne peut pas produire.
- Une validation à l'exécution sur chaque côté récepteur. Un schéma strict : champs non déclarés rejetés, aucune coercition de type, vocabulaires fermés pour les valeurs catégorielles, longueurs bornées. Dans une topologie distribuée, chaque récepteur valide, côté domaine compris quand l'expéditeur est un service maison : « nous avons validé avant l'envoi » est une affirmation sur un autre déployable, peut-être sur une version plus ancienne de celui-ci.
- La provenance dans le contrat. Déclarée, obligatoire, et préservée par chaque mapper et chaque sérialiseur en sortie.
- Une étape de promotion explicite. La seule voie d'une proposition vers un enregistrement faisant autorité est une fonction, un endpoint ou une commande qui exige ce que le côté probabiliste ne peut pas fournir. Pour les valeurs dérivées par un modèle de langue, le fence public F-03 de COADF exige une vérification humaine avant qu'elles n'atteignent une sortie publiée. La promotion est elle-même consignée dans la piste d'audit.
Une validation déterministe autour d'une sortie stochastique
Les contrôles qui ne lisent que la valeur (sa forme, son vocabulaire, son unité, les plages que le domaine définit lui-même) s'exécutent avant que quiconque la regarde. Ils ne rendent pas une valeur vraie. Ils rendent impossible une valeur impossible, et ils le font chaque fois de la même façon, ce sur quoi la personne qui vérifie la valeur peut compter.
Options d'application, de la plus faible à la plus forte
| Mécanisme | Arrête | N'arrête pas |
|---|---|---|
| Séparation des types | La promotion accidentelle dans du code qui passe la vérification de types | Les casts, le typage dynamique, la réflexion |
| Validation de schéma à l'exécution | Les réponses mal formées ou qui revendiquent trop | Une valeur bien formée mais fausse |
| Test d'architecture sur les imports | Le module probabiliste qui importe du code écrivant les enregistrements | Les imports faits par nom à l'exécution ; les chemins de données qui ne sont pas des imports |
| Droits de base de données par composant | Un composant qui écrit dans le stockage faisant autorité | Les composants qui partagent des identifiants |
| Déploiement séparé et politique réseau | Un composant déployé séparément qui atteint le stockage, de quelque façon que ce soit | Tout ce qui se trouve dans un même processus ; les clusters dont le plugin réseau n'applique pas la politique |
La plupart des systèmes veulent toujours les trois premiers, et les deux derniers quand le composant probabiliste est déployé à part. Le profil Python et FastAPI met en œuvre les trois premiers ; le profil Cloud-native ajoute les deux derniers.
Modes de défaillance
La sortie du modèle écrite directement dans le stockage faisant autorité
La réponse est analysée dans l'entité que le reste du système lit comme un fait, parce que les champs correspondaient. Aucune ligne n'est fausse prise isolément. La frontière n'existe tout simplement pas.
Des propositions impossibles à distinguer des données vérifiées
Une table, un type, et un indicateur qui dit lequel est lequel. L'indicateur prend par défaut la valeur commode, ou bien le JSON du modèle lui-même peut le positionner.
La provenance perdue en sortie
Un mapper, un modèle de réponse ou un export omet la méthode et la source. Les frameworks qui filtrent la sortie selon un schéma déclaré le font sans bruit : FastAPI filtre une réponse selon son modèle de réponse, et le sérialiseur de Fastify laisse de côté les propriétés qu'un schéma de réponse ne liste pas, sauf si le schéma autorise des propriétés supplémentaires.
Une validation seulement dans l'interface utilisateur
Le formulaire valide. L'import par lots, le script d'administration et le second client, non.
Les systèmes en aval ne distinguent pas l'inféré du vérifié
Le format d'export n'a pas de champ pour la méthode : le système suivant reçoit une valeur et rien d'autre, et chacun de ses contrôles est aveugle à la différence.
La réponse brute crue sans validation à l'exécution
Un cast en TypeScript, un dictionnaire en Python, un mapper permissif en Java : le contrat existe dans les types du code et nulle part dans son comportement. Les annotations de type de TypeScript sont effacées à la compilation, et ses typages standard déclarent le résultat de
JSON.parsecommeany.La coercition masque l'erreur du modèle
Une analyse laxiste transforme
"12"en12et"true"enTrue. Le nombre a l'air juste et n'a jamais été un nombre. Le mode strict de Pydantic refuse cette coercition ; la configuration par défaut du validateur de Fastify active la coercition de types. Il faut savoir lequel des deux est en service.La réparation silencieuse
La réponse échoue à la validation, alors l'appel est relancé jusqu'à ce qu'une réponse passe, et seule la dernière est conservée. Les échecs étaient des éléments de preuve sur le modèle, et ils ont disparu.
La validation contournée pour la vitesse
Une optimisation qui construit des objets sans validation devient le chemin le plus facile, puis le contournement.
model_constructde Pydantic crée un modèle sans le valider, exactement comme documenté.
Vérification
Test unitaire
Réussit quand : Chaque réponse mal formée est rejetée : un champ non déclaré, un type coercible, une provenance manquante, un attribut que personne n'a demandé.
Preuve de dents : Supprimer une contrainte dans une copie jetable (par exemple la règle qui rejette les champs non déclarés) : le test correspondant doit échouer.
Test d'architecture
Réussit quand : Le module probabiliste n'a aucun chemin d'import vers le code qui écrit les enregistrements faisant autorité.
Preuve de dents : Ajouter l'import interdit dans une copie jetable : le contrat échoue et le build s'arrête.
Test de contrat
Réussit quand : Producteur et consommateur s'accordent sur le schéma de proposition, avec les champs de provenance obligatoires.
Preuve de dents : Retirer un champ de provenance du schéma du producteur : le test de contrat du consommateur échoue.
Test d'intégration
Réussit quand : Quand le composant probabiliste est déployé à part, ses identifiants ne peuvent pas écrire dans le stockage faisant autorité.
Preuve de dents : Tenter l'écriture avec ces identifiants : la base de données la refuse.
Test de bout en bout
Réussit quand : Une sortie dont un attribut provient d'une proposition que personne n'a vérifiée ne peut pas être publiée.
Preuve de dents : Planter une proposition non vérifiée et demander la publication : refus, et le refus figure dans la piste d'audit.
Réalisations alternatives
- Des interfaces de suggestion seule. La sortie du modèle est montrée à une personne comme une suggestion et n'est jamais stockée comme valeur. Simple et solide ; on renonce à la traçabilité de ce qui a été suggéré, à moins de consigner aussi les suggestions.
- Un contrat partagé, neutre vis-à-vis du langage (JSON Schema, Protocol Buffers), au lieu de modèles natifs du langage. Mieux entre langages ; la question de la rigueur se déplace dans le schéma, où les propriétés supplémentaires et les formats doivent être décidés explicitement.
- Les modes de sortie structurée des API de modèles. Ils réduisent les réponses mal formées côté producteur. Ils ne dispensent pas le consommateur de valider, parce que le consommateur ne peut pas vérifier comment le producteur a été configuré.
- Un stockage séparé pour les propositions et les enregistrements, deux tables ou deux stockages plutôt qu'une table avec un statut. Plus lourd, et la séparation devient visible dans chaque requête que quiconque écrit.
Limites
- La frontière contrôle où la sortie d'un modèle peut aller. Elle ne rend pas cette sortie correcte, et une valeur bien formée mais fausse passe chaque contrôle de ce pattern.
- La séparation des types arrête les accidents, pas l'intention. Un développeur disposant d'un accès en écriture au domaine peut construire à la main un objet faisant autorité ; ce sont la revue de code et la piste d'audit qui rendent cela visible.
- Ce pattern ne dit pas quand une proposition a besoin d'une personne, ni comment la confiance est exprimée. Cela relève de P-2 et de P-3, que COADF publie seulement comme principes.
- La validation ne vaut que ce que vaut le contrat. Un schéma permissif, validé strictement, reste permissif.
Sources
- FastAPI: Response model: return type and data filtering · documentation officielle · FastAPI 0.141 · Vérifié le 2026-09-11
- Fastify: fast-json-stringify: additionalProperties · dépôt du projet · Vérifié le 2026-09-11
- TypeScript: The Basics: erased types · documentation officielle · Vérifié le 2026-09-11
- TypeScript: lib.es5.d.ts: JSON.parse · dépôt du projet · Vérifié le 2026-09-11
- Pydantic: Strict mode · documentation officielle · Pydantic 2.13 · Vérifié le 2026-09-11
- Fastify: Validation and serialization · documentation officielle · Fastify 5.12 · Vérifié le 2026-09-11
- Pydantic: Models: creating models without validation · documentation officielle · Pydantic 2.13 · Vérifié le 2026-09-11
