IA conversationnelle
Ce document explique comment créer et gérer des sessions conversationnelles à l'aide de la bibliothèque google.genai
en Python. Il couvre l'initialisation des sessions de discussion, l'envoi de messages, la surcharge de paramètres pour des requêtes individuelles, et, ce qui est important, comment mettre à jour de manière permanente des paramètres tels que temperature
et max_output_tokens
tout en préservant l'system_instruction
et l'history
existant.
Prérequis
Pour exécuter les exemples, assurez-vous que la bibliothèque google-generative-ai
est installée. Vous aurez également besoin d'une clé API pour l'API Gemini ou des identifiants Vertex AI appropriés. Pour l'API Gemini, vous pouvez définir la variable d'environnement GOOGLE_API_KEY
ou la transmettre directement au constructeur de genai.Client
.
pip install google-generative-ai
1. Initialiser le Client
Tout d'abord, initialisez l'objet Client
. C'est le point d'entrée pour interagir avec les services Google Generative AI.
import google.genai as genai
from google.genai import types
# Configurez votre clé API ou vos identifiants Vertex AI
# Option 1 : Définir la variable d'environnement GOOGLE_API_KEY
# export GOOGLE_API_KEY="VOTRE_CLE_API"
# client = genai.Client()
# Option 2 : Transmettre la clé API directement (pour l'API Gemini)
client = genai.Client(api_key="VOTRE_CLE_API")
# Option 3 : Pour Vertex AI (nécessite un projet et une localisation)
# client = genai.Client(vertexai=True, project="votre-id-projet-gcp", location="us-central1")
2. Créer une conversation avec des paramètres initiaux
Vous pouvez démarrer une conversation (session de discussion) avec un historique existant et spécifier des paramètres de génération initiaux tels que temperature
, max_output_tokens
, top_p
, top_k
, et une system_instruction
.
La méthode client.chats.create()
est utilisée pour initialiser un objet Chat
.
model
: Spécifie le modèle génératif à utiliser (par exemple, "gemini-pro").history
: Une liste facultative d'objetstypes.Content
représentant les tours précédents de la conversation. Utiliseztypes.UserContent()
ettypes.ModelContent()
pour une définition plus claire de l'historique.config
: Un objettypes.GenerateContentConfig
facultatif pour définir les paramètres de génération initiaux de la session de discussion. Cetteconfig
servira de valeur par défaut pour tous les messages ultérieurs dans cette session.
# Définir un historique de conversation initial facultatif
initial_history = [
types.UserContent("Bonjour !"),
types.ModelContent("Salut ! Comment puis-je vous aider aujourd'hui ?"),
types.UserContent("Je souhaite en savoir plus sur les grands modèles de langage."),
]
# Définir les paramètres de configuration de génération initiaux
initial_generation_config = types.GenerateContentConfig(
temperature=0.7, # Contrôle le caractère aléatoire : 0.0 (moins aléatoire) à 1.0 (plus aléatoire)
max_output_tokens=150, # Nombre maximum de tokens à générer dans la réponse
top_p=0.9, # Les tokens sont échantillonnés jusqu'à ce que leurs probabilités totalisent cette valeur
top_k=40, # Pour chaque étape, considérer les top_k tokens ayant les probabilités les plus élevées
system_instruction=types.Content( # Utiliser types.Content pour system_instruction
parts=[types.Part(text="Vous êtes un expert en IA et en apprentissage automatique. Fournissez des réponses détaillées et informatives.")]
)
)
# Créer la session de discussion
chat = client.chats.create(
model="gemini-pro", # Utiliser un modèle approprié, par exemple "gemini-pro"
history=initial_history,
config=initial_generation_config
)
print("--- Configuration initiale de la session de discussion ---")
print(f"Modèle : {chat._model}")
# Note : L'accès direct à `_config` (par exemple, `chat._config`) est à des fins de démonstration de l'état interne.
# La méthode `send_message` utilise cette configuration par défaut en interne.
print(f"Température par défaut : {chat._config.temperature}")
print(f"Nombre maximal de tokens de sortie par défaut : {chat._config.max_output_tokens}")
print(f"Instruction système par défaut : {chat._config.system_instruction.parts[0].text if chat._config.system_instruction else 'Aucune'}")
print("\n--- Historique de discussion initial (filtré) ---")
for i, content in enumerate(chat.get_history(curated=True)):
role = content.role
# Vérifier si content.parts existe et a un attribut text avant d'y accéder
text_content = content.parts[0].text if content.parts and hasattr(content.parts[0], 'text') else "[Contenu non textuel]"
print(f"Tour {i+1} ({role}) : {text_content}")
3. Envoi de messages et surcharges de paramètres par appel
Une fois une conversation initiée, vous pouvez envoyer de nouveaux messages. La méthode send_message()
(ou send_message_stream()
pour les réponses en flux continu) vous permet de surcharger les paramètres de génération pour cet appel spécifique uniquement.
Vous pouvez modifier des paramètres tels que temperature
et max_output_tokens
pour des appels send_message
individuels en passant un nouvel objet types.GenerateContentConfig
à l'argument config
. Tout paramètre défini dans cette config
par appel aura la priorité sur la valeur par défaut de la session de discussion pour ce message particulier, mais ne modifiera pas la valeur par défaut de la session pour les messages ultérieurs.
import time
# Envoyer un message, en surchargeant la température et le nombre maximal de tokens de sortie pour ce tour
print("\n--- Envoi du Message 1 (surcharge de la config pour cet appel) ---")
message_1_config = types.GenerateContentConfig(
temperature=0.2, # Température plus basse pour moins d'aléatoire sur cet appel
max_output_tokens=80 # Générer une réponse plus courte pour cet appel
)
response1 = chat.send_message("Quelles sont les applications des LLM ?", config=message_1_config)
print(f"Utilisateur : Quelles sont les applications des LLM ?")
print(f"Modèle : {response1.text}")
print(f"Configuration pour le Message 1 (surcharge par appel) : Température={message_1_config.temperature}, Nombre maximal de tokens de sortie={message_1_config.max_output_tokens}")
# La configuration par défaut interne du chat reste inchangée par l'appel ci-dessus :
# print(f"Température par défaut du chat : {chat._config.temperature}")
# Envoyer un autre message, en surchargeant avec des paramètres différents
print("\n--- Envoi du Message 2 (surcharge à nouveau de la config pour cet appel) ---")
message_2_config = types.GenerateContentConfig(
temperature=0.9, # Température plus élevée pour une réponse plus créative sur cet appel
max_output_tokens=120 # Permettre une réponse plus longue sur cet appel
)
response2 = chat.send_message("Donnez-moi un exemple créatif.", config=message_2_config)
print(f"Utilisateur : Donnez-moi un exemple créatif.")
print(f"Modèle : {response2.text}")
print(f"Configuration pour le Message 2 (surcharge par appel) : Température={message_2_config.temperature}, Nombre maximal de tokens de sortie={message_2_config.max_output_tokens}")
# Envoyer un message sans surcharger la configuration, ce qui utilisera l'initial_generation_config
print("\n--- Envoi du Message 3 (utilisation de la configuration par défaut initiale) ---")
response3 = chat.send_message("Résumez notre conversation jusqu'à présent.")
print(f"Utilisateur : Résumez notre conversation jusqu'à présent.")
print(f"Modèle : {response3.text}")
# Cet appel utilise la température et le nombre maximal de tokens de sortie de `initial_generation_config`
print(f"Configuration pour le Message 3 (par défaut de la session de discussion) : Température={initial_generation_config.temperature}, Nombre maximal de tokens de sortie={initial_generation_config.max_output_tokens}")
# Démontrer la réponse en flux continu
print("\n--- Envoi du Message 4 (en flux continu, surcharge de la config pour cet appel) ---")
message_4_config = types.GenerateContentConfig(
temperature=0.5,
max_output_tokens=60
)
print(f"Utilisateur : Raconte-moi une très courte histoire sur un brave chevalier.")
print("Modèle (en flux continu) : ", end="")
for chunk in chat.send_message_stream("Raconte-moi une très courte histoire sur un brave chevalier.", config=message_4_config):
print(chunk.text, end="", flush=True)
time.sleep(0.05) # Simuler l'impression en temps réel
print("\n")
print(f"Configuration pour le Message 4 (surcharge par appel) : Température={message_4_config.temperature}, Nombre maximal de tokens de sortie={message_4_config.max_output_tokens}")
4. Mise à jour des paramètres de discussion persistants (préservation de l'état)
La configuration de base d'un objet Chat
, incluant son system_instruction
et ses paramètres de génération par défaut, est définie lors de sa création et n'est pas directement modifiable via des méthodes publiques. Pour apporter des modifications à temperature
et max_output_tokens
qui persistent pour tous les messages futurs d'une conversation (tout en conservant l'system_instruction
et l'historique de discussion accumulé), vous devez recréer l'instance Chat
.
Ce processus implique :
- Récupération de l'état actuel : Obtenir le nom du modèle existant, la
GenerateContentConfig
actuelle (qui inclut l'system_instruction
), et l'historique complet de la conversation à partir de l'instanceChat
active. - Collecte des nouveaux paramètres : Obtenir les nouvelles valeurs de
temperature
etmax_output_tokens
. - Fusion des configurations : Combiner les valeurs
GenerateContentConfig
existantes avec les nouvelles valeurs de paramètres dans une seule configuration mise à jour. - Recréation de l'instance
Chat
: Créer un nouvel objetChat
en utilisantclient.chats.create()
, en passant le modèle original, la configuration nouvellement fusionnée et l'historique de discussion préservé. - Remplacement de l'ancienne instance : Mettre à jour la référence de votre programme à l'objet
Chat
pour qu'elle pointe vers la nouvelle instance.
Cela assure une transition fluide vers les nouveaux paramètres sans perdre de contexte conversationnel ni d'autres réglages prédéfinis.
# Fonction pour obtenir de nouveaux paramètres de l'utilisateur (peut être intégrée à l'interface utilisateur de votre application)
def get_user_param_overrides():
"""Demande à l'utilisateur la température et le nombre maximal de tokens de sortie, permettant une saisie vide pour conserver les valeurs actuelles."""
print("\n--- Saisir les nouveaux paramètres du modèle (Laisser vide pour conserver les valeurs actuelles) ---")
temp_input = input("Nouvelle température (flottant, par exemple 0.7) : ")
max_tokens_input = input("Nouveau nombre maximal de tokens de sortie (entier, par exemple 200) : ")
new_temperature = None
if temp_input:
try:
new_temperature = float(temp_input)
if not (0.0 <= new_temperature <= 1.0):
print("Note : La température varie généralement de 0.0 à 1.0 pour les cas d'utilisation typiques.")
except ValueError:
print("Saisie invalide pour la température. Conservation de la valeur actuelle.")
new_max_output_tokens = None
if max_tokens_input:
try:
new_max_output_tokens = int(max_tokens_input)
if new_max_output_tokens <= 0:
print("Note : Le nombre maximal de tokens de sortie doit être un entier positif.")
except ValueError:
print("Saisie invalide pour le nombre maximal de tokens de sortie. Conservation de la valeur actuelle.")
return new_temperature, new_max_output_tokens
def recreate_chat_with_updates(current_chat: genai.chats.Chat) -> genai.chats.Chat:
"""
Récupère l'état actuel de la discussion, le fusionne avec de nouveaux paramètres et recrée la discussion.
Renvoie la nouvelle instance Chat.
"""
print("\n--- Mise à jour permanente des paramètres de discussion ---")
# 1. Récupérer le nom du modèle actuel, la configuration et l'historique
current_model_name = current_chat._model
# L'accès direct à _config est un modèle courant pour inspecter l'état interne
# lorsqu'aucun accesseur public n'est fourni pour l'objet de configuration complet.
current_gen_config: types.GenerateContentConfig = current_chat._config
current_history = current_chat.get_history(curated=False) # Récupérer l'historique complet
# 2. Obtenir les nouvelles valeurs de paramètres de l'utilisateur ou de la source
new_temperature, new_max_output_tokens = get_user_param_overrides()
# 3. Fusionner les configurations
# Commencer par une représentation dictionnaire de la configuration actuelle pour conserver tous ses champs
updated_config_dict = current_gen_config.model_dump()
# Appliquer les nouveaux paramètres s'ils ont été fournis (pas None)
if new_temperature is not None:
updated_config_dict['temperature'] = new_temperature
if new_max_output_tokens is not None:
updated_config_dict['max_output_tokens'] = new_max_output_tokens
# Créer un nouvel objet GenerateContentConfig à partir du dictionnaire fusionné
final_gen_config = types.GenerateContentConfig(**updated_config_dict)
print("\nRecréation de la session de discussion avec la configuration mise à jour :")
print(f" Modèle : {current_model_name}")
print(f" Nouvelle température : {final_gen_config.temperature}")
print(f" Nouveau nombre maximal de tokens de sortie : {final_gen_config.max_output_tokens}")
if final_gen_config.system_instruction:
print(f" Instruction système : '{final_gen_config.system_instruction.parts[0].text}'")
print(f" Longueur de l'historique préservée : {len(current_history)} tours")
# 4. Recréer la session de discussion avec la configuration et l'historique combinés
updated_chat = client.chats.create(
model=current_model_name,
config=final_gen_config,
history=current_history # Transmettre l'intégralité de l'historique de conversation préservé
)
return updated_chat
# --- Boucle de discussion interactive principale pour démontrer les mises à jour persistantes ---
# Cette partie est à des fins de démonstration et s'intégrerait dans le flux de votre application.
# Définir une session de discussion initiale (comme dans la section 2)
initial_system_instruction = types.Content(parts=[types.Part.from_text("Vous êtes un assistant concis. Fournissez des réponses courtes.")])
initial_history = [
types.UserContent("Qu'est-ce que Python ?"),
types.ModelContent("Python est un langage de programmation populaire.")
]
initial_gen_config = types.GenerateContentConfig(
temperature=0.4,
max_output_tokens=50,
system_instruction=initial_system_instruction
)
print("--- Initialisation de la session de discussion principale ---")
current_chat_session = client.chats.create(
model='gemini-pro',
config=initial_gen_config,
history=initial_history
)
print(f"Configuration actuelle de la discussion : Température={current_chat_session._config.temperature}, MaxTokens={current_chat_session._config.max_output_tokens}")
print(f"Instruction système actuelle : {current_chat_session._config.system_instruction.parts[0].text}")
print(f"Longueur actuelle de l'historique : {len(current_chat_session.get_history())} tours")
print("\n--- Commencer la discussion ---")
print("Tapez 'update_params' pour modifier la température et le nombre maximal de tokens de sortie de manière persistante.")
print("Tapez 'quit' ou 'exit' pour terminer la discussion.")
while True:
user_input = input("\nVous : ")
if user_input.lower() in ["quit", "exit"]:
print("Fin de la session de discussion. Au revoir !")
break
if user_input.lower() == "update_params":
# Appeler la fonction pour recréer la discussion avec les paramètres mis à jour.
# La nouvelle instance de discussion renvoyée remplace l'ancienne.
current_chat_session = recreate_chat_with_updates(current_chat_session)
print("\nParamètres de la session de discussion mis à jour. Continuez à taper des messages.")
# Continuer à la prochaine itération de la boucle sans envoyer 'update_params' au modèle
continue
try:
response = current_chat_session.send_message(user_input)
print(f"Modèle : {response.text}")
except Exception as e:
print(f"Une erreur est survenue lors de la génération du message : {e}")
break
print("\n--- Historique de discussion final (complet) ---")
# La propriété history de l'objet chat garde la trace de tous les tours
# y compris l'historique original et tous les messages envoyés et reçus.
for i, content in enumerate(current_chat_session.get_history()):
role = content.role
text_content = content.parts[0].text if content.parts and hasattr(content.parts[0], 'text') else "[Contenu non textuel]"
print(f"Tour {i+1} ({role}) : {text_content}")
print("\n--- Historique de discussion final (filtré) ---")
# L'historique filtré ne contient que les tours valides qui contribuent au contexte du modèle
for i, content in enumerate(current_chat_session.get_history(curated=True)):
role = content.role
text_content = content.parts[0].text if content.parts and hasattr(content.parts[0], 'text') else "[Contenu non textuel]"
print(f"Tour {i+1} ({role}) : {text_content}")