Structure de Chat IA Extensible
Pour une application de chat extensible où les utilisateurs peuvent choisir différents modèles d'IA (par exemple, Gemini, OpenAI et d'autres à l'avenir), une approche structurée utilisant les Classes de Base Abstraites (CBA) et le Patron de Fabrique est très efficace. Cela permet de définir une interface commune pour tous les fournisseurs d'IA et d'en intégrer facilement de nouveaux sans modifier la logique principale existante.
Concepts Clés
- Classe de Base Abstraite (CBA) : Définit les méthodes communes que tous les fournisseurs d'IA doivent implémenter. Cela assure une interface cohérente.
- Implémentations Concrètes : Classes pour chaque fournisseur d'IA spécifique (par exemple,
GeminiAIProvider
,OpenAIAIProvider
) qui héritent de la CBA et implémentent ses méthodes. - Patron de Fabrique : Un mécanisme pour créer des instances des fournisseurs d'IA basées sur un type donné (par exemple, "gemini", "openai") sans exposer la logique de création au client. Cela découple le client des classes de fournisseurs concrètes.
- Gestion de la Configuration : Gestion centralisée des clés API et des noms de modèles par défaut, idéalement chargés à partir de variables d'environnement pour la sécurité.
Structure du Code
Voici la structure de fichiers et le contenu recommandés :
project_root/
├── config.py
├── ai_providers/
│ ├── __init__.py
│ ├── abstract_ai_provider.py
│ ├── gemini_provider.py
│ └── openai_provider.py
├── ai_factory.py
└── main.py
config.py
Ce fichier gère le chargement de la configuration, en particulier des clés API sensibles, de préférence à partir des variables d'environnement.
import os
class Config:
"""Gère la configuration de l'application, y compris les clés API et les valeurs par défaut des modèles."""
# Configuration OpenAI
OPENAI_API_KEY: str = os.getenv("OPENAI_API_KEY", "your_openai_api_key_here")
OPENAI_DEFAULT_MODEL: str = "gpt-3.5-turbo" # Ou "gpt-4"
# Configuration Gemini
GEMINI_API_KEY: str = os.getenv("GEMINI_API_KEY", "your_gemini_api_key_here")
GEMINI_DEFAULT_MODEL: str = "gemini-pro"
# Ajoutez ici la configuration pour d'autres fournisseurs d'IA
# EXAMPLE_API_KEY: str = os.getenv("EXAMPLE_API_KEY", "your_example_api_key_here")
# EXAMPLE_DEFAULT_MODEL: str = "example-model"
# Exemple de configuration des variables d'environnement (par exemple, dans votre shell ou un fichier .env)
# export OPENAI_API_KEY="sk-..."
# export GEMINI_API_KEY="AIza..."
ai_providers/abstract_ai_provider.py
Définit l'interface commune pour tous les fournisseurs d'IA.
from abc import ABC, abstractmethod
from typing import List, Dict
class AbstractAIProvider(ABC):
"""Classe de Base Abstraite pour tous les fournisseurs de chat IA."""
def __init__(self, api_key: str, model_name: str):
self._api_key = api_key
self._model_name = model_name
@abstractmethod
def generate_response(self, messages: List[Dict]) -> str:
"""
Génère une réponse du modèle d'IA basée sur les messages fournis.
Arguments :
messages: Une liste de dictionnaires de messages, typiquement au format
`[{"role": "user", "content": "Hello!"}, {"role": "assistant", "content": "Hi!"}]`.
Le format exact peut varier légèrement selon le fournisseur, donc les implémentations concrètes
doivent gérer la conversion si nécessaire.
Retourne :
La réponse de l'IA sous forme de chaîne de caractères.
"""
pass
@abstractmethod
def get_model_name(self) -> str:
"""Retourne le nom du modèle d'IA utilisé."""
pass
# Ajoutez d'autres méthodes communes si nécessaire, par exemple, get_token_count, get_available_models
ai_providers/openai_provider.py
Implémente AbstractAIProvider
pour l'API d'OpenAI.
(Installez openai
avec pip install openai
)
from openai import OpenAI
from typing import List, Dict
from .abstract_ai_provider import AbstractAIProvider
class OpenAIAIProvider(AbstractAIProvider):
"""Implémentation concrète pour les modèles de chat OpenAI."""
def __init__(self, api_key: str, model_name: str):
super().__init__(api_key, model_name)
self._client = OpenAI(api_key=self._api_key)
def generate_response(self, messages: List[Dict]) -> str:
"""
Génère une réponse en utilisant l'API de complétion de chat d'OpenAI.
Le format des 'messages' est directement compatible avec l'API d'OpenAI.
"""
try:
chat_completion = self._client.chat.completions.create(
model=self._model_name,
messages=messages,
temperature=0.7, # Paramètre d'exemple
)
return chat_completion.choices[0].message.content
except Exception as e:
print(f"Erreur lors de l'appel à l'API OpenAI : {e}")
return "Désolé, je n'ai pas pu obtenir de réponse d'OpenAI."
def get_model_name(self) -> str:
return self._model_name
ai_providers/gemini_provider.py
Implémente AbstractAIProvider
pour l'API de Google Gemini.
(Installez google-generativeai
avec pip install google-generativeai
)
import google.generativeai as genai
from typing import List, Dict
from .abstract_ai_provider import AbstractAIProvider
class GeminiAIProvider(AbstractAIProvider):
"""Implémentation concrète pour les modèles de chat Google Gemini."""
def __init__(self, api_key: str, model_name: str):
super().__init__(api_key, model_name)
genai.configure(api_key=self._api_key)
self._model = genai.GenerativeModel(self._model_name)
def generate_response(self, messages: List[Dict]) -> str:
"""
Génère une réponse en utilisant l'API de chat de Google Gemini.
Note : La méthode `start_chat` de Gemini attend des messages dans un format légèrement différent
(par exemple, pas de 'rôle' pour l'utilisateur/modèle dans l'historique après le prompt initial).
Cet exemple simplifie pour plus de clarté, en supposant un nouveau chat par appel ou
une fonction de conversion. Pour un chat continu approprié, vous géreriez une `chat_session`.
"""
try:
# Par souci de simplicité, conversion des messages de type OpenAI au format de Gemini
# pour un seul tour. Pour les conversations à plusieurs tours, vous géreriez une session de chat.
gemini_messages = []
for msg in messages:
role_map = {"user": "user", "assistant": "model"}
gemini_messages.append({"role": role_map.get(msg["role"], "user"), "parts": [msg["content"]]})
# Pour une seule requête, le dernier message est le prompt.
# Pour un chat continu, utilisez `start_chat` et `send_message`.
response = self._model.generate_content(gemini_messages[-1]["parts"][0]) # Prend le dernier message de l'utilisateur
return response.text
except Exception as e:
print(f"Erreur lors de l'appel à l'API Gemini : {e}")
return "Désolé, je n'ai pas pu obtenir de réponse de Gemini."
def get_model_name(self) -> str:
return self._model_name
ai_factory.py
La fabrique qui fournit des instances de fournisseurs d'IA basées sur un identifiant de chaîne de caractères.
from typing import Type
from ai_providers.abstract_ai_provider import AbstractAIProvider
from ai_providers.openai_provider import OpenAIAIProvider
from ai_providers.gemini_provider import GeminiAIProvider
from config import Config
class AIProviderFactory:
"""Fabrique pour la création d'instances de fournisseurs d'IA."""
_providers: dict[str, Type[AbstractAIProvider]] = {
"openai": OpenAIAIProvider,
"gemini": GeminiAIProvider,
# Ajoutez de nouveaux fournisseurs d'IA ici
# "example_ai": ExampleAIProvider,
}
@staticmethod
def get_provider(provider_name: str) -> AbstractAIProvider:
"""
Retourne une instance du fournisseur d'IA spécifié.
Arguments :
provider_name: Le nom du fournisseur d'IA (par exemple, "openai", "gemini").
Retourne :
Une instance d'AbstractAIProvider.
Lève :
ValueError: Si le nom du fournisseur n'est pas reconnu.
"""
provider_class = AIProviderFactory._providers.get(provider_name.lower())
if not provider_class:
raise ValueError(f"Fournisseur d'IA inconnu : {provider_name}. Fournisseurs disponibles : {list(AIProviderFactory._providers.keys())}")
# Récupère la clé API et le nom du modèle de la configuration en fonction de `provider_name`
api_key = getattr(Config, f"{provider_name.upper()}_API_KEY")
model_name = getattr(Config, f"{provider_name.upper()}_DEFAULT_MODEL")
return provider_class(api_key=api_key, model_name=model_name)
main.py
La logique principale de l'application, démontrant comment utiliser la fabrique pour interagir avec différents modèles d'IA.
from ai_factory import AIProviderFactory
from typing import List, Dict
def chat_interface():
"""Interface de chat en ligne de commande simple."""
print("Bienvenue dans le Chatbot Multi-IA !")
print("Choisissez votre fournisseur d'IA (par exemple, openai, gemini) :")
provider_choice = input("> ").strip().lower()
try:
ai_provider = AIProviderFactory.get_provider(provider_choice)
print(f"Utilisation de {ai_provider.get_model_name()} via le fournisseur {provider_choice}.")
except ValueError as e:
print(e)
return
messages: List[Dict] = []
print("\nCommencez à chatter ! Tapez 'quit' ou 'exit' pour terminer.")
while True:
user_input = input("Vous : ").strip()
if user_input.lower() in ["quit", "exit"]:
print("Au revoir !")
break
messages.append({"role": "user", "content": user_input})
print(f"IA ({ai_provider.get_model_name()}) : Réflexion en cours...", end="\r")
ai_response = ai_provider.generate_response(messages)
print(f"IA ({ai_provider.get_model_name()}) : {ai_response}")
messages.append({"role": "assistant", "content": ai_response})
# Gardez la liste des messages courte à des fins de démonstration, ou implémentez une gestion de l'historique
if len(messages) > 10:
messages = messages[-8:] # Garde les 4 dernières tours (utilisateur + assistant)
if __name__ == "__main__":
# Définissez les variables d'environnement pour les clés API avant l'exécution, par exemple :
# export OPENAI_API_KEY="sk-..."
# export GEMINI_API_KEY="AIza..."
#
# Ou modifiez config.py directement pour les tests (non recommandé pour la production)
chat_interface()
Comment Ajouter une Nouvelle API d'IA (par exemple, ExampleAIProvider
)
-
Installer le SDK : Installez le SDK Python pour votre nouvelle IA (par exemple,
pip install example_sdk
). -
Mettre à jour
config.py
: Ajoutez les configurations de la clé API et du nom du modèle.# config.py
class Config:
# ... configuration existante
EXAMPLE_API_KEY: str = os.getenv("EXAMPLE_API_KEY", "your_example_api_key_here")
EXAMPLE_DEFAULT_MODEL: str = "example-model-v1" -
Créer
ai_providers/example_ai_provider.py
:- Créez un nouveau fichier
example_ai_provider.py
dans le répertoireai_providers
. - Importez
AbstractAIProvider
. - Créez une classe
ExampleAIProvider
qui hérite deAbstractAIProvider
. - Implémentez les méthodes
__init__
etgenerate_response
conformément au SDK de la nouvelle API. N'oubliez pas de convertir les messages au format attendu par l'API si nécessaire. - Implémentez
get_model_name
.
# ai_providers/example_ai_provider.py
# from example_sdk import ExampleClient # Import SDK hypothétique
from typing import List, Dict
from .abstract_ai_provider import AbstractAIProvider
class ExampleAIProvider(AbstractAIProvider):
def __init__(self, api_key: str, model_name: str):
super().__init__(api_key, model_name)
# self._client = ExampleClient(api_key=self._api_key) # Initialise le client SDK
def generate_response(self, messages: List[Dict]) -> str:
# Convertit les messages au format attendu par le SDK d'ExampleAIProvider
# Par souci de simplicité, supposons qu'il prenne le dernier message utilisateur sous forme de chaîne de caractères
last_user_message = next((m["content"] for m in reversed(messages) if m["role"] == "user"), "")
if not last_user_message:
return "Aucun message utilisateur fourni."
try:
# response = self._client.generate(model=self._model_name, prompt=last_user_message)
# return response.text # Ou ce que le SDK renvoie
return f"Réponse d'ExampleAI utilisant le modèle '{self._model_name}' : {last_user_message.upper()} (simulé)"
except Exception as e:
print(f"Erreur lors de l'appel à l'API ExampleAI : {e}")
return "Désolé, je n'ai pas pu obtenir de réponse d'ExampleAI."
def get_model_name(self) -> str:
return self._model_name - Créez un nouveau fichier
-
Mettre à jour
ai_factory.py
:- Importez votre nouvelle classe de fournisseur.
- Ajoutez-la au dictionnaire
_providers
.
# ai_factory.py
# ... importations existantes
from ai_providers.example_ai_provider import ExampleAIProvider # Nouvelle importation
class AIProviderFactory:
_providers: dict[str, Type[AbstractAIProvider]] = {
"openai": OpenAIAIProvider,
"gemini": GeminiAIProvider,
"example_ai": ExampleAIProvider, # Ajouter le nouveau fournisseur ici
}
# ... reste de la classe
Maintenant, lorsque vous exécutez main.py
, vous pouvez taper example_ai
comme choix, et le système instanciera et utilisera votre nouveau fournisseur d'IA sans aucune modification de main.py
lui-même.