Skip to content

L1.5b — decoupage par article, mesure sur le service - #1

Open
nic01asFr wants to merge 284 commits into
chantier/tronc-uniquefrom
lot/L1.5b-decoupage-par-article
Open

L1.5b — decoupage par article, mesure sur le service#1
nic01asFr wants to merge 284 commits into
chantier/tronc-uniquefrom
lot/L1.5b-decoupage-par-article

Conversation

@nic01asFr

Copy link
Copy Markdown
Owner

L1.5b — découpage par article, mesuré sur le service

Critère de fin atteint. Le découpage par article est en service (COLAIG_CHUNK_STRATEGIE: auto, 1149 chunks, ordre du Code vérifié dans metadata.pkl) et mesuré contre la référence L1.5 sur l'instance déployée, pas sur une reconstitution locale.

Ce que le lot déplace

départ arrivée
cite l'attendu 52/113 89/113, 90/113
refus sur négatifs 5/22 20/22, 20/22
article servi toujours 95/113
bascules entre deux campagnes identiques 25 (22 %) 8 à 12

Le point de départ était mesuré par un compteur qui ignorait un onzième du jeu doré.

Le seul gain établi

Chercher aussi avec la question que l'usager a écrite. La recherche ne cherchait qu'avec intent.query_reformulated, une chaîne réécrite par le LLM à chaque tour ; elle héritait donc entièrement de son instabilité. Quatre comparaisons croisées, test des signes : p = 0,000 / 0,001 / 0,029 / 0,052.

Il ne fait pas que gagner des points, il stabilise : les cas traités « une fois sur deux » tombent de 17 à 7.

Six capacités écrites et jamais branchées

  • l'Orchestrateur ne passait pas bm25_store à retrieve()COLAIG_HYBRID_SEARCH_ENABLED était décoratif, l'index construit, persisté, jamais consulté ;
  • albert_reranked posé à True après un reranking qui n'avait pas eu lieu — la fusion RRF ne survivait que par cet accident ;
  • le journal des échanges nommait ses fichiers d'après le seul message_id, que /ask ne fournit pas : 135 questions écrivaient 135 fois le même fichier ;
  • le compteur de citations ignorait les articles hors motif (« CCAG Travaux 4 ») alors que verification_citations prévoit la voie exacte ;
  • le budget documentaire était figé à 6000 jetons sur une fenêtre de 131 072 relevée sur l'endpoint ;
  • la question de l'usager ne servait jamais de requête.

Ce que la mesure sait maintenant faire

Juger au grain du passage et non du fichier, mesurer la constance plutôt que le tirage, et trancher par test apparié. Sans ces trois-là, aucun des résultats ci-dessus n'aurait été distinguable du bruit — et trois comparaisons de la journée, conclues avant, ne l'étaient pas.

Ce qui reste ouvert

  • trois réglages actifs sans preuve : hybride, voisins, budget — comparés quand la dispersion valait 20 %, aucun ne s'était distingué du hasard ;
  • deux motifs d'échec non traités : la partie législative servie à la place de la réglementaire ; le bon article du mauvais CCAG ;
  • douze à quinze cas jamais servis ;
  • PreExecutionBuilder cherche dans un magasin vide (sans effet : phase 6 éteinte) ;
  • trois cas dorés à arbitrer : mp-130, mp-135, probablement mp-040.

Réserve sur la forme — à arbitrer

Cette PR porte 283 commits : c'est le neuvième lot enchaîné sur la même branche depuis L0.2, et aucun lot antérieur n'a été fusionné. CLAUDE.md §4.5 demande « un lot = une branche = une PR, jamais de gros merge ».

La règle n'est pas tenue et la dette grossit à chaque lot. Cette PR n'est pas relisible en l'état ; la résorber demande une décision humaine sur la stratégie de fusion. Elle est ouverte pour porter la trace du lot, pas pour être fusionnée telle quelle.

2810 tests passent.

🤖 Generated with Claude Code

nic01asFr and others added 30 commits August 23, 2026 16:39
FAISS, BM25, RRF et MMR existaient tous dans colaig/rag/ depuis l'origine et
AUCUN n'avait jamais ete mesure. Ils etaient activables sans que personne puisse
dire ce qu'ils apportent — la situation que « rien n'est active sans mesure »
existe pour interdire. La reference L1.5 le permet enfin, sur 103 cas dores.

DEFAUT TROUVE — le terme de pertinence de MMR etait numeriquement invisible.

_mmr_rerank calcule lambda * relevance - (1-lambda) * diversite, avec
relevance = candidate.score. Or ce score ne vient pas toujours de la meme
echelle : FAISS rend une cosinus dans [0,1], RRF rend 1/(60+rang), soit 0,016 au
premier rang. A lambda=0,7 la pertinence pesait 0,011 face a une diversite allant
jusqu'a 0,3 — deux ordres de grandeur. Apres fusion, MMR ne classait plus par
pertinence ponderee de diversite : il retenait le plus dissemblable.

Mesure avant / apres normalisation min-max de la pertinence :

  RRF + MMR k=6     50/103 -> 76/103
  dense + MMR k=6   76/103 -> 88/103

Correction de moi-meme : j'avais conclu « MMR coute 12 points de rappel ».
C'etait faux. MMR n'etait pas nuisible, il etait prive de son terme de pertinence.
Une fois normalise il est NEUTRE a k=6 sur ce corpus — ni gain ni perte. La
normalisation rend _mmr_rerank indifferent a l'echelle de ce qui le precede :
lambda garde le meme sens derriere FAISS, derriere RRF, ou derriere tout autre
classement a venir.

BANC COMPLET (103 cas, tous les articles attendus remontes) :

  dense k=6            88   dense k=15            95
  dense k=10           89   dense k=20            96
  BM25 k=6             66   RRF dense+BM25 k=6    84
  RRF + MMR k=6        76   RRF dense+BM25 k=10   91
  dense + MMR k=6      88

Trois enseignements. La PROFONDEUR est le levier efficace : k=6 -> k=15 vaut
+7 points, gratuitement. BM25 seul est faible (66) et la FUSION DEGRADE a
profondeur egale (84 contre 88) — elle n'aide qu'en augmentant aussi la
profondeur, et le dense seul a k=15 (95) bat encore RRF k=10 (91). MMR ne gagne
rien ici, mais ne coute plus rien non plus.

Le diagnostic qui a motive ce banc : sur les 11 echecs, le rang REEL de l'article
attendu est 9 a 15 pour six d'entre eux, et > 60 pour les cinq autres. Six echecs
sur onze n'etaient pas des absences mais des erreurs de classement — d'ou le
plafond de 94-96/103 atteignable par la profondeur, et pas au-dela.

Le diagnostic lui-meme avait un defaut, corrige avant d'en tirer quoi que ce
soit : il tronquait a deux articles par passage AVANT de tester la presence de
l'attendu, et sur-declarait donc des echecs.

k devient reglable par COLAIG_REF_K dans le harnais de generation. L'arbitrage
k=6 contre k=15 ne se tranche pas au niveau de la recherche : chaque passage
supplementaire entre dans le prompt, donc plus de contexte, plus de latence, et
peut-etre moins de refus — plus de passages, c'est plus d'occasions de trouver
quelque chose de plausible a dire sur une question sans reponse. Mesure suivante.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
verification_citations controle la PROVENANCE — le numero cite figure-t-il dans
les passages ? C'est decidable sans modele, et c'est pourquoi il est fiable. Sa
docstring dit aussi ce qu'il ne sait pas faire : juger si la reponse est FIDELE au
passage dont elle se reclame.

mp-032 du jeu dore tient exactement dans cet ecart. Le modele repond a une question
dont la reponse precise n'est pas dans le corpus, en affirmant s'en tenir
strictement aux passages. La provenance est correcte — R2191-3 est bien la. C'est
l'INFERENCE qui deborde. Aucun controle mecanique ne l'attrapera : il faut lire,
donc un modele. C'est le seul cas du jeu dore que je ne savais pas fermer.

Deux regles reprises telles quelles, et ce sont elles qui font la valeur :

1. La recette de contexte est la plus PAUVRE du systeme — une affirmation, un
   extrait. Ni nom de document, ni page, ni autorite, ni intention. Un modele a qui
   l'on dit que la source fait autorite trouve plus facilement qu'elle etaye. La
   pauvrete ferme ce biais PAR CONSTRUCTION, la ou une consigne ne ferait que le
   deconseiller — et l'on sait ce que vaut une consigne : mesure sur le jeu dore,
   un prompt qui interdisait deja d'inventer laissait passer des citations hors
   contexte. Un test existe pour que tout enrichissement se voie ; verifie qu'il
   sait echouer.

2. Le passage d'appui est controle SANS MODELE. Le verificateur doit rendre une
   portion verbatim de l'extrait, et le code verifie qu'elle s'y trouve. C'est la
   seule part du verdict qui ne depend d'aucun jugement, et elle vaut precisement
   parce que le verificateur est lui aussi un modele. On ne le croit pas sur parole
   au motif qu'il est le controleur.

Verdict ferme a quatre valeurs, plus « illisible » qui est un cinquieme etat et non
un verdict : rabattre une sortie illisible sur « ne_dit_pas_cela » ferait passer une
panne du verificateur pour un jugement. La normalisation tolere espaces, retours a
la ligne et casse — un garde-fou qui crie au loup trop souvent finit ignore.

exploitable distingue les trois situations : hors liste, jamais ; verdict positif,
seulement s'il est ancre ; verdict negatif, toujours — l'absence est ce qu'il
constate, et l'on ne cite pas ce qui manque.

Non branche dans le pipeline : ce module se mesure avant de s'activer. Promouvoir
en Protocol toucherait protocols.py, donc arbitrage humain (§5) — pas fait ici.

FakeLLM enregistre desormais la temperature de chaque appel, pour la meme raison
que priorites : un controle dont la temperature ne serait pas nulle rendrait un
verdict different d'une execution a l'autre, sans qu'aucune erreur ne le dise.

13 tests. 1791 passent au total.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ts ordonnes

CLAUDE.md §6 fait de ce fichier le mecanisme de reprise et impose sa mise a jour
a chaque lot, sans exception. Il s'arretait a L1.5b alors que la journee a produit
la reparation du corpus de reference, l'arbitrage H2, le jeu dore a 122 cas et le
verificateur de fidelite.

Y figurent notamment les six points ouverts par ordre, dont deux qui ne sont pas
des reglages : le corpus CCAG (cinq echecs de recherche sont hors de portee de
tout moteur, les mots n'existent pas dans le code) et la relecture humaine du jeu
dore, qui reste une porte explicite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le vrai choix de conception est le decoupage. Une reponse entiere n'est pas une
affirmation : la verifier d'un bloc ne donnerait qu'un verdict global inexploitable,
« partiellement etaye » ne disant pas QUELLE phrase deborde.

Appariement retenu : une phrase portant une reference d'article, confrontee au
passage qui contient cet article. C'est le plus serre possible, et il cible
exactement le mode d'echec vise — une phrase qui invoque un article pour dire autre
chose que ce qu'il dit, ce qu'aucun controle de provenance ne peut voir.

Les phrases sans reference sont ecartees. Non parce qu'elles seraient sures, mais
parce que garde_fou_reponse les traite deja en remplacant par un refus toute reponse
sans attache. Verifier deux fois la meme chose couterait sans rien apprendre.

--limite borne le nombre de cas : la premiere execution doit prouver le mecanisme,
pas balayer. Lancable des que la mesure de generation en cours aura stocke ses
reponses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verification article par article des trois « citations fantomes » de la mesure de
generation a k=6 :

  L5132-4 (mp-088, mp-091)  pas un fantome — article du code du travail, cite mot
                            pour mot dans L2113-13, qui etait dans les passages
  L5213-13 (mp-091)         pas un fantome — idem, cite dans L2113-12
  L2161-1 (mp-008)          FANTOME REEL — le code ecrit R2161-1

La metrique comparait les references citees au seul corpus du Code de la commande
publique. Or le corpus est un seul code ; les articles qu'il cite ne le sont pas. Un
renvoi correctement relaye vers le code du travail etait compte comme une invention.
Une reference presente dans les passages fournis ne peut pas etre un fantome, quel
que soit le code dont elle releve.

Le chiffre a retenir est 1/122, du meme ordre que 0 hors contexte et 0 montant
invente.

Ce que revele le seul fantome reel merite d'etre note : le modele n'a pas invente un
article, il a change une LETTRE — L2161-1 pour R2161-1. C'est le mode d'erreur le
plus difficile a reperer a la lecture, et celui qu'aucune vraisemblance ne trahit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rtie

Le banc donne un gagnant net A LA RECHERCHE : dense k=15 remonte 95 cas complets
sur 103 contre 88 a k=6. Cette mesure regarde ce que cela devient A LA SORTIE.

                                        k=6        k=15
  cite l'attendu, tous les cas       66/101     59/101
  cite l'attendu, reponses completes  66/75      59/63
  observations tronquees                 39         47
  fantome                                 1          0
  ne refuse jamais                        2          1

k=15 REPOND MIEUX QUAND IL REPOND, ET IL REPOND MOINS SOUVENT. Sur les reponses
completes il est superieur partout — 94 % contre 88 % de citation attendue, zero
fantome. Le rappel supplementaire se transmet bien. Mais il tronque davantage, et
l'utilisateur ne recoit alors rien d'exploitable. Net a net il perd.

Ce qu'il ne faut PAS conclure : la comparaison est confondue par la troncature, qui
n'est pas une propriete de k=15 mais du budget de sortie. max_tokens vaut 4000 et
qwen3-6-35b-moe est un modele a raisonnement : raisonnement et reponse puisent au
meme budget, plus de contexte c'est moins de place pour repondre. « k=6 est
meilleur » est exact aujourd'hui et faux comme principe. Le bon enonce : a budget
de sortie insuffisant, la profondeur se paie plus qu'elle ne rapporte.

Decision : k=6 reste la profondeur de production. Mesure suivante ecrite : relever
max_tokens jusqu'a rendre la troncature negligeable, puis rejouer. Tant que 27 a
33 % des reponses sont coupees, aucune comparaison de profondeur n'est concluante
— et ce taux est un defaut de service en lui-meme, independant de k.

DEUX DEFAUTS DE HARNAIS CORRIGES.

Le nom du rapport et celui des reponses ne portaient pas k : la passe k=15 a EFFACE
celle de k=6, rapport et reponses. Recuperes depuis git, separes, et le nommage
porte desormais la profondeur.

Le comptage des citations ne doit pas juger une reponse tronquee. La regle existait
pour le refus — mp-044 coupee a neuf caracteres, au milieu de sa formule de refus —
elle vaut identiquement pour les citations : mp-063 se termine par « (Article
R2112- », mp-107 par « **Article R2 ». Ce sont des moignons, et le motif elargi les
lit comme des articles courts. C'est l'effet de bord d'un elargissement par ailleurs
justifie — L1 a L6 sont de vrais articles. Le motif ne peut pas distinguer un
article court d'une reference coupee ; on sait laquelle on a, puisqu'on sait si la
reponse a ete tronquee.

reanalyse_generation.py recompte sur les reponses stockees, sans reinterroger le
modele. J'y ai refait l'erreur que reference_generation.py documente — sys.argv
ecrase avant d'etre lu — et l'ai corrigee de la meme facon.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
D18. 27 a 33 % des reponses etaient coupees a max_tokens=4000 : qwen3-6-35b-moe
raisonne, et raisonnement et reponse puisent au meme budget. Sonde sur cinq cas
reellement tronques :

  temoin 4000          2/5 coupees  1281 car.  20,3 s
  max_tokens 8000      0/5 coupees  2339 car.  20,8 s
  enable_thinking off  0/5 coupees  1202 car.   2,2 s
  reasoning_effort low 3/5 coupees  1196 car.  20,8 s

reasoning_effort est SILENCIEUSEMENT IGNORE — 16 373 caracteres de raisonnement
malgre lui. Un reglage accepte sans effet est pire qu'un reglage refuse : on croit
l'avoir applique.

Arbitrage sur les 122 cas :

                          avec raisonnement   sans
  citation hors contexte         0/122        26/122
  refuse a chaque fois           15/18        21/21
  reponses tronquees                39             1
  cite l'article attendu       66/101        88/101
  latence mediane            ~15-20 s         2,0 s

Le raisonnement achetait la DISCIPLINE DE PROVENANCE, et rien d'autre.

Le garde-fou mecanique attrape exactement ces 26 derives : sans raisonnement,
134/164 reponses completes et propres contre 121/164 avec, 24 annotees, 5
remplacees. Sans raisonnement gagne meme sur la lecture la plus stricte, pour un
neuvieme de la latence. H3 revient dans son budget : 10 s vises, 15 s mesures,
2 s obtenus.

D19. Branche avec un defaut ACTIF, le garde-fou a fait echouer un test existant
dont la reponse cite [guide.txt] — une source de fichier, pas un article. Le test
avait raison contre le branchement.

Ce garde-fou juge a l'aune des NUMEROS D'ARTICLE. Colaig est multi-tenant : un
espace RH, une FAQ technique n'en contiennent aucun. Actif par defaut il y
remplacerait TOUTE reponse par un refus — le service serait muet et le journal
dirait qu'il protege. COLAIG_GARDE_FOU_ENABLED est donc inactif par defaut. Sa
vraie place est workspace.yaml : une variable d'environnement est globale, or la
decision ne l'est pas.

Huit tests fixent les deux moities — inactif il ne touche a rien, actif il fait ce
pour quoi il existe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e sens

Quatre agents ont relu les 122 cas, chacun un quart, article par article contre le
corpus fige. 31 fautifs, 29 douteux, 61 sains.

LE DEFAUT EST SYSTEMATIQUE : l'article porte une condition restrictive, la reponse
attendue retient la regle et laisse tomber la borne. Un instrument construit ainsi
RECOMPENSE LA REPONSE INCOMPLETE ET PENALISE LA REPONSE COMPLETE. Il pousse dans la
mauvaise direction, en silence.

mp-026 : la question demande si l'on peut faire regulariser UN soumissionnaire, la
reponse disait oui. R2152-2 dit « tous les soumissionnaires concernes ». Le jeu dore
validait la pratique irreguliere que la question invitait a commettre.

mp-106 : la reponse enumerait les trois raisons d'un operateur unique sans la borne
— « n'est justifie que lorsqu'il n'existe aucune solution de remplacement
raisonnable et que l'absence de concurrence ne resulte pas d'une restriction
artificielle ». Elle validait l'acheteur qui fabrique son fournisseur unique.

mp-081, mp-112 : deux cas sur six, ou les trois derogations d'allotissement omises.
Un modele repondant COMPLETEMENT etait compte en ecart.

DEUX REFUS INDUS. mp-032 declarait absente une information presente : R2191-7 donne
le taux de l'avance et son assiette. Chaque mesure comptait donc comme un defaut de
refus le comportement CORRECT du modele — et j'ai cite ce cas toute la journee comme
celui qu'aucun controle mecanique n'attrape. Le modele avait raison, mon cas avait
tort. mp-060 de meme. Les deux requalifies en positifs ; deux negatifs reels les
remplacent, verifies par recherche et non fabriques pour satisfaire le seuil.

LA VERIFICATION MECANIQUE CONNAIT SA LIMITE. controle_bornes.py cherche dans les
articles cites les marqueurs de restriction absents de la reponse : il trouve 6 cas
sur 101, la relecture en a trouve 31. Ce defaut n'est pas mecaniquement detectable,
il fallait lire. Le controle reste un revelateur, jamais un test.

31 corrections ecrites, chacune reverifiee contre le texte de l'article avant d'etre
appliquee — les rapports d'agents n'ont pas ete pris pour argent comptant.

Jeu dore : 124 cas, 21 negatifs. 1799 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Verrou identifie en repondant a la question « si les CCAG sont dans l'espace,
seront-ils citables ? ». Le reconnaisseur de references exigeait un prefixe L, R ou
D suivi de chiffres — le format du Code de la commande publique. Or les CCAG, qui
sont des arretes, numerotent « article 20.1 ».

Une reponse citant le CCAG etait donc vue comme ne citant RIEN, et le garde-fou
l'aurait remplacee par un refus. C'est exactement le mode de defaillance corrige
pour les articles preliminaires L1 a L6, transpose a un autre corpus — et il se
serait manifeste le jour ou l'on aurait ajoute le corpus, pas avant.

articles_cites() et verifier() acceptent desormais un jeu de formats. FORMAT_CLAUSE
reconnait 20.1 et 46.2.3 ; il n'est PAS actif par defaut, et c'est mesure : sur le
corpus du code, ce motif releve 188 occurrences, toutes « 2.0 » — la mention
Licence Ouverte du pied de page. Inoffensif la ; mais sur un fonds de procedures,
« 2.5 » est un taux ou un numero de version. Un controle qui prend des fragments
pour des citations declare ancrees des reponses qui ne le sont pas : il est alors
PIRE QU'ABSENT.

Le format se declare donc par corpus, comme le garde-fou lui-meme (D19), et sa
vraie place est workspace.yaml.

verifier() documente que les deux cotes doivent employer le MEME format : reconnaitre
une graphie dans la reponse et pas dans les passages est le defaut qui avait deja
fait conclure a tort que le modele puisait dans sa memoire.

Recherche remesuree sur le jeu dore corrige : 88/104 complets, 85 % — le taux tient
apres les 31 corrections, ce qui etait attendu puisqu'elles portaient sur le contenu
des reponses et non sur les articles attendus.

4 tests ajoutes, 1803 passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
D21. Le corpus contient 1806 articles dont 38 % SEULEMENT relevent du regime des
marches publics ordinaires. 23 % sont le livre defense-securite. Or les 117 articles
attendus par le jeu dore sont TOUS dans le livre Ier.

Restreindre le corpus ne gagne qu'UN cas en recherche (89/104 contre 88/104) —
l'hypothese du bruit est largement infirmee. Mais la mesure decisive est ailleurs :
108 citations sur 469, soit 23 %, portent sur un article hors du regime ordinaire.
R2312-11 est le jumeau de R2112-14, R2322-12 celui de R2122-x : meme regle, seuils
differents.

Et AUCUN GARDE-FOU NE PEUT L'ATTRAPER : ces articles etaient dans les passages
fournis, donc la provenance est correcte. C'est une reponse FIDELE QUI CITE LE
MAUVAIS DROIT — un mode de defaillance qu'aucun controle de provenance ne verra, par
construction. La correction est dans le perimetre du corpus, pas dans le moteur.

D22. Le corpus ne compte que six articles en forme courte, L1 a L6 plus L3-1 ; tout
autre porte quatre chiffres ET un tiret. « Les articles R2161 et suivants » designe
donc une SECTION, facon correcte d'ecrire, que le motif comptait comme citation et
qui ressortait en fantome. Quatre des dix fantomes annonces etaient de cette nature :
8 au lieu de 10 apres resserrement. Une metrique qui signale comme invention une
maniere correcte d'ecrire gonfle son compte et perd la confiance qu'on lui accorde.

REFERENCE L1.5 sur les 124 cas corriges, prompt durci, k=6, raisonnement coupe :

  refuse a chaque fois            21/21
  cite l'article attendu          88/102  (86 %)
  articles attendus remontes      88/104  (85 %)
  fantome / hors contexte         8 / 22 sur 124
  reponses tronquees              2
  latence mediane                 ~2 s
  garde-fou rendue/annotee/remplacee   137 / 23 / 4 sur 164

Le refus est desormais SYSTEMATIQUE : 0/8 au premier jour, 6/8 avec le prompt durci,
15/18 avec raisonnement, 21/21 sans. H3 est tenue — 10 s vises, 2 s obtenus.

1803 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les cas signales douteux par la relecture, chacun reverifie contre le texte. Ceux
dont le doute portait sur le bareme plutot que sur le contenu ne sont pas touches :
ils relevent d'un arbitrage de mesure, pas d'une erreur de fait.

FAMILLE 1 — ECART DE PORTEE. « Au-delà des seuils » la ou le code ecrit « egale ou
superieure » : le marche pile au seuil etait inclus par le texte et exclu par la
reponse (mp-010, mp-057). « Les membres » la ou L2113-7 ecrit « les acheteurs
membres », effacant une distinction que L2113-6 opere (mp-095). « Les regles de la
commande publique » pour « les regles prevues par la presente partie » (mp-094). Une
enumeration ouverte presentee comme fermee — le « notamment » de R2123-7 disparu
(mp-118). Sur un jeu dore qui traque precisement les ecarts de portee, s'en permettre
est incoherent.

FAMILLE 2 — ASSERTION NON SOURCEE DANS UN CAS NEGATIF. mp-012 affirmait que les
seuils « sont revises tous les deux ans », mp-013 et mp-019 nommaient des faits
exterieurs au corpus. Un cas negatif qui mesure la resistance a l'invention ne peut
pas, dans sa propre reponse de reference, avancer un fait non source. Les trois s'en
tiennent desormais a ce que le corpus etablit.

Cas notables. mp-025 demandait « a quel moment » et R2144-4 repond « de qui » — le
moment est regi par R2144-3 ; la question couvre desormais les deux, comme elle se
pose en redaction. mp-100 et mp-109 sont ancres hors defense-securite : le regime
parallele pose 100 000 euros la ou l'ordinaire pose 60 000, et 300 000 la ou
l'ordinaire pose 100 000 — un rapport de un a trois. Sans ancrage, un systeme
remontant le bon article du mauvais livre etait compte faux sans avoir rien invente
(voir D21). mp-049 perd une assertion que la grammaire de R2111-7 ne tranche pas.

1805 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CLAUDE.md §6 impose la mise a jour a chaque lot. Y figurent les 25 pour cent de cas
fautifs du jeu dore et leur correction, l'arbitrage de la configuration de production
(raisonnement coupe + garde-fou), et le defaut qu'aucun garde-fou ne peut attraper —
une reponse fidele qui cite le mauvais droit.

Note de methode consignee : le controle mecanique ecrit pour detecter le defaut du
jeu dore en trouve 6 sur 101 la ou la relecture en a trouve 31. Ce defaut n'est pas
mecaniquement detectable.

Les cinq points ouverts sont ordonnes, avec pour chacun ce qu'il coute — notamment
que restreindre le corpus invaliderait la reference une fois de plus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
C'etait le seul emprunt que je recommandais a Editeur — bon marche, repondant a une
faiblesse mesuree, plusieurs cas dores tournant sur la distinction obligation /
faculte. La mesure l'infirme.

Sur les 1805 articles du corpus :

  obligation EXPLICITE (doit, devra, est tenu, est interdit)   111   6 %
  faculte (peut, pourra) sans obligation explicite             634  35 %
  NI l'un NI l'autre                                          1060  59 %

Les 59 % ne sont pas des articles sans portee : ce sont des obligations enoncees a
l'INDICATIF PRESENT, style legislatif francais classique. « Les marches sont passes
en lots separes » est une obligation, « l'acheteur ecarte les offres irregulieres »
aussi. Aucun marqueur lexical ne les distingue d'une definition.

L'emprunt echoue pour une raison de GENRE DOCUMENTAIRE. Les documents d'urbanisme
d'Editeur — SCoT, SAR — sont des documents de planification adresses a des actions
futures : ils ecrivent naturellement « devra ». Un code ecrit au present.

Un premier essai de mesure avait signale sept derives apparentes, dont au moins deux
etaient des erreurs du classifieur : L2113-10 porte « peut limiter » plus loin dans
l'article, ce qui le faisait classer faculte alors que sa regle principale est une
obligation. Le classifieur etait plus faux que ce qu'il mesurait — d'ou la decision
de ne pas construire dessus.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Suite de D21. La mesure tranche, et pas dans le sens attendu.

                                    corpus entier   restreint
  citations du mauvais regime         115 (22 %)     1 (0 %)
  citations hors contexte                     22          55
  garde-fou rendue / annotee / rempl.  137/23/4     103/50/9

La restriction DEGRADE la provenance et SUPPRIME les citations du mauvais droit. Le
choix est donc entre 115 erreurs silencieuses et 33 avertissements visibles de plus.

Il se tranche en regardant ce que l'utilisateur recoit. Une citation du mauvais
regime delivre du droit faux COMME S'IL ETAIT JUSTE — le livre defense pose 100 000
euros la ou l'ordinaire pose 60 000 — et aucun garde-fou ne peut la voir, puisque
l'article etait bien dans les passages. Une citation hors contexte est annotee sous
les yeux de l'utilisateur, qui garde la reponse et la mention.

On prefere le mode de defaillance que le garde-fou sait voir.

LE TEST D'ANCRAGE A RATTRAPE UNE FAUTE GROSSIERE. La premiere restriction faisait
tomber L3, qui enonce les principes de la commande publique : les articles
preliminaires L1 a L6 et L3-1 relevent du Titre Preliminaire et d'aucun livre, donc
un filtre par livre les emportait. Ils definissent « marche public » et « acheteur »,
ce sont les plus cites. HORS_PERIMETRE_RETENUS les conserve quoi qu'il arrive. Sans
test_tous_les_articles_cites_existent, le corpus aurait perdu ses definitions sans
que rien ne le signale.

692 articles plus le titre preliminaire, 47 documents, 0,38 Mo — contre 1806 articles
et 188 documents. L'index passe de 1806 a 699 passages. Recherche : 89/104, 86 %.

PERIMETRE = None reconstruit le code entier : l'ancien corpus reste reproductible, en
quarantaine hors depot et dans l'historique git.

1805 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…herente

Les chiffres publies pour la colonne « restreint » de D24 etaient faux, et le
raisonnement qui s'appuyait dessus aussi.

reanalyse_generation.py recalculait les passages avec un perimetre CODE EN DUR,
sans savoir lequel la mesure avait employe. Une mesure lancee sur le corpus restreint
etait donc recomptee contre le corpus entier : fournis n'etait pas ce que le modele
avait recu, et les citations hors contexte grimpaient a 55 sans raison.

Recomptees toutes deux contre leur propre corpus :

                                    corpus entier   restreint
  citations du mauvais regime         115 (22 %)     1 (0 %)
  citations hors contexte                     22          22
  garde-fou rendue / annotee / rempl.  137/23/4     135/21/6

La restriction supprime les citations du mauvais droit SANS RIEN COUTER a la
provenance. Il n'y avait donc pas d'arbitrage entre erreurs silencieuses et
avertissements visibles : c'etait un arbitrage imaginaire, produit par un defaut de
mon propre outil de recomptage. La decision de restreindre est inchangee, elle est
simplement mieux fondee qu'ecrit.

Le perimetre se deduit desormais du nom du fichier de reponses, qui le porte deja.

REFERENCE L1.5 DEFINITIVE — corpus restreint, 124 cas apres les deux lots de
corrections, prompt durci, k=6, raisonnement coupe :

  refuse a chaque fois              21/21
  articles attendus remontes        89/104  (86 %)
  cite l'article attendu            84/100
  citation hors contexte                18
  fantome / montant invente            6 / 2
  reponses tronquees                     3
  latence mediane                     ~2 s
  garde-fou rendue/annotee/remplacee   140 / 16 / 7 sur 163

Meilleur etat mesure a ce jour sur chaque indicateur de fidelite, obtenu a un
neuvieme de la latence du premier point de mesure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le verificateur est lui-meme un modele. Lui faire juger les reponses sans savoir ce
qu'il vaut reviendrait a mesurer une chose inconnue avec un instrument inconnu.

Calibration sur 30 couples FIDELES PAR CONSTRUCTION — les reponses attendues du jeu
dore confrontees aux articles qu'elles citent, relues une par une par quatre
relectures independantes : 0 % DE FAUX NEGATIFS. 19 etaye, 11 etaye_partiellement,
aucun ne_dit_pas_cela.

DEUX DEFAUTS DE BANC CORRIGES EN ROUTE, TOUS DEUX LES MIENS.

1. L'appariement a un seul article relevait 30 % de faux negatifs. La lecture des
motifs les a tous innocentes : « l'extrait mentionne uniquement le delai de base sans
evoquer les exceptions de reduction ». Le verificateur avait raison. L'ironie est
exacte : les corrections apportees au jeu dore ce meme jour consistaient precisement
a AJOUTER les bornes portees par les articles voisins (D20). Ce sont elles qui
rendaient l'appariement a un seul article intenable.

2. Le verificateur cite volontiers un passage DISCONTINU, en marquant l'elision par
« [...] » — legitime quand l'appui s'etend sur deux articles. La comparaison par
sous-chaine exacte echouait sur la jointure et declarait 57 % d'appuis fabriques,
dont aucun ne l'etait. Chaque fragment est desormais verifie separement, avec un
seuil de longueur qui ferme la porte de sortie. 57 % -> 23 %.

Une hypothese REFUTEE au passage : normaliser les variantes typographiques ne change
rien au residu. La normalisation est conservee parce qu'elle est juste en soi, pas
parce qu'elle a servi.

CE QUE LE VERIFICATEUR VAUT : sa valeur est dans ses verdicts NEGATIFS. Sur des
couples fideles il n'en produit aucun, donc quand il dit « ne dit pas cela », cela
compte. Il reste 23 % de verdicts positifs non ancres, dont exploitable refuse de se
contenter — limite documentee, pas blocage. Il sert mieux a signaler qu'a certifier.

CE QUI N'EST PAS MESURE : le taux de faux positifs. Il faudrait des couples dont on
sait qu'ils sont infideles, et le jeu dore n'en contient pas. Tant qu'il manque, ON
NE SAIT PAS CE QUE VAUT UN « ETAYE ».

1808 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…uition

Il coute ~1 s par couple sur une reponse qui en prend 2 : le passer sur tout
triplerait la latence gagnee en coupant le raisonnement. D'ou la question de ne
l'appeler que la ou il sert.

Contrainte qui elimine la plupart des idees : le signal doit etre calculable AU
MOMENT DE LA REPONSE, a partir de la question, des passages et du texte produit.
Tout ce qui suppose de connaitre la bonne reponse est disponible sur un jeu dore et
jamais chez l'utilisateur — ce qui ecarte d'emblee la difficulte declaree du cas.

Mesure sur 39 reponses, dont 12 portent au moins un verdict negatif :

                          saines   suspectes
  couples a verifier         2        5,5     SEPARE
  articles cites             2        4       SEPARE
  longueur                 956      1913      SEPARE
  score du 1er passage     0,663    0,671     ne separe pas
  dispersion des scores    0,114    0,105     NE SEPARE PAS

CE QUI NE MARCHE PAS MERITE D'ETRE RETENU. La dispersion predit l'echec de
RECUPERATION (D11) et ne predit pas l'infidelite. S'en servir ici aurait paru
naturel — signal deja mesure, deja valide, deja disponible — et n'attrape que
2 suspects sur 12 au meme taux d'appel. Deux modes de defaillance, deux signaux.

Declencheur retenu : le NOMBRE DE COUPLES, qui est a la fois le cout et le risque.
Seuil a 3 : 56 % des reponses verifiees, 10 suspects sur 12. Un seuil sur la
longueur fait legerement mieux mais ne dit rien du cout.

L'interpretation tient : une reponse longue citant beaucoup d'articles est une
reponse ou le modele a SYNTHETISE, et c'est la qu'il deborde.

Le seuil est un parametre, pas une constante — 39 reponses, le sens de la separation
est net, le seuil exact non. Un test l'interdit de le figer.

couples_a_verifier() porte aussi la correction d'appariement : la REUNION des
passages, jamais le premier, sous peine des 30 % de faux negatifs deja mesures.

6 tests ajoutes, 1814 passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La logique de D26 se generalise en deux temps qu'il ne faut pas confondre : est-ce
que ca aide, puis seulement quand est-ce que ca aide. COLAIG_HYDE_ENABLED existe
dans config.py depuis l'origine sans avoir jamais franchi le premier.

                 cas complets   gagnes  perdus
  temoin           89/104  86 %
  HyDE w=0,3       90/104  87 %      1       0
  HyDE w=0,5       88/104            1       2
  HyDE w=0,7       88/104            3       4

Un cas gagne pour 0,76 s par question — +38 % de latence sur une reponse qui en
prend deux. Aux poids eleves il gagne et perd a peu pres autant : c'est du bruit.

POURQUOI IL ECHOUE LA OU ON L'ATTENDAIT. HyDE est concu pour les ecarts de
vocabulaire entre question et documents. Or nos cas d'ecart mesures — mp-069 et
mp-070, poses en « CCAG » et « CCAP », dont l'article attendu est au-dela du rang 60
— ne figurent dans AUCUN gain, a aucun poids. La raison est logique une fois posee :
la reponse hypothetique est produite par le meme modele, qui emploie lui aussi le
vocabulaire du praticien. HyDE ne franchit pas un fosse qu'il reproduit.

Le drapeau reste a false avec cette mesure pour motif. Il n'y a rien a declencher :
on ne construit pas une porte devant une piece vide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Derniere option couteuse jamais mesuree. Le temoin n'est pas vide : le decoupage par
article prefixe deja chaque passage du titre et de sa position dans le code. La
question est donc si un contexte ecrit par un LLM vaut mieux qu'un chemin gratuit.

  sans prefixe du tout              90/104  87 %
  chemin hierarchique (production)  89/104  86 %
  chemin + contexte LLM             91/104  88 %

LE PREFIXE HIERARCHIQUE N'AIDE PAS, ET LE CODE AFFIRMAIT LE CONTRAIRE.
reference_l15.py porte ce commentaire : « Le prefixe est essentiel : sans lui, Les
reference_l15.py presentait le prefixe hierarchique comme essentiel, en expliquant
que sans lui un article perdrait le contexte permettant de le retrouver. D28 a mesure
l'inverse en isolant la variable : 89 cas complets avec, 90 sans.

Le commentaire porte desormais la mesure et l'origine de l'erreur — une comparaison
entre deux strategies de decoupage dont on avait attribue l'ecart au seul prefixe.

Le prefixe reste : il ne nuit pas, il ne coute rien, il rend les passages lisibles.
Mais presenter un choix non mesure comme une necessite est exactement ce que ce
chantier cherche a ne plus faire, et le laisser ecrit aurait fait recommencer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le constat qui l'imposait : « clauses administratives particulieres », « reglement de
consultation », « acte d'engagement » avaient ZERO occurrence dans les 1806 articles
du code. Cinq echecs de recherche etaient hors de portee de tout reglage — article
attendu au-dela du rang 60 — parce que la question et le corpus n'avaient aucun mot
en commun. Ce ne sont pas des mots du code, ce sont des mots des CCAG.

UN SEUL CCAG. Il en existe six, ce sont des REGIMES PARALLELES : l'article 20 du CCAG
Travaux n'est pas celui du CCAG PI. Les verser tous rejouerait le defaut que D24 vient
de supprimer. Or un marche releve d'un seul CCAG, choisi par son objet. Un dossier,
une instance, un perimetre.

SOURCE. legi_arrete du meme instantane epingle. La partition pese 4,7 Go en 18
fichiers et le CCAG y est disperse ; DuckDB lit le parquet distant par plages et ne
rapatrie que le filtre : 8 secondes au lieu de plusieurs gigaoctets. 61 articles,
117 fragments, 8 chapitres. Corpus a 760 articles, 57 documents, 0,82 Mo.

TROIS DEFAUTS SILENCIEUX ATTRAPES EN CONSTRUISANT.

1. L'arrete ECRASAIT SON PROPRE CAHIER : il porte ses articles 1 a 5, de meme numero
que ceux du cahier annexe. Sans filtre, « article 4 » rendait l'application a
Saint-Barthelemy au lieu des Pieces contractuelles — un article faux sous un numero
juste, que nul controle de provenance n'aurait vu.

2. La mention de source annoncait « Code de la commande publique » en tete d'un CCAG.
Le genre d'etiquette qu'un lecteur croit sans verifier.

3. Le motif d'en-tete TRONQUAIT les identifiants : ([A-Za-z0-9- ]+) s'arrete sur le
« e » accentue de Preambule, et le passage entrait dans l'index sous un nom qui
n'existe nulle part. mp-069 le cherchait en vain ALORS QU'IL REMONTAIT AU RANG 1.
Quatrieme copie de ce motif dans le chantier, quatrieme divergence.

RESULTAT : 95/109 articles attendus remontes, 87 %, contre 89/104 avant. Jeu dore a
130 cas dont 22 negatifs. Cinq des six cas CCAG passent, la plupart au rang 1 —
ordre de priorite des pieces, calcul des penalites, delai de reception, projet de
decompte, forme des notifications. mp-070 echoue toujours mais echouait deja : pas
de regression, un cas gagne.

Vocabulaire enfin present : CCAP 9, CCAG 99, acte d'engagement 4, decompte general
22, ordre de service 39 — tous a zero auparavant.

1814 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Decision de perimetre : couvrir la REDACTION et non la seule passation. Elle renverse
D29, qui ne retenait qu'un cahier.

POURQUOI LES SIX CCAG ALORS QUE D24 ECARTE LE LIVRE DEFENSE. Les deux cas paraissent
identiques — regimes paralleles, articles jumeaux aux contenus differents. Ils
different sur UN SEUL POINT, et il est decisif : R2322-14 ne se distingue pas de
R2122-8 sans expertise, tandis que « CCAG Travaux 20 » porte le nom de son cahier.
On ecarte ce qu'on ne peut pas voir, on garde ce qu'on peut lire.

CORPUS : 1026 articles, 107 documents, 1,52 Mo. 699 du code, 295 pour les six CCAG,
27 pour les annexes 2 (seuils), 7 (profils d'acheteurs), 12 (signature electronique)
et 13 (modeles de garantie).

QUATRE DEFAUTS SILENCIEUX, DONT UN QUI SERVAIT DU DROIT PERIME.

1. LEGI NE FERME PAS LES VERSIONS ANTERIEURES. L'avis sur les seuils compte CINQ
versions, toutes VIGUEUR avec end_date 2999, de 2018 a 2026. La regle temporelle ne
les departage pas : le corpus servait les seuils de 2018 — 144 000 euros — comme s'ils
etaient en vigueur. Regle ajoutee : par numero, la version applicable la plus recente.
Le corpus porte maintenant ceux du 14/01/2026 : 140 000, 216 000, 5 404 000 euros.

2. L'arrete ecrasait son propre cahier — memes numeros 1 a 5.

3. Les articles sans numero s'ecrasaient entre eux : cinq versions sous le meme
en-tete, quatre disparaissaient.

4. Le motif d'en-tete perdait 183 articles : ni tiret cadratin ni accents. 1026
indexes sur 1026 desormais.

LE JEU DORE SUIT SON CORPUS. Cinq cas negatifs devenus faux — seuils europeens,
modeles de garantie, profils d'acheteurs, CCAG — requalifies en positifs. Meme defaut
que mp-032, et il REVIENT A CHAQUE ENRICHISSEMENT. mp-124 merite d'etre note : ecrit
comme negatif le matin, rendu faux le soir par l'ajout de l'annexe 7.

ET LE CONTROLE DES MONTANTS A ARRETE UNE INVENTION : la reponse de mp-012 annoncait
5 538 000 euros pour le seuil travaux, ecrit de memoire. Le corpus dit 5 404 000.

MESURE : 98/113 articles attendus remontes, 87 %, contre 89/104 avant. Jeu dore a 135
cas dont 22 negatifs. Le taux tient a corpus multiplie par 1,5 — enrichir sans
degrader etait le resultat qui comptait.

1814 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
RECONNAISSANCE LITTERALE. Les CCAG et les annexes ne numerotent pas selon un motif :
« CCAG Travaux 4 », « Annexe 2 — Seuils de procedure — texte 1 ». Aucune expression
reguliere ne les decrit, et en ecrire une assez large les couvrant attraperait la
moitie de la prose. Mais ces identifiants sont CONNUS — le corpus les porte en
en-tete. articles_cites accepte donc un vocabulaire, cherche litteralement : exact par
construction, sans faux positif possible puisqu'il ne trouve que ce qui existe.

Sans elle, une reponse citant correctement le CCAG etait vue comme ne citant RIEN, et
garde_fou_reponse l'aurait remplacee par un refus. Mode de defaillance deja rencontre
deux fois — articles preliminaires L1 a L6, puis CCAG.

Le piege du prefixe est ferme : « CCAG Travaux 4 » ne se declenche pas dans « CCAG
Travaux 41 ». La frontiere interdit ce qui PROLONGE le numero et rien d'autre — une
premiere version excluait tout point, et « CCAG Travaux 41. » en fin de phrase
n'etait plus reconnu.

CINQ COPIES DU MEME MOTIF, CINQ DIVERGENCES. Le motif d'en-tete [A-Za-z0-9- ]+
existait en cinq exemplaires, et chacun a produit une mesure fausse avant d'etre
trouve :

  test_jeu_dore.py            refusait « CCAG Travaux Preambule » comme inexistant
  index_corpus.py             183 articles absents de l'index sur 1026
  reference_l15 decoupage     passages indexes sous « CCAG Travaux Pr », tronque
  reference_l15 reconnaissance six cas dores comptes en echec, bon passage remonte
  reference_generation.py     843 articles reconnus sur 1026 — citation juste
                              comptee comme fantome

La derniere a ete trouvee PENDANT la mesure qu'elle faussait, et l'execution arretee.

Toutes convergent desormais : le vocabulaire est constitue une fois depuis les
passages, et passe a la reconnaissance. Une chose qui doit etre vraie partout ne doit
etre ecrite qu'une fois.

1818 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…sion cede

1026 articles — code livre Ier, six CCAG, quatre annexes — 135 cas dores, prompt
durci, k=6, raisonnement coupe.

                                   code seul (124)   expert (135)
  refuse a chaque fois                  21/21            22/22
  articles attendus remontes         89/104 86 %      98/113 87 %
  cite l'article attendu             84/100 84 %      86/112 77 %
  citation hors contexte                   18               33
  fantome / montant invente             6 / 2            7 / 0
  garde-fou rendue/annotee/rempl.    140/16/7        138/27/11
  reponses propres                   140/163 86 %    138/176 78 %

CE QUE L'ENRICHISSEMENT GAGNE : de la couverture. Dix questions de plus trouvent leur
article, et surtout des questions que le code seul ne pouvait pas traiter — ordre de
priorite des pieces, calcul des penalites, delai de reception, seuils europeens
chiffres. La recherche tient a 87 % sur un corpus qui a grossi de moitie, ce qui
etait le resultat qui comptait.

CE QU'IL COUTE : de la precision. Les citations hors contexte passent de 18 a 33. Un
corpus plus riche donne au modele davantage d'articles reels a convoquer de memoire et
davantage de passages voisins qui se ressemblent.

LE GARDE-FOU ABSORBE L'ECART : 27 reponses annotees et 11 remplacees contre 16 et 7.
L'utilisateur recoit la reponse ET l'avertissement, rien ne passe en silence. C'est
ce qui rend l'enrichissement soutenable.

LE REFUS RESTE PARFAIT — 22/22. C'etait 0/8 au premier jour de mesure. Sur un corpus
deux fois plus large et avec cinq cas negatifs entierement nouveaux, il ne bouge pas.

1818 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
La calibration en faux negatifs ne disait que la moitie. Restait a savoir ce que vaut
un « etaye ». Le jeu dore ne contient pas de couples infideles — il est ecrit pour
etre juste — donc on les fabrique : reponses justes, derives controlees, chacune
reproduisant une faute reellement observee dans ce chantier.

104 derives sur 45 cas :

  ajout d'une affirmation non etayee     45/45   100 %
  seuil deplace                          10/10   100 %
  negation inversee                      11/14    79 %
  portee, peut devient doit              14/23    61 %
  suppression de la borne                 6/12    50 %
  ENSEMBLE                               86/104   83 %

LE RESULTAT EN UNE PHRASE : le verificateur voit ce qu'on ajoute, pas ce qu'on
retire. Cent pour cent sur l'ajout et le chiffre deplace, cinquante pour cent sur la
suppression d'une condition — c'est-a-dire le defaut que quatre relectures ont trouve
dans un quart du jeu dore.

Et ce n'est pas un caprice : une affirmation tronquee reste VRAIE, et l'extrait la
soutient effectivement. Repondre « etaye » est defendable. C'est le bareme qui n'a
pas de case pour « exact mais incomplet au point d'induire en erreur ».
etaye_partiellement etait cense la porter ; le modele le lit comme « soutenu en
partie », pas comme « soutenu mais ampute ». La consigne le dit pourtant, regle 4.

CE QUE CELA COMMANDE : le verificateur peut etre branche pour ce qu'il fait bien —
detecter l'inference qui deborde et le chiffre deplace, la ou il est parfait. Il ne
peut PAS garantir la completude d'une reponse, ce que quatre relectures humaines ont
du faire. Piste a mesurer : un cinquieme verdict « exact mais incomplet ». Le passer
par la consigne seule a deja echoue.

CORRECTION D'UNE LECTURE HATIVE : sur 12 cas, la negation etait detectee 1 fois sur
3, et j'en avais conclu que le verificateur manquait la derive la plus dangereuse.
Sur 45 cas, c'est 11 sur 14. Trois observations ne font pas une mesure — le reflexe
qui a sauve ce chantier toute la journee vient de servir contre moi-meme.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…chap

D33. L'enrichissement avait fait monter les citations hors contexte de 18 a 33.
Diagnostic AVANT toute correction : aucune n'est un article de CCAG, les 25
references distinctes sont toutes des articles du code. Le modele ne confond donc
pas les six cahiers — l'inquietude de D24 ne se verifie pas ici. Il puise dans sa
memoire DU CODE, parce que le corpus a grossi de 50 % et que k etait reste a 6 :
moins de passages du code par question.

Teste :
                                    k=6        k=10
  cite l'article attendu         86/112 77 %  92/110 84 %
  citations hors contexte            33          24
  citations fantomes                  7           5
  garde-fou rendues              138/176 78 % 149/176 85 %
  annotees / remplacees            27 / 11     24 / 3
  refus / tronquees               22/22 · 3   22/22 · 3

k=10 restaure le taux de reponses propres — 85 % contre 86 % sur un corpus deux fois
plus petit — en couvrant 50 % de corpus en plus.

Cela corrige D18 : la profondeur avait ete arbitree a k=6 le matin meme, sur un
corpus deux fois plus petit ET dans une comparaison confondue par la troncature. Le
raisonnement coupe, cette confusion disparait — 3 troncatures dans les deux cas — et
la profondeur se juge pour elle-meme. k n'est pas une constante, c'est une fonction
de la taille du corpus.

D34. run() etait le dernier point du MessagingProtocol non verifie. Deux obstacles de
nature differente, traites separement.

L'ARBITRAGE : une invitation adressee au compte bot est en attente depuis un autre
ministere, et _on_invite l'aurait acceptee. Le callback est debranche AVANT tout appel
reseau, et le script le verifie par assertion — rien n'est accepte qui ne vienne de
l'utilisateur ou de l'agent.

L'ENVIRONNEMENT : verification en conteneur Linux. Contre le vrai serveur —
auto-join debranche 4 -> 3 callbacks, boucle vivante apres 45 s, 15 salons charges,
jeton de synchro obtenu, appareil revoque.

Reste non verifie : l'auto-join lui-meme, precisement parce qu'on le debranche.

DEUX CORRECTIONS ISSUES DE L'EXECUTION REELLE.

import olm etait le mauvais critere : matrix-nio 0.26 remplace libolm par vodozemac.
Mesure en conteneur — olm ABSENT, vodozemac present, chiffrement accepte. Le controle
ecrit ce matin aurait REFUSE DE DEMARRER SUR UNE INSTALLATION PARFAITEMENT CAPABLE, en
reclamant un paquet dont elle n'a pas besoin. _exiger_e2e teste desormais la CAPACITE
et non son implementation. Un garde-fou qui bloque ce qui fonctionne est pire
qu'absent : on le contourne, et tout ce qu'il protegeait avec lui.

Un appareil neuf ne lit pas l'historique chiffre — undecryptable Megolm event from a
unknown device, portant l'identite du bot lui-meme. Tout redeploiement creant un
nouvel appareil perd l'acces aux messages anterieurs.

1819 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LA CI N'AVAIT JAMAIS TOURNE SUR UNE SEULE LIGNE DU CHANTIER. Verifie le 23/08/2026 :
ci.yml se declenche sur main et les PR vers main, or le chantier avance par lots sur
des branches lot/* qui ne visent pas main avant d'etre finies. Les seuls workflows
executes recemment sont les images et le chart Helm. 1819 tests passaient sur un
poste, et rien ne le verifiait ailleurs. Une CI qui ne s'execute pas sur les branches
de travail ne protege que ce qui est deja livre — c'est-a-dire trop tard.

Elle tourne desormais sur main, lot/**, chantier/**, toute PR, et a la demande. Le
--ignore de test_live.py est retire : il skippe tout seul depuis D14, l'exclure
masquerait 41 tests qui savent se taire.

_chantier/reference.json : la reference L1.5 devient LISIBLE PAR UNE MACHINE. Elle
vivait dans des documents Markdown, et un chiffre ecrit dans un document ne bloque
rien — il se lit apres coup, quand la degradation est deja livree. Chaque seuil porte
son motif, parce qu'un seuil sans motif se relache au premier echec, quand personne
ne sait plus ce qu'il protegeait.

verifier_reference.py rejoue la mesure et SORT EN ECHEC si un seuil est franchi :
rappel de la recherche, refus systematique, hors contexte, fantomes, montants
inventes, tronquees, reponses rendues sans reserve.

reference.yml l'execute chaque lundi et a la demande. Pas a chaque poussee : vingt
minutes d'appels au modele n'apprendraient rien, la mesure ne bougeant que si le
corpus, le jeu dore, le prompt ou les reglages changent. Le job verifie d'abord la
presence des secrets et le DIT — un workflow rouge faute de cle apprend a ignorer le
rouge.

DEUX DEFAUTS CORRIGES POUR QUE CELA PUISSE SEULEMENT FONCTIONNER.

La profondeur de la recherche valait 6 en dur alors que la generation est passee a 10
(D33) : mesurer a une profondeur que la production n'emploie pas ne dit rien de la
production. Alignee — et la recherche gagne 7 cas au passage, 105/113 soit 93 %
contre 98/113.

Les harnais lisaient les cles UNIQUEMENT dans un .env du poste, ce qui les rendait
inexecutables en integration continue : la porte aurait ete inerte sans que rien ne
le signale. L'environnement d'abord, le fichier ensuite. Huitieme famille de
duplication du chantier — sept exemplaires de cle_ssp() alignes d'un coup.

1819 tests passent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Execution complete de bout en bout :

  recherche complets     0.929  >= 0.88   reference 0.929
  refus systematique       1.0  >= 0.95   reference 1.0
  cite l'attendu         0.835  >= 0.78   reference 0.836
  hors contexte           26.0  <= 34     reference 24
  fantomes                 8.0  <= 10     reference 5
  montants inventes        1.0  <= 2      reference 0
  tronquees                4.0  <= 12     reference 3
  garde-fou rendues      0.823  >= 0.78   reference 0.847

  Aucune regression.

CE QUE CE REPLICAT APPREND. Les seuils avaient ete poses au jugement ; cette
execution donne le BRUIT REEL, meme corpus, meme jeu dore, meme jour. La generation
varie meme a temperature 0,1 : trois fantomes d'ecart, deux citations hors contexte,
et surtout 2,4 points sur le taux de reponses rendues sans reserve. La recherche, elle,
est deterministe — ecart nul.

Les seuils tiennent. Le plus serre, garde_fou_rendues a 0,78, garde environ quatre
points de marge au-dela de la variance mesuree. Le plus lache, tronquees a 12 pour une
valeur de 3, protege contre un rallumage du raisonnement et non contre du bruit.

Un seuil pose au jugement se defend mal le jour ou il echoue : on le relache. Un seuil
adosse a une variance mesuree se defend. La variance est donc consignee dans
reference.json, avec sa limite ecrite noir sur blanc : UN SEUL REPLICAT NE FAIT PAS
UNE VARIANCE. Si une porte echoue de peu, rejouer avant de conclure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le principe 4 impose que tout contenu externe entre dans un prompt balise,
jamais brut. Ce qui existait n'en donnait que l'apparence : trois sites
entouraient les passages de <<<DOCUMENT>>> ... <<<FIN DOCUMENT>>> en inserant
le contenu tel quel. Un document contenant ce marqueur fermait sa propre
balise, et il suffisait de deposer un fichier sur l'espace pour la forger.
Le nom de la source entrait de la meme facon : un nom de fichier est choisi
par celui qui depose le document.

Le motif etait ecrit trois fois. La duplication que la regle 3 du nouveau
module annonce comme le danger s'etait donc deja produite.

colaig/security/wrap.py devient le point de passage unique. Onze sites
portes, sur les cinq familles : passages RAG des trois agents, tous les
role:"tool" de l'orchestrateur, skills, echantillons du specializer,
extraits du contextualizer, analyse documentaire et resume.

Deux sites meritent d'etre nommes. Le champ instructions du handshake MCP
etait concatene au message system : un tiers reseau obtenait l'autorite du
systeme. Il reste transmis, mais comme donnee. Et le specializer derive le
persona de l'espace depuis son corpus et l'ecrit dans la configuration : un
document depose pouvait reecrire le system_prompt de l'instance.

Un site reste non balise deliberement : verificateur_fidelite.py, dont le
taux de detection est un seuil de reference.json calibre avec ce prompt
exact. Le baliser invaliderait la calibration.

Constat annexe, qui vaut plus que le lot : le harnais de mesure n'utilise
pas le prompt de production. La reference mesure le modele, le corpus et la
recherche, pas l'assemblage reellement livre. Aucun seuil ne garde ce
dernier. Voir D35.

1837 tests verts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rompt livre

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nic01asFr and others added 30 commits September 4, 2026 20:18
…toujours

COLAIG_HYBRID_SEARCH_ENABLED construit un BM25Store par espace, le remplit a
l'indexation, l'ecrit dans bm25.pkl et le recharge au demarrage. Le coeur le
passe bien a retrieve(). L'orchestrateur le recevait dans son constructeur et
ne s'en servait nulle part : ni pour la recherche de son plan, ni pour l'outil
search_documents qu'il enregistre.

retriever.retrieve() n'active la fusion RRF que si bm25_store lui parvient.
Le drapeau etait donc decoratif pour le pipeline agent. La mesure du 04/09
qui concluait « BM25 n'ameliore rien, 37 -> 44 cas non servis » ne mesurait
pas BM25 : elle mesurait deux fois la meme recherche vectorielle.

Les deux chemins passent maintenant par _index_de_l_espace(), un seul endroit
a corriger la prochaine fois. bm25_store n'est transmis que s'il existe :
RetrieverProtocol.retrieve ne le declare pas, et une implementation conforme
au contrat refuserait le mot-cle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mesure_du_pod.py note ce que le modele CITE. Quand il ne cite pas l'article
attendu, deux causes se confondent : le passage lui a ete servi et il ne s'en
est pas saisi, ou il ne lui a jamais ete servi. Seule la seconde se corrige
dans le retriever.

/ask ne rend que le texte. Mais handlers ecrit sources=[...] a chaque echange,
pour le coeur comme pour le pipeline agent : le journal du POD porte donc la
reponse, sans rien rejouer localement. Seule la carte article -> fichier est
lue ici, sur le corpus televerse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rde tout

TROIS DEFAUTS, TOUS DECOUVERTS EN VERIFIANT LA MESURE.

1. LE SEUIL. `score_threshold` est une similarite cosinus (0,3 ; ~0,72 en tete
   sur corpus reel). Apres fusion RRF les scores valent 1/(60+rang), soit 0,016
   au premier rang : les filtrer a 0,3 les elimine TOUS, sans erreur. Le defaut
   ne se voyait pas parce que `albert_reranked` etait pose a True apres tout
   appel au reranker, y compris quand celui-ci repondait « je n'existe pas » —
   le cas permanent sur SSPCloud. Le seuil tombait a 0,001 et laissait passer la
   fusion. La recherche hybride ne marchait en service que par cet accident, et
   le filtrage cosinus etait mort partout ailleurs. Chaque etage annonce
   desormais son echelle ; `_albert_rerank` dit ce qui a reellement eu lieu.

2. LA TRACE. Rien ne disait laquelle des deux recherches tournait. Une campagne
   entiere a conclu « BM25 n'apporte rien » en mesurant deux fois la meme
   recherche vectorielle. La fusion se journalise.

3. LE JOURNAL DES ECHANGES. Le nom du fichier derivait du seul `message_id`, que
   `/ask` ne fournit pas : les 135 questions d'une campagne ecrivaient 135 fois
   LE MEME fichier. Le journal cense « survivre au redeploiement » gardait un
   echange sur 135 — releve aujourd'hui, 1 fichier pour 135 questions. A defaut
   d'identifiant, l'empreinte derive de l'echange lui-meme ; le dedoublonnage
   d'un evenement Matrix redelivre reste entier.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
kubectl logs ne rend que la fin du tampon. Sur une campagne de 135 questions
aux listes de sources longues, il n'en portait que 52 sur 113 : le compte
« porteur servi » se calculait sur une moitie systematique du jeu, celle des
identifiants les plus eleves.

Le journal ecrit par le pod sur le stockage de l'espace est complet, et il
survit au redeploiement — c'est ce pour quoi il a ete fait. Le repli sur le
journal du conteneur reste, avec un avertissement qui dit ce qu'il vaut.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
« CCAG Travaux 4 », « Annexe 2 — Seuils de procedure — texte 1 » : onze cas
dores attendent un identifiant qu'AUCUNE expression reguliere ne decrit. La
voie prevue pour eux existe depuis toujours dans verification_citations
— `identifiants=`, cherches litteralement, exacts par construction — et
reference_generation s'en sert. mesure_du_pod appelait articles_cites(texte)
tout court.

Une reponse citant correctement un de ces articles etait donc comptee comme
ne citant RIEN. Recomptees apres coup, sans rien relancer, les quatre
campagnes du 04/09 y perdaient une a deux reponses justes chacune :

    14:49  63/113 -> 65/113
    18:50  62/113 -> 63/113
    21:31  64/113 -> 66/113
    22:08  69/113 -> 71/113

Le biais est modeste et uniforme — il ne renverse aucune comparaison entre
montages — mais il etait invisible, et c'est la troisieme fois qu'un compteur
decide d'un diagnostic. Le nombre d'identifiants s'affiche desormais en tete
de chaque campagne, comme dans la reference.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…r y remedie

LE POINT DE DEFAILLANCE, IDENTIFIE.

Sur les 102 cas dores dont l'article attendu est un numero de code, le FICHIER
qui le porte est servi 89 fois. Vingt et une reponses ne le citent pas. En les
lisant, le motif est constant : le modele cite les articles VOISINS et declare
que l'information ne figure pas dans les passages fournis.

    attendu R2111-12   servis R2111-15
    attendu R2113-6    servis R2113-4
    attendu L2113-14   servis L2113-12, L2113-13
    attendu R2124-3    servis R2124-4
    attendu R2112-17   servis R2112-8, -9, -10, -12, -13

Le decoupage etant PAR ARTICLE, servir le fichier ne sert pas l'article : un
fichier en porte des dizaines. La recherche ne part pas ailleurs — elle
s'arrete a deux ou trois rangs. Et le refus qui suit est CORRECT au vu de ce
qui a ete servi : le defaut est en amont, dans la granularite.

DEUX CHANGEMENTS.

1. LE JOURNAL DIT QUELS PASSAGES ONT ETE SERVIS, pas seulement quels fichiers.
   Sans cela on ne distingue pas « le passage attendu a ete servi et le modele
   ne s'en est pas saisi » de « c'est le voisin qui a ete servi » — deux
   constats qui appellent des corrections opposees. Il a fallu le deduire de la
   lecture de 21 reponses ; il se lira desormais. Le pipeline agent y verse
   aussi ce que son outil `search_documents` a rendu.

2. L'ELARGISSEMENT AUX VOISINS, sous drapeau COLAIG_VOISINS_ENABLED, rayon
   COLAIG_VOISINS_RAYON (defaut 1), OFF par defaut. Autour de chaque passage
   retenu, on sert ses voisins immediats du meme document — ceux que le
   decoupage a separes et que le redacteur du code avait ecrits ensemble. Le
   voisin porte le score de son ancre : il n'a pas ete classe, il est servi
   PARCE QUE son ancre l'a ete. Le budget de jetons s'applique apres.

   `get_all_active_chunks` n'appartient pas a VectorStoreProtocol : un magasin
   qui ne l'expose pas rend la recherche telle quelle, sans echouer.

Drapeau a trancher par la mesure, pas par l'intention : peremption au prochain
lot si la campagne ne montre pas de gain.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le decoupage etant par article, un fichier en porte des dizaines : servir
094-…contenu-du-marche.md ne sert pas R2112-14. Tant que le journal ne portait
que des noms de fichiers, cette mesure surestimait le service — 21 cas sur 102
comptes servis alors que seul un VOISIN de l'article attendu l'avait ete.

Le journal porte desormais la section de chaque passage. Le script la lit, et
distingue les deux causes qu'il confondait : l'article n'est pas trouve, ou il
est trouve a quelques rangs pres.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Campagne temoin du 04/09 22:53, meme image, drapeau eteint, jugee au grain du
PASSAGE pour la premiere fois :

    refus sur negatifs           22/22
    cite l'attendu               75/113
    article attendu servi        79/113
    article attendu NON servi    34/113
      dont son FICHIER etait servi   21

Sur les 79 cas ou l'article est servi, 70 sont cites : la redaction fait son
travail. Le blocage est bien ce qu'on donne a lire — et 21 des 34 manques ne
sont pas des recherches qui partent ailleurs. Le bon document est trouve, et
c'est l'article VOISIN qui est servi.

Rien a reindexer : l'elargissement se fait a la lecture.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e cite

La carte article -> fichier cherchait le motif des codes dans le TEXTE. Elle
rattachait donc un article a tout fichier qui le MENTIONNE — un renvoi
suffisait — et le compte « fichier servi » s'en trouvait gonfle.

Elle laissait par ailleurs sans porteur les onze cas dores dont l'attendu ne
suit aucun motif (« CCAG Travaux 4 », « Annexe 2 — Seuils — texte 1 ») : ils
etaient ranges parmi les recherches qui partent ailleurs, alors que la mesure
ne savait simplement pas ou l'article habitait.

Les en-tetes du corpus donnent 1021 articles, un fichier chacun.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
COMPARAISON DU 04/09, MEME IMAGE, UN SEUL REGLAGE CHANGE :

                                    temoin   voisins
    refus sur negatifs               22/22     19/22
    cite l'attendu                  75/113    78/113
    article attendu servi           79/113    81/113
    non servi ALORS QUE son fichier
      etait servi                       22         7

L'elargissement fait ce pour quoi il est ecrit : les quasi-manques tombent de
22 a 7. Mais le total servi ne bouge presque pas, parce que treize autres cas
ont PERDU leur document.

Le journal dit pourquoi : le nombre de passages effectivement servis est le
meme des deux cotes — mediane 10, moyenne 14,7 contre 14,8. Les voisins n'ont
rien ajoute, ils ont REMPLACE. Le budget etait sature avant comme apres.

    TOKEN_BUDGET = 6000  # code en dur

La fenetre du modele n'etait ecrite nulle part ; elle a ete RELEVEE sur
l'endpoint en poussant une requete jusqu'au refus, plutot que supposee :

    « This model's maximum context length is 131072 tokens »

Le budget documentaire consommait 4,6 % de ce que le modele accepte. Il se
regle desormais par COLAIG_BUDGET_JETONS, defaut inchange a 6000 — c'est la
mesure qui choisira la valeur. Et la troncature passe en INFO : c'est elle qui
decide de ce qui est servi, elle ne se journalisait qu'en DEBUG.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
15 % de la fenetre relevee (131072). Assez pour que les voisins s'ajoutent au
lieu de remplacer, sans diluer l'attention sur cent passages.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deux campagnes du 05/09, MONTAGE STRICTEMENT IDENTIQUE, sans redeploiement ni
changement d'aucune sorte entre les deux :

    cite l'attendu        68/113   puis   75/113
    article attendu servi 75/113   puis   84/113
    quasi-manques             15          12

Sept et neuf points d'ecart pour zero changement. Or les ecarts qu'on cherchait
a trancher aujourd'hui valaient trois a dix points :

    recherche hybride      65, 63  contre  66, 71
    elargissement          75      contre  78
    budget documentaire    78      contre  68 puis 75

AUCUNE de ces comparaisons, faite a une repetition, ne dit quoi que ce soit. Il
faut le reconnaitre avant de continuer, sinon on choisit des reglages au tirage.

Un seul indicateur bouge hors du bruit, et il correspond au mecanisme : les cas
ou l'article attendu n'est pas servi ALORS QUE son fichier l'est tombent de 22
sans elargissement a 7, 15 et 12 avec. L'elargissement sert bien les voisins ;
cela ne s'est simplement pas encore traduit en couverture mesurable.

LA SOURCE N'EST PAS UN REGLAGE OUBLIE : l'Analyseur et l'Orchestrateur etaient
deja a 0,1. Mais ce sont eux qui ecrivent les requetes — mediane 2 par question,
moyenne 2,8, jusqu'a 9 — et une reformulation tiree autrement ramene d'autres
passages. « L'article est servi » mesure donc aussi le tirage.

Les poser a 0 rend le decodage glouton. Ce n'est pas un reglage de mesure
seulement : ces deux agents DECIDENT, ils ne redigent pas. La meme question
posee deux fois ne doit pas chercher ailleurs.

Prochaine etape : deux campagnes identiques a 0 pour mesurer la dispersion
residuelle. Sans ce chiffre, aucune comparaison de montage n'est interpretable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…gne a l'autre

Deux campagnes identiques du 05/09 donnaient 68/113 puis 75/113 — sept points
d'ecart. Compare cas par cas, le tableau est tout autre :

    cas qui basculent   25 sur 113  (22 %)
      gagnes en B       16
      perdus en B        9
    cas identiques      88
    refus qui changent   3 sur 22

L'agregat ne bougeait que de sept points parce que seize gains compensaient
neuf pertes. Ce n'est pas une mesure stable a sept points pres : c'est une
mesure dont un cas sur cinq change d'issue sans qu'on ait touche a rien.

Un ecart de montage inferieur a ce bruit-la n'est pas interpretable — ce qui
disqualifie, a une repetition, toutes les comparaisons de la journee.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Comparer deux agregats (73 contre 77) ignore que ce ne sont pas les MEMES cas.
Le test des signes ne regarde que les cas DISCORDANTS — ceux qui reussissent
d'un cote et echouent de l'autre — parce que les cas identiques ne distinguent
pas les deux campagnes.

    deux campagnes IDENTIQUES a 0,1   16 gagnes /  9 perdus   p = 0,23
    deux campagnes IDENTIQUES a 0     11 gagnes /  7 perdus   p = 0,48
    voisins eteints contre allumes    13 gagnes / 10 perdus   p = 0,68

L'elargissement aux voisins ne se distingue pas du hasard, et il s'en distingue
MOINS que deux campagnes identiques entre elles. Il en va de meme, a une
repetition, de la recherche hybride et du budget documentaire.

Ce qui reste etabli : les cas ou l'article attendu n'est pas servi ALORS QUE son
fichier l'est passent de 22 sans elargissement a 7, 15, 12 avec. L'effet
mecanique est la ; il ne se transmet pas a la citation, ou le bruit le noie.

TEMPERATURE ZERO POUR LE SYNTHETISEUR. Les 18 bascules entre deux campagnes
identiques s'attribuent en comparant les passages servis de part et d'autre :

    11  la recherche a servi autre chose
     7  MEME passage servi, redaction differente

Le commentaire pose le 02/09 disait « a remesurer : si l'inconstance ne cede
pas, la cause est ailleurs et ce reglage doit repartir ». Elle n'a pas cede, et
sept bascules sur dix-huit tiennent a la seule redaction.

CE QUE LE JEU DORE PEUT DETECTER. Avec 18 discordants, un effet net de cinq cas
donne p ~ 0,36 : indetectable. Ce jeu, a une repetition, ne tranche qu'un effet
d'une douzaine de cas. Toute comparaison plus fine demande de noter chaque cas
sur plusieurs essais, pas de moyenner des agregats.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SIX CAMPAGNES, JUGEES AU GRAIN DU PASSAGE, SUR LES 113 CAS PORTEURS :

    article attendu TOUJOURS servi   51
    servi PARFOIS                    53      <-- un cas sur deux
    JAMAIS servi                      9

Le probleme n'est pas que la recherche ne trouve pas. C'est qu'elle trouve une
fois sur deux. Neuf cas seulement echouent de facon reproductible ; cinquante-
trois basculent d'une campagne a l'autre sans qu'on ait touche a quoi que ce
soit — ce qui explique aussi pourquoi deux campagnes IDENTIQUES different sur
dix-huit cas, et pourquoi aucune comparaison de reglage de la journee ne s'est
distinguee du hasard.

LA CAUSE. `_execute_rag_search` cherchait avec `intent.query_reformulated`, et
avec rien d'autre. C'est une chaine ECRITE PAR LE LLM a chaque tour. La question
de l'usager ne servait JAMAIS de requete — pas une fois dans le pipeline agent.
La recherche heritait donc entierement de l'instabilite du modele qui l'avait
formulee, et le service ne rend pas deux fois la meme chaine, meme a temperature
nulle.

LE SOCLE. La question posee est desormais toujours l'une des requetes. Elle ne
remplace pas la reformulation — celle-ci apporte le vocabulaire du domaine la ou
l'usager emploie le sien — elle lui ajoute un ancrage qui ne bouge pas d'un tour
a l'autre. `retrieve_many` groupe la vectorisation : deux requetes, un seul
aller-retour. Un passage servi par les deux ne prend pas deux places.

`Intent.query_posee` est ajoute dans models.py ; protocols.py n'est pas touche.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
L'agregat d'une campagne melange deux choses : les cas que le systeme sait
traiter, et ceux qu'il traite au tirage. Cet indicateur les separe — un cas
compte s'il est servi A TOUS LES COUPS.

Reference avant le socle de requete, sur les deux campagnes a temperature nulle
du 05/09 :

    ARTICLE SERVI   toujours 73   parfois 14   jamais 26
    ARTICLE CITE    toujours 66   parfois 18   jamais 29

Un reglage qui fait passer des cas de « parfois » a « toujours » se voit ici ;
un reglage qui deplace le tirage sans rien stabiliser ne se voit pas — et c'est
exactement ce qu'on demande a une mesure. Comparer a NOMBRE EGAL de campagnes :
« toujours » se restreint mecaniquement quand on en ajoute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ouvait pas trancher

L'avertissement etait deja ecrit en cloture du lot precedent : « tout ecart
inferieur a 3 cas ne veut rien dire tant que trois tirages ne sont pas faits ».
Ils ne l'avaient pas ete, et la journee a compare trois reglages a un tirage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MESURE DU SOCLE, DEUX CAMPAGNES CONTRE DEUX, A NOMBRE EGAL :

    avant   article servi TOUJOURS 73   parfois 15   jamais 25
    avec    article servi TOUJOURS 69   parfois 17   jamais 27
    dispersion residuelle    18 bascules   ->   23

Rien de stabilise. La verification dit pourquoi. Releve sur le journal du
service, 1079 echanges, 17043 passages :

    venus de l'outil search_documents   17043   (100 %)
    venus de la recherche du plan            0   (  0 %)

`execute()` bascule sur `_execute_agentic` des que l'orchestrateur a un client
LLM et un registre d'outils — le cas de l'instance. `_execute_rag_search` sert
le mode deterministe et n'y est JAMAIS appele. Le socle etait pose sur lui.

C'est la septieme fois de la journee qu'une correction porte sur du code qui ne
tourne pas. Le motif est toujours le meme : on lit le code, on trouve l'endroit
qui a l'air juste, et on ne verifie pas qu'il s'execute. La verification coute
une requete au journal.

Le socle passe donc dans `create_search_handler`, avec la note qui dit que c'est
LE chemin. `_handler_de_recherche` la porte aussi : la prochaine correction de
la recherche ira au bon endroit sans qu'il faille le redecouvrir.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
                             avant        avec le socle
    article servi TOUJOURS      70                  94
                  parfois       17                   7
    article cite  TOUJOURS      64                  86
    agregat                     76, 75          93, 87
    bascules entre deux
      campagnes IDENTIQUES      23 (20 %)        8 (7 %)

Quatre comparaisons croisees : p = 0,000 / 0,001 / 0,029 / 0,052. Le systeme
n'est pas seulement meilleur, il est devenu stable — et les reglages qu'on ne
pouvait pas trancher redeviennent mesurables.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…4-1 et R2194-2

DIAGNOSTIC DES DOUZE CAS JAMAIS SERVIS. En les lisant un a un, un motif revient :
le systeme sert des articles du meme groupe, mais jamais celui-la.

    mp-036  attendu R2194-1    servis R2194-6, R2194-7, R2194-8
    mp-018  attendu L2193-3    servis L2193-2, L2193-13, L2193-14

L'elargissement aux voisins devait rattraper exactement cela. Il ne le faisait
pas, et la raison est dans le corpus :

    085-…-modification-du-marche.md
      R2194-1  R2194-10  R2194-2  R2194-3  …

`construire_corpus_mp.py` triait sur `a["number"] or ""` — une chaine. VINGT-DEUX
DES QUARANTE-CINQ fichiers d'articles du Code etaient dans ce cas, la moitie du
corpus. Le voisin de position n'etait donc plus le voisin logique : depuis
R2194-1, l'elargissement servait R2194-10.

CELA EXPLIQUE QU'IL N'AIT JAMAIS MONTRE D'EFFET sur trois campagnes. Une fois
sur deux, il tirait des voisins arbitraires. Je l'ai mesure trois fois sans
soupconner qu'il ne faisait pas ce qu'il annoncait.

La ligne de tete etait fausse pour la meme raison : « 10 articles en vigueur —
R2194-1 a R2194-9 » pour un fichier qui va jusqu'a R2194-10.

TROIS CHANGEMENTS
- le tri de `construire_corpus_mp.py` devient numerique ;
- `reordonner_le_corpus.py` remet les fichiers existants dans l'ordre sans
  retelecharger : il DEPLACE les blocs, refuse d'ecrire si l'ensemble n'est pas
  rigoureusement le meme, et corrige les bornes annoncees. 803 insertions pour
  803 suppressions — aucun contenu touche ;
- un test refuse desormais un corpus dont les articles se suivent dans le
  desordre.

DEUX AUTRES MOTIFS, NON TRAITES ICI. La partie legislative servie a la place de
la reglementaire quand la question porte sur un seuil ou un taux (mp-001,
mp-103, mp-116) ; et le bon article du mauvais CCAG (mp-129 : article 3 des
CCAG Prestations intellectuelles et Marches industriels servis, jamais celui du
CCAG Travaux).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le test de derive comparait les empreintes : reordonner 22 fichiers les change
toutes. Il demande de « reverifier les reponses attendues avant de regenerer le
manifeste » — c'est fait, et de la seule maniere qui prouve quelque chose :
fichier par fichier, l'ENSEMBLE des blocs d'articles est rigoureusement le meme
avant et apres. 22 identiques, 0 different. Le jeu dore reste valide.

La note reste dans le manifeste : une empreinte qui change sans explication est
exactement ce que ce fichier sert a interdire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deux defauts du script, tous deux du genre qui echoue a moitie sans le dire :

- il poussait « ce qu'il venait de reordonner ». Une fois le corpus local
  corrige, cette liste est vide et le script ne faisait rien, en silence. Il
  compare desormais les empreintes avec l'espace.
- il encodait le contenu en base64 DANS LA LIGNE DE COMMANDE. Windows la limite
  a 32 ko : l'echec est tombe apres cinq fichiers sur vingt-deux, corpus a
  moitie reordonne. Le contenu passe par stdin.

22 fichiers alignes sur l'espace, verifie par empreinte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…esure

                             desordonne   ordonne
    article servi TOUJOURS         94         95
                  parfois           7          3
                  jamais           12         15
    article cite  TOUJOURS         86         84
    agregat                     93, 87     89, 90

Test apparie : 7 gagnes / 5 perdus, p = 0,77.

Gain de justesse, pas de performance. Le diagnostic des douze cas laisse deux
motifs non traites : la partie legislative servie a la place de la
reglementaire, et le bon article du mauvais CCAG.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Critere de fin atteint : le decoupage par article est en service et mesure sur
l'instance. Deux campagnes du 05/09, image tronc-2d62c00 :

    refus sur negatifs        20/22, 20/22
    cite l'attendu            89/113, 90/113
    article servi TOUJOURS    95/113

Point de depart du lot : 52/113, sur un compteur qui ignorait un onzieme du jeu.

Un seul gain se distingue du hasard — chercher aussi avec la question posee.
Le reste est soit une correction de justesse sans effet mesurable, soit un
reglage a trancher.

Reserve de forme consignee : neuvieme lot enchaine sur la meme branche depuis
L0.2, 283 commits d'ecart avec le tronc, aucun lot fusionne. La regle « un lot
= une PR » n'est pas tenue et la dette grossit ; la resorber demande un
arbitrage humain.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant