Files

440 lines
17 KiB
Python
Raw Permalink Normal View History

#!/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_v3.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é — nom de sortie dérivé du template
# (prompt_the_designer_v3.md → prompt_the_designer_injected_v3.md)
designer_out = Path(args.designer).name.replace(
"_v3.md", "_injected_v3.md").replace("_v2.md", "_injected_v2.md")
inject(Path(args.designer), {
"{{LAYOUTS_CATALOGUE}}": catalogue,
"{{SEQUENCING_RULES}}": SEQUENCING_RULES,
"{{NARRATIVE_PATTERNS}}": NARRATIVE_PATTERNS,
}, Path(designer_out))
# 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()