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.
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 :
-
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.
-
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.
-
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ôtMarkusPfundstein/mcp-obsidiann’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"etobsidian search:context "query"exécutent des recherches dans le coffre depuis n’importe quel script shell, hook ou pipeline d’automatisation. La variantesearch:contextrenvoie les lignes correspondantes avec leur contexte environnant, ce qui est utile pour alimenter des prompts d’IA. - Automatisation des notes quotidiennes.
obsidian dailyouvre 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 listetobsidian template creategé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 setetobsidian property getlisent 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/listgè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/completefournit 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.mdou 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.mdpropre à 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 BM25type— 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ésultatsstatus— 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 :
- Préférez les chunks qui apparaissent dans les deux listes à ceux qui n’apparaissent que dans une seule
- Parmi les chunks présents dans les deux listes, préférez celui dont le rang combiné est le plus faible
- 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 :
- Analyse le coffre à la recherche de tous les fichiers
.mddans les dossiers autorisés - Lit le
mtime_nsde chaque fichier depuis le système de fichiers - Compare cette valeur au
mtime_nsstocké dans la base de données - Identifie trois catégories :
- Nouveaux fichiers : le chemin existe dans le système de fichiers, mais pas dans la base de données
- Fichiers modifiés : le chemin existe dans les deux, mais
mtime_nsdiffère - 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 :
- 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.
- Analyse des fichiers. Parcourt les dossiers autorisés, collecte les chemins de fichiers et les mtimes.
- Détection des changements. Compare avec les données stockées.
- Traitement par lots. Redécoupe en chunks et régénère les embeddings des fichiers modifiés par lots de 64.
- Rapport de progression. Affiche le nombre de fichiers traités et le temps écoulé.
- 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 :
-
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.
-
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. -
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 :
.indexignorepour 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-knownpour 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-knownbé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 :
-
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.
-
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. -
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.
-
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 :
- 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.
- 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 createou ajouter du contenu à des notes existantes viaobsidian 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.
Sémantique des backlinks
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 :
- 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.
- 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é :
- Recherchez dans le coffre : « Que sais-je sur [technology/pattern] ? »
- Passez en revue les 5 premiers résultats pour retrouver les décisions précédentes et les pièges connus
- Vérifiez s’il existe un MOC pour le domaine ; sinon, créez-en un
- 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
- Exportez Apple Notes avec l’option « Export All » (macOS) ou utilisez un outil de migration comme
apple-notes-liberator - Convertissez les exports HTML en markdown avec
markdownifyoupandoc - Déplacez les fichiers convertis dans le dossier
00-inbox/de votre coffre - Relisez et ajoutez du frontmatter à chaque note
- Déplacez les notes vers les dossiers de domaine appropriés
Depuis Notion
- Exportez depuis Notion : Settings → Export → Markdown & CSV
- Décompressez l’export dans le dossier
00-inbox/de votre coffre - Corrigez les artefacts markdown propres à Notion :
- Notion utilise
- [ ]pour les checklists — c’est du markdown standard - Notion inclut les tables de propriétés sous forme de HTML — convertissez-les en frontmatter YAML
- Notion intègre les images sous forme de chemins relatifs — copiez les images dans votre dossier de pièces jointes
- Ajoutez le frontmatter standard (
type,domain,tags) - Remplacez les liens de pages Notion par des wiki-links Obsidian
Depuis Google Docs
- Utilisez Google Takeout pour exporter tous les documents
- Convertissez les fichiers
.docxen markdown :pandoc -f docx -t markdown input.docx -o output.md - Conversion par lot :
for f in *.docx; do pandoc -f docx -t markdown "$f" -o "${f%.docx}.md"; done - 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 :
- Ouvrez le répertoire comme coffre Obsidian (Obsidian → Open Vault → Open folder)
- Ajoutez
.obsidian/à.gitignoresi le répertoire est versionné - Créez des templates de frontmatter et appliquez-les aux fichiers existants
- Commencez à relier les notes avec des
[[wiki-links]]à mesure que vous les lisez et les organisez - 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 :
- 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.
- Migrez le contenu, pas l’index. Les fichiers du coffre sont la source de vérité. L’index est un artefact dérivé.
- 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
-
Internet Vin, « 22 commandes que j’utilise avec Obsidian et Claude Code », mars 2026, x.com/internetvin/status/2026461256677245131. ↩
-
Nicopreme, skill d’agent « Visual Explainer » avec commandes slash, x.com/nicopreme/status/2023495040258261460. ↩
-
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. ↩↩↩
-
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 $. ↩
-
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. ↩
-
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. ↩
-
Model Context Protocol Specification. La norme MCP pour connecter des outils AI à des sources de données. ↩
-
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. ↩↩↩
-
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. ↩↩
-
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. ↩↩
-
Smart Connections for Obsidian. Smart Connections v4 : embeddings AI local-first, recherche sémantique fonctionnant hors ligne après l’indexation initiale. ↩
-
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. ↩
-
MCPVault v0.11.0. Mars 2026. Nouvel outil
list_all_tagspour analyser frontmatter et hashtags avec comptages. Meilleure gestion des dossiers avec points, prise en charge des fichiers.baseet.canvas. Package renommé@bitbonsai/mcpvaultsur npm. ↩ -
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. ↩↩↩
-
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. ↩ -
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. ↩↩
-
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. ↩
-
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. ↩
-
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. ↩
-
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. ↩↩
-
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. ↩
-
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. ↩
-
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. ↩↩↩