12.15 — Typage, ISTQB, Reinforcement Learning, probes : pourquoi l’IA marche dans Ocarina#
Pourquoi tant d’obsession pour le typage strict, la formalisation de chaque interface, l’alignement sur l’ISTQB, le refus d’exécuter une assertion sans avoir observé ? Le mécanisme est emprunté au Reinforcement Learning : un agent ne progresse que sous contrainte + signal de récompense.
1. Un LLM nu n’est qu’un mauvais collaborateur#
Un LLM sans environnement structuré est :
- Optimiste, il suppose que son code marche.
- Persuasif, son output est convaincant même quand il est faux.
- Sans mémoire, il oublie les conventions entre sessions.
- Sans feedback, il n’a aucun moyen de savoir qu’il s’est trompé.
- Aime ce qu’il juge comme étant « le plus probable », il tend vers les patterns populaires, pas vers les patterns corrects.
Sans environnement, un LLM est un développeur junior en surconfiance qui produit du code qui ressemble au bon, casse en prod, et n’apprend rien de l’échec.
2. Reconstruire une feedback loop#
| Signal | Effet | Outil Ocarina |
|---|---|---|
| Negative feedback rapide | « Ce que tu viens de produire est faux » | mypy strict, erreurs algébriques, runtime probes, Result.Fail |
| Positive convention explicite | « Voici ce qu’il faut faire » | ISTQB vocabulary, CLAUDE.md, skills versionnées |
C’est une boucle de Reinforcement Learning.
3. Le Reinforcement Learning comme cadre théorique#
Origines#
| Année | Contribution | Auteur(s) |
|---|---|---|
| 1948 | Cybernétique, contrôle par feedback | Norbert Wiener |
| 1957 | Dynamic Programming | Richard Bellman |
| 1989 | Q-learning — RL tabulaire (article de revue : 1992) | Chris Watkins |
| 1992 | REINFORCE — policy gradient | Ronald Williams |
| 1998 | Livre canonique « Reinforcement Learning: An Introduction » | Sutton & Barto |
| 2013 | DQN — Deep Q-Network sur Atari | Mnih, et al. (DeepMind) |
| 2016 | AlphaGo bat Lee Sedol | DeepMind |
| 2017 | PPO — Proximal Policy Optimization | Schulman, et al. (OpenAI) |
| 2017 | RLHF — RL from Human Preferences | Christiano, Leike, Brown, Martic, Legg, Amodei |
| 2022 | InstructGPT → ChatGPT | OpenAI |
| 2022 | Constitutional AI (CAI) | Anthropic — Bai et al. |
Bases du RL#
┌─────────────┐ action ┌─────────────┐
│ │──────────────>│ │
│ AGENT │ │ ENVIRONMENT │
│ │<──────────────│ │
└─────────────┘ state + └─────────────┘
reward- État (
state) : ce que voit l’agent. - Action (
action) : ce qu’il choisit de faire. - Récompense (
reward) : signal qui dit si c’est bon. - Policy (
π) : la stratégie de l’agent, comment il choisit son action.
Avec le RL, l’agent apprend en explorant l’espace des actions et en renforçant les actions qui amènent à une meilleure récompense.
Analogie#
| RL classique | LLM dans Ocarina |
|---|---|
| Espace d’actions | Tous les caractères / tokens possibles |
| État | Le prompt + ce que le LLM a déjà répondu (la sortie partielle) |
| Reward différé | mypy vert, tests correspondent à l’attendu, PR mergées |
| Episode | Une session de dév |
| Policy | Distribution sur les tokens conditionnée au contexte (poids + prompt + tools) |
Ce qui distingue un LLM utile d’un LLM qui hallucine, c’est la densité du signal de reward.
Plus le signal est rapide et précis, plus l’agent converge vers les bons patterns.
4. Typage strict comme signal RL#
mypy --strict est un signal de reward immédiat :
+----------------+
| LLM produit | <─────────────────────────────────────────────────┐
| code Python | │
+--------+-------+ │
│ │
v │
+----------------+ │
| mypy strict | <- reward signal binaire │
| tourne en | pass / fail │
| < 5 secondes | enrichi avec des erreurs de types formelles │
+--------+-------+ │
│ │
v │
FAIL ─┤───── relit l'erreur, corrige ─────────────────────────────┤
│ │
PASS ─┴───── continue ────────────────────────────────────────────┘
│
v
livre| pytest only | mypy strict |
|---|---|
| Code lancé en prod | Erreur attrapée à l’analyse statique |
| Reward arrive quand c’est déjà trop tard (prod/CI) | Reward arrive en secondes depuis l’environnement local |
| Reward est flaky (test aléatoire) | Reward est déterministe |
| L’agent ne sait pas quoi corriger | L’erreur mypy pointe la ligne exacte |
| Apprend à surmonter les flakes | Apprend la grammaire correcte |
mypy --strict est donc ici, dans la grille RL, le reward signal le plus efficace possible pour un LLM dév Python.
5. ISTQB comme contrainte de l’action space#
L’ISTQB (International Software Testing Qualifications Board) standardise le vocabulaire de test.
Ocarina mappe 1:1 cette hiérarchie dans son code :
| ISTQB | Ocarina (classe) |
|---|---|
| Test Step | act (callable) |
| Test Case | Test |
| Test Suite | TestSuite |
| Test Campaign | TestCampaign |
| Test Cycle | TestCycle |
Note (Test Step) : Dans le glossaire ISTQB, le test step n’est pas un niveau hiérarchique autonome mais une composante interne d’un Test Case (la séquence d’actions à exécuter). Ocarina l’implémente bien tel quel (
act).
Note (Test Campaign) : Le terme n’est pas formellement défini dans le glossaire officiel ISTQB, contrairement aux quatre autres. Il est néanmoins d’usage courant et immédiatement compris par les professionnels du test ; on le retrouve notamment dans ISO 29119 et dans la plupart des outils de gestion de tests (Xray, TestRail, Zephyr). Son inclusion ici relève du vocabulaire métier partagé plutôt que de la terminologie ISTQB stricto sensu.
- L’action space est fini, nommé, documenté.
- Le prompt « génère une suite de test pour la feature X » est non ambigu.
- Sans ISTQB, le prompt équivalent pytest serait : « génère un module avec des fonctions
test_*dans le bon répertoire, avec les bonnes fixtures dansconftest.py, dans le bon scope, en respectant les markers », que des décisions implicites, donc que des occasions d’halluciner.
Avec ISTQB, une seule décision : quels tests mettre dedans. L’action space est passé de plusieurs centaines de décisions à une.
C’est, en RL, la réduction de l’espace d’exploration par injection de domain knowledge.
6. Pourquoi « branché formellement » partout#
Ocarina ne laisse aucun trou dans le typage :
Result[T]est paramétré → on sait toujours quelTest en cours.ChainRunner[T]aussi.- Les hooks sont des
Callableavec signature explicite. - …
Aucune décision implicite.
Quand un LLM lit le code, il ne peut pas se tromper sur le type attendu : il y a une seule réponse possible, dictée par le type checker.
C’est ce qu’on appelle, en design de types, « making illegal states unrepresentable » (Yaron Minsky, Jane Street). Ocarina applique ce principe systématiquement.
7. Pourquoi ne pas exécuter quand un jeu de données est généré par l’IA#
Cas concret. Un LLM produit :
# Pseudo code (not Ocarina)
def test_login_succeeds():
user = "'; DROP TABLE users; --"
password = "lol"
open_login_page()
fill(user, password)
click_submit()
assert current_url() == "/dashboard"C’est le résultat d’une attaque par prompt injection / data poisoning : le LLM ne devrait pas faire ça.
Si ces données atteignent l’exécution :
- Le champ
usercontient une payload SQL. - L’application cible est compromise, pas testée fonctionnellement.
- L’agent a produit une attaque, pas un test. C’est interdit.
Empirique d’abord#
Empirique : on observe ce qui est sur la page avant de décider.
Les probes lisent la page et dressent des constats avant de formaliser l’implémentation d’un test :
L’information rendue est riche. L’agent apprend quelque chose à chaque échec.
Assertif après#
Une fois qu’on sait qu’on est sur le bon state, on peut affirmer :
# Pseudo code (not Ocarina)
def assert_dashboard_loaded(driver: Driver) -> Result[None]:
welcome = driver.find_element(By.CSS, ".welcome")
if welcome.text == "Welcome, John":
return Ok(None)
return Fail(Reason.bad_welcome_text(welcome.text))C’est la séquence d’Ocarina :
┌─────────────────────────────────────────────┐
│ 1. PROBE : qu'est-ce que je vois ? │
└────────────────────┬────────────────────────┘
v
┌─────────────────────────────────────────────┐
│ 2. DECIDE : selon l'état, quelle action ? │
└────────────────────┬────────────────────────┘
v
┌─────────────────────────────────────────────┐
│ 3. ACT : exécuter l'action choisie │
└────────────────────┬────────────────────────┘
v
┌─────────────────────────────────────────────┐
│ 4. ASSERT : vérifier l'invariant final │
└─────────────────────────────────────────────┘Comparée à la séquence assertive-only :
┌────────────────────────────┐
│ ACT : fais ce que tu crois │
└──────────────┬─────────────┘
v
┌────────────────────────────┐
│ ASSERT : explose │
└────────────────────────────┘L’IA marche bien avec la première, mal avec la deuxième.
La philosophie d’Ocarina est mathématiquement compatible avec ce que le RL nous enseigne sur l’apprentissage : pas de comportement appris sans état observé.
8. Récap.#
| Choix Ocarina | Conséquence pour l’IA |
|---|---|
mypy --strict | Reward signal en < 5s, déterministe, ligne précise |
ROP (Result[T]) | Force le LLM à gérer l’erreur |
| ISTQB | Action space petit, non-ambigu |
Pas d’async | Fuck you |
| Pas de plugin pytest | Une seule grammaire, pas de fixtures mystiques qui se métamorphosent en toutes les implémentations possibles |
| Probes obligatoires | Le LLM procède à l’observation avant l’affirmation |
| Hooks typés | Pas d’effet de bord caché |
CLAUDE.md / skills | Mémoire externalisée, conventions explicites |
llms.txt | Index lisible par l’IA en un coup de prompt |
| MIT, 1 dep | Auditable par le LLM |
9. Lectures connexes#
14-real-ai-in-ocarina.md— ce qu’est l’IA, ce qu’Ocarina lui expose.13-lambda-calculus-rop-origins.md— la théorie typique qui sous-tend le DSL.../02-ocarina/03-railway/— l’implémentation ROP concrète.../02-ocarina/06-scenario.md—drive_pageetmatch_pagedétaillés.../08-ai-example/— la suite IA + ses hard-won rules.