Leçon 2 — Démarrer avec google-genai

Streaming et conversation multi-tours

4 min de lecture

Streaming et conversation multi-tours

Le streaming : voir la réponse arriver

Sans streaming, on attend la réponse complète avant d'afficher quoi que ce soit. Avec le streaming, l'API envoie la réponse en morceaux (chunks) dès qu'ils sont prêts : l'utilisateur lit les premières phrases pendant que la suite se génère.

sequenceDiagram
  participant S as Script
  participant G as Gemini
  S->>G: generate_content_stream(...)
  G-->>S: morceau 1
  G-->>S: morceau 2
  G-->>S: ...
  G-->>S: dernier morceau (usage_metadata)
generate_contentgenerate_content_stream
Renvoieune réponse complèteun itérateur de morceaux
Premier mot visibleà la finaprès le premier morceau
Idéal pourtraitements, JSON, lotsinterfaces de chat, longues réponses
files/reunion.py
def resume_stream(client, transcription: str) -> str:
    """Affiche le résumé morceau par morceau ; renvoie le texte complet."""
    def lancer():
        # La requête part à la première itération : on réessaie donc toute la boucle.
        texte = ""
        for chunk in client.models.generate_content_stream(
                model=MODEL, contents=f"Transcription :\n{transcription}\n\nRésume en 5 lignes.",
                config=config()):
            print(chunk.text or "", end="", flush=True)
            texte += chunk.text or ""
        return texte

    texte = avec_reessais(lancer)
    print()
    return texte

Deux détails importants : chunk.text peut valoir None (d'où le or ""), et flush=True force l'affichage immédiat au lieu d'attendre la fin de ligne.

La conversation : client.chats

Un modèle ne garde aucune mémoire entre deux appels : chaque requête est indépendante. Pour une conversation, il faut renvoyer tout l'historique à chaque tour. L'objet chat du SDK le fait pour vous.

flowchart LR
  Q1["Tour 1 : question"] --> H["Historique : [q1, r1]"]
  H --> Q2["Tour 2 : question + historique"]
  Q2 --> H2["Historique : [q1, r1, q2, r2]"]
files/reunion.py
def chat(client, transcription: str, entrees=sys.stdin) -> None:
    session = client.chats.create(model=MODEL, config=config(), history=[
        types.Content(role="user", parts=[types.Part(text=f"Transcription :\n{transcription}")]),
        types.Content(role="model", parts=[types.Part(text="Transcription reçue. Posez vos questions.")]),
    ])
    print("Posez vos questions (ligne vide pour quitter).")
    for question in entrees:
        if not question.strip():
            break
        r = avec_reessais(lambda: session.send_message(question.strip()))
        print(f"> {question.strip()}\n{r.text.strip()}\n")

On amorce l'historique avec la transcription : toutes les questions suivantes s'y rapportent. Le paramètre entrees vaut le clavier par défaut, et un texte préparé dans les tests. Code complet : reunion.py.

Pourquoi le modèle « se souvient »-il du prénom donné au tour précédent dans un chat ?

Quand préférer generate_content_stream ?

Résultat obtenu

Le 2026-10-15, en mode clé d'API (gemini-3.5-flash), un streaming de trois conseils est arrivé en 6 morceaux (extrait, chaque ligne est un chunk.text) :

morceaux reçus
'Voici 3 conseils courts pour bien préparer une réunion :\n\n1. **Définissez'
' un objectif clair et un ordre du jour** : Sachez exactement ce que vous devez accomplir et partagez les sujets'
" à aborder aux participants à l'avance.\n2. **Invitez uniquement les personnes indispensables** : Limitez le nombre"
...
''

Et une conversation de deux tours :

chat
1> Bonjour Inès ! Enchanté. C'est un plaisir de t'aider. ...
2> Tu t'appelles **Inès** !
history=4

L'historique contient 4 messages : deux questions, deux réponses. Les morceaux coupent les mots n'importe où (« **Définissez » / « un objectif ») : n'analysez jamais un morceau isolé, seulement le texte recollé.

Exercice

Ajoutez à chat une commande spéciale : si l'utilisateur tape /historique, le programme affiche le nombre de messages de l'historique au lieu d'interroger le modèle.

Voir la solution
files/reunion.py (dans la boucle de chat)
    for question in entrees:
        if not question.strip():
            break
        if question.strip() == "/historique":
            print(f"{len(session.get_history())} messages dans l'historique\n")
            continue
        r = avec_reessais(lambda: session.send_message(question.strip()))
        print(f"> {question.strip()}\n{r.text.strip()}\n")

session.get_history() renvoie la liste des Content échangés, amorce comprise. Le continue passe à la ligne suivante sans appel au modèle (donc sans coût).

Je sais faire

  • Afficher une réponse en streaming avec generate_content_stream
  • Gérer les morceaux vides (chunk.text à None)
  • Tenir une conversation avec client.chats.create et send_message
  • Expliquer pourquoi le coût d'une conversation augmente à chaque tour
Suivant