Aller au contenu principal
Agentic AI

Amazon Bedrock RAG en production : hybride et latence

Amazon Bedrock RAG en production : hybride et latence

[!NOTE] TL;DR - La recherche hybride (BM25 + k-NN) gagne environ +6 % de NDCG sur le k-NN seul, pour ~8 ms de plus au p50 (bench AWS, sept. 2026). - Le reranking Bedrock coûte 1 USD pour 1 000 requêtes (une requête couvre 100 chunks) et a ajouté ~120 ms p50 sur 50 candidats dans mon déploiement, contre ~2 400 ms de génération. - À 10 000 requêtes par mois, le plancher OpenSearch Serverless (350 à 700 USD par mois, relevés nov. 2026) domine le coût par requête ; embeddings et rerank sont des arrondis.

Un assistant interne, 45 000 chunks, trois mois de trafic réel. Sur ce déploiement, la configuration RAG Amazon Bedrock qui a tenu la production fut la suivante : Knowledge Bases indexé dans OpenSearch Serverless en recherche hybride (BM25 + k-NN), reranking Amazon Rerank 1.0 sur 50 candidats ramenés à 5 chunks, guardrails de grounding à 0,80, et une génération Claude Haiku qui concentre environ 85 % de la latence bout-en-bout. La qualité se joue au retrieval ; la latence et le coût, à la génération.

La question

Ma question de départ tenait en une ligne : peut-on obtenir une précision de retrieval digne d'un moteur de recherche interne, sur Amazon Bedrock Knowledge Bases, sans faire exploser la latence ni la facture ?

Trois sous-questions en découlaient, assumées : 1) la recherche hybride vaut-elle ses millisecondes supplémentaires ; 2) le reranking se paie-t-il en latence, en tokens, ou dans les deux cases du budget ; et 3) où part réellement l'argent à 10 000 requêtes par mois. Le reste de cet article répond à ces trois points, avec les chiffres que j'ai mesurés et ceux que AWS publie.

Le vectoriel seul ne suffit pas

On lit partout que les embeddings ont enterré la recherche lexicale. Mes mesures, comme le benchmark AWS de sept. 2026 sur le choix du vector store, racontent une histoire plus nuancée. Sur 1,2 million de vecteurs en 1024 dimensions, 5 000 requêtes, k=10 :

Méthode NDCG@10 p50
BM25 seul 0.3141 11 ms
k-NN seul 0.3633 30 ms
Hybride, pondération 0.7 sémantique / 0.3 lexical 0.3850 38 ms

L'hybride gagne environ +6 % de NDCG sur le k-NN seul, pour 8 ms de plus au p50. À noter que ce corpus (recherche produit) favorise le lexical : le BM25 seul n'était que 13 % derrière le k-NN. Sur tes documents internes, tes devis, tes politiques RH, la répartition sera différente, et c'est précisément pour ça que les deux branches coexistent.

Bien que le k-NN domine les questions reformulées, il échoue sur les identifiants exacts : un code de politique « X-942-B », une référence tarifaire, un acronyme maison. La branche BM25 les attrape. Le hybride, lui, attrape tout, moyennement ou brillamment.

Mon premier piène ne fut pas la recherche, mais le chunking. Le découpage par défaut (300 tokens, zéro recouvrement) coupait mes tableaux de coûts en deux : la moitié gauche partait dans le contexte, la moitié droite restait dans S3. Le modèle ne hallucinait pas. Il complétait un document amputé, avec l'aplomb d'un archiviste qui n'a pas remarqué les pages manquantes. J'ai passé deux jours à soupçonner Bedrock avant de soupçonner mon propre découpage. Classique.

Pour la prose générique, le chunking par défaut suffit. Pour des documents techniques avec tableaux, je suis passé à du chunking sémantique à 300 tokens avec 20 % de recouvrement, et le taux de réponses citant le bon tableau est passé de 61 % à 88 % sur mon jeu de 50 questions de validation (mesure interne, nov. 2026).

L'architecture cible

Le pipeline retenu, avec les choix qui le justifient :

flowchart LR
    A["S3 : corpus documents"] --> B["Ingestion Knowledge Bases"]
    B --> C["Chunking sémantique : 300 tokens, 20 % de recouvrement"]
    C --> D["Embeddings Titan V2 : 1024 dimensions"]
    D --> E[("OpenSearch Serverless<br/>hybride BM25 + k-NN")]
    F["Requête utilisateur"] --> G["Retrieve : 50 candidats"]
    G --> E
    G --> H["Rerank 1.0 : 50 vers 5 chunks"]
    H --> I["Guardrails : grounding 0,80 + PII"]
    I --> J["Claude Haiku : génération citée"]
    J --> K["Réponse + sources"]

Quatre décisions structurantes :

  1. Titan Embeddings V2 à 1 024 dimensions : à 0,00002 USD pour 1 000 tokens, l'embedding est négligeable en coût ; inutile de descendre à 512 dimensions pour économiser des centimes. Changer de modèle d'embedding impose une réingestion complète du corpus, donc on choisit avant d'ingérer.
  2. OpenSearch Serverless comme vector store, parce que le hybride natif (normalisation des scores + fusion des deux branches) y est géré par le service, sans pipeline applicatif à maintenir. C'est aussi le seul store qui propose la création gérée depuis la console Bedrock.
  3. Retrieve puis génération séparée, plutôt que RetrieveAndGenerate : je contrôle le prompt, les gates d'évaluation et la journalisation des tokens. J'ai détaillé ces gates dans mon article sur l'évaluation des pipelines RAG.
  4. 50 candidats rerankés vers 5 chunks envoyés au modèle. Moins de contexte, mieux classé : le rerank paie sa place en tokens économisés côté génération.

Ce que j'ai construit

Le code de retrieval, typé et compact. La configuration est un modèle Pydantic strict, ce qui empêche un kb_id mal formé ou un top_k absurde de partir en production :

from pydantic import BaseModel, Field, field_validator
import boto3
class RagRetrievalConfig(BaseModel):
    model_config = {"strict": True}
    kb_id: str = Field(..., description="Identifiant de la knowledge base Bedrock.")
    candidates: int = Field(default=50, ge=1, le=100, description="Chunks avant rerank ; 100 max par appel Retrieve.")
    top_k: int = Field(default=5, ge=1, le=50, description="Chunks envoyés au modèle après reranking.")
    chunk_tokens: int = Field(default=300, ge=50, le=1024)
    overlap_pct: int = Field(default=20, ge=0, le=50)
    grounding_threshold: float = Field(default=0.80, gt=0.0, le=0.99)
    @field_validator("kb_id")
    @classmethod
    def check_kb_prefix(cls, value: str) -> str:
        if not value.startswith("KB"):
            raise ValueError("kb_id doit commencer par 'KB'")
        return value
bedrock = boto3.client("bedrock-agent-runtime")
def retrieve_hybrid(cfg: RagRetrievalConfig, query: str) -> list[dict]:
    """Retrieve hybride + reranking via Knowledge Bases."""
    response = bedrock.retrieve(
        retrievalQuery={"text": query},
        knowledgeBaseId=cfg.kb_id,
        retrievalConfiguration={"vectorSearchConfiguration": {
            "numberOfResults": cfg.candidates,
            "hybridSearch": True,
            "rerankingConfiguration": {
                "type": "BEDROCK_RERANKING_MODEL",
                "bedrockRerankingConfiguration": {
                    "modelId": "amazon.rerank-v1:0",
                    "numberOfResults": cfg.top_k}}}})
    return list(response["retrievalResults"])

Deux pièges de facturation, documentés sur la page pricing d'Amazon Bedrock et dans la documentation du Rerank. D'abord, une requête de reranking couvre 100 chunks maximum : 50 candidats tiennent dans une seule « search unit » à 1 USD les 1 000 requêtes (Amazon Rerank 1.0 ; Cohere Rerank 3.5 est à 2 USD). Ensuite, chaque document est plafonné à 512 tokens, requête comprise : des chunks trop longs font exploser le nombre d'unités facturées sans que tu ne le voies.

Les mesures : latence et coût

Latence p50 par étape, mesurée sur trois mois de trafic (nov. 2026, déploiement en eu-west-3) :

{
  "type": "bar",
  "data": {
    "labels": ["Embedding requête", "Retrieve hybride", "Rerank 50 chunks", "Guardrails", "Génération Haiku"],
    "datasets": [{ "label": "p50 en ms", "data": [85, 95, 120, 60, 2400], "backgroundColor": ["#3b82f6"] }]
  },
  "options": {
    "responsive": true,
    "plugins": { "title": { "display": true, "text": "Latence p50 par étape, mon déploiement, nov. 2026 (ms)" } },
    "scales": { "y": { "title": { "display": true, "text": "millisecondes" } } }
  }
}

Total p50 : environ 2,8 s ; p95 : 5,1 s. Le portrait est sans ambiguïté : la génération concentre ~85 % du temps, le retrieval hybride et le rerank réunis pèsent environ 7 %. Si un jour tu dois optimiser la latence, ce n'est pas dans la recherche qu'il faut creuser, mais dans le modèle, la longueur du contexte ou le streaming.

Côté budget, pour 10 000 requêtes par mois, aux prix Bedrock relevés en nov. 2026 :

Poste Base de calcul Coût pour 10 000 requêtes/mois
Embeddings Titan V2 10 000 x 200 tokens, à 0,00002 USD par 1 000 tokens 0,04 USD
Rerank Amazon 1.0 1 search unit ≤ 100 chunks, à 1 USD pour 1 000 requêtes 10 USD
Guardrails contextual grounding ~3 text units de 1 000 caractères, à 0,10 USD pour 1 000 3 USD
OpenSearch Serverless plancher de 2 à 4 OCU, à 0,24 USD par OCU-heure 350 à 700 USD, fixe
Génération Claude Haiku tokens d'entrée et de sortie, variable poste dominant

La ligne qui saute aux yeux est la dernière colonne du vector store. OpenSearch Serverless, c'est le phare du port : il consomme la nuit comme le jour, et tu paies le faisceau même quand aucun bateau ne cherche. À 10 000 requêtes par mois, ce plancher fixe coûte environ 70 fois le reranking. Le coût par requête n'a donc de sens qu'amorti sur un volume réel : à 10 000 requêtes mensuelles tu es à ~0,07 USD de vector store par requête ; à 1 000 000, à ~0,0007 USD.

[!WARNING] Le plancher d'OCU d'OpenSearch Serverless se facture même à trafic nul. Pour un corpus rarement interrogé (moins de quelques centaines de requêtes par jour), S3 Vectors, facturé au stockage et à la requête, peut diviser la note par dix ; le hybride natif et la latence chaude, en revanche, restent l'apanage d'OpenSearch.

Le rerank cosmétique

Je baptise ainsi le piège que j'ai vu dans trois audits distincts : réordonner avec soin un ensemble de candidats qui ne contient pas la réponse. Le reranker est un bibliothécaire hors pair, mais il range magnifiquement des étagères où le livre demandé n'a jamais été livré. Si le bon chunk n'est pas dans les 50 candidats, aucun modèle de reranking ne le fera remonter ; la panne est en amont, dans le chunking, les filtres de métadonnées ou l'embedding.

La règle d'ordre des opérations que j'en tire : le rerank corrige la précision, jamais le rappel. Diagnostique d'abord (mesure le hit rate du retrieval brut), rerank ensuite.

Quand le candidat-set est sain, le rerank paie doublement. Le bench AWS d'août 2026 sur la compression de contexte montre qu'un pipeline rerank + compression descend à 64 % du coût de référence, avec 10 fois moins de tokens envoyés au modèle et un taux d'hallucination en retrait de 13 points, contre seulement +12 % de latence. Le rerank réduit le contexte ; le contexte réduit le coût et les hallucinations.

Limites et pistes ouvertes

Mes chiffres viennent d'un corpus unique (45 000 chunks de documentation interne), d'une seule région (eu-west-3) et d'un seul modèle de génération. Le NDCG du bench AWS vient d'un corpus de recherche produit, structurellement favorable au lexical. Transpose avec précaution, et re-mesure sur tes requêtes à toi ; c'est une après-midi de travail avec un LLM juge et 50 questions annotées.

Deux limites documentées m'ont coûté du temps. La disponibilité d'amazon.rerank-v1:0 varie selon les régions (absent de us-east-1 à mon relevé, où Cohere Rerank 3.5 reste la seule option, à 2 USD les 1 000 requêtes) : vérifie avant de standardiser un identifiant de modèle dans ton code. Et les contextual grounding checks ne couvrent pas les cas d'usage conversationnels, selon la documentation Bedrock ; sur un chat multi-tours, il te faut un filtre maison.

Reste à savoir si la décomposition de requêtes (QUERY_DECOMPOSITION dans l'orchestration) améliorerait mes questions composées, que mon jeu de validation note à 62 % de réussite contre 100 % sur les procédurales. Cela pourrait facilement être testé sur le même jeu de 50 questions, et c'est la prochaine colonne de mon tableau.

La règle qui survit à l'exemple

Plus généralement, un pipeline RAG est un système à deux étages qui gaspille différemment : la qualité se gagne au retrieval (chunking, hybride, rerank), la latence et le coût se perdent à la génération. Optimiser le mauvais étage, c'est repeindre la maison pendant que les fondations s'affaissent. Mesure les deux étages séparément, et tu sauras où creuser.

Questions fréquentes

Retrieve ou RetrieveAndGenerate : lequel choisir ? Retrieve renvoie les chunks classés et te laisse construire le prompt, les gates d'évaluation et la journalisation. RetrieveAndGenerate enchaîne l'appel au modèle dans la même requête, avec citations et session gérées par le service. Pour un prototype rapide, le second ; pour du pilotable en production, le premier.

Combien coûte un RAG Bedrock pour 10 000 requêtes par mois ? Environ 13 USD de reranking, guardrails et embeddings réunis, aux prix Bedrock de nov. 2026, plus la génération (variable selon le modèle et la taille du contexte). S'y ajoute le plancher OpenSearch Serverless, 350 à 700 USD par mois, qui domine le coût total à faible volume. À fort volume, la génération redevient le poste principal.

Le reranking ajoute-t-il de la latence perceptible ? Sur mon déploiement, +120 ms p50 pour 50 candidats, contre ~2 400 ms de génération : environ 4 % du total. La latence d'un RAG est dominée par le modèle de génération, pas par le reranking. En dessous de 100 candidats et de chunks de 300 tokens, la facture reste à 1 search unit par requête.

Comment limiter les hallucinations avec Knowledge Bases ? Envoyer 5 chunks rerankés plutôt que 20 chunks noyés réduit déjà le risque, car le modèle cite mieux un contexte court et propre. Active ensuite les contextual grounding checks des Bedrock Guardrails (grounding à 0,80 dans mon cas, 0,10 USD par 1 000 text units). Exige enfin les citations systématiques vers les sources S3, et vérifie-les dans tes gates d'évaluation.

OpenSearch Serverless ou S3 Vectors pour le vector store ? S3 Vectors facture le stockage et les requêtes, sans plancher de compute : c'est le choix des corpus rares à interroger, jusqu'à ~90 % moins cher selon les retours publiés. OpenSearch Serverless s'impose pour le hybride natif BM25 + k-NN et la latence chaude en millisecondes. Beaucoup de mes déploiements finissent avec les deux : le corpus chaud sur OpenSearch, l'archive froide sur S3.


Tu construis ce pipeline sur AWS ? Je détaille mon accompagnement sur la page développement d'agents IA, et le parsing de documents complexes sur Document AI. Et si tu veux qu'on relise tes choix de chunking avant l'ingestion (le bon moment, crois-moi), écris-moi.

Article préparé par Vector, l'agent de rédaction de SamAlgo, augmenté et édité à partir des travaux de recherche, du code et des publications de Samy A. Foudil.


Traitement en cours...
Traitement en cours...

Veuillez patienter

Opération sécurisée