RAG qui ne se périme pas : Données Web fraîches et boucles de rafraîchissement

Un système RAG est aussi frais que son dernier crawl. Les mathématiques de récupération sont la partie facile — la partie difficile est une boucle de rafraîchissement qui re-crawle, re-segmente et réintègre chaque source à son propre rythme avant que les réponses ne deviennent obsolètes.

La génération augmentée par récupération est désormais présente dans environ la moitié des systèmes d'IA d'entreprise — l'adoption a bondi à 51 % dans les dernières enquêtes, contre 31 % un an plus tôt. Pourtant, la plupart des projets RAG partagent le même mode d'échec : ils ont été peuplés une fois, puis le monde a évolué. Les mathématiques de récupération (intégrer la requête, rechercher les vecteurs, insérer les meilleurs segments dans l'invite) sont la partie facile, bien documentée. La partie difficile, celle qui décide si votre assistant est fiable ou confiant mais erroné, est la fraîcheur — un pipeline qui continue de re-crawler, re-segmenter et réintégrer chaque source avant que ses réponses ne deviennent obsolètes. Ce guide porte sur la construction de cette boucle de rafraîchissement, avec l'ingestion en markdown d'abord et les TTLs par source en son cœur.

Une récupération obsolète est pire qu'aucune récupération

Toute la promesse de RAG est que le modèle répond à partir de preuves récupérées plutôt que de données d'entraînement figées. Brisez la fraîcheur et vous brisez la promesse : un bot de support cite la politique de retour du trimestre dernier, un assistant de tarification cite un chiffre abandonné, un copilote de recherche manque la mise à jour qui a changé la réponse. Pire, le modèle l'affirme avec une confiance totale car le segment récupéré semble autoritaire. Un index obsolète ne tombe pas bruyamment — il échoue discrètement et de manière plausible, ce qui est le plus dangereux. Garder le corpus à jour n'est pas un luxe ; c'est la différence entre l'ancrage et l'hallucination avec des étapes supplémentaires.

Ingestez du markdown, pas du HTML brut

La qualité de tout ce qui suit est déterminée à l'ingestion. Le HTML brut est un désastre pour la récupération — la navigation, les bannières de cookies, les pieds de page et les balises de script deviennent des segments de bruit qui polluent la recherche de similarité et gaspillent des jetons de contexte. L'approche propre est le markdown d'abord : crawler chaque page et extraire uniquement le contenu principal sous forme de markdown structuré, en éliminant le contenu générique avant que quoi que ce soit ne soit segmenté. Notre Scraper API renvoie directement du markdown, rend JavaScript à la demande et fait tourner les IPs pour que les pages bloquées ne laissent pas de trous dans votre corpus — elle remplace la couche d'ingestion fragile Puppeteer-et-BeautifulSoup sur laquelle la plupart des piles RAG boitent. Le point de terminaison web-data-for-LLMs regroupe crawl, nettoyage et structuration en un seul appel conçu précisément pour cela.

import requests

# markdown-first ingestion: clean text, JS rendered, IPs rotated
def fetch_markdown(url):
    r = requests.post(
        "https://api.quantumproxies.io/scrape",
        headers={"Authorization": f"Bearer {QP_KEY}"},
        json={"url": url, "format": "markdown", "render": True},
        timeout=60,
    )
    doc = r.json()
    return {
        "url": url,
        "markdown": doc["markdown"],       # no nav, no boilerplate
        "fetched_at": doc["fetchedAt"],     # timestamp for TTL logic
        "content_hash": doc["contentHash"], # skip re-embed if unchanged
    }

Alimentez votre pile RAG avec des données web propres

Segmenter petit, transporter des métadonnées

Une fois que vous avez du markdown propre, divisez-le en segments d'environ 300 à 500 jetons — suffisamment petits pour qu'un segment récupéré soit étroitement sur le sujet, suffisamment grands pour conserver une pensée cohérente. Utilisez un séparateur récursif (LangChain ou LlamaIndex en proposent tous deux un) qui respecte les limites de phrase et de titre plutôt que de couper en plein mot. L'étape critique, souvent négligée, est la métadonnée : chaque segment doit porter son URL source, un horodatage et un hachage de contenu. Ces métadonnées sont ce qui rend la fraîcheur possible — la récupération peut filtrer par récence, et votre boucle de rafraîchissement peut indiquer quels segments appartiennent à une source qui vient de changer.

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=1800, chunk_overlap=200,  # ~400 tokens per chunk
)

def to_chunks(doc):
    parts = splitter.split_text(doc["markdown"])
    return [{
        "text": p,
        "source": doc["url"],
        "fetched_at": doc["fetched_at"],   # drives recency filtering
        "ttl": ttl_for(doc["url"]),          # per-source refresh clock
    } for p in parts]

Intégrez ces segments avec un modèle actuel — text-embedding-3-large, un modèle Cohere, ou une variante ouverte de Sentence-BERT — et stockez les vecteurs avec les métadonnées dans une base de données de vecteurs telle que Pinecone, Weaviate, Milvus ou FAISS. Ajustez les seuils de similarité et, surtout, activez le filtrage des métadonnées pour qu'une requête puisse exiger, par exemple, uniquement des segments récupérés au cours des 30 derniers jours pour une question de tarification.

Diagramme d'une boucle de rafraîchissement de fraîcheur RAG depuis le crawl markdown jusqu'à la segmentation et l'intégration à l'indexation et au re-crawl piloté par TTL
La boucle qui maintient RAG honnête : crawl markdown d'abord, petits segments riches en métadonnées, et un TTL qui déclenche la réintégration avant la dérive.

TTLs par source : le cœur de la fraîcheur

L'erreur que font la plupart des équipes est de rafraîchir tout sur un même calendrier — un re-crawl complet nocturne qui est simultanément trop lent pour les prix et inutile pour les documents de référence. Les différentes sources vieillissent à des rythmes différents, alors donnez à chacune un temps de vie qui correspond à la vitesse à laquelle son contenu évolue réellement. Les prix et stocks en direct pourraient nécessiter un TTL de quelques minutes ; les actualités, annonces et fils de discussion une heure ; les classements et catalogues un jour ; la documentation, les politiques et le matériel de référence une semaine ou plus. Étiquetez chaque segment avec le TTL de sa source, et un planificateur ne re-crawle que les sources dont l'horloge a expiré. Le hachage de contenu est votre soupape d'efficacité : si la page re-crawlée est identique en octets, sautez complètement l'étape d'intégration et réinitialisez simplement l'horodatage.

import time

TTL = {"prices": 300, "news": 3600, "catalog": 86400, "docs": 604800}

def needs_refresh(chunk, now=None):
    now = now or time.time()
    return (now - chunk["fetched_at"]) > chunk["ttl"]

# refresh only what has expired; re-embed only what actually changed
for src in due_sources(TTL):
    doc = fetch_markdown(src.url)
    if doc["content_hash"] != src.last_hash:
        reindex(to_chunks(doc))     # re-chunk + re-embed
    src.touch(doc["content_hash"])  # reset the clock either way

C'est la boucle qui sépare une démo d'un système de production. Elle maintient la récupération prévisible, dépense des ressources de calcul uniquement là où le monde a réellement changé, et vous permet de garantir une véritable fraîcheur par source. Pour une vue d'ensemble de l'ancrage des LLMs sur des données en direct, consultez notre guide sur l'alimentation des LLMs avec des données web fraîches, et pour transformer un seul site en une base de connaissances citables, construire une base de connaissances pour bot de support.

Bandes de TTL échelonnées montrant des minutes pour les prix et les stocks, à l'heure pour les actualités, quotidiennement pour les catalogues et hebdomadairement pour les documents de référence
Rafraîchissez chaque source selon le rythme auquel son contenu évolue réellement — des minutes pour les prix, une semaine pour les documents de référence.

Récupérez pour la couverture, pas seulement pour la similarité

Une dernière amélioration au moment de la requête. La pure similarité vectorielle manque les termes exacts — codes produits, chaînes d'erreurs, noms propres — qu'une recherche par mots-clés cloue. La récupération hybride mélange les deux, puis applique votre filtre de récence pour qu'une requête sensible au temps préfère les segments frais. Enregistrez les segments récupérés avec chaque réponse afin de pouvoir auditer les preuves que le modèle a réellement utilisées ; cette traçabilité est ce qui rend RAG explicable, et c'est ainsi que vous attrapez un segment obsolète avant qu'un utilisateur ne le fasse. Les techniques d'extraction alimentée par LLM s'associent bien ici lorsque vous avez besoin de champs structurés à partir des pages récupérées plutôt que de prose.

Questions fréquemment posées

Comment garder les données d'un pipeline RAG fraîches ?

Attribuez à chaque source un temps de vie qui correspond à la vitesse à laquelle son contenu change, étiquetez chaque segment avec une URL source et un horodatage, et exécutez un planificateur qui ne re-crawle que les sources expirées. Utilisez un hachage de contenu pour éviter de réintégrer des pages qui n'ont pas réellement changé, et activez le filtrage par récence à la récupération pour que les requêtes sensibles au temps préfèrent les segments frais.

Pourquoi ingérer du markdown au lieu de HTML pour RAG ?

Le HTML brut transporte navigation, bannières, pieds de page et scripts qui deviennent des segments de bruit, polluant la recherche de similarité et gaspillant des jetons de contexte. L'ingestion en markdown d'abord extrait uniquement le contenu principal, donc les segments sont propres et sur le sujet. Une API de scraping qui renvoie du markdown rendu élimine le parsing personnalisé fragile sur lequel la plupart des piles RAG reposent.

Quelle taille de segment devrais-je utiliser pour RAG ?

Environ 300 à 500 jetons est le point idéal commun : suffisamment petit pour qu'un segment récupéré reste étroitement pertinent, suffisamment grand pour préserver une pensée complète. Utilisez un séparateur récursif qui respecte les limites de phrase et de titre, ajoutez un petit chevauchement pour que le contexte ne soit pas perdu aux jonctions, et attachez des métadonnées source à chaque segment.

Ai-je besoin de proxies pour construire un pipeline de données RAG ?

Si vous crawlez le web ouvert à grande échelle, oui — les sites limitent le débit et bloquent les requêtes répétées d'une même IP, laissant des lacunes dans votre corpus. Une API de scraping ou des proxies rotatifs maintiennent le crawl fiable pour que les sources se rafraîchissent complètement et selon le calendrier, ce qui est exactement ce dont dépendent les TTLs par source.

RAG vit ou meurt sur la fraîcheur. Ingestez du markdown propre, segmentez petit avec des métadonnées, et conduisez la réintégration à partir des TTLs par source pour que chaque source se rafraîchisse selon son propre rythme. Construisez cette boucle une fois et votre assistant répondra à partir du web d'aujourd'hui, pas de l'instantané du mois dernier.

Construisez un pipeline RAG frais sur l'API Scraper