Aller au contenu principal

Module 7 — Construction de la consigne finale et citation des sources

Cinq passages sont retenus, la question de l'utilisateur est prête. Il reste à écrire la consigne qui les combinera pour que le modèle produise une réponse fidèle aux passages, vérifiable par ses citations et prudente quand l'information manque. Ce module traite chacun de ces trois objectifs.

Le squelette de la consigne finale

Une consigne RAG opérationnelle a toujours à peu près la même forme :

Tu es un assistant documentaire pour les procedures internes.
Reponds a la question de l'utilisateur en utilisant UNIQUEMENT les
extraits ci-dessous. Si la reponse n'est pas dans les extraits, dis
« Je ne trouve pas cette information dans les procedures fournies. »
Cite chaque affirmation avec le numero de l'extrait entre crochets.

Extraits :
[1] {texte du passage 1} (source : {source_1}, page {page_1})
[2] {texte du passage 2} (source : {source_2}, page {page_2})
...

Question : {question}

Reponse :

Trois éléments méritent d'être discutés séparément : l'ordre des passages, la consigne d'abstention, et la mécanique de citation.

L'ordre des passages compte

Contre-intuitif mais mesurable : dans un long contexte, les modèles accordent plus de poids au début et à la fin de la fenêtre qu'au milieu. C'est l'effet « perdu au milieu » évoqué au module 1.

Trois ordres possibles :

  1. Meilleur en premier : place le passage le mieux classé en tête. Simple, souvent bon.
  2. Meilleur aux deux extrémités : place le premier en tête, le deuxième à la fin, les autres au milieu. Exploite les deux positions fortes.
  3. Ordre aléatoire : à ne jamais utiliser, mais utile en test A/B pour mesurer la sensibilité du modèle.

Sur le fil rouge, l'ordre « meilleur aux deux extrémités » gagne 2 à 4 points de fidélité par rapport à « meilleur en premier », pour un coût nul. C'est un réglage à activer.

def ordonner_pour_prompt(passages_classes):
if len(passages_classes) <= 2:
return passages_classes
tete, queue = passages_classes[0], passages_classes[1]
milieu = passages_classes[2:]
return [tete, *milieu, queue]

La consigne d'abstention

Sans instruction contraire, un modèle veut répondre. Si les extraits ne contiennent pas la réponse, il en fabriquera une plausible à partir de sa mémoire d'entraînement. C'est précisément l'hallucination que RAG était censé éviter.

L'instruction efficace tient en deux points :

  1. Une règle claire : « uniquement à partir des extraits ci-dessous ».
  2. Une phrase de repli explicite que le modèle doit produire mot pour mot en cas d'échec : « Je ne trouve pas cette information dans les procédures fournies. »

Le second point est crucial. Sans phrase de repli, un modèle poli répond « les documents ne mentionnent pas cette information mais en général... » et enchaîne sur sa mémoire d'entraînement. Avec une phrase de repli fixée et courte, on obtient un signal binaire, mesurable, filtrable en aval.

CONSIGNE_SYSTEME = """Tu es un assistant documentaire pour les procedures
internes. Tu reponds UNIQUEMENT a partir des extraits fournis. Si la
question n'a pas de reponse dans les extraits, tu ecris exactement :
« Je ne trouve pas cette information dans les procedures fournies. »
et rien d'autre. Ne complete jamais avec des connaissances externes."""
L'abstention est une compétence à mesurer

Sur un jeu de questions dont la moitié n'a pas de réponse dans le corpus, un bon système RAG doit s'abstenir sur la moitié. Un système qui répond « je ne sais pas » sur 5 % des questions insolubles est aussi mauvais qu'un système qui hallucine sur 95 % : dans les deux cas, l'utilisateur ne peut pas se fier à la présence ou à l'absence d'une réponse. Le module 8 mesure séparément le rappel utile et le taux d'abstention correcte.

Citations vérifiables

Une citation utile n'est pas un ornement de fin de réponse : elle est intégrée dans le texte, à côté de chaque affirmation.

Bon :

La période d'essai pour un cadre est de quatre mois, renouvelable une fois [1]. Le renouvellement doit être notifié par écrit avant la fin de la période initiale [2].

Mauvais :

La période d'essai pour un cadre est de quatre mois, renouvelable une fois. Le renouvellement doit être notifié par écrit. Sources : [1] [2]

Dans le premier cas, un utilisateur peut vérifier chaque affirmation en cliquant sur son numéro. Dans le second, on ne sait pas quelle source justifie quoi ; les évaluations de fidélité qu'on écrira au module 8 échouent à faire le lien.

Pour ancrer cette contrainte, la consigne doit imposer le format :

Chaque phrase de ta reponse se termine par [n] ou [n, m] indiquant
les extraits qui la justifient. Une phrase sans citation est
interdite.

Cette contrainte tient dans presque tous les modèles modernes.

Traiter les contradictions

Deux extraits disent des choses opposées : la note du 12 janvier annonce quatre semaines de congés, la version du 3 mars en annonce cinq. Le modèle doit-il choisir, dire les deux, ou renoncer ?

La règle applicable est de dire les deux avec leur date, et de signaler la contradiction :

Si deux extraits se contredisent, cite les deux et signale la
contradiction. Si l'un des extraits est explicitement plus recent
(champ « date »), indique cette information sans conclure a la place
de l'utilisateur.

Trancher automatiquement — « on prend le plus récent » — est tentant mais dangereux : parfois la note ancienne est le règlement de fond, la récente une note ponctuelle avec date d'expiration. Laisser cette décision à l'utilisateur, en la rendant visible, est plus sûr.

Le budget de jetons

La consigne finale est limitée par la fenêtre du modèle. Un budget typique pour un modèle 8k :

ÉlémentJetons
Instruction système200
Question de l'utilisateur50
Cinq passages avec métadonnées5 × 400 = 2000
Réponse à générer500
Marge de sécurité200
Total2 950 sur 8 000

À 16k ou 32k, on peut monter à huit passages, mais l'effet « perdu au milieu » réapparaît. Un point de vigilance : la longueur totale doit rester stable d'une requête à l'autre. Un système qui envoie parfois 2 000 jetons et parfois 6 000 rend la facturation imprévisible et augmente la latence perceptible.

Le squelette complet, exécutable

def construire_consigne(question, passages):
passages = ordonner_pour_prompt(passages)
extraits = "\n\n".join(
f"[{i+1}] {p['texte']}\n(source : {p['source']}, page {p.get('page','-')}, "
f"date : {p.get('date_maj','-')})"
for i, p in enumerate(passages)
)
utilisateur = (
f"Extraits :\n\n{extraits}\n\n"
f"Question : {question}\n\nReponse :"
)
return [
{"role": "system", "content": CONSIGNE_SYSTEME},
{"role": "user", "content": utilisateur},
]
Toujours tester la consigne d'abstention en premier

Le jour de la mise en production, prenez dix questions volontairement hors sujet et vérifiez que le système répond dix fois par la phrase de repli. Si l'une d'elles reçoit une vraie réponse, la consigne d'abstention est mal calée, ou un des passages retrouvés est un faux positif convaincant. Ce test tient en cinq minutes et évite quatre-vingt-dix pour cent des dérapages en clientèle.

En résumé

  • La consigne place les extraits au meilleur des positions (début et fin) et impose de répondre uniquement à partir d'eux.
  • L'abstention doit être une phrase de repli littérale, courte, mesurable ; sans elle, le modèle recolle à sa mémoire d'entraînement.
  • Chaque affirmation est suivie de son numéro d'extrait, sous une contrainte de format explicite ; les citations globales en fin de réponse ne se vérifient pas.
  • Les contradictions entre passages sont signalées et datées ; laisser au modèle le rôle d'arbitre est une source d'erreurs difficiles à détecter.

Module suivant : mesurer sur un jeu annoté combien de fois la recherche a raison, combien de fois la réponse est fidèle, et combien de fois l'abstention est correcte.