Un agent d'IA puissant comme un humain, mais sans interface, reste invisible. Découvrez comment créer une page web interactive en Streamlit pour transformer votre agent LangGraph en outil utilisable par tous.

TRANSFORMER UN AGENT TERMINAL EN OUTIL WEB

Imaginez un agent conversationnel qui gère des réservations de nettoyage comme un vrai conseiller clientèle. Dans une première version, cet agent fonctionnait uniquement dans un terminal Python, comme un programme en mode texte. Le résultat était correct, mais pour le montrer à un client ou l'utiliser soi-même, c'était peu pratique. Une interface web change tout : au lieu de taper des commandes dans un écran noir, on clique sur des boutons, on voit les messages s'afficher en temps réel, et l'expérience devient intuitive.

C'est exactement ce que propose Streamlit, un outil qui permet de créer des interfaces web en Python avec très peu de code. Contrairement à un site web classique, Streamlit ne demande pas de maîtriser HTML ou JavaScript. Il suffit d'écrire du Python, et Streamlit génère automatiquement une page web fonctionnelle. Dans cet article, nous allons voir comment encapsuler un agent LangGraph dans une interface Streamlit, pour en faire un outil prêt à l'emploi.

Un agent LangGraph sans interface est comme une voiture sans volant : ça roule, mais personne ne peut l'utiliser.

DÉCOMPOSER LE PROJET : AGENT + INTERFACE

Le projet se compose de deux parties distinctes mais complémentaires :

1. L'agent LangGraph : c'est le cerveau du système. Il gère la logique métier, les décisions, et les interactions avec l'utilisateur. Dans notre cas, il s'agit d'un agent capable de gérer des réservations de nettoyage de maison ou de canapé. L'agent utilise des Outils comme LangChain pour communiquer avec des modèles de langage et OpenAI pour générer des réponses.

2. L'interface Streamlit : c'est la vitrine. Elle affiche les messages échangés, les détails de la réservation, les options de créneau horaire, et permet à l'utilisateur d'interagir avec l'agent. Streamlit ne modifie pas la logique de l'agent, il se contente de présenter son état actuel et de transmettre les actions de l'utilisateur vers l'agent.

Cette séparation est cruciale : elle permet de réutiliser l'agent dans d'autres contextes (une application mobile, une API, un chatbot WhatsApp) sans avoir à tout réécrire. L'interface Streamlit est juste une couche supplémentaire qui facilite l'interaction humaine.

INSTALLER STREAMLIT ET SES DÉPENDANCES

Pour commencer, il faut installer Streamlit et les bibliothèques nécessaires. Le projet utilise Poetry, un gestionnaire de dépendances pour Python, qui simplifie l'installation des paquets et la gestion des versions.

poetry add streamlit

Cette commande ajoute Streamlit au projet et met à jour les fichiers pyproject.toml et poetry.lock. Une fois Streamlit installé, on peut créer un nouveau fichier streamlit_app.py qui contiendra toute la logique de l'interface.

STRUCTURE DE BASE : IMPORTER LES COMPOSANTS

Le fichier streamlit_app.py commence par importer tous les modules nécessaires. Voici les principales dépendances :

from __future__ import annotations

import os
from datetime import datetime
from typing import Any
from uuid import uuid4

import streamlit as st
from dotenv import load_dotenv
from langchain_core.messages import AIMessage, HumanMessage
from langchain_openai import ChatOpenAI

from customerserviceagent.graph import build_graph
from customerserviceagent.models import (
    AgentState,
    BookingDetails,
    TimeOption,
)
from customerserviceagent.observability import (
    createlangfusehandler,
    flush_langfuse,
    graph_config,
)

Chaque import correspond à une fonctionnalité précise :

  • streamlit as st : pour créer l'interface web.
  • langchain_core.messages : pour gérer les messages échangés entre l'utilisateur et l'agent.
  • langchain_openai : pour connecter l'agent à un modèle de langage comme GPT-4o-mini.
  • customerserviceagent.graph : pour construire le graphe de l'agent (sa logique interne).
  • customerserviceagent.models : pour définir les types de données utilisés (état de l'agent, détails de réservation, options horaires).
  • customerserviceagent.observability : pour ajouter des outils de suivi comme Langfuse, qui permet de tracer les interactions et d'analyser les performances.

INITIALISER L'ÉTAT DE L'AGENT

L'agent LangGraph fonctionne avec un état qui contient toutes les informations nécessaires à son fonctionnement. Cet état est défini comme un dictionnaire Python avec des clés spécifiques. Voici l'état initial utilisé pour notre agent de réservation :

INITIAL_STATE: AgentState = {
    "messages": [],
    "booking_details": BookingDetails(),
    "calculated_price": None,
    "time_options": [],
    "selected_slot": None,
    "status": "gathering_info",
}

Cet état initial contient :

  • messages : une liste vide, prête à accueillir les échanges entre l'utilisateur et l'agent.
  • booking_details : un objet vide de type BookingDetails, qui contiendra les informations de la réservation (adresse, type de nettoyage, etc.).
  • calculated_price : un prix calculé, initialement à None.
  • time_options : une liste vide d'options horaires disponibles.
  • selected_slot : le créneau horaire sélectionné par l'utilisateur, initialement à None.
  • status : un statut indiquant que l'agent est en phase de collecte d'informations (gathering_info).

Après le premier échange avec l'utilisateur, LangGraph conserve cet état et l'enrichit avec les nouvelles informations.

CONSTRUIRE LE GRAPHE DE L'AGENT

Le graphe de l'agent est construit à partir de l'état initial. Voici comment on l'initialise dans le code :

graph = StateGraph(AgentState)

Ce graphe est une structure de données qui définit comment l'agent passe d'un état à un autre en fonction des actions de l'utilisateur. Par exemple, si l'utilisateur fournit une adresse, le graphe passe à l'état suivant où l'agent calcule un prix. Si l'utilisateur accepte un créneau horaire, le graphe passe à l'état final où la réservation est confirmée.

CRÉER UNE SESSION POUR CONSERVER L'ÉTAT

Streamlit est un framework qui relance le script Python à chaque interaction de l'utilisateur (clic sur un bouton, envoi d'un message, etc.). Sans précaution, toutes les variables locales seraient réinitialisées à chaque relance. Pour éviter cela, Streamlit propose un session_state, un espace de stockage persistant qui conserve les données entre les relances.

La fonction initialize_session s'assure que le graphe et les outils nécessaires sont initialisés une seule fois par session navigateur :

def initialize_session() -> None:
    if "graph" in st.session_state:
        return

    llm = ChatOpenAI(
        model=os.getenv("OPENAI_MODEL", "gpt-4o-mini"),
        temperature=0,
    )

    handler = createlangfusehandler()

    st.sessionstate.graph = buildgraph(llm)
    st.session_state.handler = handler
    st.sessionstate.config = graphconfig(
        str(uuid4()),
        handler,
    )
    st.sessionstate.agentstate = INITIAL_STATE.copy()
    st.session_state.started = False

Cette fonction vérifie d'abord si le graphe existe déjà dans la session (if "graph" in st.session_state). Si c'est le cas, elle ne fait rien. Sinon, elle initialise :

  • Un modèle de langage ChatOpenAI avec le modèle gpt-4o-mini (par défaut, configurable via une variable d'environnement).
  • Un gestionnaire de suivi Langfuse pour tracer les interactions.
  • Le graphe de l'agent avec build_graph(llm).
  • Une configuration pour le graphe avec graph_config, qui génère un identifiant unique pour la conversation.
  • L'état initial de l'agent, copié pour éviter les modifications accidentelles.
  • Un indicateur started à False, pour savoir si la conversation a commencé.
Sans sessionstate, Streamlit effacerait tout à chaque clic. Avec sessionstate, l'agent se souvient de tout.

ENVOYER UN MESSAGE À L'AGENT

Lorsque l'utilisateur envoie un message, la fonction _invoke est appelée pour transmettre ce message à l'agent et récupérer sa réponse. Voici son code :

def invoke(customertext: str) -> None:
    """Submit one customer turn to the graph and retain its latest state."""
    graphinput: dict[str, Any] = {"messages": [HumanMessage(content=customertext)]}
    if not st.session_state.started:
        graphinput.update(INITIALSTATE)
        graphinput["messages"] = [HumanMessage(content=customertext)]
        st.session_state.started = True

    try:
        result = st.sessionstate.graph.invoke(graphinput, config=st.session_state.config)
        st.sessionstate.agentstate = result
        flushlangfuse(st.sessionstate.handler)
    except Exception:
        st.sessionstate.started = bool(st.sessionstate.agent_state.get("messages"))
        st.error("The assistant could not process that request. Please try again.")

Cette fonction fait plusieurs choses :

  1. Elle crée un graph_input, un dictionnaire contenant le message de l'utilisateur sous forme de HumanMessage.
  2. Si la conversation n'a pas encore commencé (if not st.session_state.started), elle ajoute l'état initial au graph_input et marque la conversation comme démarrée.
  3. Elle invoque le graphe de l'agent avec st.session_state.graph.invoke, en passant le message et la configuration de la session.
  4. Elle met à jour l'état de l'agent avec le résultat de l'invocation (st.sessionstate.agentstate = result).
  5. Elle vide le buffer de suivi Langfuse avec flush_langfuse pour enregistrer les dernières interactions.
  6. En cas d'erreur, elle réinitialise le statut de la conversation et affiche un message d'erreur à l'utilisateur.

AFFICHER LES MESSAGES DANS L'INTERFACE

La fonction rendermessages est responsable de l'affichage des messages échangés entre l'utilisateur et l'agent. Voici son code :

def rendermessages(state: AgentState) -> None:
    if not state.get("messages"):
        with st.chat_message("assistant"):
            st.write(
                "Hi. I can help you book house or couch cleaning. "
                "Tell me what you need, including the size and service address."
            )
        return

    for message in state["messages"]:
        if isinstance(message, HumanMessage):
            role = "user"
        elif isinstance(message, AIMessage):
            role = "assistant"
        else:
            continue
        with st.chat_message(role):
            st.write(str(message.content))

Cette fonction fait deux choses :

  1. Si aucun message n'a été échangé (if not state.get("messages")), elle affiche un message de bienvenue de l'assistant, invitant l'utilisateur à commencer la réservation.
  2. Sinon, elle parcourt tous les messages de l'état et les affiche dans des bulles de chat adaptées au rôle (utilisateur ou assistant).

Les messages sont affichés avec st.chat_message, une fonction Streamlit qui crée des bulles de chat stylisées, comme dans une application de messagerie classique.

LANCER L'APPLICATION LOCALEMENT

Pour tester l'application, il suffit d'exécuter la commande suivante dans le terminal :

poetry run streamlit run customerserviceagent/streamlit_app.py

Cette commande lance un serveur web local sur le port 8501. L'interface est alors accessible à l'adresse :

http://localhost:8501/

Pour que l'agent fonctionne, il faut avoir configuré une clé API OpenAI dans un fichier .env. Sans cette clé, l'agent ne pourra pas générer de réponses. Le coût d'utilisation est généralement de quelques centimes pour un test complet.

Un agent LangGraph avec une interface Streamlit coûte quelques centimes à tester, mais change tout pour l'expérience utilisateur.

TESTER L'AGENT EN CONDITIONS RÉELLES

Voici ce qui se passe lors d'une interaction avec l'agent :

1. L'utilisateur arrive sur la page et voit un message de bienvenue : « Hi. I can help you book house or couch cleaning. Tell me what you need, including the size and service address. »

2. L'utilisateur envoie un message sans fournir d'adresse : l'agent demande l'adresse.

3. L'utilisateur envoie un message avec l'adresse : l'agent calcule un prix et propose des créneaux horaires.

4. L'utilisateur accepte un créneau : l'agent confirme la réservation et termine la conversation.

Voici un exemple de dialogue :

• Utilisateur : « Je veux réserver un nettoyage de canapé. »

• Agent : « Quelle est l'adresse du service ? »

• Utilisateur : « 123 rue des Écoles, Paris. »

• Agent : « Le nettoyage de canapé à Paris coûte 45€. Voici les créneaux disponibles : [liste de créneaux]. Quel créneau souhaitez-vous ? »

• Utilisateur : « Je choisis le 15 mai à 14h. »

• Agent : « Votre réservation est confirmée pour le 15 mai à 14h. Merci . »

AMÉLIORATIONS POSSIBLES : VERS UNE SOLUTION PRO

L'interface actuelle est fonctionnelle, mais elle peut être améliorée pour offrir une expérience plus professionnelle :

  • Ajouter des boutons pour accepter/refuser un prix ou un créneau horaire.
  • Intégrer un système de paiement en ligne pour finaliser la réservation.
  • Permettre à l'utilisateur de modifier ses informations en cours de conversation.
  • Ajouter un historique des réservations passées.
  • Intégrer l'agent à d'autres canaux comme WhatsApp ou un site web.

Ces améliorations permettraient de transformer cet agent en un véritable outil commercial, utilisable par des entreprises locales pour automatiser leur service client.

POUR ALLER PLUS LOIN : DÉCOUVRIR D'AUTRES PROJETS

Ce projet n'est qu'un exemple parmi d'autres de l'utilisation de LangGraph et Streamlit. Voici d'autres articles qui pourraient vous intéresser :

  • Automatiser le réglage des modèles avec Python, Gemini, LangGraph et Streamlit pour améliorer les performances en régression et classification.
  • Masquage d'outils pour les agents IA : comment optimiser les surfaces d'outils MCP pour réduire le nombre de tokens et améliorer les performances.
  • Gardes-fous pour les systèmes multi-agents : comment protéger vos agents contre les actions non autorisées avec l'OpenAI Agents SDK.
  • Un guide humain pour l'automatisation IA à destination des chefs de produit.

Ces projets montrent comment combiner des outils comme LangGraph, Streamlit, et des modèles de langage pour créer des solutions concrètes et utiles.

EN RÉSUMÉ : STREAMLIT, LE PONT ENTRE IA ET UTILISATEURS

Créer une interface web pour un agent LangGraph avec Streamlit, c'est comme ajouter une carrosserie à une voiture sans châssis. Sans l'agent, l'interface ne sert à rien. Sans l'interface, l'agent reste invisible. Ensemble, ils forment un outil puissant, prêt à être utilisé par n'importe qui, sans compétence technique particulière.

Le code présenté ici est une base solide, mais il peut être étendu et personnalisé selon vos besoins. Que vous souhaitiez automatiser un service client, créer un assistant personnel, ou tester une idée d'application, Streamlit et LangGraph offrent un combo gagnant pour transformer une idée en réalité.

Et vous, quel agent LangGraph allez-vous encapsuler dans une interface Streamlit ?

Sources :
  • Towards Data Science

L'indépendance de CLODCO est votre garantie.

Pour que l'actualité de l'IA reste sans filtre et sans concession, votre soutien est indispensable. Votre contribution est le seul moteur de notre liberté éditoriale.

Soutenir CLODCO