Quel échec corriger ensuite : lire ActiveSaddler
Un harnais corrige un bogue de guillemets dans le shell, puis dépense ses dix essais suivants sur des tâches déjà réussies.

Un harnais corrige un bogue de guillemets dans le shell, puis dépense ses dix essais suivants sur des tâches déjà réussies. Aucune étape n’est cassée, mais le budget est gaspillé. Ce problème d’ordonnancement est au centre du préprint ActiveSaddler publié sur arXiv le 1er octobre.
En bref
Résultats rapportés : ActiveSaddler choisit les scénarios d’entraînement qui pilotent les mises à jour automatiques d’un harnais. Il traite les motifs d’échec comme les bras d’un bandit non stationnaire, estime le progrès attendu de chacun et équilibre le retour aux faiblesses connues avec l’exploration de nouveaux scénarios. Sur GAIA2, il rapporte 59,8 % de Pass@1 en test contre 55,4 % pour le même optimiseur avec un ordre fixe mélangé. Sur Terminal-Bench 2.0 : 80,0 % contre 72,5 %. ↗

Les chiffres et leur périmètre
Le jeu de test GAIA2 compte 300 tâches et celui de Terminal-Bench 2.0 en compte 40. Les scores sont des moyennes sur trois exécutions de test : 59,8 ± 1,0 sur GAIA2 (agent par défaut 53,6, GEPA 54,2, Meta-Harness 54,2) et 80,0 ± 2,5 sur Terminal-Bench 2.0 (Terminus-KIRA manuel 69,2). Les gains de 4,4 et 7,5 points sont mesurés contre le même optimiseur avec un ordre de scénarios fixé avant l’optimisation. Les auteurs rapportent aussi une exécution GAIA2 indépendante au même verdict et un gain de 3,0 points en appliquant la méthode à GEPA (de 54,2 % à 57,2 %). ↗
Quarante tâches de test
Ceci est mon analyse, pas une affirmation de l’article. Sur 40 tâches, une tâche vaut 2,5 points : 7,5 points équivalent à trois tâches en moyenne sur les exécutions. Le signal est utile et mince. Le résultat GAIA2 sur 300 tâches est plus stable, et le gain de 4,4 points est celui sur lequel je m’appuierais. Aucun ne dit comment la méthode se comporte sur une base de code privée, un autre modèle ou un harnais déjà bon. ↗
Comment les échecs deviennent des bras
Chaque bras est un motif d’échec extrait de trajectoires ratées, par exemple une erreur répétée de construction de commande, pas une catégorie ni une tâche isolée. Les ablations rendent ce choix important : des bras par catégorie baissent Terminal-Bench 2.0 de 10,8 points, des bras par scénario de 6,7, et retirer le priorisateur ou le contrôleur d’exploration ramène vers l’ordre fixe. Ma lecture : le gain vient du souvenir qu’un correctif a échoué et du retour au motif, tout en gardant une part du budget pour découvrir de nouveaux motifs.
Un test jouet que j’ai écrit
J’ai écrit un exemple comptable inventé et l’ai exécuté en local : sept échecs corrigibles dans quatre motifs, six essais. Un ordre prédéfini qui commence par un motif déjà résolu en corrige 3 ; cibler toujours le plus grand motif non résolu en corrige 6. Le code vérifie aussi qu’une tâche sur 40 déplace un score de 2,5 points. Ces entrées sont inventées. Elles illustrent l’idée d’allocation et ne prouvent rien sur ActiveSaddler.
Limites et sécurité
Les auteurs indiquent qu’il s’agit d’une exploration de recherche sur des données publiques et synthétiques, non évaluée comme système de production ni sur la sécurité. Ils notent qu’une mise à jour générée peut modifier invites, interfaces d’outils, construction de commandes ou flux de contrôle sans toucher aux poids, et recommandent moindre privilège, bac à sable, tests adverses et revue explicite des changements touchant aux permissions. L’article annonce code et site à venir ; je n’ai pas vérifié de publication, rien téléchargé, et n’ai pas exécuté la méthode. C’est un préprint arXiv v1.
Le test jouet, en code
# Invented toy: two ways to spend 6 rollouts on failure patterns.
remaining = {"quoting": 4, "paths": 2, "timeouts": 1, "stale-docs": 0}
def fixed(order, budget):
left = dict(remaining); fixed_n = 0
for name in order[:budget]: # one pass in a preset order
if left[name]:
left[name] -= 1; fixed_n += 1
return fixed_n
def adaptive(budget):
left = dict(remaining); fixed_n = 0
for _ in range(budget): # always target the largest unresolved pattern
name = max(left, key=left.get)
if left[name]:
left[name] -= 1; fixed_n += 1
return fixed_n
order = ["stale-docs", "stale-docs", "timeouts", "paths", "stale-docs", "quoting"]
a, b = fixed(order, 6), adaptive(6)
assert (a, b) == (3, 6)
print("preset order repaired", a, "of 6; adaptive repaired", b, "of 6")
# 1 test task on a 40-task split moves the score by 2.5 points.
assert 100 / 40 == 2.5