Retour à l'accueil

Développement d'un Chat Console LLM en Python avec Ollama et LiteLLM

Guide Étape par Étape pour Créer un Chat Console Interactif avec des LLM Locaux en Python. Apprenez sur les Prompts Système, la Gestion d'Erreurs et l'Optimisation avec Ollama et LiteLLM.

Chat LLM Interactif en Python : Ollama et LiteLLM pour Développeurs
Advertisement 728x90

Créer un Chat LLM Interactif en Console avec Python, Ollama et LiteLLM

Dans un contexte d'intérêt croissant pour les grands modèles linguistiques (LLM) locaux et leur intégration dans des applications personnalisées, il est essentiel non seulement de faire fonctionner un modèle, mais aussi de créer une interface interactive pleinement fonctionnelle. Cet article, qui s'inscrit dans la continuité de notre série, se concentre sur la transformation d'un script basique de requête LLM unique en un chat console fonctionnel en Python. Nous aborderons les étapes clés pour structurer le code, implémenter des invites système (system prompts), ajouter de l'interactivité et gérer les erreurs, en utilisant la combinaison puissante d'Ollama et LiteLLM. L'objectif est de montrer comment passer d'une simple exécution de commande à la construction d'une application robuste et gérable, capable d'interagir avec l'utilisateur en temps réel, et de jeter les bases d'une expansion fonctionnelle future, y compris la gestion du contexte de dialogue.

D'un Script à Usage Unique à une Application Interactive

La partie précédente de notre guide a démontré l'intégration basique d'un LLM local, hébergé via Ollama, avec du code Python utilisant la bibliothèque LiteLLM. Le résultat était un script capable d'envoyer une seule requête prédéfinie au modèle et d'afficher la réponse, après quoi le programme se terminait. Bien que suffisant pour une introduction initiale à la technologie, cette approche est peu pratique pour toute application réelle. Un véritable chatbot doit offrir une interaction continue : accepter l'entrée de l'utilisateur, la traiter, recevoir une réponse du modèle, puis attendre la question suivante jusqu'à ce que l'utilisateur décide de mettre fin à la session. Cette transformation fondamentale est précisément ce que nous allons implémenter, en convertissant un script statique en un chat console dynamique.

Les améliorations clés à introduire dans cette partie comprennent :

Google AdInline article slot
  • L'extraction de la logique de requête LLM dans une fonction séparée et réutilisable.
  • L'utilisation d'une invite système (system prompt) pour définir le rôle et le comportement de l'assistant.
  • L'organisation d'une boucle de conversation, permettant à l'utilisateur de poser plusieurs questions consécutivement.
  • L'implémentation de la mesure du temps de réponse pour évaluer les performances.
  • La gestion basique des exceptions pour améliorer la robustesse de l'application.

Comprendre le format des messages avec lequel les API LLM fonctionnent est fondamental pour une interaction efficace. Les modèles n'opèrent pas sur une seule ligne de texte ; ils traitent une liste de messages, chacun ayant un rôle spécifique :

  • system : Il s'agit d'une instruction pour le modèle, définissant sa personnalité, son style de réponse, ses limitations ou des données contextuelles importantes. L'utilisateur ne voit pas directement ce message ; il est généré au niveau du code.
  • user : Le message ou la question provenant de l'utilisateur.
  • assistant : La réponse générée par le modèle lui-même. Dans les itérations futures, les réponses précédentes du modèle seront ajoutées ici pour maintenir le contexte.

Architecture d'un Chat LLM en Console en Python

Passons maintenant à l'implémentation. Nous allons modifier le fichier main.py pour encapsuler la logique d'interaction LLM et organiser la boucle de saisie utilisateur. Voici le code mis à jour que vous pouvez utiliser pour remplacer le contenu de votre main.py.

# -*- coding: utf-8 -*-
import time
from typing import Optional
from litellm import completion
 
MODEL = "ollama_chat/qwen2.5:3b"
API_BASE = "http://localhost:11434"
SYSTEM_PROMPT = "You are a helpful assistant. Answer concisely and to the point."
 
 
def send_request_to_llm(user_message: str) -> Optional[str]:
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": user_message},
    ]
    try:
        start_time = time.time()
        response = completion(
            model=MODEL,
            messages=messages,
            api_base=API_BASE,
            request_timeout=120,
        )
        duration = time.time() - start_time
        print(f"\n⏱ Temps de génération : {duration:.2f} sec")
        return response.choices[0].message.content
    except Exception as e:
        print(f"\n❌ Erreur lors de la requête : {e}")
        return None
 
 
def main() -> None:
    print("🤖 Assistant IA local démarré.")
    print("Posez votre question ou tapez 'quitter' pour sortir.\n")
 
    while True:
        user_input = input("👤 Vous : ").strip()
 
        if user_input.lower() in ("quitter", "exit", "sortir"):
            print("Au revoir !")
            break
 
        if not user_input:
            print("⚠️ Veuillez entrer une question.")
            continue
 
        print("\n🤔 Le modèle réfléchit...")
        answer = send_request_to_llm(user_input)
 
        if answer is not None:
            print(f"\n🦙 IA : {answer}\n")
        else:
            print("\n⚠️ Échec de la récupération de la réponse. Vérifiez si Ollama est en cours d'exécution.\n")
 
 
if __name__ == "__main__":
    main()

Analyse des Composants Clés du Code

Examinons de plus près le fonctionnement de chaque bloc de ce script.

Google AdInline article slot

1. Constantes de Configuration :

Au début du fichier, des constantes sont définies : MODEL, API_BASE et SYSTEM_PROMPT. Cette approche permet de modifier facilement les paramètres de configuration, comme l'utilisation d'un modèle LLM différent ou l'ajustement du comportement de l'assistant, sans avoir à modifier la logique principale du code. Ici, SYSTEM_PROMPT joue un rôle crucial, en définissant les instructions initiales pour le modèle qui influencent le ton général et le style de ses réponses.

2. La Fonction send_request_to_llm :

Google AdInline article slot

Cette fonction (def send_request_to_llm(user_message: str) -> Optional[str]:) est l'élément central pour interagir avec le LLM. Elle prend un message utilisateur, construit une liste complète de messages (incluant SYSTEM_PROMPT et user_message) et l'envoie via litellm.completion au modèle Ollama local.

  • Encapsulation : La logique d'envoi d'une requête et de réception d'une réponse est entièrement encapsulée, ce qui rend la boucle main plus propre et plus compréhensible.
  • Mesure du temps : La mesure du temps intégrée (time.time()) permet d'évaluer les performances du modèle, en indiquant le nombre de secondes nécessaires pour générer une réponse. C'est particulièrement utile lors de l'expérimentation avec différents modèles ou paramètres.
  • Gestion des erreurs : Le bloc try...except offre une tolérance aux pannes de base. Si une erreur se produit pendant la requête LLM (par exemple, si Ollama n'est pas disponible), la fonction renvoie None au lieu de lever une erreur critique, permettant à la fonction main de gérer la situation avec élégance et de poursuivre le chat.

3. La Fonction main et la Boucle Interactive :

La fonction main gère l'interface utilisateur et le flux global du chat.

  • Boucle infinie (while True) : Crée une session de chat continue qui persiste jusqu'à ce que l'utilisateur décide explicitement de quitter.
  • Saisie utilisateur : input("👤 Vous : ").strip() attend la saisie de l'utilisateur. La méthode .strip() supprime les espaces de début et de fin, empêchant l'envoi de requêtes vides ou mal formées.
  • Commandes de sortie : La vérification user_input.lower() in ("quitter", "exit", "sortir") permet à l'utilisateur de terminer le programme en utilisant diverses options de commande de sortie.
  • Entrée vide : La vérification if not user_input: empêche l'envoi de messages vides au modèle, invitant l'utilisateur à poser une question.
  • Interaction : main appelle send_request_to_llm avec l'entrée de l'utilisateur et traite la réponse reçue, l'affichant sur la console ou signalant une erreur. La séparation des responsabilités entre main (gestion de l'interface) et send_request_to_llm (interaction avec le modèle) est un principe fondamental de bonne conception logicielle.

Contrôler le Comportement du LLM via l'Invite Système

L'un des outils les plus puissants lorsque l'on travaille avec les LLM est l'invite système (system prompt). Elle permet d'attribuer un rôle spécifique, un style de communication ou un ensemble de contraintes au modèle, modifiant considérablement ses réponses sans changer le modèle lui-même ni ses paramètres. Expérimenter avec SYSTEM_PROMPT est un excellent moyen de comprendre comment vous pouvez subtilement adapter le comportement de l'assistant à des tâches spécifiques. Examinons quelques exemples de la façon dont la modification d'une seule ligne peut avoir un impact drastique sur le dialogue :

  • `SYSTEM_PROMPT = "Tu es un assistant sarcastique. Réponds avec ironie."` — Le modèle répondra avec humour et esprit.
  • `SYSTEM_PROMPT = "Tu es un professeur strict. Corrige les erreurs dans les questions."` — L'assistant ne se contentera pas de répondre, mais signalera également les inexactitudes grammaticales ou factuelles dans les requêtes.
  • `SYSTEM_PROMPT = "Réponds uniquement en anglais. Fournis toujours un exemple de code."` — Le modèle adhérera strictement à la langue anglaise et essaiera d'inclure des exemples de code dans ses réponses, si applicable.

Ces exemples démontrent comment une invite système transforme un modèle statique en un outil flexible capable de s'adapter à divers cas d'utilisation. C'est d'une importance capitale pour la création de chatbots spécialisés, qu'il s'agisse d'assistants techniques, de partenaires créatifs ou d'outils éducatifs.

Points Clés à Retenir

À ce stade de développement, notre chat LLM en console a atteint les résultats importants suivants :

  • Interactivité : L'utilisateur peut engager un dialogue continu, poser des questions et recevoir des réponses en temps réel.
  • Comportement contrôlé : L'invite système permet une configuration facile du rôle et du style de communication du modèle.
  • Modularité du code : La logique d'interaction LLM est séparée de la logique de l'interface utilisateur, ce qui simplifie le développement et le débogage futurs.
  • Performance : La mesure intégrée du temps de génération des réponses aide à surveiller la vitesse du modèle.
  • Fiabilité : La gestion basique des erreurs empêche les plantages du programme dus à des problèmes de connexion ou de modèle.

Défis Courants et Solutions

Lorsque vous travaillez avec des LLM locaux et des applications console, certains problèmes courants peuvent survenir :

  • Le programme se fige après avoir entré une question : Cela se produit le plus souvent parce qu'Ollama n'est pas en cours d'exécution ou que le modèle n'a pas encore été entièrement chargé en mémoire. Assurez-vous qu'Ollama est bien actif (ollama list dans votre terminal) et donnez au modèle le temps de s'initialiser.
  • Réponses trop brèves du modèle : Si le SYSTEM_PROMPT spécifie "répondre de manière concise", le modèle suivra cette instruction. Pour des réponses plus détaillées, modifiez l'invite système en supprimant cette contrainte ou en demandant explicitement "une réponse détaillée avec des exemples".
  • KeyboardInterrupt lors de l'appui sur Ctrl+C : C'est un comportement Python standard. Bien que non critique pour un projet d'apprentissage, en production, vous pouvez envelopper main() dans un bloc try/except KeyboardInterrupt pour un arrêt plus élégant.
  • Problèmes d'encodage (caractères illisibles sous Windows) : Cela peut être résolu en définissant l'encodage du terminal en UTF-8, par exemple, en utilisant la commande $OutputEncoding = [System.Text.Encoding]::UTF8 dans PowerShell.

Prochaines Étapes : Vers un Dialogue Complet

Bien que notre chat console soit devenu significativement plus fonctionnel, il lui manque actuellement une "mémoire". Chaque requête est traitée indépendamment, et le modèle ne se souvient pas des tours de dialogue précédents. Cela signifie que si vous demandez : "Pouvez-vous faire la même chose, mais en plus court ?", le modèle ne comprendra pas à quoi "la même chose" fait référence. Dans la prochaine partie, nous explorerons comment implémenter l'historique des messages, en passant l'intégralité du contexte de dialogue avec chaque nouvelle requête, ce qui le transformera en un partenaire conversationnel véritablement intelligent.

— Editorial Team

Advertisement 728x90

Lire ensuite