Aller au contenu principal

Templates modernes avec l'aide de Gemini CLI

Comment transformer un template Twig généré automatiquement (austère) en une page moderne et conviviale, en se faisant aider par un agent IA en ligne de commande, sans jamais risquer de casser le projet

Schéma

Petite vérification​

Avant de commencer, vérifiez si vous savez...

  1. citer les trois options de CollectionType qui autorisent l'ajout dynamique de réponses, leur suppression, et qui forcent Symfony à passer par addAnswer()/removeAnswer()
  2. expliquer pourquoi php bin/console make:crud Question n'a généré aucun champ pour la collection $answers dans ses formulaires
  3. dans le template templates/question/show.html.twig généré par make:crud, citer les deux seuls champs de Question qui sont actuellement affichés
📌 Réponse attendue

Notions théoriques​

Le problème : un CRUD fonctionnel, mais un rendu austère et incomplet​

La commande make:crud Question, utilisée dans la séance précédente, a généré ce template pour afficher une question :

{# templates/question/show.html.twig (généré automatiquement, tel quel) #}
{% extends 'base.html.twig' %}

{% block title %}Question{% endblock %}

{% block body %}
<h1>Question</h1>

<table class="table">
<tbody>
<tr>
<th>Id</th>
<td>{{ question.id }}</td>
</tr>
<tr>
<th>Text</th>
<td>{{ question.text }}</td>
</tr>
<tr>
<th>Explication</th>
<td>{{ question.explication }}</td>
</tr>
</tbody>
</table>

<a href="{{ path('app_question_index') }}">back to list</a>
<a href="{{ path('app_question_edit', {'id': question.id}) }}">edit</a>
{{ include('question/_delete_form.html.twig') }}
{% endblock %}

Deux problèmes sautent aux yeux :

  • Les réponses ne sont pas affichées du tout. make:crud ne génère jamais de code pour une relation OneToMany : il faudra l'ajouter nous-mêmes, comme pour le formulaire de la séance précédente.
  • La présentation est un simple tableau HTML brut, avec des libellés en anglais (Id, Text) : rien qui ressemble à l'écran d'un quiz que passerait un vrai utilisateur.

On va corriger les deux problèmes : afficher les réponses, et donner à cette page une présentation moderne, pensée pour un quiz (carte centrée, boutons de réponse, mise en évidence de la bonne réponse). Pour la partie visuelle, plutôt que de tout dessiner à la main en CSS, on va se faire aider par Gemini CLI, un agent IA qui s'exécute dans le terminal.

Qu'est-ce que Gemini CLI ?​

Gemini CLI est un outil en ligne de commande, développé par Google, qui donne accès au modèle d'IA Gemini directement depuis le terminal. Contrairement à un simple chatbot dans un navigateur, Gemini CLI est un agent : il peut lire des fichiers, en écrire de nouveaux, et exécuter des commandes dans le dossier où il est lancé.

Un agent IA en CLI peut modifier vos fichiers

C'est justement parce que Gemini CLI peut écrire et modifier des fichiers tout seul qu'il faut apprendre à l'utiliser prudemment, avant même d'apprendre à écrire un bon prompt. Un agent mal encadré pourrait, par exemple, écraser un fichier existant du projet Symfony en croyant bien faire. Les règles de sécurité ci-dessous ne sont pas facultatives.

Installer Gemini CLI​

Gemini CLI est un paquet Node.js, installable avec npm :

npm install -g @google/gemini-cli
Pour installer Nodes.js

Pour Installer Node.js, consultez le tutoriel : Installer Node.js

Une fois installé, la commande gemini est disponible dans tout le terminal. Pour vérifier l'installation :

gemini --version
Erreur 'Rename failed with EPERM'

Si l'erreur 'Rename failed with EPERM' s'affiche, veuillez exécuter la commande gemini --version dans un dossier dans vos Documents.

S'authentifier auprès de Gemini​

Avant de pouvoir poser la moindre question, Gemini CLI a besoin d'une clé d'API. Sans elle, voici exactement le message d'erreur qui s'affiche :

Please set an Auth method in your ~/.gemini/settings.json or specify one of the
following environment variables before running: GEMINI_API_KEY, GOOGLE_GENAI_USE_VERTEXAI, GOOGLE_GENAI_USE_GCA

Pour un usage simple en classe, la méthode la plus directe est une clé d'API personnelle, gratuite, obtenue sur aistudio.google.com, puis déclarée comme variable d'environnement :

Obtenir une clé d'API AI Studio​

Voici les étapes actuelles pour créer une clé API Gemini personnelle avec Google AI Studio :

  1. Ouvrez Google AI Studio :
    https://aistudio.google.com/

  2. Connectez-vous avec votre compte Google.

  3. Acceptez les conditions d’utilisation si Google vous les demande.

  1. Dans le menu ou le 'Tableau de bord' (Dashboard), ouvrez 'Clé d'API' (API Keys).

  1. Cliquez sur 'Créer une clé API' (Create API key).

  1. Créer un projet Google Cloud :
    • pour un nouveau compte, AI Studio peut créer automatiquement un projet par défaut ;
    • si vous avez déjà des projets Google Cloud, choisissez-en un ou cliquez sur Import projects pour l’importer dans AI Studio.



  1. Cliquez sur Create key, puis copiez immédiatement la clé affichée. Chaque clé API est associée à un projet Google Cloud.



La clé peut être utilisée avec le quota gratuit disponible pour Gemini API.

Pour l’utiliser avec Gemini CLI sous Windows PowerShell :

$env:GEMINI_API_KEY="VOTRE_CLE_API"
gemini

Pour la conserver entre les sessions :

[Environment]::SetEnvironmentVariable(
"GEMINI_API_KEY",
"VOTRE_CLE_API",
"User"
)

Fermez puis rouvrez PowerShell après cette commande.

Sous Linux ou macOS :

export GEMINI_API_KEY="VOTRE_CLE_API"
gemini
Ne jamais committer une clé d'API

Une clé d'API est une information secrète, personnelle, au même titre qu'un mot de passe. Elle ne doit jamais apparaître dans un fichier versionné avec Git (pas de clé en dur dans un script, pas de capture d'écran partagée). La déclarer avec export dans le terminal, ou dans un fichier .env ajouté au .gitignore, sont les deux bonnes pratiques.

Lancer Gemini CLI​

Gemini CLI propose 2 façons de fonctionner :

gemini

Lance le mode interactif : une conversation continue s'ouvre dans le terminal.

gemini -p "Saisir ici votre prompt"

Lance le mode non interactif (ou « headless ») : Gemini exécute une seule instruction (-p, pour prompt), affiche sa réponse, puis rend la main.

C'est ce mode qu'on utilisera pour cette séance : une seule demande précise, un résultat, terminé.

Le réglage le plus important : --approval-mode​

Avant d'écrire la moindre ligne dans un fichier, Gemini CLI demande en principe une confirmation.

Ce comportement se règle avec l'option --approval-mode, qui accepte quatre valeurs :

ValeurComportement
defaultDemande une confirmation avant chaque action (écriture de fichier, commande shell...). Réglage à utiliser dans cette séance.
auto_editApprouve automatiquement les modifications de fichiers, mais demande confirmation pour le reste.
yoloApprouve tout, automatiquement, sans jamais demander.
planMode lecture seule : Gemini peut analyser, mais ne peut rien écrire ni exécuter.
Ne jamais utiliser --yolo (ni l'option courte -y)

Le mode yolo autorise l'agent à écrire, modifier ou supprimer n'importe quel fichier accessible, sans demander votre accord. Son nom (You Only Live Once) résume bien le risque : une seule erreur de compréhension du prompt, et un fichier important peut être écrasé ou supprimé sans confirmation. Pour cette séance (et par prudence, en général), on reste systématiquement en mode default : chaque écriture de fichier proposée par Gemini doit être lue et validée une par une.

2 règles de sécurité à respecter, avant même d'écrire le prompt​

Avant de penser au contenu du prompt, voici 2 précautions à prendre pour garantir une sécurité :

  1. Ne jamais lancer gemini depuis la racine du projet Symfony. Si l'agent a accès à tout le projet (entités, formulaires, configuration, base de données), la moindre ambiguïté dans le prompt peut l'amener à vouloir « corriger » ou « compléter » un fichier existant. On crée donc un dossier vide, dédié, en dehors du projet, et on y lance Gemini :
    mkdir ~/maquette-quiz
    cd ~/maquette-quiz
    gemini -p "Saisir ici votre prompt" --approval-mode default
    Dans ce dossier vide, même si l'agent se trompait, il n'y a rien d'important à casser.
  2. Garder --approval-mode default (vu ci-dessus) : chaque fichier que Gemini propose de créer est affiché à l'écran avant d'être réellement écrit sur le disque. On lit, on vérifie que ça correspond bien à la demande, puis on accepte.

Rédiger un prompt qui ne produit que 2 fichiers, et rien d'autre​

Un bon prompt pour ce genre de demande est précis sur ce qu'il faut faire, et explicite sur ce qu'il ne faut pas faire :

Il faut créer uniquement 2 fichiers dans ce dossier :
- quiz.html
- quiz.css
Il ne faut pas créer d'autre fichier.
Il ne faut pas modifier ni supprimer un fichier existant.

quiz.html doit être une page HTML autonome qui affiche une question de quiz avec
4 réponses possibles, sous forme de boutons ou de cartes cliquables, dans le style
d'une application de quiz moderne (comme Kahoot) :
- il faut utiliser un contenu d'exemple : question "Quelle est la capitale de la France ?", réponses "Paris", "Lyon", "Marseille", "Toulouse".
- il faut ajouter une zone pour afficher une explication sous la question.
- il faut relier quiz.html à quiz.css avec une balise <link>.

quiz.css doit contenir uniquement du CSS moderne, sans framework externe (pas de
Bootstrap, pas de Tailwind) :
- dégradé de couleurs,
- coins arrondis,
- une légère ombre et un effet de survol sur chaque bouton de réponse.
Il faut prévoir une classe CSS nommée "correct" pour mettre en évidence visuellement une bonne réponse.

Décortiquons pourquoi ce prompt est sûr :

  • « Il faut crér uniquement 2 fichiers (...). Il ne faut pas créer d'autre fichier. » : élimine toute ambiguïté sur le nombre et le nom des fichiers attendus. Si Gemini (en mode default) proposait un troisième fichier ou un nom différent, ce serait immédiatement suspect, à refuser.
  • « Il ne faut pas modifier ni supprimer un fichier existant. » : rappelle explicitement l'interdiction, même si le dossier est déjà vide (bonne habitude à prendre, utile aussi le jour où ce même prompt serait relancé dans un dossier non vide par erreur).
  • Un contenu d'exemple fourni (la question sur la capitale de la France) : sans exemple concret, l'IA inventerait un contenu différent à chaque essai, rendant le résultat difficile à comparer d'un étudiant à l'autre.
  • Une classe CSS nommée à l'avance (correct) : c'est vous, dans l'étape suivante, qui déciderez dans le template Twig quelle réponse mérite cette classe ({{ answer.isCorrect ? 'correct' : '' }}). Le mockup HTML/CSS ne doit fournir que l'apparence, jamais la logique : celle-ci reste écrite par vous, dans Twig.

Du mockup statique au template Twig : ce qui change, et ce qui ne change pas​

Le fichier quiz.html généré par Gemini contient du HTML statique : une seule question écrite en dur, 4 réponses écrites en dur. Il ne connaît ni Twig, ni vos entités Question/Answer. Rien dans quiz.html ne doit être copié tel quel dans le projet : il faut distinguer, dans sa structure HTML, ce qui restera identique (la structure des balises, les classes CSS) de ce qui doit devenir dynamique (le texte de la question, la liste des réponses).

Prenons un exemple de résultat typique que pourrait produire Gemini pour quiz.html :

<div class="quiz-card">
<h1 class="quiz-question">Quelle est la capitale de la France ?</h1>
<div class="quiz-answers">
<button class="quiz-answer">Paris</button>
<button class="quiz-answer">Lyon</button>
<button class="quiz-answer">Marseille</button>
<button class="quiz-answer">Toulouse</button>
</div>
<p class="quiz-explication">La capitale de la France est Paris depuis le Moyen Âge.</p>
</div>

Pour l'intégrer dans templates/question/show.html.twig, le travail consiste à remplacer le contenu écrit en dur par des variables et une boucle Twig, en conservant exactement les mêmes classes CSS :

<div class="quiz-card">
<h1 class="quiz-question">{{ question.text }}</h1>
<div class="quiz-answers">
{% for answer in question.answers %}
<button class="quiz-answer {{ answer.isCorrect ? 'correct' : '' }}">{{ answer.text }}</button>
{% endfor %}
</div>
{% if question.explication %}
<p class="quiz-explication">{{ question.explication }}</p>
{% endif %}
</div>

Trois transformations, à bien repérer :

  • "Quelle est la capitale de la France ?" (texte fixe) devient {{ question.text }} (variable).
  • Les 4 <button> écrits en dur deviennent un seul <button>, répété par {% for answer in question.answers %} : le nombre de réponses affichées dépendra alors réellement du nombre de réponses en base, pas d'un nombre figé à 4.
  • La classe correct n'est plus écrite en dur sur un bouton : elle est ajoutée conditionnellement avec l'opérateur ternaire de Twig, uniquement si answer.isCorrect vaut true.

Le fichier quiz.css, lui, ne change pas du tout : on le copie tel quel dans public/css/quiz.css, et on l'inclut dans le template avec une balise <link>, exactement comme Bootstrap est déjà inclus dans base.html.twig.

remarque

Veuillez penser à placer le fichier quiz.css dans public/css et à le lier avec le template.

attention
  • Dans notre application, il ne s'agit pas d'une évaluation, la lecture du code source de la page peut permettre de deviner la bonne réponse.
  • Un vrai quiz, destiné à une évaluation, ne doit jamais révéler la bonne réponse avant que l'élève ait répondu.

Test de mémorisation/compréhension​


Que signifie le fait que Gemini CLI soit un agent, et pas un simple chatbot ?


Quelle commande installe Gemini CLI ?


Sans clé d'API configurée, que fait Gemini CLI quand on lui pose une question ?


Quelle commande exécute une seule instruction non interactive avec Gemini CLI ?


Que fait concrètement le mode `--approval-mode yolo` ?


Pourquoi cette séance recommande-t-elle de toujours garder `--approval-mode default` ?


Pourquoi lance-t-on Gemini CLI dans un dossier vide et dédié, plutôt que depuis la racine du projet Symfony ?


Dans le prompt donné en exemple, à quoi sert la phrase « Ne crée aucun autre fichier. Ne modifie et ne supprime aucun fichier existant. » ?


Pourquoi demande-t-on dans le prompt une classe CSS nommée `correct`, plutôt que de laisser l'IA décider elle-même quelle réponse mettre en avant ?


Dans le template Twig final, à quoi correspond `{% for answer in question.answers %}` ?


Que faut-il faire du fichier quiz.css généré par Gemini, une fois le mockup validé ?


Pourquoi ne faut-il jamais afficher la classe `correct` sur l'écran d'un joueur en train de répondre à un vrai quiz ?


TP pour réfléchir et résoudre des problèmes​

Votre défi pour aujourd'hui consiste à générer une maquette moderne avec Gemini CLI, puis à l'intégrer, en toute sécurité, dans le template question/show.html.twig.

Étape 1 — Installer Gemini CLI​


Bonne pratique - Vérifier l'installation avant de continuer

Après l'installation, exécutez gemini --version. Si la commande échoue (command not found), le dossier global de npm n'est probablement pas dans votre PATH : mieux vaut corriger ce problème immédiatement plutôt que de continuer en espérant qu'il se résolve tout seul.

Étape 2 — Créer un dossier de travail isolé, hors du projet Symfony​


Bonne pratique - Un dossier vide, pas un sous-dossier du projet

Ne créez jamais ce dossier à l'intérieur du projet Symfony (par exemple dans public/ ou var/) : même isolé dans un sous-dossier, l'agent pourrait détecter la présence du projet Symfony autour de lui et tenter d'en tenir compte. Un dossier complètement extérieur au projet (ici, dans votre répertoire personnel) élimine ce risque.

Étape 3 — Lancer Gemini CLI avec le bon réglage de sécurité​


Bonne pratique - Lire chaque proposition avant de valider

En mode default, Gemini CLI affiche le contenu de chaque fichier avant de l'écrire, et attend votre validation (habituellement en appuyant sur une touche, selon l'interface proposée par la version installée). Prenez le temps de lire ce contenu : c'est précisément ce moment de lecture qui remplace la sécurité qu'offrirait un mode automatique.

Étape 4 — Transformer le HTML statique en boucle Twig​


Solution du TP​

📌 Une solution complète du TP