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#

SignalEffetOutil 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éeContributionAuteur(s)
1948Cybernétique, contrôle par feedbackNorbert Wiener
1957Dynamic ProgrammingRichard Bellman
1989Q-learning — RL tabulaire (article de revue : 1992)Chris Watkins
1992REINFORCE — policy gradientRonald Williams
1998Livre canonique « Reinforcement Learning: An Introduction »Sutton & Barto
2013DQN — Deep Q-Network sur AtariMnih, et al. (DeepMind)
2016AlphaGo bat Lee SedolDeepMind
2017PPO — Proximal Policy OptimizationSchulman, et al. (OpenAI)
2017RLHF — RL from Human PreferencesChristiano, Leike, Brown, Martic, Legg, Amodei
2022InstructGPT → ChatGPTOpenAI
2022Constitutional 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 classiqueLLM dans Ocarina
Espace d’actionsTous les caractères / tokens possibles
ÉtatLe 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
EpisodeUne session de dév
PolicyDistribution 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 onlymypy strict
Code lancé en prodErreur 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 corrigerL’erreur mypy pointe la ligne exacte
Apprend à surmonter les flakesApprend 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 :

ISTQBOcarina (classe)
Test Stepact (callable)
Test CaseTest
Test SuiteTestSuite
Test CampaignTestCampaign
Test CycleTestCycle

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 dans conftest.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 quel T est en cours.
  • ChainRunner[T] aussi.
  • Les hooks sont des Callable avec 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 injectiondata poisoning : le LLM ne devrait pas faire ça.
Si ces données atteignent l’exécution :

  • Le champ user contient 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 OcarinaConséquence pour l’IA
mypy --strictReward signal en < 5s, déterministe, ligne précise
ROP (Result[T])Force le LLM à gérer l’erreur
ISTQBAction space petit, non-ambigu
Pas d’asyncFuck you
Pas de plugin pytestUne seule grammaire, pas de fixtures mystiques qui se métamorphosent en toutes les implémentations possibles
Probes obligatoiresLe LLM procède à l’observation avant l’affirmation
Hooks typésPas d’effet de bord caché
CLAUDE.md / skillsMémoire externalisée, conventions explicites
llms.txtIndex lisible par l’IA en un coup de prompt
MIT, 1 depAuditable par le LLM

9. Lectures connexes#