Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

AWS Bedrock Knowledge Base : leçons tirées d'un système d'audit de politiques en production

By Cloud Intelligence™Jun 8, 202611 min read

Cette page est également disponible en English, Deutsch, Español, Italiano, 日本語 et Português.

Par Dima Kramskoy — Senior Cloud Architect chez DoiT International


Pourquoi cet article

La plupart des tutoriels sur Bedrock Knowledge Base suivent le même scénario : on dépose un PDF dans S3, on crée une Knowledge Base, on lui pose une question. Terminé. Article publié.

Très bien pour une démo. Inutile en production.

J'ai récemment développé un système d'audit de politiques pour une fintech latino-américaine — un outil qui évalue en temps réel les notes de frais au regard des politiques internes, à l'aide de l'IA. Le système traite environ 500 requêtes par jour sur plus de 100 documents de politique, dans deux langues. Il tourne en production. Les notes de frais des collaborateurs sont approuvées ou refusées sur la base de ses réponses.

Voici ce que j'en ai réellement retiré — les choix d'architecture, les erreurs de chunking, les surprises côté coûts et les pièges dont aucune documentation AWS ne vous prévient.


Votre première Bedrock Knowledge Base (en 5 minutes)

Avant d'entrer dans le vif du sujet, posons les bases. Si vous avez déjà construit une KB, passez à la section suivante. Sinon, voici le chemin le plus court pour arriver à un "ça marche" :

Étape 1 : créer un bucket S3 avec vos documents

Terminal window
aws s3 mb s3://my-kb-source-docs
aws s3 cp ./policies/ s3://my-kb-source-docs/ --recursive

Étape 2 : créer la Knowledge Base (console)

  • Rendez-vous dans Amazon Bedrock → Knowledge Bases → Create
  • Donnez-lui un nom, sélectionnez un modèle d'embedding (Titan Embeddings v2 est le choix par défaut — parfait pour démarrer)
  • Pointez-le vers votre bucket S3
  • Pour le vector store : choisissez S3 Vectors (serverless, zéro configuration) ou laissez la console en créer un
  • Cliquez sur Create → patientez 2 à 3 minutes pour la synchronisation

Étape 3 : interroger la base

import boto3
client = boto3.client('bedrock-agent-runtime')
response = client.retrieve_and_generate(
input={'text': 'What is our policy on travel expenses?'},
retrieveAndGenerateConfiguration={
'type': 'KNOWLEDGE_BASE',
'knowledgeBaseConfiguration': {
'knowledgeBaseId': 'YOUR_KB_ID',
'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-pro-v1:0'
}
}
)
print(response['output']['text'])

Et voilà. Vous avez un système RAG fonctionnel, qui répond à des questions à partir de vos documents.

Mais voilà le hic : on n'a fait que 60 % du chemin. Les 40 % restants — là où se joue la qualité en production — font l'objet de la suite. Stratégie de chunking, maîtrise des coûts, latence, pipeline de transformation, et tous les pièges qui vous rattraperont à l'échelle.


Le cas d'usage

Le client avait un problème que toute entreprise en croissance finit par rencontrer : des politiques internes complexes que personne ne lit, appliquées de manière incohérente, dans plusieurs pays et plusieurs langues.

Concrètement : une entreprise dotée de plus de 100 documents de politique internes (anglais et espagnol) avait besoin d'une IA capable d'évaluer en temps réel les notes de frais des collaborateurs au regard de ces politiques. Pas "rechercher une politique" — bel et bien décider si une dépense est conforme, citer la règle pertinente et expliquer pourquoi.

Les exigences étaient claires :

  • Latence de décision sous les 3 secondes
  • Chaque décision traçable jusqu'à un paragraphe de politique source
  • Validation humaine pour toute modification de politique (responsabilité juridique)
  • Support multilingue (anglais/espagnol)
  • Modèle économique tenable à l'échelle (~500 requêtes/jour, en croissance)

Vue d'ensemble de l'architecture

Voici le flux à haut niveau :

Chemin d'évaluation des transactions :

API Gateway → SQS → Step Functions → Bedrock Nova Pro (validation du reçu)
→ Bedrock AgentCore (décision de politique via KB) → Aurora MySQL (persistance de la décision)

Chemin d'ingestion des politiques :

Upload S3 → Step Functions (validation humaine via task token)
→ Transformation LLM (restructuration de la politique, extraction des dépenses, structuration de la nouvelle vue)
→ Chunks transformés stockés dans S3 (état transitoire)
→ API exposant les chunks pour revue → Validation humaine
→ Ingestion dans Bedrock Knowledge Base

Deux chemins distincts. Un pour les décisions en temps réel, un pour la gestion du cycle de vie des politiques. Step Functions orchestre les deux — et ce n'est pas un hasard. Les state machines offrent précisément la visibilité et la sémantique de retry qu'exige une conformité légale.


Pourquoi S3 Vectors (et pas OpenSearch)

Je vais vous épargner ici des semaines de tergiversations.

Quand j'ai démarré ce projet, le vector store par défaut de Bedrock KB était OpenSearch Serverless. Ça fonctionne. C'est éprouvé. Et c'est aussi largement surdimensionné pour un corpus de moins de 10 000 documents.

S3 Vectors est arrivé comme alternative plus simple, et pour ce cas d'usage, le choix était évident :

Critère OpenSearch Serverless S3 Vectors
Coût mensuel de base ~700 $+ (2 OCU minimum) Paiement à la requête
Charge opérationnelle Gestion d'index, scaling Aucune
Complexité de mise en place Modérée Minimale
Latence de requête (p50) ~200 ms ~350 ms
Cas idéal 10K+ documents, requêtes complexes <10K documents, récupération simple

Le cadre de décision est simple : si votre corpus compte moins de 10 000 documents et que vous n'avez pas besoin de filtrage avancé ni de recherche hybride, S3 Vectors vous fera économiser de l'argent et bien des tracas opérationnels. S'il vous faut une latence sous les 200 ms ou si vous gérez des dizaines de milliers de documents avec des requêtes de métadonnées complexes, optez pour OpenSearch.

Pour nos ~100 documents de politique ? S3 Vectors s'imposait. Nous payons quelques centimes par jour au lieu de 700 $/mois minimum. Le compromis sur la latence (environ 150 ms de plus) passe inaperçu dans un workflow qui inclut de toute façon de l'inférence LLM.


Les stratégies de chunking qui comptent vraiment

Avant de partager ce qui a fonctionné chez nous, voici le panorama — car la plupart des guides n'en présentent qu'une ou deux variantes :

Stratégie Fonctionnement Idéal pour Points de vigilance
Taille fixe (par défaut) Découpe tous les N caractères/tokens Démarrage rapide, documents génériques Coupe au milieu d'une phrase ou d'une règle — d'où des réponses hallucinées
Par phrase Découpe aux frontières de phrase Documents simples, FAQ Ignore les sections logiques ; une règle peut s'étendre sur 5 phrases ou plus
Sémantique / par section Découpe selon la structure du document (titres, sections) Documents structurés à hiérarchie claire Exige de parser la structure ; les chunks varient en taille
Hiérarchique (parent-enfant) Chunks parents (section complète) + chunks enfants (paragraphes) Meilleure qualité de récupération — on match l'enfant, on renvoie le parent pour le contexte Plus complexe, stockage plus lourd, indexation plus lente
Assisté par LLM Le LLM restructure le document AVANT le chunking Documents à fort enjeu où mauvaise récupération = mauvaise décision Coût et latence supplémentaires à l'ingestion ; rentable quand la précision compte

Notre choix : assisté par LLM. Pour des documents de politique où une mauvaise récupération entraîne une mauvaise décision sur une dépense, le coût initial de la transformation par LLM se rentabilise immédiatement. Détails dans la section pipeline ci-dessous.

Mais d'abord — laissez-moi vous montrer le mode d'échec qui nous a menés là :

C'est là que j'ai commis ma plus coûteuse erreur de débutant.

Ce qui n'a pas fonctionné : le chunking par défaut

Le chunking par défaut de Bedrock KB découpe les documents par nombre de caractères, avec un chevauchement. Pour des documents génériques, c'est très bien. Pour des documents de politique, c'est catastrophique.

Voici pourquoi : une règle peut énoncer "Les repas de plus de 75 $ nécessitent l'approbation d'un manager, sauf en déplacement client où la limite est de 150 $." Le chunking par défaut peut couper cette phrase en deux. La récupération renvoie alors "Les repas de plus de 75 $ nécessitent l'approbation d'un manager" sans l'exception. L'agent refuse un dîner client légitime à 100 $. Vos utilisateurs perdent confiance dans le système dès le premier jour.

Ce qui a fonctionné : le chunking par frontières sémantiques

Nous prétraitons les documents avant l'ingestion, en découpant par section de politique — chaque règle ou sous-règle devient un chunk à part entière, contexte complet préservé.

import re
from dataclasses import dataclass
@dataclass
class PolicyChunk:
content: str
metadata: dict
def chunk_policy_document(text: str, doc_id: str, language: str) -> list[PolicyChunk]:
"""Chunk policy documents by semantic boundaries (section headers)."""
# Split on policy section patterns (numbered rules, headers)
section_pattern = r'\n(?=\d+\.\s|\#{1,3}\s|Article\s+\d+|Artículo\s+\d+)'
sections = re.split(section_pattern, text)
chunks = []
for i, section in enumerate(sections):
section = section.strip()
if len(section) < 50: # Skip trivial sections
continue
# Keep chunks between 200-1500 chars for optimal retrieval
if len(section) > 1500:
# Sub-chunk by paragraph, preserving section header
header = section.split('\n')[0]
paragraphs = section.split('\n\n')
for j, para in enumerate(paragraphs[1:], 1):
chunks.append(PolicyChunk(
content=f"{header}\n\n{para}",
metadata={
"doc_id": doc_id,
"section_index": i,
"sub_index": j,
"language": language,
"chunk_type": "policy_rule"
}
))
else:
chunks.append(PolicyChunk(
content=section,
metadata={
"doc_id": doc_id,
"section_index": i,
"sub_index": 0,
"language": language,
"chunk_type": "policy_rule"
}
))
return chunks

Conseils pratiques

  • Taille de chunk optimale : 200 à 1500 caractères pour des documents de politique. Les chunks plus courts améliorent la précision ; les plus longs préservent le contexte. À vous de trouver le bon équilibre.
  • Chevauchement : si vous tenez absolument à un chunking par caractères, prévoyez 20 % de chevauchement minimum. Mais sérieusement, découpez plutôt aux frontières sémantiques.
  • Les métadonnées sont la récupération : taguez chaque chunk avec language, policy_type, effective_date et department. Vous filtrerez là-dessus plus tard — ce n'est pas optionnel.

Le pipeline d'ingestion

Les documents de politique ne sont pas des articles de blog. Pas question de les jeter dans un vector store en espérant que tout se passe bien. Une mauvaise interprétation d'une politique a des conséquences juridiques.

Voici le pipeline :

Le pattern Transformation LLM + Human-in-the-Loop

Le principe de conception clé : transformer AVANT la validation. Le flux :

Upload S3 (PDF de politique brut)
Transformation LLM (restructurer, extraire les règles de dépenses, structurer selon la vue cible)
Chunks transformés stockés dans S3 (état transitoire — mis en cache pour réutilisation)
L'API expose les chunks structurés pour revue
Le relecteur approuve / rejette (en un clic)
Les chunks approuvés sont ingérés dans Bedrock Knowledge Base

Il s'agit d'un point de validation logique, pas d'une orchestration lourde. Le LLM fait le gros du travail en amont — parser des PDF de plusieurs pages, extraire les règles de dépenses individuelles, les structurer de façon homogène. Quand le relecteur voit enfin le résultat, il a sous les yeux des chunks propres et structurés — pas des documents bruts.

Pourquoi cela compte en production :

  • La ré-ingestion est rapide — la transformation est mise en cache dans S3. Les mises à jour de politique n'imposent pas un retraitement complet.
  • Les relecteurs voient un résultat de qualité — ils approuvent des règles structurées, pas des pavés de texte issus de PDF.
  • Moins d'erreurs à la récupération — comme le LLM pré-structure le contenu, la KB reçoit à chaque fois des chunks au format cohérent.

Pourquoi le human-in-the-loop compte

J'ai vu des équipes faire l'impasse sur la validation au motif que "on fait confiance à notre équipe Politiques". Puis quelqu'un dépose un brouillon, il est embarqué, et l'IA se met à appliquer des règles non finalisées. Un incident de ce type et vous perdez toute la confiance de l'organisation dans le système.

Anti-pattern : auto-ingestion à l'upload S3. À proscrire pour des documents sensibles à la conformité.

Pattern : Upload → le LLM transforme et structure la politique → chunks transformés mis en cache dans S3 → l'API expose les chunks pour revue → l'humain approuve ou rejette → puis ingestion dans la KB. La ré-ingestion reste rapide (la transformation est déjà faite et mise en cache), et les valideurs voient un résultat propre et structuré — pas des PDF bruts.


Latence et coût de récupération

Chiffres réels issus de la production (charge de 500 requêtes/jour) :

Latence (S3 Vectors)

p50 = temps de réponse médian (expérience typique). p99 = 99e centile (pire cas hors valeurs aberrantes extrêmes).

  • p50 : 340 ms (récupération seule, hors inférence LLM)
  • p99 : 890 ms
  • Décision de bout en bout (AgentCore inclus) : p50 ~2,1 s, p99 ~4,8 s

Détail des coûts mensuels

Composant Coût mensuel
S3 Vectors (stockage + requêtes) ~12 $
Appels API Bedrock KB ~8 $
Titan Embeddings (ingestion) ~3 $
Nova Pro (validation des reçus) ~45 $
AgentCore (décisions de politique) ~120 $
Step Functions ~5 $
Aurora MySQL (persistance) ~65 $
Total ~258 $/mois

À comparer aux 700 $/mois minimum d'OpenSearch Serverless seul. Les choix d'architecture se cumulent.


Les pièges dont personne ne vous parle

Après trois mois en production, voici ma liste :

  1. Délai de synchronisation après upload. Après un appel à StartIngestionJob, la KB n'est pas immédiatement interrogeable sur le nouveau contenu. Comptez 30 à 90 secondes pour de petites mises à jour. Prévoyez-le dans votre UX — affichez un état "mise à jour de politique en cours".

  2. Le filtrage des métadonnées se fait uniquement en correspondance exacte (S3 Vectors). Pas de requêtes par plage, pas de correspondance partielle sur les métadonnées. Concevez votre schéma autour de filtres d'égalité. S'il vous faut "toutes les politiques mises à jour après janvier 2025", il faudra une autre approche.

  3. Le choix du modèle d'embedding est définitif. Une fois une KB créée avec Titan Embeddings v2, impossible de basculer vers Cohere sans tout recréer. Choisissez bien dès le départ. (Nous avons retenu Titan v2 — bon compromis coût/qualité pour du contenu bilingue.)

  4. La récupération multilingue n'a rien de magique. Une requête en espagnol récupérera correctement des chunks en espagnol, mais la récupération inter-langues (requête en espagnol → politique en anglais) n'est pas fiable. Nous l'avons contournée en maintenant des chunks parallèles dans les deux langues et en filtrant selon la langue détectée de la requête.

  5. Pics de coût à la ré-indexation. Si vous synchronisez l'intégralité de votre KB fréquemment (au lieu de mises à jour incrémentales), les coûts d'embedding explosent. Une ré-indexation complète de 100 documents coûte ~2 $. Faites-le toutes les heures par erreur et vous brûlez 1 400 $/mois rien qu'en embeddings.

  6. Les quotas KB sont étonnamment bas. Jobs d'ingestion concurrents par défaut : 1. Taille de document par défaut : 50 Mo. Demandez vos augmentations de quotas avant d'atteindre l'échelle de production.

  7. Le problème de la "mauvaise réponse assénée avec assurance". Quand l'agent récupère un chunk proche mais pas juste, il applique la mauvaise règle sans la moindre hésitation. Atténuez ce risque en fixant un seuil de score de similarité (nous utilisons 0,7) et en redirigeant les récupérations à faible confiance vers une revue humaine.


Comment se lancer

Cinq étapes pour votre première implémentation Bedrock KB en production :

  1. Commencez avec 10 documents, pas 100. Calibrez votre stratégie de chunking sur un petit ensemble. Validez manuellement la qualité de récupération avant de passer à l'échelle.

  2. Choisissez S3 Vectors, sauf raison contraire. Pour la plupart des cas d'usage Knowledge Base sous les 10 000 documents, c'est moins cher et plus simple. Ne passez à OpenSearch que lorsque le besoin est réel.

  3. Investissez dans le chunking avant tout le reste. Le chunking par défaut est un piège pour les documents structurés. Consacrez une semaine à votre stratégie de chunking — c'est le travail à plus fort effet de levier que vous ferez.

  4. Construisez le pipeline de validation dès le premier jour. Même si le human-in-the-loop n'est pas nécessaire aujourd'hui, il le deviendra quand les enjeux monteront. Le pattern task token dans Step Functions rend cela trivial à ajouter par la suite, mais coûteux à intégrer rétroactivement.

  5. Instrumentez tout. Loggez les scores de récupération, les IDs de chunks, le niveau de confiance des décisions. On ne peut pas améliorer ce qu'on ne mesure pas. Quand la qualité de récupération se dégradera (et elle se dégradera, à mesure que votre corpus grossira), vous aurez besoin de données pour en diagnostiquer la cause.


Pour conclure

Bedrock Knowledge Base est une infrastructure réellement solide pour des systèmes RAG en production. Mais c'est dans l'écart entre "démo" et "production" que se joue chaque décision d'ingénierie intéressante — stratégies de chunking, pipelines d'ingestion, optimisation des coûts, chaînes de preuves, modes d'échec.

S3 Vectors a rendu ce projet économiquement viable à une échelle où OpenSearch aurait été démesuré. Step Functions nous a apporté les garanties d'orchestration qu'exige la conformité. Et AgentCore a transformé la récupération en décisions structurées et auditables.

La stack tient la route. Le plus difficile n'a jamais été les services AWS — c'était l'ingénierie de la donnée autour.

Faites-le bien dès la première fois. Votre vous futur (et l'équipe juridique de votre client) vous remerciera.


Dima Kramskoy est Senior Cloud Architect chez DoiT International, fort de plus de 20 ans d'expérience en ingénierie logicielle, de 10 certifications AWS, et AWS Community Builder (2026). Il accompagne les organisations dans la construction de systèmes IA/ML en production sur AWS.