obsidian:~/vault$ search --hybrid obsidian

Obsidian MCP + récupération hybride : référence 2026

# Utilisez la documentation officielle d’Obsidian pour les bases de l’application ; utilisez la référence de Blake pour MCP, la recherche hybride et l’indexation d’un coffre IA de 16 894 fichiers.

words: 16585 read_time: 83m updated: 2026-05-29 09:56
$ retriever search --hybrid obsidian

Obsidian n’est pas une application de prise de notes. C’est un corpus Markdown en texte brut, local-first et structuré en graphe, qui devient un réservoir de contexte pour l’IA dès que vous lui ajoutez une infrastructure de recherche. 16 894 fichiers. 49 746 chunks. Requêtes en 23 ms. Zéro appel à API. Un fichier SQLite de 83 Mo. Ce guide couvre le système complet : de l’architecture du coffre Obsidian au hybrid retrieval, puis à l’intégration MCP et aux workflows opérationnels.


Points clés

Ingénierie de contexte, pas prise de notes. La valeur d’un coffre 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 de 16 000 fichiers sans récupération est une base de données en écriture seule. Un coffre de 200 fichiers avec recherche hybrid et intégration MCP est 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 hybrid surpasse la recherche purement par mots-clés ou purement sémantique. BM25 repère les identifiants exacts et les noms de fonctions. La recherche vectorielle repère les synonymes et les correspondances conceptuelles entre terminologies différentes. Reciprocal Rank Fusion (RRF) fusionne les deux sans exiger d’étalonnage des scores. Aucune des deux méthodes ne couvre seule ces deux modes d’échec. Les recherches sur le classement des passages MS MARCO confirment ce schéma : la récupération hybrid surpasse systématiquement chaque méthode prise isolément.3 L’analyse approfondie du récupérateur hybrid couvre les calculs de RRF, des exemples détaillés avec de vrais chiffres, l’analyse des modes d’échec et un calculateur interactif de fusion.

MCP donne aux outils d’IA un accès direct au coffre. Les serveurs Model Context Protocol (MCP) exposent le récupérateur comme un outil que Claude Code, Codex CLI, Cursor et d’autres outils d’IA peuvent appeler directement. L’agent interroge le coffre, 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 enveloppe autour du moteur de récupération.

Le local-first signifie zéro coût API et une confidentialité totale. Toute la pile fonctionne 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. La réintégration complète de 49 746 fragments coûterait environ 0,30 $ aux tarifs API d’OpenAI, mais les vrais coûts sont la latence, l’exposition de la confidentialité 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 redécoupés et réintégrés. Une réindexation complète prend environ quatre minutes sur du matériel Apple M-series. Les mises à jour incrémentales des modifications d’une journée typique s’exécutent en moins de dix secondes. Le système reste à jour sans intervention manuelle.

L’architecture passe de 200 à plus de 20 000 notes. La même conception en trois couches (ingestion, récupération, intégration) fonctionne quelle que soit la taille du coffre. Commencez par une recherche BM25 seule sur un petit coffre. 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 utile indépendamment et peut être retirée indépendamment.


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
Vous débutez avec Obsidian + IA Pourquoi Obsidian pour l’infrastructure IA, Démarrage rapide Architecture du coffre, Architecture du serveur MCP
Vous avez déjà un coffre et voulez un accès IA Architecture du serveur MCP, Intégration Claude Code Modèles d’embeddings, Recherche en texte intégral
Vous construisez un système de récupération Pipeline de récupération complet, Reciprocal Rank Fusion Optimisation des performances, Dépannage
Vous travaillez en contexte d’équipe ou d’entreprise Cadre de décision, Schémas de graphes de connaissances Recettes de workflow développeur, Guide de migration

Les sections marquées Contract incluent les détails d’implémentation, les blocs de configuration et les modes d’échec. Les sections marquées Narrative se concentrent sur les concepts, les décisions d’architecture et le raisonnement derrière les choix de conception. Les sections marquées Recipe proposent des workflows étape par étape.


Pourquoi Obsidian pour l’infrastructure IA

La thèse de ce guide : les coffres 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’apportent pas

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, aucun API requis pour lire le contenu. Tout outil capable de lire des fichiers peut lire votre coffre. 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 reste toujours cohérent avec la source, car la source est le système de fichiers.

Architecture local-first. Le coffre vit sur votre machine. Aucun serveur, aucune dépendance à une synchronisation cloud, aucune limite de débit API, aucune condition d’utilisation dictant comment vous traitez votre propre contenu. Vous pouvez intégrer, indexer, découper et rechercher vos notes sans service externe. C’est important pour l’infrastructure IA, car le pipeline de récupération s’exécute à la vitesse permise par votre disque, pas à celle d’un endpoint API. C’est aussi 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.

Structure en graphe grâce aux wiki-links. La syntaxe [[wiki-link]] d’Obsidian crée un graphe dirigé entre les notes. Une note sur l’implémentation OAuth renvoie à des notes sur la rotation des tokens, la gestion des sessions et la sécurité API. La structure en graphe encode des relations entre concepts choisies par un 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.

Écosystème de plugins. Obsidian compte plus de 2 500 plugins communautaires (en mars 2026, contre plus de 1 800 mi-2025). Dataview interroge votre coffre comme une base de données. Templater génère des notes à partir de modèles avec de la logique JavaScript. L’intégration Git synchronise votre coffre vers un dépôt. Linter impose une cohérence de formatage. Le plugin cœur 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, en utilisant les propriétés frontmatter comme champs et en les enregistrant sous forme de fichiers .base.15 Ces plugins ajoutent de la structure au coffre 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 dispose d’une grande communauté active qui produit des modèles, des workflows, des plugins et de la documentation. Lorsque vous rencontrez un problème d’organisation de coffre ou de configuration de plugin, quelqu’un a probablement documenté une solution. La communauté produit aussi des outils adjacents à Obsidian : serveurs MCP, scripts d’indexation, pipelines de publication et enveloppes API.

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

Un dossier de fichiers markdown présente l’avantage du texte brut, mais il lui manque trois choses qu’Obsidian ajoute :

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

  2. Aperçu en direct avec rendu des plugins. Les requêtes Dataview, les diagrammes Mermaid et les blocs de callout 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 en texte brut. Vous écrivez et organisez dans un environnement riche ; le système de récupération indexe le markdown brut.

  3. Infrastructure communautaire. Découverte de plugins, marketplace de thèmes, service de synchronisation (facultatif), service de publication (facultatif) et é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 propose une recherche de base (texte intégral, 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 des identifiants, pas de stratégie de chunking et pas de hooks d’intégration pour les outils d’IA externes. Ce guide couvre l’infrastructure que vous construisez au-dessus d’Obsidian. Le coffre est le substrat. Le pipeline de récupération, le serveur MCP et les hooks d’intégration constituent l’infrastructure.

L’architecture décrite ici est markdown-first, pas exclusive à Obsidian. Si vous utilisez Logseq, Foam, Dendron ou un simple dossier de fichiers markdown, le pipeline de récupération fonctionne de la même manière. 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 propres à Obsidian. La contribution d’Obsidian est l’environnement d’écriture et d’organisation qui produit les fichiers markdown indexés par le récupérateur.


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

Cette section connecte 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 expliquent comment créer 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 limitées à MCP)
  • 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 dont vous vous souviendrez : le serveur MCP a besoin du chemin absolu.

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

Ajoutez quelques notes pour fournir du contenu au moteur de récupération. Même 10 à 20 notes suffisent pour voir des résultats. Chaque note doit être un fichier .md avec un titre explicite et au moins un paragraphe de contenu.

Étape 2 : installer un serveur MCP

Plusieurs serveurs MCP communautaires donnent un accès immédiat au coffre. L’écosystème a beaucoup évolué en 2025-2026. Parmi les mises à jour récentes notables, MCPVault v0.11.0 (mars 2026) a ajouté list_all_tags pour analyser le frontmatter et les hashtags avec leurs décomptes, amélioré la gestion des dossiers à points et ajouté la prise en charge des fichiers .base et .canvas.13 Le package a également été renommé @bitbonsai/mcpvault sur npm.

Virage d’avril 2026 — Obsidian CLI comme pont privilégié : Obsidian 1.12.0 a introduit CLI en tant que fonctionnalité de premier plan, et l’installateur public 1.12.7 (23 mars 2026) a intégré le binaire autonome, la TUI et les améliorations de fichier socket qui ont facilité l’installation et l’exécution des workflows de terminal.16 Les outils communautaires migrent activement du plugin Local REST API (qui alimentait mcp-obsidian) vers une intégration fondée sur CLI, car elle est plus rapide et plus stable. Le dépôt MarkusPfundstein/mcp-obsidian n’a eu aucun commit depuis juin 2025 et aucune release taguée : considérez-le comme en mode maintenance et privilégiez les serveurs basés sur CLI ou les alternatives communautaires plus récentes listées ci-dessous.20 Consultez la section « Obsidian CLI pour les workflows d’IA » plus loin dans ce guide pour la configuration recommandée.

Serveur Auteur Transport Plugin requis Fonctionnalité clé
obsidian-mcp-server StevenStavrakis STDIO Non Léger, basé sur les fichiers
mcp-obsidian MarkusPfundstein STDIO Local REST API CRUD complet du coffre via REST — mode maintenance, aucun commit depuis juin 202520
obsidian-mcp-tools jacksteamdev STDIO Oui (plugin) Recherche sémantique + Templater
obsidian-claude-code-mcp iansinnott WebSocket Oui (plugin) Découverte automatique pour Claude Code
obsidian-mcp-server cyanheads STDIO Local REST API Gestion des tags et du frontmatter
Hybrid Search MCP communauté STDIO Non Serveur MCP avec BM25 + recherche sémantique + CLI. Nouveau et activement maintenu en avril 2026.

Pour ce 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 ceci à ~/.claude/settings.json :

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

Codex CLI — ajoutez ceci à .codex/config.toml :

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

Cursor — ajoutez ceci à .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 les notes de votre coffre peuvent répondre :

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

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

Ce que vous venez de créer

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

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

Le reste de ce guide explique comment créer chacune de ces capacités. Le démarrage rapide prouve le concept. Le pipeline complet fournit une récupération de qualité production.


Obsidian CLI pour les workflows d’IA

Obsidian 1.12 (février 2026) a introduit une interface de ligne de commande intégrée qui ouvre une nouvelle surface d’intégration pour les workflows d’IA.16 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-la dans Settings > General > Command line interface.

Pourquoi CLI compte pour l’infrastructure d’IA

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

  • 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 leur contexte environnant, ce qui est utile pour alimenter des prompts d’IA.
  • Automatisation des notes quotidiennes. obsidian daily ouvre ou crée la note quotidienne du jour. Combiné à des scripts shell, cela permet des workflows automatisés de briefing quotidien : 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 Templater ou de modèles natifs, ce qui permet aux agents d’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, ce qui permet de mettre à jour les métadonnées depuis des scripts sans analyser YAML.
  • Contrôle des plugins. obsidian plugin enable/disable/list gère les plugins de manière programmatique, ce qui est utile pour activer ou désactiver des plugins d’indexation pendant des 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 d’IA qui gèrent des éléments de travail dans le coffre.

CLI vs MCP pour l’accès IA

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

Aspect Obsidian CLI Serveur MCP
Appelant Scripts shell, hooks, tâches cron Agents d’IA (Claude Code, Codex, Cursor)
Protocole Processus POSIX (stdin/stdout/stderr) MCP (JSON-RPC via STDIO ou HTTP)
Point fort Opérations natives d’Obsidian (modèles, plugins, propriétés) Récupération personnalisée (embeddings, BM25, fusion RRF)
Limite 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 d’IA en temps réel pendant les sessions

Recommandation : utilisez CLI pour l’automatisation de l’ingestion (création de notes, gestion des propriétés, exécution de recherches natives Obsidian) et MCP pour la récupération (recherche hybrid avec embeddings). Un hook PreToolUse peut appeler obsidian search:context comme pré-vérification rapide avant de basculer vers le récupérateur MCP complet pour obtenir des résultats classés.

Exemple : hook d’ingestion alimenté par 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 d’agents 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 externe d’un serveur MCP. Ces plugins exécutent l’agent IA dans la barre latérale d’Obsidian au lieu de se connecter depuis un outil externe.

Claudian

Claudian intègre Claude Code comme collaborateur IA dans le coffre. Le dossier du coffre devient le répertoire de travail de Claude, ce qui lui donne des capacités agentiques complètes : lecture/écriture de fichiers, recherche, commandes bash et workflows en plusieurs étapes.17

Fonctionnalités clés pour l’infrastructure IA : - Prompts sensibles au contexte. Joint automatiquement la note active, prend en charge les mentions de fichiers @notename, l’exclusion par tags et la sélection dans l’éditeur comme contexte. - Prise en charge de la vision. Analyse les images par glisser-déposer, collage ou chemin de fichier — utile pour traiter les captures d’écran et les diagrammes enregistrés dans le coffre. - Commandes slash. Créez des modèles de prompts réutilisables déclenchés par /command, ce qui permet de standardiser les opérations dans le coffre. - Modes d’autorisation. Modes YOLO (approbation automatique), Safe (approbation de chaque action) et Plan (plan uniquement), avec liste de blocage de sécurité et 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 l’Agent Client Protocol (ACP).18

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

Quand utiliser des plugins d’agents plutôt qu’un MCP externe

Scénario Plugin d’agent MCP externe
Rédaction et modification de notes du coffre avec assistance IA Mieux — l’agent voit le contexte de l’éditeur Fonctionne, mais sans conscience de l’éditeur
Développement de code sur plusieurs repos Limité — restreint au coffre Mieux — restreint au projet, avec accès complet au système de fichiers
Retrieval à partir d’un grand corpus indexé Recherche de base uniquement Pipeline complet de retrieval hybrid
Questions-réponses rapides sur le coffre pendant les sessions de prise de notes Idéal — pas de changement de contexte Nécessite de passer au terminal

Recommandation : utilisez les plugins d’agents pour les workflows centrés sur le coffre (rédaction, organisation, synthèse de notes). Utilisez des serveurs MCP externes pour les workflows de développement où l’agent IA a besoin du pipeline complet de retrieval et d’un accès à des bases de code hors du coffre. Les deux approches peuvent coexister — exécutez Claudian dans Obsidian pour le travail sur les notes et Claude Code avec MCP en externe pour le développement.


Cadre de décision : Obsidian ou les alternatives

Tous les cas d’usage n’ont pas besoin d’Obsidian. Cette section indique quand Obsidian est le bon support, quand il est disproportionné et quand une autre solution convient mieux.

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 brut CLAUDE.md
Local-first 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 Cache en lecture seule Partiel Complet Complet
Passe à l’échelle de 10 000+ 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 disproportionné

  • Contexte d’un seul projet. Si l’IA n’a besoin que du contexte de la base de code actuelle, placez-le dans CLAUDE.md, AGENTS.md ou dans la documentation au niveau du projet. Ces fichiers voyagent avec le repo et sont chargés automatiquement.
  • Données structurées. Si le contenu est composé de tableaux, d’enregistrements ou de schémas, utilisez une base de données. Les notes Obsidian sont d’abord pensées pour la prose. Dataview peut interroger les champs de frontmatter, mais une vraie base de données gère mieux les requêtes structurées.
  • Recherche temporaire. Si les notes seront jetées à la fin du projet, un dossier de travail avec des fichiers markdown est plus simple. Ne construisez pas d’infrastructure de retrieval pour du contenu éphémère.

Quand Obsidian est le bon choix

  • Accumuler des connaissances pendant des mois ou des années. La valeur se compose à mesure que le corpus grandit. Un coffre de 200 notes interrogé chaque jour pendant six mois apporte plus de valeur qu’un coffre de 5 000 notes interrogé une seule fois.
  • Plusieurs domaines dans un même corpus. Un coffre contenant des notes sur la programmation, l’architecture, la sécurité, le design et des projets personnels bénéficie d’un retrieval inter-domaines qu’un CLAUDE.md propre à un projet ne peut pas fournir.
  • Contenu sensible à la confidentialité. Le local-first signifie que le pipeline de retrieval n’envoie jamais de contenu à des services externes. Le coffre contient ce que vous y mettez, y compris du contenu que vous ne téléverseriez 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 renforcent lorsqu’elles sont combinées. Chaque couche répond à un enjeu différent et présente 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’entrée détermine ce qui rejoint le coffre. Sans curation, le coffre accumule du bruit : captures d’écran de tweets, articles copiés-collés sans annotation, idées à moitié formées sans contexte. La couche d’entrée est responsable du contrôle qualité au moment de l’ajout. Un pipeline de scoring, une convention de tags ou un processus de revue manuelle — tout mécanisme garantissant que le coffre contient du contenu qui mérite d’être retrouvé.

Le retrieval rend le coffre interrogeable. C’est le moteur : découper les notes en unités de recherche, encoder les chunks en embeddings dans un espace vectoriel, indexer pour la recherche par mots-clés et sémantique, fusionner les résultats avec RRF. La couche de retrieval transforme un dossier de fichiers en base de connaissances interrogeable. Sans cette couche, le coffre reste navigable par exploration manuelle et recherche de base, mais il n’est pas accessible programmatiquement aux outils IA.

L’intégration connecte la couche de retrieval aux outils IA. Un serveur MCP expose le retrieval comme outil appelable. Des hooks injectent automatiquement le contexte. Des skills capturent les nouvelles connaissances dans le coffre. La couche d’intégration est 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 scoring à l’entrée ne sait rien des embeddings. Le retriever ne sait rien des règles de routage des signaux. Le serveur MCP ne sait rien de la manière dont les notes ont été créées. Ce découplage vous permet d’améliorer chaque couche indépendamment. Remplacez le modèle d’embedding sans modifier le pipeline d’entrée. Ajoutez une nouvelle capacité MCP sans modifier le retriever. Changez les heuristiques de scoring des signaux sans toucher à l’index.


Architecture du coffre Obsidian pour l’exploitation par l’IA

Un coffre optimisé pour la récupération par l’IA suit des conventions différentes d’un coffre optimisé pour la navigation personnelle. Cette section couvre la structure des dossiers, le schéma des notes, les conventions de frontmatter et les motifs précis qui améliorent la qualité de récupération.

Structure des dossiers

Utilisez des préfixes numériques pour les dossiers de premier niveau afin de créer une hiérarchie d’organisation prévisible. Les numéros n’impliquent aucune priorité : ils regroupent les domaines connexes et rendent la structure lisible rapidement.

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, ressources, signaux, notes quotidiennes.

Dossiers à exclure de l’indexation : les modèles (ils contiennent des variables de remplacement, pas du contenu), les pièces jointes (fichiers binaires), la configuration Obsidian et tout dossier contenant du contenu sensible que vous ne souhaitez pas voir apparaître dans l’index de récupération.

Le fichier .indexignore

Créez un fichier .indexignore à la racine du coffre pour exclure explicitement des chemins de l’index de récupération. La syntaxe correspond à 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 l’analyse et ignore entièrement les chemins correspondants. Les fichiers situés dans les chemins exclus ne sont jamais découpés en chunks, ne reçoivent jamais d’embeddings et n’apparaissent jamais dans les résultats de recherche.

Schéma des notes

Chaque note doit comporter un frontmatter YAML. Le système de récupération utilise les champs de frontmatter pour le filtrage et l’enrichissement du contexte :

---
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 requis pour la récupération :

  • 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 (« montrez-moi uniquement les MOCs » ou « uniquement les signaux »)
  • tags — Indexé dans le contexte de titre FTS5 avec un poids de 0,3, ce qui fournit des correspondances par mots-clés même lorsque le corps utilise une terminologie différente

Champs facultatifs mais utiles :

  • domain — Permet les requêtes limitées à un domaine (« rechercher uniquement dans les notes de sécurité »)
  • source — Attribution du contenu capturé ; le système de récupération peut inclure les URL sources dans les résultats
  • status — Permet d’exclure les notes archivées ou les brouillons de la recherche active

Conventions de chunking

Le système de récupération découpe les notes en chunks aux limites des titres H2 (##). Cela signifie que la structure de vos notes affecte directement la granularité de récupération :

Bon pour la récupération :

## 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 dispose d’assez de contexte pour que l’embedding en capture le sens. Une requête sur « la gestion des tokens expirés » correspond précisément au troisième chunk.

Mauvais pour la récupération :

# 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 grand chunk. L’embedding fait une moyenne de tous les sujets de la section. Une requête sur n’importe quel sous-sujet correspond de la même manière à toute la note.

Règle pratique : si une section couvre plus d’un concept, divisez-la en sous-sections H2. Le découpeur de chunks s’occupe du reste.

Ce qu’il ne faut pas mettre dans les notes

Contenu qui dégrade la qualité de récupération :

  • Copier-coller bruts d’articles entiers sans annotation. Le système de récupération indexe les mots-clés de l’article original, ce qui dilue votre coffre avec du contenu que vous n’avez pas écrit. Ajoutez plutôt un résumé, extrayez les points clés ou faites un lien vers l’URL source.
  • Captures d’écran sans description textuelle. Le système de récupération indexe le texte 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, l’approche la plus sûre consiste à ne jamais coller de secrets dans vos notes. Référencez-les plutôt par leur nom (« le token API Cloudflare dans ~/.env »).
  • Contenu généré automatiquement sans curation. Si un outil génère une note (transcription de réunion, surlignages Readwise, import RSS), relisez-la et annotez-la avant qu’elle n’entre dans le coffre permanent. Les imports automatiques non triés ajoutent du volume sans ajouter de valeur récupérable.

Écosystème de plugins pour les workflows IA

Les plugins Obsidian qui améliorent la qualité d’un coffre pour la recherche par IA se répartissent en trois catégories : structurels (imposer la cohérence), requêtage (exposer les métadonnées) et synchronisation (maintenir le coffre à jour).

Plugins essentiels

Dataview. Interroge votre coffre comme une base de données à l’aide des champs de frontmatter. Créez des index dynamiques : « toutes les notes taguées security mises à jour au cours des 30 derniers jours » ou « toutes les notes de projet avec le statut active ». Dataview n’aide pas directement la recherche, mais vous aide à repérer les lacunes dans la couverture de votre coffre et à trouver les notes à mettre à jour.

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

Templater. Crée des notes à partir de modèles avec des champs dynamiques. Assurez-vous que chaque nouvelle note commence avec le bon frontmatter en utilisant un modèle qui préremplit les champs created, type et domain. Un frontmatter cohérent améliore le filtrage 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

## References

Linter. Applique des règles de mise en forme dans tout le coffre. Une hiérarchie de titres cohérente (H1 pour le titre, H2 pour les sections, H3 pour les sous-sections) garantit que le chunker produit des résultats prévisibles. Règles Linter importantes pour la recherche :

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

Intégration Git. Contrôle de version pour votre coffre. Suivez les changements dans le temps, synchronisez entre machines et récupérez les suppressions accidentelles. Git fournit aussi des données mtime que l’indexeur utilise pour détecter les changements incrémentiels.

Plugins qui aident l’indexation

Smart Connections. Un plugin Obsidian qui fournit une recherche sémantique alimentée par 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 appels API.11 v4.5.0 (5 mai 2026) intègre les connexions de pied de page à Smart Connections Core, de sorte que chaque installation peut afficher des connexions vers des notes liées dans le pied de page sans ouvrir de panneau latéral. Les versions v4 récentes ont aussi ajouté des vues graphe pour les listes de connexions, des emplacements de dock configurables, une meilleure récupération des embeddings de blocs après des exécutions d’indexation interrompues, ainsi que « Substrate », un environnement inter-plugins permettant à Smart Connections, Smart Chat et Smart Composer de partager l’état.21 Même si le système de recherche de ce guide est externe à Obsidian (il s’exécute comme un pipeline Python), Smart Connections est utile pour explorer les relations sémantiques pendant l’écriture. Les deux systèmes indexent le même contenu, mais répondent à des usages différents : Smart Connections pour la découverte dans l’éditeur, le moteur de recherche externe pour l’intégration d’outils IA via MCP.

Plugins AI-native publiés en avril 2026. Une vague de nouveaux plugins communautaires cible directement le workflow Claude Code / Codex / Gemini-CLI :

Plugin Sortie Fonction
Cortex 4 avril Agent de coffre alimenté par Claude Code — traite le coffre comme un espace de travail d’agent, pas seulement comme un magasin de notes
VaultSearch 7 avril Recherche hybrid locale d’abord : BM25 + sémantique + fuzzy (chevauchement direct avec la stack de recherche de ce guide)
LLM Wiki 9 avril Transforme votre coffre en knowledge base interrogeable en privé
Drift 11 avril Visionneuse de diff façon VS Code pour l’édition Obsidian assistée par IA ; positionnée pour les workflows Claude Code
EngramQuest 11 avril Génère des exercices de mémoire à partir des notes ; fournit des « AI Skills » pour Claude Code / Gemini CLI / Cursor
Hybrid Search MCP Mars (encore récent) Serveur MCP + CLI avec BM25 + recherche sémantique — conçu spécifiquement pour les assistants IA

Considérez cela comme une surface émergente : plusieurs de ces plugins vont probablement se consolider ou être absorbés par Smart Connections / le cœur d’Obsidian au cours des prochains trimestres. Si vous devez en choisir un aujourd’hui, VaultSearch et Hybrid Search MCP sont les plus proches, dans l’esprit, du moteur de recherche externe de ce guide.

Note Dataview : Dataview (le plugin de requêtage Obsidian historique) a publié sa dernière version, 0.5.70, en avril 2025 et est depuis effectivement dormant. Pour les nouveaux travaux, la fonctionnalité intégrée Bases d’Obsidian (1.9+) est son successeur implicite et la voie recommandée.

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

Plugins qui nuisent à l’indexation

Excalidraw. Stocke les dessins sous forme de JSON intégré dans des fichiers markdown. Le JSON est du markdown syntaxiquement valide, mais produit des déchets lorsqu’il est découpé en chunks et intégré sous forme d’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é. Le format est conçu pour le rendu Kanban, pas pour la recherche dans de la prose. Le chunker produit des fragments de titres de cartes et de métadonnées qui ne donnent pas de bons embeddings. Excluez les tableaux Kanban de l’index.

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

Configuration de plugins importante

Récupération de fichiers → Activée. Protège contre la suppression accidentelle de notes. Pas directement lié à la recherche, mais critique pour une knowledge base dont vous dépendez.

Sauts de ligne stricts → Désactivés. Les sauts de ligne conformes au standard Markdown (double saut de ligne pour un paragraphe) produisent des chunks plus propres que le mode strict d’Obsidian (un seul 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 domaines. L’inbox est une zone de préparation ; les fichiers sont déplacés vers les dossiers de domaines après triage.

Format des wiki-links → Chemin le plus court lorsque possible. Les cibles de liens plus courtes sont plus faciles à résoudre par le moteur de recherche lors de l’indexation de la structure des liens.


Modèles d’embeddings : choix et configuration

Le modèle d’embeddings convertit les segments de texte en vecteurs numériques pour la recherche sémantique. Le choix du modèle détermine la qualité de récupération, la taille de l’index, la vitesse de génération des embeddings et les dépendances à l’exécution. Cette section explique pourquoi potion-base-8M de Model2Vec est le choix par défaut et quand choisir d’autres options.

Pourquoi Model2Vec potion-base-8M

Modèle : minishlab/potion-base-8M Paramètres : 7,6 millions Dimensions : 256 Taille : ~30 MB Dépendances : model2vec (numpy uniquement, pas de PyTorch) Inférence : CPU uniquement, embeddings de mots statiques (pas de couches 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 d’autres modèles transformer), Model2Vec produit des vecteurs par moyenne pondérée d’embeddings de tokens précalculés.5 Conséquence pratique : la génération d’embeddings est 50 à 500 fois plus rapide que les modèles basés sur transformer, puisqu’il n’y a pas de calcul séquentiel.

Sur la page actuelle des résultats Model2Vec, potion-base-8M atteint environ 92 % du score toutes tâches de all-MiniLM-L6-v2 (51,32 contre 55,80), tout en restant plusieurs ordres de grandeur plus rapide.6 L’écart de qualité restant est le compromis associé aux gains de vitesse et de simplicité. Pour de courts segments Markdown (200 à 400 mots en moyenne dans un coffre typique), la différence de qualité est moins marquée que sur des documents plus longs, car les deux modèles convergent vers des représentations similaires pour des textes courts et ciblés.

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, pas au moment de l’import. Importer le module d’embedding n’a aucun coût lorsque le retriever fonctionne en mode de secours BM25 uniquement (par exemple lorsque le venv d’embeddings n’est pas installé).

Environnement virtuel isolé. Le modèle s’exécute dans un venv dédié (par exemple ~/.claude/venvs/memory/) afin d’éviter les conflits de dépendances avec le reste de la chaîne d’outils. La fonction _activate_venv() ajoute le site-packages du venv à sys.path à 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 coût fixe de Model2Vec. L’indexer envoie les segments à embed_batch() au lieu de générer l’embedding d’un segment à la fois.

Quand choisir d’autres options

Modèle Dim Taille Vitesse Qualité (MTEB) Idéal pour
potion-base-8M 256 30 MB 500x 51,32 Par défaut : local, rapide, sans GPU
potion-base-32M 256 120 MB 400x 52,83 Qualité supérieure, toujours statique
potion-retrieval-32M 256 120 MB 400x 35,06 (retrieval) Statique optimisé pour la récupération
potion-multilingual-128M 256 ~500 MB 300x Coffres multilingues (101 langues)
all-MiniLM-L6-v2 384 80 MB 1x 55,80 Qualité supérieure, toujours local
nomic-embed-text-v1.5 768 270 MB 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 lorsque vous voulez une meilleure qualité que potion-base-8M sans quitter la famille des embeddings statiques. Il utilise un vocabulaire plus large distillé à partir de baai/bge-base-en-v1.5, atteint un score toutes tâches de 52,83 (environ 3 % de plus que potion-base-8M) tout en conservant une sortie à 256 dimensions et une dépendance limitée à numpy.8 Le fichier de modèle 4 fois plus volumineux augmente l’usage mémoire, mais la vitesse de génération des embeddings reste 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). Cette variante est fine-tuned à partir de potion-base-32M spécifiquement pour les tâches de récupération, avec un score de 35,06 dans le tableau de benchmark de récupération de Model2Vec, contre 32,67 pour potion-base-32M.8 Le compromis : elle est optimisée pour la récupération plutôt que pour la qualité générale des embeddings.

Choisissez potion-multilingual-128M lorsque votre coffre contient des notes dans plusieurs langues. Publié en mai 2025, ce modèle couvrant 101 langues est le modèle d’embeddings statiques le plus performant pour les tâches multilingues ; il génère des embeddings pour n’importe quel texte dans n’importe quelle langue tout en conservant la même dépendance limitée à numpy que les autres modèles potion.12 Le fichier de modèle plus volumineux (~500 MB) est le compromis nécessaire pour la capacité interlingue. Utilisez-le si vous avez des notes en japonais, chinois, allemand ou dans d’autres langues non anglaises en plus du contenu en anglais.

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

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

Choisissez text-embedding-3-small lorsque la latence réseau et la confidentialité sont des compromis acceptables. Le API produit les embeddings de la meilleure 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 limitée à numpy évite la complexité d’installation de PyTorch, et les vecteurs à 256 dimensions gardent la base de données compacte.

Quantization et réduction de dimensionnalité

Model2Vec v0.5.0+ permet de charger des modèles avec une précision et des dimensions réduites.8 C’est utile pour le déploiement sur du matériel contraint ou pour réduire 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 quantized conservent une qualité de récupération presque identique avec une empreinte mémoire bien plus faible. La réduction de dimensionnalité suit une troncature de type Matryoshka : les N premières dimensions portent le plus d’information. Passer de 256 à 128 dimensions divise par deux le stockage des vecteurs avec une perte de qualité minimale pour la récupération de textes courts.

Model2Vec v0.8.x met à jour les mécanismes internes de tokenizer/persistance, abandonne la prise en charge de Python 3.9 et actualise les résultats publiés avec les tableaux MTEB plus récents. Épinglez ou testez model2vec avant de mettre à niveau un indexer en production, car les mises à niveau de bibliothèque peuvent modifier les chemins de chargement des modèles même lorsque le nom du modèle d’embeddings reste identique.10

Fine-tuning pour des embeddings propres au coffre

Model2Vec v0.4.0+ prend en charge l’entraînement de modèles de classification personnalisés au-dessus d’embeddings statiques, v0.7.0 ajoute la quantization du vocabulaire et un pooling configurable pour la distillation, et v0.8.x refactorise le comportement du tokenizer et de la persistance.10 C’est pertinent pour les coffres avec un vocabulaire spécialisé (notes médicales, références juridiques, jargon métier), où les modèles potion par défaut peuvent ne pas saisir 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, potion-base-8M produit une qualité de récupération suffisante. Le fine-tuning ne vaut la peine que lorsque la récupération manque régulièrement des connexions propres au domaine qu’un modèle généraliste ne peut pas capturer.

Suivi du hash du modèle

L’indexer stocke un hash dérivé du nom du modèle et de la taille du vocabulaire. Si vous changez de modèle d’embeddings, l’indexer détecte l’incohérence 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 de mélanger dans la même base de données des vecteurs issus de différents modèles, ce qui produirait des scores de cosine similarity dénués de sens.

Modes d’échec

É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 retriever 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’indexer le détecte via le hash du modèle et déclenche une réindexation complète. Si la vérification du hash échoue (modèle personnalisé sans hash correct), sqlite-vec produira une erreur sur les requêtes KNN avec des dimensions incompatibles.

Pression mémoire sur les grands coffres. Générer les embeddings de plus de 50 000 segments en un seul lot peut consommer beaucoup de mémoire. L’indexer traite les données 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 en texte intégral avec FTS5

L’extension FTS5 de SQLite fournit une recherche en texte intégral avec classement BM25. FTS5 est le composant de recherche par mots-clés du pipeline de récupération hybride (« hybrid retrieval »). Cette section présente la configuration de FTS5, les cas où BM25 excelle, ainsi que ses modes d’échec spécifiques.

Table virtuelle FTS5

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

Mode de synchronisation du contenu. Le paramètre content=chunks indique à FTS5 de référencer directement la table chunks au lieu de stocker une copie du texte. Cela réduit de moitié l’espace de stockage requis, mais impose de synchroniser FTS5 manuellement lorsque des chunks sont insérés, mis à jour ou supprimés.

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 des documents 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 de colonnes (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 coffre Obsidian contient des titres descriptifs qui prédisent fortement la qualité du contenu, augmentez le poids de section. Si vos tags sont complets 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
  • Flags CLI : --incremental, --vault, --model
  • Clés de configuration : bm25_weight, max_tokens, batch_size
  • Messages d’erreur : SQLITE_LOCKED, ConnectionRefusedError
  • Termes techniques précis : PostToolUse, PreToolUse, AGENTS.md

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

Quand BM25 échoue

BM25 échoue avec les requêtes qui emploient une terminologie différente de celle du contenu stocké :

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

BM25 échoue aussi avec la collision de mots-clés à grande échelle. Dans un coffre de 15 000 fichiers, une recherche sur « configuration » correspond à des centaines de notes, car presque chaque note de projet mentionne la configuration. Les résultats sont techniquement corrects, mais pratiquement inutiles : le classement ne peut pas déterminer quelle note sur la « configuration » est pertinente pour la requête actuelle.

Tokenizer FTS5

FTS5 utilise par défaut le tokenizer unicode61, qui gère le texte ASCII et Unicode. Pour les coffres contenant beaucoup 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 par défaut unicode61 segmente selon les limites de mots, ce qui fonctionne mal pour les langues sans espaces entre les mots. Le tokenizer trigram segmente tous les trois caractères, ce qui permet les correspondances de 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 change :

# 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 des insertions en masse (réindexation complète), mais pas après des mises à jour incrémentales individuelles : pour celles-ci, utilisez INSERT INTO chunks_fts(rowid, chunk_text, section, heading_context) afin de synchroniser chaque ligne.


Recherche vectorielle avec sqlite-vec

L’extension sqlite-vec apporte la recherche vectorielle KNN (K-Nearest Neighbors) à SQLite. Cette section couvre la configuration de sqlite-vec, le pipeline d’embeddings depuis la note jusqu’au vecteur interrogeable, ainsi que les patterns de requête 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 float à 256 dimensions sous forme de données binaires compactées. La colonne id correspond 1:1 à la table chunks, ce qui permet les jointures entre les résultats vectoriels et les métadonnées des chunks.

Pipeline d’embeddings

Le pipeline va de la note au 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 vectorielle

Le module struct de Python sérialise les vecteurs float 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 génère l’embedding de la requête d’entrée, puis trouve les K chunks les plus proches selon la 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 dans sqlite-vec effectue une recherche approximative des plus proches voisins. Le paramètre k contrôle le nombre de résultats à renvoyer. 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 < ?, ce qui permet une pagination basée sur curseur dans de grands ensembles de résultats sans rescanner les pages précédentes.14 Les versions stables ultérieures v0.1.8 et v0.1.9 sont des releases de packaging et de correction de bugs DELETE plutôt que de nouvelles releases du modèle de requête ; v0.1.7 reste donc la frontière fonctionnelle pour ce pattern de pagination.23

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

Cela remplace l’ancien pattern qui consistait à récupérer un grand k puis à découper les résultats dans Python, ce qui réduit l’utilisation mémoire pour les requêtes exploratoires sur de grands vaults.

Prise en charge de DELETE dans les tables vec0

sqlite-vec v0.1.7 a ajouté la prise en charge native de DELETE pour les tables virtuelles vec0, et v0.1.9 a corrigé un chemin d’erreur DELETE impliquant des colonnes de texte de métadonnées de plus de 12 caractères.1423 Auparavant, supprimer des vecteurs nécessitait de supprimer puis de recréer la table. Désormais, le chemin de suppression de fichiers de l’indexer peut supprimer directement les vecteurs :

# 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’indexer n’a plus besoin de maintenir une table fantôme d’« active IDs » ni de reconstruire par lots.

Quand la recherche vectorielle est avantageuse

La recherche vectorielle excelle pour les requêtes où le concept compte davantage que les mots précis :

  • 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 lié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é par des détails d’implémentation)

Quand la recherche vectorielle échoue

La recherche vectorielle gère mal les identifiants exacts :

  • Requête : _rrf_fuse → Renvoie des notes sur les « fusion algorithms » 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 précis du hook

La recherche vectorielle gère aussi mal 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 qu’un sens sémantique. Un fichier JSON avec "review": true produit un embedding différent d’une discussion en prose sur la revue de code.

Dégradation progressive

Si sqlite-vec ne parvient pas à se charger (extension manquante, plateforme incompatible, bibliothèque corrompue), le retriever revient à 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 retriever vérifie vec_available avant de tenter des requêtes vectorielles. Lorsqu’elle est désactivée, toutes les recherches utilisent uniquement BM25, et l’étape de fusion RRF est ignorée.


Reciprocal Rank Fusion (RRF)

RRF fusionne deux listes classées sans nécessiter de calibrage des scores. Cette section couvre l’algorithme, une trace détaillée de requête, l’ajustement du paramètre k et les raisons du choix de RRF plutôt que d’autres approches. Pour un calculateur interactif avec rangs modifiables, préréglages de scénarios et explorateur visuel d’architecture, consultez l’analyse approfondie du retriever hybride.

L’algorithme

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

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

Où : - k est une constante de lissage (60, selon 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 propre à chaque liste (1.0 par défaut)

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

Pourquoi choisir RRF plutôt que d’autres approches

La combinaison linéaire pondérée exige de calibrer les scores BM25 par rapport aux distances cosinus. Les scores BM25 ne sont pas 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 utilise uniquement 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 annotées — des paires requête-document avec jugement de pertinence. Pour une base de connaissances personnelle, ces données d’entraînement n’existent pas. Vous devriez juger manuellement des centaines de paires requête-document pour entraîner un modèle utile. RRF fonctionne sans aucune donnée d’entraînement.

Les méthodes de vote Condorcet (Borda count, Schulze method) sont élégantes en théorie, mais plus complexes à implémenter et à ajuster. L’article original sur RRF a montré que RRF surpasse les méthodes Condorcet sur des 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 de mots-clés sur « review », « aggregator », « disagreements »), mais place deux fichiers de configuration plus haut (ils correspondent plus fortement à « review »). La recherche vectorielle classe le même chunk en position 1 (correspondance sémantique sur la résolution de conflits). Après fusion RRF :

Chunk 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 chunks bien classés dans les deux listes remontent en tête. Les chunks qui n’apparaissent que dans une seule liste obtiennent un score à source unique et passent sous les résultats classés par les deux méthodes. La logique réelle de résolution des désaccords l’emporte parce que les deux méthodes l’ont trouvée — BM25 grâce aux mots-clés, la recherche vectorielle grâce à la sémantique.

Pour la trace complète étape par étape avec les calculs 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

Ajuster 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 plus faible (par exemple, 10) : Les résultats les mieux classés dominent. Le rang 1 obtient 1/11 = 0,091, le rang 10 obtient 1/20 = 0,050 (écart de 1,8x). Utile lorsque vous faites confiance aux classeurs individuels pour placer le bon résultat en tête.
  • k par défaut (60) : Équilibré. Le rang 1 obtient 1/61 = 0,0164, le rang 10 obtient 1/70 = 0,0143 (écart de 1,15x). Les différences de rang sont compressées, ce qui donne plus de poids à l’apparition dans plusieurs listes.
  • k plus élevé (par exemple, 200) : L’apparition dans les deux listes compte beaucoup plus que la position de rang. Le rang 1 obtient 1/201, le rang 10 obtient 1/210 — presque identique. À utiliser lorsque les classeurs individuels produisent des classements bruités, mais que l’accord entre listes est fiable.

Commencez avec k=60. L’article original sur RRF a montré que cette valeur était robuste sur divers jeux de données TREC. Ajustez-la seulement après avoir mesuré les cas d’échec sur votre propre distribution de requêtes.

Départager les égalités

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

  1. Préférez les chunks qui apparaissent dans les deux listes à ceux qui n’apparaissent que dans une seule
  2. Parmi les chunks présents dans les deux listes, préférez celui dont le rang combiné est le plus faible
  3. Parmi les chunks présents dans une seule liste, préférez celui dont le rang est le plus faible dans cette liste

Le pipeline de récupération complet

Cette section suit une requête de l’entrée à la sortie dans l’ensemble du pipeline : recherche BM25, recherche vectorielle, fusion RRF, troncature selon le 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 : ~23ms pour une base de données de 49 746 chunks sur du matériel Apple M3 Pro.

Le 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 selon le budget de tokens

Le paramètre max_tokens empêche le retriever de renvoyer plus de contexte que l’outil d’AI ne peut en utiliser. L’estimation utilise 4 caractères par token (une approximation raisonnable pour de la prose anglaise). Les résultats sont tronqués de manière gloutonne : ajoutez les résultats dans l’ordre du classement jusqu’à épuisement du budget.

C’est une stratégie prudente. Une approche plus sophistiquée tiendrait compte des scores de qualité par résultat et privilégierait les résultats plus courts et de meilleure qualité plutôt que les résultats plus longs et moins pertinents. L’approche gloutonne est plus simple et fonctionne bien en pratique, car le classement RRF ordonne déjà les résultats par pertinence.

Schéma de 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 retriever vérifie les capacités à l’initialisation et adapte sa stratégie de requête. Un composant manquant dégrade la qualité, mais ne provoque pas d’erreurs. Le seul échec bloquant est l’absence du fichier de base de données.

Statistiques de production

Mesuré sur un coffre de 16 894 fichiers, 49 746 chunks, une base de données SQLite de 83 MB, 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 MB
Latence de requête BM25 (p50) 12ms
Latence de requête vectorielle (p50) 8ms
Latence de fusion RRF 3ms
Latence de recherche de bout en bout (p50) 23ms
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 changements

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

Comparaison de l’heure de modification des fichiers

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

  1. Analyse le coffre à 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 cette valeur au 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 mtime_ns diffère
  7. Fichiers supprimés : le chemin existe dans la base de données, mais pas 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, et non un hachage du contenu

Le hachage du contenu (SHA-256 du contenu des fichiers) serait plus fiable qu’une comparaison de mtime : il détecterait les cas où un fichier a été touché sans modification (par exemple, un git checkout restaurant le mtime d’origine). Toutefois, le hachage exige de lire chaque fichier à chaque exécution incrémentale. Pour 16 894 fichiers, la lecture du contenu des fichiers prend 2 à 3 secondes. La lecture des mtimes depuis le système de fichiers prend <100ms.

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

Gestion des suppressions

Lorsqu’un fichier est supprimé du coffre, 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, avec un correctif de bug en v0.1.9 pour les opérations DELETE sur les tables vec0 comportant des colonnes de texte de métadonnées plus longues.1423 Les versions antérieures nécessitaient des contournements (suppression et recréation de la table virtuelle, ou maintien d’un ensemble externe d’« IDs actifs »). Si vous exécutez une version antérieure à 0.1.9, mettez à niveau avant de vous appuyer sur des suppressions directes dans des schémas riches en métadonnées.

Les tables de synchronisation de contenu FTS5 exigent une suppression explicite via INSERT INTO chunks_fts(chunks_fts, rowid, ...) VALUES('delete', ?, ...) pour chaque ligne supprimé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, pour un usage quotidien) et complet (lent, occasionnel). Cette section explique quand utiliser 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 des notes. C’est le mode par défaut.

Ce qu’elle fait : 1. Analyse le coffre Obsidian pour détecter les changements de fichiers (comparaison des mtimes) 2. Supprime les chunks des fichiers supprimés 3. Redécoupe en chunks et régénère les embeddings des fichiers modifiés 4. Insère de nouveaux chunks pour les nouveaux fichiers 5. Synchronise l’index FTS5

Durée typique : <10 secondes pour une journée de modifications dans un coffre de 16 000 fichiers.

python index_vault.py --incremental

Réindexation complète

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

Ce qu’elle fait : 1. Supprime toutes les données existantes (chunks, vecteurs, entrées FTS5) 2. Analyse l’ensemble du coffre 3. Découpe tous les fichiers en chunks 4. Génère les embeddings de tous les chunks 5. Reconstruit l’index FTS5 depuis zéro

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

python index_vault.py --full

Idempotence

Les deux modes sont idempotents : exécuter deux fois la même commande produit le même résultat. L’indexeur supprime les chunks existants d’un fichier avant d’en insérer de nouveaux ; ainsi, relancer l’indexation incrémentale sur une base de données déjà à jour ne produit aucun changement. Relancer 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 l’écriture, erreur disque, processus tué au milieu d’une 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, pas la base de données. La base de données est un artefact dérivé qui peut être reconstruit à tout moment. C’est une propriété de conception essentielle : vous n’avez jamais besoin de sauvegarder la base de données.

L’option --incremental

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

  1. Vérification du hash du modèle. Compare le hash de modèle stocké au modèle actuel. S’il est différent, bascule automatiquement en mode de réindexation complète et avertit l’utilisateur.
  2. Analyse des fichiers. Parcourt les dossiers autorisés, collecte les chemins de fichiers et les mtimes.
  3. Détection des changements. Compare avec les données stockées.
  4. Traitement par lots. Redécoupe en chunks et régénère les embeddings des fichiers modifiés par lots de 64.
  5. Rapport de progression. Affiche le nombre de fichiers traités et le temps écoulé.
  6. Arrêt propre. Gère SIGINT en terminant le fichier en cours avant de s’arrêter.

Filtrage des identifiants et limites des données

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

Le problème

Une note sur le débogage d’une intégration OAuth peut 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 tous deux découpés en chunks, transformés en embeddings et stockés dans la base de données. Une recherche sur « authentification » renverrait le chunk contenant de vrais secrets. Pire encore, si le système de récupération transmet les résultats à un outil d’AI via MCP, les secrets apparaissent dans la fenêtre de contexte de l’AI et potentiellement dans les journaux de l’outil.

Filtrage par motifs

Le filtre d’identifiants s’exécute sur chaque chunk avant son stockage, en recherchant 25 motifs propres à des fournisseurs, ainsi que des motifs génériques :

Motifs propres aux fournisseurs :

Motif 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,}
AWS Access Key AKIA... AKIA[0-9A-Z]{16}
Clé Stripe sk_live_... [sr]k_(live\|test)_[a-zA-Z0-9]{24,}
Token Cloudflare ... Divers motifs

Motifs génériques :

Motif Détection
Tokens JWT eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+
Bearer tokens Bearer\s+[a-zA-Z0-9_\-\.]+
Clés privées -----BEGIN (RSA\|EC\|OPENSSH) PRIVATE KEY-----
base64 à forte entropie Chaînes avec >4.5 bits/caractère d’entropie, 40+ caractères
Affectations de mots 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 clés :

  1. Filtrer avant l’embedding. Le texte nettoyé est celui qui sert à générer l’embedding. La représentation vectorielle n’encode jamais les motifs d’identifiants. Une requête sur « clé API » renvoie des notes qui parlent de la gestion des clés API, pas des notes contenant de vraies clés.

  2. Remplacer, pas supprimer. Le token [REDACTED:pattern-name] préserve le contexte sémantique du texte environnant. L’embedding capture le fait que « quelque chose ressemblant à un identifiant était ici », sans encoder l’identifiant lui-même.

  3. Journaliser les motifs, pas les valeurs. Le filtre journalise les motifs qui ont correspondu (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 fournit une exclusion grossière par chemin. Le filtre d’identifiants apporte un nettoyage fin à l’intérieur des fichiers indexés. Les deux sont nécessaires :

  • .indexignore pour les dossiers entiers dont vous savez qu’ils contiennent du contenu sensible (notes de santé, documents financiers, documents de carrière)
  • Filtre d’identifiants pour les secrets accidentellement intégrés à du contenu qui peut par ailleurs être indexé

Classification des données

Pour les coffres contenant des contenus variés, envisagez de classer les notes par 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 de santé Non (.indexignore) N/A
Restreint Identifiants, clés privées Non (.indexignore) N/A

Architecture du serveur MCP

Les serveurs Model Context Protocol (MCP) exposent le module de récupération comme un outil que les agents IA peuvent appeler. Cette section couvre la conception du serveur, la surface de capacités et les limites d’autorisation.

Choix du protocole : STDIO ou HTTP

MCP prend en charge 2 modes de transport :

STDIO — L’outil IA lance le serveur MCP comme 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 STDIO MCP.

{
  "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 s’exécute comme service HTTP autonome. Utile pour l’accès distant, les configurations multi-clients ou les configurations d’équipe où le coffre se trouve sur un serveur partagé.

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

Recommandation : utilisez STDIO pour les coffres personnels. Il est plus simple, plus sécurisé (aucune exposition réseau), et le cycle de vie du serveur est géré par l’outil IA. N’utilisez HTTP que lorsque plusieurs outils ou plusieurs machines doivent accéder simultanément au même coffre.

Évolution de la spécification MCP. La spécification MCP de juin 2025 a ajouté l’autorisation OAuth 2.1, les sorties d’outils structurées (schémas de retour typés) et l’élicitation (prompts utilisateur initiés par le serveur). La version de novembre 2025 a livré Streamable HTTP comme mode de transport de premier ordre, la découverte d’URL .well-known pour parcourir automatiquement les capacités du serveur, des annotations d’outils structurées qui déclarent si un outil est en lecture seule ou modifie l’état, ainsi qu’un système de standardisation par niveaux SDK.79 La prochaine version de la spécification (provisoirement mi-2026) propose des opérations asynchrones pour les tâches longues, des extensions de protocole propres à certains domaines comme la santé et la finance, et des standards de communication entre agents pour les workflows multi-agents.9 Pour les serveurs de coffres personnels, STDIO reste la voie la plus simple. Le transport Streamable HTTP et la découverte .well-known bénéficient surtout aux déploiements HTTP d’entreprise avec routage multi-tenant et équilibrage de charge. Surveillez la feuille de route MCP pour les mises à jour qui influencent votre choix de transport.

Conception des capacités

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

search — L’outil principal. Exécute une récupération hybrid (hybrid retrieval) 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 complet d’une note précise par chemin. Utile lorsque l’agent veut 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 n’a pas de requête précise.

{
  "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 adapté à l’injection 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 }
  }
}

Limites d’autorisation

Le serveur MCP doit appliquer des limites strictes :

  1. Lecture seule. Le serveur lit le coffre 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, et non par le serveur MCP.

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

  3. Sortie filtrée pour les identifiants. Même si la base de données contient du contenu préfiltré, appliquez un filtrage des identifiants en sortie comme mesure de défense en profondeur.

  4. Réponses limitées en tokens. Appliquez max_tokens à toutes les réponses d’outils afin d’é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 qui aident l’outil IA à 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 de Claude Code

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

Configuration MCP

Ajoutez le serveur Obsidian MCP à ~/.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 comme processus enfant. Vérifiez qu’il est en cours d’exécution :

> 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 du 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 d’outil importantes dans le coffre pour les futures récupérations.

#!/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 modèle obsidian_bridge.py

Un module passerelle fournit une API Python 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)

La skill /capture

Une skill Claude Code pour capturer les idées dans le coffre :

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

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

Modèles de commandes personnalisées

Les skills Claude Code peuvent encapsuler les opérations du coffre dans des commandes nommées. Des praticiens ont constitué des bibliothèques de commandes propres à Obsidian, qui traitent le coffre à la fois comme source de lecture et comme cible d’écriture.

Analyse des signaux. Une commande /scan-intel interroge des sources externes, évalue les résultats par rapport à des intérêts de recherche personnels, puis écrit les signaux qualifiés sous forme de notes de 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 score (pertinence, exploitabilité, profondeur, autorité), puis écrit les signaux retenus dans des dossiers thématiques du coffre. Le coffre devient le consommateur aval d’un pipeline de veille automatisé.

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

/captains-log

La commande extrait l’historique des commits depuis GitHub, regroupe les éléments par dépôt et les met en forme comme une entrée de journal narrative. Au fil du temps, les journaux quotidiens créent un historique consultable de ce qui a été livré et pourquoi.

Capture Obsidian. Une commande /obsidian-capture prend une idée issue de la session Claude Code actuelle 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

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

Exemples communautaires. 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 projet, la capture de recherche et les workflows de contenu.1 Un autre a créé une skill « Visual Explainer » qui génère des notes de diagrammes dans le coffre à partir d’une analyse de code.2 Les commandes varient, mais l’architecture reste constante : les skills Claude Code comme interface, les notes du coffre comme couche de stockage et l’infrastructure de récupération comme moteur de requête.

Gestion de la fenêtre de contexte

L’intégration doit tenir compte de 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 concurrence avec la mémoire de travail de l’agent.
  • Incluez l’attribution de la source. Incluez toujours le chemin du fichier et le titre de section afin que l’agent puisse référencer la source.
  • Tronquez le texte des chunks. Les chunks longs doivent être tronqués avec ... plutôt qu’omis entièrement. Les 300-500 premiers caractères contiennent généralement l’information clé.
  • N’injectez pas de contexte à 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 Write et Edit en bénéficient.

Intégration de Codex CLI

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

Configuration MCP

Ajoutez ceci à .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"

Modèles AGENTS.md

Codex CLI lit AGENTS.md pour les instructions au niveau du projet. Incluez des consignes 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 modèle 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 Obsidian MCP. Cette section couvre la configuration des outils courants.

Cursor

Ajoutez ceci à .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 utiliser le vault :

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 Prise en charge de MCP Transport Emplacement de configuration
Claude Code Complète STDIO ~/.claude/settings.json
Codex CLI Complète STDIO .codex/config.toml
Cursor Complète STDIO .cursor/mcp.json
Windsurf Complète STDIO .windsurf/mcp.json
Continue.dev Partielle HTTP ~/.continue/config.json
Zed En cours STDIO Interface des paramètres
Claudian (plugin Obsidian) Sans objet (intégré) Claude Code CLI Paramètres du plugin Obsidian
Agent Client (plugin Obsidian) Sans objet (intégré) ACP Paramètres du plugin Obsidian

Solution de repli pour les outils non-MCP

Pour les outils qui ne prennent pas en charge MCP, le retriever 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 un texte structuré qui peut être collé manuellement dans l’entrée de n’importe quel outil d’IA. C’est moins élégant qu’une intégration MCP, mais cela fonctionne partout.


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

Les notes structurées du vault peuvent servir de blocs de contexte réutilisables qui réduisent l’utilisation des tokens entre les interactions avec l’IA. Cette section couvre la conception des clés de cache et la gestion du budget de tokens.

Le pattern

Au lieu de rechercher du contexte à chaque interaction, préconstruisez des blocs de contexte à partir de notes bien structurées du vault et 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 a une durée de vie. Lorsque le TTL expire, le bloc est reconstruit en réinterrogeant le vault.
  2. Détection des changements du vault. Lorsque l’indexeur détecte des modifications sur des fichiers ayant contribué à un bloc de contexte mis en cache, ce bloc est immédiatement invalidé.

Gestion du budget de tokens

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

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 mis en cache sont chargés au démarrage de la session. Les résultats de recherche dynamiques remplissent le budget restant requête par requête. Cette approche hybrid donne à l’agent une base de contexte fréquemment nécessaire tout en préservant du budget pour les requêtes spécifiques.

Utilisation des tokens avant/après

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

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


Hooks PostToolUse pour la compression du contexte

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

Le problème

Un appel à l’outil Bash qui exécute des tests peut 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 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 du déclenchement récursif

Un hook de compression qui émet 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 les réussites/échecs, afficher uniquement les échecs
Listes de fichiers ls ou find dans la commande Tronquer aux 20 premières entrées + compteur
Traces de pile Mot-clé Traceback Conserver la première et la dernière frame + le message d’erreur
Statut Git modified: / new file: Résumer les compteurs par statut
Sortie de build warning: / error: Supprimer les lignes d’information, conserver les avertissements/erreurs

Pipeline de réception et de tri des signaux

La couche de réception détermine ce qui entre dans le coffre. Sans curation, le coffre accumule du bruit. Cette section couvre le pipeline de notation qui oriente les signaux vers les dossiers de domaine.

Sources

Les signaux proviennent de plusieurs canaux :

  • Flux RSS : blogs techniques, avis de sécurité, notes de version
  • Bookmarks via Web Clipper : l’extension officielle Obsidian Web Clipper (Chrome, Firefox, Safari) est le chemin de réception le plus fidèle pour la capture côté navigateur. Le cycle de versions d’avril 2026 l’a rendue nettement plus utile pour les workflows d’IA :22
    • 1.4.0 (9 avr.) : interface interactive de transcription YouTube — épinglez la vidéo, parcourez la transcription, activez le défilement automatique et mettez en évidence la position actuelle. S’y ajoute une option « Open in Reader » par défaut, qui envoie une capture en un clic directement vers le mode Reader.
    • 1.5.0–1.5.1 (15 avr.) : visionneuse de surlignages — parcourez et recherchez les surlignages capturés dans tout le coffre. Transition en fondu vers Reader. Lecture/pause YouTube plus fluide. La version 1.5.1 a corrigé une régression de compilation webpack.
    • 1.6.0–1.6.2 (21–23 avr.) : refonte de l’UX du surligneur avec prise en charge mobile. Defuddle 0.18 ajoute des extracteurs propres aux sources pour LinkedIn, Threads, Bluesky, Discourse et Medium. La version 1.6.2 corrige une régression du presse-papiers en mode intégré dans Safari. Configurez des templates par domaine source afin que les transcriptions YouTube, les README GitHub et les articles longs aboutissent chacun dans une note au nom pertinent, avec le bon frontmatter pour le pipeline de notation ci-dessous.
  • Newsletters : extraits clés de newsletters par e-mail
  • Capture manuelle : notes rédigées pendant la lecture, des conversations ou des recherches
  • Sortie d’outil : sorties significatives d’outils d’IA capturées via hooks
  • Extension de partage iOS : l’app 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 apps directement dans le coffre sans ouvrir Obsidian.19 Cela crée un chemin de réception mobile à faible friction : partagez un article depuis Safari et il arrive sous forme de note de coffre prête à être notée.
  • Obsidian CLI : les scripts shell et les hooks peuvent créer des notes via obsidian file create ou ajouter du contenu à des notes existantes via obsidian file append, ce qui permet des pipelines de réception automatisés sur ordinateur.

Dimensions de notation

Chaque signal est noté selon 4 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 pertinent pour le travail en cours
Exploitabilité Puis-je utiliser cette information ? Pure théorie, aucune application Technique ou pattern spécifique que je peux appliquer
Profondeur Le contenu est-il substantiel ? Titres, résumé superficiel Analyse détaillée avec exemples
Autorité La source est-elle crédible ? 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 de domaine
0.40 - 0.55 Mise en file d’attente pour revue manuelle
< 0.40 Rejet (ne pas stocker)

Routage par domaine

Les signaux avec un score supérieur à 0.55 sont orientés vers 1 des 12 dossiers de domaine selon une correspondance par mots-clés et une classification thématique :

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 de fonctionnement :

Métrique Valeur
Total des signaux traités 7 771
Routés automatiquement (>0.55) 4 832 (62 %)
Mis en file d’attente pour revue (0.40-0.55) 1 543 (20 %)
Rejetés (<0.40) 1 396 (18 %)
Dossiers de domaine actifs 12
Signaux moyens par jour ~18

Patterns de graph de connaissances

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

Chaque wiki-link crée une arête dirigée dans le graph. 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 y fait référence

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

Pattern de lien Sémantique Exemple
Lien en ligne « Est lié à » « Voir [[OAuth Token Rotation]] pour plus de détails »
Lien d’en-tête « A pour sous-thème » ”## Related\n- [[Token Rotation]]\n- [[Session Management]]”
Lien de type tag « Est catégorisé comme » ”[[type/reference]]”
Lien MOC « Fait partie de » Une note Map of Content listant des notes liées

Maps of Content (MOCs)

Les MOCs sont des notes d’index qui organisent les notes liées dans 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 améliorent la récupération de 2 manières :

  1. Correspondance directe. Une recherche sur « authentication overview » correspond au MOC lui-même, ce qui fournit à l’agent une liste curée de notes liées.
  2. Expansion de contexte. Après avoir trouvé une note spécifique, le récupérateur peut vérifier si la note apparaît dans des MOCs et inclure la structure du MOC dans les résultats, donnant à l’agent une carte du sujet plus large.

Parcours de graph pour l’expansion de contexte

Amélioration future du récupérateur : après avoir trouvé les meilleurs résultats, étendre 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)

Ce n’est pas implémenté dans le récupérateur actuel, mais cela représente une extension naturelle de la structure du graph.

Anti-patterns

Clusters orphelins. Groupes de notes qui pointent les unes vers les autres, mais n’ont aucune connexion avec le reste du coffre. Le panneau de graph dans Obsidian les rend visibles sous forme d’îlots déconnectés. Les clusters orphelins indiquent des MOCs manquants ou des liens inter-domaines manquants.

Prolifération de tags. Utilisation incohérente des tags ou création d’un trop grand nombre de tags très granulaires. Un coffre avec 500 tags uniques pour 5 000 notes compte en moyenne 1 note pour 10 tags : les tags ne sont pas utiles pour filtrer. Consolidez-les en 20-50 tags de haut niveau correspondant à vos dossiers de domaine.

Notes riches en liens, pauvres en contenu. Notes constituées entièrement de wiki-links, sans prose. Ces notes s’indexent mal, car le chunker n’a pas de texte à embed. Ajoutez au moins un paragraphe de contexte expliquant pourquoi les notes liées sont liées.

Liens bidirectionnels pour tout. Chaque référence n’a pas besoin d’être un wiki-link. Mentionner « OAuth » au passage ne nécessite pas [[OAuth 2.0 Overview]]. Réservez les wiki-links aux relations intentionnelles et navigables, pour lesquelles cliquer sur le lien fournirait un contexte utile.

Recettes de workflow développeur

Workflows pratiques qui combinent la recherche dans le coffre avec les tâches de développement quotidiennes.

Chargement du contexte le matin

Commencez la journée en chargeant le contexte pertinent :

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

Le moteur de recherche renvoie les notes récentes sur votre projet actif, ce qui vous permet de vous remettre rapidement en tête où vous en étiez. C’est plus efficace que de relire les messages de commit de la veille.

Capture de recherche pendant le codage

Pendant l’implémentation d’une fonctionnalité, capturez vos observations 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’observation capturée est immédiatement indexée et disponible pour de futures recherches. Au fil des mois, ces micro-captures constituent un corpus de connaissances propres à l’implémentation.

Lancement de projet

Lorsque vous démarrez un nouveau projet ou une nouvelle fonctionnalité :

  1. Recherchez dans le coffre : « Que sais-je sur [technology/pattern] ? »
  2. Passez en revue les 5 premiers résultats pour retrouver les décisions précédentes et les pièges connus
  3. Vérifiez s’il existe un MOC pour le domaine ; sinon, créez-en un
  4. Recherchez les modes de défaillance : « problèmes avec [technology] »

Débogage avec la recherche dans le coffre

Lorsque vous rencontrez une erreur ou un comportement inattendu :

Search my vault for [error message or symptom]

Les notes de débogage précédentes contiennent souvent la cause racine et le correctif. C’est particulièrement précieux pour les problèmes récurrents entre projets : le coffre se souvient de ce que vous oubliez.

Préparation d’une code review

Avant de passer une PR en revue :

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

Le coffre renvoie les décisions précédentes, les contraintes d’architecture et les standards de code pertinents pour le code examiné. La review s’appuie sur la connaissance institutionnelle, pas seulement sur le diff.


Optimisation des performances

Cette section couvre les stratégies d’optimisation selon différentes tailles de coffres et différents schémas d’utilisation.

Gestion de la taille de l’index

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

Au-delà de 50 000 notes, envisagez de : - Augmenter la taille de batch de 64 à 128 pour accélérer l’embedding - Utiliser le mode WAL (par défaut) pour les accès concurrents - Lancer une réindexation complète pendant les heures creuses

Optimisation des requêtes

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

db.execute("PRAGMA journal_mode=WAL")

C’est essentiel lorsque le serveur MCP traite des requêtes pendant que l’indexer exécute une mise à jour incrémentielle.

Pooling de connexions. Le serveur MCP doit réutiliser les connexions à la base de données plutôt que d’ouvrir une nouvelle connexion pour chaque requête. Une seule connexion longue durée 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

I/O mappées en mémoire. Le pragma mmap_size indique à SQLite d’utiliser des I/O mappées en mémoire pour le fichier de base de données. Pour une base de données de 83 MB, mapper tout le 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');

Cela fusionne les segments b-tree internes de FTS5, ce qui réduit la latence des requêtes pour les recherches suivantes.

Benchmarks de scaling

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

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

Tous les benchmarks incluent 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 à 2ms.


Dépannage

Dérive de l’index

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

Cause : L’indexer incrémentiel n’a pas été exécuté après l’ajout des notes, ou le mtime d’un fichier n’a pas été mis à jour (par exemple, après une synchronisation depuis une autre machine avec conservation des timestamps).

Correctif : Exécutez une réindexation complète : python index_vault.py --full

Changement de modèle d’embedding

Symptôme : Après un changement de modèle d’embedding, 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.

Correctif : L’indexer doit détecter la non-correspondance du hash du 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émentielles.

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

Correctif : Reconstruisez et optimisez :

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

Timeout MCP

Symptôme : L’outil d’AI indique 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 timeout MCP par défaut de l’outil d’AI peut être plus court.

Correctif : Préchargez 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

Verrous de fichier SQLite

Symptôme : Erreurs SQLITE_BUSY ou SQLITE_LOCKED.

Cause : Plusieurs processus écrivent simultanément dans la base de données. Le mode WAL permet les lectures concurrentes, mais un seul writer.

Correctif : Assurez-vous qu’un seul processus (l’indexer) écrit dans la base de données. Le serveur MCP et les hooks doivent uniquement lire. Si vous avez besoin d’écritures concurrentes, utilisez le mode WAL et définissez un busy timeout :

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, est introuvable dans le chemin de bibliothèque ou est incompatible avec la version de SQLite.

Correctif :

# 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 :

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 mémoire insuffisante pendant la réindexation complète d’un grand coffre (50 000 notes ou plus).

Cause : La taille de batch d’embedding est trop grande, ou tout le contenu des fichiers est chargé en mémoire en une seule fois.

Correctif : Réduisez la taille de batch et traitez les fichiers de manière incrémentielle :

BATCH_SIZE = 32  # Reduce from 64

Assurez-vous aussi que l’indexer traite les fichiers un par un (lecture, chunking et embedding de 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 avec l’option « Export All » (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. Relisez et ajoutez du frontmatter à chaque note
  5. Déplacez les notes vers les dossiers de domaine appropriés

Depuis Notion

  1. Exportez depuis Notion : Settings → Export → 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 checklists — c’est du markdown standard
  5. Notion inclut les tables de propriétés sous forme de HTML — convertissez-les en frontmatter YAML
  6. Notion intègre les images sous forme de 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 lot : for f in *.docx; do pandoc -f docx -t markdown "$f" -o "${f%.docx}.md"; done
  4. Déplacez-les vers le coffre, ajoutez du frontmatter et organisez-les en dossiers

Depuis du markdown brut (sans Obsidian)

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

  1. Ouvrez le répertoire comme coffre Obsidian (Obsidian → Open Vault → Open folder)
  2. Ajoutez .obsidian/ à .gitignore si le répertoire est versionné
  3. Créez des templates de frontmatter et appliquez-les aux fichiers existants
  4. Commencez à relier les notes avec des [[wiki-links]] à mesure que vous les lisez et les organisez
  5. Lancez l’indexer immédiatement — le système de retrieval fonctionne dès le premier jour

Depuis un autre système de retrieval

Si vous migrez depuis un autre système d’embedding ou de recherche :

  1. N’essayez pas de migrer les vecteurs. Différents modèles produisent des espaces vectoriels incompatibles. Exécutez 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. Lancez 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-05-28 Obsidian 1.13.0 desktop + 1.13.0 mobile (accès anticipé Catalyst) publiés. Desktop : panneau Settings remanié, ouvert dans sa propre fenêtre avec recherche intégrée et navigation au clavier ; les URI Obsidian affichent désormais une boîte de dialogue de confirmation avant de déclencher des actions ; nouvel avertissement avant le chargement de ressources HTML depuis des lecteurs réseau ; Search ajouté à la vue Bookmarks ; gestion des images améliorée dans l’éditeur ; améliorations de File Explorer / Properties / Sync ; nombreux correctifs developer-API et de bugs. Mobile : nouvelle feuille de partage iOS avec emplacements cibles configurables ; réorganisation des onglets depuis le sélecteur d’onglets ; gestes d’appui prolongé sur tablette pour redimensionner les divisions et les barres latérales épinglées ; Bases ajoute un élément de menu pour redimensionner les colonnes dans les vues tableau ; correctifs de bugs iOS et de recherche. Implications pour les workflows d’IA : la boîte de dialogue de confirmation sur les URI Obsidian ajoute un garde-fou volontaire aux intégrations MCP/agents pilotées par URI ; le menu de redimensionnement des colonnes de Bases rend Bases plus utilisable comme index frontal de coffre interrogé par les agents ; la cible configurable de la feuille de partage iOS rend le chemin de capture iPhone (déjà documenté comme point d’entrée principal) plus rapide à connecter aux pipelines Claude/Codex.
2026-05-06 Actualisation de la fraîcheur vérifiée par les sources : Smart Connections v4.5.0 a déplacé les connexions de pied de page dans Core ; les versions stables sqlite-vec v0.1.8/v0.1.9 ont mis à jour le packaging et le comportement DELETE ; Model2Vec v0.8.x a mis à jour les éléments internes du tokenizer/de la persistance ainsi que les tableaux de benchmarks ; correction de la chronologie Obsidian CLI de « 1.12.7 a introduit CLI » à « 1.12.0 a introduit CLI, 1.12.7 a amélioré le packaging d’installation/d’exécution. »
2026-04-27 Cycle d’avril de Web Clipper : 1.4.0 (interface interactive de transcription YouTube + Open in Reader par défaut), 1.5.0 (visionneuse Highlights), 1.6.0 (refonte UX de Highlighter + extracteurs de source Defuddle 0.18 pour LinkedIn/Threads/Bluesky/Discourse/Medium), 1.6.1 + 1.6.2 (correctifs Reader et Safari). Repositionner Web Clipper comme principal chemin d’entrée côté navigateur pour les workflows d’IA, plutôt que comme une simple mention de favori. Aucune publication Obsidian desktop, Sync ou Bases dans cette fenêtre.
2026-04-16 Smart Connections v4.3.0 (vue graphe, dock configurable, récupération des block-embeddings, environnement inter-plugin Substrate). Documenter la vague de plugins AI-native d’avril 2026 (Cortex, VaultSearch, LLM Wiki, Drift, EngramQuest, Hybrid Search MCP). Signaler MarkusPfundstein/mcp-obsidian comme en mode maintenance (dernier commit en juin 2025). Dataview est dormant ; Bases est le successeur pour les nouveaux travaux. Obsidian CLI 1.12.7 reste la passerelle privilégiée pour les assistants d’IA.
2026-04-01 Ajouter une section Obsidian CLI (commandes v1.12 pour les workflows d’IA). Ajouter une section sur les plugins agents (Claudian, Agent Client). Documenter le plugin principal Bases pour l’organisation du coffre. Mettre à jour le nombre de plugins à 2 500+. Ajouter l’extension de partage iOS comme source d’entrée. Mettre à jour la matrice de compatibilité avec les plugins agents intégrés.
2026-03-30 MCPVault v0.11.0 : outil list_all_tags, prise en charge de .base/.canvas, renommé en @bitbonsai/mcpvault. Obsidian Desktop v1.12.7 embarque le binaire CLI pour des interactions terminal plus rapides.
2026-03-23 Documenter sqlite-vec v0.1.7 stable : prise en charge de DELETE pour les tables vec0, contraintes de distance KNN pour la pagination. Index DiskANN de plus proches voisins approximatifs annoncé pour une prochaine version.
2026-03-07 Ajouter potion-multilingual-128M (101 langues, mai 2025) à la comparaison des modèles d’embeddings. sqlite-vec en v0.1.7-alpha.10 (correctifs CI/CD, aucune modification de fonctionnalité). Spécification MCP et techniques de retrieval confirmées comme à jour.
2026-03-03 Mettre à jour l’évolution de la spécification MCP (livrée en novembre 2025 : Streamable HTTP, .well-known, annotations d’outils). Ajouter le fine-tuning Model2Vec et la prise en charge du tokenizer BPE/Unigram. Ajouter un tableau comparatif des serveurs MCP communautaires. Mettre à jour Smart Connections vers v4.
2026-03-02 Ajouter potion-base-32M et potion-retrieval-32M à la comparaison des modèles. Ajouter une section sur la quantification/la réduction de dimensionnalité. Ajouter une note sur l’évolution de la spécification MCP.
2026-03-01 Publication initiale

Références


  1. Internet Vin, « 22 commandes que j’utilise avec Obsidian et 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., and Buettcher, S. Reciprocal Rank Fusion outperforms Condorcet and individual Rank Learning Methods. SIGIR, 2009. Présente 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 vault par réindexation complète : environ 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 sentence transformers. 

  6. potion-base-8M Model Card et Model2Vec results. Les tableaux publiés actuels indiquent potion-base-8M à 51,32 Avg (All) / 51,08 Avg (MTEB), contre all-MiniLM-L6-v2 à 55,80 Avg (All) / 55,93 Avg (MTEB), soit environ 92 % de conservation du score toutes tâches confondues. 

  7. Model Context Protocol Specification. La norme MCP pour connecter des outils AI à des sources de données. 

  8. Model2Vec Potion Models, potion-base-32M, et potion-retrieval-32M. Les model cards actuelles indiquent potion-base-32M à 52,83 Avg (All) et potion-retrieval-32M à 35,06 dans le tableau de retrieval. 

  9. Update on the Next MCP Protocol Release. La version de novembre 2025 a livré le transport Streamable HTTP, la découverte d’URL .well-known, les annotations d’outils structurées et la standardisation du niveau SDK. Prochaine version provisoirement prévue mi-2026, avec opérations asynchrones, extensions propres aux domaines et communication agent à agent. 

  10. Model2Vec Releases. v0.4.0 (févr. 2025) : prise en charge de l’entraînement et du fine-tuning. v0.5.0 (avr. 2025) : réécriture du backend, quantization, réduction de dimensionnalité. v0.7.0 (oct. 2025) : quantization du vocabulaire, prise en charge du tokenizer BPE/Unigram. v0.8.0/v0.8.1 (mars 2026) : refontes du tokenizer et de la persistance, dépréciation de Python 3.9, mises à jour des résultats MTEB V2 et compatibilité des chemins Windows. 

  11. Smart Connections for Obsidian. Smart Connections v4 : embeddings AI local-first, recherche sémantique fonctionnant hors ligne après l’indexation initiale. 

  12. potion-multilingual-128M. Minish Lab, mai 2025. Modèle d’embeddings statiques en 101 langues, les meilleurs embeddings statiques multilingues. Même dépendance numpy-only que les autres modèles potion. 

  13. MCPVault v0.11.0. Mars 2026. Nouvel outil list_all_tags pour analyser frontmatter et hashtags avec comptages. Meilleure gestion des dossiers avec points, prise en charge des fichiers .base et .canvas. Package renommé @bitbonsai/mcpvault sur npm. 

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

  15. Introduction to Bases. Plugin cœur Obsidian introduit en v1.9.10. Vues de type base de données (tables, galeries, calendriers, kanban boards) sur les fichiers du vault à l’aide des propriétés frontmatter comme champs. Fichiers enregistrés au format .base

  16. Obsidian Desktop v1.12.0 Changelog et Obsidian Desktop v1.12.7 Changelog. v1.12.0 a introduit le CLI pour l’automatisation de vault depuis le terminal ; v1.12.7 a amélioré le packaging d’installation et d’exécution avec un binaire autonome, une TUI et un comportement de fichier socket. Voir aussi la documentation CLI

  17. Claudian. Plugin Obsidian qui intègre Claude Code comme collaborateur AI dans le vault. Fournit un chat en barre latérale, des prompts contextuels, la prise en charge de la vision, des slash commands et des modes d’autorisation. 

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

  19. Obsidian iOS Changelog. Les mises à jour du début 2026 incluent Share Extension pour enregistrer directement dans le vault du contenu provenant d’autres apps, des correctifs pour les widgets Daily Note et Bookmark, ainsi que des améliorations du rafraîchissement du widget View Note. 

  20. MarkusPfundstein/mcp-obsidian. Dernier commit le 28 juin 2025. Aucune release taguée. Les discussions du forum (avril 2026) signalent une migration de la communauté vers une intégration basée sur CLI depuis qu’Obsidian 1.12.x a livré le CLI first-class. Conservé dans ce guide pour le contexte historique et pour les utilisateurs ayant des configurations existantes, mais non recommandé pour les nouveaux déploiements. 

  21. Smart Connections v4.5.0 Release. 5 mai 2026. Les connexions de pied de page sont devenues une fonctionnalité Core ; les versions v4 récentes incluent aussi des vues graphe pour les listes de connexions, des emplacements configurables pour le panneau de connexions, une meilleure récupération des block-embeddings, l’état cross-plugin Substrate, des correctifs de fallback transformer et une réduction des calculs de connexions dupliqués. 

  22. obsidianmd/obsidian-clipper releases — source principale pour la correspondance version-fonctionnalité de Web Clipper. Cycle d’avril 2026 : 1.4.0 (9 avr., UI de transcription YouTube + Open in Reader par défaut), 1.5.0 (15 avr., visionneuse Highlights + fondu d’entrée Reader), 1.5.1 (15 avr., correctif de compilation webpack), 1.6.0 (21 avr., UX Highlighter + Defuddle 0.18 avec extracteurs LinkedIn/Threads/Bluesky/Discourse/Medium), 1.6.1 (22 avr., correctifs de plan Reader + recherche dans les highlights), 1.6.2 (23 avr., correctif du presse-papiers Safari en mode intégré). Également listé sur le Mozilla Add-ons store et le Chrome Web Store

  23. sqlite-vec v0.1.8, sqlite-vec v0.1.9, et sqlite-vec v0.1.10-alpha.3. v0.1.8 a corrigé le packaging npm ; v0.1.9 a corrigé un bug DELETE pour les colonnes de texte de métadonnées dépassant 12 caractères ; v0.1.10-alpha.3 ajoute une prise en charge correcte de INSERT OR REPLACE INTO, mais reste une prerelease. 

VAULT obsidian.md INDEXED