Aller au contenu
English

Travail final

  • Après les études de cas
  • environ 3 h

Ce que vous saurez faire

Appliquez chaque module du cours à un nouveau problème de stockage. Vous concevez et construisez le stockage d’un deuxième service de Larkfield, l’assistant documentaire, des modes d’accès à l’exercice de restauration. Ensuite, vous apportez des preuves avec des tests, des plans et des mesures. La base de données doit respecter ses règles, ses requêtes doivent être sûres et rapides, et vous devez pouvoir récupérer et supprimer les données.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 180 minutes
  • Vous avez terminé les modules 1 à 5. Chaque partie ci-dessous nomme les leçons qu’elle utilise.
  • Vous n’avez pas besoin des études de cas pour ce travail. Le certificat exige au moins une d’entre elles.
  • Utilisez votre ticket-api à la fin du module 5, ou l’instantané end-of-m05, comme l’explique Utiliser un instantané. Vous commencez un nouveau projet, mais vous pouvez copier du code de ticket-api : l’exécuteur de migrations, le pool de connexions (pool), le contrat FileStore et les scripts. Indiquez dans votre rapport ce que vous avez copié et modifié.
  • Utilisez Docker avec PostgreSQL 18 et pgvector, ainsi qu’Azurite, comme dans le cours. Aucun compte ni paiement n’est nécessaire. Le stockage hébergé est facultatif.
  • Le modèle de plongement (embedding) est facultatif. Sans lui, vos segments n’ont pas de vecteurs, mais la partie 1 demande quand même de les prévoir.
  • Vous pouvez réaliser le travail en plusieurs séances. Exécutez les tests après chaque modification.

La situation : l’assistant documentaire de Larkfield

Section intitulée « La situation : l’assistant documentaire de Larkfield »

Dans le premier module, vous avez réparti les données d’un assistant documentaire entre différents stockages, sur papier. Maintenant, Larkfield le construit. Le personnel téléverse les documents de politiques du service de soutien. Les agents posent des questions pendant qu’ils traitent un billet de soutien. L’assistant répond à partir des documents, avec des citations. Priya veut enregistrer chaque appel de modèle pour comparer les modèles et les requêtes (prompts). Grace pose sa question habituelle : « Si une personne quitte l’équipe, ou si un document est retiré, peut-on tout supprimer partout? »

Le service s’appelle docs-assistant. Il a son propre dossier, ~/projects/docs-assistant, et sa propre base de données, docs. Il stocke :

  • Documents : chaque téléversement représente une version d’un document, avec un titre, un propriétaire, un statut (draft (brouillon), published (publié) ou withdrawn (retiré)), des dates et les personnes autorisées à le lire. Le fichier original va dans le stockage objet.
  • Segments : des morceaux du texte de chaque version de document, avec leur position et un vecteur facultatif accompagné de sa version de plongement.
  • Questions et réponses : la personne qui a posé la question, le billet d’origine, la réponse et les segments qu’elle cite.
  • Exécutions d’IA : chaque appel de modèle, avec le modèle, la version de requête, les jetons (tokens) entrants et sortants, le coût, la durée et le statut.

Les billets restent dans ticket-api. Une question stocke l’ID du billet sous forme de texte. Elle ne peut pas avoir de clé étrangère vers une table de la base de données d’un autre service. Décidez ce que cela implique pour vos règles et notez-le.

Vos données. Utilisez les 36 documents de politiques dans data/source/documents/ de votre ticket-api comme fichiers à téléverser. Ce sont des fichiers Markdown, sous licence CC0, qui forment la collection du cours RAG. Écrivez un script avec une graine fixe pour générer les questions, les réponses et les exécutions d’IA. Créez deux tailles de jeux de données, comme dans le cours. Vous pouvez lire le petit jeu ligne par ligne. Dans le grand jeu, la requête que vous optimisez à la partie 3 lit clairement une table entière. Toutes les données sont inventées : indiquez-le dans votre rapport.

Utilise Inventaire des modes d’accès, Catégories de stockage, Propriété et source de vérité, Entités et relations et Contraintes d’intégrité.

  1. Rédigez l’inventaire des modes d’accès : chaque entité, ses requêtes, sa fréquence de modification, son volume et la fraîcheur requise des réponses. Nommez les trois requêtes les plus fréquentes.
  2. Rédigez la carte du stockage. Pour chaque donnée, indiquez son stockage et précisez s’il s’agit d’un original ou d’une copie. Pour une copie, indiquez sa source et comment la recréer.
  3. Dessinez le diagramme entité-relation, avec la cardinalité de chaque relation.
  4. Écrivez le schéma sous forme de migrations en SQL simple. Appliquez-les avec un exécuteur de migrations qui enregistre une somme de contrôle pour chaque fichier. Donnez une clé primaire à chaque table. Ajoutez NOT NULL (non nul), des contraintes d’unicité, des règles CHECK (vérification) et des clés étrangères là où une règle existe. Incluez au moins ces règles : une seule version publiée par document à la fois; un fichier stocké a une taille et une somme de contrôle; un coût n’est jamais négatif et n’est pas un nombre à virgule flottante; une citation pointe vers un segment existant; chaque vecteur indique sa version de plongement, et une seule version est courante.
  5. Choisissez la règle de suppression de chaque clé étrangère (CASCADE (suppression en cascade), RESTRICT (restriction) ou SET NULL (mise à NULL)). Justifiez chaque choix en une phrase.

Utilise Transactions, Requêtes sûres, Gestion des connexions et Migrations.

  1. Placez chaque requête dans un module d’accès aux données. Chaque valeur provenant d’une requête HTTP est un paramètre. Si un nom doit figurer dans le texte SQL, il provient de votre code ou de votre base de données. Il ne provient jamais de la requête HTTP. Expliquez pourquoi dans un commentaire.
  2. Ajoutez au moins ces points de terminaison : téléverser un document (partie 4), lister les questions d’un billet, lister les exécutions d’IA pour Priya avec des filtres sur le modèle et la version de requête (partie 3), et enregistrer une réponse.
  3. Enregistrez une réponse dans une seule transaction : la question, la réponse, ses citations et son exécution d’IA. Si une partie échoue, rien n’est stocké.
  4. Utilisez un pool de connexions qui s’ouvre au démarrage et se ferme à l’arrêt. Aucune connexion ne reste occupée pendant que l’API communique avec le stockage objet ou un modèle.
  5. Écrivez une migration qui modifie une table existante contenant déjà des données. Répétez-la sur votre grand jeu de données. Rapportez sa durée et indiquez si les lectures ont attendu.

Partie 3 : une amélioration mesurée grâce à un index

Section intitulée « Partie 3 : une amélioration mesurée grâce à un index »

Utilise Lire un plan de requête, Indexer de façon réfléchie et Limiter la taille des résultats.

  1. Chargez le grand jeu de données. Choisissez un point de terminaison lent dans votre inventaire. Par exemple, la liste de Priya des exécutions d’IA pour un modèle et une version de requête, les plus récentes d’abord.
  2. Mesurez-le avant toute modification, avec une charge de travail fixe : mêmes requêtes, même nombre de requêtes simultanées et même nombre de passages. Notez la médiane et le p95, et enregistrez le résultat dans un fichier. Lisez le plan de chaque requête SQL envoyée par le point de terminaison. Trouvez ce qui prend du temps.
  3. Faites une seule modification à la fois : un index pour une requête mesurée, moins d’allers-retours, pagination par clé (keyset), colonnes nommées. Après chaque modification, relisez le plan et exécutez la même charge de travail.
  4. Mesurez le coût de chaque nouvel index : sa taille et la durée d’un lot d’insertions avec et sans lui.
  5. Montrez que le résultat se répète : deux exécutions après la modification, avec des résultats proches, et une vérification de la charge de travail identique.

Utilise Modèles de stockage objet, Stockage des vecteurs et Restauration et conservation.

  1. Stockez les fichiers des documents avec un contrat FileStore : Azurite en développement et une simulation sur système de fichiers dans les tests. Les téléversements et téléchargements utilisent des URL signées à courte durée de validité. La ligne commence à pending (en attente). Elle devient stored (stocké) seulement après que l’API a vérifié la taille réelle et la somme de contrôle. Choisissez une limite de taille. Appliquez-la à complete (finalisation) et dans une contrainte CHECK.
  2. Écrivez un script de sauvegarde qui produit l’export de la base et le manifeste à partir d’un même instantané. Écrivez un exercice de restauration qui restaure les données dans une base vide et exécute ses vérifications. Vérifiez notamment que les fichiers stockés se trouvent dans le stockage objet.
  3. Supprimez les données partout, dans deux cas. Premier cas : un document est retiré. Supprimez ses fichiers, ses segments, ses vecteurs et toute réponse en cache, mais conservez le coût des exécutions d’IA. Deuxième cas : une personne quitte l’équipe. Supprimez ses questions et réponses, ou retirez-en ses renseignements personnels. Supprimez les lignes dans une seule transaction avec une boîte d’envoi pour les fichiers, puis supprimez les fichiers.
  4. Écrivez un balayage qui trouve les fichiers orphelins, les fichiers manquants, les suppressions en attente et les segments périmés. Ces segments proviennent d’un texte qui a changé depuis.
  5. Rédigez le plan de conservation sur une page. Indiquez ce que vous sauvegardez, à quelle fréquence et pour combien de temps, et comment vous vérifiez une sauvegarde. Listez l’emplacement de chaque donnée et la durée de conservation des questions et des exécutions d’IA. Indiquez ce qui arrive à une suppression quand vous restaurez une ancienne sauvegarde.

Un parcours sans erreur qui fonctionne ne suffit pas. Montrez chaque cas avec un test ou une exécution enregistrée, et précisez lequel :

  1. La base de données rejette un doublon, un orphelin et une valeur qui enfreint une contrainte CHECK. L’erreur provient de PostgreSQL, pas seulement de votre code Python.
  2. Une entrée d’injection, comme x' OR '1'='1, dans un filtre ne renvoie aucune ligne supplémentaire.
  3. Un échec pendant l’opération « enregistrer une réponse » ne laisse aucune réponse partielle. Ensuite, toutes les connexions du pool sont libres.
  4. La modification d’une migration déjà appliquée arrête l’exécuteur avec un message clair.
  5. Une URL signée échoue pour un autre nom de fichier, pour la mauvaise action et après son expiration (403). Un fichier dépassant la limite passe à rejected (rejeté), et le fichier disparaît.
  6. Un exercice de restauration échoue pour une vraie raison que vous avez créée. Par exemple, un fichier manque ou un manifeste ne correspond pas. Indiquez que vous avez créé cette situation.
  7. Le balayage détecte une suppression arrêtée après l’étape de base de données (situation créée), puis la termine.
  8. Si vous avez des vecteurs : une recherche ne compare jamais deux versions de plongement, et une recherche filtrée renvoie assez de résultats.

Avant de remettre votre travail, prouvez que le projet fonctionne à partir de zéro, sur votre ordinateur :

  1. Copiez le projet dans un nouveau dossier, sans .venv, .env, backups/ ni les données générées.
  2. Supprimez la base de données et les fichiers avec docker compose down --volumes, puis redémarrez les services.
  3. Créez un nouvel environnement virtuel, installez les dépendances depuis requirements.txt et créez .env à partir de .env.example.
  4. Appliquez les migrations, chargez le petit jeu de données et exécutez les tests. Ensuite, générez et chargez le grand jeu de données, puis exécutez votre banc d’essai une fois.
  5. Faites une sauvegarde et exécutez l’exercice de restauration : toutes les vérifications réussissent.

Collez les commandes et leurs sorties dans votre rapport. Si une étape exigeait quelque chose d’absent du README, corrigez le README et recommencez les étapes.

Votre dossier docs-assistant (sans .env, .venv, backups/ ni le grand jeu de données), avec vos migrations, votre code, vos tests, vos scripts, les fichiers du banc d’essai et REPORT.md :

# Larkfield document assistant: storage design
## Inventory and storage map (the entities, the top queries, what is an original and what is a copy)
## Schema and constraints (the ERD, the migrations, each rule and delete rule with its reason)
## Safe access (the repository, the one-transaction answer, the pool, the rehearsed migration)
## Measured improvement (before and after: plans, workload, median and p95, index sizes, write cost)
## Files (the FileStore contract, the signed URLs, the size limit)
## Backup, restore and retention (the drill, the failed drill, the deletions, the scan, the one-page plan)
## Failure paths (each of the eight, with the test or the run that shows it)
## Fresh-environment run (the commands and their outputs)
## Limits (made-up data, your computer, what you copied, what you did not test)

Une personne évalue chaque élément requis comme démontré, partiellement démontré ou pas encore démontré, avec une raison concrète. Pour terminer le cours, vous devez démontrer les quatre éléments. Vous pouvez améliorer un élément et le soumettre de nouveau.

Élément requis Démontré Partiellement démontré, par exemple Pas encore démontré, par exemple
Schéma et contraintes Les migrations s’appliquent à une base vide. Chaque règle de la partie 1 est une contrainte. Un test montre que PostgreSQL rejette un doublon, un orphelin et une violation de CHECK. Chaque règle de suppression est justifiée. Une ligne de fichier devient stored seulement après une vérification. Les règles existent, mais l’une est seulement dans Python. Ou une règle de suppression n’est pas justifiée. Ou le coût est un real (nombre à virgule flottante). Une table n’a pas de clé primaire. Ou la base accepte une citation d’un segment inexistant.
Requêtes paramétrées Chaque valeur de requête HTTP est un paramètre, et l’entrée d’injection ne renvoie aucune ligne supplémentaire. « Enregistrer une réponse » forme une seule transaction : un échec ne stocke rien et libère la connexion. Toutes les valeurs sont des paramètres, mais une réponse échouée laisse une question sans réponse. Ou chaque requête HTTP ouvre une nouvelle connexion. Une requête SQL utilise le texte de la requête HTTP dans une chaîne formatée ou avec +, même une seule fois.
Amélioration mesurée grâce à un index Les exécutions avant et après utilisent les mêmes données et la même charge de travail. Une vérification le prouve. Chaque modification a son plan, et l’amélioration se répète. Chaque nouvel index a une taille et un coût d’écriture mesurés. Le point de terminaison est plus rapide, mais une seule exécution a été mesurée. Ou les plans manquent, ou le coût d’écriture n’a pas été mesuré. Des index ont été ajoutés sans mesure préalable. Ou les deux exécutions utilisaient des données différentes.
Plan de sauvegarde, de restauration et de conservation L’exercice réussit dans l’environnement neuf, et un échec créé le fait échouer. Les suppressions touchent les lignes, les fichiers et les vecteurs. Le balayage trouve une suppression à moitié terminée. Le plan répond à toutes les questions de la partie 4, étape 5. La sauvegarde fonctionne, mais l’exercice ne vérifie pas les fichiers. Ou le plan ne précise pas ce qui arrive à une suppression après une restauration. Aucune restauration n’a été exécutée. Ou une suppression laisse des fichiers que rien ne détecte.

Même si le reste est solide, le travail reste incomplet si une vérification d’intégrité, d’accès par URL signée ou de suppression échoue. Un point de terminaison rapide ne compense pas une règle que la base de données n’applique pas.

Les résultats du cours peuvent vous aider à juger les vôtres, mais ce ne sont pas des cibles. Sur l’ordinateur du cours, la liste des billets est passée de 375 ms à 2,8 ms par page. L’exercice de restauration a réussi 7 vérifications sur 7. Votre service, vos données et votre ordinateur sont différents.

Erreur courante

Le point de terminaison est rapide, mais le banc d'essai diffère

Symptôme : la médiane « après » est 50 fois plus basse que la médiane « avant ». Mais entre les deux exécutions, vous avez aussi chargé le petit jeu au lieu du grand, ou changé la taille des pages.

Cause : la comparaison mesure deux changements à la fois. Le résultat plus rapide peut venir du volume réduit de données, pas de votre index.

Correction : enregistrez la charge de travail avec chaque résultat. Comparez les deux fichiers avant leurs nombres, comme dans Limiter la taille des résultats. Rechargez les mêmes données et changez une seule chose à la fois.

Récapitulatif

Dans ce cours, vous avez donné à un service d’IA une conception de stockage sur laquelle il peut compter :

  • Choisir le stockage selon la charge de travail : un inventaire des modes d’accès, un stockage pour chaque donnée et un original pour chaque copie.
  • Concevoir le schéma : des clés, contraintes et règles de suppression appliquées par la base, et des transactions qui gardent chaque modification entière.
  • Accéder aux données en toute sécurité : des requêtes paramétrées, un pool de connexions et des migrations répétées sur des données représentatives.
  • Accélérer grâce aux mesures : des plans de requête, un index pour une requête mesurée, des pages par clé et la même charge avant et après.
  • Fichiers, vecteurs, sauvegardes et suppression : des URL signées, des versions de plongement, un exercice de restauration qui peut échouer et une suppression qui atteint chaque stockage.

Si vous n’en avez pas encore fait, choisissez une étude de cas pour le certificat : Concevoir et optimiser le stockage d’un jeu de données public ouvert ou Recherche vectorielle dans une collection de documents ouverte.