
Ingénierie de boucle pour la génération RAG : itérer les top-k un par un
L’utilisateur demande « quelle est la date d’entrée en vigueur de cette politique ? ». La récupération renvoie cinq fenêtres de ligne candidates et les transmet toutes au LLM en une seule invite. Le LLM lit les cinq pour extraire la même date que celle que le premier candidat avait déjà. Les morceaux deux à cinq étaient des signatures, des notes de bas de page et un paragraphe sur les dates historiques. Payé pour rien. Envoyez d’abord le top 1, demandez au LLM si cela suffit, et arrêtez quand il dit oui : l’alimentation séquentielle réduit le coût du jeton de 80 % sur cette classe de questions. Le reste de l’article répertorie où le séquentiel gagne, où un appel unique sur l’ensemble de K est la bonne valeur par défaut et comment l’analyseur de questions se répartit entre les deux.
Cet article accompagne Intelligence documentaire d’entreprisela série dont la philosophie est exposée dans Amplify the Expert, la série qui construit l’entreprise RAG à partir de quatre briques (analyse de documents, analyse de questions, récupération, génération). Il se situe entre l’article 8 (génération) et l’article 9 (évolution du mini-RAG) et développe une décision spécifique : comment alimentez-vous les candidats top-K récupérés dans la brique de génération ?. La réponse que donnent la plupart des pipelines est « tout K à la fois ». Cet article catalogue le deuxième régime (séquentiel, top 1 en premier) et montre quand chacun gagne.
La base naïve sur laquelle cet article repousse

Navires RAG naïfs lot par défaut. La récupération renvoie le top 5, le LLM obtient les 5, la réponse revient. Cela fonctionne, et sur des questions difficiles (comparaison, listing), c’est le bon choix. Mais le coût silencieux est payé pour toutes les autres questions : les questions factuelles faciles pour lesquelles le top 1 avait déjà la réponse. Cet article parcourt le deuxième régime et la répartition qui sélectionne par question.

📓 Le notebook exécutable pour cet article est sur GitHub: doc-intel/notebooks-vol1. Il envoie les mêmes questions via une génération par lots et séquentielle, imprime le coût du jeton par question et les deux booléens de suffisance (answer_found, complete_answer_found) qui arrête la boucle et reproduit la table de répartition sur votre propre machine.

1. Deux régimes pour nourrir le top-K jusqu’à la génération
La récupération des mains génère un top-K ordonné. Il existe deux manières de l’alimenter, et elles coûtent très différemment.
1.1 Le pipeline fixe top-K et ses gaspillages
Le modèle par défaut dans la plupart des didacticiels RAG ressemble à ceci :
top_k = retrieval(question, k=5)
answer = generation(question, top_k)
Il se déroule en deux étapes pour chaque question. Le coût du jeton est d’environ Generation_cost (question + 5 morceaux de contexte). La latence est à peu près récupération + un appel LLM. Et le LLM traite volontiers les 5 morceaux même lorsque le top 1 était déjà suffisant et que les morceaux 2..5 n’ajoutaient aucune information.
Concrètement : l’utilisateur demande « quelle est la date d’entrée en vigueur de cette politique ? ». La récupération renvoie 5 fenêtres de ligne où le mot-clé "effective" apparaît. La première est la réponse (« à compter du 1er janvier 2026 »). Les morceaux 2 à 5 sont des signatures, des notes de bas de page et un paragraphe sur les dates d’entrée en vigueur historiques des politiques passées. Le LLM lit les 5 pour extraire la même date que le premier. Sur un corpus d’un seul document, le coût est une erreur d’arrondi. Sur un corpus de 50 000 polices, cela représente de l’argent réel par mois.
1.2 Séquentiel : top-1 en premier, prédicat de suffisance, escalader si nécessaire
Le régime séquentiel traite les K candidats comme une liste ordonnée et demande à la brique de génération de valider la suffisance à chaque étape :
for i, candidate in enumerate(top_k):
answer = generation(question, [candidate])
if answer.answer_found and answer.complete_answer_found:
break
Le signal de suffisance réside à l’intérieur du contrat dactylographié introduit par l’article 8A. Le AnswerWithEvidence le schéma expose answer_found (les informations de la question étaient-elles présentes chez ce candidat ?) et complete_answer_found (la réponse entière était-elle présente, pas un fragment ?). La boucle lit ces champs, pas une heuristique personnalisée.
Dans le date d’entrée en vigueur exemple ci-dessus : la génération s’exécute une fois sur le candidat top 1, le LLM renvoie answer_found = True, complete_answer_found = Truela boucle se termine. Jetons dépensés : Generation_cost (question + 1 morceau de contexte). C’est 1/5 du coût du lot pour cette question, sur un pipeline qui gère des milliers de recherches similaires par jour.
1.3 Batch : envoyer tous les K d’un coup, laisser le LLM arbitrer
Le mode batch conserve le comportement par défaut : un appel LLM voit tous les K candidats et produit une réponse saisie. Son argumentaire repose sur trois types de questions où la séquence se décompose :
- Liste des questions: « lister toutes les exclusions dans ce contrat ». La réponse est chaque candidat correspondant, pas le premier. Sequential s’arrêterait au top 1 (une exclusion trouvée) et manquerait les quatre autres. Le mode batch est le seul mode correct.
- Questions de comparaison: « la prime est-elle plus élevée que celle de l’année précédente ? ». La réponse nécessite que les deux candidats (cette année + l’année dernière) participent au même appel afin que le LLM puisse les comparer. Sequential extrairait chacun indépendamment et perdrait la jointure.
- Récupération de scores serrés: lorsque les scores de pertinence des candidats top-K sont à moins de 5 % les uns des autres, la récupération ne peut pas promouvoir de manière fiable le top 1 vers le sommet. Batch permet au LLM d’arbitrer avec toutes les preuves visibles.
Analyse des coûts : le lot paie toujours le prix Generation_cost (question + K morceaux de contexte) une fois. Paiements séquentiels Generation_cost (question + 1 morceau de contexte) dans le cas facile (top-1 suffisant) et Generation_cost (question + 1 morceau) × K dans le pire des cas (chaque candidat est insuffisant). Sur un corpus d’entreprise typique avec K = 5, le séquentiel est en moyenne moins cher pour les recherches factuelles (~ 80 % du trafic typique) et plus cher pour le référencement/comparaison (~ 20 %).
2. La décision d’envoi : par question, pas par pipeline
L’architecture propre ne sélectionne pas les lots ou les séquences de manière globale. Il sélectionne par question, en utilisant l’analyse question_df rangée à partir de la brique 2. La forme de la question, le modèle de décomposition et l’intention déterminent le choix :

Le répartiteur lit question_df.answer_shape et question_df.decomposition et des itinéraires. Naive RAG n’a aucun moyen de faire cette distinction car il n’a aucune question analysée à lire.
La même table de routage s’applique à tous les secteurs et professions. Différents domaines portent les mêmes modèles de forme et les mêmes décisions séquentielles/par lots en découlent :

Dans chaque ligne, la colonne séquentielle est une valeur saisie unique (Montant, Date, Booléen) et bénéficie du premier stop. La colonne batch est une liste ou une comparaison et nécessite que tous les K candidats soient visibles en même temps. Le tableau du répartiteur couvre les cinq secteurs avec la même logique.
3. Le signal de suffisance
Le mode séquentiel s’appuie sur une chose : la brique de génération indiquant si le candidat qu’elle vient de lire était suffisant. Ce signal et les règles qui arrêtent la boucle vivent ici.
3.1 Où le signal de suffisance réside dans le contrat dactylographié
Le mode séquentiel ne fonctionne que si la brique de génération peut auto-évaluation si le candidat qu’il vient de voir contenait la réponse. Le contrat dactylographié de l’article 8A est ce qui rend cela possible :
class AnswerWithEvidence(BaseModel):
value: Any
evidence: list[Span]
answer_found: bool
complete_answer_found: bool
confidence: float = Field(ge=0, le=1)
caveats: list[str] = []
La boucle séquentielle lit answer_found et complete_answer_foundpas un flotteur de confiance ou une heuristique personnalisée. La séparation nette entre les deux booléens (du modèle 4 de l’article 8ter) est ce qui rend la boucle déterministe. Trouvé + incomplet dit « continuer avec le candidat suivant »; trouvé + terminé dit « arrêt »; pas trouvé dit « continuez ou abandonnez en K ».
Un flottement de confiance imposerait un seuil (par exemple « arrêter à 0,8 »), et ce seuil dérive d’un modèle à l’autre. Deux booléens ne dérivent pas.

3.2 Itération limitée : même la séquence doit s’arrêter
La boucle séquentielle a trois sorties, pas une :
- Suffisance:
answer_found and complete_answer_foundsur un candidat. Arrêtez, envoyez la réponse. - Épuisement: chacun des candidats K vus, aucun n’est suffisant. Arrête, reviens
answer_found = False(une réponse de première classe par laquelle passe le validateur). - Budget: un jeton ou un budget temps fixé par le répartiteur. Utile lorsque le corpus est volumineux et qu’une boucle séquentielle incontrôlable ferait exploser le plafond. Même forme que les noms des itérations limitées M4 (ingénierie de boucle).
Les implémentations séquentielles naïves ignorent la troisième sortie et brûlent les jetons pour toujours dans les cas extrêmes. La version série fixe un budget à l’avance et le répartiteur l’enregistre à chaque appel.
4. Coût et où s’arrête la série
Deux régimes, deux profils de coûts et une frontière que la série ne franchit pas.
4.1 Une comparaison concrète des coûts sur un lot d’une centaine de questions
Pour que le compromis soit réel, voici un verso d’une charge de travail de questions et réponses sur l’assurance d’entreprise :
- 100 questions/jour, K = 5, taille moyenne des morceaux = 600 jetons.
- Référence du lot: 100 × Generation_cost (question + 5×600 jetons) = ~ 330 000 jetons d’entrée par jour pour la génération.
- Séquentiel (80 % top-1 suffisant): 80 × coût_génération (question + 600) + 20 × génération_cost(question + 5×600) (dans le pire des cas pour les 20 % de questions complexes) = ~ 115 000 jetons d’entrée par jour.
Le rapport est 65 % d’économie sur les jetons d’entrée pour la génération sur cette charge de travail. Le rapport exact dépend de la proportion de questions faciles et de la taille des morceaux ; le principe est que le séquentiel est la solution par défaut la moins chère une fois que le contrat dactylographié est en place. Le lot est réservé aux types de questions qui en ont besoin.
4.2 La tentation agentique et où s’arrête la série
Une prochaine étape naturelle consiste à laisser le LLM décider entre le batch et le séquentiel par question (envoi agent). La série s’arrête avant cela. Le répartiteur de la section 2 est répartiteur déterministe (une des trois approches cataloguées à l’article 6C), non LLM-décide. La raison est la même que celle qui prévaut dans toute la série : l’audit. La même question, le même jour, doit être acheminée de la même manière ; un LLM qui replanifie la répartition par appel ne peut pas donner cette garantie.
Si vous avez besoin d’une boucle agentique plus solide (le LLM sélectionne les candidats à examiner, dans quel ordre et avec quelle portée), l’article plus complet sur les boucles RAG adaptatives est le bon endroit. Cet article garde la portée du déterminisme séquentiel vs batch décision motivée par la question analysée.
5. La décision appartient à l’analyseur, pas au LLM
Le pipeline fixe top-K + batch est la bonne valeur par défaut pour les types de questions où chaque candidat compte. Ce n’est pas la bonne valeur par défaut pour les recherches factuelles qui constituent la majeure partie du trafic d’entreprise. La série ajoute deux éléments : le signal de suffisance typé (answer_found, complete_answer_found) de l’article 8A et la table de répartition de la section 2. Ensemble, ils laissent la brique de génération s’arrêter après le premier candidat lorsque cela suffit, et traitent tous les K lorsque le type de question l’exige. La décision est prise par l’analyseur, et non par le LLM, qui conserve la piste d’audit intacte.
6. Lectures complémentaires et sources
La décision séquentielle/batch se situe à la frontière entre la récupération et la génération. Les articles qui encadrent chaque face :



