ARTEFACT· STUDIO
Devis
Studio interne2026·5 jours + 1 post-benchmark·Architecte + dev + benchmarkeur solo

725 fichiers Obsidian transformés en retrieval cognitif local.

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.

  • 86.7 %Hit@1
  • 0.914MRR
  • -55.7 %Tokens par mission
  • 273 MBStorage embeddings
Contexte

Le vault BRAIN brûlait 119 k tokens par mission. Inacceptable.

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.

Architecture

Trois couches, un mode par complexité.

Le Smart Router classifie la query, puis dispatche sur hybrid retrieval, et — si la query le mérite — sur le graph expansion + reasoning agent.

  1. L1

    Smart Router · 3 modes

    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.

  2. L2

    Hybrid retrieval BM25 + embeddings

    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).

  3. L3

    Graph expansion + reasoning

    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.

Stack

100 % local, 0 € cloud, reproductible.

01

Storage

  • ChromaDB persistent
  • 8 438 chunks v4
  • Smart Re-Chunking par content_type
02

Embeddings

  • Ollama nomic-embed-text (274 MB)
  • Ollama qwen2.5:7b (4,7 GB)
03

Retrieval

  • rank_bm25
  • NetworkX 3.6.1
  • RRF fusion k=30
04

Scripts

  • index-vault-v2
  • build-bm25-index
  • build-graph
  • query-vault
  • reasoning_agent
  • route-mission
Décisions clés

Quatre choix calibrés sur benchmark.

  1. 01

    RRF k=30 (data-driven)

    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.

  2. 02

    Smart Re-Chunking par content_type

    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.

  3. 03

    3 leviers de calibration

    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 %.

  4. 04

    Fallback heuristique si Ollama down

    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.

Métriques

Ce qui s’est mesuré, pas ce qui se raconte.

86,7 %
Hit@1
bench retrieval v4
96,7 %
Hit@3
0 %
Hit@5
0,914
MRR
Mean Reciprocal Rank
-55,7 %
Tokens par mission
119 k → 53 k
0 MB
Storage embeddings
0 MB
RAM runtime
0
Chunks v4 indexés
3,5 s
Latence FLAT
20 - 35 s
Latence DEEP
reasoning multi-round
Code

Smart Router · dispatch 3 modes

scripts/route-mission.py — classification + dispatch FLAT / CONNECTED / DEEP.

python
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)

Lessons / Next

Ce que ce projet a gravé.

  1. Latence DEEP 20-35 s acceptable pour reasoning lourd — mais le Smart Router doit bien classifier FLAT vs DEEP, sinon on perd l’utilisateur.
  2. 312 wiki-links cassés dans le vault, 219 sans suggestion fiable : dette accumulée du vault, pas un bug du système. À nettoyer en amont.
  3. Threshold trop strict si le LLM hallucine des missing_aspects sur des queries vagues — sanity check côté agent obligatoire.
  4. Smart Re-Chunking : trade-off Hit@3/5 ↑ mais Hit@1 ↓ à 57 % avant leviers. Toujours mesurer avant de garder un changement d’indexation.