Comment fonctionne un LLM (bonus 6) : pourquoi les LLM sont si bons avec le code informatique ?

Un atelier pastel : une rangée de tuiles avec des coches et des croix, et une personne devant son écran (illustration pixel art). Nov 8, 2026

Comment fonctionne un LLM (bonus 6) : pourquoi les LLM sont si bons avec le code informatique ?

Qui a écrit cette série ?

Cette série a été pilotée, critiquée et validée par Alex, qui en a fixé le plan, les règles de sources et le ton. Les textes ont été rédigés en co-écriture avec Claude (Anthropic) : Claude a proposé des plans, des calculs, des sources et des formulations, qu’Alex a relus, corrigés ou refusés. Alex est responsable du contenu publié.

Les modèles de langage produisent souvent du code qui passe des tests. Pourquoi ? Le code suit des règles strictes, et un programme peut être exécuté pour vérifier sa réponse. Mais un test réussi ne prouve pas qu’un programme répond au besoin. Ce bonus explique ces deux faces et mesure ce qu’on peut mesurer.

Ce bonus s’appuie sur trois notions de la série : la prédiction du token suivant (épisode 1), l’attention (épisode 3) et le réglage après pré-entraînement (épisode 6).

La réponse tient à deux propriétés du code, sa régularité et la possibilité de l’exécuter, et à des mesures qui ont leurs limites. Ce bonus ne prédit pas l’avenir du métier de développeur : il dit ce qui est mesuré.

Ce que vous saurez à la fin

  • Expliquer pourquoi le code est un texte plus régulier que la plupart des textes, et pourquoi cela aide la prédiction du token suivant.
  • Distinguer ce qu’un test vérifie (un comportement, sur quelques cas) de ce qu’il ne vérifie pas (le besoin réel).
  • Lire un score d’évaluation de code (HumanEval, MBPP, EvalPlus, SWE-bench) : ce qu’il mesure, ce qu’il ne mesure pas.
  • Calculer un pass@k à partir de n essais et de c réussites.
  • Repérer les erreurs typiques : code plausible mais faux, API inventée, paquet inventé, faille, test complaisant.
  • Classer une affirmation sur les agents de code en fait publié, tendance observée, annonce ou espérance.

1. Pourquoi le code est favorable aux modèles

Une syntaxe régulière

Le code a une syntaxe stricte. Une parenthèse ouverte attend sa fermeture, def est suivi d’un nom, for x in est suivi d’un ensemble à parcourir. Dans un programme, beaucoup de suites de tokens sont impossibles ; parmi celles qui restent valides, quelques-unes sont très fréquentes.

Conséquence possible : la distribution de probabilité du token suivant peut être très concentrée, et la perte reste faible quand le modèle suit l’usage courant. Un exemple, avec des probabilités inventées, montre le mécanisme (figure 1).

def somme(liste):
    total = 0
    for x in liste:
        total += x
    return total

Dans la boucle, après « total », le modèle doit choisir le token suivant. Les probabilités de la figure 1 sont fictives : elles illustrent le calcul, elles ne viennent d’aucun modèle réel.

Probabilités fictives du token suivantQuatre barres : « += » 0,90 ; « = » 0,06 ; « . » 0,02 ; autres 0,02. Valeurs inventées pour l'exemple.Token suivant après « total » (probabilités fictives)« += »0,90 (fictif)« = »0,06 (fictif)« . »0,02 (fictif)autres0,02 (fictif)Exemple inventé : aucune de ces valeurs ne vient d'un modèle réel.
Figure 1. Probabilités du token suivant après « total » dans la boucle. Valeurs fictives : elles illustrent le calcul de la perte, elles ne viennent d’aucun modèle.

Sur ces valeurs fictives, si le token réel est « += », la perte vaut −ln(0,90) ≈ 0,11. S’il est « = » (valide, mais moins courant ici), elle vaut −ln(0,06) ≈ 2,81. Le rapport des deux probabilités est de 15 contre 1.

Ce calcul ne prouve aucune compréhension : il mesure un écart de probabilité. Régulier ne veut pas dire simple. Un programme de dix lignes peut contenir un choix de conception difficile, que la régularité de la syntaxe ne révèle pas.

Un corpus abondant

Fait publié : le corpus The Stack (Kocetkov et al., 2022, arXiv 2211.15533) rassemble 3 To de code sous licence permissive (chiffre du titre ; le corps de l’article indique 3,1 To, et la version 1.1, citée à l’épisode 1, 6 To).

Fait publié : Llama 3 (Grattafiori et al., 2024, arXiv 2407.21783, section 3.1.2) indique que 17 % des tokens de son mélange d’entraînement sont du code. Ce chiffre vaut pour ce mélange seul et ne se transpose pas à tous les modèles.

Un signal de vérification

La prose se juge par lecture. Le code se juge aussi par exécution : un compilateur ou un interpréteur signale les erreurs de syntaxe, et des tests unitaires, c’est-à-dire des vérifications automatiques sur quelques cas, disent si une fonction renvoie la bonne valeur pour ces cas. Ce signal sert à l’entraînement et à l’évaluation.

Boucle générer, exécuter, garder ou corrigerTrois étapes en ligne : générer, exécuter les tests, garder ou corriger. Une flèche en pointillés revient de la dernière étape à la première quand le code échoue.GénérerExécuter les testsGarder ou corrigersi le code échoue : nouvelle tentative ou correction
Figure 2. Boucle de génération vérifiée. Schéma sans chiffres : il décrit le principe, pas une performance mesurée.

Fait publié : AlphaCode (Li et al., 2022, revue Science, DOI 10.1126/science.abq1158 ; préprint arXiv 2203.07814) produit beaucoup de programmes pour un même problème de concours, écarte ceux qui échouent aux tests d’exemple, regroupe les restes selon leur comportement et en soumet un petit nombre. En évaluation simulée sur des concours Codeforces, il se classe en moyenne dans le top 54,3 %.

Fait publié : CodeRL (Le et al., 2022, arXiv 2207.01780) utilise des signaux liés aux tests unitaires pour entraîner la génération de code par apprentissage par renforcement. Le détail de la récompense est décrit dans l’article.

Fait publié : RLEF (Gehring et al., 2024, arXiv 2410.02089, ICML 2025) utilise les tests publics pendant l’essai et des tests privés pour la récompense. Les gains sont mesurés sur des problèmes de concours, avec un transfert déclaré vers HumanEval+ et MBPP+.

La différence avec l’épisode 6 : le réglage par retours humains s’appuie sur des jugements de personnes ; ici, quand un test existe, le signal est automatique. Les deux ne se confondent pas.

2. Pourquoi Rust est un cas à part

Rust ajoute au compilateur des règles sur la mémoire, la durée de vie des références et les emprunts. Un programme qui les viole ne compile pas. Pour un modèle, l’erreur de compilation est précise et immédiate, mais la liste des règles à respecter est plus longue que dans un langage plus permissif.

Infrastructure : le compilateur et les outils de test existent, donc la boucle de la figure 2 s’applique à Rust comme à Python. La difficulté tient aux règles du langage, pas à l’outillage.

Fait publié : MultiPL-E (Cassano et al., 2022, arXiv 2208.08227) traduit HumanEval et MBPP en plusieurs langages, dont Rust, avec des tests traduits pour chacun. Le protocole est le même pour tous les langages ; les scores par langage se lisent dans l’article et ne sont pas repris ici.

Hypothèse, non mesurée ici : un compilateur strict donne des erreurs précises, qui pourraient rendre l’apprentissage par retour vérifiable plus net en Rust qu’en Python. La contrepartie : il rejette davantage de programmes, donc un modèle doit respecter plus de contraintes à la fois. Vérifier cette hypothèse demanderait une mesure à protocole identique pour chaque langage.

Un score en Rust dépend du benchmark et du protocole : une comparaison entre langages n’a de sens que si les tests et le nombre d’essais sont les mêmes.

3. Mesurer : HumanEval, MBPP, EvalPlus, SWE-bench

Fait publié : HumanEval (Chen et al., 2021, arXiv 2107.03374) regroupe des problèmes de programmation en Python, écrits à la main, avec une description et des tests unitaires. Selon l’article de 2021, Codex résout 28,8 % des problèmes au premier essai ; avec 100 échantillons par problème, la part de problèmes pour lesquels au moins une solution passe les tests atteint 70,2 %.

La métrique est pass@k : la probabilité qu’au moins une des k solutions tirées passe les tests. Avec n essais dont c réussissent, l’article utilise un estimateur non biaisé :

def pass_at_k(n, c, k):
    if n - c < k:
        return 1.0
    return 1 - math.comb(n - c, k) / math.comb(n, k)

Exemple inventé : n = 10 essais, c = 3 réussites. pass@1 = 0,30 ; pass@5 ≈ 0,917. Le même modèle paraît meilleur quand on lui laisse plusieurs essais : il faut toujours demander quel k.

MBPP (Austin et al., 2021, « Program Synthesis with Large Language Models », arXiv 2108.07732) propose de petits problèmes en Python, vérifiés par des tests. Il partage les limites de HumanEval : tâches courtes, couverture de tests limitée, contamination possible.

Fait publié : Liu et al. (2023, « Is Your Code Generated by ChatGPT Really Correct? », arXiv 2305.01210, EvalPlus) multiplient par 80 le nombre de tests de HumanEval. Avec ces tests renforcés, la baisse du pass@k peut atteindre 19,3 à 28,9 % (version 3 de l’article, 26 modèles). Un test trop faible valide donc du code faux : c’est le test complaisant.

Fait publié : SWE-bench (Jimenez et al., arXiv 2310.06770, ICLR 2024) contient 2 294 problèmes tirés de vraies issues de 12 dépôts Python. Le modèle doit modifier le dépôt pour que les tests réussissent. C’est plus proche d’un travail de maintenance qu’un problème isolé, mais ce n’est pas une mesure du travail réel d’un développeur : les tâches sont choisies et bornées, et leur formulation compte.

Annonce : un billet d’OpenAI de février 2026 (texte du billet) sur SWE-bench Verified indique que l’entreprise ne publie plus de scores, car des modèles ont pu voir les solutions pendant leur entraînement. C’est l’affirmation de l’entreprise, non reproduite ici par un tiers.

BenchmarkCe qu’il mesureTailleLimite principale
HumanEval (2021)Fonctions Python à partir d’une description, vérifiées par des tests unitairesProblèmes écrits à la mainTâches courtes ; contamination possible
MBPP (2021)Petits programmes Python, vérifiés par des testsProblèmes courtsTâches courtes ; couverture de tests limitée
EvalPlus (2023)HumanEval avec beaucoup plus de testsMêmes problèmes, tests renforcésDes tests faibles valident du code faux
SWE-bench (2023 ; ICLR 2024)Modifier un dépôt Python à partir d’une issue2 294 problèmes, 12 dépôtsPas une mesure du travail réel d’un développeur
Figure 3. Quatre benchmarks de code : ce qu’ils mesurent, leur taille et leur limite principale. Aucun score de modèle fermé n’est donné.

4. Ce que les tests ne voient pas

Quatre familles d’erreurs reviennent, et les tests les laissent souvent passer.

  • Le code plausible mais faux : il compile, passe les cas prévus, et échoue ailleurs. L’exercice 2 en donne un exemple.
  • L’API inventée : une fonction ou un paramètre qui n’existe pas dans la version de la bibliothèque utilisée.
  • Le test complaisant : des tests trop peu nombreux, qui valident du code faux (voir la section 3, EvalPlus).
  • La faille : un code qui fonctionne peut être vulnérable.

Les paquets inventés

Un modèle peut recommander une bibliothèque qui n’existe pas. Fait publié : Spracklen et al. (arXiv 2406.10279, version 3) rapportent 5,2 % de paquets inventés pour les modèles commerciaux testés, et 21,7 % pour les modèles ouverts testés. Ces chiffres valent pour leur échantillon et leur protocole ; ils ne se généralisent pas à tous les modèles. Un nom inexistant peut être enregistré par un tiers : l’erreur devient alors un risque de sécurité.

Le contexte et la contamination

Le modèle ne voit qu’une partie du dépôt, dans la fenêtre que l’on lui donne : la qualité dépend donc de ce qu’on lui montre, comme l’épisode 3 l’a décrit pour l’attention. Un benchmark ne montre pas non plus un dépôt entier.

La contamination est une autre limite : un problème peut avoir été public avant l’entraînement, et un score ne dit alors pas toujours une capacité nouvelle. Il est rarement possible de savoir précisément ce qu’un modèle a vu.

5. Ce que la contrainte ne fait pas

Un code régulier et exécutable n’est pas pour autant juste ni sûr. Le compilateur vérifie la syntaxe et, selon le langage, une partie du typage ; il ne vérifie pas que le programme répond au besoin. Un programme peut compiler, passer ses tests et répondre à une autre question que celle posée. Il peut aussi contenir une faille que les tests ne déclenchent pas.

L’hypothèse « cadre + processeur contraint »

Une idée revient souvent : le modèle serait contraint par le cadre strict du code et par le processeur qui l’exécute. Une partie est juste, une autre est un contresens.

  • Juste : le code suit une syntaxe stricte, et un programme sans effet non déterministe donne le même résultat sur la même entrée. L’interpréteur ou le compilateur fournit un signal objectif et reproductible. Le déterminisme dépend du langage : C et C++ ont des comportements indéfinis.
  • Contresens : le processeur qui exécute le code ne contraint pas le modèle. Un LLM s’entraîne et tourne sur des GPU ; le code produit s’exécute ailleurs, sur des machines variées. Pendant une génération simple, le modèle n’exécute pas le code ; il ne l’exécute que si un agent lance le programme comme outil.
  • Le vrai levier : le signal de vérification. C’est lui qui sert à entraîner et à filtrer les réponses (AlphaCode, CodeRL, RLEF), à condition que les tests existent, soient séparés de l’évaluation et soient assez nombreux.

6. Et ensuite ?

Le bonus 5 pose la question générale et sa grille de lecture ; on n’en reprend ici que ce qui touche au code. Chaque affirmation sur le sujet relève d’un de ces quatre niveaux :

  • Fait publié : une mesure publiée, avec son protocole. HumanEval, EvalPlus et SWE-bench en sont des exemples, avec leurs limites.
  • Tendance observée : une observation d’usage, datée et sourcée.
  • Annonce : une affirmation de produit ou d’entreprise, comme le billet d’OpenAI cité plus haut. Elle peut être juste, mais un tiers ne l’a pas reproduite ici.
  • Espérance : une attente. « Des agents qui maintiendront les dépôts » est une espérance, non une mesure.

Un agent est un programme qui enchaîne des appels au modèle, des outils et des vérifications : il lance les tests, lit le résultat, corrige, relance. Ce montage ne garantit pas que la réponse soit juste ; il rend la boucle de vérification plus fréquente. Les mesures sur des dépôts réels se lisent avec les limites de la section 3.

7. Lien avec la série

  • Prédiction : le token suivant de l’épisode 1 reste le cœur du mécanisme ; la régularité du code le rend plus facile à prédire.
  • Attention : le code est long, et la dépendance à distance compte (épisode 3).
  • Réglage : l’épisode 6 décrit les retours humains ; ce bonus décrit des retours automatiques, quand un test existe.

8. Deux exercices

Exercice 1 (pass@k, exemple inventé). Un modèle produit n = 20 solutions pour un problème ; c = 4 passent les tests. Calculer pass@1 et pass@5.

Corrigé : pass@1 = 1 − C(16,1) / C(20,1) = 1 − 16/20 = 0,20. pass@5 = 1 − C(16,5) / C(20,5) = 1 − 4 368 / 15 504 ≈ 1 − 0,282 = 0,718, soit 0,72 à deux décimales. Question : pourquoi un pass@5 plus élevé ne signifie-t-il pas une meilleure solution unique ? Réponse : pass@5 est la probabilité qu’au moins une de cinq solutions tirées passe les tests ; il ne mesure pas la qualité d’une solution unique, qui reste celle de pass@1.

Exercice 2 (un test trop faible, exemple inventé). La fonction attendue renvoie True si l’année est bissextile : divisible par 4, sauf les années divisibles par 100, sauf celles divisibles par 400. Une réponse générée est :

def bissextile(a):
    return a % 4 == 0

Les tests fournis sont : bissextile(2024) doit valoir True ; bissextile(2023) doit valoir False. 1. Vérifier que cette fonction passe les deux tests. 2. Trouver une année où elle échoue, et dire quelle règle manque.

Corrigé : 1. 2024 % 4 = 0, donc True ; 2023 % 4 = 3, donc False. Les deux tests passent. 2. 1900 % 4 = 0, donc la fonction renvoie True ; la règle exacte donne False, car 1900 est divisible par 100 et non par 400. Règle manquante : l’exception des années divisibles par 100. L’année 2000 ne sert pas de contre-exemple, car elle est divisible par 400. Morale : des tests trop peu nombreux valident du code faux ; il faut tester les cas limites.

9. Conclusion

Trois idées. Le code est régulier, donc prévisible. Il est exécutable, donc vérifiable : ce signal sert à entraîner et à évaluer. Et un test mesure un comportement sur quelques cas, pas le besoin. Restent à mesurer la maintenance, la sécurité et la conception d’un code qui doit durer.

Bonus suivant, et dernier : la carte comme langage, où l’idée de prédire un élément d’après son contexte quitte le texte pour une grille d’hexagones.

Mini-glossaire

  • Test unitaire : vérification automatique d’une fonction sur un cas donné.
  • Compilateur : programme qui vérifie et traduit du code ; il signale les erreurs de syntaxe.
  • Contamination : un élément d’un test a pu se trouver dans les données d’entraînement.
  • Pass@k : probabilité qu’au moins une des k solutions tirées passe les tests.
  • Benchmark : ensemble de tâches et de tests servant à comparer des modèles.
  • Agent : programme qui enchaîne des appels au modèle, des outils et des vérifications.
  • Hallucination de paquet : recommandation d’une bibliothèque qui n’existe pas.
  • Retour vérifiable : signal automatique (test réussi ou non) utilisé pour entraîner ou choisir.

Références

  • Chen et al., « Evaluating Large Language Models Trained on Code », 2021, arXiv 2107.03374 : HumanEval et pass@k, en anglais ; chiffres selon l’article de 2021.
  • Austin et al., « Program Synthesis with Large Language Models », 2021, arXiv 2108.07732 : MBPP, en anglais.
  • Li et al., « Competition-level code generation with AlphaCode », Science, 2022, DOI 10.1126/science.abq1158 ; préprint arXiv 2203.07814.
  • Jimenez et al., « SWE-bench : Can Language Models Resolve Real-World GitHub Issues? », arXiv 2310.06770, publié à ICLR 2024.
  • Liu et al., « Is Your Code Generated by ChatGPT Really Correct? », 2023, arXiv 2305.01210 (EvalPlus) : chiffres de la version 3.
  • Le et al., « CodeRL », 2022, arXiv 2207.01780.
  • Gehring et al., « RLEF : Grounding Code LLMs in Execution Feedback with Reinforcement Learning », 2024, arXiv 2410.02089, ICML 2025.
  • Spracklen et al., « We Have a Package for You! », arXiv 2406.10279 (chiffres de la version 3).
  • Cassano et al., « MultiPL-E », 2022, arXiv 2208.08227 : HumanEval et MBPP traduits en plusieurs langages, dont Rust.
  • Kocetkov et al., « The Stack », 2022, arXiv 2211.15533 : 3 To de code sous licence permissive (titre).
  • Grattafiori et al., « The Llama 3 Herd of Models », 2024, arXiv 2407.21783, section 3.1.2 : 17 % du mélange en code.
  • OpenAI, billet d’entreprise sur SWE-bench Verified, 23 février 2026, openai.com : annonce, non reproduite par un tiers ici.

ByAlexandre MODESTO

J'ai passé plus de vingt ans à connecter la Maurienne au monde : fibre, webcams, faisceaux hertziens. Je suis ses laves torrentielles et ses éboulements sur le long terme, et j'écris toutes les deux semaines environ la lettre « L'Univers n'est pas la limite ».

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *