Aller au contenu
English

Chercher dans une documentation technique

  • Étude de cas 2
  • 120 min
  • Facultative · 1 étude de cas requise pour le certificat

Ce que vous saurez faire

Choisissez une méthode de recherche pour une vraie documentation technique, avec des preuves tirées de vos propres questions. Vous comparez la recherche par mots-clés, vectorielle, hybride et avec reclassement sur la documentation de SQLite. Vous mesurez leurs résultats par type de question, avec leur durée et leur coût. Et vous constatez que la méthode gagnante ici n’est pas celle qui gagnait sur les politiques de Larkfield.

Étude de casPièce de portfolio

Durée
Environ 2 h
S’exécute dans
Colab, Kaggle ou VS Code
Compétences utilisées
  • Analyse avec provenance
  • Étiquettes de pertinence et mise en commun
  • BM25, e5 et plongements hébergés
  • Recherche hybride et reclassement
  • recall@k, MRR, hit@1 par type
  • Comparaison appariée
  • Réponses avec citations

Les carnets sont en anglais. Colab et Kaggle ouvrent votre propre copie. Pour utiliser VS Code, téléchargez le carnet depuis GitHub. Vous êtes bloqué ? Consultez le carnet de solution.

Le carnet (notebook) Jupyter exécute chaque étape de cette page avec le même code et les mêmes sorties. Il ajoute trois tâches avec des vérifications. Sa configuration télécharge l’instantané de la documentation et les vecteurs précalculés (environ 20 Mo), ainsi que les deux modèles locaux du cours. L’exécution a pris environ 2 minutes sur le portable de test, avec les modèles déjà téléchargés.

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

Ce que vous devez savoir

Cette étude de cas utilise tout le cours. Ces leçons sont les plus importantes :

Ce qu’il vous faut sur votre ordinateur (uniquement pour l’exécution locale; Colab et Kaggle n’en ont pas besoin)

  • Un environnement virtuel avec les paquets du cours, comme dans le projet de Les plongements pour la recherche. Le carnet installe seulement pandas et matplotlib s’ils manquent.
  • Le carnet, téléchargé à partir du lien ci-dessus. Sa cellule de configuration crée un dossier sqlite-docs à côté de lui. Pour recommencer, supprimez ce dossier.

Aucun compte ni aucune clé. Testé sur macOS (Apple silicon) avec Python 3.12.13, sentence-transformers 6.1.0 et SQLite 3.53.4. Windows, Linux, Colab et Kaggle n’ont pas encore été testés.

Dana dirige le service de soutien aux développeurs d’une petite entreprise de logiciels. Son produit stocke ses données dans SQLite. Son équipe reçoit environ 40 questions par semaine. La moitié vient de personnes qui collent un nom exact tiré d’une erreur ou d’un journal (log) : SQLITE_BUSY, PRAGMA journal_size_limit, .once. L’autre moitié décrit un problème dans ses propres mots : « J’ai supprimé la moitié des lignes et le fichier n’a pas diminué. »

Felix, un développeur de l’équipe, veut un champ de recherche dans la documentation officielle de SQLite. Plus tard, il veut un assistant qui répond avec des citations. Il a lu que « la recherche hybride gagne toujours » et veut la mettre en production. Dana demande d’abord des preuves : quelle méthode trouve la bonne section pour les deux types de questions, et à quel coût? Un mauvais premier résultat fait perdre quelques minutes à son équipe. Une réponse assurée tirée de la mauvaise page coûte plus cher, car un client peut agir en s’y fiant.

Vous construisez une comparaison équitable de six méthodes de recherche sur un instantané fixe de la documentation, avec des questions étiquetées. Vous mesurez chaque méthode par type de question, avec sa durée et son coût, puis vous rédigez une recommandation pour Dana. À la fin, vous traitez une nouvelle question de bout en bout et examinez la réponse avec citations fournie par un modèle.

Les compétences viennent de tous les modules du cours. Le module 2 analyse avec provenance, et le module 3 compare la recherche par mots-clés, vectorielle, hybride et le reclassement. Le module 4 rédige et vérifie des réponses avec citations, et Mesurer la récupération définit les mesures. Une idée est nouvelle : comment étiqueter les passages pertinents quand vous ne pouvez pas lire toute la collection. Elle a sa propre section ci-dessous.

L’auteur a d’abord comparé deux autres possibilités. La documentation de pytest 9.1.1 (licence MIT) a une étiquette de version claire, mais ses sources Sphinx exigent une étape de compilation. La documentation de Kubernetes (CC BY 4.0) compte 1 721 pages, sans versions étiquetées, et exige une attribution à chaque réutilisation. SQLite l’a emporté pour quatre raisons. Sa documentation est dans le domaine public, elle est versionnée et elle contient de nombreux types de noms exacts. Sa taille convient après une sélection claire. Et SQLite est le moteur de la recherche par mots-clés du cours. Vous cherchez dans la documentation de l’outil que vous utilisez.

L’archive contient 224 pages au premier niveau. L’instantané en conserve 195. Il exclut 29 pages qui ne sont pas de la documentation technique : listes de liens, index de références croisées, actualités, historique des versions, pages organisationnelles et juridiques. Un fichier pages.csv indique chaque page et la raison. L’analyseur suit les règles du cours. Il ignore ce que SQLite marque comme navigation (<div class=nosearch>) et conserve chaque section avec son titre et son ancre. Ainsi, chaque segment renvoie à son emplacement exact, par exemple https://sqlite.org/pragma.html#pragma_busy_timeout.

195 pages, 2231 sections, 4300 chunks
Your chunks are identical to the shipped ones.
597,305 words; median chunk 151 words; longest 571

Ce que cela nous apprend. L’instantané est environ 90 fois plus volumineux que les 36 politiques de Larkfield (6 520 mots). Chaque segment correspond à une section, découpée à 200 mots entre les paragraphes. Certains tableaux sont beaucoup plus longs : celui des codes d’opération du moteur de code intermédiaire contient environ 16 000 mots. Ces tableaux sont découpés entre les lignes, et chaque partie répète la ligne d’en-tête du tableau. L’outil de découpage du cours ne divise jamais un tableau, mais à cette taille, il faut le faire.

Le carnet repère aussi deux pièges qui auront de l’importance plus tard :

fileformat: 106 chunks; identical text in fileformat2: 106 (100%)
The C language interface to SQLite Version 2

Ce que cela nous apprend. Deux pages, fileformat.html et fileformat2.html, contiennent exactement le même texte, comme sur le site Web. Une recherche peut donc retourner deux fois la même réponse et occuper deux des cinq places. Et c_interface.html décrit l’interface C de SQLite version 2, vieille de plus de 20 ans, dans la documentation 3.53.4. Une recherche sur « database is locked » (la base de données est verrouillée) peut la trouver, et une réponse peut la citer. Une vraie documentation conserve son propre historique.

L’unité est une question. Une méthode de recherche retourne une liste classée de segments. Les 5 premiers vont au lecteur ou au modèle, comme dans le projet du cours. Une seule version s’applique (3.53.4) et toutes les pages sont publiques. Il n’y a donc ici ni filtre de date ni filtre d’accès.

La mesure principale est hit@5 : y a-t-il au moins un segment pertinent parmi les 5 premiers? À côté figurent recall@5 et recall@10 : la proportion des passages pertinents d’une question parmi les 5 ou 10 premiers. MRR@10 est la moyenne de 1 ÷ le rang du premier segment pertinent, ou 0 s’il n’est pas parmi les 10 premiers. hit@1 indique si le premier segment est pertinent. Une personne qui utilise un champ de recherche regarde d’abord le premier résultat. hit@1 et MRR comptent donc autant que hit@5. Les tableaux donnent chaque mesure par type de question, car une moyenne cache les endroits où une méthode échoue.

Les questions sans réponse n’ont aucun passage pertinent; elles sont donc exclues de ces mesures. S’abstenir d’y répondre relève de l’étape de réponse (Traiter les preuves difficiles).

L’étude de cas comprend 53 questions, rédigées par l’auteur du cours dans le style des demandes reçues par Dana. 18 contiennent un identifiant exact; 5 demandent uniquement ce que signifie le nom, comme « Qu’est-ce que SQLITE_BUSY_SNAPSHOT? ». 15 sont des reformulations sans identifiant, et 15 demandent comment accomplir une tâche. L’instantané ne peut pas répondre aux 5 dernières. Elles portent sur un autre produit (un code d’erreur PostgreSQL, un paramètre Django), une page hors de l’instantané ou un nom inventé comme PRAGMA wal_buffer_size.

Chaque question a besoin de ses passages pertinents : les sections qui y répondent. Pour les 36 documents de Larkfield, Grace pouvait tout lire. Ici, il y a 2 231 sections, et personne ne les lit toutes pour chacune des 53 questions.

Imaginez un enseignant qui corrige un questionnaire avec un corrigé. Un élève donne une réponse correcte qui n’y figure pas. Un enseignant attentif la lit et l’ajoute au corrigé. La recherche a le même problème. L’auteur a rédigé chaque question à partir d’une section et a étiqueté les sections qu’il connaissait déjà. Mais la documentation dit souvent la même chose à deux endroits. La FAQ dit : « Vous pouvez utiliser PRAGMA integrity_check pour effectuer un test… approfondi », et la page du pragma le dit aussi. Le corrigé considérerait comme incorrecte une méthode qui trouve la FAQ.

La solution habituelle porte un nom : mise en commun (pooling). Exécutez chaque méthode, réunissez leurs premiers résultats dans un ensemble commun, puis lisez chaque segment pour le juger. Un segment est pertinent si un lecteur peut répondre à la question avec ce seul segment. L’ensemble est assez petit pour être lu. Chaque méthode a la même chance de voir ses trouvailles comptées.

Une rangée d'étapes de gauche à droite. 53 questions, chacune rédigée à partir d'une section. Étiquettes initiales : 82 sections connues de l'auteur. Exécuter les 6 méthodes. Ensemble commun : les 5 premiers résultats de chaque méthode, par question, soit 637 segments non étiquetés. Lire et juger chacun : peut-il répondre seul à la question? Deux branches : 31 passages pertinents supplémentaires à la première lecture et, dans un encadré pointillé, 9 autres à une deuxième lecture du texte intégral, après qu'une réponse a cité un segment jugé non pertinent. Les deux mènent aux étiquettes figées : 122 passages.
Comment les 53 questions ont été étiquetées. La deuxième lecture reposait sur une preuve : une réponse enregistrée citait un segment jugé non pertinent à la première lecture.
Description textuelle du diagramme

Six étapes de gauche à droite. 1 : 53 questions, chacune rédigée à partir d’une section. 2 : les étiquettes initiales, 82 sections connues de l’auteur. 3 : les six méthodes sont exécutées. 4 : l’ensemble commun, les 5 premiers résultats de chaque méthode pour chaque question. Il contient 637 segments sans étiquette initiale. 5 : l’auteur lit et juge chacun. Deux flèches partent de l’étape 5. L’une donne 31 passages pertinents supplémentaires à la première lecture. L’autre, en pointillé, en donne 9 autres à une deuxième lecture du texte intégral. 6 : les étiquettes figées, 122 passages.

La première lecture jugeait chaque segment de l’ensemble commun à partir de ses 420 premiers caractères. Elle a trouvé 31 passages pertinents supplémentaires. Puis une réponse enregistrée (ci-dessous) a cité un segment de fileformat2.html jugé « non pertinent » à la première lecture. Son texte intégral donne la réponse, « environ 281 téraoctets », après les 420 premiers caractères. Chaque segment jugé « non pertinent » dont le texte intégral contient un terme clé de la réponse de référence a donc été relu entièrement. 9 autres sont devenus pertinents. Les étiquettes ont ensuite été figées.

Énoncez clairement les limites. C’est la lecture d’une seule personne, sans deuxième juge. Une section pertinente qu’aucune méthode n’a placée dans ses 5 premiers résultats manque toujours, pour toutes les méthodes. Et les étiquettes ont changé après les premières mesures. L’ordre des méthodes aux résultats proches a changé aussi. Avec les étiquettes initiales, la recherche hybride avec e5 était légèrement en dessous d’e5 seul (hit@5 de 0,667 contre 0,688). Avec les étiquettes finales, elle est au-dessus (0,792 contre 0,729). Une différence d’une ou deux questions dépend des étiquettes. La procédure et chaque jugement se trouvent dans les fichiers de référence de l’étude de cas.

Une référence de base : la recherche par mots-clés

Section intitulée « Une référence de base : la recherche par mots-clés »

Commencez par la méthode la plus simple, comme au module 1 : SQLite FTS5 avec BM25 sur les 4 300 segments. La requête est celle du cours : chaque mot entre guillemets, relié par OR.

index built in 0.04 s
bm25 {'hit@1': 0.458, 'hit@5': 0.708, 'recall@5': 0.555, 'recall@10': 0.604, 'MRR@10': 0.553}
hit@5 by kind: {'identifier': 0.944, 'paraphrase': 0.533, 'howto': 0.6}

Ce que cela nous apprend. La recherche par mots-clés trouve 17 des 18 questions avec identifiant, mais seulement environ la moitié des reformulations et des tâches pratiques. « Comment voir si ma requête utilise un index? » partage très peu de mots avec « EXPLAIN QUERY PLAN ». BM25 classe donc la bonne section au 18e rang. Voici le résultat à dépasser : 0,708.

Recherche vectorielle avec le modèle local, e5. Le carnet charge les vecteurs e5 des 4 300 segments, créés avec le modèle du cours à sa révision fixée. Il vérifie le modèle et la révision avant toute comparaison de vecteurs. Créer les plongements de tous les segments a pris 108 secondes sur le portable de test (2 fils d’exécution). Les vecteurs sont donc fournis avec leur somme de contrôle. Définissez RECOMPUTE = True pour les créer vous-même.

e5 vectors: 4300 x 384
dense-e5 {'hit@1': 0.542, 'hit@5': 0.729, 'recall@5': 0.547, 'recall@10': 0.629, 'MRR@10': 0.621}
hit@5 by kind: {'identifier': 0.889, 'paraphrase': 0.6, 'howto': 0.667}

Ce que cela nous apprend. Sur les courtes politiques de Larkfield, e5 battait clairement BM25 (0,915 contre 0,831). Ici, il ne fait mieux que d’une question pour hit@5. Son premier résultat est toutefois correct plus souvent (hit@1 de 0,542 contre 0,458). e5 est un petit modèle multilingue, et ces textes sont longs, techniques et en anglais. Il manque -bail, une option dans une longue liste --help (aide), que BM25 classe au 2e rang.

Recherche vectorielle avec le modèle hébergé, embed-small. Ses vecteurs ont été enregistrés une fois : 828 297 jetons (tokens) pour les segments, 0,0166 $ US, environ 10 minutes avec les limites de débit du déploiement partagé. Le carnet les réutilise, donc vous n’avez besoin d’aucune clé.

dense-embed-small {'hit@1': 0.688, 'hit@5': 0.917, 'recall@5': 0.691, 'recall@10': 0.75, 'MRR@10': 0.789}
hit@5 by kind: {'identifier': 1.0, 'paraphrase': 0.867, 'howto': 0.867}

Ce que cela nous apprend. Le modèle hébergé est la meilleure méthode seule pour chaque type de question. Sur les données de Larkfield, e5 l’égalait. Un modèle « aussi bon » sur une collection peut être nettement plus faible sur une autre.

Recherche hybride et reclassement. La recherche hybride combine les 20 premiers résultats de BM25 et d’une recherche vectorielle par fusion par rang réciproque. Le reclassement suit le réglage par défaut du cours : le cross-encoder relit les 20 premiers résultats de la recherche hybride avec e5.

hybrid-e5 {'hit@1': 0.604, 'hit@5': 0.792, 'recall@5': 0.58, 'recall@10': 0.724, 'MRR@10': 0.695}
hybrid-embed-small {'hit@1': 0.667, 'hit@5': 0.833, 'recall@5': 0.625, 'recall@10': 0.789, 'MRR@10': 0.752}
rerank {'hit@1': 0.625, 'hit@5': 0.875, 'recall@5': 0.69, 'recall@10': 0.751, 'MRR@10': 0.723}
rerank: 789 ms per question on this computer

Ce que cela nous apprend. Ici, la recherche hybride avec e5 aide : 0,729 → 0,792. BM25 et e5 échouent sur des questions différentes, donc leurs votes se complètent. Avec embed-small, la recherche hybride nuit : 0,917 → 0,833, car la liste plus faible de BM25 fait descendre les bons résultats. Larkfield montrait cet effet avec e5; ici, il apparaît avec l’autre modèle. Le reclassement ajoute quatre questions à la recherche hybride avec e5 (six gagnées, deux perdues), pour environ 0,8 seconde par question.

Les six méthodes ont utilisé les mêmes segments et les mêmes 48 questions évaluées. Elles partagent les étiquettes figées et les mesures.

  • Mots-clés
  • Modèles locaux (e5, encodeur croisé)
  • embed-small hébergé (enregistré)
La proportion de questions avec un segment pertinent parmi les 5 premiers (Hyb = hybride, e-s = embed-small). Le modèle hébergé seul arrive en tête; parmi les méthodes locales, la recherche hybride avec reclassement arrive en tête.
Description textuelle du graphique

Un graphique à colonnes du hit@5 de six méthodes, sur 48 questions. Recherche par mots-clés, BM25 : 0.708. Modèles locaux : e5 seul 0.729, hybride avec e5 0.792, hybride e5 avec reclassement 0.875. Modèle hébergé : embed-small seul 0.917, hybride avec embed-small (e-s) 0.833. L’axe va de 0 à 1.

Par type de question, le portrait est plus clair :

hit@5 Identifiant (18) Paraphrase (15) Marche à suivre (15) MRR@10, toutes
Mots-clés (BM25) 0.944 0.533 0.600 0.553
Vectorielle, e5 0.889 0.600 0.667 0.621
Hybride, e5 0.889 0.733 0.733 0.695
Hybride e5 + reclassement 1.000 0.800 0.800 0.723
Vectorielle, embed-small 1.000 0.867 0.867 0.789
Hybride, embed-small 0.944 0.800 0.733 0.752

Chaque méthode trouve les 5 identifiants seuls (« Qu’est-ce que SQLITE_BUSY_SNAPSHOT? ») au premier rang. Dans le jeu de Larkfield, la recherche vectorielle échouait sur « Qu’est-ce que LB-1? ». Pourquoi cette différence? Les noms de SQLite sont longs et composés de mots (BUSY, SNAPSHOT, cacheflush). Chaque section nomme aussi son sujet dans son titre. Un code court comme LB-1 n’a aucun sens pour un modèle de plongement. Les identifiants seuls ne sont pas difficiles en général; les codes courts sans signification le sont.

Les vraies différences concernent les paraphrases et les questions sur une marche à suivre. La recherche par mots-clés en trouve environ la moitié. Le modèle hébergé en trouve 26 sur 30.

  • embed-small (hébergé, pointillés)
  • Hybride e5 + reclassement
  • e5 seul (pointillés)
  • BM25
La proportion de tous les passages pertinents trouvés parmi les k premiers. Le reclassement et embed-small sont proches dès k = 3, et leurs courbes se superposent dès k = 5. Aucune méthode ne trouve 80 % des passages, même à k = 10.
Description textuelle du graphique

Un graphique à courbes du recall@k pour k = 1, 3, 5 et 10. embed-small : 0.408, 0.628, 0.691, 0.750. Hybride e5 avec reclassement : 0.370, 0.603, 0.690, 0.751. e5 seul : 0.333, 0.503, 0.547, 0.629. BM25 : 0.312, 0.465, 0.555, 0.604.

Le rappel reste sous 0,8 même à k = 10 pour chaque méthode. Plusieurs questions ont trois ou quatre sections pertinentes sur des pages différentes. L’utilisateur a besoin d’une bonne section; hit@5 est donc la mesure de décision ici. Un assistant qui doit montrer à la fois la FAQ et la page du pragma aurait besoin de plus de 5 segments.

Avec 48 questions, une question représente 0,021 de hit@5. Comptez donc les questions qu’une seule des deux méthodes trouve. Tirez ensuite 10 000 échantillons par bootstrap (rééchantillonnage) des questions pour obtenir un intervalle à 95 % de la différence :

dense-embed-small vs rerank: only dense-embed-small ['D26', 'D27', 'D39', 'D41', 'D44']; only rerank ['D30', 'D40', 'D45']; 95% interval of the hit@5 difference [-0.062, +0.146]
rerank vs hybrid-e5: only rerank ['D04', 'D18', 'D29', 'D32', 'D40', 'D43']; only hybrid-e5 ['D19', 'D41']; 95% interval of the hit@5 difference [-0.021, +0.208]
rerank vs bm25: only rerank ['D18', 'D22', 'D23', 'D32', 'D33', 'D35', 'D43', 'D45', 'D48']; only bm25 ['D41']; 95% interval of the hit@5 difference [+0.042, +0.292]

Ce que cela nous dit. Le modèle hébergé bat le modèle de reclassement sur 5 questions et perd sur 3. Ce n’est pas une preuve : l’intervalle va de −0,062 à +0,146. Une seule comparaison est claire ici : le reclassement contre la recherche par mots-clés seule, 9 questions contre 1. Les données n’appuient pas le « l’hybride gagne toujours » de Felix. Et « embed-small est meilleur » est probable, mais 48 questions ne le prouvent pas.

La question « Comment vérifier si un fichier de base de données est corrompu? » est facile pour une personne : PRAGMA integrity_check. Regardez les 5 premiers résultats après reclassement :

1 5.589 howtocorrupt | Mispairing database files and hot journals
2 4.241 howtocorrupt | Carrying an open database connection across a fork()
3 4.074 howtocorrupt | False corruption reports due to database shrinkage
4 3.611 howtocorrupt | File locking problems
5 3.479 howtocorrupt | Memory corruption
first relevant rank: {'bm25': 5, 'dense-e5': 7, 'dense-embed-small': 1, 'hybrid-e5': 1, 'hybrid-embed-small': 1, 'rerank': 8}
La recherche par mots-clés classe la réponse de la FAQ au 5e rang, et la recherche vectorielle au 7e. Les deux flèches mènent à la recherche hybride : 1/65 + 1/67 = 0,0303, rang 1. L'hybride mène au reclassement, qui lit ensemble la question et le segment. Deux résultats : les 5 premiers sont cinq sections de How To Corrupt An SQLite Database File, avec des scores de 5,59 à 3,48; dans un cadre en pointillés, la réponse de la FAQ « Vous pouvez utiliser PRAGMA integrity_check » obtient 2,55 et tombe au 8e rang, hors des 5 premiers.
La vraie question D41. La fusion place le bon segment en premier; le modèle de reclassement, un second modèle, l'exclut des 5 premiers.
Description textuelle du diagramme

La recherche par mots-clés classe le segment de la FAQ contenant la réponse au 5e rang, et la recherche vectorielle au 7e. La recherche hybride additionne 1/65 + 1/67 = 0,0303 et le classe premier, car les deux listes le favorisaient. L’encodeur croisé relit ensuite les 20 candidats. Cinq sections de la page How To Corrupt An SQLite Database File obtiennent des scores de 5,59 à 3,48. La réponse de la FAQ obtient 2,55. Elle tombe au 8e rang, hors des 5 premiers.

La page How To Corrupt An SQLite Database File contient le plus de mots sur la corruption. Elle semble donc la plus pertinente pour chaque modèle qui lit les mots. La section du pragma dit « vérification de cohérence », pas « corruption ». Ici, la fusion a trouvé la bonne réponse et le modèle de reclassement l’a retirée. Avec les 5 premiers résultats reclassés, la réponse enregistrée s’est abstenue. Elle disait : « Les passages de la documentation ne disent pas comment vérifier si un fichier de base de données est corrompu. » C’est prudent, mais la documentation le dit.

Les autres échecs suivent le même schéma. Quand les mots de l’utilisateur diffèrent de ceux de la documentation, la recherche par mots-clés échoue. Le petit modèle e5 échoue souvent aussi. « Une colonne peut-elle se remplir toute seule avec l’heure d’ajout de la ligne? » signifie DEFAULT CURRENT_TIMESTAMP. Seul le modèle hébergé le trouve parmi ses 5 premiers résultats.

Changez une seule chose. Le reclassement peut seulement réordonner ce que la première étape a trouvé. Donnez-lui donc plus : 40 candidats plutôt que 20, sur les 30 questions de paraphrase et de marche à suivre (tâche 3 du carnet) :

40 candidates: 0.733 (1367 ms per question)
20 candidates: 0.8 (789 ms per question)

Plus de candidats ont empiré les résultats, en prenant presque deux fois plus de temps. Les candidats supplémentaires ont donné à l’encodeur croisé plus de mauvais segments à faire monter. « Plus » est un réglage à mesurer, pas un gain gratuit.

Méthode Indexer les 4 300 segments Par question (médiane, ordinateur portable) Argent Les questions quittent l’ordinateur
BM25 32.7 ms 3.93 ms 0 Non
e5 108 s (2 fils d’exécution du processeur) 6.65 ms, plus 1.06 ms pour chercher 0 (un téléchargement de 470 Mo) Non
Hybride e5 les deux ci-dessus environ 10 ms 0 Non
Hybride e5 + reclassement les deux ci-dessus 790 ms 0 (un second modèle de 470 Mo) Non
embed-small 0,0166 $ US, environ 10 minutes 180–770 ms par appel (8 appels, enregistrés) environ 0,0000003 $ US par question Oui, chaque question est envoyée au fournisseur

Chercher parmi 4 300 vecteurs avec NumPy prend environ 1 ms. Cette collection n’a donc pas encore besoin de base de données vectorielle. L’étape lente est le reclassement : environ 0,7 à 0,8 seconde par question sur le processeur d’un ordinateur portable. Le modèle hébergé ne coûte presque rien en argent ici. Ses vrais coûts sont un appel réseau par question, un compte et une clé. Chaque question d’utilisateur quitte aussi l’entreprise.

Le slogan de Felix ne tient pas. Sur cette collection, la recherche hybride aide un modèle de plongement et nuit à l’autre. Voici la recommandation pour l’équipe de Dana :

  1. Si l’équipe peut envoyer les questions de ses utilisateurs à un fournisseur externe, utilisez embed-small seul. Il offre le meilleur hit@5 (0,917), le meilleur premier résultat (hit@1 de 0,688) et le meilleur MRR (0,789), sans reclassement. Vérifiez d’abord les conditions du fournisseur : la documentation est publique, mais les questions peuvent contenir des noms de clients ou des journaux d’erreurs.
  2. Si les questions doivent rester sur les ordinateurs de l’entreprise, utilisez l’hybride e5 avec reclassement (0,875). Acceptez environ 0,8 seconde par question. Si c’est trop lent pour un champ de recherche, l’hybride e5 sans reclassement (0,792) répond en environ 10 ms.
  3. Ne choisissez pas entre les options 1 et 2 selon ces chiffres seulement. La différence est de 5 questions contre 3. Ajoutez les 50 prochaines vraies questions de l’équipe, étiquetez-les de la même façon et mesurez à nouveau.

Quelle que soit la méthode, gardez la recherche par mots-clés accessible pour les noms exacts. Elle est rapide et gratuite, et elle trouve 17 questions d’identifiant sur 18.

De bout en bout : une nouvelle question avec une réponse citée

Section intitulée « De bout en bout : une nouvelle question avec une réponse citée »

Voici une nouvelle question de soutien, absente du jeu : « Une tâche cron nocturne et mon application web écrivent dans le même fichier SQLite. L’application échoue parfois avec “database is locked” (la base de données est verrouillée). Que puis-je changer? » Le carnet exécute le pipeline local de reclassement sur cette question. Il rejoue ensuite la réponse que chat-small (gpt-6-luna sur Azure) a donnée aux mêmes cinq passages. La requête (prompt) suit Répondre et citer, avec « Documentation SQLite, version 3.53.4 » à la place des politiques de Larkfield.

65ef89eb3990 https://sqlite.org/rtree.html#reading_and_writing_at_the_same_time | The SQLite R*Tree Module
398ca44b61b8 https://sqlite.org/c_interface.html#4_error_codes | The C language interface to SQLite Version 2
ed0c02ce8939 https://sqlite.org/lockingv3.html#writing | File Locking And Concurrency In SQLite Version 3
db447e805ee1 https://sqlite.org/tclsqlite.html#the_timeout_method | The Tcl interface to the SQLite library
9c8a6b3b7716 https://sqlite.org/c_interface.html#4_error_codes | The C language interface to SQLite Version 2
chat-small (gpt-6-luna on Azure, recorded): SQLite allows only one writer at a time, so coordinate the cron job and app to avoid overlapping writes. If your app uses the Tcl interface, increase its lock wait with the `timeout` method; the default is 0 milliseconds. [398ca44b61b8] [db447e805ee1]
- SQLite permits only one process or thread to have the database open for writing at a time, and the lock applies to the entire database. ['398ca44b61b8'] problems: none
- The Tcl `timeout` method controls how long SQLite waits for locks; its default is 0 milliseconds, and it can be set to a positive value. ['db447e805ee1'] problems: none
L'affirmation 1, un seul processus écrit à la fois avec un verrou sur toute la base de données, pointe vers c_interface.html, section 4, The C language interface to SQLite Version 2. L'affirmation 2, la méthode timeout de Tcl définit le temps d'attente avec une valeur par défaut de 0 ms, pointe vers tclsqlite.html, l'interface Tcl seulement. Les deux passages mènent à un cadre : vérifications du code, cité, dans le contexte, mots trouvés, les deux affirmations passent. Un cadre en pointillés reste à part : absents des 5 premiers, PRAGMA busy_timeout et sqlite3_busy_timeout().
Une vraie réponse enregistrée. Chaque affirmation cite un passage qui l'appuie, mais la réponse n'est toujours pas celle dont un développeur a besoin.
Description textuelle du diagramme

Deux affirmations, chacune reliée à son passage. L’affirmation 1, un seul processus ou fil d’exécution peut écrire à la fois, cite la page sur l’interface C de SQLite version 2. L’affirmation 2, sur la méthode timeout de Tcl, cite la page de l’interface Tcl. Les vérifications du code passent pour les deux. À part, dans un cadre en pointillés : la réponse dont un développeur a besoin, PRAGMA busy_timeout ou sqlite3_busy_timeout(), n’était pas parmi les 5 premiers.

Chaque affirmation passe les vérifications du code, et la réponse est mauvaise. Une source concerne la version 2, et l’autre, l’interface Tcl, que l’utilisateur n’a jamais mentionnée. Le bon conseil, un délai d’attente en cas de verrouillage, n’était pas parmi les 5 premiers. Le modèle ne pouvait donc pas le donner. C’est la leçon de Répondre et citer sur de nouvelles données : une citation montre d’où vient une affirmation. Elle ne montre pas que la source convient à cette version et à cet utilisateur.

Les autres réponses enregistrées se comportent comme prévu. Avec un segment pertinent parmi les 5 premiers, chat-small répondait correctement. Par exemple : « L’option -bail fait arrêter l’interpréteur de commandes après une erreur » et la limite de 281 téraoctets. Sans segment pertinent, il s’abstenait, sur trois questions auxquelles on pouvait répondre et sur les deux sans réponse de l’enregistrement. Encore une fois, une meilleure récupération améliorerait le plus ces réponses. Les 9 réponses enregistrées ont coûté 0,0020 $ US au total.

Erreur courante

Un seuil de score pour détecter les questions sans réponse

Symptôme : Felix propose un raccourci : « Si le meilleur score du modèle de reclassement est bas, dites que la documentation ne couvre pas le sujet. » Pour « Quelle est la valeur par défaut de PRAGMA wal_buffer_size? », un pragma inexistant, le meilleur score est 6,12.

Cause : le nom inventé ressemble à de vrais pragmas (wal_autocheckpoint, journal_size_limit). L’encodeur croisé leur donne donc un score élevé. La moitié des vraies questions auxquelles on peut répondre (24 sur 48) ont un meilleur score inférieur à 6,12. Aucun seuil ne sépare les deux groupes.

Correction : laissez l’étape de réponse décider de s’abstenir. Elle lit les passages et doit dire « les passages de la documentation ne disent pas » quand ils ne répondent pas (Traiter les preuves difficiles). chat-small s’est abstenu sur cette question.

Pour un vrai service d’IA : testez l’abstention avec des questions sur des noms inventés et d’autres produits, comme dans ce jeu. Les scores classent les segments pour une question; ils ne permettent pas de comparer les questions entre elles.

  • La meilleure méthode dépend de la collection et du modèle de plongement. Sur les politiques de Larkfield, la recherche hybride avec e5 nuisait aux résultats. Ici, elle aide, et la recherche hybride avec embed-small nuit. Mesurez sur vos propres questions.
  • Les noms exacts sont faciles quand ils sont longs et composés de mots, et que chaque section nomme son sujet dans un titre. Les paraphrases et les questions sur une marche à suivre déterminent la comparaison.
  • Un second modèle n’est pas toujours un meilleur juge : le reclassement a retiré le bon segment pour D41. Et 40 candidats ont donné de moins bons résultats que 20.
  • La documentation elle-même contient des pièges : des pages identiques, une page de la version 2 et des sections aux titres en gras que l’analyseur ne voit pas (limits.html).

Les limites : 48 questions évaluées, rédigées et étiquetées par une seule personne; un seul outil de découpage et une seule taille de segment; un seul instantané (3.53.4). Les vecteurs hébergés ont été enregistrés une fois, et le fournisseur peut changer le modèle derrière le nom. Les temps proviennent d’un seul ordinateur portable avec 2 fils d’exécution.

  • Citez la version. Chaque réponse devrait nommer SQLite 3.53.4 et fournir un lien vers la section exacte. La documentation change avec les versions : une valeur par défaut ou une limite peut changer. Les anciennes pages (version 2) restent dans les archives.
  • Ne présentez pas les réponses comme le soutien officiel du projet. Les développeurs de SQLite n’ont pas rédigé ni révisé ces réponses. Dites que l’assistant cherche dans une copie de la documentation du domaine public, et liez sqlite.org.
  • Gardez les questions des utilisateurs privées. La documentation est publique, mais les questions peuvent contenir des données de clients et des journaux d’erreurs. Un modèle de plongement hébergé envoie chaque question au fournisseur.
  • Gardez une personne dans le processus pour les actions. Une mauvaise réponse sur la récupération, la corruption ou la suppression de données peut causer du tort. L’assistant montre les preuves; un développeur décide.

Placez le carnet dans un dépôt public avec un court README. Voici un modèle :

# Searching the SQLite documentation: keyword, vector, hybrid and reranking
## Problem
A support team needs the right section of the SQLite 3.53.4 documentation,
both for exact names (SQLITE_BUSY, PRAGMA journal_size_limit)
and for questions in plain words.
## Data and licence
195 pages of the official SQLite 3.53.4 documentation (public domain),
4,300 chunks with page, section and URL. 53 questions (identifier,
paraphrase, how-to, unanswerable), labelled by pooling the top 5 of
six methods and reading 637 chunks.
## Approach
BM25 (SQLite FTS5), e5 (local), embed-small (hosted, recorded),
hybrid with RRF, cross-encoder reranking. hit@5, recall@k, MRR@10 and
hit@1 by kind of question; paired counts and a bootstrap interval.
## Results
hit@5: BM25 0.708, e5 0.729, hybrid e5 0.792, rerank 0.875,
embed-small 0.917. Hybrid helps e5 and hurts embed-small.
Only rerank vs BM25 is a clear difference on 48 questions.
## Limits
One labeller, 48 scored questions, one snapshot. Not SQLite's official support.

En entrevue, dites-le en quelques phrases :

J’ai comparé six méthodes de recherche dans la documentation SQLite, avec 53 questions que j’ai étiquetées en regroupant les résultats. Le modèle de plongement hébergé en a trouvé le plus. Mais sur 48 questions, une seule différence était claire. J’ai donc recommandé de mesurer à nouveau avec de vraies questions d’utilisateurs.

Allez plus loin : ajoutez 50 questions d’un vrai forum et étiquetez-les de la même façon. Essayez une seconde taille de segment avec une taille de contexte égale, comme dans Découper de façon réfléchie. Retirez les pages fileformat2 en double et mesurez le changement. Étiquetez chaque page avec la version qu’elle décrit, et excluez des résultats les pages sur la version 2.

Vérifiez votre compréhension

Sur les politiques de Larkfield, la recherche hybride avec e5 obtenait un score inférieur à e5 seul. Ici, elle fait mieux. Expliquez pourquoi les deux résultats peuvent être vrais et ce que Felix devrait conclure.

Afficher la réponse

La recherche hybride additionne les votes de deux listes. Elle aide quand les deux méthodes échouent sur des questions différentes et que les deux listes sont raisonnables. C’est le cas ici : BM25 trouve des noms exacts qu’e5 manque (-bail), et e5 trouve des paraphrases. Elle nuit quand une liste est faible pour beaucoup de questions et que ses mauvais classements font descendre les bons résultats. C’est arrivé avec les questions en français du jeu de Larkfield, et avec embed-small ici (0,917 → 0,833). Felix devrait conclure qu’aucune méthode ne gagne partout et mesurer les résultats sur ses propres questions avant la mise en production.

Vérifiez votre compréhension

Le modèle hébergé obtient un hit@5 de 0,917 et le modèle de reclassement, de 0,875. Dana demande si cela prouve que le modèle hébergé est meilleur. Que répondez-vous?

Afficher la réponse

Pas sur 48 questions. La différence nette est de 2 questions. Le modèle hébergé trouve 5 questions que le modèle de reclassement manque, et ce dernier en trouve 3 que l’autre manque. Un intervalle obtenu par bootstrap pour la différence va de −0,062 à +0,146; il inclut donc 0. La décision devrait aussi tenir compte des coûts. Le modèle hébergé envoie chaque question à un fournisseur, et le modèle de reclassement prend environ 0,8 seconde par question. Ajoutez d’autres questions réelles, étiquetez-les de la même façon et mesurez de nouveau.

Vérifiez votre compréhension

La réponse enregistrée à la question « database is locked » (la base de données est verrouillée) réussit toutes les vérifications des citations. Nommez deux raisons pour lesquelles elle reste une mauvaise réponse, et un changement qui corrigerait la cause.

Afficher la réponse

Premièrement, une de ses sources décrit SQLite version 2, et non 3.53.4. Deuxièmement, ses conseils concernent l’interface Tcl, que l’utilisateur n’a jamais mentionnée. La cause est la récupération. Les sections sur PRAGMA busy_timeout et sqlite3_busy_timeout() ne figuraient pas parmi les 5 premiers résultats. Le modèle ne pouvait donc pas les utiliser. Une solution : étiquetez chaque page avec la version qu’elle décrit et excluez les pages de la version 2 des résultats. Une autre : comparez les méthodes sur ce type de question. Pour la question similaire D19 du jeu, la recherche hybride e5 classe une section pertinente au 2e rang. Le modèle de reclassement la place au 14e rang. Dans tous les cas, affichez la page et la version avec chaque affirmation pour qu’une personne puisse la juger.

Récapitulatif

  • Sur 195 pages de documentation SQLite et 48 questions évaluées, le modèle de plongement hébergé seul a trouvé le plus de réponses (hit@5 de 0,917). La meilleure méthode locale était la recherche hybride e5 avec reclassement (0,875). La recherche par mots-clés seule a obtenu 0,708.
  • La recherche hybride a aidé e5 et nui à embed-small. La plupart des écarts entre les méthodes fortes étaient de 1 à 3 questions : ce n’est pas une preuve.
  • Regrouper les meilleurs résultats de chaque méthode et les lire a permis de trouver 40 passages pertinents manqués par les premières étiquettes.
  • Une réponse avec citations peut réussir toutes les vérifications et quand même provenir de la mauvaise version ou interface. Citez la version et ne présentez pas les réponses comme du soutien officiel.

Ensuite, la dernière étude de cas ajoute la dimension du temps : répondre à partir de règlements qui changent. Il faut citer la version en vigueur à une date donnée.

Leçon suivante : Répondre à partir de règlements versionnés