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_content | generate_content_stream | |
|---|---|---|
| Renvoie | une réponse complète | un itérateur de morceaux |
| Premier mot visible | à la fin | après le premier morceau |
| Idéal pour | traitements, JSON, lots | interfaces de chat, longues réponses |
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 texteDeux 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]"]
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 ?
Le modèle est sans état : la mémoire, c'est l'historique que le SDK renvoie à chaque tour. C'est aussi pour cela que le coût d'entrée grandit avec la conversation.
Quand préférer generate_content_stream ?
Le streaming améliore le temps d'affichage du premier mot, pas le coût. Pour un JSON, on a besoin du texte complet avant de le valider : pas d'intérêt à streamer.
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) :
'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 :
1> Bonjour Inès ! Enchanté. C'est un plaisir de t'aider. ...
2> Tu t'appelles **Inès** !
history=4L'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
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.createetsend_message - Expliquer pourquoi le coût d'une conversation augmente à chaque tour