Mode deckbuilding Mode deckbuilding Dernière mise à jour : 2026-05-10 Depuis 2026-04-24 (Axe 2 Phase 1), un 4e intent 'deckbuilding' s'ajoute à 'rules' | 'synergy' | 'meta'. Il déclenche un pipeline dédié pour produire des decklists structurées exécutables (60 cartes MTG, 40+12+3 Riftbound, 60+sideboard FAB) là où le RAG classique produisait de la prose d'analyse. Flux ( src/services/rag/deckbuilding/) 1. Classification ( classify.ts) Haiku retourne 'deckbuilding' quand la question exige une liste exhaustive avec un nombre de cartes précis ("decklist complète", "40 + 12 runes", "avec sideboard"). À distinguer de synergy qui reste une discussion d'archétype en prose. Ordre de priorité : deckbuilding > meta > synergy > rules. 2. Dispatch ( answer.ts) Early-return vers askDeckbuilding() juste après résolution de l'intent. Le retrieval standard est jeté, askDeckbuilding relance son propre retrieval avec intent='synergy' forcé. 3. Spec Haiku ( deckbuilding/spec.ts) Un appel Haiku produit un DeckSpec (Zod) structuré : format tailles (mainboard, sideboard, runes, battlefields, equipment) max copies (généralement 4 MTG, 3 Riftbound, etc.) rôles cibles ( creatures, spells, lands, equipment…) anchor cards (cartes obligatoires) Fallback defaults par jeu si Haiku renvoie un JSON invalide : MTG Standard : 60 + 15, max 4 Riftbound : 40 + 0 + 3 + 12r + 3bf FAB CC : 60 + 0, max 3 4. Anchor cards Les fresh + sticky mentions (@-cartes de l'Axe 1) deviennent obligatoires dans la decklist. Le prompt Opus a une règle stricte « tu DOIS inclure ces cartes ». Log warning serveur si une anchor est absente de la réponse finale. 5. Retrieval synergy retrieveChunks() est appelé avec intent='synergy' → déclenche l'expansion mécanique ( retrieve/synergy-expansion.ts). retrieveChunks accepte 'deckbuilding' mais le mappe vers 'synergy' en interne (fallback safe). 6. Prompt Opus spécialisé ( prompt-deckbuilding.ts) DECKBUILDING_SYSTEM_PROMPT impose : total exact par section max copies non-basiques (basic lands FR/EN whitelistés) anchor cards obligatoires pool strict (uniquement cartes du contexte) format markdown par rôles : ### Créatures (24) + 4× [[card:Nom]] 7. userPrompt assemblage Ordre des blocs : SPEC DU DECK (JSON Haiku) CARTES OBLIGATOIRES (ANCHORS) (anchors fresh + sticky) CARTES CITÉES PAR LE JOUEUR (ce tour) (full) CARTES ÉVOQUÉES DANS LA CONVERSATION (sticky abrégées) DECKLISTS DE TOURNOI (mainboard + sideboard complets, jusqu'à 5) POOL DE CARTES ÉLIGIBLES (organisé par rôle, cap 30 par rôle) CARTES CANDIDATES (chunks [CARD] + [META], pas [BASE]/ [EXT]) 8. Génération Opus promptStream(DECKBUILDING_SYSTEM_PROMPT, userPrompt, 'opus') streamé. Post-processing : autoWrapCardMentions + buildWrapAliases + enrichissement extraCards. 9. Pool exhaustif + decklists d'inspiration (Phase 2, 2026-04-24) fetchEligibleCards() ( deckbuilding/pool.ts) scroll Qdrant avec filtre structurel : MTG : card_mtg_legal_formats + post-filtre card_mtg_color_identity ⊆ spec.colors FAB : card_legal_heroes Riftbound : sans filtre Qdrant (collection mono-format) + post-filtre card_domains ⊆ spec.domains fetchInspirationDecklists() : hybrid search (dense+sparse) sur meta_type='tournament_deck' filtré par meta_format — jusqu'à 5 decklists similaires (infra : MTGTop8 pour MTG, RiftboundStats pour Riftbound, fabtcg.com pour FAB) selectRoleCandidates() : pré-filtre chaque rôle de la spec à cap 30 par rôle, priorité aux cartes déjà vues dans les decklists d'inspiration TM / Ark Nova : pas de champs structurels → retombent silencieusement sur Phase 1 (retrieval synergy seul) scrollAllPoints() accepte opts.filter et opts.withVector (défaut true — Phase 2 passe false pour économiser ~120 MB sur MTG 30k cartes) 10. Validation structurelle + retry auto (Phase 3, 2026-04-24) parseDeckResponse() : extrait les cartes par section (main / sideboard / runes / battlefields) via regex stricte avec ×/x/X obligatoire — les lignes de prose ne matchent pas validateDeck() : Total exact par section Max copies non-basiques (basic lands whitelist FR/EN : Plains/Island/Swamp/Mountain/Forest/Wastes + Plaine/Île/Marais/Montagne/Forêt) Anchor cards présentes (≥1 copie) Cartes inconnues du catalogue → warning non bloquant Si validation échoue : formatRetryPrompt() construit nouveau userPrompt listant les issues + contraintes strictes Opus relancé jusqu'à 2 retries (3 tentatives max) Pendant retry : token séparateur visible streamé pour transparence — done.fullAnswer écrase le content côté frontend, l'utilisateur final voit uniquement la version corrigée Si après 3 tentatives la decklist reste invalide : on garde la dernière + warning log serveur (pas d'échec total) 11. Frontend Aucun changement. Le rendu markdown gère titres + listes + Nx [[card:X]] (texte standard). CardZoomModal fonctionne automatiquement. Hors scope Phases 1/2/3 Pas de composant Pas d'export .dec ni bouton Copier Pas de badge de validation côté frontend (verdict logué serveur, pas exposé via done) Pas de support TM / Ark Nova (pas d'équivalent format/archétype dans leurs données) Helpers exportés autoWrapCardMentions, buildWrapAliases, renderFullCard, renderShortCard sont exportés depuis answer.ts pour réutilisation par deckbuilding/ask.ts.