Un RAG serverless sur AWS qui ne coûte presque rien au repos

Ingestion documentaire et recherche sémantique, entièrement managées : Cognito, Lambda, Bedrock et S3 Vectors, sur une infrastructure Terraform et un compte AWS personnel. Une contrainte tenue depuis le premier jour : aucune capacité réservée à payer quand personne ne s’en sert. La génération des réponses, elle, attend encore l’accès au modèle.

Mis à jour septembre 20266 min de lecture

AWS LambdaAPI GatewayCognitoBedrockS3 VectorsTerraform

Le point de départ

Un RAG (Retrieval-Augmented Generation, une génération de réponses appuyée sur vos propres documents) se prototype en quelques heures, à condition d’accepter une base vectorielle managée. La facture arrive plus vite que la démo : la plupart de ces bases facturent une capacité réservée, qu’on les interroge ou non.

Pour un projet personnel qui doit rester en ligne en permanence, c’était exclu. La contrainte était donc double : rester intégralement serverless, et ne presque rien payer à vide.

Le pipeline

  • Ingestion : connexion Cognito, puis appel à une API qui renvoie une URL S3 présignée ; le navigateur envoie le fichier directement à S3, sans jamais le faire transiter par une Lambda.
  • Extraction et découpage : un événement S3 déclenche la Lambda d’ingestion, qui extrait le texte et le découpe en fragments.
  • Vectorisation : chaque fragment devient un vecteur de 1 024 dimensions, calculé par Amazon Titan Text Embeddings v2 sur Bedrock.
  • Stockage : les vecteurs sont écrits dans un index S3 Vectors.
  • Interrogation : la question est vectorisée à son tour, les fragments les plus proches sont récupérés, puis transmis à un modèle de génération.
1. URL présignée2. PUT présigné3. événementembedvecteurs4. PutVectorsNavigateurAPI GatewayLambda /uploadsAmazon S3bucket landingAmazon BedrockTitan Embeddings v2Lambda ingestS3 Vectors
Le chemin d’ingestion. Le fichier ne transite jamais par une Lambda : le navigateur écrit directement dans S3 avec une URL présignée. L’interrogation réutilise les deux mêmes services : Bedrock pour vectoriser la question, S3 Vectors pour la recherche.

La décision structurante : la base vectorielle

OpenSearch Serverless est la réponse par défaut sur AWS. Elle impose un plancher de capacité facturé en continu, de l’ordre de 175 à 350 $ par mois selon la redondance, pour un index que personne n’interroge : plusieurs dizaines de fois le budget total de ce portfolio.

S3 Vectors stocke les vecteurs comme des objets S3 et se facture au stockage et à la requête. Le compromis est réel : la latence est plus élevée que celle d’un index en mémoire, et les possibilités de filtrage plus limitées. Pour un corpus de quelques centaines de documents consulté ponctuellement, ce compromis est sans conséquence. La décision et l’alternative rejetée sont tracées dans un ADR (Architecture Decision Record) du dépôt d’infrastructure.

Découper le texte sans perdre le contexte

Le découpage décide de la qualité des réponses bien avant le choix du modèle. Il est ici volontairement simple et déterministe : les fragments suivent les frontières de paragraphes, plafonnent à 1 500 caractères, et la fin de chacun est recopiée en tête du suivant pour qu’une phrase coupée reste compréhensible.

typescript
const DEFAULT_CHUNK_MAX_CHARS = 1500;
const DEFAULT_CHUNK_OVERLAP_CHARS = 200;
const DEFAULT_MAX_CHUNKS_PER_DOC = 200;

Ces valeurs viennent de l’environnement et sont validées au démarrage de la Lambda par un schéma Zod. Une variable manquante fait échouer le démarrage avec un message explicite, au lieu de produire silencieusement des embeddings inutilisables.

Une logique testable sans AWS

Le cœur métier (découpage, ingestion, réponse à une question) ne dépend que d’interfaces, appelées ports : un pour la vectorisation, un pour le stockage des vecteurs, un pour la source documentaire, un pour la génération. Les appels réels à Bedrock, S3 et Cognito vivent dans des adaptateurs séparés.

C’est l’architecture hexagonale, et sur un projet serverless elle règle surtout un problème très concret : la logique se teste en mémoire, sans appel réseau ni déploiement, et changer de fournisseur d’embeddings revient à écrire un adaptateur.

Sécurité et déploiement

  • Aucune clé AWS longue durée : la chaîne d’intégration continue s’authentifie par OIDC (OpenID Connect) et assume un rôle le temps du déploiement.
  • Un dépôt, un rôle, ses artefacts : trois rôles distincts (infrastructure, site, RAG), pour qu’un commit sur l’un ne puisse pas réécrire les artefacts de l’autre.
  • Lambdas hors VPC : aucune passerelle NAT à payer, et des permissions IAM scopées ressource par ressource.
  • Aucun secret dans le code : l’upload passe par une URL présignée à durée de vie courte, et les Lambdas ne reçoivent que des références.
  • Un budget AWS mensuel avec alerte encadre l’ensemble du compte.

Où en est le projet

Ingestion et recherche fonctionnent de bout en bout en production, dans la région Paris (eu-west-3). Un document déposé depuis le navigateur ressort indexé dans S3 Vectors, et l’endpoint POST /query, protégé par Cognito, vectorise la question, récupère les fragments les plus proches et renvoie les documents sources dédoublonnés avec leur score de similarité.

Deux garde-fous bornent le coût d’une requête : la question est plafonnée à 2 000 caractères et le nombre de fragments récupérés à 8, via un contrat Zod partagé entre le client et le serveur. Et quand la recherche ne remonte rien, une réponse fixe est renvoyée sans jamais appeler le modèle : on ne paie pas une génération sans contexte.

Reste le dernier maillon, la génération de la réponse : l’adaptateur est écrit et testé, il attend l’ouverture de l’accès au modèle côté Bedrock, une démarche administrative et non un blocage technique.