Retour à l'accueil

RAG : recherche intelligente de documents pour développeurs

Retrieval-Augmented Generation (RAG) combine la recherche de fragments de documents pertinents avec la génération de réponses LLM. L'article décompose les étapes : parsing de format, chunking sémantique, recherche hybride (vectors+BM25+RRF), reranking et optimisations pour les grandes bases de données.

RAG en pratique : algorithmes de recherche de code et de base de données
Advertisement 728x90

Génération augmentée par récupération : une recherche documentaire intelligente pour les entreprises

RAG combine la recherche vectorielle et l'IA générative pour fournir des réponses précises à partir de bases documentaires privées. Le système extrait le contenu pertinent dans des fichiers PDF, Excel, du code source et bien plus encore, puis génère des explications strictement fondées sur les documents sources — réduisant considérablement les hallucinations.

Le processus commence par un prétraitement : les documents sont analysés, découpés en morceaux intelligents en tenant compte de leur type, puis convertis en embeddings stockés dans une base de données vectorielle.

Extraction de texte selon les formats de fichiers

Le système prend en charge plusieurs formats sans perte de données. Chaque format dispose d'un extracteur dédié.

Google AdInline article slot
SUPPORTED = {
    '.pdf':  lambda f: ''.join(p.extract_text() or '' for p in PyPDF2.PdfReader(f).pages),
    '.docx': lambda f: '\n'.join(p.text for p in Document(f).paragraphs),
    '.pptx': lambda f: '\n'.join(
        shape.text
        for slide in Presentation(f).slides
        for shape in slide.shapes
        if hasattr(shape, "text") and shape.text.strip()
    ),
    '.xlsx': extract_xlsx,
    '.csv':  extract_csv,
    # .txt, .json, .py, .js, etc.
}

Le résultat est un texte propre, prêt à être traité.

Découpage intelligent par type de contenu

Le texte n’est pas divisé par taille fixe — il est segmenté sémantiquement. Tout d’abord, le type de contenu est détecté :

def detect_content_type(text):
    if re.search(r'<table|</td>|<tr>', text, re.IGNORECASE):
        return 'table'
    tab_lines = sum(1 for line in text.splitlines() if '\t' in line)
    if tab_lines > max(3, len(text.splitlines()) * 0.4):
        return 'table'
    if re.search(r'function\s+\w+\s*\(|\bdef\s+\w+\s*\(|\bclass\s+\w+\b', text):
        return 'code'
    return 'prose'
  • Tableaux : les en-têtes sont répétés dans chaque morceau
  • Code : découpage par fonctions ou classes sans rompre la logique
  • Texte continu : frontières sémantiques basées sur les embeddings de phrases

Pour les tableaux :

Google AdInline article slot
def split_table_by_rows(text, chunk_size):
    lines = [l for l in text.splitlines() if l.strip()]
    header = lines[0]
    chunks = []
    cur_lines = [header]
    for line in lines[1:]:
        if sum(len(l) for l in cur_lines) + len(line) >= chunk_size:
            chunks.append('\n'.join(cur_lines))
            cur_lines = [header, line]
        else:
            cur_lines.append(line)
    if cur_lines:
        chunks.append('\n'.join(cur_lines))
    return chunks

Le découpage sémantique utilise la similarité cosinus entre phrases adjacentes avec un seuil adaptable (percentile 25).

Génération des embeddings

Les morceaux sont vectorisés grâce à des modèles comme text-embedding-3-small (1536 dimensions). Les vecteurs et métadonnées sont stockés dans la base de données.

def get_embeddings(texts, batch_size=100):
    embeddings = []
    for i in range(0, len(texts), batch_size):
        resp = requests.post(
            "https://openrouter.ai/api/v1/embeddings",
            headers=HEADERS,
            json={
                "model": "openai/text-embedding-3-small",
                "input": texts[i:i + batch_size],
                "encoding_format": "float"
            },
        ).json()
        embeddings.extend(d["embedding"] for d in resp.get("data", []))
    return np.array(embeddings)

Recherche en plusieurs étapes

La requête est transformée en vecteur, reformulée en trois variantes via un modèle LLM, puis traitée par une recherche hybride.

Google AdInline article slot

Recherche hybride : vecteurs + BM25

  • Recherche vectorielle : similarité cosinus dans l’espace des embeddings
  • BM25 : TF-IDF avec k1=1.5, b=0.75 pour des correspondances exactes

Les résultats sont fusionnés via RRF :

# Fusion RRF
for rank, idx in enumerate(sorted_by_vector):
    rrf_scores[idx] += 1.0 / (k_rrf + rank)
for rank, idx in enumerate(sorted_by_bm25):
    rrf_scores[idx] += bm25_normalized[idx] * (1.0 / (k_rrf + rank))

Ré-évaluation et filtres

Les 50 meilleurs candidats sont évalués par LLM, dont 5 à 10 sont sélectionnés. Règles d’inclusion forcée :

  • Citations entre guillemets « » ou " "
  • Nombres ou identifiants (regex : \b(№|N|number)\s*\d+\b)
quote_matches = re.findall(r'«([^»]+)»|"([^"]+)"', query)
for match in quote_matches:
    quote = (match[0] or match[1]).strip()
    if len(quote) > 5:
        for i, chunk in enumerate(chunks_data):
            if quote in chunk['content']:
                forced_indices.append(i)
                break

Optimisations de performance

Recherche hiérarchique

D’abord, les documents pertinents sont identifiés via des annotations ; ensuite, les morceaux à l’intérieur sont recherchés.

doc_summaries = [chunks_data_by_doc[doc_id][0]['content'][:500] for doc_id in all_doc_ids]
doc_embs = get_embeddings(doc_summaries)
sims = np.dot(doc_embs_normalized, query_embedding_normalized)
top_doc_ids = [all_doc_ids[i] for i in np.argsort(sims)[::-1][:5]]
chunks_data = [ch for ch in chunks_data if ch['doc_id'] in top_doc_ids]

Recherche itérative

Pour les requêtes complexes, plusieurs tours affinent les résultats grâce aux retours du modèle LLM.

Points clés

  • RAG minimise les hallucinations en générant des réponses strictement à partir de morceaux pertinents
  • La recherche hybride (vecteurs + BM25 + RRF) améliore la précision de 20 à 30 % par rapport à la recherche purement vectorielle
  • Le découpage sémantique préserve le contexte dans les tableaux et le code, réduisant le bruit
  • L’approche hiérarchique accélère la recherche dans de grandes corpora (>10 000 documents)
  • Les filtres obligatoires garantissent la récupération de faits contenant des nombres ou du texte entre guillemets

— Editorial Team

Advertisement 728x90

Lire ensuite