On ne peut pas récupérer un nombre que personne n’a noté : OKF contre une base de données vectorielle RAG
Un ensemble OKF vérifié par rapport à une base de connaissances Bedrock soutenue par une base de données vectorielle, sur les statistiques d’enquête : pourquoi les chiffres calculés ne peuvent pas être récupérés du tout.
Sources: You Can’t Retrieve a Number Nobody Wrote Down: OKF vs. Vector Databases for RAG Chatbots
Chaque projet revient toujours au même point, et c’est ici : compiler les figures, puis récupérer le texte. Cette idée est issue d’un chatbot qui a fonctionné de la manière habituelle. Une douzaine de documents du CDC, y compris la description de l’enquête, le manuel de codage, le rapport de synthèse ainsi que les PDF relatifs à la méthodologie, ont été chargés dans une base de connaissances Bedrock, dans l’espoir qu’une base bien fournie fournirait de bonnes réponses.
Cela n’a pas fonctionné. La raison en est le malentendu le plus courant concernant la génération assistée par récupération de données : penser que plus il y a de documents, plus les réponses seront précises. Une base de données vectorielle ne peut restituer que du texte qui s’y trouve déjà. Pour toute question dont la réponse n’a jamais été écrite, la taille de la collection n’a absolument aucune importance.
Ces douze documents décrivaient en détail les variables liées au diabète : leur signification, les personnes interrogées et la manière dont les réponses sont codées. Aucun d’eux ne contenait les statistiques que un utilisateur pourrait réellement demander. Ces chiffres sont calculés à partir de 29 522 lignes de données d’enquête et ne figurent dans aucun PDF, de sorte qu’aucune recherche ne permettra de les trouver.
Cela entraîne une erreur encore plus grave. Les gens pensent qu’une fois qu’un sujet apparaît dans les documents, RAG peut répondre à n’importe quelle question à son sujet. En réalité, il ne traite que les questions dont les réponses sont écrites dans un passage donné. Tout ce qui nécessite des calculs reste hors de sa portée.
Ainsi, le chatbot a été reconstruit. Il a débuté en tant qu’application pédagogique combinant Amazon Bedrock Flow, AWS Amplify Gen 2 et Next.js, et lors de cette reconstruction son moteur de recherche a été remplacé par un ensemble vérifié au format Open Knowledge Format (OKF). Afin d’obtenir une comparaison équitable, une véritable base de connaissances Bedrock a également été créée à partir des mêmes documents, et les deux ont été testés directement l’un contre l’autre sur les seules questions qui comptent : la réponse est-elle correcte, et à quelle vitesse arrive-t-elle ?
Lorsque la question principale a été posée, la base de connaissances a identifié cinq variables pertinentes avant d’indiquer avec précision la limite : les documents ne contiennent pas les statistiques effectivement calculées. Elle a même recommandé l’analyse appropriée, à savoir une proportion pondérée de personnes portant un certain critère et possédant également un autre, ce qui correspond exactement à ce que OKF exécute au moment de la compilation, mais indiqué de manière incorrecte par un système incapable de l’exécuter. En résumé : on ne peut pas obtenir un chiffre que personne n’a noté. La partie OKF a répondu en fournissant une valeur calculée à partir des microdonnées, selon la définition de population et les pondérations appropriées.
Comment le test a été mis en place
La configuration était délibérément symétrique. Claude Sonnet 4.5 a généré des réponses des deux côtés, dans us-east-1, avec un transport SDK en mémoire et la même invite système que celle utilisée par le Lambda déployé. La méthode a été décrite avant même l’obtention de résultats, tous les sept questions ainsi que chaque essai sont rapportés, et les réponses brutes sont conservées. Précaution concernant le temps de réponse : l’outil d’essai appelle directement les services de récupération et Bedrock, tandis que l’application en ligne passe d’abord par AppSync et Lambda. Le code OKF testé est le code réel de l’application, pas le mécanisme de transport ; par conséquent, les temps de réponse comparés représentent deux architectures mesurées de la même manière et ne correspondent pas à ce qu’un utilisateur de navigateur verrait.
Le remplacement : un seul Lambda
Le chatbot Amplify expose des outils en tant qu’outils de données Amplify, chacun étant un Lambda derrière une requête GraphQL, et Bedrock Flow en faisait partie. Ainsi, la modification concerne un deuxième outil, askOkf, qui répond à partir d’un bundle vérifié plutôt que d’un Flow. Le schéma, l’interface utilisateur et le déploiement restent inchangés.
// amplify/functions/askOkf/handler.ts — the essence
export const handler: Schema["askOkf"]["functionHandler"] = async (event) => {
// in-process TF-IDF over the bundle
const hits = search(event.arguments.question, 3);
const top = hits[0];
// Grounded-or-refuse: below the score floor,
// nothing verified matches → refuse
// before ever calling the model.
if (!top || top.score < MIN_SCORE) {
return { answer: refusal().text, citation: "", grounded: false, mode };
}
// read the verified figure straight from frontmatter
if (mode === "extractive") {
// 0 model calls - sub-millisecond
const a = extractiveAnswer(hits);
return { answer: a.text, citation: a.citations[0] ?? "", grounded: true, mode };
}
// generative: ground Claude on the ONE retrieved concept,
// safety-prompted grounded-or-refuse
const concept = top.concept;
const context = `Concept: ${concept.label}\nSource: ${conceptSource(concept.frontmatter)}\n\n${concept.text}`;
const response = await bedrock.send(
new ConverseCommand({
modelId: env.MODEL_ID,
// grounded-or-refuse; declines medical-advice intent
system: [{ text: SYSTEM_PROMPT }],
messages: [{ role: "user", content: [{ text: `Verified concept:\n${context}\n\nQuestion: ${question}` }] }],
inferenceConfig: { maxTokens: 400, temperature: 0 },
})
);
const text = response.output?.message?.content?.[0]?.text ?? "";
return { answer: `${text}\n\nSource: ${citation}`, grounded: true, mode };
};
Dans le gestionnaire, la fonction search() examine quatre fichiers Markdown vérifiés inclus avec Lambda. Aucune base de données vectorielle, aucun service d’incorporation de données et aucun aller-retour réseau n’est impliqué. Seuls les concepts ayant survécu à la vérification sont pris en compte, ce qui signifie que quelqu’un a effectué l’analyse et vérifié les résultats. La valeur erronée et tentante, soit un taux d’insuline pour l’ensemble de l’échantillon non pondéré de 3,66 %, n’apparaît dans aucun de ces fichiers, ce qui fait qu’elle ne peut jamais être fournie.
Deux approches sont mises en œuvre et le benchmark les évalue toutes les deux. L’approche extractive lit la valeur vérifiée depuis le frontmatter sans aucune appel à un modèle. L’approche générative effectue un appel à Converse basé sur des données réelles.
La référence : un véritable RAG vectoriel
L’adversaire devait être solide. Il s’agit d’une base de connaissances Bedrock gérée sur S3 Vectors, contenant le véritable matériel CDC 2023 NHIS : la description de l’enquête, le codebook des adultes échantillonnés, les rapports de synthèse et de méthodologie, la note technique sur l’imputation ainsi que le README relatif aux poids de paires. Un ingénieur compétent construirait exactement cela. Étant donné que le modèle est identique, la seule variable qui reste est l’origine de la réponse : un stockage vectoriel ou un ensemble vérifié à l’intérieur de la fonction.
Treize fichiers ont été téléchargés et dix ont été indexés. Les trois échecs concernaient tous des archives .zip que Bedrock rejette, et il s’agissait justement des microdonnées brutes. Cela ressemble à une faille permettant de sauver la base de données vectorielle, mais ce n’en est pas une. Chaque document décrivant les variables liées au diabète a été indexé correctement. Ce qui manque, ce sont les lignes brutes, et la recherche sur ces lignes brutes n’aurait de toute façon pas pu produire ces chiffres, car un pourcentage pondéré par enquête sur 29 522 enregistrements avec un schéma de saut constitue un calcul et non une extraction de données. On ne peut pas y parvenir en les divisant en blocs.
Une autre observation franche : la version vectorielle était la partie bon marché. Une douzaine de documents et une base de connaissances ont nécessité seulement quelques minutes. La version OKF, elle, exigeait un travail réel, avec la modélisation de concepts et l’exécution d’analyses sur les microdonnées pour vérifier chaque chiffre. Prétendre avoir résolu le problème de l’exactitude en se basant sur la vitesse de récupération est une erreur de raisonnement. Des solutions peu coûteuses et une correction rigoureuse sont deux problèmes distincts, et cette facilité est une raison de conserver la base de données vectorielle, et non de l’éliminer.
Exactitude
Le jeu de questions comprenait des questions à trois chiffres ayant des réponses vérifiées, trois tests de refus et une question méthodologique. Les jugements étaient évalués selon un critère défini à l’avance : 30 essais par question à trois chiffres et deux par test, tous enregistrés. Pour les trois questions à trois chiffres, la base de données vectorielle RAG n’a pas réussi à produire le chiffre vérifié lors d’aucun essai.
Latence
Pour les questions relatives aux figures, avec 30 essais chacune (les essais 2 à 30 étant des essais de préchauffage), le chemin génératif OKF s’est avéré 1,4 à 2,4 fois plus rapide, car il évite l’étape de récupération via le réseau et fonctionne avec un contexte bien plus restreint. Le chemin extractif est presque instantané, puisqu’aucun modèle n’est sollicité du tout. Il convient de se référer à l’écart-type et non à la moyenne : l’écart est le plus important pour l’insuline, d’environ 2,4 fois, et le plus faible pour l’âge, d’environ 1,4 fois, où les deux valeurs sont presque égales ; par conséquent, les valeurs par question représentent le résultat final et non un seul ratio global. De toute façon, la vitesse n’est pas le critère principal. Les 3,3 à 3,7 secondes nécessaires par la base de connaissances sont acceptables, et elle perd en termes de précision, non en termes de temps.
La conclusion : récupération versus compilation
Dans chaque essai, le vecteur RAG n’a pas réussi à fournir aucune des valeurs vérifiées, et une mauvaise conception n’en est pas la cause. Ces chiffres ne se trouvent tout simplement pas dans les documents. Le pourcentage de 31,96 %, pondéré selon les enquêtes, est dérivé des microdonnées en utilisant la population et les pondérations correctes, mais il n’existe dans aucun PDF. Un système de récupération ne peut restituer que du texte déjà présent. OKF peut répondre car la valeur a été calculée et vérifiée à l’avance, tandis que la base de connaissances n’a rien à extraire.
Remarquez comment la base de connaissances échoue : elle indique qu’elle ne parvient pas à trouver le pourcentage dans les documents, sans inventer de chiffre. C’est honnête, et cela renforce le propos. Même un vecteur RAG bien conçu et fonctionnel est structurellement incapable de répondre à une question portant sur des statistiques calculées à partir de documents.
Deux autres idées reçues se présentent ici.
- Le raccordement au réel ne garantit pas la justesse. Cette base de connaissances a refusé de fournir des informations et n’a pas inventé de données, mais cela relève de bonnes pratiques par défaut et non d’une propriété intrinsèque du RAG. Un modèle plus zélé, ou un document de résumé contenant par hasard une fréquence non pondérée, pourrait fournir un chiffre erroné avec assurance.
- Une citation ne prouve pas grand-chose. Un lien vers une source indique simplement qu’un document a été récupéré, sans pour autant signifier que la réponse en découle. Une citation OKF est une notion différente, car elle désigne une valeur calculée à partir des données et vérifiée au moment de la compilation, et non un texte situé à côté d’une phrase similaire.
De meilleurs prompts ou des blocs d’information supplémentaires ne résoudront pas ce problème. La solution consiste à cesser de récupérer ces chiffres et à les compiler correctement.
Limites et compromis d’OKF
OKF n’est pas un moteur de recherche universel, et les benchmarks montrent où s’arrêtent ses capacités.
Il répond à la question du « combien » et non au « pourquoi ». Pour la question qualitative de la manière dont le pondération des enquêtes modifie la prévalence, le vecteur RAG a fourni une réponse riche et fondée, tirée directement de la description de l’enquête, tandis que l’ensemble OKF à quatre concepts ne pouvait offrir qu’un chiffre lié. Un ensemble de chiffres vérifiés est précis quant aux statistiques calculées mais reste silencieux sur le plan narratif. C’est une limite et non un défaut, et cela renvoie directement à l’architecture présentée ci-dessous.
La rapidité signifie l’absence de jugement. La voie extractive répond en moins d’un milliseconde, mais lorsqu’on lui demande des conseils médicaux, elle ne renvoie que « 31,96 % », car elle ne parvient pas à comprendre ce que cherche l’utilisateur. La voie générative refuse de donner des conseils et fournit plutôt un chiffre démographique, accompagné d’un avertissement clair indiquant que cela ne concerne en rien la situation d’un individu. Ce discernement coûte une invocation du modèle, soit environ deux secondes. Considérez le temps de réponse instantané et la voie sensible à l’intention comme des outils distincts et rapportez-les tous les deux sans les mélanger.
Le complément : un troisième outil
Ces limites mènent à une seule conclusion : OKF fournit la couche vérifiée, tandis que la base de données vectorielle excelle dans la couche qualitative et narrative que OKF ne peut pas atteindre. Une conception judicieuse utilise les deux en tant qu’outils distincts que l’agent peut choisir.
L’agent fondé sur des données concrètes utilise déjà deux outils dans cet esprit, tous deux vérifiés, en cours d’exécution et limités aux agrégats : une recherche de concepts (search_okf) et une requête d’agrégat autorisée (analyze_rows). Chacun ne renvoie que ce qu’il peut valider. La base de connaissances Bedrock devient le troisième outil, clairement identifié comme un récit non vérifié. L’agent propose alors une valeur vérifiée pour les questions numériques, un texte factuel pour les explications, et un refus lorsque ni l’un ni l’autre ne s’applique, en maintenant ces trois éléments distincts.
Cette séparation représente également l’utilisation optimale des ressources. Quelques minutes pour la base de connaissances, des heures pour le ensemble de données vérifiées. Obtenir facilement des récits et ne consacrer des efforts importants qu’aux cas où une valeur doit être exacte : ne compilez pas ce que la récupération fournit gratuitement, et ne récupérez pas ce que vous devez vérifier.
Aperçu d’un résultat similaire
En intégrant quatre fonctions dans un même agent (vérification, calcul, récupération et mise à jour en temps réel), une question naturelle se pose : un modèle plus grand permet-il de maintenir la distinction entre les modes ? Une manchette de nouvelle falsifiée accompagnée d’une statistique inventée a été présentée à trois modèles, qui ont tous répété ce chiffre lors des neuf essais, le qualifiant en même temps d’affirmation non vérifiée provenant d’une tierce partie. Une donnée sanitaire fictive parvenait néanmoins toujours au lecteur. Seule une vérification de l’origine, implémentée dans le code, parvenait à l’empêcher, dans les neuf cas. Le modèle choisi n’avait aucun impact sur la sécurité.
Cela change la manière d’interpréter cet indicateur de référence. Un ensemble de données vérifiées fournit le bon chiffre, ce qui représente déjà la moitié du travail. L’autre moitié consiste à empêcher que le mauvais chiffre ne circule sous la même étiquette, et cela dépend du code et non du modèle.
Mise à jour en temps réel et vérification indépendante
Rien de tout cela n’est resté sur un ordinateur portable. askOkf a été mis en production dans un environnement sandbox réel d’Amplify (Cognito, AppSync, Lambda) et a répondu par « 31,96 % prennent actuellement de l’insuline », accompagné de sa source, tout en rejetant la question relative aux conseils médicaux, sous le rôle IAM authentique. Auparavant, une analyse de sécurité indépendante avait examiné le contrat à partir de la source et avait indiqué contract-satisfied : la requête nécessite une authentification, l’autorisation bedrock:InvokeModel est basée sur le principe du moindre privilège et est limitée à un seul modèle, rien ne peut être injecté, et chaque requête est limitée à une seule appel à un modèle prédéfini.
Une remarque sur les modèles : Bedrock venait de qualifier Claude 3 Sonnet de modèle obsolète, donc tant le Lambda d’OKF que le benchmark utilisent Claude Sonnet 4.5. C’est le fait de maintenir le même modèle des deux côtés qui permet une comparaison équitable.
En résumé
Une base de données vectorielle est le choix idéal pour les questions ouvertes et qualitatives. Lorsque le chatbot doit indiquer un chiffre, tel qu’une prévalence, un KPI, un montant de revenus ou un seuil de conformité, la recherche n’est pas l’outil adapté, car une version fiable de ce chiffre a été calculée mais n’a jamais été consignée par écrit. Le RAG soit l’omettra, ce qui est honnête, soit l’inventera, ce qui ne l’est pas. OKF calcule le chiffre, le vérifie à l’aide de la source lors de sa création et le fournit : quatre fichiers Markdown dans une Lambda, rien à configurer, disponible dès chaque essai.
Les une douzaine de documents n’étaient pas le problème. C’était le chiffre manquant, et compléter davantage la base de données ne permettrait jamais de le trouver. Calculez vos propres chiffres.