Déploiement d'une Application Llama3 Autohébergée en Quatre Étapes

Plusieurs approches existent pour développer des applications basées sur des modèles de langage:

1. Plateformes cloud

Soltuions comme GPT-4 ou Huggingface Space présentent des limites :

  • Problèmes de confidentialité des données
  • Latence liée aux ressources partagées
  • Coûts des appels d'API

2. Applications autogérées

Solutions clés en main comme Ollama+OpenWebUI simplifient le déploiement mais manquent de flexibilité pour des personnalisatinos avancées.

3. Applications personnalisées

Notre solution combine des composants sur mesure pour un contrôle total du flux de traitement.

Architecture d'application autohébergée### Étapes de développement

1. Modèle LLM avec Huggingface

Chargement du modèle Llama-3-8B avec quantification 4-bit :

from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
import torch

modele_id = "meta-llama/Meta-Llama-3-8B-Instruct"
tokeniseur = AutoTokenizer.from_pretrained(modele_id)

config_quant = BitsAndBytesConfig(load_in_4bit=True)
modele = AutoModelForCausalLM.from_pretrained(
    modele_id,
    device_map="auto",
    torch_dtype=torch.bfloat16,
    cache_dir="modeles",
    quantization_config=config_quant
)

Fonction de génération avec gestion du contexte :

def generer_reponse(utilisateur_id, nouveau_message, historiques, tokeniseur, modele):
    historique = historiques.get(utilisateur_id, [])
    historique.append({"role": "utilisateur", "content": nouveau_message})
    
    inputs = tokeniseur.apply_chat_template(
        historique,
        add_generation_prompt=True,
        return_tensors="pt"
    ).to(modele.device)

    try:
        sorties = modele.generate(
            inputs,
            max_new_tokens=256,
            do_sample=True,
            temperature=0.6,
            top_p=0.9,
            eos_token_id=[tokeniseur.eos_token_id, tokeniseur.convert_tokens_to_ids("<|eot_id|>")]
        )
        
        reponse = tokeniseur.decode(sorties[0], skip_special_tokens=True)
        reponse = reponse.split("assistantn")[-1].strip()
        historique.append({"role": "system", "content": reponse})
        historiques[utilisateur_id] = historique
        
        return reponse
    except Exception as e:
        print(f"Erreur: {e}")
        return "Erreur de traitement, veuillez réessayer"

2. Backend avec Tornado

Serveur web avec gestion des conversations :

import tornado.ioloop
import tornado.web
import json

historiques_conversations = {}

class GestionnairePrincipal(tornado.web.RequestHandler):
    def get(self):
        self.render("web/index.html")

class GestionnaireChat(tornado.web.RequestHandler):
    def post(self):
        donnees = json.loads(self.request.body)
        reponse = generer_reponse(
            donnees.get('user_id'),
            donnees.get('message'),
            historiques_conversations,
            tokeniseur,
            modele
        )
        self.write({'reponse': reponse})

def creer_app():
    return tornado.web.Application([
        (r"/", GestionnairePrincipal),
        (r"/chat", GestionnaireChat),
        (r"/(style.css)", tornado.web.StaticFileHandler, {"path": "web/"}),
        (r"/(script.js)", tornado.web.StaticFileHandler, {"path": "web/"})
    ])

if __name__ == "__main__":
    app = creer_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

3. Interface utilisateur

Structure HTML de base :


<html>
<head>
    <meta charset="UTF-8">
    <title>Assistant Local</title>
    <link rel="stylesheet" href="style.css">
    <script src="https://cdn.jsdelivr.net/npm/showdown/dist/showdown.min.js"></script>
</head>
<body>
    <div class="conteneur-chat">
        <div id="boite-chat"></div>
        <input type="text" id="saisie_utilisateur" placeholder="Votre message..." autofocus>
        <button onclick="envoyerMessage()">Envoyer</button>
    </div>
    <script src="script.js"></script>
</body>
</html>

Fonctions JavaScript essentielles :

function obtenirIdUtilisateur() {
    let id = localStorage.getItem('user_id')
    if (!id) {
        id = 'uid' + Math.random().toString(36).substr(2, 9)
        localStorage.setItem('user_id', id)
    }
    return id
}

function envoyerMessage() {
    const saisie = document.getElementById("saisie_utilisateur")
    const message = saisie.value.trim()
    const userId = obtenirIdUtilisateur()

    if (message === "") return

    ajouterMessage(message, "message-utilisateur")

    fetch('/chat', {
        method: 'POST',
        body: JSON.stringify({ 'user_id': userId, 'message': message}),
        headers: {'Content-Type': 'application/json'}
    }).then(reponse => reponse.json())
    .then(donnees => {
        ajouterMessage(donnees.reponse, "message-systeme")
    }).catch(erreur => {
        console.error('Erreur:', erreur)
    })

    saisie.value = ""
}

Interface de chat#### 4. Exposition publique avec Ngrok

Étapes de déploeiment :

ngrok config add-authtoken <votre_token>
ngrok http http://localhost:8888

Configuration NgrokApplication mobile

Étiquettes: Llama3 HuggingFace tornado Ngrok quantification

Publié le 17 juin à 19h58