Obsirag

Obsirag

Un cerveau qui apprend de tes notes Obsidian : RAG local, génération d'insights automatiques, enrichissement web sur sources fiables. Python · ChromaDB · Ollama.

ObsiRAG

ObsiRAG

Un système RAG (Retrieval-Augmented Generation) local pour votre coffre Obsidian, tournant nativement en Python sur macOS et utilisant MLX-LM (Apple Silicon) comme moteur IA local et ChromaDB comme base vectorielle.


Vision du projet

ObsiRAG vous permet d'interagir avec l'intégralité de votre coffre Obsidian via un chat en langage naturel — le tout en local, sans envoyer vos données dans le cloud.

Exemples de requêtes :

  • "Quelles sont mes dernières notes ? Fais une synthèse de cette semaine."
  • "Quelles sont les notes où je parle de X ou Y ?"
  • "Qu'est-ce que j'ai appris ce mois-ci sur le sujet Z ?"

Principes fondamentaux

  • 100% local : vos notes ne quittent jamais votre machine
  • Coffre en lecture seule : ObsiRAG ne modifie jamais vos notes Obsidian existantes
  • Accès complet au coffre : pas de fenêtre contextuelle limitée, l'ensemble du coffre est exploitable
  • Insights générés automatiquement : chaque note du coffre donne lieu à des questions perspicaces auxquelles le LLM répond en combinant votre coffre et le web — les réponses sont sauvegardées en Markdown avec provenance et références citées
  • Synapses : connexions implicites découvertes entre vos notes par similarité sémantique — des ponts thématiques que vous n'auriez pas forcément tracés vous-même, sauvegardés comme notes dans votre coffre
  • Artefacts traçables : les insights et synapses générés indiquent leur provenance (Coffre, Web, ou Coffre et Web) et sont eux-mêmes interrogeables dans le chat
  • Déploiement natif macOS : service launchd, environnement Python isolé (venv)

Documentation technique

Validation locale post-changement

Pour standardiser la verification locale apres un lot de changements UI/Streamlit :

./scripts/validate_local.sh

Cette commande unique enchaine automatiquement :

  • redemarrage controle via ./scripts/post_restart_check.sh,
  • tests UI cibles (pytest --no-cov),
  • verification rapide des logs de demarrage.

Pour lancer la validation exhaustive (suite complete des tests) :

./scripts/validate_local.sh --full

Pour une boucle de dev tres rapide (subset critique sans suite complete) :

./scripts/validate_local.sh --smoke

Pour la suite de non-regression ultra-courte (7 tests @pytest.mark.nrt, < 2s) :

./scripts/validate_local.sh --nrt
./scripts/validate_local.sh --nrt --no-restart

Pour relancer rapidement les validations sans redémarrage (quand seuls les changements code/tests sont concernés) :

./scripts/validate_local.sh --no-restart
./scripts/validate_local.sh --full --no-restart
./scripts/validate_local.sh --smoke --no-restart

Chaque execution de validate_local.sh produit maintenant des rapports machine-readable dans logs/validation/ :

  • un rapport JUnit XML (*.junit.xml) pour integration CI,
  • un resume JSON (*.json) avec mode, statut, duree et code de sortie,
  • deux pointeurs stables : latest.junit.xml et latest.json.

Pour rediriger ces artefacts dans un autre dossier :

./scripts/validate_local.sh --smoke --report-dir /tmp/obsirag-validation

En cas de refactor majeur au-dela de la couche UI, completer ensuite avec la suite complete :

source .venv/bin/activate
pytest --no-cov

Fonctionnalités

Chat avec le coffre

Interface conversationnelle connectée à MLX-LM (inférence locale Apple Silicon, sans serveur externe) et au moteur de recherche du coffre. Les requêtes sont traitées en combinant récupération sémantique et synthèse par l'IA.

Comportement conversationnel

Le chat conserve maintenant un contexte conversationnel exploitable pour la récupération et pas seulement pour la génération finale.

  • Relances résolues dans le fil : une question courte comme "tu as plus de détail sur les objectifs" ou "et la durée de la mission ?" est rattachée automatiquement au sujet précédent si ce sujet est identifiable dans l'échange
  • Note principale : pour les questions mono-sujet, ObsiRAG identifie la note la plus dominante sur le thème et la fait remonter en priorité dans le contexte envoyé au modèle
  • Sources plus lisibles : la réponse affiche désormais la note principale au-dessus des sources, et cette note est marquée comme Principale dans la liste détaillée
  • Garde-fou anti hors-sujet : si une requête mono-sujet ne retrouve aucun chunk lexicalement fiable, ObsiRAG répond directement "Cette information n'est pas dans ton coffre." au lieu de laisser partir le modèle sur un faux contexte

Format des réponses

Les réponses mono-sujet sont désormais structurées en Markdown avec des intertitres courts pour améliorer la lisibilité dans le chat :

  • ### Aperçu de ...
  • ### Détails utiles

Les synthèses multi-thèmes conservent leur structure d'étude existante avec plusieurs chapitres.

Documentation dédiée

Le mécanisme de gestion des conversations, des relances et de la note dominante est documenté dans docs/conversation-management.md.

Diagrammes Mermaid

Lorsque la réponse du LLM contient un bloc Mermaid, le chat affiche un bouton de visualisation intégré qui ouvre le diagramme dans un viewer dédié — sans quitter l'interface.

Cerveau — graphe de connaissances

Visualisation interactive du réseau de vos notes sous forme de graphe interactif (rendu Pyvis sur fond sombre). Chaque nœud est une note, chaque arête une connexion.

Ce qui est affiché :

  • Nœuds : chaque note du coffre est un nœud coloré selon son dossier d'appartenance (palette de 8 couleurs distinctes). La taille du nœud est proportionnelle à son nombre de connexions — les notes les plus référencées apparaissent plus grandes
  • Arêtes (connexions) : les [[wikilinks]] entre notes forment les arêtes du graphe
  • Tooltip au survol : titre, date de modification, tags et deux boutons d'action — ouvrir la note dans le visualiseur intégré ou directement dans Obsidian
  • Métriques en en-tête : nombre de nœuds, connexions, densité du graphe et nombre de notes filtrées
  • Top 5 nœuds les plus connectés : liste sous le graphe avec leur score de centralité, avec bouton d'ouverture directe

Filtres disponibles (barre latérale) :

  • Par dossier (tous ou sélection multiple)
  • Par tag Obsidian (sélection multiple)
  • Par type de note (notes utilisateur, insights, synapses, synthèses, conversations...)
  • Sélecteur de note alphabétique pour ouvrir directement une note dans le visualiseur

Le graphe est mis en cache 5 minutes et recalculé à la demande via le bouton 🔄. Il est également exporté en JSON (data/graph/knowledge_graph.json) pour un usage externe éventuel.

Page Note — visualiseur intégré

Chaque note du coffre est consultable dans un visualiseur Markdown intégré, accessible depuis :

  • Le graphe Cerveau — bouton Ouvrir dans ObsiRAG au survol d'un nœud, ou via le top 5 des nœuds les plus connectés
  • Les résultats du chat — bouton d'ouverture directe dans un message de réponse
  • La page Note directement — sélecteur alphabétique en barre latérale

Ce qui est affiché :

  • Rendu Markdown complet (titres, listes, code, callouts Obsidian…)
  • [[Wikilinks]] cliquables : chaque lien interne navigue vers la note cible dans le même visualiseur
  • Rétroliens : toutes les notes du coffre qui référencent la note affichée
  • Tags et métadonnées du frontmatter YAML

La liste de sélection est triée par ordre alphabétique. En cas de doublons de titre, le chemin complet est affiché pour distinguer les notes.

Auto-apprentissage (background learner)

Un processus léger tourne en arrière-plan et :

  1. Détecte les notes récemment modifiées dans le coffre
  2. Détermine le champ sémantique de chaque note (domaine, concepts-clés, angle traité) pour ancrer la génération dans le bon univers lexical
  3. Génère des questions perspicaces strictement alignées avec ce champ sémantique
  4. Répond via RAG sur le coffre — et complète avec le web si la réponse est insuffisante
  5. N'utilise que des sources fiables (Wikipédia, presse de référence, institutions, revues scientifiques…)
  6. Sauvegarde les insights en Markdown dans obsirag/insights/ avec indication de provenance et références citées
  7. Génère une synthèse hebdomadaire le dimanche dans obsirag/synthesis/ — si le Mac était en veille au moment prévu, la synthèse est lancée automatiquement dès le réveil

Les artefacts générés sont indexés et deviennent eux-mêmes interrogeables dans le chat.

Le système est conçu pour fonctionner sans pénaliser l'utilisation normale de la machine : les appels LLM sont espacés (pause configurable entre chaque note et chaque question), le nombre de notes traitées par cycle est limité, et tout tourne dans un thread d'arrière-plan isolé. La machine reste pleinement disponible pendant le traitement.

Gestion du cycle de vie du modèle LLM : l'auto-learner charge le modèle MLX au début de chaque cycle et le décharge à la fin si l'interface web est inactive — libérant ainsi la mémoire GPU/Metal entre les cycles. Si l'interface est ouverte, le modèle reste chargé pour répondre immédiatement aux requêtes chat.

Traitements automatiques

#TraitementDéclenchementDescription
1Bulk initialUne seule fois au 1er démarrage (après 120 s de délai)Traite toutes les notes non-traitées (max AUTOLEARN_BULK_MAX_NOTES, défaut 20) : génère un insight Q&A et renomme la note avec un titre en français
2Cycle autolearnToutes les AUTOLEARN_INTERVAL_MINUTES min (défaut 60), 5 min après le démarrage, uniquement entre AUTOLEARN_ACTIVE_HOUR_START et AUTOLEARN_ACTIVE_HOUR_END (défaut 8h–22h)Pass 1 : jusqu'à AUTOLEARN_MAX_NOTES_PER_RUN notes récentes (modifiées dans les 24 h). Pass 2 : jusqu'à AUTOLEARN_FULLSCAN_PER_RUN notes jamais traitées (full-scan)
3Découverte de synapsesÀ la fin de chaque cycle autolearnTrouve AUTOLEARN_SYNAPSE_PER_RUN paires de notes sémantiquement proches sans lien existant et génère une note de connexion dans obsirag/synapses/
4Synthèse hebdomadaireChaque dimanche à 20 h UTC (avec rattrapage si le Mac était en veille)Résume les notes modifiées dans la semaine et crée une note dans obsirag/synthesis/
5Watcher de coffreEn temps réel (watchdog filesystem)Détecte les créations / modifications / suppressions / renommages de fichiers .md et re-indexe dans ChromaDB avec un debounce

Alignement sémantique des questions

Avant de générer des questions, l'auto-learner extrait le champ sémantique de la note :

Domaine: [domaine principal] | Concepts: [concept1, concept2, concept3] | Angle: [angle spécifique]

Ce champ est injecté comme contrainte explicite dans le prompt de génération, garantissant que les questions — et donc les insights produits — restent dans le même univers thématique que la note source. Une note sur la finance comportementale génère des questions sur les biais cognitifs et non sur un sujet adjacent que le LLM pourrait dériver.

Entités nommées (NER) — validation WUDD.ai

Chaque insight généré est enrichi avec des entités nommées validées (personnes, organisations, pays, produits) issues de la liste officielle WUDD.ai. Le processus :

  1. Extrait les entités candidates par analyse spaCy du texte Q&A
  2. Valide chaque entité contre la liste officielle WUDD.ai (top 5 000 entités, triées par fréquence de mention) — les entités non reconnues sont ignorées
  3. Génère les tags Obsidian (personne/, org/, lieu/, produit/…) en utilisant le nom canonique officiel
  4. Insère une galerie d'images (table Markdown) avec la photo/logo de l'entité principale par type (PERSON, ORG, GPE, PRODUCT), depuis le cache Wikimedia de WUDD.ai
  5. Injecte location: [lat, lng] dans le frontmatter YAML pour la géolocalisation Obsidian Map View (coordonnées Wikipedia)

Dépendance externe : WUDD.ai doit être accessible sur WUDDAI_ENTITIES_URL (configurable dans .env). En cas d'indisponibilité, l'extraction spaCy seule est utilisée en fallback — les insights sont créés mais sans validation officielle. La liste est mise en cache localement pendant 24h.

Pour migrer les insights existants (tags + géolocalisation + galeries) :

.venv/bin/python scripts/migrate_insight_tags.py --dry-run  # simulation
.venv/bin/python scripts/migrate_insight_tags.py              # application

Pour renommer en batch les insights/synapses/syntheses selon un titre court généré par le LLM :

# Prévisualisation sans modification
.venv/bin/python scripts/rename_insights.py --dry-run

# Renommage avec LLM (tous les dossiers, pause 2 s entre appels)
.venv/bin/python scripts/rename_insights.py --sleep 2

# Cibler un seul dossier
.venv/bin/python scripts/rename_insights.py --dir insights

# Mode rapide sans LLM (retire uniquement le suffixe _YYYYMMDD)
.venv/bin/python scripts/rename_insights.py --no-llm

Le script :

  • Saute le frontmatter pour lire le corps de la note (évite que les tags YAML consomment le contexte LLM)
  • Propage [[ancien_titre]][[nouveau_titre]] dans tout le vault
  • Met à jour synapse_index.json (paires fp_a|||fp_b)
  • Re-indexe dans ChromaDB les fichiers modifiés

Sauvegarde des conversations

À tout moment, le bouton 💾 Sauvegarder cette conversation (affiché sous le chat dès qu'un échange existe) enregistre l'intégralité de la conversation en cours sous forme de note Markdown dans votre coffre.

  • Le titre du fichier est généré par le LLM à partir des questions posées (4 à 8 mots, en français), selon la même logique de nommage que les insights : slug normalisé + horodatage
  • Le fichier est créé dans obsirag/conversations/YYYY-MM/ et est immédiatement visible dans Obsidian
  • Le frontmatter contient les tags conversation et obsirag
  • Chaque échange (question / réponse) est mis en forme en Markdown navigable
  • La note est indexée par ObsiRAG au prochain cycle : les conversations passées deviennent elles-mêmes interrogeables dans le chat

Exemple de chemin : obsirag/conversations/2026-04/Connexions-entre-notes-ML_20260409_1423.md


Page Insights

Consultation des artefacts, synapses et synthèses générés, avec :

  • Progression & estimation du temps restant : widget affichant le nombre de notes traitées, restantes, et une estimation de la durée nécessaire pour compléter le traitement — avec heure du prochain cycle en heure locale
  • Historique des requêtes posées dans le chat


Conditions de génération d'un insight

Toutes les notes ne donnent pas lieu à un insight. Voici les cas où une note est ignorée :

ConditionRaisonCe qui se passe
Note trop courte / mal indexéeAucun chunk trouvé dans ChromaDBLa note n'est pas dans l'index vectoriel ; elle sera ignorée jusqu'à la prochaine réindexation
Aucune question généréeLe LLM n'a pas suivi le format attendu, ou le contenu est trop pauvre pour formuler une questionL'étape de génération de questions est sautée
Toutes les réponses QA ont échouéErreur LLM (contexte dépassé, modèle non disponible…) pour les 3 questionsL'insight n'est pas sauvegardé
Note mal parsée (YAML invalide)Le frontmatter Obsidian contient des caractères illégaux ou est mal forméLa note n'est pas indexée du tout

Notes qui produisent un insight

Une note génère un insight lorsque :

  1. Elle est présente et correctement indexée dans ChromaDB (au moins un chunk)
  2. Le LLM génère au moins une question orientée vers des connaissances externes pertinentes au domaine
  3. Au moins une réponse QA aboutit — soit via web (DDG + synthèse LLM), soit en fallback via RAG
  4. La réponse n'est pas détectée comme vide ou générique (filtres anti-réponse-creuse)

L'insight est sauvegardé dans obsirag/insights/YYYY-MM/ avec :

  • Les questions générées et leurs réponses
  • La provenance (Web, Coffre, ou Web+Coffre)
  • Une synthèse des sources web lorsque des URLs ont été récupérées et analysées

Astuce : Si une note attendue ne produit pas d'insight, vérifiez qu'elle est bien indexée (bouton "Re-indexer le coffre" dans le chat) et que le modèle MLX est correctement chargé (page Paramètres).


Synapses — connexions implicites entre notes

Pourquoi "synapse" ?

En neurologie, une synapse est la jonction entre deux neurones : elle transmet un signal d'un neurone à l'autre, créant une connexion qui n'existait pas de façon anatomique directe. Le terme est utilisé ici par analogie : deux notes de votre coffre sont des "neurones", et ObsiRAG découvre une connexion implicite entre elles — une connexion qui n'a jamais été formalisée par un wikilink.

Comment ça fonctionne

  1. Détection de paires : à chaque cycle, ObsiRAG tire aléatoirement des notes du coffre et cherche dans ChromaDB les notes sémantiquement proches (similarité cosinus au-dessus d'un seuil configurable), en excluant celles qui ont déjà un wikilink entre elles
  2. Mémoire des paires : chaque paire Note A ↔ Note B est mémorisée dans data/synapse_index.json — elle ne sera jamais retraitée deux fois
  3. Génération du texte : le LLM reçoit un extrait des deux notes (600 premiers caractères chacune) et rédige une explication complète de la connexion implicite qui les unit, ainsi qu'une question à approfondir
  4. Fichier résultat : une note Markdown est créée dans obsirag/synapses/ nommée {NoteA}__{NoteB}_{date}.md, avec le score de similarité, la connexion expliquée et les extraits des deux notes sources

Ce que vous voyez dans Obsidian

Les fichiers synapses contiennent des wikilinks vers chacune des deux notes sources, ce qui les intègre automatiquement dans le graphe de connaissances — révélant visuellement des ponts thématiques entre des notes que vous n'auriez peut-être pas rapprochées vous-même.


Nom et structure des fichiers insights

Nom du fichier

Le fichier est nommé automatiquement à partir du titre de la note source :

obsirag/insights/YYYY-MM/{titre_note}_{YYYYMMDD}.md
  • Les caractères spéciaux sont supprimés, les espaces remplacés par _, le tout tronqué à 60 caractères
  • La date du jour (heure locale) est ajoutée en suffixe
  • Les fichiers sont regroupés par mois dans un sous-dossier YYYY-MM/

Exemple : une note intitulée "La vitesse des LLMs", traitée le 7 avril 2026, produit : obsirag/insights/2026-04/La_vitesse_des_LLMs_20260407.md

Mise à jour vs. création

À chaque cycle, avant de créer un nouveau fichier, ObsiRAG cherche un insight existant pouvant être complété, selon deux critères :

  1. Même note source : le nom du fichier correspond au titre de la note (priorité maximale)
  2. Même thématique : au moins 2 tags entités NER en commun dans le frontmatter

Si un fichier correspondant est trouvé, les nouveaux Q&A sont ajoutés à la suite (numérotation continue ## Question N), la date "Mise à jour le" est rafraîchie, les tags NER sont fusionnés et la galerie d'images mise à jour. Sinon, un nouveau fichier est créé.

Structure du contenu

---                          ← Frontmatter YAML
tags:
  - insight
  - {tags de la note source}
  - {entités NER : personne/, org/, lieu/…}
location: [lat, lng]         ← optionnel, si entité géolocalisable
---

# Insights : {titre de la note}

**Note source :** [[lien wikilink]]
**Générée le / Mise à jour le :** {date heure locale}
**Provenance :** Web | Coffre | Coffre et Web

## Entités clés          ← galerie d'images des entités principales (WUDD.ai)

## Question 1
> {question générée}
{réponse LLM}
*Provenance / Notes consultées / Références web*

## Question 2 …

## Synthèse des sources web   ← si des pages web ont été analysées


Comment fonctionne la recherche sémantique

1. Découpage en chunks

Une note Obsidian peut être longue et couvrir plusieurs sujets. Pour permettre une recherche précise, chaque note est découpée en morceaux (chunks) d'environ 300 mots, avec un léger chevauchement entre chaque morceau pour préserver le contexte aux jonctions.

Le découpage respecte la structure de la note : d'abord par section (## Titre), puis par paragraphe, puis par mots si nécessaire. Chaque chunk hérite des métadonnées de la note (titre, tags, dates, wikilinks, entités NER…).

Qu'est-ce qu'un chunk ?

Un chunk est un fragment de texte extrait d'une note, avec ses métadonnées associées. Il contient le texte brut, un identifiant unique {file_hash}_{index}, et toutes les métadonnées de la note source (titre, section, tags, dates, wikilinks, entités NER…). C'est l'unité atomique de recherche dans ChromaDB.

Algorithme de découpage

Le chunking est implémenté en Python pur — aucune API externe, aucune dépendance réseau. C'est du découpage de chaînes de caractères (split(), split("\n\n")) : rapide, déterministe, 100% local.

Note Obsidian
    │
    ▼
[Parser] → liste de sections (chaque ## titre + son contenu)
    │
    ▼
Pour chaque section :
    │
    ├─ Section courte (≤ chunk_size mots) ?
    │       └─→ 1 chunk direct
    │
    ├─ Section longue avec plusieurs paragraphes (\n\n) ?
    │       └─→ Fusion de paragraphes :
    │               Accumuler les paragraphes un par un.
    │               Quand le total dépasse chunk_size :
    │                 → fermer le chunk
    │                 → repartir avec les N derniers mots (overlap)
    │                   + le paragraphe suivant
    │
    └─ Section longue sans paragraphes (un seul bloc) ?
            └─→ Fenêtre glissante :
                    Fenêtre de chunk_size mots,
                    avance de (chunk_size - overlap) mots à chaque pas
    │
    ▼
Chaque chunk reçoit :
  - le texte
  - chunk_id = {file_hash}_{index}
  - toutes les métadonnées de la note (titre, tags, dates, wikilinks, NER…)

Le principe clé : l'overlap

À chaque rupture de chunk, les overlap derniers mots du chunk précédent sont répétés en tête du suivant. Cela évite de couper une phrase en deux et de perdre le fil du contexte lors de la recherche sémantique.

ParamètreRôle
chunk_size_wordstaille max d'un chunk en mots (~300)
chunk_overlap_wordschevauchement entre chunks (~30)

2. Vectorisation (embedding)

Chaque chunk est transformé en un vecteur numérique — une liste de ~384 nombres — par le modèle paraphrase-multilingual-MiniLM-L12-v2 via sentence-transformers (calculs en local, CPU). Ce vecteur encode le sens du texte : deux passages sémantiquement proches produisent des vecteurs proches dans l'espace mathématique, même s'ils n'ont aucun mot en commun.

3. Stockage dans ChromaDB

Les vecteurs et leurs métadonnées sont stockés dans ChromaDB, une base vectorielle locale. L'indexation est incrémentale : seules les notes nouvelles ou modifiées sont retraitées.

4. Recherche à la requête

Quand vous posez une question dans le chat :

  1. La question est elle-même vectorisée
  2. ChromaDB identifie les chunks dont le vecteur est le plus proche → similarité cosinus
  3. Ces chunks (vos notes) sont injectés comme contexte dans le prompt envoyé à MLX-LM
  4. Le modèle génère une réponse ancrée dans votre coffre, pas dans ses seules connaissances pré-entraînées

C'est ce mécanisme qui permet de retrouver une note sur "les effets des écrans sur le sommeil" en posant la question "comment la lumière bleue affecte-t-elle le repos ?" — sans que ces mots exacts apparaissent dans la note.


Défi principal : les coffres de grande taille

  • Index vectoriel incrémental (mise à jour uniquement des notes nouvelles/modifiées)
  • Chunking adaptatif des notes
  • Métadonnées légères pour le filtrage rapide avant recherche sémantique

Performances et recommandations matérielles

ObsiRAG est conçu pour fonctionner en tâche de fond sur un MacBook Air M5 16 Go — la machine de référence du projet. L'ensemble du traitement (indexation, génération d'insights, synapses) tourne de façon transparente sans perturber l'utilisation normale : navigation web, rédaction dans Obsidian, appels visio.

Temps d'amorçage initial : pour un coffre d'environ 200 notes, comptez 1 à 2 jours pour que l'ensemble des insights soit généré.

Ce délai est intentionnel et s'explique par la mécanique du cycle :

  • L'auto-learner se réveille toutes les 15 minutes et traite au maximum 3 notes nouvelles par cycle (full-scan)
  • Le traitement complet d'une note avec MLX-LM (génération des questions + réponses + recherche web) prend de 2 à 5 minutes selon la complexité du contenu
  • Résultat : 200 notes ÷ 3 notes/cycle × 15 min/cycle ≈ 17 heures de fonctionnement actif

Ces pauses sont délibérées — elles garantissent que le modèle MLX reste disponible pour le chat en temps réel. Les paramètres AUTOLEARN_FULLSCAN_PER_RUN et AUTOLEARN_INTERVAL_MINUTES dans .env permettent d'accélérer l'amorçage si vous le souhaitez.

Sur MacBook : ObsiRAG se remet automatiquement en marche à la sortie de veille (service launchd) — aucune intervention manuelle n'est nécessaire. L'auto-learner reprend son cycle là où il s'était arrêté, de façon totalement transparente.

Une fois l'amorçage terminé, seules les notes nouvelles ou récemment modifiées sont retraitées à chaque cycle — le fonctionnement courant est quasi-instantané.

Pour les détails de débit, temps de traitement par note et choix du modèle : voir docs/performances.md.


Modèle IA utilisé via MLX-LM

ObsiRAG utilise MLX-LM pour la génération locale, sans serveur externe. Le modèle tourne directement dans le processus Python, exploitant le GPU unifié Apple Silicon via le framework MLX.

Chargement et déchargement automatiques

Le modèle est géré dynamiquement pour minimiser l'empreinte mémoire :

ÉvénementComportement
Ouverture de l'interface webChargement immédiat du modèle (~2 s sur M5)
Utilisation du chatModèle maintenu en mémoire tant que l'UI est active
Inactivité UI > 2 minDéchargement automatique (watchdog toutes les 30 s)
Début d'un cycle auto-learnerChargement automatique si absent
Fin d'un cycle auto-learnerDéchargement si l'UI est inactive
Appel LLM sans modèle chargéChargement à la volée transparent (try-load automatique)

Ce mécanisme garantit que le modèle ne consomme pas de mémoire GPU/Metal inutilement entre les sessions, tout en restant disponible instantanément dès qu'une requête arrive.

UsageOpérationModèle configuré
Chat / RAGRéponses aux questions sur le coffreMLX_CHAT_MODEL (ex. mlx-community/Qwen2.5-7B-Instruct-4bit)
Génération de questionsAuto-learner — questions ancrées dans le champ sémantiqueMême modèle que le chat
Synapses & synthèsesConnexions implicites, synthèse hebdomadaireMême modèle que le chat
EmbeddingsVectorisation des notes et des requêtessentence-transformers local — paraphrase-multilingual-MiniLM-L12-v2 (384 dimensions)

Un seul modèle de chat suffit pour tout. Configurer MLX_CHAT_MODEL dans .env avec le nom HuggingFace de la forme mlx-community/<modele>-4bit. Le modèle est téléchargé automatiquement au premier démarrage.

Les modèles de la communauté mlx-community sur HuggingFace sont déjà convertis et quantizés pour MLX — aucune conversion manuelle n'est nécessaire.

Performances observées (M5, 16 Go)

OpérationOllama (avant)MLX-LM (actuel)Gain
Génération (tokens/s)~13 tok/s~27 tok/s×2
Chargement du modèle30–60 s~2 s×20
Dépendance serveurOllama daemon requisAucune

Stack technique

ComposantTechnologie
LangagePython 3.12
DéploiementmacOS natif (launchd + Python venv)
IAMLX-LM (Apple Silicon, sans serveur)
Base vectorielleChromaDB
Embeddingssentence-transformers — paraphrase-multilingual-MiniLM-L12-v2 (384 dim, CPU)
InterfaceStreamlit
GrapheNetworkX + Pyvis
Recherche webDuckDuckGo Search (sources fiables)
Entités NERspaCy + validation WUDD.ai (top 5 000 entités officielles)
GéolocalisationWikipedia Coordinates API → frontmatter location: (Obsidian Map View)
CoffreObsidian (lecture seule)
Artefacts générésobsirag/insights/, obsirag/synthesis/, obsirag/synapses/, obsirag/conversations/

Fréquence et comportement de l'auto-learner

Paramètre .envValeur par défautRôle
AUTOLEARN_INTERVAL_MINUTES15 minFréquence du cycle — l'auto-learner se réveille toutes les 15 minutes
AUTOLEARN_LOOKBACK_HOURS24 hFenêtre de détection — seules les notes modifiées dans les dernières 24h sont candidates
AUTOLEARN_MIN_REPROCESS_DAYS7 joursDélai de grâce — une note déjà traitée ne sera pas retraitée avant 7 jours

Le premier cycle démarre 5 minutes après le démarrage de l'application, pour laisser le temps au modèle MLX de se charger.

Ces trois paramètres permettent d'adapter le comportement selon l'usage : un intervalle plus court (ex. 30 min) pour un coffre très actif, un lookback plus large (ex. 48h) pour rattraper des notes modifiées en dehors des heures habituelles, et un MIN_REPROCESS_DAYS plus court si vous souhaitez qu'une note soit ré-enrichie plus fréquemment.


Installation

# Cloner le dépôt
git clone https://github.com/PatrickOstertagCH/obsirag.git
cd obsirag

# Configurer l'environnement
cp .env.example .env
# Éditer .env : renseigner VAULT_PATH, MLX_CHAT_MODEL, etc.

# Installer les dépendances Python et configurer le service
./setup.sh

# Démarrer l'application
./start.sh

L'interface est accessible sur http://localhost:8501.

Le modèle MLX est téléchargé automatiquement depuis HuggingFace au premier démarrage (~4 Go pour Qwen2.5-7B-Instruct-4bit).

L'interface est accessible sur http://localhost:8501.

Pour installer ObsiRAG comme service macOS (démarrage automatique au login) :

./install_service.sh

Statut

Projet actif — développé de façon créative et itérative avec Claude Code.

Le dépôt est public. Contributions et idées bienvenues.

How to Install

  1. Download the ZIP or clone the repository
  2. Open the folder as a vault in Obsidian (File → Open Vault)
  3. Obsidian will prompt you to install required plugins

Stats

Stars

1

Forks

0

Last updated 1d ago

Tags

auto-learningchromadbduckduckgoknowledge-baseknowledge-graphllmlocal-aimlxmlx-lmnernlpobsidianpythonragretrieval-augmented-generationspacystreamlit