Orchestration d’un debat : arbitrer entre sept architectures par le budget

Ce carnet n’enseigne pas une capacite mais un instrument : comment comparer des architectures qui font toutes « la meme chose » — ici, sept modes d’orchestration d’une analyse argumentative — sans se faire pieger par la variable qui semble la plus anodine du protocol experimental : le budget de temps.

La matiere premiere est une distillation deterministe du carnet EPITA docs/coursia_contrib/orchestration_modes_compared.ipynb (instrument scripts/compare_orchestration_modes.py, issue EPITA #1735, mandat Triple Distillation, EPIC #4960). L’instrument original faisait tourner les sept modes sur un meme texte synthetique avec un modele BYOK — cout reel mesure : 0,55 USD pour le premier run, 0,95 USD pour le second. Ici, aucune cle, aucun appel reseau : les mesures committes (2 runs x 7 modes, 2026-09-15) sont embarquees comme donnees dans l’organe orchestration_modes.py, et tout ce que ce carnet affiche est rejouable a l’identique.

Le piege documente par la source. Au budget par defaut de 180 secondes, 4 des 7 modes sont tues par le filet de securite du harnais. Le tableau qui emergait de ce run ressemblait a un classement d’architectures — qui termine, qui decide, qui exhauste son perimetre. Il n’etait qu’un artefact du budget. Le meme instrument, le meme texte, les memes modes, un budget calibre (600 s) : le classement change du tout au tout. Ce carnet montre les deux runs, nomme les trois registres d’arret qui les composent, et derive la regle qui evite le piege — calibrer l’instrument avant de comparer.

L’organe est deterministe : stdlib pure, aucune dependance, aucun aleatoire. Les 14 records embarques (7 modes x 2 budgets) portent chacun leur perimetre de travail (scope_of_work) ; la table d’asymetrie structurelle (DEPTH_PARITY) porte ses constantes mesurees avec leur provenance. Le port documente ses divergences en docstring du module — les runs LLM ne sont pas portes (les mesures le sont), l’introspection vivante des workflows est remplacee par les constantes mesurees, et la plage de delegation degeneree « 4-4 » est conservee comme distribution, pas comme entier. Le fil de lecture du carnet suit la structure de la source : un tableau sous-budget qui a l’air juste (§1), la preuve que ses colonnes ne mesurent pas la meme chose (§2), le meme tableau a budget calibre qui le renverse (§3), et la regle qui derive un budget d’une mesure — avec la limite casee de sa forme naive (§4). Chaque section lit son output apres l’avoir produit : les valeurs citees dans la prose sont celles des cellules precedentes, pas des souvenirs du carnet source. Perimetre du snapshot (signale par audit #18435, mesure du 2026-09-30). Les 14 records embarques (7 modes x 2 budgets) datent du 2026-09-15. Depuis, l’instrument du tronc en balaie huit : la livraison #2456 du 2026-09-23 a ajoute conversation_real (builder dedie, kernel dedie, runner dedie, scope : “ConversationOrchestrator(mode=real, informal + FOL agents, LLM + Tweety)”). Ce huitieme mode est annonce dans l’organe via la constante KNOWN_UNMEASURED_MODES = ("conversation_real",) mais ne figure pas dans RUNS (les mesures n’ont pas ete re-integrees). Consequence pour le lecteur : les exercices et les tableaux qui suivent (verdicts, bascules du filet, projections lineaires, derivation de budget) restent exacts sur leur perimetre de 7 modes ; aucune valeur citee n’est fausse – c’est l’exhaustivite qui est perimee. Le mode manquant est precisement celui qui complete la lecon du §2 (non-comparabilite des axes) : meme DAG conversationnel que conversation_deterministic, mais avec de vrais agents informels et FOL, LLM + Tweety – le seul mode conversationnel qui exerce le raisonnement formel.

# Organe de la serie + provenance des mesures embarquees.
from orchestration_modes import (
    RUNS, PROVENANCE, DEPTH_PARITY, ModeRun,
    verdict_text, termination_registers, depth_families, depth_family,
    depth_parity_verdict, project_full_duration, derive_calibrated_budget,
    budget_dependence,
)

print("Instrument :", PROVENANCE["instrument"])
print("Modele     :", PROVENANCE["model"])
print("Corpus     :", ", ".join(PROVENANCE["corpora"]), "-", PROVENANCE["corpus_note"])
print("Mesure le  :", PROVENANCE["measured_on"])
for name, run in PROVENANCE["runs"].items():
    print(f"run {name:14s} budget={run['max_wall_seconds']:>4}s cout=${run['cost_usd']}")
print()
print("Records embarques :", sum(len(v) for v in RUNS.values()))
Instrument : scripts/compare_orchestration_modes.py (EPITA #1735)
Modele     : gpt-5.6-luna (BYOK — cout reel mesure, pas estime)
Corpus     : corpus_A - textes synthetiques embarques dans le script source (aucun corpus chiffre, aucun raw_text)
Mesure le  : 2026-09-15
run under_budget   budget= 180s cout=$0.5504
run calibrated     budget= 600s cout=$0.9485

Records embarques : 14

Les deux runs ne different que par le budget : meme modele, meme corpus, memes modes. C’est la condition du contraste — si une autre variable bougeait, la difference entre les deux tableaux ne prouverait rien. Le premier run (sous-budget, 180 s) est celui qu’un experimentaliste lance « pour voir » ; le second (calibre, 600 s) est celui que la mesure du premier a derive.

1. Le run sous-budget — le tableau qui a l’air juste

Chaque mode recoit le meme budget. Trois colonnes du tableau disent trois choses differentes, et c’est leur ecart qui est le sujet.

# Le tableau du run sous-budget, reconstruit depuis les mesures embarquees.
def table_mode_run(records):
    head = "| Mode | Verdict | Duree | Phases | Filet |"
    sep = "|---|---|---|---|---|"
    body = [
        f"| `{r.mode}` | {verdict_text(r)} | {r.duration_seconds:.2f} s "
        f"| {r.phases_completed}/{r.phases_total} "
        f"| {'oui' if r.terminated_by_budget else 'non'} |"
        for r in records
    ]
    return "\n".join([head, sep] + body)

UNDER = RUNS["under_budget"]
CALIBRATED = RUNS["calibrated"]
print(table_mode_run(UNDER))
print()
killed = [r for r in UNDER if r.terminated_by_budget]
print(f"Tues par le filet : {len(killed)}/7 -> {', '.join(r.mode for r in killed)}")
| Mode | Verdict | Duree | Phases | Filet |
|---|---|---|---|---|
| `pipeline_standard` | tue par le filet du harnais | 180.01 s | 6/15 | oui |
| `pipeline_light` | phases completes, mode satisfait | 148.76 s | 3/3 | non |
| `pipeline_full` | tue par le filet du harnais | 180.02 s | 7/17 | oui |
| `conversational` | mode satisfait d'un travail partiel | 180.01 s | 1/3 | non |
| `conversation_deterministic` | phases completes, mode satisfait | 0.06 s | 3/3 | non |
| `hierarchical_bridge` | tue par le filet du harnais | 180.01 s | 2/4 | oui |
| `hierarchical_delegation` | tue par le filet du harnais | 180.02 s | 1/5 | oui |

Tues par le filet : 4/7 -> pipeline_standard, pipeline_full, hierarchical_bridge, hierarchical_delegation

Lecture du resultat. Quatre modes sur sept finissent tues par le filet du harnais : les trois pipelines longs (standard a 6/15 phases, full a 7/17) et les deux hierarchiques (bridge a 2/4, delegation a 1/5) — soit quatre tues, avec hierarchical_bridge et hierarchical_delegation arretes avant même d’avoir parcouru la moitie de leur perimetre. Les trois survivants racontent autre chose : pipeline_light termine ses 3 phases en 148,76 s ; conversation_deterministic finit en 0,061 s (aucun LLM, dialogue simule) ; et conversational… arrete sa course a exactement 180,01 s sans etre tue. Les trois registres du tableau suivant expliquent pourquoi ce dernier point n’est pas un detail.

Un lecteur pressé lirait ce tableau comme un podium : trois « gagnants », quatre « perdants ». C’est exactement la lecture que la source qualifie d’artefact — les quatre perdants ne doivent leur place qu’a un parametre externe a leur architecture. Noter aussi ce que le tableau ne dit pas : state_fill_rate (le taux de remplissage de l’etat partage) va de 0,096 a 0,196 sur ce run — aucune colonne ne le montre, et il faudrait le run calibre pour voir monter le remplissage du pipeline_standard a 0,462. Le tableau est une projection choisie ; le record complet porte plus que la projection.

# Les trois registres d'arret, explicites, sur les deux cas limites.
ps = next(r for r in UNDER if r.mode == "pipeline_standard")
conv = next(r for r in UNDER if r.mode == "conversational")
for r in (ps, conv):
    print(f"{r.mode} (duree {r.duration_seconds:.2f} s) :")
    for k, v in termination_registers(r).items():
        print(f"  {k:24s} {v}")
    print(f"  perimetre exhaustif     {r.perimeter_is_exhaustive}")
    print()
pipeline_standard (duree 180.01 s) :
  harnais (filet)          True
  mode (declaration)       False
  realite (phases)         6/15
  perimetre exhaustif     False

conversational (duree 180.01 s) :
  harnais (filet)          False
  mode (declaration)       True
  realite (phases)         1/3
  perimetre exhaustif     False

Lecture du resultat. pipeline_standard : le filet du harnais a tire (oui), le mode ne se declare pas satisfait, la realite dit 6 phases sur 15 — les trois registres sont coherents, l’echec est net. conversational : le filet dit non — ce n’est pas le harnais qui l’a arrete — pourtant sa duree affiche 180,01 s, au budget au centieme pres. C’est son plafond interne : le mode est borne en wall-time par construction (il s’arrete de lui-meme a 180 s), et il declare success avec 1 phase sur 3. La distinction a une consequence pratique : relever le budget du harnais ne changera rien pour un mode plafonne en interne — la colonne « Filet » du tableau ne mesure pas la meme chose que la colonne « Duree ».

2. Pourquoi le classement est faux — les modes ne partagent pas le meme axe

Un mode « plus long » n’est pas un mode « plus faible » : les sept modes ne mesurent pas la meme chose. L’introspection ci-dessous est deterministe (aucun LLM, aucun run) : elle lit la structure des modes, pas leurs performances.

# La table d'asymetrie structurelle : 7 modes, 3 familles d'axes.
head = "| Mode | Axe de profondeur | Compte | Nature |"
sep = "|---|---|---|---|"
body = [
    f"| `{r.mode}` | {r.depth_dimension} | {r.depth_count}"
    + (f" ({r.measured_range})" if r.measured_range else "")
    + f" | {r.nature} |"
    for r in DEPTH_PARITY
]
print("\n".join([head, sep] + body))
print()
print("Familles d'axes :", ", ".join(sorted(depth_families(DEPTH_PARITY))))
| Mode | Axe de profondeur | Compte | Nature |
|---|---|---|---|
| `pipeline_light` | workflow phases (DAG) | 3 | breadth |
| `pipeline_standard` | workflow phases (DAG) | 15 | breadth |
| `pipeline_full` | workflow phases (DAG) | 17 | breadth |
| `hierarchical_bridge` | strategic objectives (default axes) | 4 | delegation |
| `hierarchical_delegation` | strategic objectives (LLM-derived, measured) | 4 (4-4 objectifs -> 5-5 taches (n=3, corpus_A/B/C, firsthand R711, po-2023 projet-is)) | delegation (3-tier depth) |
| `conversational` | dialogue macro-phases (multi-turn) | 3 | dialogue-depth |
| `conversation_deterministic` | dialogue macro-phases (deterministic) | 3 | dialogue-depth (no LLM) |

Familles d'axes : breadth, delegation, dialogue-depth

Lecture du resultat. Sept modes, trois familles d’axes : la largeur (breadth — le catalogue de phases DAG du pipeline : 3, 15, 17 phases), la delegation (peu d’objectifs, plusieurs etages — 4 axes pour le bridge, palier 3-tier pour la delegation), la profondeur de dialogue (3 macro-phases multi-tours). Les comptes ne sont pas sur une echelle commune : 17 phases de DAG et 3 macro-phases de dialogue disent des choses differentes sur des structures differentes — c’est exactement pourquoi la colonne « Axe » existe. Noter la ligne hierarchical_delegation : son compte porte une plage mesuree (« 4-4 objectifs -> 5-5 taches, n=3 ») et non un entier — le palier est derive par LLM dans le moteur d’origine, donc son compte est une distribution mesuree (qui se trouve d’etre degeneree : trois corpus, toujours 4 objectifs). Le facteur 100 du tableau du §1 se rejoue ici : pipeline_full (17 phases, 448 s calibre) et conversation_deterministic (3 macro-phases, 0,06 s) sont tous deux « termines » — comparer leurs durees n’a pas de sens, et l’instrument source l’ecrit noir sur blanc.

La colonne « Axe » est la reponse methodologique a cette non-comparabilite : plutot que de fabriquer une echelle commune (aligner les comptes en gonflant ou en tronquant), la table etiquette ce que chaque compte mesure. Un lecteur qui voudrait quand meme ranger les modes par profondeur devrait au minimum ranger par famille — 17 > 15 > 3 phases a l’interieur de la famille breadth a un sens ; 17 phases contre 3 macro-phases n’en a aucun. C’est la difference entre une metrique et une etiquette de metrique : la premiere se compare, la seconde se lit.

# Le verdict d'asymetrie, dont les comptes DERIVENT de la table.
print(depth_parity_verdict(DEPTH_PARITY))
print()
# Discipline anti-litteral : tronquer la table change les comptes du verdict.
trois_pipelines = DEPTH_PARITY[:3]
print("Sur les seuls pipelines :", depth_parity_verdict(trois_pipelines))
Les 7 modes sont comparables en interface (tous produisent un verdict sur le meme texte) mais pas en perimetre de travail : ils occupent 3 axes de profondeur differents. Pipeline = largeur (catalogue large, peu profond par capacite), hierarchique = delegation (peu d'objectifs, plusieurs etages), conversationnel = profondeur de dialogue (peu de macro-phases, multi-tours). Cette asymetrie est un choix de conception documente, pas un defaut : aligner les axes fabriquerait une parite factice.

Sur les seuls pipelines : Les 3 modes sont comparables en interface (tous produisent un verdict sur le meme texte) mais pas en perimetre de travail : ils occupent 1 axes de profondeur differents. Pipeline = largeur (catalogue large, peu profond par capacite), hierarchique = delegation (peu d'objectifs, plusieurs etages), conversationnel = profondeur de dialogue (peu de macro-phases, multi-tours). Cette asymetrie est un choix de conception documente, pas un defaut : aligner les axes fabriquerait une parite factice.

Lecture du resultat. Le verdict compte « 7 modes » et « 3 axes » parce que la table en contient sept et trois — pas parce qu’une phrase en dure le souvenir. La seconde commande le demontre : restreinte aux trois pipelines, la meme fonction derive « 3 modes » et « 1 axe » sans qu’aucun litteral ne soit mis a jour. C’est une discipline de fiabilite du prose (un litteral obsoleterait silencieusement au premier ajout d’un mode — famille d’incidents #1019 sur le moteur source), et elle coute une ligne : deriver les comptes de la structure decrite. L’asymetrie elle-meme est un choix de conception documente : aligner les axes exigerait soit tronquer le catalogue du pipeline, soit gonfler artificiellement la profondeur des autres — les deux fabriqueraient une parite factice.

3. Le run calibre — memes modes, meme texte, budget derive de la mesure

Le budget calibre n’est pas choisi au confort. La source documente qu’a 180 s, pipeline_standard meurt a 6/15 de ses phases, et qu’il lui faut environ 500 s pour atteindre 15/15 sur un corpus court. Le run calibre prend donc 600 s — au-dessus du besoin documente, avec une marge. C’est la regle que ce carnet enseigne : calibrer l’instrument avant de comparer.

# Le tableau calibre — seul le budget a change.
print(table_mode_run(CALIBRATED))
print()
print("Tues par le filet :", sum(r.terminated_by_budget for r in CALIBRATED))
print("Succes auto-declares :", sum(r.success for r in CALIBRATED))
complets = [r for r in CALIBRATED if r.phases_completed == r.phases_total]
print("Phases completes :", f"{len(complets)}/7")
| Mode | Verdict | Duree | Phases | Filet |
|---|---|---|---|---|
| `pipeline_standard` | phases completes, mode satisfait | 461.36 s | 15/15 | non |
| `pipeline_light` | phases completes, mode satisfait | 178.80 s | 3/3 | non |
| `pipeline_full` | phases completes, mode satisfait | 448.12 s | 17/17 | non |
| `conversational` | mode satisfait d'un travail partiel | 600.02 s | 2/3 | non |
| `conversation_deterministic` | phases completes, mode satisfait | 0.06 s | 3/3 | non |
| `hierarchical_bridge` | phases completes, mode satisfait | 236.38 s | 4/4 | non |
| `hierarchical_delegation` | mode satisfait d'un travail partiel | 217.02 s | 3/5 | non |

Tues par le filet : 0
Succes auto-declares : 7
Phases completes : 5/7

Lecture du resultat. Zero tue, sept succes auto-declares — mais le tableau n’est pas « tout vert » pour autant : seuls 5 modes sur 7 ont completement epuise leurs phases. conversational s’arrete a 2/3 (son plafond interne a suivi le budget : 600,02 s, toujours sans filet), et hierarchical_delegation declare succes a 3 phases sur 5. Ce dernier est le cas-ecole du §1 : le mode est satisfait d’un travail partiel — son verdict (mode satisfait d'un travail partiel) n’est ni un echec ni un complet, c’est un troisieme etat que seul le registre « realite » rend visible. Les trois pipelines, eux, completent (15/15, 3/3, 17/17) et marquent leur perimetre exhaustif. Le classement du §1 est renverse : les quatre modes « faibles » d’hier terminent tous aujourd’hui.

Reste ce que le budget ne peut pas acheter : perimeter_is_exhaustive est passe a True pour les trois pipelines seulement. Meme a budget ouvert, les modes conversationnels et hierarchiques ne pretendent pas epuiser leur perimetre — le plafond interne de l’un et la satisfaction partielle de l’autre sont des proprietes de conception, pas des contraintes de temps. Un budget infini ne changerait ni le 2/3 du conversational ni le 3/5 du delegation : ces deux modes sont construits pour s’arreter en declarant l’essentiel fait. C’est la frontiere entre ce qu’un experimentaliste peut corriger (le filet) et ce qu’il doit documenter (le contrat du mode).

# Le contraste mode par mode : la preuve que le budget fabriquait le classement.
for d in budget_dependence(UNDER, CALIBRATED):
    bascule = "FILET BASCULE" if d["filet_bascule"] else "inchange"
    print(f"{d['mode']:26s} {bascule:14s} {d['duree_sous_budget']:8.2f} s -> {d['duree_calibre']:8.2f} s")
print()
n_bascules = sum(d["filet_bascule"] for d in budget_dependence(UNDER, CALIBRATED))
print(f"Bascules du filet : {n_bascules}/7")
pipeline_standard          FILET BASCULE    180.01 s ->   461.36 s
pipeline_light             inchange         148.76 s ->   178.80 s
pipeline_full              FILET BASCULE    180.02 s ->   448.12 s
conversational             inchange         180.01 s ->   600.02 s
conversation_deterministic inchange           0.06 s ->     0.06 s
hierarchical_bridge        FILET BASCULE    180.01 s ->   236.38 s
hierarchical_delegation    FILET BASCULE    180.02 s ->   217.02 s

Bascules du filet : 4/7

Lecture du resultat. Quatre bascules exactement — les quatre modes tues a 180 s passent tous sous le filet a 600 s, avec des durees reelles qui s’etalent de 217 a 461 s : personne n’avait besoin de 600 s sauf le mode plafonne en interne. conversational et conversation_deterministic ne basculent pas, pour deux raisons differentes : le premier ne doit son arret qu’a son propre plafond, le second n’a jamais ete en danger (0,06 s). C’est la demonstration complete : le « classement » du run sous-budget etait une photographie du filet, pas une propriete des architectures. Deux runs identiques a une variable pres suffisent a l’etablir — c’est la definition meme d’un contraste experimental propre.

4. Deriver le budget — la regle, et la limite de sa forme naive

La regle de calibration dit : derive le budget de la mesure, pas du confort. Sa forme la plus simple est une projection lineaire : un mode tue apres d secondes en ayant fait k phases sur n — son travail complet devrait couter environ d * n / k. Voyons ce que cette regle rend sur le run sous-budget, et ce que le run calibre en dit.

# Projections lineaires des quatre modes tues au sous-budget.
for r in sorted((r for r in UNDER if r.terminated_by_budget),
                key=project_full_duration):
    p = project_full_duration(r)
    reel = next(c for c in CALIBRATED if c.mode == r.mode)
    print(f"{r.mode:26s} {r.phases_completed:>2}/{r.phases_total:<2} a {r.duration_seconds:7.2f} s "
          f"-> projete {p:7.1f} s (reel calibre : {reel.duration_seconds:7.2f} s)")
hierarchical_bridge         2/4  a  180.01 s -> projete   360.0 s (reel calibre :  236.38 s)
pipeline_full               7/17 a  180.02 s -> projete   437.2 s (reel calibre :  448.12 s)
pipeline_standard           6/15 a  180.01 s -> projete   450.0 s (reel calibre :  461.36 s)
hierarchical_delegation     1/5  a  180.02 s -> projete   900.1 s (reel calibre :  217.02 s)

Lecture du resultat. Les projections ne se trompent pas dans le meme sens pour tous les modes. Les deux hierarchiques sont massivement surestimes : hierarchical_delegation projete a 900,1 s pour un reel a 217,0 s (x4,1), hierarchical_bridge a 360,0 s pour 236,4 s (x1,5). Les deux pipelines sont au contraire legerement sous-estimes : pipeline_full a 437,2 s pour 448,1 s, pipeline_standard a 450,0 s pour 461,4 s — environ 2,5 % sous le reel chacun.

L’ecart des hierarchiques est compatible avec un cout fixe concentre en tete de run (mise en route, chargement du contexte, etablissement des agents) : une phase deja faite y coute en moyenne plus que les suivantes, et l’extrapolation lineaire herite ce biais sur tout le restant. L’ecart des pipelines, lui, ne se lit pas : le reel vient d’un second run, et le §3 montre qu’un mode complet dans les deux runs varie bien plus d’un run a l’autre (pipeline_light : 148,76 s puis 178,80 s, +20 %). La projection n’est donc ni une borne superieure ni une borne inferieure : c’est un ordre de grandeur, juste a quelques pour cent pres sur deux modes et faux d’un facteur 4 sur un autre.

# La derivation naive, et ce qu'elle aurait impose comme budget.
naif = derive_calibrated_budget(UNDER)
print("Budget derive (projection max x 1,2) :", naif, "s")
print("Budget du run calibre reel            : 600 s")
print("Surestimation                         : x", round(naif / 600, 2))
print()
# Le besoin reel se lit sur les modes qui ont fini leurs phases : un mode
# arrete par son propre plafond donne sa duree de plafond, pas son besoin.
complets = [c for c in CALIBRATED if c.phases_completed == c.phases_total]
plus_long = max(complets, key=lambda c: c.duration_seconds)
print(f"Mode complet le plus long au calibre : {plus_long.mode} "
      f"({plus_long.duration_seconds:.2f} s)")
for c in CALIBRATED:
    if c.success and c.phases_completed < c.phases_total:
        print(f"Arrete sur travail partiel           : {c.mode} "
              f"({c.duration_seconds:.2f} s, {c.phases_completed}/{c.phases_total} phases)")
Budget derive (projection max x 1,2) : 1081 s
Budget du run calibre reel            : 600 s
Surestimation                         : x 1.8

Mode complet le plus long au calibre : pipeline_standard (461.36 s)
Arrete sur travail partiel           : conversational (600.02 s, 2/3 phases)
Arrete sur travail partiel           : hierarchical_delegation (217.02 s, 3/5 phases)

Lecture du resultat. La derivation naive rend 1081 s (la projection la plus longue, 900,1 s pour le mode tue a 1 phase sur 5, majoree de 20 %) ; le run calibre reel a suffi a 600 s — surestimation x1,8. La regle correcte est donc plus subtile que « extrapoler le plus lent » : la source a retenu le besoin documente du mode pivot (pipeline_standard, environ 500 s mesures au prealable) plus une marge, soit 600 s — et ce budget a suffi a faire terminer tout le monde sauf les deux modes qui s’arretent d’eux-memes. Les deux dernieres lignes le disent : le mode complet le plus long, pipeline_standard, a eu besoin de 461,4 s ; conversational tourne 600,0 s, mais c’est son propre plafond interne qui l’arrete a 2 phases sur 3 — sa duree mesure le plafond, pas le besoin. La lecon pour l’experimentaliste : la plus longue projection, majoree, donne un plafond defendable (1081 s couvre largement les 461,4 s du besoin reel), pas une cible ; et le budget choisi (600 s) a ete valide par la mesure du run suivant — zero tue — pas par la projection seule. L’instrument et la mesure se corrigent mutuellement, en deux temps.

Cette boucle en deux temps (mesurer, calibrer, re-mesurer) est le vrai produit de l’instrument — plus que tout classement particulier. Un experimentaliste qui saute le premier temps compare des timeouts ; un qui s’arrete au deuxieme ne sait pas si son budget genereux ne masque pas un mode plafonne en interne. La source l’a paye en dollars reels (0,55 puis 0,95) : ici la boucle se rejoue en lecture de donnees committes, pour le cout d’une execution de cellules.

Exercices

Les trois exercices suivent la convention de la serie : le stub s’execute sans erreur, la solution est a ecrire. Ils se resolvent uniquement avec l’organe importe plus haut — aucun appel externe, aucune donnee a charger.

  • Exercice 1 (registres) : un Counter sur les verdicts du run sous-budget suffit — le resultat attendu compte 4 tues, 2 complets et 1 plafonne.
  • Exercice 2 (efficacite) : attention au piege du §2 — le ratio phases/duree compare des axes differents ; c’est justement le sujet de la question.
  • Exercice 3 (projection) : construire un ModeRun fictif tue a 300 s dans l’etat du pipeline_standard sous-budget, et laisser project_full_duration faire l’extrapolation.
# Exercice 1 — Compter les verdicts par registre.
# Completez : pour le run sous-budget, combien de modes par verdict distinct ?
from collections import Counter

compteur = Counter()  # TODO etudiant : compter verdict_text(r) pour r dans UNDER
print("Verdicts sous-budget :", dict(compteur))
total = sum(compteur.values())
if total != 7:
    print(f"Exercice a completer : le compteur porte {total} mode(s) sur 7 attendus")
Verdicts sous-budget : {}
Exercice a completer : le compteur porte 0 mode(s) sur 7 attendus
# Exercice 2 — Le mode le mieux calibre par dollar.
# Completez : quel mode couvre le plus de phases par seconde au run calibre ?
efficacite = []  # TODO etudiant : liste triee de tuples (mode, phases_completed / duration_seconds)
for _ in []:  # remplacer par l'iteration sur CALIBRATED
    pass
print("Top efficacite (phases/s) :", efficacite[:3] if efficacite else "Exercice a completer")
Top efficacite (phases/s) : Exercice a completer
# Exercice 3 — Un troisieme budget.
# Completez : que se passerait-il a un budget de 300 s pour pipeline_standard,
# si la duree par phase etait uniforme ? Utilisez project_full_duration.
prediction = None  # TODO etudiant : projection a partir d'un ModeRun fictif tue a 300 s
print("Projection :", prediction if prediction else "Exercice a completer")
Projection : Exercice a completer

A retenir

  1. Un budget par defaut n’est pas un budget neutre. Il fabrique un classement (« qui meurt le plus tard ») qui ressemble a un resultat d’architecture. Les deux runs de ce carnet ne different que par cette variable — et leurs tableaux disent l’inverse l’un de l’autre.
  2. Calibrer avant de comparer. Le budget calibre est derive de la mesure precedente, pas choisi au confort — et la derivation naive (projection lineaire du plus lent) surestime d’un facteur 1,8 : les couts fixes des premieres phases gonflent l’extrapolation.
  3. Trois registres d’arret, pas un. Filet du harnais, declaration du mode, realite des phases : ils divergent reellement (un mode peut se declarer satisfait a 3 phases sur 5 ; un autre s’arreter tout seul au budget sans jamais toucher le filet).
  4. Les modes ne partagent pas le meme axe. Largeur, delegation, profondeur de dialogue : comparer des durees entre axes distincts n’a pas de sens — le facteur 100 entre le pipeline plein et le dialogue simule n’est pas un resultat, c’est une non-comparabilite.

Position dans la serie : les carnets precedents construisent ce qu’on arbitre — Toulmin_Model deploie un argument, Schemes_Walton etiquette sa forme, Knowledge_Base en garde la memoire, Dung_AF_Semantics tranche les attaques. Celui-ci regarde au-dessus : quand plusieurs architectures d’agents produisent le meme type de verdict, laquelle choisir ? La reponse de l’instrument est methodologique — un axe de comparaison honnete par famille, un budget calibre, et des registres d’arret qui ne mentent pas chacun dans leur registre.

Retour au sommet