Aller au contenu

Architecture future : Embeddings pour learnings.jsonl

Statut : DIFFÉRÉ — À implémenter quand learnings.jsonl dépasse 200 entrées

Compteur actuel : ~57 entrées (2026-02-10)

Problème

La recherche par grep sur les tags devient inefficace au-delà de 200 learnings :

  • Résultats trop nombreux pour les tags fréquents (#backend, #auth)
  • Pas de pertinence sémantique (un learning sur "CORS" ne matche pas une query "security headers")
  • Pas de ranking par similarité

Solution proposée

Remplacer grep par une recherche sémantique via embeddings vectoriels.

Architecture cible

data/
├── learnings.jsonl           # Source de vérité (append-only, inchangé)
├── learnings-embeddings.npy  # Vecteurs NumPy (régénéré)
├── learnings-index.faiss     # Index FAISS pour recherche k-NN
└── embeddings-meta.json      # Métadonnées: model, timestamp, count

Format embeddings-meta.json

{
  "model": "text-embedding-3-small",
  "dimensions": 1536,
  "count": 247,
  "last_rebuilt": "2026-04-15T10:32:00Z",
  "source_hash": "sha256:abc123..."
}

Flow de recherche

1. Query: "security headers rate limiting CORS"
2. Embed query → vecteur 1536d
3. FAISS search top-k (k=10)
4. Retourner learnings pertinents triés par similarité

Intégration dans gov.md

Modifier l'étape 0 "Charger les learnings pertinents" :

0. **Charger les learnings pertinents** :
   - SI `data/learnings-index.faiss` existe ET embeddings-meta.count > 200 :
     - Construire query = "{domain} {tags probables}"
     - Recherche sémantique top-5 via embeddings
   - SINON :
     - Fallback grep sur tags (comportement actuel)
   - Injecter les learnings dans le contexte

Trigger de rebuild

L'index doit être reconstruit quand :

  1. wc -l learnings.jsonl != embeddings-meta.count
  2. Ou manuellement via scripts/rebuild-embeddings.py

Vérification au démarrage du workflow :

LINES=$(wc -l < data/learnings.jsonl)
META_COUNT=$(jq -r '.count' data/embeddings-meta.json 2>/dev/null || echo 0)
if [ "$LINES" -ne "$META_COUNT" ]; then
  python scripts/rebuild-embeddings.py
fi

Dépendances requises

faiss-cpu>=1.7.4        # ou faiss-gpu pour GPU
sentence-transformers   # si modèle local
openai>=1.0             # si OpenAI embeddings
numpy>=1.24

Script rebuild-embeddings.py (esquisse)

#!/usr/bin/env python3
import json
import numpy as np
import faiss
from openai import OpenAI  # ou sentence_transformers

def main():
    # 1. Lire learnings.jsonl
    learnings = []
    with open("data/learnings.jsonl") as f:
        for line in f:
            learnings.append(json.loads(line))

    # 2. Préparer textes à embedder
    texts = [f"{l['story']} {' '.join(l['tags'])} {l['learning']}" for l in learnings]

    # 3. Générer embeddings
    client = OpenAI()
    response = client.embeddings.create(
        model="text-embedding-3-small",
        input=texts
    )
    embeddings = np.array([e.embedding for e in response.data])

    # 4. Construire index FAISS
    dimension = embeddings.shape[1]
    index = faiss.IndexFlatIP(dimension)  # Inner Product (cosine après normalisation)
    faiss.normalize_L2(embeddings)
    index.add(embeddings)

    # 5. Sauvegarder
    np.save("data/learnings-embeddings.npy", embeddings)
    faiss.write_index(index, "data/learnings-index.faiss")

    meta = {
        "model": "text-embedding-3-small",
        "dimensions": dimension,
        "count": len(learnings),
        "last_rebuilt": datetime.now().isoformat()
    }
    with open("data/embeddings-meta.json", "w") as f:
        json.dump(meta, f, indent=2)

if __name__ == "__main__":
    main()

Alternatives considérées

Option Avantages Inconvénients
OpenAI embeddings Haute qualité, pas de GPU Coût API, latence réseau
sentence-transformers local Gratuit, offline Qualité moindre, RAM
ChromaDB Persistance intégrée Dépendance lourde
Qdrant Production-ready Overengineering pour ~500 learnings

Recommandation : OpenAI text-embedding-3-small pour la qualité, avec fallback sentence-transformers si offline requis.

Critère de déclenchement

Implémenter quand :

  • wc -l data/learnings.jsonl > 200
  • ET recherche grep devient inefficace (retours > 20 résultats régulièrement)

Documenté le 2026-02-10 — À réviser lors du passage du seuil