#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ prompt_injection_v2.py — Sliding Pipeline v2 · Pernod Ricard ============================================================= Génère les blocs de contraintes injectés dans les prompts des agents, à partir du design system v2 (layouts_v2.yaml). Produit : 1. agent_constraints_v2.md — référence complète (debug/doc) 2. prompt_the_designer_injected_v2.md — prompt Designer prêt pour Mistral Studio 3. prompt_the_encoder_injected_v2.md — prompt Encoder prêt pour Mistral Studio Usage : python3 prompt_injection_v2.py python3 prompt_injection_v2.py --layouts layouts_v2.yaml \ --designer prompt_the_designer_v2.md \ --encoder prompt_the_encoder_v2.md """ import argparse import sys from pathlib import Path import yaml # ───────────────────────────────────────────────────────────────────────────── # DONNÉES ÉDITORIALES v2 (savoirs métier — 21 layouts uniquement) # ───────────────────────────────────────────────────────────────────────────── SEQUENCING_RULES = """### Règles de fluidité - Maximum 2 layouts texte consécutifs : `default_bullets`, `two_cols_text` - Après 3 slides denses → intercaler une respiration : `big_stat`, `key_message`, `section_divider` - Jamais 2 `section_divider` consécutifs - Alterner les modes : un slide dark (`section_divider`, `key_message`) toutes les 3-5 slides claires donne le rythme "sandwich" du design system - Le dernier slide de contenu avant `end_slide` doit être narratif (`recommendation_card` ou `key_message`) ### Règles de choix de layout - 1 seul chiffre décisif → `big_stat` (jamais `kpi_grid` avec 1 item) - 2 à 6 indicateurs chiffrés → `kpi_grid` - Comparaison ou opposition (avant/après, attentes/réalité, risques/solutions) → `two_cols_text` - 2 à 5 étapes séquentielles → `numbered_steps` - Phases avec périodes temporelles (mois, trimestres) → `phases_timeline` - 3 à 6 concepts ou valeurs formant un système → `circular_diagram` - Recommandation unique et actionnable → `recommendation_card` - Citation ou message à marteler seul → `key_message` - Transformation avant/après → `from_to_pairs` (plus parlant que two_cols_text) - Planning projet avec tâches datées → `gantt_timeline` - Frise chronologique d'historique ou de jalons annuels → `yearly_timeline` - Tableau comparatif multi-critères → `comparison_table` - Responsabilités par rôle → `raci_table` - Flux séquentiel de 3 à 6 étapes (sans dates) → `process_arrow` - Organigramme hiérarchique → `org_chart` - Priorisation effort/impact → `matrix_2x2` - Si le contenu ne rentre dans aucun layout spécialisé → `default_bullets` ### Règles de quantité - Présentation 20 min → 10 à 15 slides max - Présentation 10 min → 6 à 10 slides max - Fusionner si > 15 slides : regrouper les slides proches thématiquement - `default_bullets` : 5 bullets maximum — privilégier le format "Mot-clé : explication" - Un `###` du Markdown avec un seul chiffre fort → envisager `big_stat` séparé - Un `##` du Markdown = 1 `section_divider` (sauf présentation < 6 slides)""" NARRATIVE_PATTERNS = """### Pattern "Problem → Solution → Proof" Adapté aux présentations de recommandation stratégique. ``` cover_split executive_summary (synthèse SCR dès le début) section_divider ("Le problème") big_stat (chiffre choc) two_cols_text ou default_bullets section_divider ("Notre réponse") numbered_steps kpi_grid (preuve chiffrée) recommendation_card (ce qu'on demande) end_slide ``` ### Pattern "Roadmap Deck" Adapté aux présentations de planification / lancement de projet. ``` cover_split executive_summary (où on va et pourquoi) kpi_grid (état des lieux chiffré) phases_timeline (le calendrier) numbered_steps (comment on s'organise) recommendation_card (décisions à prendre) end_slide ``` ### Pattern "Executive Briefing" Adapté aux présentations courtes (< 10 slides) pour un CODIR. ``` cover_split executive_summary key_message (le So What en 1 slide) kpi_grid ou big_stat recommendation_card end_slide ``` ### Pattern "Vision & Valeurs" Adapté aux présentations de transformation ou de culture. ``` cover_split executive_summary section_divider circular_diagram (le système de valeurs/concepts) default_bullets (le cadre, les principes) key_message (la conviction) recommendation_card end_slide ``` ### Règles d'assemblage des patterns - Les patterns sont des points de départ, pas des contraintes rigides - Hybrider 2 patterns est possible si le contenu le justifie - Toujours préserver : cover_split en premier, end_slide en dernier - Les section_dividers sont optionnels pour les patterns courts (< 8 slides)""" # ───────────────────────────────────────────────────────────────────────────── # EXEMPLES YAML v2 PAR LAYOUT (champs canoniques render_engine_v2) # ───────────────────────────────────────────────────────────────────────────── EXAMPLES = { "cover_split": [ 'titre: "Titre de la présentation"', 'sous_titre: "Tagline en une phrase"', ], "executive_summary": [ 'titre: "Le So What en titre affirmatif"', 'situation: "État des lieux factuel..."', 'complication: "Le problème ou la tension..."', 'resolution: "La réponse proposée..."', ], "section_divider": [ 'titre: "Nom de la section"', "# le numéro de section est généré automatiquement", ], "big_stat": [ 'titre: "Titre affirmatif"', 'valeur: "78%"', 'description: "ce que le chiffre signifie"', 'source: "Référence — 2026"', ], "two_cols_text": [ 'titre: "Titre affirmatif"', "left:", ' titre: "Titre colonne gauche"', " bullets:", ' - texte: "Premier point"', ' - texte: "Deuxième point"', "right:", ' titre: "Titre colonne droite"', " bullets:", ' - texte: "Premier point"', ], "kpi_grid": [ 'titre: "Titre affirmatif"', "items:", ' - label: "Indicateur 1"', ' valeur: "+13%"', ' description: "Source ou contexte de la valeur"', ' - label: "Indicateur 2"', ' valeur: "85%"', ' description: "Source ou contexte"', ], "key_message": [ 'message: "Le message clé en une phrase forte."', 'detail: "Sous-texte optionnel en une ligne."', ], "circular_diagram": [ 'titre: "Titre affirmatif"', "segments:", ' - label: "Concept 1"', ' description: "Description courte"', ' - label: "Concept 2"', ' description: "Description courte"', ' - label: "Concept 3"', ' description: "Description courte"', ], "default_bullets": [ 'titre: "Titre affirmatif"', "bullets:", ' - texte: "Mot-clé : explication du point"', " niveau: 1", ' - texte: "Autre mot-clé : explication"', " niveau: 1", ], "numbered_steps": [ 'titre: "Titre affirmatif"', "steps:", " - numero: 1", ' titre: "Première étape"', ' description: "Ce que ça implique concrètement"', " - numero: 2", ' titre: "Deuxième étape"', ' description: "Ce que ça implique"', ], "phases_timeline": [ 'titre: "Titre affirmatif"', "phases:", ' - label: "Mois 1-2"', ' periode: "Ce qui se passe dans cette phase"', ' - label: "Mois 3-8"', ' periode: "Ce qui se passe"', ], "recommendation_card": [ "numero: 1", 'titre: "Nom court de la recommandation"', 'headline: "TROIS DÉCISIONS À PRENDRE MAINTENANT"', 'cta: "Décider en réunion du 30 juin"', "bullets:", ' - texte: "Première décision précise"', ' - texte: "Deuxième décision précise"', ], "end_slide": [ 'message: "Phrase de conclusion mémorable"', ], "from_to_pairs": [ 'titre: "Titre affirmatif"', 'label_from: "SITUATION ACTUELLE"', 'label_to: "SITUATION CIBLE"', "pairs:", ' - from: "État de départ"', ' to: "État cible"', ' - from: "Autre point de départ"', ' to: "Autre cible"', ], "gantt_timeline": [ 'titre: "Titre affirmatif"', 'periods: ["Juin", "Juil", "Août", "Sept"]', "workstreams:", ' - label: "Workstream 1"', " tasks:", ' - label: "Tâche A"', " start: 0", " end: 2", ' - label: "Workstream 2"', " tasks:", ' - label: "Tâche B"', " start: 1", " end: 3", ], "yearly_timeline": [ 'titre: "Titre affirmatif"', "milestones:", ' - annee: "2024"', ' label: "Premier jalon"', ' - annee: "2025"', ' label: "Deuxième jalon"', ' - annee: "2026"', ' label: "Jalon courant"', " actif: true", ], "comparison_table": [ 'titre: "Titre affirmatif"', 'headers: ["Critère", "Option A", "Option B"]', "rows:", ' - label: "Premier critère"', ' values: ["Valeur A", "Valeur B"]', ' - label: "Deuxième critère"', ' values: ["Valeur A", "Valeur B"]', ], "raci_table": [ 'titre: "Titre affirmatif"', 'roles: ["Rôle 1", "Rôle 2", "Rôle 3"]', "tasks:", ' - label: "Première activité"', ' raci: ["A", "R", "C"]', ' - label: "Deuxième activité"', ' raci: ["A", "I", "R"]', ], "process_arrow": [ 'titre: "Titre affirmatif"', "steps:", ' - titre: "Étape 1"', ' description: "Ce qui se passe"', ' - titre: "Étape 2"', ' description: "Ce qui se passe"', ' - titre: "Étape 3"', ' description: "Ce qui se passe"', ], "org_chart": [ 'titre: "Titre affirmatif"', "root:", ' label: "Responsable racine"', " children:", ' - label: "Manager 1"', " children:", ' - label: "Équipe A"', ' - label: "Manager 2"', " children:", ' - label: "Équipe B"', ], "matrix_2x2": [ 'titre: "Titre affirmatif"', "axis_x:", ' label: "Effort"', ' low: "Faible"', ' high: "Élevé"', "axis_y:", ' label: "Impact"', ' low: "Faible"', ' high: "Élevé"', "quadrants:", ' top_left: "Gains rapides"', ' top_right: "Projets stratégiques"', ' bottom_left: "Déprioritiser"', ' bottom_right: "À planifier"', "items:", ' - label: "Initiative A"', " x: 30", " y: 80", ' - label: "Initiative B"', " x: 70", " y: 75", ], } # ───────────────────────────────────────────────────────────────────────────── # GÉNÉRATEURS DE BLOCS (adaptés à la structure layouts_v2) # ───────────────────────────────────────────────────────────────────────────── def generate_layouts_catalogue(layouts: dict) -> str: """Bloc LAYOUTS_CATALOGUE pour le Designer — depuis layouts_v2.""" lines = [] current_famille = None for name, cfg in layouts.items(): famille = cfg.get("famille", "") if famille != current_famille: lines.append(f"\n#### {famille}") current_famille = famille layout_id = cfg.get("id", "") mode = cfg.get("mode", "light") hint = " ".join(cfg.get("agent_hint", "").split()) lines.append(f"\n`{name}` ({layout_id}) — mode {mode}") lines.append(f" → {hint}") return "\n".join(lines) def generate_yaml_schemas(layouts: dict) -> str: """Bloc YAML_SCHEMAS pour l'Encoder — champs requis/optionnels v2 + exemple complet par layout.""" lines = [] for name, cfg in layouts.items(): layout_id = cfg.get("id", "") champs = cfg.get("champs", []) requis = cfg.get("champs_requis", []) optionnels = [c for c in champs if c not in requis] lines.append(f"\n### `{name}` ({layout_id})") if requis: lines.append(f"**Requis :** {', '.join(requis)}") if optionnels: lines.append(f"**Optionnels :** {', '.join(optionnels)}") lines.append("**Exemple :**") lines.append("```yaml") lines.append(f"layout: {name}") lines.extend(EXAMPLES.get(name, [])) lines.append("```") return "\n".join(lines) # ───────────────────────────────────────────────────────────────────────────── # GÉNÉRATION DES FICHIERS # ───────────────────────────────────────────────────────────────────────────── def inject(template_path: Path, replacements: dict, out_path: Path): content = template_path.read_text(encoding="utf-8") for placeholder, value in replacements.items(): if placeholder not in content: print(f" ! Placeholder {placeholder} absent de {template_path}") content = content.replace(placeholder, value) out_path.write_text(content, encoding="utf-8") print(f" + Généré : {out_path}") def main(): parser = argparse.ArgumentParser( description="Injection des contraintes v2 dans les prompts agents") parser.add_argument("--layouts", default="layouts_v2.yaml") parser.add_argument("--designer", default="prompt_the_designer_v2.md") parser.add_argument("--encoder", default="prompt_the_encoder_v2.md") args = parser.parse_args() layouts_path = Path(args.layouts) if not layouts_path.exists(): print(f" ! {layouts_path} introuvable") sys.exit(1) with open(layouts_path, encoding="utf-8") as f: layouts = yaml.safe_load(f)["layouts"] print(f" + {layouts_path} chargé — {len(layouts)} layouts") catalogue = generate_layouts_catalogue(layouts) schemas = generate_yaml_schemas(layouts) # 1. Référence complète (debug / doc) constraints = "\n\n---\n\n".join([ "# AGENT CONSTRAINTS v2 — généré par prompt_injection_v2.py\n" "# Design System « PR Editorial » — 21 layouts", "## LAYOUTS_CATALOGUE\n" + catalogue, "## SEQUENCING_RULES\n" + SEQUENCING_RULES, "## NARRATIVE_PATTERNS\n" + NARRATIVE_PATTERNS, "## YAML_SCHEMAS\n" + schemas, ]) Path("agent_constraints_v2.md").write_text(constraints, encoding="utf-8") print(" + Généré : agent_constraints_v2.md") # 2. Designer injecté inject(Path(args.designer), { "{{LAYOUTS_CATALOGUE}}": catalogue, "{{SEQUENCING_RULES}}": SEQUENCING_RULES, "{{NARRATIVE_PATTERNS}}": NARRATIVE_PATTERNS, }, Path("prompt_the_designer_injected_v2.md")) # 3. Encoder injecté inject(Path(args.encoder), { "{{YAML_SCHEMAS}}": schemas, }, Path("prompt_the_encoder_injected_v2.md")) print("\n Fichiers *_injected_v2.md prêts à coller dans Mistral Studio.") if __name__ == "__main__": main()