obsidian:~/vault$ search --hybrid obsidian

Emplacement de l'exemple de coffre

# Obsidian comme infrastructure IA : la référence technique définitive

words: 15500 read_time: 78m updated: 2026-04-16 16:01
$ retriever search --hybrid obsidian

Points clés

L’ingénierie de contexte, pas la prise de notes. La valeur d’un coffre-fort Obsidian pour l’IA ne réside pas dans les notes elles-mêmes, mais dans la couche de récupération qui les rend interrogeables. Un coffre-fort de 16 000 fichiers sans récupération est une base de données en écriture seule. Un coffre-fort de 200 fichiers avec recherche hybride et intégration MCP constitue une base de connaissances IA. L’infrastructure de récupération est le produit. Les notes sont la matière première.

La récupération hybride surpasse la recherche par mots-clés pure ou la recherche sémantique pure. BM25 capture les identifiants exacts et les noms de fonctions. La recherche vectorielle capture les synonymes et les correspondances conceptuelles à travers différentes terminologies. Reciprocal Rank Fusion (RRF) fusionne les deux sans nécessiter de calibration des scores. Aucune méthode seule ne couvre les deux modes de défaillance. Les recherches sur le classement de passages MS MARCO confirment ce constat : la récupération hybride surpasse systématiquement chaque méthode prise isolément.3 L’analyse approfondie du récupérateur hybride couvre les mathématiques du RRF, des exemples concrets avec des chiffres réels, l’analyse des modes de défaillance et un calculateur de fusion interactif.

MCP donne aux outils IA un accès direct au coffre-fort. Les serveurs Model Context Protocol (MCP) exposent le récupérateur comme un outil que Claude Code, Codex CLI, Cursor et d’autres outils IA peuvent appeler directement. L’agent interroge le coffre-fort, reçoit des résultats classés avec attribution des sources et utilise le contexte sans charger des fichiers entiers. Le serveur MCP est une fine couche autour du moteur de récupération.

Le local-first signifie zéro coût d’API et confidentialité totale. L’ensemble de la pile s’exécute sur une seule machine : SQLite pour le stockage, Model2Vec pour les embeddings, FTS5 pour la recherche par mots-clés, sqlite-vec pour le KNN vectoriel. Aucun service cloud, aucun appel API, aucune dépendance réseau. Les notes personnelles ne quittent jamais la machine. Le ré-embedding complet de 49 746 chunks coûterait environ 0,30 $ aux tarifs API d’OpenAI, mais les véritables coûts sont la latence, l’exposition de la vie privée et la dépendance réseau pour un système qui devrait fonctionner hors ligne.4

L’indexation incrémentale maintient le système à jour en moins de 10 secondes. La comparaison des dates de modification des fichiers détecte les changements. Seuls les fichiers modifiés sont re-découpés et ré-embedés. Une réindexation complète prend environ quatre minutes sur du matériel Apple M-series. Les mises à jour incrémentales sur les modifications d’une journée typique s’exécutent en moins de dix secondes. Le système reste à jour sans intervention manuelle.

L’architecture s’adapte de 200 à plus de 20 000 notes. La même conception à trois couches (ingestion, récupération, intégration) fonctionne quelle que soit la taille du coffre-fort. Commencez avec une recherche BM25 uniquement sur un petit coffre-fort. Ajoutez la recherche vectorielle lorsque les collisions de mots-clés deviennent problématiques. Ajoutez la fusion RRF lorsque vous avez besoin à la fois de correspondances exactes et sémantiques. Chaque couche est indépendamment utile et indépendamment supprimable.


Comment utiliser ce guide

Ce guide couvre le système complet. Votre point de départ dépend de votre situation :

Vous êtes… Commencez ici Puis explorez
Nouveau sur Obsidian + IA Pourquoi Obsidian comme infrastructure IA, Démarrage rapide Architecture du coffre-fort, Architecture du serveur MCP
Coffre-fort existant, accès IA souhaité Architecture du serveur MCP, Intégration Claude Code Modèles d’embedding, Recherche plein texte
Construction d’un système de récupération Le pipeline de récupération complet, Reciprocal Rank Fusion Optimisation des performances, Dépannage
Contexte équipe ou entreprise Cadre décisionnel, Patterns de graphe de connaissances Recettes de workflow développeur, Guide de migration

Les sections marquées Contrat incluent les détails d’implémentation, les blocs de configuration et les modes de défaillance. Les sections marquées Narratif se concentrent sur les concepts, les décisions architecturales et le raisonnement derrière les choix de conception. Les sections marquées Recette fournissent des workflows étape par étape.


Pourquoi Obsidian comme infrastructure IA

La thèse de ce guide : les coffres-forts Obsidian sont le meilleur substrat pour les bases de connaissances IA personnelles, car ils sont local-first, en texte brut, structurés en graphe, et l’utilisateur contrôle chaque couche de la pile.

Ce qu’Obsidian apporte à l’IA que les alternatives n’offrent pas

Des fichiers markdown en texte brut. Chaque note est un fichier .md sur votre système de fichiers. Aucun format propriétaire, aucun export de base de données, aucune API requise pour lire le contenu. Tout outil capable de lire des fichiers peut lire votre coffre-fort. grep, ripgrep, pathlib de Python, SQLite FTS5 — tous fonctionnent directement sur les fichiers sources. Lorsque vous construisez un système de récupération, vous indexez des fichiers, pas des réponses API. L’index est toujours cohérent avec la source, car la source est le système de fichiers.

Une architecture local-first. Le coffre-fort réside sur votre machine. Pas de serveur, pas de dépendance à la synchronisation cloud, pas de limites de débit API, pas de conditions d’utilisation régissant la manière dont vous traitez votre propre contenu. Vous pouvez générer les embeddings, indexer, découper et rechercher vos notes sans aucun service externe. C’est essentiel pour l’infrastructure IA, car le pipeline de récupération s’exécute aussi vite que votre disque le permet, et non aussi vite qu’un point de terminaison API répond. C’est également important pour la confidentialité : les notes personnelles contenant des identifiants, des données de santé, des informations financières et des réflexions privées ne quittent jamais votre machine.

La structure en graphe via les wiki-links. La syntaxe [[wiki-link]] d’Obsidian crée un graphe orienté entre les notes. Une note sur l’implémentation OAuth renvoie vers des notes sur la rotation des jetons, la gestion des sessions et la sécurité API. La structure en graphe encode des relations entre concepts, curées par l’humain. Les embeddings vectoriels capturent la similarité sémantique, mais les wiki-links capturent les connexions intentionnelles que l’auteur a établies en réfléchissant au sujet. Le graphe est un signal que les embeddings ne peuvent pas reproduire.

L’écosystème de plugins. Obsidian dispose de plus de 2 500 plugins communautaires (en mars 2026, contre plus de 1 800 mi-2025). Dataview interroge votre coffre-fort comme une base de données. Templater génère des notes à partir de modèles avec une logique JavaScript. L’intégration Git synchronise votre coffre-fort avec un dépôt. Linter impose la cohérence du formatage. Le plugin core Bases (introduit dans la v1.9.10) ajoute des vues de type base de données — tableaux, galeries, calendriers et tableaux kanban — sur les fichiers du coffre-fort en utilisant les propriétés du frontmatter comme champs, sauvegardées sous forme de fichiers .base.27 Ces plugins ajoutent de la structure au coffre-fort sans modifier le format texte brut sous-jacent. Le système de récupération indexe la sortie de ces plugins, pas les plugins eux-mêmes.

Plus de 5 millions d’utilisateurs. Obsidian bénéficie d’une large communauté active qui produit des modèles, des workflows, des plugins et de la documentation. Lorsque vous rencontrez un problème d’organisation du coffre-fort ou de configuration de plugins, quelqu’un a probablement documenté une solution. La communauté produit également des outils adjacents à Obsidian : des serveurs MCP, des scripts d’indexation, des pipelines de publication et des wrappers API.

Ce qu’un système de fichiers seul ne vous apporte pas

Un répertoire de fichiers markdown possède l’avantage du texte brut, mais il lui manque trois éléments qu’Obsidian ajoute :

  1. Les liens bidirectionnels. Obsidian suit automatiquement les backlinks. Lorsque vous liez la Note A à la Note B, la Note B indique que la Note A la référence. Le panneau de graphe visualise les clusters de connexions. Cette conscience bidirectionnelle est une métadonnée qu’un système de fichiers brut ne fournit pas.

  2. L’aperçu en direct avec rendu des plugins. Les requêtes Dataview, les diagrammes Mermaid et les blocs de légende s’affichent en temps réel. L’expérience d’écriture est plus riche qu’un éditeur de texte, tandis que le format de stockage reste du texte brut. Vous écrivez et organisez dans un environnement enrichi ; le système de récupération indexe le markdown brut.

  3. L’infrastructure communautaire. Découverte de plugins, marketplace de thèmes, service de synchronisation (optionnel), service de publication (optionnel) et un écosystème de documentation. Vous pouvez reproduire chaque fonctionnalité individuellement avec des outils autonomes, mais Obsidian les rassemble dans un workflow cohérent.

Ce qu’Obsidian ne fait PAS (et ce que vous construisez)

Obsidian n’inclut pas d’infrastructure de récupération. Il dispose d’une recherche basique (plein texte, nom de fichier, tag) mais pas de pipeline d’embedding, pas de recherche vectorielle, pas de classement par fusion, pas de serveur MCP, pas de filtrage d’identifiants, pas de stratégie de découpage (chunking), et pas de hooks d’intégration pour les outils IA externes. Ce guide couvre l’infrastructure que vous construisez par-dessus Obsidian. Le coffre-fort est le substrat. Le pipeline de récupération, le serveur MCP et les hooks d’intégration sont l’infrastructure.

L’architecture décrite ici est markdown-first, pas exclusive à Obsidian. Si vous utilisez Logseq, Foam, Dendron ou un simple répertoire de fichiers markdown, le pipeline de récupération fonctionne de manière identique. Le découpeur lit les fichiers .md. L’embedder traite des chaînes de texte. L’indexeur écrit dans SQLite. Aucun de ces composants ne dépend de fonctionnalités spécifiques à Obsidian. La contribution d’Obsidian est l’environnement d’écriture et d’organisation qui produit les fichiers markdown que le récupérateur indexe.


Démarrage rapide : premier coffre connecté à l’IA

Cette section vous permet de connecter un coffre à un outil d’IA en cinq minutes. Vous allez installer Obsidian, créer un coffre, installer un serveur MCP et exécuter votre première requête. Ce démarrage rapide utilise un serveur MCP communautaire pour obtenir des résultats immédiats. Les sections suivantes couvrent la construction d’un pipeline de récupération personnalisé pour un usage en production.

Prérequis

  • macOS, Linux ou Windows
  • Node.js 18+ (pour le serveur MCP)
  • Obsidian 1.12+ (pour l’intégration CLI ; les versions antérieures fonctionnent pour les configurations MCP uniquement)
  • Claude Code, Codex CLI ou Cursor installé

Étape 1 : Créer un coffre

Téléchargez Obsidian depuis obsidian.md et créez un nouveau coffre. Choisissez un emplacement facile à retrouver — le serveur MCP a besoin du chemin absolu.

# Example vault location
~/Documents/knowledge-base/

Ajoutez quelques notes pour donner au système de récupération de la matière à exploiter. Même 10 à 20 notes suffisent pour observer des résultats. Chaque note doit être un fichier .md avec un titre significatif et au moins un paragraphe de contenu.

Étape 2 : Installer un serveur MCP

Plusieurs serveurs MCP communautaires offrent un accès immédiat au coffre. L’écosystème s’est considérablement enrichi au cours de 2025-2026. Parmi les mises à jour récentes notables, MCPVault v0.11.0 (mars 2026) a ajouté list_all_tags pour l’analyse du frontmatter et des hashtags avec compteurs, amélioré la gestion des dossiers avec points, et pris en charge les fichiers .base et .canvas.25 Le paquet a également été renommé @bitbonsai/mcpvault sur npm.

Serveur Auteur Transport Nécessite un plugin Fonctionnalité clé
obsidian-mcp-server StevenStavrakis STDIO Non Léger, basé sur les fichiers
mcp-obsidian MarkusPfundstein STDIO REST API local CRUD complet du coffre via REST
obsidian-mcp-tools jacksteamdev STDIO Oui (plugin) Recherche sémantique + Templater
obsidian-claude-code-mcp iansinnott WebSocket Oui (plugin) Auto-découverte pour Claude Code
obsidian-mcp-server cyanheads STDIO REST API local Tags, gestion du frontmatter

Pour le démarrage rapide, l’option la plus simple est un serveur basé sur les fichiers qui lit directement les fichiers .md :

npm install -g obsidian-mcp-server

Étape 3 : Configurer votre outil d’IA

Claude Code — ajoutez dans ~/.claude/settings.json :

{
  "mcpServers": {
    "obsidian": {
      "command": "obsidian-mcp-server",
      "args": ["--vault", "/absolute/path/to/your/vault"]
    }
  }
}

Codex CLI — ajoutez dans .codex/config.toml :

[mcp_servers.obsidian]
command = "obsidian-mcp-server"
args = ["--vault", "/absolute/path/to/your/vault"]

Cursor — ajoutez dans .cursor/mcp.json :

{
  "mcpServers": {
    "obsidian": {
      "command": "obsidian-mcp-server",
      "args": ["--vault", "/absolute/path/to/your/vault"]
    }
  }
}

Étape 4 : Exécuter votre première requête

Ouvrez votre outil d’IA et posez une question à laquelle vos notes peuvent répondre :

Search my Obsidian vault for notes about [topic you wrote about]

L’outil d’IA appelle le serveur MCP, qui effectue une recherche dans votre coffre et renvoie le contenu correspondant. Vous devriez voir des résultats avec les chemins de fichiers et des extraits pertinents.

Ce que vous venez de construire

Vous avez connecté une base de connaissances locale à un outil d’IA via un protocole standard. Le serveur MCP lit les fichiers de votre coffre, effectue une recherche basique et renvoie les résultats. C’est la version minimale viable.

Ce que ce démarrage rapide ne vous apporte PAS : - Récupération hybride (BM25 + recherche vectorielle + fusion RRF) - Recherche sémantique par embeddings - Filtrage des identifiants - Indexation incrémentale - Injection automatique de contexte par hooks

Le reste de ce guide couvre la construction de chacune de ces fonctionnalités. Le démarrage rapide valide le concept. Le pipeline complet offre une récupération de qualité production.


CLI Obsidian pour les workflows IA

Obsidian 1.12 (février 2026) a introduit une interface en ligne de commande intégrée qui ouvre une nouvelle surface d’intégration pour les workflows IA.28 Le CLI agit comme une télécommande pour l’interface graphique d’Obsidian — Obsidian doit être en cours d’exécution (ou se lancera automatiquement à la première commande). Activez-le dans Paramètres > Général > Interface en ligne de commande.

Pourquoi le CLI est important pour l’infrastructure IA

Le CLI offre un accès programmatique aux opérations natives d’Obsidian qui nécessitaient auparavant l’interface graphique ou des APIs de plugins. Pour les workflows IA, les fonctionnalités clés sont :

  • Recherche depuis des scripts et des hooks. obsidian search "query" et obsidian search:context "query" exécutent des recherches dans le coffre depuis n’importe quel script shell, hook ou pipeline d’automatisation. La variante search:context renvoie les lignes correspondantes avec le contexte environnant, utile pour alimenter les résultats dans des prompts IA.
  • Automatisation des notes quotidiennes. obsidian daily ouvre ou crée la note quotidienne du jour. Combiné avec des scripts shell, cela permet des workflows de briefing quotidien automatisés — un hook peut ajouter des résumés générés par l’IA à la note quotidienne.
  • Création de notes basée sur des modèles. obsidian template list et obsidian template create génèrent des notes à partir de modèles Templater ou de modèles natifs, permettant aux agents IA de créer des entrées structurées dans le coffre sans écrire directement des fichiers markdown.
  • Gestion des propriétés. obsidian property set et obsidian property get lisent et écrivent les propriétés du frontmatter, permettant la mise à jour des métadonnées depuis des scripts sans analyser le YAML.
  • Contrôle des plugins. obsidian plugin enable/disable/list gère les plugins de manière programmatique, utile pour activer ou désactiver les plugins d’indexation lors d’opérations par lots.
  • Gestion des tâches. obsidian task list/add/complete fournit un accès structuré aux tâches, utile pour les agents IA qui gèrent des éléments de travail dans le coffre.

CLI vs MCP pour l’accès IA

Le CLI et les serveurs MCP remplissent des rôles différents et sont complémentaires, non concurrents :

Aspect CLI Obsidian Serveur MCP
Appelant Scripts shell, hooks, tâches cron Agents IA (Claude Code, Codex, Cursor)
Protocole Processus POSIX (stdin/stdout/stderr) MCP (JSON-RPC sur STDIO ou HTTP)
Point fort Opérations natives Obsidian (modèles, plugins, propriétés) Récupération personnalisée (embeddings, BM25, fusion RRF)
Limitation Pas de recherche vectorielle, pas de pipeline d’embeddings Pas d’accès aux opérations internes d’Obsidian
Idéal pour Scripts d’automatisation, pipelines d’ingestion, actions de hooks Requêtes d’agents IA en temps réel pendant les sessions

Recommandation : Utilisez le CLI pour l’automatisation de l’ingestion (création de notes, gestion des propriétés, recherche native Obsidian) et MCP pour la récupération (recherche hybride avec embeddings). Un hook PreToolUse peut appeler obsidian search:context comme pré-vérification rapide avant de recourir au récupérateur MCP complet pour des résultats classés.

Exemple : hook d’ingestion alimenté par le CLI

#!/bin/bash
# Hook: append today's signals to daily note via CLI
DATE=$(date +%Y-%m-%d)
SUMMARY="$1"
obsidian daily  # ensure daily note exists
obsidian file append "Daily Notes/${DATE}.md" "## AI Summary\n${SUMMARY}"

Plugins agents pour Obsidian

Une catégorie croissante de plugins Obsidian intègre des agents de codage IA directement dans l’interface du coffre, offrant une alternative à la configuration de serveurs MCP externes. Ces plugins exécutent l’agent IA dans la barre latérale d’Obsidian plutôt que de se connecter depuis un outil externe.

Claudian

Claudian intègre Claude Code comme collaborateur IA dans le coffre. Le répertoire du coffre devient le répertoire de travail de Claude, lui donnant des capacités agentiques complètes : lecture/écriture de fichiers, recherche, commandes bash et workflows multi-étapes.29

Fonctionnalités clés pour l’infrastructure IA : - Prompts contextuels. Attache automatiquement la note active, prend en charge les mentions de fichiers via @notename, l’exclusion par tags et la sélection dans l’éditeur comme contexte. - Support de la vision. Analysez des images par glisser-déposer, collage ou chemin de fichier — utile pour traiter les captures d’écran et diagrammes stockés dans le coffre. - Commandes slash. Créez des modèles de prompts réutilisables déclenchés par /command, permettant des opérations standardisées sur le coffre. - Modes de permission. Modes YOLO (approbation automatique), Safe (approbation de chaque action) et Plan (planification uniquement) avec une liste de blocage de sécurité et un confinement au coffre.

Agent Client

Agent Client réunit Claude Code, Codex CLI et Gemini CLI dans une barre latérale Obsidian unifiée via le protocole Agent Client Protocol (ACP).30

Fonctionnalités clés : - Basculement multi-agents. Discutez avec Claude Code, Codex ou Gemini CLI depuis le même panneau, en passant d’un agent à l’autre selon vos besoins. - Mentions de notes. Utilisez @notename pour inclure le contenu des notes dans les prompts, similaire à Claudian mais indépendant de l’agent. - Exécution shell. Exécutez des commandes terminal directement dans le chat — scripts de build, commandes git ou toute opération terminal sans quitter la conversation. - Approbation des actions. Contrôle granulaire sur les lectures de fichiers, les modifications et les exécutions de commandes.

Quand utiliser les plugins agents vs un MCP externe

Scénario Plugin agent MCP externe
Rédaction et édition de notes avec assistance IA Préférable — l’agent voit le contexte de l’éditeur Fonctionne mais sans perception de l’éditeur
Développement de code sur plusieurs dépôts Limité — restreint au coffre Préférable — accès complet au système de fichiers par projet
Récupération dans un large corpus indexé Recherche basique uniquement Pipeline de récupération hybride complet
Questions rapides sur le coffre pendant la prise de notes Idéal — pas de changement de contexte Nécessite de basculer vers le terminal

Recommandation : Utilisez les plugins agents pour les workflows centrés sur le coffre (rédaction, organisation, résumé de notes). Utilisez les serveurs MCP externes pour les workflows de développement où l’agent IA a besoin du pipeline de récupération complet et d’un accès aux bases de code en dehors du coffre. Les deux approches coexistent parfaitement — exécutez Claudian dans Obsidian pour le travail sur les notes et Claude Code avec MCP en externe pour le développement.


Cadre décisionnel : Obsidian vs alternatives

Obsidian ne convient pas à tous les cas d’usage. Cette section identifie quand Obsidian constitue le bon substrat, quand c’est excessif, et quand une autre solution s’avère plus adaptée.

Arbre de décision

START: What is your primary content type?

├─ Structured data (tables, records, schemas)
   Use a database. SQLite, PostgreSQL, or a spreadsheet.
   Obsidian is for prose, not tabular data.

├─ Ephemeral context (current project, temporary notes)
   Use CLAUDE.md / AGENTS.md in the project repo.
   These travel with the code and reset per project.

├─ Team wiki (shared documentation, onboarding)
   Evaluate Notion, Confluence, or a shared git repo.
   Obsidian vaults are personal-first. Team sync is possible
    but not native.

└─ Growing personal knowledge corpus
   
   ├─ < 50 notes
      A folder of markdown files + grep is sufficient.
      Obsidian adds value mainly through the link graph,
       which needs density to be useful.
   
   ├─ 50 - 500 notes
      Obsidian adds value. Wiki-links create a navigable graph.
      BM25-only search (FTS5) is sufficient at this scale.
      Skip vector search and RRF until keyword collisions appear.
   
   ├─ 500 - 5,000 notes
      Full hybrid retrieval becomes valuable. Keyword collisions
       increase. Semantic search catches queries that BM25 misses.
      Add vector search + RRF fusion at this scale.
   
   └─ 5,000+ notes
       Full pipeline is essential. BM25-only returns too much noise.
       Credential filtering becomes critical (more notes = more
        accidentally pasted secrets).
       Incremental indexing matters (full reindex takes minutes).
       MCP integration pays dividends on every AI interaction.

Matrice de comparaison

Critère Obsidian Notion Apple Notes Système de fichiers CLAUDE.md
Local d’abord Oui Non (cloud) Partiel (iCloud) Oui Oui
Texte brut Oui (markdown) Non (blocs) Non (propriétaire) Oui Oui
Structure en graphe Oui (wiki-links) Partiel (mentions) Non Non Non
Indexable par l’IA Accès direct aux fichiers API requis Export requis Accès direct aux fichiers Déjà dans le contexte
Écosystème de plugins 2 500+ plugins Intégrations Aucun N/A N/A
Utilisable hors ligne Complet Lecture seule en cache Partiel Complet Complet
Passage à l’échelle (10K+ notes) Oui Oui (avec API) Se dégrade Oui Non (fichier unique)
Coût Gratuit (cœur) 10 $/mois+ Gratuit Gratuit Gratuit

Quand Obsidian est excessif

  • Contexte limité à un seul projet. Si l’IA n’a besoin que du contexte relatif à la base de code actuelle, placez-le dans CLAUDE.md, AGENTS.md ou la documentation au niveau du projet. Ces fichiers accompagnent le dépôt et sont chargés automatiquement.
  • Données structurées. Si le contenu se compose de tableaux, d’enregistrements ou de schémas, utilisez une base de données. Les notes Obsidian sont conçues pour la prose. Dataview peut interroger les champs du frontmatter, mais une véritable base de données gère bien mieux les requêtes structurées.
  • Recherche temporaire. Si les notes seront supprimées à la fin du projet, un répertoire de travail avec des fichiers markdown suffit. Ne construisez pas d’infrastructure de recherche pour du contenu éphémère.

Quand Obsidian est le bon choix

  • Accumulation de connaissances sur des mois ou des années. La valeur se compose à mesure que le corpus grandit. Un coffre de 200 notes interrogé quotidiennement pendant six mois apporte plus de valeur qu’un coffre de 5 000 notes interrogé une seule fois.
  • Plusieurs domaines dans un seul corpus. Un coffre contenant des notes sur la programmation, l’architecture, la sécurité, le design et des projets personnels bénéficie d’une recherche transversale qu’un CLAUDE.md spécifique à un projet ne peut offrir.
  • Contenu sensible en termes de confidentialité. L’approche locale d’abord signifie que le pipeline de recherche n’envoie jamais de contenu vers des services externes. Le coffre contient tout ce que vous y placez, y compris du contenu que vous ne téléchargeriez pas vers un service cloud.

Modèle mental : trois couches

Le système comporte trois couches qui fonctionnent indépendamment mais dont les effets se démultiplient lorsqu’elles sont combinées. Chaque couche a une préoccupation distincte et un mode de défaillance différent.

┌─────────────────────────────────────────────────────┐
                 INTEGRATION LAYER                     
  MCP servers, hooks, skills, context injection        
  Concern: delivering context to AI tools              
  Failure: wrong context, too much context, stale      
└──────────────────────┬──────────────────────────────┘
                        query + ranked results
┌──────────────────────┴──────────────────────────────┐
                  RETRIEVAL LAYER                      
  BM25, vector KNN, RRF fusion, token budget           
  Concern: finding the right content for any query     
  Failure: wrong ranking, missed results, slow queries 
└──────────────────────┬──────────────────────────────┘
                        chunked, embedded, indexed
┌──────────────────────┴──────────────────────────────┐
                   INTAKE LAYER                        
  Note creation, signal triage, vault organization     
  Concern: what enters the vault and how it's stored   │
  Failure: noise, duplicates, missing structure        
└─────────────────────────────────────────────────────┘

L’intake détermine ce qui entre dans le coffre. Sans curation, le coffre accumule du bruit : captures d’écran de tweets, articles copiés-collés sans annotation, pensées inachevées dépourvues de contexte. La couche d’intake est responsable du contrôle qualité au point d’entrée. Pipeline de notation, convention de tags ou processus de revue manuelle — tout mécanisme garantissant que le coffre contient du contenu qui mérite d’être retrouvé.

La recherche (retrieval) rend le coffre interrogeable. C’est le moteur : découpage des notes en unités de recherche (chunking), projection des fragments dans l’espace vectoriel (embeddings), indexation pour la recherche par mots-clés et sémantique, fusion des résultats avec RRF. La couche de recherche transforme un répertoire de fichiers en une base de connaissances interrogeable. Sans cette couche, le coffre reste navigable par exploration manuelle et recherche basique, mais n’est pas accessible programmatiquement aux outils d’IA.

L’intégration connecte la couche de recherche aux outils d’IA. Un serveur MCP expose la recherche sous forme d’outil appelable. Les hooks injectent du contexte automatiquement. Les skills capturent de nouvelles connaissances et les réintègrent dans le coffre. La couche d’intégration constitue l’interface entre la base de connaissances et les agents IA qui la consomment.

Les couches sont découplées par conception. Le pipeline de notation de l’intake ne sait rien des embeddings. Le moteur de recherche ne connaît rien des règles de routage des signaux. Le serveur MCP ignore comment les notes ont été créées. Ce découplage permet d’améliorer chaque couche indépendamment. Remplacez le modèle d’embedding sans toucher au pipeline d’intake. Ajoutez une nouvelle capacité MCP sans modifier le moteur de recherche. Modifiez les heuristiques de notation des signaux sans toucher à l’index.


Architecture du vault pour la consommation par l’IA

Un vault optimisé pour la recherche par IA suit des conventions différentes de celles d’un vault optimisé pour la navigation personnelle. Cette section couvre la structure des dossiers, le schéma des notes, les conventions de frontmatter et les patterns spécifiques qui améliorent la qualité de la recherche.

Structure des dossiers

Utilisez des préfixes numérotés pour les dossiers de premier niveau afin de créer une hiérarchie organisationnelle prévisible. Les numéros n’impliquent pas de priorité — ils regroupent des domaines connexes et rendent la structure facile à parcourir.

vault/
├── 00-inbox/              # Unsorted captures, pending triage
├── 01-projects/           # Active project notes
├── 02-areas/              # Ongoing areas of responsibility
├── 03-resources/          # Reference material by topic
   ├── programming/
   ├── security/
   ├── ai-engineering/
   ├── design/
   └── devops/
├── 04-archive/            # Completed projects, old references
├── 05-signals/            # Scored signal intake
   ├── ai-tooling/
   ├── security/
   ├── systems/
   └── ...12 domain folders
├── 06-daily/              # Daily notes (if used)
├── 07-templates/          # Note templates (excluded from index)
├── 08-attachments/        # Images, PDFs (excluded from index)
├── .obsidian/             # Obsidian config (excluded from index)
└── .indexignore            # Paths to exclude from retrieval index

Dossiers à indexer : tout ce qui contient de la prose en markdown — projets, domaines de responsabilité, ressources, signaux, notes quotidiennes.

Dossiers à exclure de l’indexation : les templates (ils contiennent des variables de substitution, pas du contenu), les pièces jointes (fichiers binaires), la configuration Obsidian, ainsi que tout dossier contenant du contenu sensible que vous ne souhaitez pas inclure dans l’index de recherche.

Le fichier .indexignore

Créez un fichier .indexignore à la racine du vault pour exclure explicitement certains chemins de l’index de recherche. La syntaxe est identique à celle de .gitignore :

# Obsidian internal
.obsidian/

# Templates contain placeholders, not content
07-templates/

# Binary attachments
08-attachments/

# Personal health/medical notes
02-areas/health/

# Financial records
02-areas/finance/personal/

# Career documents (resumes, salary data)
02-areas/career/private/

L’indexeur lit ce fichier avant de parcourir le vault et ignore entièrement les chemins correspondants. Les fichiers situés dans les chemins exclus ne sont jamais découpés en chunks, jamais transformés en embeddings, et n’apparaissent jamais dans les résultats de recherche.

Schéma des notes

Chaque note doit comporter un frontmatter YAML. Le moteur de recherche utilise les champs du frontmatter pour le filtrage et l’enrichissement contextuel :

---
title: "OAuth Token Rotation Patterns"
type: note           # note | signal | project | moc | daily
domain: security     # primary domain for routing
tags:
  - authentication
  - oauth
  - token-management
created: 2026-01-15
updated: 2026-02-28
source: ""           # URL if captured from external source
status: active       # active | archived | draft
---

Champs obligatoires pour la recherche :

  • title — Utilisé dans l’affichage des résultats de recherche et comme contexte de titre pour BM25
  • type — Permet les requêtes filtrées par type (« afficher uniquement les MOCs » ou « uniquement les signaux »)
  • tags — Indexés dans le contexte des titres FTS5 avec un poids de 0.3, fournissant des correspondances par mots-clés même lorsque le corps du texte utilise une terminologie différente

Champs optionnels mais précieux :

  • domain — Permet les requêtes limitées à un domaine (« rechercher uniquement dans les notes de sécurité »)
  • source — Attribution pour le contenu capturé ; le moteur de recherche peut inclure les URLs sources dans les résultats
  • status — Permet d’exclure les notes archivées ou en brouillon de la recherche active

Conventions de découpage (chunking)

Le moteur de recherche découpe les notes aux limites des titres H2 (##). La structure de vos notes affecte donc directement la granularité de la recherche :

Favorable à la recherche :

## Token Rotation Strategy

The rotation interval depends on the threat model...

## Implementation with refresh_token

The OAuth 2.0 refresh token flow requires...

## Error Handling: Expired Tokens

When a token expires mid-request...

Trois sections H2 produisent trois chunks recherchables indépendamment. Chaque chunk contient suffisamment de contexte pour que l’embedding capture son sens. Une requête portant sur « expired token handling » correspondra spécifiquement au troisième chunk.

Défavorable à la recherche :

# OAuth Notes

Token rotation depends on threat model. The OAuth 2.0 refresh
token flow requires storing the refresh token securely. When a
token expires mid-request, the client should retry after refresh.
The rotation interval is typically 15-30 minutes for access tokens
and 7-30 days for refresh tokens...

Une longue section sans titres H2 produit un seul chunk volumineux. L’embedding fait la moyenne de tous les sujets abordés dans la section. Une requête sur n’importe quel sous-thème correspond à la note entière de manière indifférenciée.

Règle générale : si une section couvre plus d’un concept, découpez-la en sous-sections H2. Le système de chunking s’occupe du reste.

Ce qu’il ne faut pas mettre dans les notes

Contenus qui dégradent la qualité de la recherche :

  • Copier-coller brut d’articles entiers sans annotation. Le moteur de recherche indexe les mots-clés de l’article original, diluant votre vault avec du contenu que vous n’avez pas rédigé. Ajoutez plutôt un résumé, extrayez les points clés ou créez un lien vers l’URL source.
  • Captures d’écran sans description textuelle. Le moteur de recherche indexe le texte en markdown. Une image sans texte alternatif ni description environnante est invisible pour BM25 comme pour la recherche vectorielle.
  • Chaînes d’identifiants. Clés API, tokens, mots de passe, chaînes de connexion. Même avec le filtrage des identifiants (credential filtering), l’approche la plus sûre consiste à ne jamais coller de secrets dans vos notes. Référencez-les par leur nom (« le token API de Cloudflare dans ~/.env ») à la place.
  • Contenu auto-généré sans curation. Si un outil génère une note (transcription de réunion, surlignages Readwise, import RSS), examinez-la et annotez-la avant qu’elle n’intègre le vault permanent. Les imports automatiques non vérifiés ajoutent du volume sans ajouter de valeur exploitable par la recherche.

Écosystème de plugins pour les workflows IA

Les plugins Obsidian qui améliorent la qualité du vault pour la recherche par IA se répartissent en trois catégories : structurels (imposent la cohérence), interrogation (exposent les métadonnées) et synchronisation (maintiennent le vault à jour).

Plugins essentiels

Dataview. Interroge votre vault comme une base de données en utilisant les champs du frontmatter. Créez des index dynamiques : « toutes les notes tagguées security mises à jour dans les 30 derniers jours » ou « toutes les notes de projet avec le statut active ». Dataview n’aide pas directement la recherche, mais il vous permet d’identifier les lacunes dans la couverture de votre vault et de repérer les notes nécessitant une mise à jour.

TABLE type, domain, updated
FROM "03-resources"
WHERE status = "active"
SORT updated DESC
LIMIT 20

Templater. Crée des notes à partir de templates avec des champs dynamiques. Assurez-vous que chaque nouvelle note démarre avec un frontmatter correct en utilisant un template qui pré-remplit les champs created, type et domain. Un frontmatter cohérent améliore le filtrage lors de la recherche.

<%* /* New Resource Note Template */ %>
---
title: "<% tp.file.cursor() %>"
type: note
domain: <% tp.system.suggester(["programming", "security", "ai-engineering", "design", "devops"], ["programming", "security", "ai-engineering", "design", "devops"]) %>
tags: []
created: <% tp.date.now("YYYY-MM-DD") %>
updated: <% tp.date.now("YYYY-MM-DD") %>
source: ""
status: active
---

## Key Points

## Details

## Références

Linter. Applique des règles de formatage à l’ensemble du coffre. Une hiérarchie cohérente des titres (H1 pour le titre, H2 pour les sections, H3 pour les sous-sections) garantit que le découpeur produit des résultats prévisibles. Règles du Linter importantes pour la recherche :

  • Incrémentation des titres : imposer des niveaux de titre séquentiels (pas de saut de H1 à H3)
  • Titre YAML : correspondre au nom du fichier
  • Espaces en fin de ligne : supprimer (évite les artefacts de tokenisation FTS5)
  • Lignes vides consécutives : limiter à 1 (fragments plus propres)

Intégration Git. Contrôle de version pour votre coffre. Suivez les modifications au fil du temps, synchronisez entre machines et récupérez les suppressions accidentelles. Git fournit également les données mtime que l’indexeur utilise pour la détection incrémentale des changements.

Extensions qui facilitent l’indexation

Smart Connections. Une extension Obsidian qui offre une recherche sémantique alimentée par l’IA directement dans Obsidian. Smart Connections v4 crée des embeddings locaux par défaut — une fois votre coffre indexé, les connexions sémantiques et la recherche fonctionnent entièrement hors ligne, sans aucun appel API.23 Bien que le système de recherche présenté dans ce guide soit externe à Obsidian (il s’exécute comme un pipeline Python), Smart Connections reste utile pour explorer les relations sémantiques pendant la rédaction. Les deux systèmes indexent le même contenu mais répondent à des cas d’usage différents : Smart Connections pour la découverte dans l’éditeur, le récupérateur externe pour l’intégration d’outils IA via MCP.

Metadata Menu. Fournit une édition structurée du frontmatter avec autocomplétion des valeurs de champs. Réduit les erreurs de saisie dans les champs type, domain et tags. Des métadonnées cohérentes améliorent la précision du filtrage lors de la recherche.

Extensions qui nuisent à l’indexation

Excalidraw. Stocke les dessins sous forme de JSON intégré dans les fichiers markdown. Le JSON est syntaxiquement valide en markdown, mais produit des résultats incohérents lorsqu’il est découpé et transformé en embeddings. Excluez les fichiers Excalidraw de l’index via .indexignore ou filtrez par extension de fichier.

Kanban. Stocke l’état du tableau sous forme de markdown spécialement formaté. Ce format est conçu pour le rendu Kanban, pas pour la recherche de prose. Le découpeur produit des fragments de titres de cartes et de métadonnées qui ne se prêtent pas bien aux embeddings. Excluez les tableaux Kanban de l’index.

Calendar. Crée des notes quotidiennes avec un contenu minimal (souvent juste un en-tête de date). Les notes vides ou quasi vides produisent des fragments de faible qualité. Si vous utilisez les notes quotidiennes, rédigez-y du contenu substantiel ou excluez le dossier des notes quotidiennes de l’index.

Configuration des extensions qui compte

Récupération de fichiers → Activé. Protège contre la suppression accidentelle de notes. Sans lien direct avec la recherche, mais essentiel pour une base de connaissances sur laquelle vous comptez.

Sauts de ligne stricts → Désactivé. Les sauts de ligne standard du markdown (double saut de ligne pour un paragraphe) produisent des fragments plus propres que le mode strict d’Obsidian (simple saut de ligne pour <br>).

Emplacement par défaut des nouveaux fichiers → Dossier désigné. Dirigez les nouveaux fichiers vers 00-inbox/ afin que les notes non catégorisées ne polluent pas les dossiers de domaine. La boîte de réception sert de zone de transit ; les fichiers sont déplacés vers les dossiers de domaine après tri.

Format des wiki-links → Chemin le plus court possible. Des cibles de lien plus courtes sont plus faciles à résoudre pour le récupérateur lors de l’indexation de la structure des liens.


Modèles d’embeddings : choix et configuration

Le modèle d’embedding convertit les fragments de texte en vecteurs numériques pour la recherche sémantique. Le choix du modèle détermine la qualité de la récupération, la taille de l’index, la vitesse d’embedding et les dépendances d’exécution. Cette section explique pourquoi potion-base-8M de Model2Vec constitue le choix par défaut et dans quels cas privilégier des alternatives.

Pourquoi Model2Vec potion-base-8M

Modèle : minishlab/potion-base-8M Paramètres : 7,6 millions Dimensions : 256 Taille : ~30 Mo Dépendances : model2vec (numpy uniquement, sans PyTorch) Inférence : CPU uniquement, embeddings de mots statiques (aucune couche d’attention)

Model2Vec distille les connaissances d’un sentence transformer en embeddings de tokens statiques. Au lieu d’exécuter des couches d’attention sur l’entrée (comme le font BERT, MiniLM et les autres modèles transformer), Model2Vec produit des vecteurs par moyenne pondérée d’embeddings de tokens pré-calculés.5 La conséquence pratique : la vitesse d’embedding est 50 à 500 fois supérieure à celle des modèles basés sur les transformers, car il n’y a aucun calcul séquentiel.

Sur la suite de benchmarks MTEB, potion-base-8M atteint 89 % des performances d’all-MiniLM-L6-v2 (50,03 contre 56,09 en moyenne).6 L’écart de qualité de 11 % représente le compromis en échange des avantages de vitesse et de simplicité. Pour des fragments markdown courts (en moyenne 200 à 400 mots dans un coffre-fort typique), la différence de qualité est moins marquée que sur des documents longs, car les deux modèles convergent vers des représentations similaires pour du texte court et ciblé.

Configuration

# embedder.py
DEFAULT_MODEL = "minishlab/potion-base-8M"
EMBEDDING_DIM = 256

class Model2VecEmbedder:
    def __init__(self, model_name=DEFAULT_MODEL):
        self._model_name = model_name
        self._model = None

    def _ensure_model(self):
        if self._model is not None:
            return
        _activate_venv()  # Add isolated venv to sys.path
        from model2vec import StaticModel
        self._model = StaticModel.from_pretrained(self._model_name)

    def embed_batch(self, texts):
        self._ensure_model()
        vecs = self._model.encode(texts)
        return [v.tolist() for v in vecs]

Chargement paresseux. Le modèle se charge à la première utilisation, et non à l’importation. Importer le module embedder ne coûte rien lorsque le système de récupération fonctionne en mode de repli BM25 uniquement (par exemple, lorsque le venv d’embedding n’est pas installé).

Environnement virtuel isolé. Le modèle s’exécute dans un venv dédié (par exemple, ~/.claude/venvs/memory/) pour éviter les conflits de dépendances avec le reste de la chaîne d’outils. La fonction _activate_venv() ajoute le répertoire site-packages du venv à sys.path au moment de l’exécution.

# Create isolated venv
python3 -m venv ~/.claude/venvs/memory
~/.claude/venvs/memory/bin/pip install model2vec

Traitement par lots. L’embedder traite les textes par lots de 64 afin d’amortir le surcoût de Model2Vec. L’indexeur fournit les fragments à embed_batch() plutôt que de les traiter un par un.

Quand choisir des alternatives

Modèle Dim Taille Vitesse Qualité (MTEB) Idéal pour
potion-base-8M 256 30 Mo 500x 50,03 Par défaut : local, rapide, sans GPU
potion-base-32M 256 120 Mo 400x 52,46 Meilleure qualité, toujours statique
potion-retrieval-32M 256 120 Mo 400x 36,35 (récupération) Statique optimisé pour la récupération
potion-multilingual-128M 256 ~500 Mo 300x Coffres-forts multilingues (101 langues)
all-MiniLM-L6-v2 384 80 Mo 1x 56,09 Meilleure qualité, toujours local
nomic-embed-text-v1.5 768 270 Mo 0,5x 62,28 Meilleure qualité locale
text-embedding-3-small 1536 API N/A 62,30 Basé sur API, qualité maximale

Choisissez potion-base-32M si vous souhaitez une meilleure qualité que potion-base-8M sans quitter la famille des embeddings statiques. Publié en janvier 2025, il utilise un vocabulaire plus large distillé à partir de baai/bge-base-en-v1.5, atteignant 52,46 de moyenne MTEB (amélioration de 5 % par rapport à potion-base-8M) tout en conservant la même sortie à 256 dimensions et la dépendance numpy uniquement.20 Le fichier modèle 4 fois plus volumineux augmente l’utilisation mémoire, mais la vitesse d’embedding reste de plusieurs ordres de grandeur supérieure à celle des modèles transformer.

Choisissez potion-retrieval-32M lorsque votre cas d’usage principal est la récupération (ce qui est le cas de la recherche dans un coffre-fort). Cette variante est affinée à partir de potion-base-32M spécifiquement pour les tâches de récupération, obtenant 36,35 sur les benchmarks de récupération MTEB contre 33,52 pour le modèle de base.20 La moyenne MTEB globale descend à 49,73, car l’affinage sacrifie les performances généralistes au profit de gains spécifiques à la récupération.

Choisissez potion-multilingual-128M lorsque votre coffre-fort contient des notes en plusieurs langues. Publié en mai 2025, ce modèle couvrant 101 langues est le meilleur modèle d’embedding statique pour les tâches multilingues, générant des embeddings pour tout texte dans n’importe quelle langue tout en conservant la même dépendance numpy uniquement que les autres modèles potion.24 Le fichier modèle plus volumineux (~500 Mo) est le compromis pour la capacité multilingue. Utilisez-le lorsque vous avez des notes en japonais, chinois, allemand ou d’autres langues non anglaises aux côtés de contenu en anglais.

Choisissez all-MiniLM-L6-v2 lorsque la qualité de récupération prime sur la vitesse et que PyTorch est installé. Les vecteurs à 384 dimensions augmentent la taille de la base de données SQLite d’environ 50 % par rapport aux vecteurs à 256 dimensions. La vitesse d’embedding passe de moins d’une minute à environ 10 minutes pour une réindexation complète de 15 000 fichiers sur du matériel M-series.

Choisissez nomic-embed-text-v1.5 si vous avez besoin de la meilleure qualité de récupération locale possible et acceptez une indexation plus lente. Les vecteurs à 768 dimensions triplent approximativement la taille de la base de données. Nécessite PyTorch et un processeur moderne ou un GPU.

Choisissez text-embedding-3-small lorsque la latence réseau et la confidentialité représentent des compromis acceptables. L’API produit les embeddings de la plus haute qualité, mais introduit une dépendance cloud, un coût par token (0,02 $/million de tokens) et envoie votre contenu aux serveurs d’OpenAI.

Restez avec potion-base-8M dans tous les autres cas. L’avantage de vitesse est crucial pour l’indexation itérative (réindexation pendant le développement), la dépendance numpy uniquement évite la complexité d’installation de PyTorch, et les vecteurs à 256 dimensions maintiennent une base de données compacte.

Quantification et réduction de dimensionnalité

Model2Vec v0.5.0+ prend en charge le chargement de modèles avec une précision et des dimensions réduites.20 C’est utile pour le déploiement sur du matériel contraint ou la réduction de la taille de la base de données sans changer de modèle :

from model2vec import StaticModel

# Load with int8 quantization (25% of original size)
model = StaticModel.from_pretrained("minishlab/potion-base-8M", quantize=True)

# Load with reduced dimensions (e.g., 128 instead of 256)
model = StaticModel.from_pretrained("minishlab/potion-base-8M", dimensionality=128)

Les modèles quantifiés conservent une qualité de récupération quasi identique pour une fraction de l’empreinte mémoire. La réduction de dimensionnalité suit une troncature de type Matryoshka — les N premières dimensions portent le plus d’information. Réduire de 256 à 128 dimensions divise par deux le stockage vectoriel avec une perte de qualité minimale pour la récupération de textes courts.

Depuis mai 2025, Model2Vec prend également en charge les tokenizers BPE et Unigram (en plus de WordPiece), ce qui élargit l’ensemble des sentence transformers pouvant être distillés en modèles statiques.22

Affinage pour des embeddings spécifiques au coffre-fort

Model2Vec v0.4.0+ permet d’entraîner des modèles de classification personnalisés par-dessus des embeddings statiques, et la v0.7.0 ajoute la quantification du vocabulaire et un pooling configurable pour la distillation.22 Ceci est pertinent pour les coffres-forts au vocabulaire spécialisé (notes médicales, références juridiques, jargon de domaine) où les modèles potion par défaut pourraient ne pas capturer les nuances sémantiques :

from model2vec import StaticModel
from model2vec.train import train_model

# Fine-tune on vault-specific data
model = StaticModel.from_pretrained("minishlab/potion-base-8M")
trained_model = train_model(model, train_texts, train_labels)
trained_model.save_pretrained("./vault-embeddings")

Pour la plupart des coffres-forts, le modèle potion-base-8M par défaut produit une qualité de récupération suffisante. L’affinage ne se justifie que lorsque la récupération manque systématiquement des connexions spécifiques au domaine qu’un modèle généraliste ne parvient pas à capturer.

Suivi du hash de modèle

L’indexeur stocke un hash dérivé du nom de modèle et de la taille du vocabulaire. Si vous changez de modèle d’embedding, l’indexeur détecte l’incompatibilité lors de la prochaine exécution incrémentale et déclenche automatiquement une réindexation complète.

def _compute_model_hash(self):
    """Hash model name + vocab size for compatibility tracking."""
    key = f"{self._model_name}:{self._model.vocab_size}"
    return hashlib.sha256(key.encode()).hexdigest()[:16]

Cela empêche le mélange de vecteurs issus de modèles différents dans la même base de données, ce qui produirait des scores de cosine similarity absurdes.

Modes de défaillance

Échec du téléchargement du modèle. La première exécution télécharge le modèle depuis Hugging Face. Si le téléchargement échoue (problème réseau, pare-feu d’entreprise), le système de récupération bascule en mode BM25 uniquement. Le modèle est mis en cache localement après le premier téléchargement.

Incompatibilité de dimensions. Si vous changez de modèle sans vider la base de données, les vecteurs stockés ont une dimension différente de celle des nouveaux embeddings. L’indexeur détecte cette situation via le hash de modèle et déclenche une réindexation complète. Si la vérification du hash échoue (modèle personnalisé sans hash approprié), sqlite-vec renverra une erreur sur les requêtes KNN avec des dimensions incompatibles.

Pression mémoire sur les coffres-forts volumineux. Générer les embeddings de plus de 50 000 fragments en un seul lot peut consommer une mémoire considérable. L’indexeur traite par lots de 64 pour limiter le pic d’utilisation mémoire. Si la mémoire reste un problème, réduisez la taille des lots.


Recherche plein texte avec FTS5

L’extension FTS5 de SQLite fournit une recherche plein texte avec classement BM25. FTS5 constitue le composant de recherche par mots-clés du pipeline de récupération hybride (hybrid retrieval). Cette section couvre la configuration FTS5, les cas où BM25 excelle et ses modes de défaillance spécifiques.

Table virtuelle FTS5

CREATE VIRTUAL TABLE chunks_fts USING fts5(
    chunk_text,
    section,
    heading_context,
    content=chunks,
    content_rowid=id
);

Mode synchronisé avec le contenu. Le paramètre content=chunks indique à FTS5 de référencer directement la table chunks au lieu de stocker une copie dupliquée du texte. Cela réduit de moitié l’espace de stockage nécessaire, mais implique que FTS5 doit être synchronisé manuellement lors des insertions, mises à jour ou suppressions de chunks.

Colonnes. Trois colonnes sont indexées : - chunk_text — Le contenu principal de chaque chunk (poids BM25 : 1.0) - section — Le texte du titre H2 (poids BM25 : 0.5) - heading_context — Titre de la note, tags et métadonnées (poids BM25 : 0.3)

Classement BM25

BM25 classe les documents selon la fréquence des termes, la fréquence inverse dans le corpus et la normalisation par longueur de document. La fonction auxiliaire bm25() dans FTS5 accepte des poids par colonne :

SELECT
    c.id, c.file_path, c.section, c.chunk_text,
    bm25(chunks_fts, 1.0, 0.5, 0.3) AS score
FROM chunks_fts
JOIN chunks c ON chunks_fts.rowid = c.id
WHERE chunks_fts MATCH ?
ORDER BY score
LIMIT 30;

Les poids par colonne (1.0, 0.5, 0.3) signifient : - Une correspondance de mot-clé dans chunk_text contribue le plus au score - Une correspondance dans section (titre) contribue moitié moins - Une correspondance dans heading_context (titre, tags) contribue à hauteur de 30 %

Ces poids sont ajustables. Si votre vault contient des titres descriptifs qui prédisent fortement la qualité du contenu, augmentez le poids de section. Si vos tags sont exhaustifs et précis, augmentez le poids de heading_context.

Quand BM25 l’emporte

BM25 excelle pour les requêtes contenant des identifiants exacts :

  • Noms de fonctions : _rrf_fuse, embed_batch, get_stale_files
  • CLI flags : --incremental, --vault, --model
  • Clés de configuration : bm25_weight, max_tokens, batch_size
  • Messages d’erreur : SQLITE_LOCKED, ConnectionRefusedError
  • Termes techniques spécifiques : PostToolUse, PreToolUse, AGENTS.md

Pour ces requêtes, BM25 trouve immédiatement la correspondance exacte. La recherche vectorielle renverrait du contenu sémantiquement proche, mais pourrait classer la correspondance exacte plus bas qu’une discussion conceptuelle.

Quand BM25 échoue

BM25 échoue face aux requêtes qui utilisent une terminologie différente de celle du contenu stocké :

  • Requête : « how to handle authentication failures » → Le vault contient des notes sur « login error recovery » et « session expiration handling ». BM25 ne trouve rien car les mots-clés diffèrent.
  • Requête : « what is the best way to manage state » → Le vault contient des notes sur « Redux store patterns » et « context providers ». BM25 passe à côté car la « gestion d’état » s’exprime à travers des noms de technologies spécifiques.

BM25 échoue également en cas de collision de mots-clés à grande échelle. Dans un vault de 15 000 fichiers, une recherche sur « configuration » correspond à des centaines de notes, car pratiquement chaque note de projet mentionne la configuration. Les résultats sont techniquement corrects mais pratiquement inutilisables — le classement ne permet pas de déterminer quelle note « configuration » est pertinente pour la requête en cours.

Tokenizer FTS5

FTS5 utilise par défaut le tokenizer unicode61, qui gère le texte ASCII et Unicode. Pour les vaults contenant une proportion importante de contenu CJK (chinois, japonais, coréen), envisagez le tokenizer trigram :

-- For CJK-heavy vaults
CREATE VIRTUAL TABLE chunks_fts USING fts5(
    chunk_text, section, heading_context,
    content=chunks, content_rowid=id,
    tokenize='trigram'
);

Le tokenizer unicode61 par défaut découpe le texte aux limites de mots, ce qui fonctionne mal pour les langues sans espaces entre les mots. Le tokenizer trigram découpe tous les trois caractères, permettant la recherche par sous-chaînes au prix d’un index plus volumineux (environ 3 fois plus grand).

Maintenance

FTS5 nécessite une synchronisation explicite lorsque la table chunks sous-jacente est modifiée :

# After inserting chunks
cursor.execute("""
    INSERT INTO chunks_fts(chunks_fts)
    VALUES('rebuild')
""")

La commande rebuild reconstruit l’index FTS5 à partir de la table de contenu. Exécutez-la après les insertions en masse (réindexation complète), mais pas après les mises à jour incrémentales individuelles — pour celles-ci, utilisez INSERT INTO chunks_fts(rowid, chunk_text, section, heading_context) afin de synchroniser les lignes une par une.


Recherche vectorielle avec sqlite-vec

L’extension sqlite-vec intègre la recherche vectorielle KNN (K-Nearest Neighbors) dans SQLite. Cette section couvre la configuration de sqlite-vec, le pipeline d’embeddings allant de la note au vecteur interrogeable, ainsi que les patterns de requêtes spécifiques.

Table virtuelle sqlite-vec

CREATE VIRTUAL TABLE chunk_vecs USING vec0(
    id INTEGER PRIMARY KEY,
    embedding float[256]
);

Le module vec0 stocke des vecteurs flottants à 256 dimensions sous forme de données binaires compactées. La colonne id correspond exactement à la table chunks (relation 1:1), ce qui permet des jointures entre les résultats vectoriels et les métadonnées des chunks.

Pipeline d’embeddings

Le pipeline transforme une note en vecteur interrogeable :

Note (.md file)
   Chunker: split at H2 boundaries
     Chunks (30-2000 chars each)
       Credential filter: scrub secrets
         Embedder: Model2Vec encode
           Vectors (256-dim float arrays)
             sqlite-vec: store as packed binary
               Ready for KNN queries

Sérialisation des vecteurs

Le module struct de Python sérialise les vecteurs flottants pour le stockage sqlite-vec :

import struct

def _serialize_vector(vec):
    """Pack float list into binary for sqlite-vec."""
    return struct.pack(f"{len(vec)}f", *vec)

def _deserialize_vector(blob, dim=256):
    """Unpack binary blob to float list."""
    return list(struct.unpack(f"{dim}f", blob))

Requête KNN

Une requête de recherche vectorielle encode d’abord la requête en vecteur, puis identifie les K chunks les plus proches par distance cosinus :

def _vector_search(self, query_text, limit=30):
    query_vec = self.embedder.embed_batch([query_text])[0]
    packed = _serialize_vector(query_vec)

    results = self.db.execute("""
        SELECT
            cv.id,
            cv.distance,
            c.file_path,
            c.section,
            c.chunk_text
        FROM chunk_vecs cv
        JOIN chunks c ON cv.id = c.id
        WHERE embedding MATCH ?
            AND k = ?
        ORDER BY distance
    """, [packed, limit]).fetchall()

    return results

L’opérateur MATCH de sqlite-vec effectue une recherche approximative du plus proche voisin. Le paramètre k contrôle le nombre de résultats renvoyés. La colonne distance contient la distance cosinus (0 = identique, 2 = opposé).

Pagination KNN avec contraintes de distance

Depuis sqlite-vec v0.1.7, les requêtes KNN prennent en charge les contraintes WHERE distance < ?, permettant une pagination par curseur à travers de grands ensembles de résultats sans re-parcourir les pages précédentes.26

def _paginated_vector_search(self, query_vec, page_size=20, max_distance=None):
    """Paginate through KNN results using distance constraints."""
    packed = _serialize_vector(query_vec)
    constraint = f"AND distance < {max_distance}" if max_distance else ""

    results = self.db.execute(f"""
        SELECT cv.id, cv.distance, c.file_path, c.chunk_text
        FROM chunk_vecs cv
        JOIN chunks c ON cv.id = c.id
        WHERE embedding MATCH ?
            AND k = ?
            {constraint}
        ORDER BY distance
    """, [packed, page_size]).fetchall()

    # Use last result's distance as cursor for next page
    next_cursor = results[-1][1] if results else None
    return results, next_cursor

Cette approche remplace l’ancien pattern consistant à récupérer un grand k puis à découper côté Python, réduisant ainsi la consommation mémoire pour les requêtes exploratoires sur de grands vaults.

Support DELETE dans les tables vec0

sqlite-vec v0.1.7 a ajouté le support natif de DELETE pour les tables virtuelles vec0.26 Auparavant, supprimer des vecteurs nécessitait de détruire et recréer la table. Désormais, le chemin de suppression de fichiers de l’indexeur peut supprimer les vecteurs directement :

# Before v0.1.7: required workaround (drop + recreate, or mark as inactive)
# After v0.1.7: direct DELETE works
db.execute("DELETE FROM chunk_vecs WHERE id = ?", [chunk_id])

Cela simplifie la réindexation incrémentale lorsque des notes sont supprimées ou déplacées. L’indexeur n’a plus besoin de maintenir une table fantôme d’« identifiants actifs » ni de procéder à des reconstructions par lots.

Quand la recherche vectorielle excelle

La recherche vectorielle se distingue pour les requêtes où le concept importe davantage que les mots exacts :

  • Requête : « how to handle authentication failures » → Trouve des notes sur « login error recovery » (même espace sémantique, mots-clés différents)
  • Requête : « what patterns exist for caching » → Trouve des notes sur « memoization », « Redis TTL strategies » et « HTTP cache headers » (concepts apparentés, terminologie variée)
  • Requête : « approaches to testing asynchronous code » → Trouve des notes sur « pytest-asyncio fixtures », « mock event loops » et « async test patterns » (même concept exprimé à travers des détails d’implémentation)

Quand la recherche vectorielle échoue

La recherche vectorielle peine avec les identifiants exacts :

  • Requête : _rrf_fuse → Renvoie des notes sur les « algorithmes de fusion » et le « rank merging », mais peut classer la définition réelle de la fonction plus bas que les discussions conceptuelles
  • Requête : PostToolUse → Renvoie des notes sur les « tool lifecycle hooks » et les « post-execution handlers » plutôt que le nom spécifique du hook

La recherche vectorielle peine également avec les données structurées. Les fichiers de configuration JSON, les blocs YAML et les extraits de code produisent des embeddings qui capturent des patterns structurels plutôt que du sens sémantique. Un fichier JSON contenant "review": true génère un embedding différent de celui d’une discussion en prose sur la revue de code.

Dégradation gracieuse

Si sqlite-vec ne parvient pas à se charger (extension manquante, plateforme incompatible, bibliothèque corrompue), le système de recherche se rabat sur une recherche BM25 uniquement :

class VectorIndex:
    def __init__(self, db_path):
        self.db = sqlite3.connect(db_path)
        self._vec_available = False
        try:
            self.db.enable_load_extension(True)
            self.db.load_extension("vec0")
            self._vec_available = True
        except Exception:
            pass  # BM25-only mode

    @property
    def vec_available(self):
        return self._vec_available

Le système vérifie vec_available avant de tenter toute requête vectorielle. Lorsque cette fonctionnalité est désactivée, toutes les recherches s’appuient exclusivement sur BM25, et l’étape de fusion RRF est ignorée.


Reciprocal Rank Fusion (RRF)

RRF fusionne deux listes classées sans nécessiter de calibration des scores. Cette section couvre l’algorithme, une trace détaillée de requête, le réglage du paramètre k, et les raisons pour lesquelles RRF est préféré aux alternatives. Pour un calculateur interactif avec rangs modifiables, scénarios prédéfinis et un explorateur visuel d’architecture, consultez le guide approfondi du retriever hybride.

L’algorithme

RRF attribue à chaque document un score basé uniquement sur sa position dans chaque liste :

score(d) = Σ (weight_i / (k + rank_i))

Où : - k est une constante de lissage (60, d’après Cormack et al.3) - rank_i est le rang du document (indexé à partir de 1) dans la liste de résultats i - weight_i est un multiplicateur optionnel par liste (1.0 par défaut)

Les documents bien classés dans plusieurs listes obtiennent des scores fusionnés plus élevés. Les documents n’apparaissant que dans une seule liste reçoivent un score provenant de cette unique source.

Pourquoi RRF plutôt que les alternatives

La combinaison linéaire pondérée exige de calibrer les scores BM25 par rapport aux distances cosinus. Les scores BM25 sont non bornés et varient avec la taille du corpus. Les distances cosinus sont bornées [0, 2]. Les combiner nécessite une normalisation, et les paramètres de normalisation dépendent du jeu de données. RRF n’utilise que les positions de rang, qui sont toujours des entiers commençant à 1, quelle que soit la méthode de scoring.

Les modèles de fusion appris nécessitent des données d’entraînement étiquetées — des paires requête-document avec jugement de pertinence. Pour une base de connaissances personnelle, ces données n’existent pas. Il faudrait juger manuellement des centaines de paires requête-document pour entraîner un modèle exploitable. RRF fonctionne sans aucune donnée d’entraînement.

Les méthodes de vote de Condorcet (comptage de Borda, méthode de Schulze) sont théoriquement élégantes mais plus complexes à implémenter et à régler. L’article original sur RRF a démontré que RRF surpasse les méthodes de Condorcet sur les données d’évaluation TREC.3

La fusion en pratique

Requête : « how does the review aggregator handle disagreements »

BM25 classe review-aggregator.py en position 3 (correspondances exactes sur les mots-clés « review », « aggregator », « disagreements ») mais place deux fichiers de configuration plus haut (ils correspondent davantage à « review »). La recherche vectorielle classe le même fragment en position 1 (correspondance sémantique sur la résolution de conflits). Après fusion RRF :

Fragment BM25 Vec Score fusionné
review-aggregator.py “Disagreement Resolution” #3 #1 0,0323
code-review-patterns.md “Multi-Reviewer” #4 #2 0,0317
deliberation-config.json “Review Weights” #1 0,0164

Les fragments bien classés dans les deux listes remontent en tête. Ceux qui n’apparaissent que dans une seule liste reçoivent un score à source unique et descendent sous les résultats doublement classés. La logique de résolution des désaccords l’emporte car les deux méthodes l’ont trouvée — BM25 par les mots-clés, la recherche vectorielle par la sémantique.

Pour la trace complète étape par étape avec le calcul RRF par rang, essayez différentes valeurs de k dans le calculateur RRF interactif.

Implémentation

RRF_K = 60

def _rrf_fuse(self, bm25_results, vec_results,
              bm25_weight=1.0, vec_weight=1.0):
    """Fuse BM25 and vector results using Reciprocal Rank Fusion."""
    scores = {}

    for rank, r in enumerate(bm25_results, start=1):
        cid = r["id"]
        if cid not in scores:
            scores[cid] = {
                "rrf_score": 0.0,
                "file_path": r["file_path"],
                "section": r["section"],
                "chunk_text": r["chunk_text"],
                "bm25_rank": None,
                "vec_rank": None,
            }
        scores[cid]["rrf_score"] += bm25_weight / (self._rrf_k + rank)
        scores[cid]["bm25_rank"] = rank

    for rank, r in enumerate(vec_results, start=1):
        cid = r["id"]
        if cid not in scores:
            scores[cid] = {
                "rrf_score": 0.0,
                "file_path": r["file_path"],
                "section": r["section"],
                "chunk_text": r["chunk_text"],
                "bm25_rank": None,
                "vec_rank": None,
            }
        scores[cid]["rrf_score"] += vec_weight / (self._rrf_k + rank)
        scores[cid]["vec_rank"] = rank

    fused = sorted(
        scores.values(),
        key=lambda x: x["rrf_score"],
        reverse=True,
    )
    return fused

Réglage de k

La constante k contrôle le poids accordé aux résultats les mieux classés par rapport aux résultats moins bien classés :

  • k faible (ex. 10) : Les résultats en tête dominent. Le rang 1 obtient 1/11 = 0,091, le rang 10 obtient 1/20 = 0,050 (rapport de 1,8x). Adapté lorsque vous faites confiance aux classeurs individuels pour identifier correctement le meilleur résultat.
  • k par défaut (60) : Équilibré. Le rang 1 obtient 1/61 = 0,0164, le rang 10 obtient 1/70 = 0,0143 (rapport de 1,15x). Les écarts de rang sont compressés, ce qui accorde davantage de poids au fait d’apparaître dans plusieurs listes.
  • k élevé (ex. 200) : Apparaître dans les deux listes compte bien plus que la position dans le classement. Le rang 1 obtient 1/201, le rang 10 obtient 1/210 — quasi identiques. À utiliser lorsque les classeurs individuels produisent des classements bruités mais que l’accord inter-listes est fiable.

Commencez avec k=60. L’article original sur RRF a montré que cette valeur est robuste sur des jeux de données TREC variés. N’ajustez qu’après avoir mesuré les cas d’échec sur votre propre distribution de requêtes.

Gestion des égalités

Lorsque deux fragments ont des scores RRF identiques (rare mais possible avec le même rang dans une liste et aucune apparition dans l’autre), départagez selon :

  1. Privilégiez les fragments présents dans les deux listes par rapport à ceux n’apparaissant que dans une seule
  2. Parmi les fragments présents dans les deux listes, privilégiez celui dont le rang combiné est le plus bas
  3. Parmi les fragments présents dans une seule liste, privilégiez celui dont le rang est le plus bas dans cette liste

Le pipeline de recherche complet

Cette section retrace le parcours d’une requête de l’entrée à la sortie à travers l’ensemble du pipeline : recherche BM25, recherche vectorielle, fusion RRF, troncature par budget de tokens et assemblage du contexte.

Flux de bout en bout

User query: "PostToolUse hook for context compression"
  │
  ├─ BM25 Search (FTS5)
  │    → MATCH "PostToolUse hook context compression"
  │    → Top 30 results ranked by BM25 score
  │    → 12ms
  │
  ├─ Vector Search (sqlite-vec)
  │    → Embed query with Model2Vec
  │    → KNN k=30 on chunk_vecs
  │    → Top 30 results ranked by cosine distance
  │    → 8ms
  │
  └─ RRF Fusion
       → Merge 60 candidates (may overlap)
       → Score by rank position
       → Top 10 results
       → 3ms
       │
       └─ Token Budget
            → Truncate to max_tokens (default 4000)
            → Estimate at 4 chars per token
            → Return results with metadata
            → <1ms

Latence totale : ~23 ms pour une base de données de 49 746 chunks sur du matériel Apple M3 Pro.

La API de recherche

class HybridRetriever:
    def search(self, query, limit=10, max_tokens=4000,
               bm25_weight=1.0, vec_weight=1.0):
        """
        Search the vault using hybrid BM25 + vector retrieval.

        Args:
            query: Search query text
            limit: Maximum results to return
            max_tokens: Token budget for total result text
            bm25_weight: Weight for BM25 results in RRF
            vec_weight: Weight for vector results in RRF

        Returns:
            List of SearchResult with file_path, section,
            chunk_text, rrf_score, bm25_rank, vec_rank
        """
        # BM25 search
        bm25_results = self._bm25_search(query, limit=30)

        # Vector search (if available)
        if self.index.vec_available:
            vec_results = self._vector_search(query, limit=30)
            fused = self._rrf_fuse(
                bm25_results, vec_results,
                bm25_weight, vec_weight,
            )
        else:
            fused = bm25_results  # BM25-only fallback

        # Token budget truncation
        results = []
        token_count = 0
        for r in fused[:limit]:
            chunk_tokens = len(r["chunk_text"]) // 4
            if token_count + chunk_tokens > max_tokens:
                break
            results.append(r)
            token_count += chunk_tokens

        return results

Troncature par budget de tokens

Le paramètre max_tokens empêche le moteur de recherche de renvoyer plus de contexte que ce que l’outil d’IA peut exploiter. L’estimation repose sur 4 caractères par token (une approximation raisonnable pour la prose anglaise). Les résultats sont tronqués de manière gloutonne : ils sont ajoutés par ordre de classement jusqu’à épuisement du budget.

Il s’agit d’une stratégie conservatrice. Une approche plus sophistiquée prendrait en compte les scores de qualité par résultat et privilégierait des résultats plus courts mais de meilleure qualité par rapport à des résultats plus longs mais moins pertinents. L’approche gloutonne reste plus simple et fonctionne bien en pratique, car le classement RRF ordonne déjà les résultats par pertinence.

Schéma de la base de données (complet)

-- Chunk content and metadata
CREATE TABLE chunks (
    id INTEGER PRIMARY KEY,
    file_path TEXT NOT NULL,
    section TEXT NOT NULL,
    chunk_text TEXT NOT NULL,
    heading_context TEXT DEFAULT '',
    mtime_ns INTEGER NOT NULL,
    embedded_at REAL NOT NULL
);

CREATE INDEX idx_chunks_file ON chunks(file_path);
CREATE INDEX idx_chunks_mtime ON chunks(mtime_ns);

-- FTS5 for BM25 search (content-synced to chunks table)
CREATE VIRTUAL TABLE chunks_fts USING fts5(
    chunk_text, section, heading_context,
    content=chunks, content_rowid=id
);

-- sqlite-vec for vector KNN search
CREATE VIRTUAL TABLE chunk_vecs USING vec0(
    id INTEGER PRIMARY KEY,
    embedding float[256]
);

-- Model metadata for compatibility tracking
CREATE TABLE model_meta (
    key TEXT PRIMARY KEY,
    value TEXT
);

Chemin de dégradation progressive

Full pipeline:     BM25 + Vector + RRF    Best results
No sqlite-vec:     BM25 only              Good results (no semantic)
No model download:  BM25 only              Good results (no semantic)
No FTS5:           Vector only             Decent results (no keyword)
No database:       Error                   Prompt user to run indexer

Le moteur de recherche vérifie les capacités disponibles à l’initialisation et adapte sa stratégie de requête en conséquence. Un composant manquant dégrade la qualité sans provoquer d’erreurs. Le seul cas d’échec critique survient lorsque le fichier de base de données est absent.

Statistiques en production

Mesures effectuées sur un coffre-fort (vault) de 16 894 fichiers, 49 746 chunks, base de données SQLite de 83 Mo, Apple M3 Pro :

Métrique Valeur
Nombre total de fichiers 16 894
Nombre total de chunks 49 746
Taille de la base de données 83 Mo
Latence requête BM25 (p50) 12 ms
Latence requête vectorielle (p50) 8 ms
Latence fusion RRF 3 ms
Latence recherche de bout en bout (p50) 23 ms
Durée de réindexation complète ~4 minutes
Durée de réindexation incrémentale <10 secondes
Modèle d’embeddings potion-base-8M (256-dim)
Pool de candidats BM25 30
Pool de candidats vectoriels 30
Limite de résultats par défaut 10
Budget de tokens par défaut 4 000 tokens

Hachage du contenu et détection des modifications

L’indexeur doit savoir quels fichiers ont changé depuis la dernière exécution. Cette section couvre le mécanisme de détection des modifications et la stratégie de hachage.

Comparaison du temps de modification des fichiers

L’indexeur stocke mtime_ns (temps de modification du fichier en nanosecondes) pour chaque chunk dans la table chunks. Lors d’une exécution incrémentale, l’indexeur :

  1. Parcourt le coffre-fort à la recherche de tous les fichiers .md dans les dossiers autorisés
  2. Lit le mtime_ns de chaque fichier depuis le système de fichiers
  3. Compare avec le mtime_ns stocké dans la base de données
  4. Identifie trois catégories :
  5. Nouveaux fichiers : le chemin existe dans le système de fichiers mais pas dans la base de données
  6. Fichiers modifiés : le chemin existe dans les deux mais le mtime_ns diffère
  7. Fichiers supprimés : le chemin existe dans la base de données mais plus dans le système de fichiers
def get_stale_files(self, vault_mtimes):
    """Find files whose mtime changed or are new."""
    stored = dict(self.db.execute(
        "SELECT DISTINCT file_path, mtime_ns FROM chunks"
    ).fetchall())

    stale = []
    for path, mtime in vault_mtimes.items():
        if path not in stored or stored[path] != mtime:
            stale.append(path)
    return stale

def get_deleted_files(self, vault_paths):
    """Find files in database that no longer exist in vault."""
    stored_paths = set(r[0] for r in self.db.execute(
        "SELECT DISTINCT file_path FROM chunks"
    ).fetchall())
    return stored_paths - set(vault_paths)

Pourquoi mtime plutôt qu’un hachage de contenu

Le hachage de contenu (SHA-256 du contenu du fichier) serait plus fiable que la comparaison par mtime — il détecterait les cas où un fichier a été « touché » sans être modifié (par exemple, un git checkout restaurant le mtime d’origine). Toutefois, le hachage nécessite de lire chaque fichier à chaque exécution incrémentale. Pour 16 894 fichiers, la lecture du contenu prend 2 à 3 secondes. La lecture des mtime depuis le système de fichiers prend moins de 100 ms.

Le compromis : la comparaison par mtime déclenche occasionnellement une réindexation inutile de fichiers inchangés (faux positifs), mais ne manque jamais les modifications réelles. Les faux positifs coûtent quelques appels d’embeddings supplémentaires par exécution. L’écart de vitesse (100 ms contre 3 secondes) fait du mtime le choix pragmatique pour un système exécuté à chaque interaction avec l’IA.

Gestion des suppressions

Lorsqu’un fichier est supprimé du coffre-fort, l’indexeur retire tous ses chunks de la base de données :

def remove_file(self, file_path):
    """Remove all chunks and vectors for a file."""
    chunk_ids = [r[0] for r in self.db.execute(
        "SELECT id FROM chunks WHERE file_path = ?",
        [file_path],
    ).fetchall()]

    for cid in chunk_ids:
        self.db.execute(
            "DELETE FROM chunk_vecs WHERE id = ?", [cid]
        )
    self.db.execute(
        "DELETE FROM chunks WHERE file_path = ?",
        [file_path],
    )

L’instruction DELETE FROM chunk_vecs fonctionne nativement depuis sqlite-vec v0.1.7.26 Les versions antérieures nécessitaient des contournements (suppression et recréation de la table virtuelle, ou maintien d’un ensemble externe d’« identifiants actifs »). Si vous utilisez une version antérieure à 0.1.7, effectuez la mise à jour avant de compter sur les suppressions directes.

Les tables FTS5 synchronisées par contenu nécessitent une suppression explicite via INSERT INTO chunks_fts(chunks_fts, rowid, ...) VALUES('delete', ?, ...) pour chaque ligne retirée. L’indexeur gère cette opération dans le cadre du processus de suppression de fichier.


Réindexation incrémentale vs complète

L’indexeur prend en charge deux modes : incrémental (rapide, usage quotidien) et complet (lent, occasionnel). Cette section couvre les cas d’utilisation de chacun, les garanties d’idempotence et la récupération après corruption.

Réindexation incrémentale

Quand l’utiliser : indexation quotidienne après modification de notes. C’est le mode par défaut.

Fonctionnement : 1. Analyse du coffre-fort pour détecter les fichiers modifiés (comparaison des mtime) 2. Suppression des fragments pour les fichiers supprimés 3. Redécoupage et recalcul des embeddings pour les fichiers modifiés 4. Insertion de nouveaux fragments pour les fichiers ajoutés 5. Synchronisation de l’index FTS5

Durée typique : moins de 10 secondes pour les modifications d’une journée sur un coffre-fort de 16 000 fichiers.

python index_vault.py --incremental

Réindexation complète

Quand l’utiliser : - Après un changement de modèle d’embeddings (détection d’une incompatibilité du hash de modèle) - Après une migration de schéma (nouvelles colonnes, index modifiés) - Après une corruption de base de données (échec du contrôle d’intégrité) - Lorsque la réindexation incrémentale produit des résultats inattendus

Fonctionnement : 1. Suppression de toutes les données existantes (fragments, vecteurs, entrées FTS5) 2. Analyse de l’intégralité du coffre-fort 3. Découpage de tous les fichiers 4. Calcul des embeddings pour tous les fragments 5. Reconstruction de l’index FTS5 depuis zéro

Durée typique : environ 4 minutes pour 16 894 fichiers sur Apple M3 Pro.

python index_vault.py --full

Idempotence

Les deux modes sont idempotents : exécuter la même commande deux fois produit un résultat identique. L’indexeur supprime les fragments existants d’un fichier avant d’insérer les nouveaux ; une réexécution de l’indexation incrémentale sur une base déjà à jour ne génère aucune modification. Une réexécution de l’indexation complète produit une base de données identique.

Récupération après corruption

Si la base de données SQLite est corrompue (coupure de courant pendant une écriture, erreur disque, processus interrompu en cours de transaction) :

# Check integrity
sqlite3 vectors.db "PRAGMA integrity_check;"

# If corruption detected, full reindex rebuilds from source files
python index_vault.py --full

La source de vérité reste toujours les fichiers du coffre-fort, et non la base de données. Celle-ci n’est qu’un artéfact dérivé, reconstructible à tout moment. C’est une propriété architecturale essentielle : vous n’avez jamais besoin de sauvegarder la base de données.

L’option --incremental

Lorsque l’indexeur s’exécute avec --incremental :

  1. Vérification du hash de modèle. Comparaison du hash stocké avec le modèle en cours. En cas de divergence, basculement automatique vers une réindexation complète avec avertissement.
  2. Analyse des fichiers. Parcours des dossiers autorisés, collecte des chemins et des mtimes.
  3. Détection des changements. Comparaison avec les données enregistrées.
  4. Traitement par lots. Redécoupage et recalcul des embeddings par lots de 64 fichiers.
  5. Rapport de progression. Affichage du nombre de fichiers traités et du temps écoulé.
  6. Arrêt gracieux. Gestion du signal SIGINT en terminant le fichier en cours avant l’arrêt.

Filtrage des identifiants et périmètres de données

Les notes personnelles contiennent des secrets : clés API, jetons bearer, chaînes de connexion à des bases de données, clés privées collées lors de sessions de débogage. Le filtre d’identifiants empêche ces éléments d’entrer dans l’index de recherche.

Le problème

Une note sur le débogage d’une intégration OAuth pourrait contenir :

The token was: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
I used this curl command:
  curl -H "Authorization: Bearer sk-ant-api03-abc123..."

Sans filtrage, le JWT et la clé API seraient découpés, transformés en embeddings et stockés dans la base de données. Une recherche sur « authentification » renverrait le fragment contenant de véritables secrets. Pire encore, si le système de recherche transmet les résultats à un outil d’IA via MCP, les secrets apparaissent dans la fenêtre de contexte de l’IA et potentiellement dans les journaux de l’outil.

Filtrage par expressions régulières

Le filtre d’identifiants s’exécute sur chaque fragment avant stockage, en appliquant 25 expressions régulières spécifiques à des fournisseurs ainsi que des expressions génériques :

Expressions spécifiques aux fournisseurs :

Expression Exemple Regex
Clé API OpenAI sk-... sk-[a-zA-Z0-9_-]{20,}
Clé API Anthropic sk-ant-api03-... sk-ant-api\d{2}-[a-zA-Z0-9_-]{20,}
PAT GitHub ghp_... gh[ps]_[a-zA-Z0-9]{36,}
Clé d’accès AWS AKIA... AKIA[0-9A-Z]{16}
Clé Stripe sk_live_... [sr]k_(live\|test)_[a-zA-Z0-9]{24,}
Jeton Cloudflare ... Diverses expressions

Expressions génériques :

Expression Détection
Jetons JWT eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+
Jetons Bearer Bearer\s+[a-zA-Z0-9_\-\.]+
Clés privées -----BEGIN (RSA\|EC\|OPENSSH) PRIVATE KEY-----
Base64 à haute entropie Chaînes dépassant 4,5 bits/caractère d’entropie, 40+ caractères
Affectations de mot de passe password\s*[:=]\s*["'][^"']+["']

Implémentation du filtre

def clean_content(text):
    """Scrub credentials from text before indexing."""
    result = ScanResult(is_clean=True, match_count=0, patterns=[])

    for pattern in CREDENTIAL_PATTERNS:
        matches = pattern.regex.findall(text)
        if matches:
            text = pattern.regex.sub(
                f"[REDACTED:{pattern.name}]", text
            )
            result.is_clean = False
            result.match_count += len(matches)
            result.patterns.append(pattern.name)

    return text, result

Choix de conception essentiels :

  1. Filtrer avant le calcul des embeddings. C’est le texte nettoyé qui est transformé en vecteur. La représentation vectorielle n’encode jamais les identifiants. Une requête pour « clé API » renvoie les notes qui traitent de la gestion des clés API, et non celles qui contiennent des clés réelles.

  2. Remplacer, ne pas supprimer. Le jeton [REDACTED:pattern-name] préserve le contexte sémantique du texte environnant. L’embedding capture le fait qu’« un élément de type identifiant se trouvait ici » sans encoder l’identifiant lui-même.

  3. Journaliser les expressions, pas les valeurs. Le filtre consigne les expressions détectées (par exemple, « Scrubbed 2 credential(s) from oauth-debug.md [jwt, bearer-token] ») mais ne journalise jamais la valeur de l’identifiant.

Exclusion par chemin

Le fichier .indexignore offre une exclusion grossière par chemin. Le filtre d’identifiants assure un nettoyage fin à l’intérieur des fichiers indexés. Les deux mécanismes sont nécessaires :

  • .indexignore pour exclure des dossiers entiers dont le contenu est sensible (notes médicales, documents financiers, documents de carrière)
  • Le filtre d’identifiants pour les secrets accidentellement intégrés dans du contenu par ailleurs indexable

Classification des données

Pour les coffres-forts contenant du contenu varié, envisagez de classer les notes par niveau de sensibilité :

Niveau Exemples Indexer ? Filtrer ?
Public Brouillons de blog, notes techniques Oui Oui
Interne Plans de projet, décisions d’architecture Oui Oui
Sensible Données salariales, dossiers médicaux Non (.indexignore) N/A
Restreint Identifiants, clés privées Non (.indexignore) N/A

Architecture du serveur MCP

Le protocole Model Context Protocol (MCP) expose le système de recherche sous forme d’outil invocable par des agents IA via des serveurs dédiés. Cette section couvre la conception du serveur, la surface de fonctionnalités et les périmètres de permissions.

Choix du protocole : STDIO vs HTTP

MCP prend en charge deux modes de transport :

STDIO — L’outil IA lance le serveur MCP en tant que processus enfant et communique via stdin/stdout. C’est le mode standard pour les outils locaux. Claude Code, Codex CLI et Cursor prennent tous en charge les serveurs MCP STDIO.

{
  "mcpServers": {
    "obsidian": {
      "command": "python",
      "args": ["/path/to/obsidian_mcp.py"],
      "env": {
        "VAULT_PATH": "/path/to/vault",
        "DB_PATH": "/path/to/vectors.db"
      }
    }
  }
}

HTTP — Le serveur MCP fonctionne comme un service HTTP autonome. Utile pour l’accès distant, les configurations multi-clients ou les environnements d’équipe où le coffre-fort se trouve sur un serveur partagé.

{
  "mcpServers": {
    "obsidian": {
      "url": "http://localhost:3333/mcp"
    }
  }
}

Recommandation : Utilisez STDIO pour les coffres-forts personnels. C’est plus simple, plus sécurisé (aucune exposition réseau), et le cycle de vie du serveur est géré par l’outil IA. Réservez HTTP aux cas où plusieurs outils ou plusieurs machines nécessitent un accès simultané au même coffre-fort.

Évolution de la spécification MCP. La spécification MCP de juin 2025 a introduit l’autorisation OAuth 2.1, les sorties d’outils structurées (schémas de retour typés) et l’élicitation (invites utilisateur initiées par le serveur). La version de novembre 2025 a livré le transport Streamable HTTP en tant que mode de première classe, la découverte d’URL .well-known pour la navigation automatique des capacités serveur, les annotations d’outils structurées déclarant si un outil est en lecture seule ou modifiant, ainsi qu’un système de standardisation par niveaux SDK.1821 La prochaine version de la spécification (provisoirement mi-2026) propose des opérations asynchrones pour les tâches longues, des extensions de protocole spécifiques à certains secteurs comme la santé et la finance, et des standards de communication agent-à-agent pour les workflows multi-agents.21 Pour les serveurs de coffre-fort personnel, STDIO reste la voie la plus simple. Le transport Streamable HTTP et la découverte .well-known bénéficient principalement aux déploiements HTTP d’entreprise avec routage multi-locataire et répartition de charge. Surveillez la feuille de route MCP pour les mises à jour susceptibles d’influencer votre choix de transport.

Conception des fonctionnalités

Le serveur MCP devrait exposer un ensemble minimal d’outils :

search — L’outil principal. Exécute la recherche hybride et renvoie les résultats classés.

{
  "name": "obsidian_search",
  "description": "Search the Obsidian vault using hybrid BM25 + vector retrieval",
  "parameters": {
    "query": { "type": "string", "description": "Search query" },
    "limit": { "type": "integer", "default": 5 },
    "max_tokens": { "type": "integer", "default": 2000 }
  }
}

read_note — Lit le contenu intégral d’une note spécifique par son chemin. Utile lorsque l’agent souhaite consulter le contexte complet d’un résultat de recherche.

{
  "name": "obsidian_read_note",
  "description": "Read the full content of a note by file path",
  "parameters": {
    "file_path": { "type": "string", "description": "Relative path within vault" }
  }
}

list_notes — Liste les notes correspondant à un filtre (par dossier, tag, type ou plage de dates). Utile pour l’exploration lorsque l’agent ne dispose pas d’une requête spécifique.

{
  "name": "obsidian_list_notes",
  "description": "List notes matching filters",
  "parameters": {
    "folder": { "type": "string", "description": "Folder path within vault" },
    "tag": { "type": "string", "description": "Tag to filter by" },
    "limit": { "type": "integer", "default": 20 }
  }
}

get_context — Un outil pratique qui exécute une recherche et formate les résultats sous forme de bloc de contexte prêt à être injecté dans une conversation.

{
  "name": "obsidian_get_context",
  "description": "Get formatted context from vault for a topic",
  "parameters": {
    "topic": { "type": "string", "description": "Topic to get context for" },
    "max_tokens": { "type": "integer", "default": 2000 }
  }
}

Périmètres de permissions

Le serveur MCP doit appliquer des limites strictes :

  1. Lecture seule. Le serveur lit le coffre-fort et la base de données d’index. Il ne crée, ne modifie ni ne supprime de notes. Les opérations d’écriture (capture de nouvelles notes) sont gérées par des hooks ou des skills distincts, pas par le serveur MCP.

  2. Portée limitée au coffre-fort. Le serveur ne lit que les fichiers situés dans le chemin du coffre-fort configuré. Les tentatives de traversée de chemin (../../etc/passwd) doivent être rejetées.

  3. Filtrage des identifiants en sortie. Même si la base de données contient du contenu pré-filtré, appliquez le filtrage des identifiants en sortie comme mesure de défense en profondeur.

  4. Réponses limitées en tokens. Appliquez max_tokens sur toutes les réponses d’outils pour éviter que l’outil IA ne reçoive des blocs de contexte excessivement volumineux.

Gestion des erreurs

Les outils MCP doivent renvoyer des messages d’erreur structurés permettant à l’outil IA de récupérer :

def search(self, query, limit=5, max_tokens=2000):
    if not self.db_path.exists():
        return {
            "error": "Index database not found. Run the indexer first.",
            "suggestion": "python index_vault.py --full"
        }

    results = self.retriever.search(query, limit, max_tokens)

    if not results:
        return {
            "results": [],
            "message": f"No results found for '{query}'. Try broader terms."
        }

    return {
        "results": [
            {
                "file_path": r["file_path"],
                "section": r["section"],
                "text": r["chunk_text"],
                "score": round(r["rrf_score"], 4),
            }
            for r in results
        ],
        "count": len(results),
        "query": query,
    }

Intégration avec Claude Code

Claude Code est le principal consommateur du système de recherche Obsidian. Cette section couvre la configuration MCP, l’intégration des hooks et le pattern obsidian_bridge.py.

Configuration MCP

Ajoutez le serveur MCP Obsidian à ~/.claude/settings.json :

{
  "mcpServers": {
    "obsidian": {
      "command": "python",
      "args": ["/path/to/obsidian_mcp.py"],
      "env": {
        "VAULT_PATH": "/absolute/path/to/vault",
        "DB_PATH": "/absolute/path/to/vectors.db"
      }
    }
  }
}

Après avoir ajouté la configuration, redémarrez Claude Code. Le serveur MCP démarrera en tant que processus enfant. Vérifiez qu’il fonctionne :

> What tools do you have from the obsidian MCP server?

Claude Code devrait lister les outils disponibles (obsidian_search, obsidian_read_note, etc.).

Intégration des hooks

Les hooks étendent le comportement de Claude Code à des points définis de son cycle de vie. Deux hooks sont pertinents pour l’intégration Obsidian :

Hook PreToolUse — Interroge le coffre avant que l’agent ne traite un appel d’outil. Injecte automatiquement le contexte pertinent.

#!/bin/bash
# ~/.claude/hooks/pre-tool-use/obsidian-context.sh
# Automatically inject vault context before tool execution

TOOL_NAME="$1"
PROMPT="$2"

# Only inject context for code-related tools
case "$TOOL_NAME" in
    Edit|Write|Bash)
        # Query the vault
        CONTEXT=$(python /path/to/retriever.py search "$PROMPT" --limit 3 --max-tokens 1500)
        if [ -n "$CONTEXT" ]; then
            echo "---"
            echo "Relevant vault context:"
            echo "$CONTEXT"
            echo "---"
        fi
        ;;
esac

Hook PostToolUse — Capture les sorties significatives des outils dans le coffre pour une recherche ultérieure.

#!/bin/bash
# ~/.claude/hooks/post-tool-use/capture-insight.sh
# Capture significant outputs to vault (selective)

TOOL_NAME="$1"
OUTPUT="$2"

# Only capture substantial outputs
if [ ${#OUTPUT} -gt 500 ]; then
    python /path/to/capture.py --text "$OUTPUT" --source "claude-code-$TOOL_NAME"
fi

Le pattern obsidian_bridge.py

Un module de pont fournit une Python API que les hooks et les skills peuvent appeler :

# obsidian_bridge.py
from retriever import HybridRetriever

_retriever = None

def get_retriever():
    global _retriever
    if _retriever is None:
        _retriever = HybridRetriever(
            db_path="/path/to/vectors.db",
            vault_path="/path/to/vault",
        )
    return _retriever

def search_vault(query, limit=5, max_tokens=2000):
    """Search vault and return formatted context."""
    retriever = get_retriever()
    results = retriever.search(query, limit, max_tokens)

    if not results:
        return ""

    lines = ["## Vault Context\n"]
    for r in results:
        lines.append(f"**{r['file_path']}** — {r['section']}")
        lines.append(f"> {r['chunk_text'][:500]}")
        lines.append("")

    return "\n".join(lines)

Le skill /capture

Un skill Claude Code pour capturer des observations dans le coffre :

/capture "OAuth token rotation requires both access and refresh token invalidation"
  --domain security
  --tags oauth,tokens

Le skill crée une nouvelle note dans 00-inbox/ avec un frontmatter approprié et déclenche une réindexation incrémentale afin que la nouvelle note soit immédiatement recherchable.

Patterns de commandes personnalisées

Les skills Claude Code permettent d’encapsuler des opérations sur le coffre dans des commandes nommées. Des praticiens ont développé des bibliothèques de commandes spécifiques à Obsidian qui traitent le coffre à la fois comme source de lecture et comme cible d’écriture.

Veille stratégique. Une commande /scan-intel interroge des sources externes, évalue les résultats par rapport aux intérêts de recherche personnels et écrit les signaux qualifiants sous forme de notes dans le coffre avec frontmatter :

/scan-intel --topics "agent infrastructure, security" --lookback 7d

La commande récupère les données depuis des sources configurées (arXiv, HN, RSS), applique un modèle de scoring (pertinence, actionnabilité, profondeur, autorité) et écrit les signaux retenus dans des dossiers thématiques du coffre. Le coffre devient ainsi le consommateur en aval d’un pipeline de veille automatisé.

Journal de bord. Une commande /captains-log agrège l’activité git quotidienne de tous les dépôts, rédige une entrée de journal structurée dans le coffre, et inclut les décisions prises, les prises de conscience et les fils ouverts :

/captains-log

La commande extrait l’historique des commits depuis GitHub, regroupe par dépôt et formate le tout en entrée de journal narrative. Au fil du temps, ces journaux quotidiens constituent un historique consultable de ce qui a été livré et pourquoi.

Capture Obsidian. Une commande /obsidian-capture prend une observation de la session Claude Code en cours et l’écrit directement dans le coffre avec les métadonnées appropriées :

/obsidian-capture "SAST gates in agent loops increase security degradation"
  --folder AI-Tools --tags security,agents

Ce pattern s’étend à toute opération sur le coffre : création de MOCs, mise à jour des notes de suivi de projet, liaison entre signaux connexes ou génération de synthèses hebdomadaires à partir des journaux quotidiens accumulés.

Exemples de la communauté. Des praticiens publient leurs bibliothèques de commandes. Un développeur a partagé 22 commandes personnalisées Obsidian + Claude Code couvrant les revues quotidiennes, la planification de projets, la capture de recherches et les workflows de contenu.1 Un autre a créé un skill « Visual Explainer » qui génère des notes de diagrammes dans le coffre à partir d’analyses de code.2 Les commandes varient, mais l’architecture reste cohérente : les skills Claude Code comme interface, les notes du coffre comme couche de stockage, et l’infrastructure de recherche comme moteur de requêtes.

Gestion de la fenêtre de contexte

L’intégration doit être attentive à la fenêtre de contexte de Claude Code :

  • Limitez le contexte injecté à 1 500-2 000 tokens par requête. Au-delà, il entre en compétition avec la mémoire de travail de l’agent.
  • Incluez l’attribution des sources. Mentionnez toujours le chemin du fichier et le titre de la section afin que l’agent puisse référencer la source.
  • Tronquez le texte des fragments. Les fragments longs doivent être tronqués avec ... plutôt qu’omis entièrement. Les 300 à 500 premiers caractères contiennent généralement l’information essentielle.
  • N’injectez pas à chaque appel d’outil. Le hook PreToolUse doit injecter le contexte de manière sélective selon l’outil appelé. Les opérations de lecture n’ont pas besoin du contexte du coffre. Les opérations d’écriture et d’édition en bénéficient.

Intégration avec Codex CLI

Codex CLI se connecte aux serveurs MCP via config.toml. Le pattern d’intégration diffère de Claude Code par la syntaxe de configuration et la méthode de transmission des instructions.

Configuration MCP

Ajoutez à .codex/config.toml ou ~/.codex/config.toml :

[mcp_servers.obsidian]
command = "python"
args = ["/path/to/obsidian_mcp.py"]

[mcp_servers.obsidian.env]
VAULT_PATH = "/absolute/path/to/vault"
DB_PATH = "/absolute/path/to/vectors.db"

Patterns AGENTS.md

Codex CLI lit AGENTS.md pour les instructions au niveau du projet. Incluez des directives de recherche dans le coffre :

## Available Tools

### Obsidian Vault (MCP: obsidian)
Use the `obsidian_search` tool to find relevant context from the knowledge base.
Search the vault when you need:
- Background on a concept or pattern
- Prior decisions or rationale
- Reference material for implementation

Example queries:
- "authentication patterns in FastAPI"
- "how does the review aggregator work"
- "sqlite-vec configuration"

Différences avec Claude Code

Fonctionnalité Claude Code Codex CLI
Config MCP settings.json config.toml
Hooks ~/.claude/hooks/ Non pris en charge
Skills ~/.claude/skills/ Non pris en charge
Fichier d’instructions CLAUDE.md AGENTS.md
Modes d’approbation --dangerously-skip-permissions suggest / auto-edit / full-auto

Différence clé : Codex CLI ne prend pas en charge les hooks. Le pattern d’injection automatique de contexte (hook PreToolUse) n’est pas disponible. À la place, incluez des instructions explicites dans AGENTS.md indiquant à l’agent de rechercher dans le coffre avant de commencer le travail.


Cursor et autres outils

Cursor et d’autres outils d’IA prenant en charge MCP peuvent se connecter au même serveur MCP d’Obsidian. Cette section couvre la configuration des outils les plus courants.

Cursor

Ajoutez ceci au fichier .cursor/mcp.json à la racine de votre projet :

{
  "mcpServers": {
    "obsidian": {
      "command": "python",
      "args": ["/path/to/obsidian_mcp.py"],
      "env": {
        "VAULT_PATH": "/absolute/path/to/vault",
        "DB_PATH": "/absolute/path/to/vectors.db"
      }
    }
  }
}

Le fichier .cursorrules de Cursor peut inclure des instructions pour exploiter le coffre :

When working on implementation tasks, search the Obsidian vault
for relevant context before writing code. Use the obsidian_search
tool with descriptive queries about the concept you're implementing.

Matrice de compatibilité

Outil Support MCP Transport Emplacement de la config
Claude Code Complet STDIO ~/.claude/settings.json
Codex CLI Complet STDIO .codex/config.toml
Cursor Complet STDIO .cursor/mcp.json
Windsurf Complet STDIO .windsurf/mcp.json
Continue.dev Partiel HTTP ~/.continue/config.json
Zed En cours STDIO Interface des paramètres
Claudian (plugin Obsidian) N/A (intégré) Claude Code CLI Paramètres du plugin Obsidian
Agent Client (plugin Obsidian) N/A (intégré) ACP Paramètres du plugin Obsidian

Solution de repli pour les outils sans MCP

Pour les outils ne prenant pas en charge MCP, le système de recherche peut être encapsulé sous forme de CLI :

# Search from command line
python retriever_cli.py search "query text" --limit 5

# Output formatted for copy-paste into any tool
python retriever_cli.py context "query text" --format markdown

Le CLI produit du texte structuré que vous pouvez copier-coller manuellement dans n’importe quel outil d’IA. Moins élégant qu’une intégration MCP, mais universellement fonctionnel.


Mise en cache de prompts à partir de notes structurées

Les notes structurées du coffre peuvent servir de blocs de contexte réutilisables, réduisant la consommation de tokens lors des interactions avec l’IA. Cette section couvre la conception des clés de cache et la gestion du budget de tokens.

Le principe

Plutôt que de rechercher du contexte à chaque interaction, pré-construisez des blocs de contexte à partir de notes bien structurées du coffre, puis mettez-les en cache :

# cache_keys.py
CONTEXT_BLOCKS = {
    "auth-patterns": {
        "vault_query": "authentication patterns implementation",
        "max_tokens": 1500,
        "ttl_hours": 24,  # Rebuild daily
    },
    "api-conventions": {
        "vault_query": "API design conventions REST patterns",
        "max_tokens": 1000,
        "ttl_hours": 168,  # Rebuild weekly
    },
    "project-architecture": {
        "vault_query": "current project architecture decisions",
        "max_tokens": 2000,
        "ttl_hours": 12,  # Rebuild twice daily
    },
}

Invalidation du cache

L’invalidation du cache repose sur deux signaux :

  1. Expiration du TTL. Chaque bloc de contexte possède une durée de vie. À son expiration, le bloc est reconstruit en interrogeant à nouveau le coffre.
  2. Détection des modifications du coffre. Lorsque l’indexeur détecte des changements dans les fichiers ayant contribué à un bloc de contexte en cache, celui-ci est immédiatement invalidé.

Gestion du budget de tokens

Une session démarre avec un budget de contexte total. Les blocs en cache en consomment une partie :

Total context budget:    8,000 tokens
├─ System prompt:        1,500 tokens
├─ Cached blocks:        3,000 tokens (pre-loaded)
├─ Dynamic search:       2,000 tokens (on-demand)
└─ Conversation:         1,500 tokens (remaining)

Les blocs en cache se chargent au démarrage de la session. Les résultats de recherche dynamique remplissent le budget restant à chaque requête. Cette approche hybride fournit à l’agent une base de contexte fréquemment nécessaire tout en préservant du budget pour les requêtes spécifiques.

Avant/Après : consommation de tokens

Sans mise en cache : chaque requête pertinente déclenche une recherche dans le coffre, renvoyant 1 500 à 2 000 tokens de contexte. Sur 10 requêtes au cours d’une session, l’agent consomme 15 000 à 20 000 tokens de contexte issu du coffre.

Avec mise en cache : trois blocs de contexte pré-construits consomment 4 500 tokens au total. Les recherches supplémentaires ajoutent 1 500 à 2 000 tokens par requête unique. Sur 10 requêtes dont 6 sont couvertes par les blocs en cache, l’agent consomme 4 500 + (4 × 1 500) = 10 500 tokens — soit environ la moitié de la consommation sans cache.


Hooks PostToolUse pour la compression de contexte

Les sorties d’outils peuvent être verbeuses : traces d’erreurs, listes de fichiers, résultats de tests. Un hook PostToolUse peut comprimer ces sorties avant qu’elles ne consomment de l’espace dans la fenêtre de contexte.

Le problème

Un appel à l’outil Bash exécutant des tests pourrait renvoyer :

PASSED tests/test_auth.py::test_login_success
PASSED tests/test_auth.py::test_login_failure
PASSED tests/test_auth.py::test_token_refresh
PASSED tests/test_auth.py::test_session_expiry
... (200 more lines)
FAILED tests/test_api.py::test_rate_limit_exceeded

La sortie complète représente 5 000 tokens, mais le signal utile tient en 2 lignes : 200 réussis, 1 échoué.

Implémentation du hook

#!/bin/bash
# ~/.claude/hooks/post-tool-use/compress-output.sh
# Compress verbose tool outputs to preserve context window

TOOL_NAME="$1"
OUTPUT="$2"
OUTPUT_LEN=${#OUTPUT}

# Only compress large outputs
if [ "$OUTPUT_LEN" -lt 2000 ]; then
    exit 0  # Pass through unchanged
fi

case "$TOOL_NAME" in
    Bash)
        # Compress test output
        if echo "$OUTPUT" | grep -q "PASSED\|FAILED"; then
            PASSED=$(echo "$OUTPUT" | grep -c "PASSED")
            FAILED=$(echo "$OUTPUT" | grep -c "FAILED")
            FAILURES=$(echo "$OUTPUT" | grep "FAILED")
            echo "Tests: $PASSED passed, $FAILED failed"
            if [ "$FAILED" -gt 0 ]; then
                echo "Failures:"
                echo "$FAILURES"
            fi
        fi
        ;;
esac

Prévention des déclenchements récursifs

Un hook de compression émettant une sortie pourrait se déclencher lui-même s’il n’est pas protégé :

# Guard against recursive invocation
if [ -n "$COMPRESS_HOOK_ACTIVE" ]; then
    exit 0
fi
export COMPRESS_HOOK_ACTIVE=1

Heuristiques de compression

Type de sortie Détection Stratégie de compression
Résultats de tests Mots-clés PASSED / FAILED Compter réussites/échecs, afficher uniquement les échecs
Listes de fichiers ls ou find dans la commande Tronquer aux 20 premières entrées + total
Traces d’erreurs Mot-clé Traceback Conserver le premier et le dernier cadre + message d’erreur
Statut Git modified: / new file: Résumer les comptages par statut
Sortie de build warning: / error: Supprimer les lignes d’info, conserver avertissements/erreurs

Pipeline d’acquisition et de tri des signaux

La couche d’acquisition détermine ce qui entre dans le coffre. Sans curation, le coffre accumule du bruit. Cette section couvre le pipeline de scoring qui achemine les signaux vers les dossiers thématiques.

Sources

Les signaux proviennent de multiples canaux :

  • Flux RSS : blogs techniques, avis de sécurité, notes de version
  • Signets : favoris du navigateur enregistrés via Obsidian Web Clipper ou un bookmarklet
  • Newsletters : extraits clés de lettres d’information par e-mail
  • Capture manuelle : notes prises pendant la lecture, les conversations ou la recherche
  • Sorties d’outils : résultats significatifs d’outils d’IA capturés via des hooks
  • Extension de partage iOS : l’application iOS d’Obsidian (mise à jour début 2026) inclut une extension de partage qui enregistre du contenu depuis Safari, les réseaux sociaux et d’autres applications directement dans le coffre sans ouvrir Obsidian.31 Cela crée un chemin d’acquisition mobile à faible friction — partagez un article depuis Safari et il arrive sous forme de note dans le coffre, prêt à être évalué.
  • Obsidian CLI : des scripts shell et des hooks peuvent créer des notes via obsidian file create ou en ajouter à des notes existantes via obsidian file append, permettant des pipelines d’acquisition automatisés sur ordinateur.

Dimensions de scoring

Chaque signal est évalué selon quatre dimensions (de 0,0 à 1,0 chacune) :

Dimension Question Score faible (0,0-0,3) Score élevé (0,7-1,0)
Pertinence Est-ce lié à mes domaines actifs ? Tangentiel, hors périmètre Directement lié au travail en cours
Actionnabilité Puis-je exploiter cette information ? Théorie pure, aucune application Technique ou patron spécifique applicable
Profondeur Quelle est la substance du contenu ? Titres, résumé superficiel Analyse détaillée avec exemples
Autorité Quelle est la crédibilité de la source ? Blog anonyme, non vérifié Source primaire, revue par les pairs, expert reconnu

Score composite et routage

composite = (relevance * 0.35) + (actionability * 0.25) +
            (depth * 0.25) + (authority * 0.15)
Plage de score Action
0,55+ Routage automatique vers le dossier thématique
0,40 - 0,55 Mise en file d’attente pour revue manuelle
< 0,40 Rejet (ne pas stocker)

Routage par domaine

Les signaux dépassant 0,55 sont acheminés vers l’un des 12 dossiers thématiques selon la correspondance de mots-clés et la classification par sujet :

05-signals/
├── ai-tooling/        # Claude, LLMs, AI development tools
├── security/          # Vulnerabilities, auth, cryptography
├── systems/           # Architecture, distributed systems
├── programming/       # Languages, patterns, algorithms
├── web/               # Frontend, backends, APIs
├── data/              # Databases, data engineering
├── devops/            # CI/CD, containers, infrastructure
├── design/            # UI/UX, product design
├── mobile/            # iOS, Android, cross-platform
├── career/            # Industry trends, hiring, growth
├── research/          # Academic papers, whitepapers
└── other/             # Signals that don't fit a domain

Statistiques de production

Sur 14 mois d’exploitation :

Métrique Valeur
Total de signaux traités 7 771
Routés automatiquement (>0,55) 4 832 (62 %)
En attente de revue (0,40-0,55) 1 543 (20 %)
Rejetés (<0,40) 1 396 (18 %)
Dossiers thématiques actifs 12
Moyenne de signaux par jour ~18

Patterns de Knowledge Graph

Le graphe de wiki-link d’Obsidian encode les relations entre les notes. Cette section couvre la sémantique des liens, le parcours de graphe pour l’expansion contextuelle et les anti-patterns qui dégradent la qualité du graphe.

Chaque wiki-link crée une arête dirigée dans le graphe. Obsidian suit à la fois les liens sortants et les backlinks :

  • Lien sortant : La note A contient [[Note B]] → A pointe vers B
  • Backlink : La note B indique que la note A la référence

Le graphe encode différents types de relations selon le contexte :

Pattern de lien Sémantique Exemple
Lien en ligne « Est lié à » “See [[OAuth Token Rotation]] for details”
Lien vers un titre « A pour sous-sujet » ”## Related\n- [[Token Rotation]]\n- [[Session Management]]”
Lien de type tag « Est catégorisé comme » ”[[type/reference]]”
Lien MOC « Fait partie de » Une note Maps of Content listant les notes associées

Maps of Content (MOCs)

Les MOCs sont des notes d’index qui organisent les notes associées en une structure navigable :

---
title: "Authentication & Security MOC"
type: moc
domain: security
---

## Core Concepts
- [[OAuth 2.0 Overview]]
- [[JWT Token Anatomy]]
- [[Session Management Patterns]]

## Implementation Patterns
- [[OAuth Token Rotation]]
- [[Refresh Token Security]]
- [[PKCE Flow Implementation]]

## Failure Modes
- [[Token Expiry Handling]]
- [[Session Fixation Prevention]]
- [[CSRF Defense Strategies]]

Les MOCs bénéficient à la recherche de deux manières :

  1. Correspondance directe. Une recherche « authentication overview » correspond au MOC lui-même, fournissant à l’agent une liste organisée de notes associées.
  2. Expansion contextuelle. Après avoir trouvé une note spécifique, le système de recherche peut vérifier si la note apparaît dans des MOCs et inclure la structure du MOC dans les résultats, offrant à l’agent une cartographie du sujet plus large.

Parcours de graphe pour l’expansion contextuelle

Une amélioration future du système de recherche : après avoir trouvé les meilleurs résultats, enrichir le contexte en suivant les liens :

def expand_context(results, depth=1):
    """Follow wiki-links from top results to find related context."""
    expanded = set()
    for result in results:
        # Parse wiki-links from chunk text
        links = extract_wiki_links(result["chunk_text"])
        for link_target in links:
            # Resolve link to file path
            target_path = resolve_wiki_link(link_target)
            if target_path and target_path not in expanded:
                expanded.add(target_path)
                # Include target's most relevant chunk
                target_chunks = get_chunks_for_file(target_path)
                # ... rank and include best chunk
    return results + list(expanded_results)

Cette fonctionnalité n’est pas implémentée dans le système de recherche actuel, mais représente une extension naturelle de la structure de graphe.

Anti-patterns

Clusters orphelins. Groupes de notes qui se lient entre elles mais n’ont aucune connexion avec le reste du coffre-fort. Le panneau de graphe dans Obsidian rend ces îlots déconnectés visibles. Les clusters orphelins indiquent des MOCs manquants ou des liens inter-domaines absents.

Prolifération de tags. Utiliser des tags de manière incohérente ou créer trop de tags trop granulaires. Un coffre-fort avec 500 tags uniques répartis sur 5 000 notes donne en moyenne 1 note pour 10 tags — ces tags ne sont pas utiles pour le filtrage. Consolidez vers 20 à 50 tags de haut niveau correspondant à vos dossiers de domaine.

Notes riches en liens, pauvres en contenu. Notes constituées uniquement de wiki-links sans prose. Ces notes s’indexent mal car le système de découpage n’a aucun texte à convertir en embeddings. Ajoutez au minimum un paragraphe de contexte expliquant pourquoi les notes liées sont associées.

Liens bidirectionnels pour tout. Chaque référence ne nécessite pas un wiki-link. Mentionner « OAuth » en passant ne requiert pas [[OAuth 2.0 Overview]]. Réservez les wiki-links aux relations intentionnelles et navigables, où cliquer sur le lien apporterait un contexte utile.


Recettes de workflow développeur

Workflows pratiques combinant la recherche dans le coffre-fort avec les tâches de développement quotidiennes.

Chargement du contexte matinal

Commencez la journée en chargeant le contexte pertinent :

Search my vault for notes about [current project] updated in the last week

Le système de recherche renvoie les notes récentes concernant votre projet actif, vous offrant un rappel rapide de là où vous en étiez. Plus efficace que relire les messages de commit de la veille.

Capture de recherche pendant le développement

Lors de l’implémentation d’une fonctionnalité, capturez vos découvertes sans quitter l’éditeur :

/capture "FastAPI dependency injection with async generators requires yield,
not return. The generator is the dependency lifecycle."
  --domain programming
  --tags fastapi,dependency-injection

L’insight capturé est immédiatement indexé et disponible pour les recherches futures. Au fil des mois, ces micro-captures construisent un corpus de connaissances spécifiques à l’implémentation.

Lancement de projet

Au démarrage d’un nouveau projet ou d’une nouvelle fonctionnalité :

  1. Recherchez dans le coffre-fort : « Qu’est-ce que je sais sur [technologie/pattern] ? »
  2. Passez en revue les 5 meilleurs résultats pour les décisions antérieures et les pièges connus
  3. Vérifiez si un MOC existe pour le domaine ; sinon, créez-en un
  4. Recherchez les modes de défaillance : « problèmes avec [technologie] »

Débogage avec la recherche dans le coffre-fort

Face à une erreur ou un comportement inattendu :

Search my vault for [error message or symptom]

Les notes de débogage antérieures contiennent souvent la cause racine et la correction. Cette approche est particulièrement précieuse pour les problèmes récurrents entre projets — le coffre-fort se souvient de ce que vous oubliez.

Préparation de revue de code

Avant de passer en revue une PR :

Search my vault for patterns and conventions about [module being changed]

Le coffre-fort renvoie les décisions antérieures, les contraintes architecturales et les standards de code pertinents pour le code en cours de revue. La revue s’appuie ainsi sur la connaissance institutionnelle, pas uniquement sur le diff.


Optimisation des performances

Cette section couvre les stratégies d’optimisation pour différentes tailles de coffre-fort et différents patterns d’utilisation.

Gestion de la taille de l’index

Taille du coffre-fort Chunks Taille DB Réindexation complète Incrémentale
500 notes ~1 500 3 Mo 15 secondes <1 seconde
2 000 notes ~6 000 12 Mo 45 secondes 2 secondes
5 000 notes ~15 000 30 Mo 2 minutes 4 secondes
15 000 notes ~50 000 83 Mo 4 minutes <10 secondes
50 000 notes ~150 000 250 Mo 15 minutes 30 secondes

Au-delà de 50 000 notes, envisagez : - Augmenter le batch size de 64 à 128 pour accélérer la génération d’embeddings - Utiliser le mode WAL (par défaut) pour les accès concurrents - Lancer la réindexation complète en dehors des heures de pointe

Optimisation des requêtes

Mode WAL. Le mode Write-Ahead Logging de SQLite permet les lectures concurrentes pendant que l’indexeur écrit :

db.execute("PRAGMA journal_mode=WAL")

C’est essentiel lorsque le serveur MCP traite des requêtes pendant que l’indexeur effectue une mise à jour incrémentale.

Pool de connexions. Le serveur MCP devrait réutiliser les connexions à la base de données plutôt que d’en ouvrir une nouvelle par requête. Une connexion unique persistante avec le mode WAL prend en charge les lectures concurrentes.

# MCP server initialization
db = sqlite3.connect(DB_PATH, check_same_thread=False)
db.execute("PRAGMA journal_mode=WAL")
db.execute("PRAGMA mmap_size=268435456")  # 256 MB mmap

E/S mappées en mémoire. Le pragma mmap_size indique à SQLite d’utiliser les E/S mappées en mémoire pour le fichier de base de données. Pour une base de 83 Mo, mapper l’intégralité du fichier en mémoire élimine la plupart des lectures disque.

Optimisation FTS5. Après une réindexation complète, exécutez :

INSERT INTO chunks_fts(chunks_fts) VALUES('optimize');

Cette opération fusionne les segments internes du b-tree de FTS5, réduisant la latence des requêtes pour les recherches ultérieures.

Benchmarks de mise à l’échelle

Mesurés sur Apple M3 Pro, 36 Go de RAM, SSD NVMe :

Opération 500 notes 5K notes 15K notes 50K notes
Requête BM25 2 ms 5 ms 12 ms 25 ms
Requête vectorielle 1 ms 3 ms 8 ms 20 ms
Fusion RRF <1 ms <1 ms 3 ms 5 ms
Recherche complète 3 ms 8 ms 23 ms 50 ms

L’ensemble des benchmarks inclut l’accès à la base de données, l’exécution de la requête et le formatage des résultats. La latence réseau pour la communication MCP STDIO ajoute 1 à 2 ms.

Dépannage

Dérive de l’index

Symptôme : La recherche renvoie des résultats obsolètes ou ne retrouve pas les notes récemment ajoutées.

Cause : L’indexeur incrémental ne s’est pas exécuté après l’ajout de notes, ou le mtime d’un fichier n’a pas été mis à jour (par exemple, fichier synchronisé depuis une autre machine avec horodatages préservés).

Correction : Lancez une réindexation complète : python index_vault.py --full

Changement de modèle d’embeddings

Symptôme : Après avoir changé le modèle d’embeddings, la recherche vectorielle renvoie des résultats incohérents.

Cause : Les anciens vecteurs (issus du modèle précédent) sont comparés aux nouveaux vecteurs de requête. Les dimensions ou la sémantique de l’espace vectoriel sont incompatibles.

Correction : L’indexeur devrait détecter l’incohérence du hash de modèle et déclencher automatiquement une réindexation complète. Si ce n’est pas le cas, videz manuellement la base de données et réindexez :

rm vectors.db
python index_vault.py --full

Maintenance FTS5

Symptôme : Les requêtes FTS5 renvoient des résultats incorrects ou incomplets après de nombreuses mises à jour incrémentales.

Cause : Les segments internes de FTS5 peuvent se fragmenter après de nombreuses petites mises à jour.

Correction : Reconstruisez et optimisez :

INSERT INTO chunks_fts(chunks_fts) VALUES('rebuild');
INSERT INTO chunks_fts(chunks_fts) VALUES('optimize');

Délai d’expiration MCP

Symptôme : L’outil d’IA signale que le serveur MCP a expiré.

Cause : La première requête déclenche le chargement du modèle (initialisation paresseuse), ce qui prend 2 à 5 secondes. Le délai d’expiration MCP par défaut de l’outil d’IA peut être plus court.

Correction : Préchauffez le modèle au démarrage du serveur :

# In MCP server initialization
retriever = HybridRetriever(db_path, vault_path)
retriever.search("warmup", limit=1)  # Trigger model load

Verrouillage de fichiers SQLite

Symptôme : Erreurs SQLITE_BUSY ou SQLITE_LOCKED.

Cause : Plusieurs processus écrivent simultanément dans la base de données. Le mode WAL autorise les lectures concurrentes, mais un seul processus peut écrire à la fois.

Correction : Assurez-vous qu’un seul processus (l’indexeur) écrit dans la base de données. Le serveur MCP et les hooks ne doivent effectuer que des lectures. Si vous avez besoin d’écritures concurrentes, utilisez le mode WAL et définissez un délai d’attente :

db.execute("PRAGMA busy_timeout=5000")  # Wait up to 5 seconds

sqlite-vec ne se charge pas

Symptôme : La recherche vectorielle est désactivée ; le moteur de recherche fonctionne en mode BM25 uniquement.

Cause : L’extension sqlite-vec n’est pas installée, introuvable dans le chemin des bibliothèques, ou incompatible avec la version de SQLite.

Correction :

# Install via pip
pip install sqlite-vec

# Or compile from source
git clone https://github.com/asg017/sqlite-vec
cd sqlite-vec && make

Vérifiez que l’extension se charge correctement :

import sqlite3
db = sqlite3.connect(":memory:")
db.enable_load_extension(True)
db.load_extension("vec0")
print("sqlite-vec loaded successfully")

Problèmes de mémoire avec les grands coffres

Symptôme : Erreurs de dépassement de mémoire lors de la réindexation complète d’un grand coffre (plus de 50 000 notes).

Cause : La taille des lots d’embeddings est trop importante, ou l’ensemble du contenu des fichiers est chargé en mémoire simultanément.

Correction : Réduisez la taille des lots et traitez les fichiers de manière incrémentale :

BATCH_SIZE = 32  # Reduce from 64

Assurez-vous également que l’indexeur traite les fichiers un par un (lecture, découpage et génération d’embeddings pour chaque fichier avant de passer au suivant) plutôt que de charger tous les fichiers en mémoire.


Guide de migration

Depuis Apple Notes

  1. Exportez Apple Notes via l’option « Tout exporter » (macOS) ou utilisez un outil de migration comme apple-notes-liberator
  2. Convertissez les exports HTML en markdown avec markdownify ou pandoc
  3. Déplacez les fichiers convertis dans le dossier 00-inbox/ de votre coffre
  4. Vérifiez chaque note et ajoutez le frontmatter
  5. Déplacez les notes dans les dossiers de domaine appropriés

Depuis Notion

  1. Exportez depuis Notion : Paramètres → Exporter → Markdown & CSV
  2. Décompressez l’export dans le dossier 00-inbox/ de votre coffre
  3. Corrigez les artefacts markdown propres à Notion :
  4. Notion utilise - [ ] pour les listes de contrôle — il s’agit de markdown standard
  5. Notion inclut des tableaux de propriétés en HTML — convertissez-les en frontmatter YAML
  6. Notion intègre les images via des chemins relatifs — copiez les images dans votre dossier de pièces jointes
  7. Ajoutez le frontmatter standard (type, domain, tags)
  8. Remplacez les liens de pages Notion par des wiki-links Obsidian

Depuis Google Docs

  1. Utilisez Google Takeout pour exporter tous les documents
  2. Convertissez les fichiers .docx en markdown : pandoc -f docx -t markdown input.docx -o output.md
  3. Conversion par lots : for f in *.docx; do pandoc -f docx -t markdown "$f" -o "${f%.docx}.md"; done
  4. Déplacez les fichiers dans le coffre, ajoutez le frontmatter, organisez en dossiers

Depuis du markdown brut (sans Obsidian)

Si vous disposez déjà d’un répertoire de fichiers markdown :

  1. Ouvrez le répertoire en tant que coffre Obsidian (Obsidian → Ouvrir un coffre → Ouvrir un dossier)
  2. Ajoutez .obsidian/ à .gitignore si le répertoire est versionné
  3. Créez des modèles de frontmatter et appliquez-les aux fichiers existants
  4. Commencez à relier les notes avec des [[wiki-links]] au fil de votre lecture et de votre organisation
  5. Lancez l’indexeur immédiatement — le système de recherche fonctionne dès le premier jour

Depuis un autre système de recherche

Si vous migrez depuis un système d’embeddings/recherche différent :

  1. Ne tentez pas de migrer les vecteurs. Des modèles différents produisent des espaces vectoriels incompatibles. Effectuez une réindexation complète avec le nouveau modèle.
  2. Migrez le contenu, pas l’index. Les fichiers du coffre sont la source de vérité. L’index est un artefact dérivé.
  3. Vérifiez après la migration. Exécutez 10 à 20 requêtes dont vous connaissez les réponses et vérifiez que les résultats correspondent à vos attentes.

Journal des modifications

Date Modification
2026-04-01 Ajout de la section Obsidian CLI (commandes v1.12 pour les flux de travail IA). Ajout de la section plugins d’agents (Claudian, Agent Client). Documentation du plugin natif Bases pour l’organisation du coffre. Mise à jour du nombre de plugins à 2 500+. Ajout de l’extension de partage iOS comme source d’entrée. Mise à jour de la matrice de compatibilité avec les plugins d’agents intégrés.
2026-03-30 MCPVault v0.11.0 : outil list_all_tags, prise en charge .base/.canvas, renommé en @bitbonsai/mcpvault. Obsidian Desktop v1.12.7 intègre le binaire CLI pour des interactions terminal plus rapides.
2026-03-23 Documentation de sqlite-vec v0.1.7 stable : prise en charge du DELETE pour les tables vec0, contraintes de distance KNN pour la pagination. Index DiskANN de type approximate nearest neighbor annoncé pour une prochaine version.
2026-03-07 Ajout de potion-multilingual-128M (101 langues, mai 2025) à la comparaison des modèles d’embeddings. sqlite-vec à la version v0.1.7-alpha.10 (correctifs CI/CD, aucun changement fonctionnel). Spécification MCP et techniques de recherche confirmées à jour.
2026-03-03 Mise à jour de l’évolution de la spécification MCP (novembre 2025 livré : Streamable HTTP, .well-known, annotations d’outils). Ajout du fine-tuning Model2Vec et de la prise en charge du tokenizer BPE/Unigram. Ajout du tableau comparatif des serveurs MCP communautaires. Mise à jour de Smart Connections vers la v4.
2026-03-02 Ajout de potion-base-32M et potion-retrieval-32M à la comparaison des modèles. Ajout de la section quantification/réduction de dimensionnalité. Ajout d’une note sur l’évolution de la spécification MCP.
2026-03-01 Publication initiale

Références


  1. Internet Vin, « 22 commands I use with Obsidian and Claude Code », mars 2026, x.com/internetvin/status/2026461256677245131

  2. Nicopreme, skill d’agent « Visual Explainer » avec commandes slash, x.com/nicopreme/status/2023495040258261460

  3. Cormack, G.V., Clarke, C.L.A., et Buettcher, S. Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods. SIGIR, 2009. Introduit RRF avec k=60 comme méthode sans paramètre pour combiner des listes classées. 

  4. OpenAI Embeddings Pricing. text-embedding-3-small : 0,02 $ par million de tokens. Coût estimé du coffre-fort par réindexation complète : ~0,30 $. 

  5. van Dongen, T. et al. Model2Vec: Turn any Sentence Transformer into a Small Fast Model. arXiv, 2025. Décrit l’approche de distillation produisant des embeddings statiques à partir de transformeurs de phrases. 

  6. MTEB: Massive Text Embedding Benchmark. potion-base-8M obtient un score moyen de 50,03 contre 56,09 pour all-MiniLM-L6-v2 (89 % de rétention). 

  7. SQLite FTS5 Extension. FTS5 fournit la recherche en texte intégral avec classement BM25 et pondérations de colonnes configurables. 

  8. sqlite-vec: A vector search SQLite extension. Fournit des tables virtuelles vec0 pour la recherche vectorielle KNN au sein de SQLite. 

  9. Robertson, S. et Zaragoza, H. The Probabilistic Relevance Framework: BM25 and Beyond. Foundations and Trends in Information Retrieval, 2009. 

  10. Karpukhin, V. et al. Dense Passage Retrieval for Open-Domain Question Answering. EMNLP, 2020. Les représentations denses surpassent BM25 de 9 à 19 % sur les questions ouvertes. 

  11. Reimers, N. et Gurevych, I. Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks. EMNLP, 2019. Travaux fondateurs sur la similarité sémantique dense. 

  12. Luan, Y. et al. Sparse, Dense, and Attentional Representations for Text Retrieval. TACL, 2021. La recherche hybride (hybrid retrieval) surpasse systématiquement les approches à modalité unique sur MS MARCO. 

  13. SQLite Write-Ahead Logging. Le mode WAL pour des lectures concurrentes avec un seul écrivain. 

  14. Gao, Y. et al. Retrieval-Augmented Generation for Large Language Models: A Survey. arXiv, 2024. Panorama des architectures RAG et des stratégies de découpage (chunking). 

  15. Thakur, N. et al. BEIR: A Heterogeneous Benchmark for Zero-shot Evaluation of Information Retrieval Models. NeurIPS, 2021. 

  16. Model2Vec: Distill a Small Fast Model from any Sentence Transformer. Minish Lab, 2024. 

  17. Obsidian Documentation. Documentation officielle d’Obsidian. 

  18. Model Context Protocol Specification. Le standard MCP pour connecter les outils d’IA aux sources de données. 

  19. Données de production de l’auteur. 16 894 fichiers, 49 746 fragments, 83,56 Mo de base de données SQLite, 7 771 signaux traités sur 14 mois. Latence des requêtes mesurée via time.perf_counter()

  20. Model2Vec Potion Models. Minish Lab, 2025. Potion-base-32M (MTEB 52,46), potion-retrieval-32M (MTEB retrieval 36,35), et fonctionnalités de quantification/réduction de dimensionnalité à partir de la v0.5.0+. 

  21. Update on the Next MCP Protocol Release. La version de novembre 2025 a introduit le transport Streamable HTTP, la découverte par URL .well-known, les annotations structurées d’outils et la standardisation des niveaux SDK. La prochaine version est provisoirement prévue pour mi-2026, avec les opérations asynchrones, les extensions spécifiques aux domaines et la communication entre agents. 

  22. Model2Vec Releases. v0.4.0 (fév. 2025) : support de l’entraînement/fine-tuning. v0.5.0 (avr. 2025) : réécriture du backend, quantification, réduction de dimensionnalité. v0.7.0 (oct. 2025) : quantification du vocabulaire, support des tokenizers BPE/Unigram. 

  23. Smart Connections for Obsidian. Smart Connections v4 : embeddings IA en local, la recherche sémantique fonctionne hors ligne après l’indexation initiale. 

  24. potion-multilingual-128M. Minish Lab, mai 2025. Modèle d’embeddings statiques couvrant 101 langues, les embeddings statiques multilingues les plus performants. Même dépendance numpy uniquement que les autres modèles potion. 

  25. MCPVault v0.11.0. Mars 2026. Nouvel outil list_all_tags pour scanner les tags frontmatter et hashtags avec compteurs. Amélioration de la gestion des dossiers préfixés par un point, support des fichiers .base et .canvas. Paquet renommé @bitbonsai/mcpvault sur npm. 

  26. sqlite-vec v0.1.7 Release. 17 mars 2026. Version stable : support de DELETE pour les tables virtuelles vec0, contraintes de distance KNN pour la pagination, améliorations du fuzz testing. Indexation par plus proches voisins approximatifs DiskANN annoncée pour une version future. 

  27. Introduction to Bases. Plugin principal d’Obsidian introduit dans la v1.9.10. Vues de type base de données (tableaux, galeries, calendriers, tableaux kanban) sur les fichiers du coffre-fort, utilisant les propriétés frontmatter comme champs. Fichiers enregistrés au format .base

  28. Obsidian 1.12 Desktop Changelog. 27 février 2026. Introduit le CLI d’Obsidian pour l’automatisation du coffre-fort en ligne de commande. Les commandes incluent la recherche, les notes quotidiennes, les modèles, les propriétés, les plugins, les tâches et les outils de développement. Documentation du CLI

  29. Claudian. Plugin Obsidian qui intègre Claude Code comme collaborateur IA dans le coffre-fort. Offre un chat en barre latérale, des invites contextuelles, le support de la vision, des commandes slash et des modes de permissions. 

  30. Agent Client. Plugin Obsidian fournissant une interface unifiée pour Claude Code, Codex CLI et Gemini CLI via l’Agent Client Protocol (ACP). Supporte les mentions de notes, l’exécution shell et l’approbation d’actions. 

  31. Obsidian iOS Changelog. Les mises à jour début 2026 incluent la Share Extension pour enregistrer du contenu depuis d’autres applications directement dans le coffre-fort, des corrections du widget Note quotidienne et Marque-pages, et des améliorations du rafraîchissement du widget Voir la note. 

VAULT obsidian.md INDEXED