Storage
- ChromaDB persistent
- 8 438 chunks v4
- Smart Re-Chunking par content_type
Système de retrieval cognitif local qui transforme un vault Obsidian de 725 fichiers en base interrogeable par chunks ciblés. 100 % local via Ollama, 0 € cloud.
Projet interne. Le vault BRAIN (725 fichiers Obsidian) était lu en brut par l’agent librarian, ce qui coûtait 119 000 tokens à chaque mission — un cap qui plafonnait l’usage du système multi-agents et défonçait la fenêtre de contexte sur les missions denses.
Objectif : retrieval cognitif local par chunks. 100 % local via Ollama (zéro cloud, zéro fuite de données), avec une qualité de retrieval qui dépasse la lecture brute. Cinq jours de build + une journée de post-benchmark pour calibrer les leviers.
Le Smart Router classifie la query, puis dispatche sur hybrid retrieval, et — si la query le mérite — sur le graph expansion + reasoning agent.
Classification de la query → FLAT (factuel court, latence 3,5 s), CONNECTED (multi-doc, latence intermédiaire), DEEP (reasoning multi-round, latence 20-35 s). Fallback heuristique si Ollama down.
rank_bm25 lexical + nomic-embed-text (274 MB Ollama) sémantique. Fusion RRF (Reciprocal Rank Fusion) avec k=30, choix data-driven (+10 pts Hit@1 vs k=60 initial).
Graphe NetworkX 723 nodes / 18 185 edges (wiki-links Obsidian). Expansion contextuelle puis reasoning agent multi-round sur qwen2.5:7b (4,7 GB) pour les queries DEEP.
k=60 par défaut donnait un Hit@1 médiocre. Sweep paramétrique sur le bench → k=30 gagne 10 points de Hit@1 sans dégrader Hit@3/5. Décision tranchée à la mesure, pas au feeling.
5 types détectés à l’indexation : table / code_block / frontmatter / narrative / mapping_list. Chacun a sa stratégie de chunk (taille, overlap, démérite). Trade-off : Hit@3/5 grimpe mais Hit@1 chute à 57 % avant calibration des leviers.
content_type_demerit (rééquilibrage chunks longs) + narrative_exact_boost (boost exact match sur narratif) + min_content_filter (anti-bruit). Combinés, on remonte Hit@1 de 57 % à 86,7 %.
Robustesse non-négociable : si Ollama tombe, le système bascule sur BM25 pur + heuristiques wiki-links. Aucune mission n’est bloquée par un service local indisponible.
scripts/route-mission.py — classification + dispatch FLAT / CONNECTED / DEEP.
from enum import Enum
from .classifier import classify_query
from .retrieval import flat_search, hybrid_search, deep_reasoning
class Mode(str, Enum):
FLAT = "flat"
CONNECTED = "connected"
DEEP = "deep"
def route(query: str, k: int = 30):
mode = classify_query(query) # heuristique + LLM fallback
if mode is Mode.FLAT:
# factuel court — BM25 + embeddings, RRF k=30
return flat_search(query, k=k)
if mode is Mode.CONNECTED:
# multi-doc — hybrid retrieval + graph expansion 1-hop
return hybrid_search(query, k=k, hops=1)
# DEEP — graph expansion + reasoning multi-round qwen2.5:7b
return deep_reasoning(query, k=k, max_rounds=4)