Aller au contenu
English

La passation opérationnelle

  • Leçon 18
  • 45 min
  • Module 6 : Maintenir et protéger la base de connaissances

Ce que vous saurez faire

Notez tout ce dont une autre personne a besoin pour exécuter l’assistant, reconstruire son index et obtenir les mêmes résultats. Cela comprend les versions dont dépend une réponse, son coût, sa durée et une commande pour reconstruire l’assistant.

Avant de commencerCe qu’il faut savoir et avoir sous la mainEnviron 45 minutes

Ce que vous devez savoir

Ce qu’il vous faut sur votre ordinateur

Aucun appel de modèle et aucun compte. Testé avec Python 3.12.13 sur macOS (Apple silicon). Les commandes Windows ne sont pas testées.

Une fiche de recette pour la prochaine personne en cuisine

Section intitulée « Une fiche de recette pour la prochaine personne en cuisine »

La cheffe d’un restaurant connaît par cœur la recette de son meilleur plat. Elle part en vacances, et le plat a un goût différent la semaine suivante. Le nouveau cuisinier a utilisé une autre farine, un four plus chaud et un peu moins de sel. Personne n’avait noté l’importance de ces détails.

Les cuisines gardent donc une fiche de recette : les ingrédients exacts avec leurs marques, les quantités, la température du four, la durée et le coût d’une assiette. Avec cette fiche, un nouveau cuisinier peut préparer le même plat. Quand le plat change, la fiche montre pourquoi.

Le problème : le même projet, un assistant différent

Section intitulée « Le problème : le même projet, un assistant différent »

Amira part en vacances, et Tomás prend en charge l’assistant. Il connaît bien Python. Mais les réponses de l’assistant dépendent de plus que son code. De petits changements causent des problèmes sans erreur claire :

  • Un modèle de plongement remplacé par un autre de même taille. Dans Les plongements pour la recherche, la recherche dans l’index e5 avec les vecteurs de questions all-MiniLM-L6-v2 donnait un hit@5 de 0,068, sans message d’erreur.

  • Un fichier de requête avec une espace de plus. Tomás ajoute une espace entre deux mots de answer_v1.md, pour nettoyer le texte. Ensuite, ask Q22 répond :

    Sortie
    No answer: No recording for this request (key 6c964385674edd7d). The mock replays only the questions that were recorded for the course, with the same prompt and the same model.

    Avec un modèle réel, rien n’échoue. Mais Priya a pris toutes les mesures du module 5 avec l’ancienne requête. Aucune ne constitue donc une preuve pour la nouvelle.

  • Un document modifié directement. Grace corrige une phrase, comme dans Mises à jour et suppressions. Deux identifiants de segments changent, et les anciennes réponses citent des identifiants qui n’existent plus.

Aucun de ces éléments ne fait partie du code. Chacun est une version de quelque chose que le code utilise.

Une fiche de passation est la fiche de recette de l’assistant. Elle énumère tout ce dont dépend une réponse, avec sa version exacte. Elle indique aussi le coût de l’assistant, sa durée d’exécution et comment le reconstruire. Le code peut afficher une partie de ces renseignements, car l’index et le projet les conservent déjà. Le reste va dans le README, à côté du code.

Une réponse de l’assistant des politiques dépend des éléments suivants :

  • les documents, et donc les segments et leurs identifiants;
  • le découpeur;
  • le modèle de plongement, avec sa révision;
  • le modèle de reclassement (reranker), avec sa révision;
  • les requêtes (prompts), octet par octet;
  • le modèle de réponse, avec sa date;
  • les limites : le budget de contexte et la limite de réponse;
  • les paquets : Python et les bibliothèques qui exécutent les modèles.

Un petit exemple détaillé : une empreinte par élément

Section intitulée « Un petit exemple détaillé : une empreinte par élément »

Une version doit être exacte et assez courte pour permettre une comparaison visuelle. Le projet utilise la même astuce que pour les identifiants de segments : une courte empreinte SHA-256. Pour un fichier de requête, il calcule le hachage des octets du fichier et garde 8 caractères :

Python session, in ~/projects/policy-assistant
import hashlib
hashlib.sha256(open("policy_assistant/prompts/answer_v1.md", "rb").read()).hexdigest()[:8]
Sortie
'7b7c7836'

Avec l’espace ajoutée par Tomás, la même ligne donne 290968ed. La différence tient à un caractère invisible, et l’empreinte la montre immédiatement. Pour les documents, le projet calcule le hachage de la liste des fichiers et du hachage de leur contenu. Une seule empreinte représente ainsi les 36 documents : ec1d4db6e641. Les modèles ont déjà leurs propres empreintes. La révision Hugging Face est le hachage des fichiers du modèle à une date donnée.

La nouvelle commande info affiche la partie de la fiche que le projet conserve :

Fenêtre de terminal
python -m policy_assistant info
Sortie
index: index.sqlite | last sync 2026-10-09T06:28:30+00:00
documents: 36 (fingerprint ec1d4db6e641) | chunks: 185 | vectors: 185 x 384
chunker: structure
embedding model: intfloat/multilingual-e5-small (614241f622f53c4eeff9890bdc4f31cfecc418b3)
reranker: cross-encoder/mmarco-mMiniLMv2-L12-H384-v1 (1427fd652930e4ba29e8149678df786c240d8825) | candidates: 20
prompts: answer_v1 7b7c7836, answer_v2 c4e40fe8, closed_book 9131b52f, rewrite 71abe061
context budget: 1500 estimated tokens | answer limit: 2000 tokens
answers: provider mock | model chat-small
python 3.12.13 | sentence-transformers 6.1.0 | torch 2.14.1 | pypdf 6.19.0 | openai 3.26.1
rebuild: python -m policy_assistant ingest --rebuild --chunker structure --embedder intfloat/multilingual-e5-small

Votre heure last sync (dernière synchronisation) diffère; toutes les autres lignes devraient être identiques. vectors: 185 x 384 est une vérification rapide : un vecteur par segment, avec les 384 nombres d’e5. Le diagramme associe chaque élément à sa ligne et indique ce qu’un changement implique :

Un tableau de sept éléments. Documents : 36, empreinte ec1d4db6e641; si elle change, le texte et les identifiants des segments changent. Les réponses enregistrées sont rejouées avec une note : mesurer de nouveau. Découpeur : structure; ingest refuse de mélanger les découpeurs : reconstruire, puis mesurer de nouveau. Modèle de plongement : multilingual-e5-small, révision 614241f622f5; ingest et la recherche le refusent; mélanger deux modèles donnait un hit@5 de 0,068. Modèle de reclassement : mmarco-mMiniLMv2-L12-H384-v1, révision 1427fd652930; l'ordre des 5 premiers résultats change : mesurer de nouveau. Requêtes : answer_v1 7b7c7836, answer_v2 c4e40fe8; un fichier modifié ne correspond plus aux requêtes enregistrées : enregistrer de nouveau. Modèle de réponse : mock, chat-small, enregistré avec gpt-6-luna-2026-09-22; les réponses, le coût et la durée changent : mesurer de nouveau, avec la date. Budget et limites : 1 500 jetons de contexte, 2 000 jetons de réponse; plus ou moins de passages peuvent entrer : mesurer de nouveau.
Ce dont dépend une réponse, où info l'affiche et ce qu'un changement implique. Le projet refuse les changements qui produiraient des résultats incohérents; les autres exigent une nouvelle mesure.
Description textuelle du diagramme

Un tableau de sept lignes et trois colonnes : l’élément, sa valeur dans info et les conséquences d’un changement. Documents : 36, empreinte ec1d4db6e641; le texte et les identifiants des segments changent. Les réponses enregistrées sont rejouées avec une note; il faut donc mesurer de nouveau. Découpeur : structure; ingest refuse de mélanger les découpeurs; il faut donc reconstruire, puis mesurer de nouveau. Modèle de plongement : multilingual-e5-small, révision 614241f622f5 et plus; ingest et la recherche refusent un autre modèle. Mélanger deux modèles donnait un hit@5 de 0,068. Modèle de reclassement : mmarco-mMiniLMv2-L12-H384-v1, révision 1427fd652930 et plus; l’ordre des 5 premiers résultats change; il faut donc mesurer de nouveau. Requêtes : answer_v1 7b7c7836 et answer_v2 c4e40fe8; un fichier modifié ne correspond plus aux requêtes enregistrées; il faut donc enregistrer de nouveau. Modèle de réponse : le simulateur avec chat-small, enregistré avec gpt-6-luna-2026-09-22. Les réponses, le coût et la durée changent; il faut donc mesurer de nouveau et noter la date. Budget et limites : 1 500 jetons de contexte et 2 000 jetons de réponse; plus ou moins de passages peuvent entrer; il faut donc mesurer de nouveau.

Omar pose toujours les mêmes questions : combien cela coûte-t-il, et combien de temps un agent attend-il? Les réponses viennent des exécutions enregistrées du cours, aux prix en vigueur au moment de l’enregistrement. Les prix changent; consultez donc la liste de prix actuelle. Par million de jetons d’entrée et de sortie, chat-small coûte 0,10 et 0,50 $ US, et chat-strong, 2 et 10.

Construire l’index ne coûte rien avec le modèle local e5. Avec embed-small hébergé à la place, une exécution complète de plongement coûtait 0.0005 $ US. Elle utilisait 25,362 jetons pour 443 textes : les segments des trois découpeurs, les questions et leurs reformulations. Une question coûte 0,00017 $ US avec chat-small et 0,0031 $ US avec chat-strong. Le modèle prend la majeure partie du temps, et le modèle de reclassement prend presque tout le reste :

Répartition du temps pour une question : plongement de la question avec e5, BM25, recherche vectorielle, reclassement de 20 candidats (local, 2 fils d'exécution du processeur) et réponse de chat-small (small) ou chat-strong (strong), enregistrée, médiane.
Description textuelle du graphique

Un diagramme à colonnes en millisecondes, valeurs médianes. Plongement de la question avec e5 : 5.2. BM25 : 0.26. Recherche vectorielle : 0.09. Reclassement de 20 candidats : 322. Réponse de chat-small (small) : 1460. Réponse de chat-strong (strong) : 2460.

Les étapes de recherche sont trop courtes pour être visibles, sauf le modèle de reclassement : 322 ms. Avec chat-small, un agent attend environ 1,8 seconde. Pour une question sur vingt, le modèle seul prend 3,72 secondes ou plus (son 95e centile). Pour une journée entière, le choix du modèle compte plus que tout le reste :

Le coût d'une journée de 45 questions : chat-small et chat-strong avec récupération (5 premiers résultats, environ 900 jetons d'entrée), et chat-strong avec toute la collection dans chaque requête (environ 17 850 jetons). Les prix changent : consultez la liste de prix actuelle.
Description textuelle du graphique

Un diagramme à colonnes en dollars américains pour 45 questions par jour. chat-small avec récupération : 0.0077. chat-strong avec récupération : 0.14. chat-strong avec toute la collection dans la requête : 1.7.

Le README de l’instantané end-of-m06 contient ces nombres, dans une section Handoff record (fiche de passation). Il contient aussi la configuration, les modèles avec leurs révisions et leurs licences, ainsi que les variables d’environnement. Il indique ce qui invalide les réponses enregistrées et précise quand et où le projet a été testé pour la dernière fois. Les prix changent; le README recommande donc de consulter la liste de prix et donne la date.

La dernière ligne de info est la commande de reconstruction. Exécutez-la, puis vérifiez que l’index reconstruit est identique : même empreinte, mêmes nombres et mêmes mesures.

Output of the rebuild command
36 added, 0 updated, 0 unchanged, 0 removed | chunks: +185 -0
added: 36 files
index index.sqlite: 36 documents, 185 chunks | chunker structure | embeddings intfloat/multilingual-e5-small

Ensuite, info affiche encore ec1d4db6e641, et eval affiche hit@5 0.898 | recall@5 0.856 | precision@5 0.231 | MRR 0.831, comme au module 5. Si la reconstruction donne d’autres nombres, un élément de la fiche ne correspond pas à ce qu’elle indique.

Erreur courante

Un nettoyage qui casse toutes les réponses enregistrées

Symptôme : après une petite modification de answer_v1.md, ask Q22 affiche No recording for this request (aucun enregistrement pour cette requête). Avec un modèle réel, les réponses semblent correctes; personne ne remarque donc le problème pendant des semaines.

Cause : le texte de la requête fait partie de chaque requête envoyée, donc de la clé qui trouve un enregistrement. Un vrai cache ou un jeu de données de test enregistré utilise la même clé. Une espace de plus crée une nouvelle requête.

Diagnostic : exécutez info et comparez les empreintes des requêtes avec la fiche de passation : answer_v1 290968ed au lieu de 7b7c7836.

Correction : ne modifiez jamais une requête directement. Copiez-la dans une nouvelle version (answer_v3.md), mesurez cette version sur le jeu de questions et enregistrez ses réponses. Gardez l’ancienne pour les anciennes mesures. C’est ainsi que answer_v2 a été créée dans Autorisations et isolation.

Quand une fiche de passation vaut la peine, et ce qu’elle coûte

Section intitulée « Quand une fiche de passation vaut la peine, et ce qu’elle coûte »

Rédigez-en une pour chaque assistant qu’une autre personne exécutera, ou que vous exécuterez de nouveau dans un mois. Elle coûte une commande et une section du README, et vous devez la mettre à jour quand un élément change. Une fiche périmée est pire que l’absence de fiche, car les gens lui font confiance. Laissez donc le code afficher ce qu’il peut (info). Réservez le README à ce que seules les personnes connaissent : prix, mesures et dates, ainsi que limites connues.

Cette fiche est un début, pas une gestion opérationnelle. Exécuter l’assistant pour de vrais utilisateurs exige davantage : réingestion planifiée, surveillance des réponses et des coûts, et alertes. Il faut aussi des mises en production sûres d’une nouvelle requête ou d’un nouveau modèle. Il faut pouvoir revenir en arrière si une mise en production tourne mal. Le cours MLOps et LLMOps : exploiter l’IA en production enseigne cela.

  1. Copiez le README complet de l’instantané :

    Fenêtre de terminal
    cp ../nextia-learning-labs/C09/snapshots/end-of-m06/README.md .

    Votre projet est maintenant identique à l’instantané end-of-m06, la version finale du projet du cours.

  2. Ouvrez README.md et lisez les sections Rebuild and update (reconstruire et mettre à jour) et Handoff record.

  3. Affichez la fiche :

    Fenêtre de terminal
    python -m policy_assistant info
  4. Reconstruisez l’index avec la commande de la dernière ligne de la sortie, puis exécutez encore info. L’empreinte est toujours ec1d4db6e641.

  5. Mesurez l’index reconstruit. Vous voyez hit@5 0.898 à la deuxième ligne :

    Fenêtre de terminal
    python -m policy_assistant eval
  6. Dans le README, ajoutez une ligne sous Tested (testé) avec votre date, votre système d’exploitation et votre version de Python.

Exercice du module : remplacer une version de politique et retirer un document

Section intitulée « Exercice du module : remplacer une version de politique et retirer un document »

Le service des finances publie la version 2 de la politique des cartes-cadeaux, en vigueur aujourd’hui, le 9 octobre 2026. Les cartes-cadeaux vont maintenant de 20 à 750 dollars. Le même jour, Grace retire l’avis de retours des fêtes de 2025. Il a pris fin le 31 janvier 2026, et aucun retour de cette période ne peut encore être ouvert. Faites les deux changements, puis prouvez que l’ancien texte n’apparaît plus.

  1. Copiez corpus/documents/gift-cards.md vers corpus/documents/gift-cards.v2.md.

  2. Dans gift-cards.v2.md, changez trois choses : version: 1 pour version: 2, effective_from: 2025-06-01 pour effective_from: 2026-10-09, et « from 25 to 500 dollars » (de 25 à 500 dollars) pour « from 20 to 750 dollars » (de 20 à 750 dollars).

  3. Dans gift-cards.md, ajoutez la ligne effective_to: 2026-10-08 sous effective_from. La version 1 prend maintenant fin la veille de l’entrée en vigueur de la version 2.

  4. Supprimez corpus/documents/holiday-returns-2025.md.

  5. Exécutez ingest. Prédisez le rapport avant de le lire.

  6. Cherchez « What amounts do gift cards come in? » (quels montants sont offerts pour les cartes-cadeaux?) avec --method bm25 --k 2, une fois sans --as-of et une fois avec --as-of 2026-10-08.

  7. Cherchez « Holiday returns extension 2025 » (prolongation des retours des fêtes de 2025) avec --method bm25 --k 3 --as-of none.

Afficher les résultats attendus

Étape 5 : 1 added, 1 updated, 34 unchanged, 1 removed | chunks: +6 -6. Le nouveau fichier est ajouté (3 segments). La version 1 est relue parce que ses métadonnées ont changé (3 retirés, 3 ajoutés). L’avis est retiré (3 retirés). chunks gift-cards énumère 6 segments de documents/gift-cards.md, documents/gift-cards.v2.md. La version 1 garde ses identifiants : la nouvelle ligne est une métadonnée, pas du texte.

Étape 6 : aujourd’hui, les deux premiers résultats sont [21240db11429] gift-cards v2 et [68d2e5bc69bc] gift-cards v2, « from 20 to 750 dollars ». Le 8 octobre 2026, ce sont [f27bb50ce8b7] et [8c4bc4299042], version 1, « from 25 to 500 dollars ». La date retire la version 1; elle n’est pas supprimée. Une question sur une carte-cadeau achetée le 8 octobre la trouve encore.

Étape 7 : même sans filtre de date, l’avis a disparu. Les résultats sont Document types (types de documents), Returns for trade customers (retours pour la clientèle professionnelle) et Returns and points (retours et points).

La vérification de fin de module : confirmez que les passages périmés et supprimés n’apparaissent plus dans les réponses. La recherche en constitue la moitié, et vous l’avez vérifiée plus haut. Maintenant, les réponses : exécutez audit pendant que vos deux changements sont encore en place.

Fenêtre de terminal
python -m policy_assistant audit
Output (the first 9 of 15 lines)
Q25 chat-small rerank answer_v1: [bbd5032e5107] removed from the index
Q25 chat-small bm25 answer_v1: [a5a937704e3f] removed from the index
Q25 chat-small bm25 answer_v1: [bbd5032e5107] removed from the index
Q25 chat-strong rerank answer_v1: [a5a937704e3f] removed from the index
Q25 chat-strong rerank answer_v1: [bbd5032e5107] removed from the index
Q25 chat-strong bm25 answer_v1: [a5a937704e3f] removed from the index
Q25 chat-strong bm25 answer_v1: [bbd5032e5107] removed from the index
Q63 chat-small rerank answer_v1: [52ad3a57c1d1] gift-cards v1 is not in force on 2026-10-09
Q63 chat-strong rerank answer_v1: [52ad3a57c1d1] gift-cards v1 is not in force on 2026-10-09

La dernière ligne est 314 recorded answers | 15 citations to passages that are removed, not in force or not allowed (314 réponses enregistrées; 15 citations de passages supprimés, non en vigueur ou non autorisés). Les six autres lignes sont des réponses supplémentaires enregistrées pour Q25 et Q26 qui citent aussi l’avis. Ces réponses stockées contiennent encore l’ancien texte. Q25 cite l’avis supprimé. Q63, la question en français sur le retour d’une carte-cadeau, cite Lost cards and returns (cartes perdues et retours) de la version 1. Cette version n’est plus en vigueur aujourd’hui. Posez cette question :

Fenêtre de terminal
python -m policy_assistant ask Q63

La recherche donne maintenant le passage ba3e515c5d04 de la version 2. La simulation rejoue la réponse enregistrée, et ses deux affirmations affichent CHECK: not_in_context:52ad3a57c1d1 (vérification : absent du contexte). Le passage périmé n’a pas atteint le contexte; seule l’ancienne réponse stockée le mentionne encore. Dans un véritable assistant, les 15 citations répertoriées par audit indiquent les réponses en cache à supprimer. Un modèle en service répond ensuite de nouveau à partir des passages en vigueur.

Annulez ensuite l’exercice pour que votre projet corresponde de nouveau à celui du cours. Supprimez gift-cards.v2.md, puis recopiez gift-cards.md et holiday-returns-2025.md depuis ../nextia-learning-labs/C09/corpus/documents/. Exécutez ensuite ingest. audit doit se terminer par 0 citations (0 citation).

Vérifiez votre compréhension

Q25 porte sur une lampe livrée le 1 décembre 2025. Après l'exercice, que devrait répondre un assistant en service, et que doit modifier Grace dans le jeu de questions?

Afficher la réponse

L’avis des fêtes a disparu. Les passages en vigueur pour le 1 décembre 2025 proviennent donc de la version 3 de la politique de retour : 30 jours. Un assistant en service devrait répondre à partir de cette version. La lampe a été livrée le 1 décembre; les 30 jours se terminaient donc le 31 décembre. Le retour du 15 janvier est en retard. Grace a supprimé les preuves volontairement. La réponse de référence et les passages pertinents de Q25 sont donc maintenant incorrects. Grace doit mettre à jour Q25 ou la supprimer, puis consigner le changement dans la fiche du jeu de données. Elle doit le faire avant toute nouvelle comparaison des mesures.

Vérifiez votre compréhension

Tomás veut remplacer le modèle de plongement par un modèle plus récent et plus grand. À partir du dossier de passation, énumérez les étapes à suivre, dans l'ordre.

Afficher la réponse
  1. Consigner le nom et la révision du nouveau modèle. 2. Reconstruire l’index avec ingest --rebuild --embedder NAME, car les vecteurs de deux modèles ne peuvent pas être mélangés. 3. Exécuter info : la ligne des vecteurs affiche le nouveau nombre de dimensions. 4. Exécuter eval et comparer avec hit@5 à 0,898 et MRR à 0,831 sur les mêmes 67 questions. 5. Mesurer les réponses avec un modèle en service, car les passages changent et les réponses enregistrées ne correspondent plus. 6. Noter les nouveaux temps et coûts d’ingestion et de recherche. 7. Mettre à jour le dossier de passation du README, avec la date.

Vérifiez votre compréhension

Prédisez : Grace ne modifie aucun fichier, mais Tomás installe une version plus récente de sentence-transformers. Quelles lignes d'info changent, et pourquoi devrait-il quand même exécuter eval?

Afficher la réponse

Seule la ligne des paquets change. La révision du modèle reste la même; les vecteurs stockés sont donc encore valides. Mais une nouvelle version de la bibliothèque peut calculer les vecteurs du même modèle un peu différemment. La segmentation en jetons, l’agrégation ou la précision numérique peuvent changer. Les résultats presque à égalité peuvent alors changer de rang. eval indique si les mesures restent à 0,898 pour hit@5 et à 0,831 pour MRR. Si elles ont changé, reconstruisez l’index et consignez la raison.

Récapitulatif

  • Un dossier de passation énumère tout ce dont dépend une réponse, avec les versions exactes : documents, outil de découpage, modèle de plongement, modèle de reclassement, requêtes, modèle de réponse, limites et paquets. info affiche la partie stockée par le projet, avec des empreintes courtes.
  • Un seul espace supplémentaire dans une requête a créé une nouvelle requête : answer_v1 290968ed au lieu de 7b7c7836. Aucun enregistrement ne correspondait. Créez une nouvelle version de la requête plutôt que d’en modifier une.
  • Coûts : un calcul complet des plongements avec embed-small coûte 0,0005 $ US; une question coûte 0,00017 $ US avec chat-small, 0,0031 $ US avec chat-strong. Le modèle prend le plus de temps, suivi du modèle de reclassement (322 ms).
  • La commande de reconstruction donne la même empreinte et les mêmes mesures. Sinon, le dossier est incorrect.
  • Vérification du module : après une nouvelle version et une suppression, la recherche ne renvoie plus l’ancien texte. audit répertorie chaque réponse stockée qui le cite encore.

Votre projet correspond maintenant à l’instantané end-of-m06 : le projet du cours est terminé. Les études de cas appliquent la même méthode à de véritables données ouvertes, en commençant par des pages d’orientation publiques.

Leçon suivante : Répondre à partir de pages d'orientation publiques