Ollama + Python : Créer votre IA Locale d'Entreprise (Zéro Données dans le Cloud) 2026

Par Jérémy · · 30 min de lecture
Ollama Python IA locale d'entreprise, zéro données dans le cloud
Un hacker solitaire assis à un bureau sombre, le visage éclairé par la lueur verte phosphorescente de son écran.

Les serveurs de votre DSI sont en train de cataloguer chaque prompt que vous envoyez à ChatGPT et le département juridique a envoyé une note circulaire de douze pages sur le RGPD. L'IT a bloqué les API OpenAI sur le réseau corporate car ils pensent vous avoir acculé dans un couloir sans issue.

Ils ont tort.

Il existe un protocole, un chemin de traverse que la politique de sécurité n'a pas encore cartographié, c'est Ollama + Python : un LLM qui tourne sur votre machine, sans envoyer une seule donnée vers l'extérieur.

Votre IA d'entreprise, vos règles, zéro abonnement et zéro fuite de données.

Ce guide est votre carte d'accès. Chaque script est annoté, expliqué, et conçu pour être utilisé immédiatement, que vous soyez développeur confirmé ou employé de bureau qui a décidé d'apprendre Python un soir de désespoir.

Pourquoi une IA locale ? Le dossier RGPD et le coût du cloud

Chaque requête que vous envoyez à un LLM cloud, que ce soit GPT-4, Claude, ou Gemini, quitte votre machine. Elle traverse des câbles transatlantiques, atterrit sur des serveurs que vous ne contrôlez pas, est potentiellement utilisée pour affiner des modèles futurs, et revient sous forme de réponse.

Pour une conversation sur la météo, c'est anecdotique, mais pour analyser les contrats clients de votre boîte, c'est une catastrophe juridique en slow motion.

Le RGPD ne plaisante pas avec les données personnelles ou sensibles traitées hors UE. Et au-delà du légal, il y a le financier : à 0,015€ le millier de tokens, une équipe de 10 personnes qui utilise GPT-4 intensément peut brûler 500€/mois en purs frais d'API, sans compter les abonnements SaaS qui s'accumulent.

La solution ? Rapatrier l'intelligence, la faire tourner chez vous. Ca tombe bien car c'est exactement ce que fait Ollama.

Ollama est un outil open-source qui emballe des LLMs de pointe (Llama 3, Mistral, Gemma, Qwen) dans un format portable, servi via une API REST locale. Ollama fonctionne sur Linux, macOS et Windows.

Une fois le modèle téléchargé, il n'a plus besoin d'internet pour fonctionner.

Vous avez peut-être déjà exploré la logique d'un pipeline RAG local en Python sur ce blog, Ollama est la brique de fondation qui rend tout cela souverain et autonome et nous allons construire l'arsenal complet.

Ce dont vous avez besoin (Matériel)

Pas besoin d'un datacenter dans votre garage, un laptop moderne suffit pour les modèles 7B :

  • RAM : minimum 16 Go (8 Go pour les petits modèles)
  • Stockage : 5 à 10 Go libres par modèle (format GGUF compressé)
  • GPU : optionnel mais bienvenu, NVIDIA (CUDA) ou Apple Silicon (Metal) accélèrent la génération. En CPU seul, attendez-vous à 5-15 tokens/seconde sur un 7B.
  • Python 3.10+ : Si vous n'avez pas encore configuré votre environnement, suivez notre guide d'installation Python pour Windows et Mac.

Étape 1: Installer Ollama et Télécharger votre Premier Modèle

Avant d'écrire une seule ligne de Python, Ollama doit être installé sur votre machine car c'est lui qui joue le rôle de daemon local : un petit serveur qui tourne en arrière-plan, charge les modèles en mémoire, et répond aux requêtes de vos scripts via une API REST sur le port 11434.

Pensez-y comme le gardien da votre setup, il reçoit les ordres, il exécute.

Le script ci-dessous couvre les quatre gestes d'initialisation dans l'ordre exact où vous devez les exécuter.

Ne sautez pas l'étape du modèle d'embedding (nomic-embed-text) car il sera indispensable plus tard pour le pipeline RAG. C'est un modèle léger (274 Mo) spécialisé dans la transformation de texte en vecteurs numériques qui est une opération différente de la génération de texte.

# ============================================================
# ÉTAPE 1 — Installer Ollama (Linux / macOS)
# Copiez-collez cette commande dans votre terminal.
# Elle télécharge et installe le daemon Ollama en une passe.
# ============================================================
curl -fsSL https://ollama.com/install.sh | sh

# Sur Windows : télécharger l'installeur .exe sur https://ollama.com
# et lancer l'exécutable. Le daemon démarre automatiquement au boot.

# ============================================================
# ÉTAPE 2 — Vérifier que le daemon tourne correctement
# Si cette commande retourne un numéro de version, vous êtes prêt.
# ============================================================
ollama --version
# Résultat attendu : ollama version is 0.6.x

# ============================================================
# ÉTAPE 3 — Télécharger les modèles (choix selon votre RAM)
# Ces commandes téléchargent les poids du modèle UNE SEULE FOIS.
# Après ça, tout fonctionne hors ligne.
#
#   llama3.1:8b     → 4.5 Go  — idéal avec 16 Go RAM, usage général
#   qwen2.5:7b      → 4.7 Go  — meilleur pour les tâches analytiques
#   nomic-embed-text → 274 Mo  — OBLIGATOIRE pour le RAG (Script 03)
# ============================================================
ollama pull llama3.1:8b
ollama pull qwen2.5:7b
ollama pull nomic-embed-text

# ============================================================
# ÉTAPE 4 — Test rapide en ligne de commande
# Si vous obtenez une réponse cohérente, le modèle est opérationnel.
# Remplacez [TEXTE] par n'importe quel contenu à résumer.
# ============================================================
ollama run llama3.1:8b "Résume ce contrat en 3 points clés : [TEXTE]"

# Le serveur REST écoute maintenant sur :
# http://localhost:11434
# C'est l'adresse que vos scripts Python utiliseront.

Une fois cette étape franchie, le daemon Ollama tourne en arrière-plan. Vous pouvez donc ouvrir un nouveau terminal et commencer à écrire du Python, le serveur local répondra à chaque appel sans connexion internet.

Étape 2: Premier contact Python : Appel simple et streaming

Maintenant que le daemon tourne, branchez Python dessus.

La librairie officielle ollama abstrait les appels HTTP vers localhost:11434 en fonctions propres. Vous installez la librairie une seule fois avec pip, puis vous importez et appelez.

Le script ci-dessous vous propose deux modes d'utilisation que vous choisirez selon votre contexte :

  • Mode standard (interroger_ia_locale) : attend que le modèle ait fini de générer toute la réponse, puis la retourne d'un bloc. C'est idéal pour les scripts batch, les traitements automatiques, et les cas où vous n'affichez pas la réponse à un humain en direct.
  • Mode streaming (interroger_avec_streaming) : affiche la réponse mot par mot au fur et à mesure qu'elle est générée. C'est indispensable pour les interfaces conversationnelles ou les longues réponses, l'expérience est bien plus fluide qu'attendre 30 secondes un bloc de texte.

Ce que vous devez personnaliser : le content du message system. C'est votre prompt de personnalité, il définit le comportement de l'IA pour toute la session. Adaptez-le à votre contexte métier avant de déployer.

# ============================================================
# Installation de la librairie Python officielle
# À exécuter une seule fois dans votre terminal.
# ============================================================
pip install ollama

# ============================================================
# SCRIPT 01 — Premier appel et streaming de réponse
# Sauvegardez ce fichier sous : premier_contact.py
# Exécution : python premier_contact.py
# ============================================================
import ollama

def interroger_ia_locale(question: str, modele: str = "llama3.1:8b") -> str:
    """
    Envoie une question au LLM local via Ollama.
    Retourne la réponse complète sous forme de string.
    Zéro données envoyées à l'extérieur. Garanti.

    Paramètres :
      question (str) : le texte de votre question ou consigne
      modele (str)   : le nom du modèle Ollama à utiliser
                       Changez "llama3.1:8b" par "qwen2.5:7b" si vous
                       avez téléchargé ce modèle à la place.
    """
    reponse = ollama.chat(
        model=modele,
        messages=[
            {
                "role": "system",
                # ↓ PERSONNALISEZ CE PROMPT selon votre contexte métier
                "content": (
                    "Tu es un assistant d'entreprise expert. "
                    "Tu réponds en français, de manière concise et professionnelle. "
                    "Tu n'as accès à aucune ressource externe."
                ),
            },
            {
                "role": "user",
                "content": question,  # ← Votre question arrive ici
            },
        ],
    )
    # La réponse du modèle est dans cette clé du dictionnaire retourné
    return reponse["message"]["content"]


def interroger_avec_streaming(question: str, modele: str = "llama3.1:8b"):
    """
    Même logique, mais affiche la réponse mot par mot.
    Utilisez cette version pour les interactions en direct avec un humain.
    Le paramètre stream=True est la seule différence technique.
    """
    stream = ollama.chat(
        model=modele,
        messages=[{"role": "user", "content": question}],
        stream=True,  # ← Active le mode streaming
    )
    print("--- RÉPONSE IA LOCALE ---")
    for fragment in stream:
        # Chaque fragment contient un ou plusieurs mots.
        # end="" empêche le saut de ligne entre chaque fragment.
        # flush=True force l'affichage immédiat sans bufferisation.
        print(fragment["message"]["content"], end="", flush=True)
    print("\n--- FIN ---")


if __name__ == "__main__":
    # TEST 1 : Appel standard — résultat retourné en une fois
    resultat = interroger_ia_locale(
        "Quels sont les risques RGPD d'utiliser ChatGPT avec des données clients ?"
    )
    print(resultat)

    # TEST 2 : Streaming — résultat affiché mot par mot
    interroger_avec_streaming(
        "Rédige un email de refus de réunion poli mais ferme, en 5 lignes."
    )

Lancez python premier_contact.py. Si le terminal affiche une réponse cohérente en français, votre stack est opérationnelle. La première génération peut prendre 10-15 secondes le temps que le modèle se charge en RAM, ne vous inquiétez pas, les appels suivants sont bien plus rapides.

Étape 3: Chatbot d'entreprise avec mémoire de conversation

Un appel question-réponse isolé, c'est un outil. Par contre, un assistant qui se souvient que vous parliez du Contrat Dupont au tour précédent, qui comprend que "rends-le plus formel" se réfère à l'email qu'il vient de rédiger, c'est un collègue de combat.

La mémoire de conversation fonctionne par un principe simple : à chaque appel, on envoie au modèle l'intégralité de l'historique des échanges précédents. Le modèle n'a pas de mémoire native, c'est votre script qui la lui fournit à chaque fois. C'est ce qu'on appelle le context window.

Cette approche s'inscrit dans la logique de l'IA agentique que nous avons détaillée dans un guide précédent.

Voici les points clés de la classe ChatbotEntrepriseLocal avant de la lire :

  • SYSTEM_PROMPT : la personnalité permanente de votre assistant. Vous pouvez le modifier pour l'adapter à votre secteur, juridique, RH, commercial, technique. C'est la première chose à customiser.
  • max_historique : limite le nombre de tours de conversation conservés en mémoire. Par défaut 20 (10 échanges). Si vos conversations sont longues et que le modèle commence à "oublier" le début, augmentez cette valeur, mais sachez que cela ralentit les réponses car le contexte envoyé est plus grand.
  • exporter_conversation() : sauvegarde l'échange dans un fichier texte daté. C'est utile pour garder une trace des analyses ou décisions prises avec l'IA.
# ============================================================
# SCRIPT 02 — Chatbot d'Entreprise avec Historique
# Sauvegardez sous : chatbot_entreprise.py
# Dépendances : pip install ollama
# Exécution   : python chatbot_entreprise.py
# ============================================================
import ollama
from datetime import datetime
from typing import Optional


class ChatbotEntrepriseLocal:
    """
    Assistant IA local avec mémoire de conversation.
    Tourne entièrement hors ligne via Ollama.
    RGPD-safe par architecture.
    """

    # ↓ PERSONNALISEZ CE BLOC selon votre usage métier
    SYSTEM_PROMPT = """
    Tu es un assistant d'entreprise interne, expert en :
    - Rédaction de documents professionnels (emails, comptes-rendus, rapports)
    - Analyse de contrats et documents juridiques simples
    - Résumé de réunions et de notes
    - Aide à la prise de décision basée sur des données fournies

    Règles absolues :
    - Réponds toujours en français
    - Si tu n'as pas suffisamment d'informations, dis-le clairement
    - Ne fabrique jamais de données chiffrées ou de citations
    - Sois concis : maximum 200 mots sauf si l'utilisateur demande plus
    """

    def __init__(self, modele: str = "llama3.1:8b", max_historique: int = 20):
        # modele       : nom du modèle Ollama installé sur votre machine
        # max_historique : nb max de messages conservés (hors system prompt)
        #                  Augmentez si vos sessions sont longues.
        self.modele = modele
        self.max_historique = max_historique
        self.historique: list[dict] = []
        self._initialiser_systeme()

    def _initialiser_systeme(self):
        """Place le prompt système en tête de l'historique.
        C'est le seul message permanent — il n'est jamais purgé."""
        self.historique = [
            {"role": "system", "content": self.SYSTEM_PROMPT}
        ]

    def chat(self, message_utilisateur: str) -> str:
        """
        Méthode principale. Envoyez vos messages ici.
        L'historique est géré automatiquement.
        """
        # 1. Ajouter le message de l'utilisateur à l'historique
        self.historique.append({
            "role": "user",
            "content": message_utilisateur
        })

        # 2. Élagage de l'historique pour rester dans le context window
        #    On conserve toujours le system prompt (index 0) + les N derniers messages
        if len(self.historique) > self.max_historique + 1:
            self.historique = (
                [self.historique[0]]          # system prompt conservé
                + self.historique[-(self.max_historique):]  # N derniers messages
            )

        # 3. Appel au modèle local avec l'historique complet
        reponse = ollama.chat(
            model=self.modele,
            messages=self.historique,
        )

        contenu_reponse = reponse["message"]["content"]

        # 4. Sauvegarder la réponse dans l'historique pour les tours suivants
        self.historique.append({
            "role": "assistant",
            "content": contenu_reponse
        })

        return contenu_reponse

    def reinitialiser(self):
        """Efface toute la mémoire de conversation et repart de zéro.
        Appelez cette méthode entre deux sujets sans rapport."""
        self._initialiser_systeme()
        print("[CHATBOT] Mémoire effacée. Nouvelle session démarrée.")

    def exporter_conversation(self, chemin_fichier: Optional[str] = None) -> str:
        """
        Exporte la conversation en fichier texte lisible.
        Si chemin_fichier est None, un nom daté est généré automatiquement.
        Exemple : conversation_20260318_143022.txt
        """
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        chemin = chemin_fichier or f"conversation_{timestamp}.txt"

        with open(chemin, "w", encoding="utf-8") as f:
            for message in self.historique[1:]:  # [1:] exclut le system prompt
                role = "VOUS" if message["role"] == "user" else "IA"
                f.write(f"[{role}] {message['content']}\n\n")

        return chemin


# ============================================================
# Exemple d'utilisation — 3 tours de conversation enchaînés
# Remarquez que le 2e et 3e tour font référence au 1er
# sans répéter le contexte : la mémoire fait le travail.
# ============================================================
if __name__ == "__main__":
    assistant = ChatbotEntrepriseLocal(modele="llama3.1:8b")

    # Tour 1 : demande initiale
    print(assistant.chat(
        "Je dois rédiger un email pour refuser une réunion de 2h sur un sujet déjà traité."
    ))

    # Tour 2 : raffinement — le modèle se souvient du Tour 1
    print(assistant.chat("Rends-le plus formel, c'est pour mon directeur."))

    # Tour 3 : ajout — toujours dans le même contexte
    print(assistant.chat("Ajoute une proposition de point de 15 minutes en alternative."))

    # Exporter la conversation complète
    fichier = assistant.exporter_conversation()
    print(f"\nConversation exportée dans : {fichier}")

Vous pouvez réutiliser la classe ChatbotEntrepriseLocal dans n'importe quel script Python, une application Flask, ou même l'intégrer dans un bot Slack. Elle est délibérément isolée et sans dépendances externes — copiez-la telle quelle dans vos projets.

Étape 4: RAG Local : Interroger vos documents internes sans les exposer

Le Graal de l'IA d'entreprise : poser des questions à vos propres documents, contrats, notes de réunion, procédures internes, sans les importer sur un serveur externe. Ici, on va droit à l'opérationnel.

Le pipeline RAG fonctionne en trois temps que le script exécute pour vous :

  1. Indexation : vos documents sont découpés en petits blocs (chunks), chaque bloc est converti en vecteur numérique par nomic-embed-text (d'où l'importance de l'avoir téléchargé à l'étape 1), et ces vecteurs sont stockés dans une base ChromaDB sur votre disque local.
  2. Recherche : quand vous posez une question, elle est aussi convertie en vecteur. ChromaDB trouve alors les blocs de vos documents dont le vecteur est le plus proche mathématiquement, ce sont les passages pertinents.
  3. Génération : ces passages sont injectés dans le prompt envoyé à Llama 3. Le modèle répond en se basant uniquement sur ce contexte. Il n'y a pas d'hallucination sur des données qu'il ne possède pas.

Ce que vous devez préparer : un dossier ./documents_entreprise contenant vos fichiers .txt. Le paramètre chunk_size (défaut : 500 mots) détermine la granularité de l'indexation, vous pouvez le réduire si vous avez des documents très denses,  et l'augmenter pour des textes narratifs longs.

# ============================================================
# SCRIPT 03 — Pipeline RAG Local complet
# Sauvegardez sous : rag_local.py
# Dépendances : pip install ollama chromadb
# Exécution   : python rag_local.py
#
# STRUCTURE DE DOSSIER ATTENDUE :
# votre_projet/
# ├── rag_local.py          ← ce script
# ├── documents_entreprise/ ← vos fichiers .txt à indexer
# │   ├── contrat_dupont.txt
# │   ├── procedure_onboarding.txt
# │   └── notes_reunion_q1.txt
# └── .chromadb_local/      ← créé automatiquement par ChromaDB
# ============================================================
import ollama
import chromadb
from pathlib import Path


# ============================================================
# Configuration — adaptez ces valeurs à votre contexte
# ============================================================
MODELE_LLM = "llama3.1:8b"          # Modèle de génération de texte
MODELE_EMBEDDING = "nomic-embed-text" # Modèle de vectorisation (léger, 274 Mo)
DOSSIER_DOCUMENTS = "./documents_entreprise"  # Chemin de vos documents
NOM_COLLECTION = "base_connaissance_interne"  # Nom logique de votre base


class RAGLocalEntreprise:
    """
    Système RAG entièrement local.
    Documents indexés localement, requêtes traitées localement.
    Aucune donnée ne quitte la machine.
    """

    def __init__(self):
        # PersistentClient sauvegarde les vecteurs sur disque dans .chromadb_local/
        # La prochaine fois que vous lancez le script, les vecteurs sont déjà là.
        # Pas besoin de ré-indexer à chaque exécution.
        self.client_chroma = chromadb.PersistentClient(path="./.chromadb_local")
        self.collection = self.client_chroma.get_or_create_collection(
            name=NOM_COLLECTION,
            metadata={"hnsw:space": "cosine"}  # Similarité cosinus = standard pour le texte
        )
        print(f"[RAG] Collection '{NOM_COLLECTION}' chargée.")

    def _generer_embedding(self, texte: str) -> list[float]:
        """
        Convertit un texte en vecteur numérique via nomic-embed-text.
        Ce vecteur capture la "signification" sémantique du texte.
        N'appelez pas cette méthode directement — elle est utilisée en interne.
        """
        reponse = ollama.embeddings(
            model=MODELE_EMBEDDING,
            prompt=texte
        )
        return reponse["embedding"]

    def indexer_document(self, contenu: str, source: str, chunk_size: int = 500):
        """
        Découpe un document en chunks de chunk_size mots et les indexe.
        source : identifiant unique du document (ex: nom du fichier)

        Quand utiliser chunk_size plus petit ? Documents très techniques,
        contrats denses, procédures step-by-step → essayez 200-300 mots.
        Quand l'agrandir ? Textes narratifs, rapports longs → 700-1000 mots.
        """
        mots = contenu.split()
        chunks = [
            " ".join(mots[i:i + chunk_size])
            for i in range(0, len(mots), chunk_size)
        ]

        for idx, chunk in enumerate(chunks):
            embedding = self._generer_embedding(chunk)
            doc_id = f"{source}_chunk_{idx}"  # ID unique : nom_fichier + numéro de chunk

            # upsert = insert ou update si l'ID existe déjà
            # Pratique pour ré-indexer un document modifié sans dupliquer
            self.collection.upsert(
                ids=[doc_id],
                embeddings=[embedding],
                documents=[chunk],
                metadatas=[{"source": source, "chunk_index": idx}]
            )

        print(f"[RAG] '{source}' indexé : {len(chunks)} chunks.")

    def indexer_dossier(self, chemin_dossier: str = DOSSIER_DOCUMENTS):
        """
        Indexe automatiquement tous les fichiers .txt du dossier spécifié.
        Appelez cette méthode une fois lors de l'initialisation,
        ou chaque fois que vous ajoutez de nouveaux documents.
        """
        dossier = Path(chemin_dossier)
        fichiers = list(dossier.glob("*.txt"))

        if not fichiers:
            print(f"[RAG] Aucun fichier .txt trouvé dans {chemin_dossier}")
            return

        for fichier in fichiers:
            with open(fichier, "r", encoding="utf-8") as f:
                contenu = f.read()
            self.indexer_document(contenu, source=fichier.name)

        print(f"[RAG] Indexation terminée : {len(fichiers)} document(s) traité(s).")

    def interroger(self, question: str, n_resultats: int = 3) -> str:
        """
        Méthode principale — posez vos questions ici.

        n_resultats : nombre de chunks récupérés pour construire le contexte.
        Augmentez à 5-6 pour des questions complexes qui nécessitent
        plusieurs passages de document. Diminuez à 1-2 pour des
        questions très précises et factuelles.
        """
        # 1. Encoder la question avec le même modèle d'embedding
        embedding_question = self._generer_embedding(question)

        # 2. Trouver les n_resultats chunks les plus similaires sémantiquement
        resultats = self.collection.query(
            query_embeddings=[embedding_question],
            n_results=n_resultats,
            include=["documents", "metadatas"]
        )

        # Assembler les chunks en un seul bloc de contexte
        contexte = "\n\n---\n\n".join(resultats["documents"][0])
        sources = [m["source"] for m in resultats["metadatas"][0]]

        # 3. Générer la réponse en injectant le contexte dans le prompt
        # L'instruction "UNIQUEMENT sur le contexte fourni" est cruciale :
        # elle empêche le modèle d'utiliser ses connaissances générales
        # et de potentiellement halluciner des informations.
        prompt = f"""
        Réponds à la question suivante en te basant UNIQUEMENT sur le contexte fourni.
        Si la réponse n'est pas dans le contexte, dis clairement : "Cette information
        ne figure pas dans les documents fournis."

        CONTEXTE EXTRAIT DE VOS DOCUMENTS :
        {contexte}

        QUESTION : {question}

        RÉPONSE (en français, concise et sourcée) :
        """

        reponse = ollama.chat(
            model=MODELE_LLM,
            messages=[{"role": "user", "content": prompt}]
        )

        contenu = reponse["message"]["content"]
        sources_uniques = list(set(sources))

        # Affichage de la réponse + des fichiers sources utilisés
        return f"{contenu}\n\n[Sources utilisées : {', '.join(sources_uniques)}]"


# ============================================================
# Utilisation complète
# ============================================================
if __name__ == "__main__":
    rag = RAGLocalEntreprise()

    # PREMIÈRE UTILISATION : indexer vos documents (à faire une seule fois)
    # Créez le dossier et ajoutez vos fichiers .txt avant de lancer
    rag.indexer_dossier("./documents_entreprise")

    # UTILISATION COURANTE : poser des questions
    print(rag.interroger(
        "Quelles sont les clauses de résiliation du contrat fournisseur 2024 ?"
    ))

    print(rag.interroger(
        "Quelle est la procédure d'onboarding pour les nouveaux arrivants ?"
    ))

Note pratique : la première indexation d'un dossier volumineux peut prendre quelques minutes (le modèle d'embedding traite chaque chunk séquentiellement). Les exécutions suivantes sont quasi-instantanées car ChromaDB recharge les vecteurs depuis le disque sans les recalculer.

Étape 5: Automatiser vos tâches bureautiques en batch

Un LLM local, ce n'est pas juste un chatbot, c'est un moteur de traitement de texte intelligent que vous pouvez brancher sur n'importe quel workflow Python existant. Les mêmes scripts que vous utilisez pour contourner l'interface Jira peuvent maintenant être augmentés d'une couche d'intelligence locale.

Le script suivant regroupe trois fonctions indépendantes que vous pouvez utiliser séparément ou combiner:

  • resumer_document() : génère un résumé calibré selon le niveau de détail souhaité. Trois modes : "court" (3 phrases, pour une revue rapide), "moyen" (un paragraphe de 100 mots, pour partager en réunion), "detaille" (5 points structurés, pour archivage ou rapport). Passez le texte brut de n'importe quel email, compte-rendu ou document.
  • classifier_message() : analyse un email ou ticket et retourne un dictionnaire Python structuré avec la catégorie, la priorité, un résumé et une suggestion d'action. Le paramètre format="json" force Ollama à retourner uniquement du JSON valide, indispensable pour brancher la sortie sur d'autres scripts. Adaptez les valeurs de categorie à votre contexte (ex: ajoutez "technique", "legal", etc.)
  • traiter_dossier_en_batch() : orchestre les deux fonctions précédentes sur un dossier entier de fichiers .txt. Chaque fichier est résumé et classifié. Un rapport rapport_batch.json est généré à la fin. Idéal pour traiter une semaine d'emails archivés en une seule passe, un lundi matin, pendant la réunion hebdomadaire que vous avez déclinée grâce à notre script Google Calendar.
# ============================================================
# SCRIPT 04 — Automatiseur de Tâches Bureautiques
# Sauvegardez sous : automatiseur_bureau.py
# Dépendances : pip install ollama
# Exécution   : python automatiseur_bureau.py
#
# STRUCTURE DE DOSSIER ATTENDUE :
# votre_projet/
# ├── automatiseur_bureau.py
# ├── email_test.txt              ← un fichier de test
# ├── emails_a_traiter/           ← dossier pour le traitement batch
# │   ├── email_001.txt
# │   └── email_002.txt
# └── rapport_batch.json          ← généré automatiquement
# ============================================================
import ollama
import json
from pathlib import Path
from typing import Literal


MODELE = "llama3.1:8b"


def resumer_document(
    texte: str,
    longueur: Literal["court", "moyen", "detaille"] = "moyen"
) -> str:
    """
    Génère un résumé en français, calibré selon longueur.

    Paramètres :
      texte    (str)  : le texte brut à résumer (email, compte-rendu, etc.)
      longueur (str)  : "court" | "moyen" | "detaille"
                        Voir les définitions dans le dict instructions ci-dessous.

    Retourne : le résumé sous forme de string.
    """
    # Définition des niveaux de détail — modifiez ces instructions selon vos besoins
    instructions = {
        "court":    "en 3 phrases maximum",
        "moyen":    "en un paragraphe de 100 mots",
        "detaille": "en 5 points structurés avec un titre pour chaque point"
    }

    prompt = f"""
    Résume le document suivant {instructions[longueur]}.
    Réponds uniquement en français.
    Pas de préambule — commence directement le résumé.

    DOCUMENT :
    {texte[:4000]}
    """
    # Note : [:4000] limite le texte aux 4000 premiers caractères.
    # Augmentez si vos documents sont longs et que vous avez un modèle
    # avec un grand context window (ex: qwen2.5:7b supporte 128k tokens).

    reponse = ollama.chat(
        model=MODELE,
        messages=[{"role": "user", "content": prompt}]
    )
    return reponse["message"]["content"]


def classifier_message(contenu: str) -> dict:
    """
    Classifie un email ou ticket dans une catégorie.
    Retourne un dictionnaire Python avec 4 clés structurées.

    Retourne un dict avec ces clés :
      - categorie       : type de message
      - priorite        : niveau d'urgence
      - resume          : résumé en une phrase
      - action_suggeree : que faire avec ce message

    En cas d'échec du parsing JSON, retourne un dict avec une clé "erreur".
    """
    prompt = f"""
    Analyse ce message professionnel et retourne UNIQUEMENT un objet JSON valide.
    Aucun texte avant ou après le JSON. Aucun markdown. Juste le JSON.

    Les champs obligatoires du JSON :
    - "categorie"       : une valeur parmi ["urgent", "facturation", "support_technique",
                          "commercial", "rh", "autre"]
                          → Adaptez cette liste à votre contexte métier
    - "priorite"        : une valeur parmi ["haute", "normale", "basse"]
    - "resume"          : résumé du message en une phrase
    - "action_suggeree" : quelle action concrète réaliser

    MESSAGE À ANALYSER :
    {contenu}
    """

    reponse = ollama.chat(
        model=MODELE,
        messages=[{"role": "user", "content": prompt}],
        format="json"  # ← Force Ollama à retourner uniquement du JSON valide
    )

    try:
        return json.loads(reponse["message"]["content"])
    except json.JSONDecodeError:
        # En cas d'échec de parsing, on retourne l'erreur sans planter le script
        return {
            "erreur": "Classification échouée — JSON invalide",
            "contenu_brut": reponse["message"]["content"]
        }


def traiter_dossier_en_batch(dossier: str = "./emails_a_traiter") -> list[dict]:
    """
    Traite tous les fichiers .txt d'un dossier en mode batch :
    résumé court + classification pour chaque fichier.

    Résultat sauvegardé dans rapport_batch.json dans le dossier courant.
    Chaque entrée du JSON contient : fichier, resume, categorie,
    priorite, action_suggeree.

    Utilisation typique : pointez vers un dossier d'emails exportés
    depuis votre client mail (Outlook, Thunderbird) au format .txt.
    """
    resultats = []
    chemin = Path(dossier)
    fichiers = list(chemin.glob("*.txt"))

    if not fichiers:
        print(f"[BATCH] Aucun fichier .txt trouvé dans {dossier}")
        return []

    for fichier in fichiers:
        print(f"[BATCH] Traitement de {fichier.name}...")
        with open(fichier, "r", encoding="utf-8") as f:
            contenu = f.read()

        # Appel séquentiel des deux fonctions sur le même contenu
        classification = classifier_message(contenu)
        resume = resumer_document(contenu, longueur="court")

        # Fusionner les résultats en un seul dictionnaire par fichier
        resultat = {
            "fichier": fichier.name,
            "resume": resume,
            **classification  # Déplie les clés du dict classification
        }
        resultats.append(resultat)

    # Export du rapport complet au format JSON
    # ensure_ascii=False : conserve les caractères accentués français
    # indent=2 : format lisible par un humain
    with open("rapport_batch.json", "w", encoding="utf-8") as f:
        json.dump(resultats, f, ensure_ascii=False, indent=2)

    print(f"[BATCH] Terminé. {len(resultats)} fichiers traités.")
    print(f"[BATCH] Rapport disponible : rapport_batch.json")
    return resultats


# ============================================================
# Point d'entrée — testez chaque fonction séparément
# avant de lancer le batch complet
# ============================================================
if __name__ == "__main__":
    # TEST 1 : Résumé d'un fichier unique
    with open("email_test.txt", "r", encoding="utf-8") as f:
        texte = f.read()

    print("=== RÉSUMÉ COURT ===")
    print(resumer_document(texte, longueur="court"))

    print("\n=== CLASSIFICATION ===")
    classification = classifier_message(texte)
    print(json.dumps(classification, ensure_ascii=False, indent=2))

    # TEST 2 : Traitement en masse du dossier
    print("\n=== TRAITEMENT BATCH ===")
    traiter_dossier_en_batch("./emails_a_traiter")

Le fichier rapport_batch.json généré peut directement alimenter un dashboard, être importé dans Google Sheets, ou servir de base à un script de tri automatique de votre boîte mail.

C'est la beauté de travailler en JSON : chaque sortie devient l'entrée d'un autre outil.

Choisir le bon modèle Ollama pour votre machine

Tous les modèles ne naissent pas égaux, et votre hardware dicte vos choix. Voici la grille de sélection rapide :

Modèles recommandés selon votre profil

  • 16 Go RAM, pas de GPU → llama3.1:8b ou qwen2.5:7b : ~5-15 tokens/sec. Suffisant pour la rédaction, la classification, le résumé.
  • 16-32 Go RAM + GPU NVIDIA (8 Go VRAM) → mistral:7b ou gemma2:9b : ~30-50 tokens/sec. Fluide, réactif. Convient pour un chatbot interne en production légère.
  • 32 Go RAM + GPU haut de gamme → llama3.1:70b (quantisé Q4) : Proche des performances GPT-4 pour les tâches complexes. Pour les Mac Apple Silicon M2/M3 Pro ou les workstations bien équipées.
  • Embedding uniquement → nomic-embed-text : Indispensable pour le RAG. Léger (274 Mo), précis, zéro coût de génération.

Conclusion

Voilà l'arsenal au complet et documenté.

Ollama est installé, les modèles sont téléchargés et Python est branché sur le daemon local. Vous disposez maintenant d'un chatbot avec mémoire qui connaît vos procédures internes, une pipeline RAG qui interroge vos documents sans les exposer et un automatiseur qui traite vos emails en batch pendant que vous faites semblant d'écouter en réunion.

Tout ça tourne sur votre machine. Il n'y a aucun token envoyé à San Francisco ni abonnement mensuel ou note circulaire RGPD dans votre boîte mail.

Le cloud vous a vendu la commodité comme un service, mais il vous a surtout vendu la dépendance. Ollama + Python, c'est la différence entre louer une cellule confortable et posséder votre propre setup.

La prochaine étape logique ? Connecter cette IA locale à vos outils existants, automatiser les tâches répétitives qui mangent vos journées. C'est exactement ce que nous explorons dans notre guide sur l'IA Agentique.

> Base_de_Données_FAQ

Q: Ollama fonctionne-t-il sans connexion internet une fois le modèle téléchargé ?

A:
Oui, c'est précisément son avantage principal. La connexion internet n'est nécessaire qu'une seule fois : lors du téléchargement initial du modèle via ollama pull. Une fois les fichiers stockés sur votre disque, le daemon tourne entièrement hors ligne. Vos requêtes Python, vos pipelines RAG, vos traitements batch, tout s'exécute en local, sans aucun appel vers un serveur externe. C'est ce qui en fait une solution RGPD-compatible par architecture, et non par politique.

Q: Quelle est la différence entre Ollama et l'API OpenAI en termes de coût ?

A:
La différence est radicale : Ollama est gratuit après le téléchargement initial du modèle. L'API OpenAI facture à la consommation, environ 0,015€ par millier de tokens pour GPT-4o. Une équipe de 10 personnes utilisant intensément les API cloud peut dépenser 300 à 700€ par mois en frais d'API, sans compter les abonnements SaaS. Avec Ollama, le seul coût est l'électricité de votre machine et l'espace disque du modèle (4 à 5 Go pour un 7B). Pour les usages d'entreprise répétitifs, résumés, classifications, traitements batch, le retour sur investissement est immédiat.

Q: Mon ordinateur n'a pas de GPU dédié. Puis-je quand même utiliser Ollama efficacement ?

A:
Oui. Ollama fonctionne en mode CPU-only sur n'importe quelle machine. Avec 16 Go de RAM et un modèle 7B comme llama3.1:8b, vous obtiendrez une vitesse de génération de 5 à 15 tokens par seconde, soit environ une phrase toutes les 2-3 secondes. C'est suffisant pour la rédaction d'emails, la classification de documents, les résumés, et l'interrogation RAG. La première réponse peut sembler lente (10-20 secondes le temps que le modèle se charge en mémoire), mais les suivantes sont bien plus rapides. Pour les traitements batch automatisés, la vitesse de génération importe peu, le script tourne pendant que vous faites autre chose.

Q: Quelle est la différence entre le mode stream=True et le mode standard dans les appels Ollama ?

A:
Les deux modes produisent la même réponse finale, la différence est dans la façon dont elle est transmise à votre script. En mode standard (stream=False, le défaut), Ollama attend que le modèle ait généré l'intégralité de la réponse avant de vous la retourner d'un bloc. Votre script est bloqué pendant ce temps. En mode streaming (stream=True), la réponse arrive fragment par fragment dès que chaque token est généré, votre code la reçoit et peut l'afficher immédiatement. Choisissez le mode standard pour les scripts automatisés et les traitements batch où aucun humain ne regarde l'écran. Choisissez le streaming pour toute interface interactive où un utilisateur attend une réponse en direct, l'expérience est nettement plus fluide.

Q: Est-ce que je peux utiliser Ollama avec des documents PDF directement ?

A:
Pas nativement, Ollama travaille avec du texte brut. Pour utiliser des PDF dans votre pipeline RAG local, vous devez d'abord les convertir en texte via une librairie Python comme PyMuPDF (pip install pymupdf) ou pdfplumber (pip install pdfplumber). Une fois le texte extrait, vous l'injectez dans la méthode indexer_document() du script RAG présenté dans cet article. Le pipeline complet devient alors : PDF → extraction texte → découpage en chunks → vectorisation via nomic-embed-text → stockage ChromaDB → interrogation via Llama 3. Aucune des étapes ne nécessite de connexion internet.
#Ollama, #Python, #IA #Locale, #LLM, #RGPD, #RAG, #Automatisation, #Chatbot, #LangChain, #Privacy, #Open #Source, #Llama3

Rejoignez la Résistance.

Recevez le Pack de Survie Office + 1 astuce d'automatisation par semaine.

Données protégées. Zéro spam.

Jérémy

Jérémy

J'ai passé 6 ans dans le Support Technique à résoudre des cas complexes. Maintenant, je montre aux employés de bureau comment gagner du temps avec Python et Google Suite.

@Twitter

> cat comments.log (0)

Écrire dans la console :

> Aucune entrée trouvée. Soyez le premier à écrire dans le log.