Architecture IA modulaire post-LLM — un pari : atteindre la qualité conversationnelle des LLMs sur des domaines ciblés, en tournant localement sur du matériel modeste, sans GPU, sans cloud, sans hallucination.
Statut : Phases 1 à 4 livrées (cœur). Pipeline end-to-end CPU, 6 modules + agent Morrigan-Code, classifieur LNN entraîné (Brigid CfC), knowledge graph (Ogham), génération neuronale RWKV avec RAG strict (0 hallucination) en streaming, et compression d'index vectoriel (quantization int8/binary ×4-32, persistance disque, ANN IVF). Reste l'optimisation de la latence de génération et l'ingestion à l'échelle Wikipedia (Phase 5 → production). Projet de recherche, pas un produit fini.
Les LLMs actuels sont puissants mais massifs, opaques, sujets aux hallucinations, et dépendants d'infrastructures cloud coûteuses. Morrigan explore une autre voie : décomposer l'intelligence conversationnelle en modules spécialisés, chacun implémenté par le composant le plus efficient pour sa tâche.
- Réseau neuronal liquide (LNN/CfC) pour l'intuition et la classification
- Moteur symbolique (pyDatalog) pour le raisonnement logique
- Mémoire vectorielle (pgvector + embeddings) pour les connaissances
- Petit modèle de langage (templates Jinja2 → RWKV → Llama Q4) pour la génération
- Mémoire de travail pour le contexte conversationnel
L'hypothèse centrale : l'information utile qu'un LLM stocke dans ses poids tient dans une fraction de l'espace, si on la décompose correctement. Les techniques de quantization et de pruning montrent que la majorité des paramètres d'un LLM sont redondants. Morrigan essaie de prouver qu'une architecture décomposée, à 100× moins de paramètres, peut rivaliser sur des domaines bien définis.
Contraintes de design :
- Tout doit tourner en local sur un PC modeste (CPU, 8-16 Go de RAM)
- Pas d'hallucination : Morrigan répond à partir de son corpus vérifiable ou admet qu'elle ne sait pas
- Chaque module est indépendant et remplaçable (clean boundaries, API claire)
- Interprétabilité : chaque réponse est traçable vers sa source
Six modules nommés d'après la mythologie celtique :
[ Utilisateur ]
↓
┌──────────────────────┐
│ An Dagda │ Orchestrateur
│ (routing, assembly) │ (classification + pipeline)
└──────────┬───────────┘
│
┌────────────┬───────┴────────┬────────────┐
↓ ↓ ↓ ↓
┌────────┐ ┌────────┐ ┌────────┐ ┌──────────┐
│ Brigid │ │ Ogham │ │ Danann │ │ Cauldron │
│ LNN │ │symbol. │ │ vector │ │ working │
│ CfC │ │pyDatal.│ │ memory │ │ memory │
└────────┘ └────────┘ └────────┘ └──────────┘
│ │ │ │
└────────────┴────────┬───────┴────────────┘
↓
┌──────────┐
│ Scáthach │ Génération de texte
│ template │ (templates → RWKV → Llama)
│ / LM │
└────┬─────┘
↓
[ Réponse ]
| Module | Rôle | Phase 1 | Roadmap |
|---|---|---|---|
| An Dagda | Orchestration, routing des queries | Heuristiques mots-clés | → classification LNN |
| Brigid | Intuition, pattern recognition, classification | Squelette | → CfC entraîné (200-500 ex) |
| Ogham | Raisonnement symbolique, règles | pyDatalog de base | → knowledge graph |
| Danann | Mémoire vectorielle, retrieval | sentence-transformers + cosine + boost lexical | → multi-level index, reranker |
| Scáthach | Génération de texte | Templates Jinja2 | → RWKV / Llama 3.2 Q4 |
| Cauldron | Mémoire de travail, contexte | Session-based historique | → condensation, épisodique |
- Python 3.11+
- PyTorch + ncps (Liquid Neural Networks / CfC)
- sentence-transformers (
all-MiniLM-L6-v2, 384-D, ~80 Mo) - pyDatalog (raisonnement symbolique)
- Jinja2 (templates de génération — Phase 1)
- Supabase pgvector (persistance optionnelle)
- python-telegram-bot v22 (interface Telegram)
- numpy (store en mémoire + cosine similarity)
- Pipeline end-to-end : input → classification → retrieval → raisonnement → génération → output
- Classification d'intention par LNN : Brigid (CfC ncps 384→16→6) entraînée sur 504 exemples FR, val_acc ~88 %, avec fallback heuristiques sous le seuil de confiance
- Génération neuronale en streaming : Scáthach v2 backend RWKV-6 1.6B Q4_K (llama.cpp, CPU), mode template en fallback, affichage token par token (CLI + Telegram)
- RAG strict (0 hallucination) : génération ancrée sur chunks Danann + faits KG ; refus déterministe « je ne sais pas » sans contexte fiable
- Knowledge graph (Ogham) : 267 entités / 430 relations extraites du corpus, requêtes
compare/facts_about - Agent Morrigan-Code : vérification de syntaxe 6 langages (Python, Bash, JS, SQL, HTML, CSS)
- Compression d'index vectoriel (Danann) : quantization int8 (×4) / binary (×32 + 2 étages), persistance disque sans float32, ANN IVF sous-linéaire — pour tenir un gros corpus sur PC modeste
- Retrieval hybride Danann : cosine + boost lexical + reranker cross-encoder, filtrage par domaine, garde anti-faux-positif
- Observabilité
/stats(CLI + Telegram) : routage, probas Brigid, generated_by, latence - Interfaces CLI + Telegram (streaming),
.envauto-load, backends Danann branchables (memory / Supabase pgvector), scripts d'ingestion - 304 tests (pytest), 5 workflows CI (tests, version-sync, release, brigid-train, kg-build)
- Retrieval/classification : ~50-150 ms par requête (CPU, i5-10210U), empreinte ~300 Mo
- Génération RWKV (1.6B Q4_K, CPU contraint) : ~10-12 tok/s, latence p50 ~12.7 s pour une réponse de quelques phrases — cf.
docs/benchmarks.md - 0 hallucination : 100 % de refus sur les queries hors-corpus (benchmark)
- Ingestion à l'échelle : 500 articles Wikipédia FR → 37 967 chunks, index int8 14.7 MB (×4.0), chargé au runtime en 0.29 s (zéro réembedding) — cf.
docs/ingestion.md
- Latence de génération élevée : un 1.6B sur CPU contraint reste loin de la cible < 1 s (p50 ~12.7 s ; le streaming masque le ressenti avec un 1er token ~1.4 s). Plafond surtout matériel (RAM saturée + CPU U-series).
- Pas de zero-shot : Morrigan ne répond que depuis son corpus. Par design — fallback honnête plutôt qu'hallucination.
- Passage à très grande échelle (Wikipedia complet) : l'ingestion à l'échelle est branchée, mesurée et servie au runtime (
MORRIGAN_INDEX, cf.docs/ingestion.md), et la recherche IVF + compression int8 est disponible (sous-linéaire sur index compressé) ; reste à éprouver le tout sur un corpus de plusieurs millions de chunks. - Benchmarks vs LLMs commerciaux : pas encore réalisés.
- Python 3.11+
- Environ 500 Mo de disque (venv + modèle d'embeddings)
git clone <repo>
cd morrigan
python -m venv .venv
.venv/Scripts/activate # Windows
# source .venv/bin/activate # Linux/macOS
pip install -r requirements.txtLe premier lancement télécharge sentence-transformers/all-MiniLM-L6-v2 depuis
HuggingFace (~80 Mo).
Copier .env.example en .env et remplir au besoin :
MORRIGAN_TELEGRAM_TOKEN= # Pour l'interface Telegram
SUPABASE_URL= # Pour la persistance vectorielle (optionnel)
SUPABASE_KEY=
HF_TOKEN= # Pour éviter le rate limit HuggingFace (optionnel)
MORRIGAN_INDEX= # Dossier d'index compressé à servir (optionnel).venv/Scripts/python interfaces/cli.pyInterface interactive dans le terminal. Tape ta question, quit pour sortir.
set MORRIGAN_TELEGRAM_TOKEN=ton_token_botfather
.venv/Scripts/python interfaces/telegram_bot.pyCommandes disponibles dans le bot :
/start— présentation du projet/help— aide sur les modules/reset— efface la mémoire de conversation/stats— statistiques des modules
.venv/Scripts/python -m uvicorn interfaces.api:app --host 0.0.0.0 --port 8000Quatre endpoints :
POST /query— JSON{"query": "...", "session_id": "..."}→ réponse complète + routage (query_type,modules,domain_hint,generated_by,latency_s).POST /query/stream— même body, réponse SSE : un eventdatapar fragment de génération, puis un eventdoneportant le routage.GET /health—{"status": "ok", "modules": [...]}.GET /stats— observabilité (texte + compteurs JSON).
Le dagda de prod (Brigid + Ogham + Danann via MORRIGAN_INDEX ou
data/knowledge + Scáthach RWKV + Cauldron) est composé au démarrage.
python -m pytest tests/Lance la suite complète (orchestration, retrieval, génération, API…). Utile pour valider une modification sans casser les cas connus.
.venv/Scripts/python scripts/ingest_knowledge.py \
--source data/knowledge \
--backend memoryIngère récursivement tous les .md et .txt du dossier donné. Backend
memory par défaut, supabase pour la persistance.
Pour servir un gros corpus sans le réembedder à chaque démarrage, construis
un index compressé sur disque, puis pointe MORRIGAN_INDEX dessus :
# Corpus local → index int8 sur disque
.venv/Scripts/python scripts/build_compressed_index.py ^
--source data/knowledge --output data/models/index_default
# … ou Wikipédia FR en streaming (Phase 5)
.venv/Scripts/python scripts/ingest_wikipedia.py --max-articles 5000
# Lancer le CLI (ou le bot) en pointant l'index
set MORRIGAN_INDEX=data/models/index_default
.venv/Scripts/python interfaces/cli.pyLe CLI et le bot chargent alors l'index via Danann.load_index (RAM
réduite, zéro réembedding). Si MORRIGAN_INDEX est absent ou invalide,
le runtime retombe sur l'ingestion de data/knowledge.
morrigan/
├── core/
│ ├── dagda.py # Orchestrateur central
│ ├── types.py # Types partagés (ModuleInput/Output, etc.)
│ └── pipeline.py
├── modules/
│ ├── brigid/ # LNN (classification, intuition)
│ ├── ogham/ # Moteur symbolique pyDatalog
│ ├── danann/ # Mémoire vectorielle (memory + Supabase)
│ ├── scathach/ # Génération de texte (templates Jinja2)
│ └── cauldron/ # Mémoire de travail
├── interfaces/
│ ├── cli.py
│ ├── telegram_bot.py
│ └── api.py
├── data/
│ └── knowledge/ # Corpus (.md, .txt)
├── scripts/
│ ├── ingest_knowledge.py
│ ├── ingest_wikipedia.py
│ ├── build_compressed_index.py
│ ├── train_brigid.py
│ └── supabase_schema.sql
├── config/
│ ├── morrigan.yaml
│ ├── ogham_rules.yaml
│ └── routing_rules.yaml
└── tests/
- An Dagda orchestrateur
- 5 modules enregistrés et branchés
- Danann retrieval hybride (cosine + lexical)
- Scáthach templates Jinja2
- Interfaces CLI + Telegram
- Corpus d'exemple, script d'ingestion
- Tests d'intégration
- Métadonnées riches par chunk (type, domaine, source, version, confiance)
- Reranker cross-encoder sur les top candidats
- Premier agent spécialisé : Morrigan-Code (Python, JS, Bash, SQL, HTML, CSS)
- Vérifieur de syntaxe par langage (
ast,bash -n,node --check,sqlparse,html.parser,tinycss2) - Corpus dédié code (6 docs FR curatés, auto-tagués
domain=code)
- Vérifieur de syntaxe par langage (
- Brigid entraînée (CfC ncps 384→16→6 sur 504 exemples, val_acc ~88 %)
- Knowledge graph via extraction d'entités (networkx, 267 entités / 430 relations)
- Scáthach v2 avec backend RWKV (RWKV-6 World 1.6B Q4_K via llama.cpp, CPU)
- Mode RAG strict (génération ancrée sur chunks Danann + faits KG Ogham ; refus déterministe hors-corpus → 0 hallucination)
- Harnais de benchmarks + rapport (
scripts/benchmark.py,docs/benchmarks.md) - Benchmarks vs LLMs commerciaux sur domaines cibles (à faire)
- Latence cible : < 1s par réponse complète sur CPU —
⚠️ non atteinte (p50 ~12.7s sur 1.6B Q4_K CPU contraint, cf.docs/benchmarks.md) → chantier d'optimisation (quant plus agressive, modèle plus petit, streaming)
- Compression d'index binary/int8 : quantization vectorielle
pure-NumPy (
Int8Index×4,BinaryIndex×32 + recherche 2 étages), branchée dans Danann (compression=int8/binary, float32 jamais conservé). Recall ≥ 0.9 (int8) / ≥ 0.8 (binary two-stage). - Index sur disque :
Danann.save_index/load_index(corpus.json + vectors.npz, chargement sans float32 ni réembedding)scripts/build_compressed_index.py. Démo : 75 chunks, ×4.0.
- Recherche scalable (ANN) :
IVFIndexpur-NumPy (k-means + probes, sous-linéaire),Danann(ann="ivf").- DiskANN/SPANN (graph-ANN natifs C++/Rust) délibérément reportés : build lourd hors philo « PC modeste, deps minimales ». L'IVF pur-NumPy couvre le retrieval scalable sans dépendance native.
- Ingestion Wikipedia FR à grande échelle — pipeline prêt
(
build_compressed_index.py, ingestion incrémentale) ; reste à brancher une source Wikipedia et mesurer à l'échelle. - Matryoshka embeddings — non retenu : nécessiterait de changer l'embedder partagé (MiniLM), ce qui casserait le checkpoint Brigid et les index existants. Le 2-étages binary → int8 en est l'équivalent fonctionnel (recherche grossière puis fine) sans changer de modèle.
- Cible « < 5 Go corpus » : atteignable via compression (×4 à ×32)
- persistance disque ; validé sur le corpus actuel, à confirmer à l'échelle Wikipedia.
- Ingestion à l'échelle Wikipedia FR (
scripts/ingest_wikipedia.py, streaming sans dump local) + index persisté servi au runtime (MORRIGAN_INDEX→Danann.load_index, zéro réembedding au boot) + IVF combiné à int8 (sous-linéaire ET compressé). Validé sur 500 art → 37 967 chunks, chargé en 0.29 s. Cf.docs/ingestion.md. - API HTTP FastAPI + SSE (
interfaces/api.py) :POST /query(JSON) +POST /query/stream(SSE, token par token viaprocess_stream),GET /health,GET /stats. Démarrage :uvicorn interfaces.api:app --host 0.0.0.0 --port 8000. - Dockerisation complète (Dockerfile CPU + compose, volumes pour
modèle GGUF + index +
.env) - Intégration Gungnir (client HTTP côté Gungnir, loose coupling)
- Monitoring et observabilité (Prometheus optionnel)
La Morrigan est une déesse celtique de la guerre, de la transformation et de la prophétie. Elle apparaît souvent sous la forme d'un corbeau et fait partie d'une triade (Morrigan, Badb, Macha). Elle incarne l'intelligence qui voit au-delà du visible et qui transforme le chaos en structure — une métaphore qui colle bien à ce qu'on essaie de faire : structurer l'intelligence en modules clairs, au lieu d'un bloc opaque et monolithique.
Chaque module porte le nom d'une figure celtique liée à sa fonction : An Dagda le père orchestrateur, Brigid la forge et l'inspiration, Ogham l'écriture sacrée et le savoir druidique, Danann la mère des Tuatha Dé (les connaissances ancestrales), Scáthach la guerrière-maîtresse (qui forme et articule), Cauldron le chaudron inépuisable (la mémoire qui se renouvelle).
Kevin — dev no-code, passionné de mythologie celtique, projets IA alternatifs. Morrigan fait partie d'une constellation de projets explorant des architectures IA post-LLM (Ogma, Munnin, Vargr, Gungnir).
Propriétaire — tous droits réservés. Copyright © 2026 Kevin (Scarlet Wolf).
Voir le fichier LICENSE. Aucun usage, copie, modification ou
distribution n'est autorisé sans accord écrit préalable du titulaire.
Projet en développement actif, en Phase 1. Les contributions, suggestions, critiques d'architecture, et idées de corpus ciblés sont les bienvenues.
Les axes où le feedback est particulièrement utile :
- Curation de corpus sur les domaines cibles (code, réseau, culture générale)
- Vérifieurs de cohérence par langage de programmation
- Benchmarks honnêtes contre les LLMs commerciaux sur domaines restreints
- Compression d'index vectoriels (binary, Matryoshka, DiskANN)
- Petits modèles génératifs locaux adaptés au français