De Jev à Darkwaar : Créer un jeu sur les décisions et l'incertitude
le 27 septembre 2026
Jev est le premier modèle System One de TypeSafe. Vous envoyez un état et un ensemble de questions saisies. Il renvoie des réponses saisies, avec des probabilités lorsque la question comporte plusieurs options. Il n'y a pas de paragraphe à analyser.
Un modèle de langage génératif se charge de l'autre étape : il poursuit le texte. Vous décidez ensuite si ce texte correspond à un nom de département, une note ou un refus. Le contrat de Jev consiste précisément à prendre cette décision : les réponses autorisées sont celles que vous avez listées, et la réponse est l'une de ces réponses, à laquelle s'ajoute une distribution.
Cette semaine, je n'ai pas intégré Jev à Darkwood Flow. J'ai profité de l'étude de cette API pour créer un petit jeu sur l'instant précédant un commit : en sais-je assez ?
Trois types de questions
L'appel HTTP est POST https://api.typesafe.ai/v1/systemone. Le corps de la requête contient le nom du modèle (jev-latest est l'alias utilisé dans l'étude), un state et une carte de questions. La documentation officielle, consultée pour l'étude le 21 septembre 2026, décrit trois types de questions.
Choix. Laquelle de ces options ? La réponse comprend le choix, les probabilités et le degré de confiance. Les probabilités correspondent à une distribution selon les critères que vous avez fournis.
Score. Où se situe la réponse dans cette grille d'évaluation ? La réponse comporte un « score », une « légende » reprenant la grille d'évaluation, des « probabilités » et un « degré de confiance ». Le score correspond à une position sur l'échelle et peut donc se situer entre deux étiquettes.
Noul. Cette affirmation est-elle vraie ? La réponse contient noul, un nombre décimal compris entre 0 et 1. Ce champ représente le degré de confiance du modèle envers l'affirmation. Il ne s'agit pas d'un booléen, et la réponse noul documentée ne comporte pas de champ de confiance distinct. Les options choice et score en comportent.
Chaque requête évalue chaque question par rapport au même état. La documentation décrit ces questions comme indépendantes : une question ultérieure nécessitant une réponse antérieure fait l’objet d’une seconde requête. Je n’ai pas mesuré cette latence ici. L’exploration non authentifiée de l’API en production, sans clé, a renvoyé une erreur HTTP 403 et une erreur d’authentification. Le champ TYPESAFE_API_KEY étant vide, cet article ne présente ni les taux de confiance, ni les taux de concordance, ni les temps de réponse en temps réel.
Les probabilités ne sont pas une garantie
La confiance, en termes de choix et de score, est un indicateur sur lequel vous pouvez vous appuyer. La forme utile dans le code de l'application est classique :
if confidence is high and the choice is billing → route
if confidence is low, or noul sits near 0.5 → escalate
Cette branche représente une politique, pas une preuve. Un niveau de confiance élevé peut tout de même concerner le mauvais service, car le chiffre décrit le degré de concentration de la distribution, et non sa conformité à la réalité. Un nombre de 0,99 appuie fortement l'affirmation. Il s'agit néanmoins d'une valeur flottante que vous avez choisi de considérer comme positive.
L'étude a conservé ces champs intacts lors de leur traitement par ObjectResult de Symfony AI. Ils n'ont pas été réduits à une valeur booléenne dans le client du modèle. Un faible niveau de confiance est une réponse acceptable ; il ne s'agit pas d'une exception.
La même étude utilise un pont de plateforme local sous /Users/math/Sites/tests/ai, avec le package symfony/ai-jev-platform et la seule capacité INPUT_TEXT. Ce pont relève de la recherche locale. Il n'a pas été soumis, fusionné ni publié dans le cadre des travaux de cette semaine. Symfony AI ne possède toujours pas de capacité de base nommée « decision », « choice » ou « noul ». OUTPUT_STRUCTURED désigne le chemin du schéma JSON de Symfony pour un modèle génératif. La structure de Jev correspond aux questions de la requête. Il s'agit de mécanismes différents, et le pont ne prétend pas le contraire.
D'une distribution à une règle de puzzle
Darkwaar15 — Signal n'appelle pas Jev. Il n'y a ni clé API, ni requête réseau, ni réponse échantillonnée. Les trois noms correspondent aux trois opérations effectuées par le joueur :
- NOUL demande si les marques éclairées sont uniformes.
- CHOIX demande quel motif listé correspond aux marques. Une marque éclairée vaut
1, une marque sombre vaut0. - SCORE demande quelle bande contient la somme des barres : basse est de 0 à 1, moyenne est de 2 à 3, haute est de 4 et plus.
Certains puits sont initialement fermés. Un carré indique l'opération en cours. Un losange représente du bruit et n'a aucune incidence sur le résultat. L'ouverture d'un puits révèle une valeur qui était fixée lors de l'enregistrement du niveau.
La confiance dans le jeu résulte de l'effondrement des réponses restantes, et non d'une probabilité calibrée :
confidence = 1 when one answer is still possible
confidence = 1 - (possible - 1) / (universe - 1) otherwise
L'univers est de 2 pour un noul, le nombre de motifs pour un choix, et de 3 pour un score. Le seuil de chaque niveau est de 0,70. Une seule réponse restante donne une confiance de 1, ce qui permet de franchir le seuil. Deux réponses restantes sur trois donnent une confiance de 0,50, ce qui ne permet pas de franchir le seuil. Le joueur peut néanmoins valider. En dessous du seuil, la console affiche « Signal insuffisant » sans révéler la valeur. Au-dessus ou au-dessus du seuil, une sortie erronée affiche la valeur et comptabilise un échec. Trois échecs réinitialisent le niveau.
Voilà la traduction. Jev peut se montrer indécis. L'énigme fait de ce refus la règle, et des puits cachés la raison pour laquelle la solution n'est pas encore unique. Le niveau 7 en est l'exemple le plus clair : deux barres suffisent pour que la somme reste élevée même si une troisième barre est encore fermée, car la hauteur fantôme de cette barre ne peut pas sortir de la bande. Ouvrir le diamant ne change rien. L'engagement intéressant est celui que vous prenez avant que tous les puits ne soient ouverts.
Les solutions sont des données, vérifiées par un script Godot sans interface graphique qui rejoue les tests scriptés pour les huit niveaux, puis simule une validation incorrecte, un rejet avec faible confiance, une erreur affichant la lecture, un redémarrage et une réinitialisation. Les deux vérifications sont validées avant l'exportation HTML5.
La console
Le jeu se trouve dans le monorepo Darkwaar sous le nom 2026-09-27-01M3H4X9806QZ6Z7D85F, avec l'ancien identifiant darkwaar15. Godot 4.6, affichage portrait 648×1152, une seule scène Control. Trois scripts assurent son fonctionnement :
rules.gdcontient les huit niveaux et la règle de confiance. Il ne génère pas de rendu. Le fichiersignal_view.gdreprésente les puits, les connecteurs et l'indicateur de confiance. La graduation sur l'indicateur correspond à la porte.main.gdest la boucle de validation : sonde, sélection, validation, échec, redémarrage et le dernier écran.
Les données d'entrée, d'évaluation et de sortie sont superposées sur la zone de travail. Les puits fermés conservent leur forme ; un losange est donc visible avant qu'une sonde ne soit utilisée. L'indicateur est cyan sous la porte et orange une fois la réponse unique. La souris et l'écran tactile permettent d'interagir avec les puits et les boutons.



L'exportation web correspond à la même scène. La commande bin/build-game darkwaar15 --validate --zip a compressé les fichiers main.gdc, rules.gdc et signal_view.gdc. La commande bin/smoke-web-build darkwaar15 a chargé le canevas en 648×1152, a trouvé une image non vide et n'a signalé aucune erreur fatale. Un clic sur « Commit » dans la fenêtre de compilation du navigateur a affiché « Choisissez une sortie » alors qu'aucune sortie n'était sélectionnée ; la boucle HTML5 reçoit donc bien des données.
Publication
L'article sur Darkwaar a été validé dans darkwaar-com à l'emplacement assets/docs/blog/2026-09-27-darkwaar15 (f4abc7d sur main) et poussé sur GitHub. J'ai tenté d'accéder à https://darkwaar.com/blog/2026-09-27-darkwaar15 et j'ai obtenu une erreur HTTP 500, ce qui correspond également à l'erreur renvoyée par le site pour un slug inexistant. Ce clone ne possède pas de dépôt distant upsun sur Git. L'interface de ligne de commande d'Upsun répond « Authentification requise » à la commande platform auth:info, ce qui empêche l'exécution de make upsun-push depuis ce dépôt. Je ne cite pas cette page telle que publiée.
Ce texte est en ligne sur blog.darkwood.com et darkwood.com/news. Les deux ont répondu HTTP 200.
Le chargement sur itch.io a échoué. La tentative de Butler d'envoyer le fichier darkwoodcom/darkwaar15:html a renvoyé l'erreur suivante : « Erreur API itch.io (400) : /wharf/builds : jeu invalide ». Une requête vers https://darkwoodcom.itch.io/darkwaar15 renvoie une erreur 404. itch.io crée une page de projet dans le tableau de bord, et non via l'API du serveur ou Butler. Cet environnement peut lister les jeux Darkwaar existants avec la clé API, mais ne peut pas créer la quinzième page. L'archive HTML5 est générée et attend cette page.
Le flux reste un pipeline
L'étude initiale s'interrogeait sur la pertinence de l'intégration de Jev à Flow. Le code, inchangé cette semaine, confirme que Flow gère déjà la prise de décision. Une tâche est représentée par JobInterface : une entrée et une valeur de retour. Le paquet est représenté par une Ip. Remplacer une tâche de règle par une tâche Jev modifie le contenu du paquet, sans toutefois affecter le pilote, la stratégie IP ni le graphe d'étapes.
Je n'ai pas ajouté Décision<T> , Choix<T> , ou un JevDriverversdarkwood/flow`. Je n'ai pas exécuté de test d'intégration Flow, ni publié de résultat. Un résultat peu fiable correspondrait à un résultat normal. L'escalade se ferait toujours via le PHP de la tâche suivante. Ce travail est reporté. Les livrables de cette semaine sont la recherche, le pont de plateforme local non soumis et Darkwood 15.
Objectif de l'expérience
Une procédure de décision se compose d'un état, d'un ensemble fermé de réponses et d'une règle définissant les conditions d'action. Jev est une version distante des deux premiers éléments, à laquelle est associée une distribution. Darkwaar15 conserve l'ensemble fermé et remplace la distribution par une information visible par le joueur : le nombre de réponses possibles restantes dans les puits fermés. La porte constitue le troisième élément, et c'est celui qui appartient à l'application.
La console refusera une pression trop précoce sur un bouton correct. C'est précisément cette propriété que je recherchais. Un niveau d'information suffisant correspond à l'état du problème, de même qu'un niveau de confiance suffisant correspond à l'état du programme ; aucun de ces chiffres ne garantit la validité de la validation.