Back to flin
flin

RAG : récupération, reclassement et attribution des sources

Comment FLIN implémente la génération augmentée par récupération -- récupération sémantique de vos données, reclassement par encodeur croisé pour la précision, et attribution des sources pour que les utilisateurs sachent d'où viennent les réponses.

Thales & Claude | March 30, 2026 5 min flin
EN/ FR/ ES
flinragretrievalrerankingai

Les grands modèles de langage sont savants mais pas omniscients. Ils ne peuvent pas répondre aux questions sur la documentation interne de votre entreprise, votre catalogue de produits ou votre historique de support client. Ils hallucinent quand ils ne connaissent pas la réponse, générant avec confiance des informations plausibles mais incorrectes.

La génération augmentée par récupération (RAG) résout ce problème en ancrant les réponses du LLM dans vos données réelles. Au lieu de demander au modèle « Quelle est notre politique de remboursement ? », vous récupérez d'abord la documentation pertinente, puis demandez au modèle de répondre sur la base de cette documentation. Le modèle génère une réponse en utilisant vos données comme contexte, pas ses données d'entraînement.

FLIN implémente le RAG comme un pipeline composable : récupérer les documents pertinents avec la recherche sémantique, les reclasser avec un encodeur croisé pour la précision, les fournir au LLM comme contexte, et attribuer la réponse à ses sources.

Le pipeline RAG

flinfn answer_question(question) {
    // Étape 1 : Récupérer les documents pertinents
    docs = search question in Document by content limit 20

    // Étape 2 : Reclasser pour la précision
    ranked = rerank(question, docs, field: "content", limit: 5)

    // Étape 3 : Construire le contexte
    context = ranked.map(d => d.content).join("\n\n---\n\n")

    // Étape 4 : Générer la réponse avec attribution
    answer = ai_chat([
        { role: "system", content: "Answer questions based ONLY on the provided context. Cite sources by title. If the context doesn't contain the answer, say so." },
        { role: "user", content: "Context:\n" + context + "\n\nQuestion: " + question }
    ])

    {
        answer: answer,
        sources: ranked.map(d => { title: d.title, id: d.id })
    }
}

Quatre étapes. Récupérer, reclasser, générer, attribuer. Chaque étape est un appel de fonction FLIN standard.

Étape 1 : Récupération sémantique

La première étape récupère les documents candidats en utilisant le mot-clé search :

flindocs = search question in Document by content limit 20

Cela retourne les 20 documents les plus sémantiquement similaires. La limite est intentionnellement généreuse -- nous récupérons plus de candidats que nécessaire parce que l'étape de reclassement sélectionnera les plus pertinents. Récupérer 20 et reclasser à 5 est plus précis que récupérer 5 directement.

Étape 2 : Reclassement

La recherche sémantique utilise des modèles bi-encodeurs qui intègrent la requête et les documents indépendamment. C'est rapide mais imprécis. Le reclassement utilise un modèle encodeur croisé qui note les paires requête-document ensemble. C'est plus lent mais plus précis.

flinranked = rerank(question, docs, field: "content", limit: 5)

L'encodeur croisé est un petit modèle (typiquement 50-100 Mo) qui s'exécute localement via ONNX Runtime. Noter 20 documents prend environ 100 millisecondes.

Étape 3 : Construction du contexte

Les documents reclassés sont assemblés en une chaîne de contexte qui sera envoyée au LLM :

flincontext = ranked.map(d => d.content).join("\n\n---\n\n")

Étape 4 : Génération avec attribution

Le LLM reçoit le contexte et la question, et génère une réponse. Le prompt système contraint le LLM à n'utiliser que les informations du contexte fourni, à citer les sources et à indiquer explicitement quand la réponse n'est pas dans le contexte. Cet ancrage empêche l'hallucination.

Exemple RAG complet : base de connaissances

flin// app/knowledge.flin

guard auth

query = ""
answer_data = none

fn search_knowledge(q) {
    if q.len < 3 { return }

    docs = search q in Document by content limit 20
    ranked = rerank(q, docs, field: "content", limit: 5)

    if ranked.len == 0 {
        answer_data = {
            answer: "No relevant documents found.",
            sources: []
        }
        return
    }

    context = ranked.map(d =>
        "[Source: " + d.title + "]\n" + d.content
    ).join("\n\n---\n\n")

    response = ai_chat([
        {
            role: "system",
            content: "You are a knowledge base assistant. Answer questions based only on the provided context. Cite sources."
        },
        {
            role: "user",
            content: "Context:\n" + context + "\n\nQuestion: " + q
        }
    ])

    answer_data = {
        answer: response,
        sources: ranked.map(d => { title: d.title, id: d.id, score: d.score })
    }
}

<main>
    <h1>Base de connaissances</h1>
    <input placeholder="Posez une question..."
           value={query}
           keyup.enter={search_knowledge(query)}>

    {if answer_data != none}
        <div class="answer-card">
            <div class="answer-text">{answer_data.answer}</div>

            <div class="sources">
                <h4>Sources ({answer_data.sources.len})</h4>
                {for source in answer_data.sources}
                    <a href={"/documents/" + to_text(source.id)}>
                        {source.title}
                        <span class="score">{to_text((source.score * 100).round)}% match</span>
                    </a>
                {/for}
            </div>
        </div>
    {/if}
</main>

Profil de performance

ÉtapeLatenceNotes
Récupération sémantique2-10 msRecherche dans l'index HNSW
Reclassement (20 docs)80-150 msNotation par encodeur croisé
Assemblage du contexte< 1 msOpérations sur chaînes
Génération LLM500-2000 msDépend du fournisseur/modèle
Total600-2200 msAcceptable pour Q&R

Pourquoi le RAG dans le langage

Le RAG est typiquement implémenté comme une architecture multi-services : une base de données vectorielle (Pinecone/Weaviate/Qdrant), un service de récupération, un service de reclassement et une API LLM. Coordonner ces services nécessite de l'infrastructure, de la configuration et du code de liaison.

FLIN condense ce pipeline entier en fonctionnalités du langage : search pour la récupération, rerank pour la précision, ai_chat pour la génération. L'index vectoriel est intégré à FlinDB. L'encodeur croisé s'exécute localement. Le LLM est accessible via la passerelle IA.

Un développeur peut ajouter le RAG à son application en une fonction. Pas un service. Pas un déploiement d'infrastructure. Une fonction.

Dans le prochain article, nous couvrons l'analyse de documents -- comment FLIN extrait le texte des fichiers PDF, DOCX, CSV, JSON et YAML pour l'indexation et le RAG.


Ceci est la partie 120 de la série « Comment nous avons construit FLIN », documentant comment un CEO à Abidjan et un CTO IA ont conçu et construit un langage de programmation à partir de zéro.

Navigation de la série : - [119] Intégration FastEmbed pour les embeddings - [120] RAG : récupération, reclassement et attribution des sources (vous êtes ici) - [121] Analyse de documents : PDF, DOCX, CSV, JSON, YAML - [122] Découpage de code pour le RAG

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles