top of page

Comment créer un chatbot IA avec Streamlit et Python : votre première web app en 40 lignes

Oct 27
10 min read

Tout le monde a déjà discuté avec un assistant conversationnel. Beaucoup moins de gens savent qu'il suffit de quelques dizaines de lignes de Python pour obtenir, sur son propre écran, une fenêtre de discussion qui se comporte de la même façon : une zone de saisie en bas, les messages qui s'empilent, la réponse qui s'écrit mot après mot. C'est exactement ce que nous allons construire ici, avec Streamlit.


Ce tutoriel s'adresse à un développeur ou à un analyste qui connaît les bases de Python, mais qui n'a jamais fait de développement web. Pas besoin de HTML, de JavaScript ni de l'un des frameworks web classiques : Streamlit s'occupe de tout ce qui s'affiche, vous n'écrivez que la logique. À la fin, vous aurez un assistant conversationnel fonctionnel, personnalisable, protégé par un mot de passe et déployé en ligne.


Si vous cherchez des ressources en anglais, vous tomberez sur des dizaines de pages intitulées « creating a chatbot in Python using Streamlit with OpenAI » ou « generative AI applications » ; cet article en est l'équivalent francophone, avec les pièges que nous rencontrons en formation.


Pourquoi Streamlit pour le développement de chatbots en Python ?

Streamlit est un framework Python open source conçu à l'origine pour les data scientists : afficher un tableau, un graphique ou un modèle de machine learning dans une page web sans toucher au front-end. Un script classique, exécuté de haut en bas, devient une page interactive. Là où Flask ou Django demandent de gérer des routes, des templates et des formulaires, Streamlit vous laisse écrire st.title("Bonjour") et le titre apparaît.


Depuis 2023, la bibliothèque intègre des composants dédiés aux conversations : une bulle de message, une zone de saisie fixée en bas de page, l'affichage progressif d'un texte. Autrement dit, les trois briques d'une fenêtre de chat sont fournies, il ne reste qu'à les relier à un modèle.


Critère

Streamlit

Flask / FastAPI + front

Gradio

Langage à connaître

Python uniquement

Python + HTML/CSS/JS

Python uniquement

Composants de chat natifs

Oui (st.chat_message, st.chat_input)

À écrire soi-même

Oui (gr.ChatInterface)

Personnalisation de la page

Bonne (barre latérale, colonnes, thème)

Totale

Limitée

Déploiement gratuit

Streamlit Community Cloud

À héberger

Hugging Face Spaces

Usage typique

Outil interne, prototype, démo client

Produit grand public

Démo de modèle


Pour un outil interne ou un prototype à montrer à un client, Streamlit est le choix le plus rapide. Pour un produit destiné à des milliers d'utilisateurs simultanés, on passera à un framework plus bas niveau, mais ce n'est pas le sujet du jour.


Ce qu'un chatbot IA fait vraiment (et ce qu'il ne fait pas)

Avant d'écrire la première ligne, une précision qui évite bien des déceptions. Un assistant conversationnel moderne ne comprend rien et ne se souvient de rien. Il repose sur un grand modèle de langage (les LLMs, dont GPT-3 fut le premier à grand public en 2020) qui prédit le mot suivant à partir du texte qu'on lui envoie. Nous avons détaillé ce mécanisme dans l'article sur les tokens et l'inférence : [LIEN-ARTICLE-TOKENS-INFERENCE] à mettre une fois publié)


Deux conséquences directes pour votre code :

  • La mémoire de la conversation, c'est vous qui la gérez. À chaque message de l'utilisateur, vous renvoyez au modèle l'historique complet. Le modèle ne stocke rien entre deux appels.

  • Le modèle tourne ailleurs. Vous n'allez pas entraîner des modèles d'apprentissage avec TensorFlow : vous allez appeler à distance un modèle déjà entraîné et hébergé par un fournisseur. Votre application n'est que l'écran et la mémoire.

Ces deux points structurent tout ce qui suit.


Prérequis : Python, Streamlit et une clé d'API OpenAI

Vous avez besoin de Python 3.10 ou plus récent, d'un éditeur de code et d'un terminal. Créez un dossier de projet, un environnement virtuel, puis lancez le pip install des trois bibliothèques nécessaires :

python -m venv .venv
source .venv/bin/activate        # Windows : .venv\Scripts\activate
pip install streamlit openai python-dotenv

Côté modèle, il vous faut un compte développeur chez un fournisseur et une clé (l'OpenAI API key que vous verrez partout dans les tutoriels, à créer sur platform.openai.com). Dans ce tutorial nous utilisons l'offre de l'éditeur de ChatGPT (the OpenAI API, dans leur documentation), mais le code s'adapte en quelques lignes à Anthropic, Mistral ou à un modèle local servi par Ollama. Une fois la clé créée, ne la collez jamais dans votre script. Déposez-la dans un fichier .env à la racine du projet :

OPENAI_API_KEY=sk-...

Et ajoutez .env à votre .gitignore immédiatement. La bibliothèque dotenv (installée ci-dessus sous le nom python-dotenv) lira ce fichier au démarrage ; la clé restera sur votre machine et n'apparaîtra jamais dans un dépôt.


Première application Streamlit : l'interface de chat en 20 lignes

Commençons sans modèle. Ce premier script affiche une fenêtre de conversation complète et répète simplement ce que vous tapez. L'objectif est de comprendre les trois composants et, surtout, la notion de session state.

# app_echo.py
import streamlit as st

st.title("Mon premier assistant")

# 1. Initialiser l'historique (une seule fois par session)
if "messages" not in st.session_state:
    st.session_state.messages = []

# 2. Réafficher tous les messages précédents
for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

# 3. Lire la saisie de l'utilisateur
if prompt := st.chat_input("Écrivez votre message…"):
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)

    reponse = f"Vous avez écrit : {prompt}"
    st.session_state.messages.append({"role": "assistant", "content": reponse})
    with st.chat_message("assistant"):
        st.markdown(reponse)

Lancez-le depuis le terminal :

streamlit run app_echo.py

Un navigateur s'ouvre sur http://localhost:8501 avec une page de discussion fonctionnelle : votre première Streamlit app, une chatbot interface complète sans une ligne de HTML.


Le piège numéro un : Streamlit réexécute tout le script

C'est la règle à comprendre absolument. À chaque interaction (un message envoyé, un bouton cliqué, un curseur déplacé), Streamlit relance le script entier depuis la première ligne. Si vous écrivez messages = [] en haut du fichier, la liste est vidée à chaque message et la conversation ne garde jamais plus d'un échange.

st.session_state est le dictionnaire qui survit à ces réexécutions, le temps que l'onglet du navigateur reste ouvert. D'où le if "messages" not in st.session_state : on initialise la liste au premier passage, puis on la laisse tranquille. Tout ce qui doit persister pendant la conversation (l'historique, le modèle choisi, un compteur) va dans st.session_state.


Les trois composants de chat

  • st.chat_input() affiche la zone de saisie fixée en bas de page et renvoie le texte quand l'utilisateur valide, sinon None. L'opérateur morse := permet de tester et d'affecter en une ligne.

  • st.chat_message(role) crée une bulle avec l'avatar correspondant au rôle ("user" ou "assistant"). On l'utilise comme contexte with et tout ce qu'on affiche à l'intérieur se retrouve dans la bulle.

  • st.markdown() affiche le contenu. Le modèle renverra souvent du Markdown (listes, gras, code), autant l'afficher correctement dès le départ.


Brancher l'API : comment créer un chatbot qui répond vraiment

Remplaçons la ligne reponse = f"Vous avez écrit…" par un vrai appel au modèle. Le script ci-dessous ajoute trois choses : le chargement de la clé depuis .env, un message « system » qui donne sa personnalité à l'assistant, et l'affichage en streaming pour que la réponse s'écrive progressivement au lieu d'apparaître d'un bloc après plusieurs secondes.

# app_chat.py
import os
import streamlit as st
from dotenv import load_dotenv
from openai import OpenAI

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

MODELE = "gpt-4o-mini"
SYSTEM = (
    "Tu es un assistant pédagogique spécialisé en Power BI et en Python. "
    "Tu réponds en français, de façon concise, avec des exemples concrets."
)

st.title("Assistant MYPE")

if "messages" not in st.session_state:
    st.session_state.messages = []

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

if prompt := st.chat_input("Posez votre question…"):
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)

    def generer():
        """Générateur qui renvoie la réponse morceau par morceau."""
        response = client.chat.completions.create(
            model=MODELE,
            messages=[{"role": "system", "content": SYSTEM}]
                     + st.session_state.messages,
            stream=True,
        )
        for chunk in response:
            texte = chunk.choices[0].delta.content
            if texte:
                yield texte

    with st.chat_message("assistant"):
        reponse_complete = st.write_stream(generer())

    st.session_state.messages.append(
        {"role": "assistant", "content": reponse_complete}
    )

Quelques points à retenir sur ce code :

  • L'historique complet part à chaque appel. La liste messages envoyée contient le message system suivi de toute la conversation. C'est ainsi que l'assistant « se souvient » : il relit tout à chaque fois. Sur une longue conversation, cela finit par coûter cher en tokens ; on verra plus bas comment tronquer.

  • Le message system n'est pas stocké dans st.session_state. On le préfixe au moment de l'appel. Ainsi il ne s'affiche pas à l'écran et on peut le modifier sans toucher à l'historique.

  • st.write_stream() fait deux choses. Il affiche les morceaux au fur et à mesure qu'ils arrivent et il renvoie le texte complet une fois le flux terminé, ce qui permet de l'ajouter à l'historique en une ligne. L'objet response renvoyé par le client est un itérateur de chunks ; chaque chunk porte quelques caractères dans delta.content.

Lancez streamlit run app_chat.py : vous avez un assistant qui répond, qui garde le fil de la conversation et qui écrit en direct. Quarante lignes.


Personnaliser le bot : prompt, modèle, température et UI

Un assistant générique n'intéresse personne. La valeur vient de la personnalisation : à qui il parle, de quoi, avec quel ton. Trois leviers, tous accessibles depuis une barre latérale sans toucher au reste du code.

# À placer après st.title(), avant l'affichage des messages
with st.sidebar:
    st.header("Réglages")
    MODELE = st.selectbox("Modèle", ["gpt-4o-mini", "gpt-4o"])
    temperature = st.slider("Température", 0.0, 1.5, 0.3, 0.1)
    SYSTEM = st.text_area(
        "Instructions de l'assistant",
        value=SYSTEM,
        height=160,
    )
    if st.button("Nouvelle conversation"):
        st.session_state.messages = []
        st.rerun()

Puis passez temperature=temperature dans l'appel client.chat.completions.create(...).

Ce que chaque réglage change :

  • Le message system est le levier le plus puissant. « Tu es l'assistant du service comptabilité, tu ne réponds qu'aux questions sur les notes de frais et tu renvoies vers le RH pour le reste » donne un assistant totalement différent du même code avec « Tu es un assistant généraliste ». Testez plusieurs formulations, c'est là que se joue la qualité perçue.

  • Le modèle arbitre entre coût, vitesse et qualité. Le petit modèle suffit pour 80 % des usages internes.

  • La température règle le caractère déterministe des réponses : basse pour de l'assistance technique ou de l'extraction, plus haute pour de la rédaction. Nous l'avons expliquée dans l'article sur l'inférence.


Côté UI, quelques touches suffisent à rendre l'outil présentable : st.set_page_config(page_title="Assistant MYPE", page_icon="💬") en première instruction du script, un avatar personnalisé avec st.chat_message("assistant", avatar="logo.png"), et un fichier .streamlit/config.toml pour imposer les couleurs de votre charte :

[theme]
primaryColor = "#47267a"
backgroundColor = "#ffffff"
secondaryBackgroundColor = "#f3eefb"
textColor = "#2a1a4a"
font = "sans serif"

Limiter la taille de l'historique

Pour éviter que les appels d'API ne grossissent indéfiniment, gardez seulement les derniers échanges au moment de l'appel (pas dans l'affichage, où l'utilisateur veut tout voir) :

HISTORIQUE_MAX = 20  # 10 échanges question/réponse
messages_envoyes = (
    [{"role": "system", "content": SYSTEM}]
    + st.session_state.messages[-HISTORIQUE_MAX:]
)

C'est une fonctionnalité de confort pour un prototype ; dans un outil de production on résumera plutôt les anciens messages, mais la logique reste la même : ce qu'on envoie au modèle est une décision de votre code, pas une fatalité.


Ajouter une authentification et protéger la clé

Dès que l'application sort de votre machine, deux questions se posent : qui a le droit de l'utiliser, et où vit la clé.

La clé. En déploiement, le fichier .env n'existe plus. Streamlit propose st.secrets, qui lit un fichier .streamlit/secrets.toml en local et, une fois en ligne, un coffre de secrets saisi dans les réglages de l'hébergeur. Le code devient indépendant de l'environnement :

client = OpenAI(api_key=st.secrets["OPENAI_API_KEY"])

Ajoutez .streamlit/secrets.toml au .gitignore, comme le .env.

L'accès. Le plus simple pour un outil interne : un mot de passe partagé, lui aussi stocké dans les secrets, et une porte à l'entrée du script.

def verifier_acces():
    if st.session_state.get("autorise"):
        return True
    mdp = st.text_input("Mot de passe", type="password")
    if mdp and mdp == st.secrets["APP_PASSWORD"]:
        st.session_state.autorise = True
        st.rerun()
    elif mdp:
        st.error("Mot de passe incorrect")
    return False

if not verifier_acces():
    st.stop()

st.stop() interrompt l'exécution du script : rien de ce qui suit ne s'affiche tant que l'utilisateur n'est pas autorisé. Pour un usage plus sérieux, les versions récentes de Streamlit proposent st.login(), qui délègue l'identification à Google ou Microsoft Entra ID (OpenID Connect) : vos collègues se connectent avec leur compte professionnel et vous récupérez leur adresse dans st.user. C'est la bonne voie dès que l'outil touche des données internes.


Déployer la webapp sur Streamlit Community Cloud via GitHub


Votre application tourne en local ; il est temps de la mettre en ligne pour que d'autres l'utilisent. L'hébergement gratuit de l'éditeur se branche directement sur un dépôt GitHub.

  1. Créez un fichier requirements.txt à la racine avec vos dépendances :streamlitopenai (plus python-dotenv si vous l'utilisez encore en local).

  2. Poussez le projet sur un dépôt GitHub, sans .env ni secrets.toml, vérifiez-le deux fois.

  3. Sur share.streamlit.io, connectez votre compte GitHub, choisissez le dépôt, la branche et le fichier principal (app_chat.py).

  4. Dans les réglages avancés de l'application, collez le contenu de votre secrets.toml (la clé et le mot de passe). C'est ce que st.secrets lira en production.

  5. Cliquez sur Deploy. Deux minutes plus tard, votre assistant a une URL publique.

À chaque git push sur la branche choisie, l'application se redéploie seule. Pour un hébergement sur votre propre infrastructure (serveur interne, Azure, conteneur Docker), la commande reste streamlit run app_chat.py ; seule la gestion des secrets change.


Deux limites de l'offre gratuite à connaître : l'application s'endort après quelques jours sans visite (elle se réveille en une vingtaine de secondes), et les ressources sont modestes. Pour un outil interne consulté par une équipe, c'est largement suffisant.


Les limites de ce premier assistant, et la suite

Ce que vous avez construit est une couche de présentation et de mémoire autour d'un modèle. Il répond à partir de ce que le modèle a appris et de ce que vous mettez dans le message system. Il ne connaît ni vos procédures, ni vos documents, ni vos chiffres. Posez-lui une question sur votre catalogue produit et il inventera une réponse plausible.


La prochaine étape consiste donc à connecter l'assistant à vos propres données : vos PDF, vos pages SharePoint, votre documentation interne. C'est le rôle du RAG (Retrieval-Augmented Generation), qui va chercher les passages pertinents dans vos documents et les injecte dans la requête avant de la transmettre au modèle.


Au-delà, les mêmes briques servent à toutes les applications d'IA générative que vous verrez passer : un assistant dans Slack ou Teams, un formulaire qui pré-remplit ses champs, un outil qui résume des tickets. L'intégration dans un outil existant remplace Streamlit par un connecteur, mais la logique « historique + message system + appel au modèle » ne bouge pas. C'est d'ailleurs exactement ce que fait Copilot dans Power BI : un modèle, un contexte (votre modèle sémantique), une fenêtre de chat. Nous en parlons dans l'article vibe coding vs Power BI.


En résumé


  • Streamlit transforme un simple script en page web interactive, avec des composants de chat prêts à l'emploi : st.chat_input, st.chat_message, st.write_stream.

  • La règle à ne jamais oublier : le script est réexécuté à chaque interaction. Tout ce qui doit survivre va dans st.session_state.

  • Un assistant conversationnel, c'est un historique que votre code renvoie intégralement au modèle à chaque message, précédé d'un message system qui fixe le comportement.

  • La personnalisation passe par le message system, le choix du modèle et la température ; le thème et l'avatar font le reste.

  • Clé et mot de passe vivent dans st.secrets, jamais dans le code ; le déploiement se fait en quelques clics depuis un dépôt GitHub.




 
 
bottom of page