Skip to content

Génération de cartes - Pipeline complet

🎴 Génération de cartes - Pipeline complet

Section titled “🎴 Génération de cartes - Pipeline complet”
Frontend Backend Service IA WebSocket

Cette section présente le flux complet de génération de cartes, depuis l’action utilisateur sur l’application mobile jusqu’à la création des cartes par le service d’intelligence artificielle. Ce pipeline illustre l’interaction entre les trois composants principaux de l’architecture Mindlet.

Pipeline de génération de cartes
100%
sequenceDiagram
    autonumber
    participant U as 📱 Utilisateur
    participant M as React Native
    participant A as Laravel API
    participant Q as Redis Queue
    participant IA as LangGraph
    participant DB as PostgreSQL
    participant WS as WebSocket

    U->>M: Upload document + paramètres
    M->>A: POST /v1/cards/generate
    A->>A: Valider quota & input
    A->>Q: Dispatch GenerateCardsJob
    A-->>M: 202 Accepted {generation_id}
    M->>M: Afficher "En cours..."

    Q->>A: Job démarre
    A->>WS: CardGenerationStarted
    WS-->>M: Event started
    M->>M: Mise à jour UI

    A->>IA: POST /runs/wait (chunks, params)
    Note over IA: Planner → Generator → Critic → Refiner → Finalizer
    IA-->>A: {cards, metadata}

    A->>WS: CardsGenerated
    WS-->>M: Event generated

    A->>DB: INSERT cards, collection
    A->>WS: CardsPersisted
    WS-->>M: Event persisted

    A->>A: Consommer quota
    A->>WS: CardGenerationCompleted
    WS-->>M: Event completed
    M->>M: Notification + refresh liste

    U->>M: Voir les cartes générées

L’utilisateur peut générer des cartes de deux manières :

  1. Depuis un prompt texte : description du sujet souhaité
  2. Depuis des ressources : documents PDF, images, audio préalablement uploadés
src/features/ai/mutations.ts
/**
* Mutation pour déclencher la génération de cartes.
*
* Gère :
* - L'envoi des paramètres au backend
* - La mise à jour du store de génération
* - L'invalidation du cache après complétion
*/
export const useAiCardGeneration = () => {
const setGenerationStarted = useAiGenerationStore((s) => s.setGenerationStarted)
return apiClient.useMutation(
'post',
'/v1/cards/generate',
{
onSuccess: (data) => {
// Initialiser le tracking côté frontend
setGenerationStarted(data.generation_id, data.collection_id)
Toast.show({
type: 'info',
text1: 'Génération en cours',
text2: 'Vous serez notifié quand les cartes seront prêtes',
})
},
onError: (error) => {
Toast.show({
type: 'error',
text1: 'Erreur',
text2: getErrorMessage(error),
})
},
}
)
}

Le store Zustand aiGeneration maintient l’état de la génération côté client :

src/stores/aiGeneration.ts
interface AiGenerationState {
isGenerating: boolean
generationId: string | null
collectionId: string | null
error: string | null
shouldScrollToTop: boolean
setGenerationStarted: (generationId: string, collectionId?: string) => void
setGenerationCompleted: (generationId: string) => void
setGenerationFailed: (generationId: string, reason?: string) => void
reset: () => void
}
export const useAiGenerationStore = create<AiGenerationState>((set) => ({
isGenerating: false,
generationId: null,
collectionId: null,
error: null,
shouldScrollToTop: false,
setGenerationStarted: (generationId, collectionId) => {
set({
isGenerating: true,
generationId,
collectionId: collectionId ?? null,
error: null,
})
},
setGenerationCompleted: (generationId) => {
set({
isGenerating: false,
shouldScrollToTop: true, // Auto-scroll vers les nouvelles cartes
})
},
setGenerationFailed: (generationId, reason) => {
set({
isGenerating: false,
error: reason ?? 'Une erreur est survenue',
})
},
}))

Le composant AiWebSocketListener reçoit les événements broadcast par le backend :

src/components/providers/AiWebSocketListener.tsx
export function AiWebSocketListener() {
const echo = useEchoStore((s) => s.echo)
const userId = useAuthStore((s) => s.user?.id)
const store = useAiGenerationStore()
useEffect(() => {
if (!echo || !userId) return
const channel = echo.private(`users.${userId}`)
// 6 événements possibles
channel.listen('.card.generation.started', (e) => {
store.setGenerationStarted(e.generation_id, e.collection_id)
})
channel.listen('.card.generation.generated', (e) => {
// Cartes générées par l'IA, en cours de persistance
})
channel.listen('.card.generation.collection_created', (e) => {
// Nouvelle collection créée pour les cartes
})
channel.listen('.card.generation.cards_persisted', (e) => {
// Cartes sauvegardées en base
})
channel.listen('.card.generation.completed', (e) => {
store.setGenerationCompleted(e.generation_id)
// Invalider le cache pour afficher les nouvelles cartes
queryClient.invalidateQueries({
queryKey: collectionsQueryKeys.list(),
})
// Notification push locale
Notifications.scheduleNotificationAsync({
content: {
title: 'Cartes générées !',
body: `${e.total_cards_persisted} cartes créées avec succès`,
},
trigger: null,
})
})
channel.listen('.card.generation.failed', (e) => {
store.setGenerationFailed(e.generation_id, e.reason)
})
return () => {
channel.stopListening('.card.generation.started')
channel.stopListening('.card.generation.completed')
channel.stopListening('.card.generation.failed')
}
}, [echo, userId])
return null
}

Le contrôleur reçoit la requête, valide les données et dispatch un job asynchrone :

app/Http/Controllers/CardGeneration/GenerateCardsController.php
class GenerateCardsController extends Controller
{
public function __invoke(
GenerateCardsRequest $request,
CardGenerationService $service
): JsonResponse {
// La Form Request valide et convertit en DTO
$input = $request->toDto();
// Le service vérifie le quota et dispatch le job
$result = $service->dispatchGeneration(
user: $request->user(),
input: $input
);
// Réponse immédiate (le traitement continue en arrière-plan)
return response()->json([
'generation_id' => $result->generationId,
'status' => $result->status->value,
'total_cards_requested' => $result->totalCardsRequested,
], 202); // HTTP 202 Accepted
}
}
app/Services/CardGeneration/CardGenerationService.php
class CardGenerationService
{
public function __construct(
private readonly QuotaService $quotaService,
private readonly GenerationHistoryService $history,
) {}
public function dispatchGeneration(
User $user,
CardGenerationInput $input
): CardGenerationDispatchResult {
// 1. Vérifier que l'utilisateur a du quota
$this->quotaService->guardCanGenerate(
$user,
$input->getTotalCardsRequested()
);
// 2. Enregistrer la demande dans l'historique
$generation = $this->history->recordRequest($user, $input);
// 3. Dispatcher le job vers la queue Redis
GenerateCardsJob::dispatch(
$user->id,
$generation->generation_id,
$input
)->onQueue('ai'); // Queue dédiée aux jobs IA
return new CardGenerationDispatchResult(
generationId: $generation->generation_id,
status: CardGenerationStatus::QUEUED,
totalCardsRequested: $input->getTotalCardsRequested(),
);
}
}

Le job s’exécute en arrière-plan et orchestre tout le processus :

app/Jobs/CardGeneration/GenerateCardsJob.php
class GenerateCardsJob implements ShouldQueue
{
public int $tries = 5;
public int $timeout = 600; // 10 minutes max
public string $queue = 'ai';
public function handle(
CardGenerationService $service,
CardPersistenceService $persistence,
QuotaService $quota,
): void {
$user = User::findOrFail($this->userId);
// 1. Notifier le début du traitement
event(new CardGenerationStarted(
$this->userId,
$this->generationId,
$this->input->getTotalCardsRequested()
));
// 2. Attendre le batch d'embeddings si nécessaire
if ($this->input->batchId) {
$this->waitForBatchReady($this->input->batchId);
}
// 3. Appeler le service IA LangGraph
$result = $service->generate($user, $this->input);
event(new CardsGenerated(
$this->userId,
$this->generationId,
$result->stats->cardsGenerated
));
// 4. Persister les cartes en base
$persistenceResult = $persistence->persist($user, $result, $this->input);
if ($persistenceResult->collectionCreated) {
event(new CollectionCreated(
$this->userId,
$this->generationId,
$persistenceResult->collectionId,
$persistenceResult->collectionTitle
));
}
event(new CardsPersisted(
$this->userId,
$this->generationId,
$persistenceResult->collectionId,
$persistenceResult->cardsPersisted
));
// 5. Consommer le quota APRÈS la persistance réussie
$quota->consumeQuota($user, $result->stats->cardsGenerated);
// 6. Notifier la complétion
event(new CardGenerationCompleted(
$this->userId,
$this->generationId,
$persistenceResult->collectionId,
$result->stats->cardsGenerated,
$persistenceResult->cardsPersisted
));
}
public function failed(\Throwable $exception): void
{
event(new CardGenerationFailed(
$this->userId,
$this->generationId,
$exception->getMessage(),
$this->input->batchId
));
}
}
app/Services/LangGraph/LangGraphCardService.php
class LangGraphCardService
{
public function generate(array $input): array
{
// Construction du payload pour LangGraph
$payload = [
'assistant_id' => config('langgraph.assistants.card_generator'),
'input' => [
'prompt' => $input['prompt'] ?? null,
'language' => $input['language'],
'user_context' => $input['user_context'] ?? null,
'card_types' => $input['card_types'],
'difficulty' => $input['difficulty'] ?? 'intermediate',
'embedded_chunks' => $input['embedded_chunks'] ?? [],
],
];
// Appel synchrone avec attente (jusqu'à 5 minutes)
$response = $this->client->runWait($payload);
return [
'generated_cards' => $response['generated_cards'] ?? [],
'collection_metadata' => $response['collection_metadata'] ?? null,
'quality_scores' => $response['quality_scores'] ?? [],
];
}
}

Une fois le backend connecté au service IA, le Card Generator Graph prend le relais. Voir les sections suivantes pour le détail de l’architecture agentic.

Résumé du flux IA
100%
flowchart LR
    subgraph "Backend Laravel"
        JOB["GenerateCardsJob"]
    end

    subgraph "Service IA"
        VAL["Validate Inputs"]
        SEL["Select Chunks"]
        PLAN["Planner"]
        GEN["Generator"]
        CRIT["Critic"]
        REF["Refiner"]
        FIN["Finalizer"]
    end

    JOB -->|HTTP POST| VAL
    VAL --> SEL --> PLAN --> GEN --> CRIT
    CRIT -->|Score < 7.5| REF --> GEN
    CRIT -->|Score >= 7.5| FIN
    FIN -->|Response JSON| JOB

    style JOB fill:#1565c0,stroke:#1976d2
    style PLAN fill:#e65100,stroke:#f57c00
    style CRIT fill:#ad1457,stroke:#c2185b

Architecture complète - Génération de cartes
100%
flowchart TB
    subgraph "Frontend Mobile"
        UI["📱 Interface utilisateur"]
        STORE["Zustand Store"]
        RQ["React Query Cache"]
        WS_CLIENT["WebSocket Client"]
    end

    subgraph "Backend Laravel"
        CTRL["Controller"]
        SVC["CardGenerationService"]
        JOB["GenerateCardsJob"]
        PERSIST["CardPersistenceService"]
        QUOTA["QuotaService"]
        EVENTS["Event Broadcaster"]
    end

    subgraph "Infrastructure"
        REDIS["Redis Queue"]
        PG[("PostgreSQL")]
        REVERB["Laravel Reverb"]
    end

    subgraph "Service IA"
        LANGGRAPH["LangGraph Cloud"]
        AGENTS["Planner → Generator → Critic → Refiner"]
        MISTRAL["Mistral AI"]
    end

    UI -->|1. POST /generate| CTRL
    CTRL --> SVC
    SVC -->|2. Dispatch| REDIS
    REDIS -->|3. Process| JOB
    JOB -->|4. Call| LANGGRAPH
    LANGGRAPH --> AGENTS
    AGENTS --> MISTRAL
    MISTRAL -->|5. Response| AGENTS
    AGENTS -->|6. Cards JSON| JOB
    JOB -->|7. Save| PERSIST --> PG
    JOB --> QUOTA
    JOB -->|8. Broadcast| EVENTS --> REVERB
    REVERB -->|9. WebSocket| WS_CLIENT
    WS_CLIENT --> STORE
    STORE --> UI
    UI -->|10. Refresh| RQ

    style UI fill:#2e7d32,stroke:#388e3c
    style LANGGRAPH fill:#e65100,stroke:#f57c00
    style PG fill:#1565c0,stroke:#1976d2
    style REVERB fill:#7b1fa2,stroke:#9c27b0

Le backend broadcast 6 événements distincts pendant le processus de génération :

ÉvénementMomentPayload
card.generation.startedJob démarregeneration_id, total_cards_requested
card.generation.generatedIA terminegeneration_id, cards_generated
card.generation.collection_createdNouvelle collectioncollection_id, title
card.generation.cards_persistedCartes en DBcollection_id, cards_persisted
card.generation.completedSuccès finalcollection_id, total_*
card.generation.failedErreurreason, batch_id

Le Card Generator Graph est le composant le plus complexe et innovant du système. Contrairement à un simple appel LLM qui génère des cartes en une seule passe, nous utilisons une architecture agentic — c’est-à-dire un système où plusieurs agents IA collaborent et s’auto-corrigent.

Le système utilise 5 agents spécialisés qui collaborent en boucle jusqu’à atteindre la qualité souhaitée :

AgentRôleAnalogie
Planner 📋Analyse le contenu et crée une stratégie de générationLe chef de projet qui fait le plan
Generator ✏️Crée les cartes selon le plan établiLe rédacteur qui écrit
Critic 🔍Évalue chaque carte et détecte les problèmesLe relecteur qui pointe les erreurs
Refiner 🔧Améliore les cartes selon le feedback du CriticLe correcteur qui améliore
FinalizerValide et génère les métadonnées finalesLe responsable qualité qui approuve
Flux de collaboration des agents
100%
flowchart LR
    A["📋 Planner"] --> B["✏️ Generator"]
    B --> C["🔍 Critic"]
    C -->|"Score < 7.5"| D["🔧 Refiner"]
    D -->|"Éessayer"| B
    C -->|"Score ≥ 7.5"| E["✅ Finalizer"]
    D -->|"Max tentatives"| E
    
    style A fill:#1565c0,stroke:#1976d2
    style B fill:#e65100,stroke:#f57c00
    style C fill:#ad1457,stroke:#c2185b
    style D fill:#2e7d32,stroke:#388e3c
    style E fill:#00838f,stroke:#0097a7

Ce diagramme montre les boucles de feedback qui sont la clé de la qualité. Notez comment le critic peut renvoyer vers le generator (pour régénérer) ou vers le refiner (pour améliorer) :

Card Generator Graph
100%
graph TD;
    __start__([__start__]):::first
    validate_inputs(validate_inputs)
    select_chunks(select_chunks)
    planner(planner)
    generator(generator)
    critic(critic)
    refiner(refiner)
    finalizer(finalizer)
    __end__([__end__]):::last
    
    __start__ --> validate_inputs;
    validate_inputs -. end .-> __end__;
    validate_inputs -.-> select_chunks;
    select_chunks --> planner;
    planner --> generator;
    generator --> critic;
    critic -.-> finalizer;
    critic -.-> generator;
    critic -.-> refiner;
    refiner -.-> finalizer;
    refiner -.-> generator;
    finalizer --> __end__;
    
    classDef default fill:#5e35b1,line-height:1.2
    classDef first fill-opacity:0
    classDef last fill:#7c4dff

Le diagramme ci-dessous montre l’architecture interne avec les différents outils (tools) que chaque agent peut utiliser :

Architecture agentic détaillée
100%
flowchart TB
    subgraph AGENT["🤖 CARD GENERATION AGENT<br/>(mistral-medium-latest)"]
        direction TB
        
        PLANNER["🎯 PLANNER<br/>Analyse le contenu<br/>Planifie la stratégie"]
        
        subgraph LOOP["🔄 Boucle d'amélioration"]
            direction LR
            GENERATOR["⚡ GENERATOR<br/>Crée les cartes"]
            CRITIC["🔍 CRITIC<br/>Évalue qualité<br/>& diversité"]
            REFINER["✨ REFINER<br/>Améliore<br/>les cartes"]
        end
        
        COLLECTION["📚 CARD COLLECTION"]
        FINALIZER["📋 FINALIZER<br/>Génère metadata<br/>Vérifie complétude"]
    end
    
    PLANNER --> GENERATOR
    GENERATOR --> CRITIC
    CRITIC -->|"Besoin d'amélioration"| REFINER
    REFINER -->|"Re-génération"| GENERATOR
    CRITIC -->|"Cartes validées"| COLLECTION
    REFINER -->|"Cartes améliorées"| COLLECTION
    COLLECTION --> FINALIZER
    
    style AGENT fill:#424242,stroke:#616161,stroke-width:2px
    style PLANNER fill:#1565c0,stroke:#1976d2
    style GENERATOR fill:#e65100,stroke:#f57c00
    style CRITIC fill:#ad1457,stroke:#c2185b
    style REFINER fill:#2e7d32,stroke:#388e3c
    style COLLECTION fill:#7b1fa2,stroke:#9c27b0
    style FINALIZER fill:#00838f,stroke:#0097a7
    style LOOP fill:#37474f,stroke:#78909c,stroke-dasharray: 5 5

Le système peut générer 9 types de cartes différents, chacun adapté à un style d’apprentissage particulier. Le Planner décide de la répartition optimale en fonction du contenu.

class CardType(str, Enum):
"""Types de cartes disponibles pour la génération.
Chaque type a un format de sortie spécifique et une difficulté
de génération différente (reflétée dans le nombre de tokens).
"""
FLASHCARD = "flashcard" # Question/Réponse classique
MCQ = "mcq" # QCM avec distracteurs intelligents
FREE_TEXT = "free_text" # Réponse libre évaluée par mots-clés
TRUE_OR_FALSE = "true_or_false" # Affirmation à valider
MATCH_PAIR = "match_pair" # Correspondance terme ↔ définition
SLIDER = "slider" # Estimation numérique
DRAG_AND_DROP = "drag_and_drop" # Placement dans des catégories
RANKING = "ranking" # Classement par ordre
GEO_GUESS = "geo_guess" # Localisation sur une carte

Flashcard

Question/réponse classique pour la mémorisation rapide. Le format le plus simple et le plus efficace pour le rappel actif.

QCM

Question à choix multiples avec distracteurs intelligents. Le système génère des options plausibles mais incorrectes pour tester la compréhension.

Vrai/Faux

Affirmation à valider avec explication détaillée. Idéal pour corriger les idées reçues.

Match Pair

Relier des éléments correspondants entre eux.

Texte libre

Réponse ouverte pour approfondir la compréhension.

Classement

Ordonner des éléments selon un critère donné.


  1. Validation des inputs

    • Vérification des chunks embeddés
    • Validation des types de cartes demandés
    • Vérification du nombre de cartes souhaité
  2. Sélection des chunks

    • Algorithme K-means pour diversité
    • Sélection des chunks les plus représentatifs
    • Équilibrage du contenu
  3. Planification (Planner)

    • Analyse du contenu des chunks
    • Répartition des types de cartes
    • Stratégie de couverture du sujet
  4. Génération (Generator)

    • Création des cartes selon le plan
    • Respect des formats par type
    • Génération des distracteurs (QCM)
  5. Évaluation (Critic)

    • Score de qualité par carte
    • Détection des doublons
    • Vérification de la diversité
  6. Raffinement (Refiner)

    • Amélioration des cartes faibles
    • Suppression des doublons
    • Ajustement du niveau de difficulté
  7. Finalisation

    • Génération des métadonnées
    • Calcul des statistiques
    • Validation finale de la collection

Chaque agent a un rôle précis et utilise un prompt YAML dédié. Voici comment ils fonctionnent en détail :

Le Planner est le stratège du système. Il analyse les chunks et décide :

  • Combien de cartes de chaque type générer
  • Quels sujets prioriser
  • Quelle courbe de difficulté adopter
class GenerationPlan(BaseModel):
"""Plan de génération créé par le Planner.
Ce plan guide le Generator pour produire des cartes équilibrées
et adaptées au contenu source.
"""
card_distribution: Dict[CardType, int] # Ex: {FLASHCARD: 5, MCQ: 3}
focus_topics: List[str] # Sujets identifiés à couvrir
difficulty_curve: List[int] # [1, 2, 2, 3, 3, 4] = progression
reasoning: str # Explication (pour le debug)

Le Generator crée les cartes selon le plan établi. Il utilise les chunks comme source de vérité et respecte le format de chaque type :

{
"type": "flashcard",
"question": "Qu'est-ce que la photosynthèse ?",
"answer": "La photosynthèse est le processus par lequel...",
"difficulty": 2,
"tags": ["biologie", "plantes"]
}

Le Critic est le contrôle qualité du système. Pour chaque carte générée, il évalue :

class CardQualityScore(BaseModel):
"""Score de qualité calculé par le Critic.
Chaque dimension est évaluée de 0 à 1, puis combinée
en un score global sur 10.
"""
clarity: float # La question est-elle claire et compréhensible ?
relevance: float # La carte teste-t-elle bien le contenu source ?
difficulty_fit: float # Le niveau correspond-il à celui demandé ?
uniqueness: float # La carte est-elle différente des autres ?
overall: float # Score final = moyenne pondérée (0-10)
suggestions: List[str] # Conseils précis pour améliorer la carte
CritèreSeuilExplication
Score minimum7.5/10En dessous, la carte part au Refiner
Similarité max75%Au-delà, considéré comme doublon
Tentatives max2Après 2 échecs, la carte est rejetée

Le Refiner améliore les cartes qui n’ont pas atteint le score minimum. Il reçoit la carte originale + les suggestions du Critic :

Le Refiner améliore les cartes selon le feedback du Critic :

Flux du Refiner
100%
graph LR
    A[Carte originale] --> B{Score < 7.5?}
    B -->|Non| C[Approuvée]
    B -->|Oui| D[Refiner]
    D --> E{Tentative < 2?}
    E -->|Oui| F[Carte améliorée]
    F --> G[Re-évaluation]
    E -->|Non| H[Rejetée]
    
    style C fill:#388e3c
    style H fill:#c62828

class AgentActionType(str, Enum):
"""Types d'actions disponibles pour l'agent"""
GENERATE = "generate" # Générer de nouvelles cartes
EVALUATE = "evaluate" # Évaluer les cartes existantes
REFINE = "refine" # Améliorer une carte
REMOVE_DUPLICATE = "remove" # Supprimer un doublon
FINALIZE = "finalize" # Finaliser la collection
class AgentDecision(str, Enum):
"""Décisions possibles après évaluation"""
APPROVE = "approve" # Carte approuvée
REFINE = "refine" # Nécessite amélioration
REGENERATE = "regenerate" # Regénérer complètement
REJECT = "reject" # Rejeter la carte
FINISH = "finish" # Terminer la génération
class CritiqueResult(BaseModel):
"""Résultat de l'évaluation par le Critic"""
cards_approved: List[str] # IDs des cartes approuvées
cards_to_refine: List[str] # IDs à améliorer
cards_to_reject: List[str] # IDs à rejeter
duplicate_pairs: List[Tuple] # Paires de doublons
diversity_score: float # Score de diversité
needs_more_generation: bool # Si plus de cartes nécessaires

Le module batch_optimizer.py calcule dynamiquement la taille des batches pour optimiser les appels LLM :

@dataclass(frozen=True)
class BatchConfig:
"""Configuration de batch optimisée"""
batch_size: int # Nombre de cartes par batch
estimated_input_tokens: int # Tokens d'entrée estimés
estimated_output_tokens: int # Tokens de sortie estimés
total_batches: int # Nombre total de batches
Type de carteTokens output estimés
Flashcard150
QCM250
Vrai/Faux120
Match Pair200
Texte libre180
Classement220
Drag & Drop280
Slider100
Geo Guess150

from src.graphs.card_generator import card_generator_graph
from src.models.card_types import CardType, CardTypeRequest
# Configuration des types de cartes souhaités
card_requests = [
CardTypeRequest(card_type=CardType.FLASHCARD, count=5),
CardTypeRequest(card_type=CardType.MCQ, count=3),
CardTypeRequest(card_type=CardType.TRUE_OR_FALSE, count=2)
]
# Invocation du graphe
result = await card_generator_graph.ainvoke({
"embedded_chunks": embedded_chunks, # Depuis le graphe d'embedding
"card_type_requests": card_requests,
"language": "fr",
"user_context": "Sujet: Biologie - La photosynthèse"
})
# Récupération des résultats
generated_cards = result["generated_cards"]
metadata = result["collection_metadata"]
print(f"Cartes générées: {len(generated_cards)}")
print(f"Score moyen: {metadata['average_quality_score']}")

Les prompts des agents sont stockés dans src/prompts/ au format YAML avec support multi-langue :

src/prompts/generator.yaml
system:
fr: |
Tu es un expert en création de contenu pédagogique.
Tu génères des cartes d'apprentissage de haute qualité.
en: |
You are an expert in educational content creation.
You generate high-quality learning cards.
user:
fr: |
Génère {count} cartes de type {card_type} à partir du contenu suivant:
{content}
Niveau de difficulté: {difficulty}
FichierAgentRôle
planner.yamlPlannerPlanification de la génération
generator.yamlGeneratorCréation des cartes
critic.yamlCriticÉvaluation de qualité
refiner.yamlRefinerAmélioration des cartes

MétriqueDescriptionCible
Score moyenQualité moyenne des cartes> 8.0/10
Taux d’approbationCartes approuvées du premier coup> 70%
Doublons détectésCartes similaires identifiées< 5%
Couverture sujet% du contenu couvert> 85%
Diversité typesÉquilibre entre les typesSelon config

  • Directorysrc/
    • Directorygraphs/
      • card_generator.py 🎴 Graph principal
    • Directorynodes/
      • Directorycard_generation/
        • validate_inputs.py
        • select_chunks.py
        • planner.py
        • generator.py
        • critic.py
        • refiner.py
        • finalizer.py
    • Directorymodels/
      • card_types.py Types de cartes
      • agent_types.py Types agent
    • Directoryprompts/
      • planner.yaml
      • generator.yaml
      • critic.yaml
      • refiner.yaml

Génération intelligente de cartes d’apprentissage grâce à une architecture agentic.