437 lines
16 KiB
Python
437 lines
16 KiB
Python
#!/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()
|