Pourquoi j'ai abandonné la recherche vectorielle pour mon wiki
La recherche vectorielle est le défaut du RAG – mais pour mon wiki personnel, un retrieval structuré sur métadonnées et texte intégral était le meilleur choix. Là où les embeddings échouent, avec des exemples SQL et de prompts.

Quand j’ai construit mon wiki LLM à la Karpathy, le premier réflexe a été : embeddings dedans, base vectorielle à côté, RAG terminé. Quelques semaines plus tard, j’ai retiré la recherche vectorielle. Pas par principe – mais parce que pour cette base de connaissances, c’était le moins bon choix. Voici pourquoi.
Ce que fait vraiment la recherche vectorielle
La recherche vectorielle plonge le texte dans des vecteurs à haute dimension et trouve les chunks « sémantiquement les plus proches » par similarité cosinus. C’est puissant pour des requêtes non structurées et floues sur d’énormes corpus hétérogènes. Or un wiki personnel bien entretenu, c’est exactement l’inverse : structuré, curé, avec des métadonnées.
Où ça a concrètement échoué pour moi
1. Le chunking détruit la structure. Une entrée de wiki a un titre, des tags, une date, des relations. Hachée en fenêtres de 512 tokens, il ne reste qu’une bouillie de texte. La frontière entre deux sujets tombe au milieu d’un chunk.
2. Proximité cosinus ≠ pertinence. « Comment déployer X ? » ramène avec enthousiasme chaque paragraphe qui dit souvent « déployer » – y compris le périmé d’il y a un an. La proximité sémantique ne connaît ni « à jour » ni « faisant autorité ».
3. Les lookups exacts se perdent. « Montre-moi l’entrée cbks-arch de mars. » Ce n’est pas un problème de similarité, c’est une clause WHERE. Les embeddings en font un jeu de devinettes.
4. Pas de filtrage propre. « Seulement tag=infra, seulement le dernier trimestre » – laborieux en recherche vectorielle pure, une ligne en SQL.
Ce que je fais à la place : le retrieval structuré
Mon wiki est de toute façon dans PostgreSQL. J’utilise donc ce qui est déjà là – des filtres de métadonnées plus du texte intégral (à la BM25) au lieu d’embeddings :
-- Candidats via structure + texte intégral, pas par devinette cosinus
SELECT id, title, tags, updated_at,
ts_rank(search_vector, query) AS rank
FROM wiki_entries,
plainto_tsquery('french', 'deployment fly.io') AS query
WHERE search_vector @@ query
AND 'infra' = ANY(tags) -- filtre de métadonnées
AND updated_at > now() - interval '180 days' -- connaissances récentes seulement
ORDER BY rank DESC
LIMIT 5;Cela donne des résultats précis, filtrables, frais – et, au passage, les métadonnées structurées avec. Exactement la matière première que je remets ensuite au modèle comme contexte compact (le principe du billet sur le context engineering) :
// Construire un contexte compact et budgété à partir des résultats
const context = rows.map((r) => `## ${r.title} [${r.tags.join(', ')}] (${r.updated_at})\n${r.snippet}`).join('\n\n');
const prompt = `<instructions>
Réponds uniquement à partir de <contexte>. Cite le titre de l'entrée comme source.
Si l'info manque, dis-le.
</instructions>
<contexte>
${context}
</contexte>
<question>${userQuery}</question>`;Quand la recherche vectorielle gagne quand même
Ce n’est pas « les embeddings, c’est mauvais ». Ils sont le bon choix quand :
- le corpus est grand et non structuré (des milliers de PDF sans métadonnées),
- les requêtes sont purement sémantiques (« trouve tout ce qui est thématiquement proche »),
- il n’y a pas de métadonnées fiables pour filtrer.
Pour un wiki curé, rien de tout cela ne tient. Le juste milieu pragmatique est souvent hybride : préfiltrer structurellement, puis classer sémantiquement au sein des résultats. Mais la couche vectorielle coûteuse comme premier pas était tout simplement le mauvais ordre chez moi.
Conclusion
La recherche vectorielle est un défaut, pas une loi de la nature. Avant de monter des embeddings, une base vectorielle et des pipelines de chunking, la question vaut la peine : mon savoir est-il déjà structuré ? Si oui, la réponse ennuyeuse – WHERE, ts_rank, métadonnées – est souvent plus précise, moins chère et plus facile à déboguer que n’importe quelle stack d’embeddings.