Architecture d'intelligence évolutive

Phase 21.5–40.12 — Système de skills auto-améliorant pour Arc OS. Statut : Prêt pour la production. Sage Worker (amélioration autonome) + benchmarks (tests A/B) + Marketplace Discovery + recherche sémantique NotebookLM.


Vue d'ensemble

Arc OS opère une architecture de bots fédérés où les child bots proxient les messages utilisateur vers Claude CLI. La Phase 21.1 a introduit le suivi qualité (logs d'exécution, boutons de feedback). La Phase 21.5 boucle la boucle : le système valide désormais ses propres sorties, mémorise les corrections, concentre le contexte et propose des améliorations de manière autonome.

Sept piliers forment la couche intelligence :

Message utilisateur
    │
    ├─► Context Router ──► SKILLS_HINT (top 5 skills pertinents)
    │
    ├─► Learnings ──► bloc LEARNINGS (corrections passées)
    │
    ▼
buildGsdPrompt() ──► Claude CLI ──► Réponse
                                        │
                                        ├─► Binary Evals ──► Notes de bas de page d'avertissement
                                        │
                                        └─► Quality Tracker ──► Métriques
                                                                   │
                                                          Nightly Improve ──► Approbation CEO

Pilier 1 : Moteur d'évaluation binaire

Module : shared/evals.ts Fichiers de spec : skills/<name>/<name>.evals.json

Ce qu'il fait

Chaque skill peut déclarer des règles de validation dans un fichier JSON. Après que Claude génère une réponse, le moteur d'évaluation la vérifie par rapport à toutes les règles applicables. Les échecs produisent des notes de bas de page d'avertissement non bloquantes ajoutées au message Telegram.

Types de règles

Type Vérifie Exemple d'usage
string_contains La réponse inclut une chaîne littérale Vérifier que la sortie JSON a un champ "verdict"
string_not_contains La réponse n'inclut PAS une chaîne Bloquer --force dans les instructions git
regex_match La réponse correspond à un pattern regex S'assurer que l'audit système mentionne disque/RAM/CPU
regex_not_match La réponse ne correspond PAS à un regex Prévenir les patterns de fuite de credentials
max_length Longueur de réponse <= N chars Garder la sortie concise
min_length Longueur de réponse >= N chars Garantir des réponses substantielles

Niveaux de sévérité

Impact des évaluations sur les métriques

Les résultats des évaluations sont enregistrés avec les événements qualité. Quand la boucle d'amélioration nocturne détecte un skill avec un faible taux de succès, les patterns d'échec d'évaluation aident à identifier la cause racine sans nécessiter d'analyse IA.

Exemple de fichier d'évaluation

{
  "version": 1,
  "skill": "code-review",
  "rules": [
    { "id": "cr-001", "name": "Must return JSON verdict", "type": "string_contains", "value": "\"verdict\"", "severity": "warning" },
    { "id": "cr-002", "name": "No console.log debug", "type": "regex_not_match", "pattern": "console\\.log\\(", "severity": "warning" }
  ]
}

Principe de conception

Inspiré du framework d'évaluation Skill Creator d'Anthropic : assertions déclaratives sur les sorties, sans IA dans la boucle pour la validation. Le binaire pass/fail élimine l'ambiguïté.


Pilier 2 : Context Router

Module : shared/context-router.ts Source de données : skills/_registry.json (champs triggers + keywords)

Ce qu'il fait

Avant de construire le prompt GSD, le routeur évalue chaque skill enregistré par rapport au message de l'utilisateur. Les 5 meilleures correspondances sont injectées comme bloc SKILLS_HINT, préparant Claude à se concentrer sur les capacités pertinentes.

Algorithme de scoring

Pour chaque skill :
  score = 0
  pour chaque trigger :   si le message contient le trigger  → score += 2
  pour chaque keyword :   si le message contient le keyword  → score += 1

Trier par score DESC → prendre les 5 premiers

Les correspondances de trigger ont un score plus élevé car les triggers sont des signaux d'invocation explicites (ex. "review", "deploy"). Les keywords fournissent une correspondance sémantique plus large (ex. "OWASP", "Docker").

Pourquoi consultatif, pas filtrant

Le routeur est uniquement consultatif. Claude CLI charge toujours tous les skills via CLAUDE.md. Raisons :

  1. Sécurité : Le filtrage dur via --allowedTools ou les mutations de symlinks risque de casser mi-session si le routeur classe mal un message.
  2. Stabilité : Pas de mutations système de fichiers sur un processus en cours.
  3. Dégradation gracieuse : Si le routeur échoue, Claude a toujours accès à tous les skills.

Économie de fenêtre de contexte

Sans le routeur, les 23 skills se disputent l'attention dans le prompt. Le bloc SKILLS_HINT dit à Claude "concentre-toi sur ceux-ci 3-5" — réduisant l'activation de skills non pertinents et gardant les réponses sur le sujet. C'est du priming de contexte, pas une réduction de contexte.


Pilier 3 : Reflect Loop (Learnings persistants)

Module : shared/learnings.ts Stockage : {PROJECT_CWD}/learnings.md

Ce qu'il fait

Quand le CEO appuie sur "Fix It" ou "👎", le système capture une règle d'apprentissage et la persiste dans un fichier markdown. À chaque message suivant, les learnings accumulés sont injectés dans le prompt GSD comme bloc LEARNINGS.

Pipeline Événement → Apprentissage

CEO appuie sur 🛠️ Fix It
    │
    ├─► addLearning(source: "fixit", rule: "Fix requested for: <context>")
    │
    └─► projectLearnings rechargé depuis le disque

CEO appuie sur 👎
    │
    ├─► addLearning(source: "negative", rule: "Negative feedback on: <context>")
    │
    └─► qualityTracker.logFeedback(positive: false)

Format du fichier learnings

# Learnings

> Auto-généré. Injecté dans le prompt GSD au début de la session.

## Règles

- [2026-04-03T14:22:00Z] [fixit] Always use t-call for translations in Odoo QWeb
- [2026-04-03T15:10:00Z] [negative] Avoid sudo in deployment scripts

Injection dans le prompt

La fonction formatForPrompt() prend les learnings les plus récents (jusqu'à 2000 chars), les inverse (les plus récents en premier) et les formate ainsi :

LEARNINGS (corrections passées — suivre ces règles) :
- Avoid sudo in deployment scripts
- Always use t-call for translations in Odoo QWeb

Principe de conception

Inspiré du Reflect System de Claude : les corrections ne corrigent pas seulement la réponse actuelle — elles deviennent des règles persistantes qui préviennent les régressions. Le système construit une "mémoire immunitaire" au fil du temps.


Pilier 4 : Karpathy Loop (Auto-amélioration nocturne)

Module : scripts/nightly-improve.ts Planning : Quotidien à 03:00 UTC via cron État : mcp-server/state/improvement-proposals.json

Ce qu'il fait

  1. Lit config/bot_registry.json pour énumérer tous les child bots
  2. Pour chaque enfant : lit quality-metrics.json depuis son répertoire d'état
  3. Appelle findUnderperformingSkills() — filtre les skills où :
    • applied_count >= 3 (taille minimale d'échantillon)
    • ET soit success_rate < 80% SOIT feedback_negative > feedback_positive
  4. Lit learnings.md pour les patterns de correction liés
  5. Génère des propositions basées sur des templates (déterministe, sans IA)
  6. Envoie un rapport résumé au Telegram du CEO
  7. Envoie des cartes de proposition individuelles avec des boutons inline Approve/Reject

Flux d'approbation CEO

Script nocturne ──► Telegram : Carte de proposition
                        │
           ┌────────────┼────────────┐
           ▼                         ▼
    ✅ Approuver                ❌ Rejeter
           │                         │
    Backup skill.md             Marquer rejeté
    comme skill.v1.md           dans proposals.json
    (max 3 versions)
           │
    Marquer approuvé
    dans proposals.json

Versioning des skills

À l'approbation, le master bot crée des sauvegardes versionnées :

Principe de conception

Inspiré de la boucle AutoResearch de Karpathy : modifier → vérifier → garder/abandonner → répéter. La différence critique : les propositions sont basées sur des templates et requièrent une approbation humaine. Pas de réécriture autonome de skills — le CEO reste l'autorité finale.


Phase 36+ — Couche sémantique NotebookLM

La Phase 36.3 a ajouté un cinquième canal de feedback : Google NotebookLM comme mémoire sémantique long terme.

Événements CRM (fermeture ticket, mise à jour wiki)
    │
    └─► POST fire-and-forget vers bridge /sync
                                    │
                                    ▼
                          NotebookLM Bridge (:19213)
                            ├── SyncWorker (asyncio.Queue, 3 essais)
                            └── notebooklm-py → Google NotebookLM
                                    │
                                    ▼
                          Notebook mis à jour avec nouvelle source
                                    │
              ┌─────────────────────┤
              ▼                     ▼
    outil ask_notebooklm     Neural Skill Generator
    (chat Cloud PM, 15s)     (POST /skills/generate, 30s)
              │                     │
              ▼                     ▼
    Réponse sémantique en    Skill Markdown pour
    contexte projet          revue + sauvegarde (Phase 36.6)

La Phase 36.6 a ajouté le Neural Skill Generator : le CEO définit un objectif d'extraction, le bridge interroge NotebookLM avec un prompt structuré d'AI Architect, retourne un Rulebook Markdown strict qui peut être sauvegardé comme skill.

La Phase 36.7 a exposé les notebooks dans l'UI sidebar (point de statut vert/rouge, nombre de sources, lien externe).


Pilier 6 : Sage Worker (Phase 40.11c)

Module : shared/sage.ts Déclencheur : POST /api/crm/sage/analyze ou scripts/nightly-improve.ts

Ce qu'il fait

Sage est un moteur autonome d'amélioration de skills. Il analyse les skills en utilisant les métriques qualité + le feedback utilisateur (learnings), puis génère du contenu amélioré concret via Anthropic API (modèle Haiku pour la rentabilité). Les résultats sont stockés comme skill_update_requests (PRs) dans la base de données pour revue CEO.

Flux

Requête Sage Analyze
    │
    ├─► Charger les skills actifs depuis DB (skillQueries)
    ├─► Croiser avec les métriques qualité (readMetrics)
    ├─► Prioriser les sous-performants (findUnderperformingSkills)
    │
    ▼ Pour chaque skill cible (max 5) :
    ├─► Charger contenu skill + fork (si project-scoped)
    ├─► Charger métriques qualité (taux succès, comptes feedback)
    ├─► Charger learnings liés (corrections utilisateur)
    ├─► Passer si PR en attente existe déjà
    ├─► Construire le prompt (contenu + métriques + learnings)
    ├─► Appeler Anthropic API (Haiku, timeout 30s)
    ├─► Parser la réponse (NO_CHANGE = passer)
    ├─► Insérer skill_update_request (proposed_by: 'sage')
    └─► Lancer benchmark automatique (Pilier 7) sur nouvelle PR

Pourquoi un endpoint CRM, pas un worker

Sage a besoin d'un accès direct à la DB (skillQueries, benchmarkQueries) — les workers s'exécutent comme sous-processus sans connexion DB. Sage ne reçoit pas de messages utilisateur ; c'est un analyseur d'arrière-plan dont la sortie = lignes DB, pas du texte de chat.


Pilier 7 : Approbations basées sur les données — Benchmarks (Phase 40.11d)

Module : shared/sage.ts (runBenchmark()) DB : shared/migrations/005_skill_benchmarks.ts API : POST /api/crm/sage/benchmark, GET /api/crm/skill-updates/:id/benchmarks

Ce qu'il fait

Avant que le CEO approuve une PR Sage, le système prouve que le changement fonctionne. Génère 3 scénarios de test, exécute l'ancien et le nouveau contenu de skill à travers eux (test A/B à l'aveugle), et utilise un juge LLM pour déterminer lequel est meilleur. Score combiné : règles d'évaluation déterministes (60%) + juge LLM (40%).

Flux de benchmark

Lancer Benchmark (requestId)
    │
    ├─► Charger skill_update_request (current_content + proposed_content)
    ├─► Charger eval_rules depuis DB
    │
    ▼ Générer 3 scénarios de test (Haiku)
    │
    ▼ Pour chaque scénario :
    ├─► Exécuter ancien contenu skill → old_output
    ├─► Exécuter nouveau contenu skill → new_output
    ├─► Randomiser l'ordre A/B (prévention du biais de position)
    ├─► Juge LLM évalue les deux (1-10) + raison
    ├─► Score déterministe eval_rules (si disponible)
    ├─► Score combiné : eval 60% + LLM 40%
    └─► Sauvegarder dans la table skill_benchmarks
    │
    ▼ Mettre à jour les métadonnées PR
    ├─► verdict : PASSED / FAILED / TIE
    ├─► improvement_pct : ((new_avg - old_avg) / old_avg * 100)
    └─► BenchmarkBadge visible sur l'en-tête PR frontend

Frontend : Mode Battle

SkillEvolution.jsx → composant BenchmarkReport :


Carte des fichiers

shared/
├── evals.ts              ← Pilier 1 : Moteur d'évaluation binaire
├── context-router.ts     ← Pilier 2 : Context Router (routeContextFromDb)
├── learnings.ts          ← Pilier 3 : Reflect Loop
├── quality.ts            ← Étendu : findUnderperformingSkills()
├── sage.ts               ← Piliers 6+7 : Sage Worker + Benchmarks (runSageAnalysis, runBenchmark)
├── db.ts                 ← SQLite SSOT : skillQueries, benchmarkQueries, chatQueries
├── migrations/
│   ├── 004_skill_system.ts     ← skills_global, forks, evolution_logs, update_requests
│   └── 005_skill_benchmarks.ts ← skill_benchmarks (résultats tests A/B)
├── crm-routes.ts         ← CRM API : 55+ endpoints (Sage + benchmarks + évolution skill)
└── ui_templates.ts       ← Étendu : improvementProposal()

scripts/
├── nightly-improve.ts    ← Pilier 4 : Karpathy Loop (utilise maintenant Sage + DB)
└── migrate-skills-to-db.ts ← Migration nucléaire : fichiers → DB

clients/
├── arc-cli.ts            ← ARC CLI (Phase 31.5) : login, start, projects
└── knowledge-mcp.ts      ← Déprécié (Phase 38 → sous-commandes CLI)

services/
└── notebooklm-bridge/    ← Pilier 5 : Recherche sémantique NotebookLM (Phase 36.3)
    ├── main.py           ← FastAPI (:19213), /query, /sync, /notebooks/init, /health
    └── seed_knowledge.py ← Import bulk des connaissances existantes

frontend/src/crm/pages/
└── SkillEvolution.jsx    ← UI deux panneaux : explorateur + détail (Content/Evals/Evolution/PRs)
                             BenchmarkBadge, BenchmarkReport, Mode Battle, bouton analyze Sage

child-bot/bot.ts          ← Intégration : démarrage + prompt GSD + notes eval + /learnings
child-bot/ingest-watcher.ts ← Auto-ingest : fs.watch sur raw/ → inbox CRM (Phase 28)
master-bot/bot.ts         ← Intégration : déclencheur cron nocturne Sage