Lesson 7 — Projet final : assistant de réunion

Construire le CLI assistant de réunion

5 min read

This content is not translated yet. Showing the original version.

Construire le CLI assistant de réunion

L'architecture

flowchart TB
  M["main() : argparse"] -->|"resume"| CR["compte_rendu()"]
  M -->|"resume --stream"| RS["resume_stream()"]
  M -->|"tokens"| TK["tokens()"]
  M -->|"chat"| CH["chat()"]
  CR --> AR["avec_reessais()"]
  RS --> AR
  TK --> AR
  CH --> AR
  AR --> CL["make_client() : clé d'API ou Vertex AI"]
  CR --> MD["en_markdown()"]
  CR --> CO["cout()"]
FonctionRôleLeçon
make_client, modeclient unique pour les deux modes, avec délaiVertex AI
configinstruction système, plafond, température facultativeConfiguration
avec_reessaisréessais sur 429 / 5xxErreurs
cout, tokensestimation du coûtTokens et coût
resume_stream, chatstreaming et multi-toursStreaming et conversation
compte_rendu, en_markdownnouveau : JSON structuré puis tableaucette leçon

Le compte rendu structuré

Le schéma décrit ce qu'on attend d'un compte rendu : un résumé, des décisions, et des actions qui ont chacune une tâche, un responsable et une échéance.

files/reunion.py
class Action(BaseModel):
    tache: str
    responsable: str
    echeance: str


class CompteRendu(BaseModel):
    resume: str
    decisions: list[str]
    actions: list[Action]


def compte_rendu(client, transcription: str) -> tuple[CompteRendu, float]:
    response = avec_reessais(lambda: client.models.generate_content(
        model=MODEL,
        contents=f"Transcription :\n{transcription}\n\nFais le compte rendu.",
        config=config(response_mime_type="application/json", response_schema=CompteRendu),
    ))
    return CompteRendu.model_validate_json(response.text), cout(response.usage_metadata)

L'échéance est un simple texte ("vendredi", "non précisé") et non une date : dans une réunion, on dit « vendredi », pas « 2026-10-16 ». Convertir en date exacte demanderait de connaître le jour de la réunion, et risquerait d'inventer.

La mise en forme

files/reunion.py
def en_markdown(cr: CompteRendu) -> str:
    lignes = ["## Résumé", cr.resume, "", "## Décisions"]
    lignes += [f"- {d}" for d in cr.decisions] or ["- aucune"]
    lignes += ["", "## Actions", "| Tâche | Responsable | Échéance |", "| --- | --- | --- |"]
    lignes += [f"| {a.tache} | {a.responsable} | {a.echeance} |" for a in cr.actions]
    return "\n".join(lignes)

Le résultat se colle tel quel dans un wiki, un ticket ou un message d'équipe. Séparer extraction (le modèle produit du JSON) et présentation (Python produit le Markdown) permet de changer le format d'affichage sans toucher au prompt, et de tester la présentation sans appeler le modèle.

Pourquoi demander du JSON au modèle puis produire le Markdown en Python, au lieu de demander directement du Markdown ?

Dans la transcription, personne ne s'engage sur une date pour une action. Que doit contenir le champ echeance ?

Résultat obtenu

python reunion.py resume reunion_exemple.txt, mode clé d'API, gemini-3.5-flash, 2026-10-15 (résumé abrégé) :

sortie
## Décisions
- Décalage du lancement de la v2 de l'application au 4 novembre

## Actions
| Tâche | Responsable | Échéance |
| --- | --- | --- |
| Analyser le problème d'export PDF sur les gros fichiers | Karim | cette semaine |
| Envoyer les captures d'écran de la v2 à Thomas | Karim | vendredi |
| Préparer le message pour informer les clients du nouveau calendrier | Léa | lundi |

Le budget publicitaire, reporté sans responsable, n'apparaît pas comme action : c'est correct. Discutable en revanche : l'échéance « lundi » de Léa est en fait la date de validation du message. Un compte rendu généré se relit avant diffusion.

Exercice

Ajoutez au compte rendu une liste questions_ouvertes (sujets évoqués mais non tranchés) et affichez-la dans une section « Questions ouvertes » du Markdown.

Voir la solution
files/reunion.py (modifications)
class CompteRendu(BaseModel):
    resume: str
    decisions: list[str]
    actions: list[Action]
    questions_ouvertes: list[str]


def en_markdown(cr: CompteRendu) -> str:
    ...
    lignes += ["", "## Questions ouvertes"]
    lignes += [f"- {q}" for q in cr.questions_ouvertes] or ["- aucune"]
    return "\n".join(lignes)

Mettez à jour le dictionnaire CR des tests (ajout de "questions_ouvertes": [...]), sinon model_validate échouera : c'est le test qui vous rappelle que le contrat a changé. Sur la transcription d'exemple, on s'attend à voir apparaître le budget publicitaire.

Je sais faire

  • Décrire un compte rendu avec des modèles Pydantic imbriqués (Action dans CompteRendu)
  • Obtenir un JSON validé puis le présenter en Markdown
  • Imposer « non précisé » plutôt qu'une information inventée
  • Organiser un petit CLI en fonctions réutilisables
Next