Une réponse avec ses sources
La génération augmentée par la recherche (en anglais RAG) trouve d’abord des passages. Elle demande ensuite à un modèle de langage de répondre à partir d’eux seuls. Cela compte : un modèle qui répond de mémoire peut se tromper avec assurance, alors qu’un modèle qui montre ses sources se vérifie.
Un visiteur demande à quelle heure ouvre la médiathèque le samedi. Le site trouve deux passages, interroge un modèle et affiche la réponse avec le passage d’où elle vient.
À la fin de cette leçon
Remarque
Je peux bâtir une consigne à partir de ce qui a été trouvé et répondre à partir de cela seulement.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Contexte | les passages que vous mettez dans la consigne | |
| Consigne | le texte que vous envoyez à un modèle | |
| Citation | une marque qui renvoie à un passage | [1] |
| Fondée | qui repose seulement sur les passages donnés | |
| Refus | une réponse qui dit qu’elle ne sait pas |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Mettre les passages dans la consigne
Le modèle n’a pas lu vos documents. La consigne les porte donc. Numérotez chaque passage pour que le modèle puisse en désigner un. Ajoutez ensuite la question et les règles.
Gardez peu de passages, et courts. Chaque mot en plus se paie, et une longue consigne rend la réponse plus difficile à vérifier.
Exemple travaillé : les parties de la consigne
| Partie | Exemple | À quoi elle sert |
|---|---|---|
| Règle | Répondez uniquement à partir des passages | Garde la réponse fondée |
| Règle de citation | Citez chaque passage utilisé sous la forme [1] | Rend la réponse vérifiable |
| Issue de secours | Sinon répondez : Je ne sais pas | Évite une réponse inventée |
| Passages | [1] Le samedi, la médiathèque ouvre à 10 h | Les seuls faits permis |
| Question | À quelle heure ouvre la médiathèque le samedi ? | Ce à quoi il faut répondre |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Vérifier ce qui revient
Une consigne demande, elle n’oblige pas. Le modèle peut citer un passage qui n’existe pas, ajouter un fait qui n’était pas donné ou oublier de citer.
Le programme vérifie donc. Il repère les marques de citation et cherche chacune. Une réponse qui ne cite rien n’est pas présentée comme sourcée.
Exemple travaillé : que faire de chaque réponse
| La réponse | Ce que fait le programme |
|---|---|
| Cite [1] et le passage 1 existe | L’afficher avec la source du passage 1 |
| Cite [7] et il y a deux passages | Ne pas lui faire confiance |
| Ne cite rien | Ne pas la présenter comme sourcée |
| Je ne sais pas. | L’afficher, sans source |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Interroger une fois et vérifier la réponse
Le Run écrit d’abord model.js, une petite aide qui envoie la consigne au modèle du labo. Si le modèle ne répond pas, elle affiche Échantillon enregistré et utilise une réponse écrite dans le script.
Il écrit ensuite reponse.js, le script lui-même. Il bâtit la consigne, fait un seul appel court, repère les citations et dit si la réponse est un refus. Rien de personnel ne va dans une consigne.
Écrire l’aide pour le modèle, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function demander(messages, echantillon, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"statut \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire l’aide pour le modèle, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (erreur) {' ' console.log(\"Échantillon enregistré : aucun modèle n’a répondu (\" + erreur.message + \").\");' ' const liste = [].concat(echantillon);' ' const tour = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return liste[Math.min(tour, liste.length - 1)];' ' }' '}' 'module.exports = { demander };' >> model.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script de réponse, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' '// Les passages qu’une recherche a trouvés, écrits ici pour que le Run coûte un seul appel court.' 'const passages = [' ' { source: \"horaires-weekend\", texte: \"Le samedi, la médiathèque ouvre à 10 h et ferme à 14 h. Elle est fermée le dimanche.\" },' ' { source: \"horaires-semaine\", texte: \"La médiathèque ouvre à 9 h et ferme à 18 h du lundi au vendredi.\" },' '];' > reponse.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script de réponse, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const question = \"À quelle heure ouvre la médiathèque le samedi ?\";' 'function construireConsigne(question, passages) {' ' const lignes = [\"Répondez uniquement à partir des passages ci-dessous. Citez chaque passage utilisé sous la forme [1]. Si les passages ne contiennent pas la réponse, répondez exactement : Je ne sais pas.\", \"\"];' ' passages.forEach(function (un, i) { lignes.push(\"[\" + (i + 1) + \"] \" + un.texte); });' ' lignes.push(\"\", \"Question : \" + question);' ' return lignes.join(\"\n\");' '}' >> reponse.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script de réponse, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Échantillon enregistré, utilisé seulement si aucun modèle ne répond.' 'const echantillon = \"Elle ouvre à 10 h le samedi [1].\";' 'function sourcesCitees(reponse) {' ' const trouvees = [];' ' (reponse.match(/\[(\d+)\]/g) || []).forEach(function (marque) {' ' const un = passages[Number(marque.slice(1, -1)) - 1];' ' if (un && trouvees.indexOf(un.source) < 0) trouvees.push(un.source);' ' });' ' return trouvees;' '}' >> reponse.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script de réponse, partie 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const consigne = construireConsigne(question, passages);' ' console.log(consigne);' ' const reponse = await demander([{ role: \"user\", content: consigne }], echantillon, 120);' ' console.log(\"Réponse :\", reponse);' ' const citees = sourcesCitees(reponse);' ' console.log(\"Sources :\", citees.length > 0 ? citees.join(\", \") : \"aucune citée\");' ' console.log(\"Dit je ne sais pas :\", /je ne sais pas/i.test(reponse) ? \"oui\" : \"non\");' '}' 'main();' >> reponse.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Lancer le script et lire la réponse
docker exec -w /home/node/project agent node reponse.jsAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une sortie possible
| Ligne | Ce qu’elle peut montrer |
|---|---|
| La consigne | Les règles, deux passages numérotés et la question |
| Réponse | Elle ouvre à 10 h le samedi [1]. |
| Sources | horaires-weekend |
| Dit je ne sais pas | non |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une erreur fréquente
Attention
Beaucoup de gens croient qu’il suffit de dire à un modèle de n’utiliser que les passages pour qu’il le fasse. La règle réduit le risque d’une réponse inventée. Elle ne le supprime pas. Gardez la vérification, et changez la question pour une que les passages ne couvrent pas, afin de voir l’issue de secours.