
Vous savez déjà utiliser Ollama en ligne de commande et avec LiteLLM. Mais saviez-vous qu’Ollama dispose d’un SDK Python natif avec des fonctionnalités exclusives ? Ce guide vous montre comment exploiter les Structured Outputs (sorties JSON typées), les embeddings pour la recherche sémantique, et comment construire un assistant DevOps complet en local.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- SDK natif vs LiteLLM : quand utiliser chaque approche
- Streaming : afficher les réponses en temps réel
- Structured Output : obtenir du JSON typé avec Pydantic
- Embeddings : générer des vecteurs pour la recherche sémantique
- Conversation multi-turn : maintenir le contexte entre les échanges
- TP fil rouge : construire DevOps Buddy, un assistant DevOps local
Prérequis
Section intitulée « Prérequis »Avant de commencer, assurez-vous d’avoir :
- Ollama installé (guide d’installation)
- Un modèle téléchargé :
ollama pull llama3.2:3b- Le SDK Python :
pip install ollama pydanticVérifiez les versions :
ollama --version # 0.16.x ou supérieurpip show ollama # 0.6.x ou supérieurSDK natif vs LiteLLM : quelle différence ?
Section intitulée « SDK natif vs LiteLLM : quelle différence ? »| Aspect | SDK ollama | LiteLLM |
|---|---|---|
| Installation | pip install ollama | pip install litellm |
| Scope | Ollama uniquement | 100+ providers (OpenAI, Anthropic…) |
| Structured Output | ✅ Natif avec format= | Via function calling |
| Embeddings | ✅ embed() intégré | ✅ embedding() |
| Pensée (Thinking) | ✅ think=True | ❌ |
| Cas d’usage | Scripts dédiés Ollama | Code portable multi-providers |
Recommandation : Utilisez le SDK natif si vous ciblez uniquement Ollama et voulez exploiter ses fonctionnalités spécifiques. Utilisez LiteLLM si vous voulez pouvoir basculer vers d’autres providers.
TP fil rouge : DevOps Buddy
Section intitulée « TP fil rouge : DevOps Buddy »Tout au long de ce guide, nous construisons DevOps Buddy, un assistant DevOps local qui :
- Répond aux questions DevOps en streaming
- Utilise une base de connaissances avec embeddings (mini-RAG)
- Retourne des commandes structurées avec Pydantic
Créez le répertoire de travail :
mkdir -p ~/lab-ollama && cd ~/lab-ollamapython3 -m venv .venv && source .venv/bin/activatepip install ollama pydanticChat basique avec le SDK natif
Section intitulée « Chat basique avec le SDK natif »La fonction chat() du SDK est l’équivalent de litellm.completion(), mais
avec une API légèrement différente.
Script 01 : Premier appel
Section intitulée « Script 01 : Premier appel »#!/usr/bin/env python3"""TP 01 : Appel basique avec le SDK Ollama natifObjectif : Comprendre la différence avec litellm"""
from ollama import chat
# Appel simple avec le SDK natifresponse = chat( model='llama3.2:3b', messages=[ {'role': 'user', 'content': 'Explique Docker en une phrase pour un débutant.'} ])
print("=== Réponse du modèle ===")print(response.message.content)
print("\n=== Métadonnées ===")print(f"Modèle utilisé : {response.model}")print(f"Tokens prompt : {response.prompt_eval_count}")print(f"Tokens réponse : {response.eval_count}")print(f"Durée totale : {response.total_duration / 1e9:.2f}s")Exécutez le script :
python 01_chat_basique.pySortie attendue :
=== Réponse du modèle ===Docker est une technologie qui permet de créer des conteneurs virtuels, qui sontdes environnements isolés et sécurisés où les applications peuvent être exécutéessans nécessiter d'installations supplémentaires sur l'hôte.
=== Métadonnées ===Modèle utilisé : llama3.2:3bTokens prompt : 36Tokens réponse : 54Durée totale : 5.88sStreaming : réponses en temps réel
Section intitulée « Streaming : réponses en temps réel »Le streaming affiche la réponse mot par mot au fur et à mesure de la génération. C’est essentiel pour une bonne expérience utilisateur.
Script 02 : Streaming
Section intitulée « Script 02 : Streaming »#!/usr/bin/env python3"""TP 02 : Streaming avec le SDK OllamaObjectif : Afficher la réponse en temps réel, mot par mot"""
from ollama import chat
# Streaming : la réponse arrive progressivementprint("=== Réponse en streaming ===")stream = chat( model='llama3.2:3b', messages=[ {'role': 'user', 'content': 'Donne 5 bonnes pratiques Dockerfile, très bref.'} ], stream=True # Active le streaming)
# Afficher chaque chunk au fur et à mesurefor chunk in stream: print(chunk.message.content, end='', flush=True)
print("\n\n=== Fin du streaming ===")Le paramètre flush=True force l’affichage immédiat de chaque caractère.
Structured Output avec Pydantic
Section intitulée « Structured Output avec Pydantic »C’est la fonctionnalité la plus puissante du SDK Ollama. Au lieu d’obtenir du texte libre, vous obtenez du JSON typé qui correspond exactement à un schéma Pydantic.
Pourquoi c’est important ?
Section intitulée « Pourquoi c’est important ? »- Parsing fiable : plus besoin de regex ou de parsing manuel
- Validation automatique : Pydantic vérifie que la réponse est conforme
- Typage : votre IDE connaît les types des champs
- Intégration : les données sont directement utilisables dans votre code
Script 03 : Structured Output basique
Section intitulée « Script 03 : Structured Output basique »#!/usr/bin/env python3"""TP 03 : Structured Output avec PydanticObjectif : Obtenir des réponses JSON typées et validées automatiquementFeature : Disponible depuis Ollama 0.5.0+"""
from ollama import chatfrom pydantic import BaseModel
# Définir le schéma de sortie avec Pydanticclass DockerCommand(BaseModel): """Une commande Docker expliquée""" command: str # La commande complète description: str # Explication en une phrase example: str # Un exemple concret level: str # "debutant", "intermediaire", "avance"
# Appeler le modèle avec le schémaresponse = chat( model='llama3.2:3b', messages=[ { 'role': 'user', 'content': 'Explique la commande docker build' } ], format=DockerCommand.model_json_schema() # Forcer le format JSON)
# Valider et parser la réponse avec Pydanticresult = DockerCommand.model_validate_json(response.message.content)
# Afficher le résultat structuréprint("=== Sortie structurée (Pydantic) ===")print(f"Commande : {result.command}")print(f"Description : {result.description}")print(f"Exemple : {result.example}")print(f"Niveau : {result.level}")
print("\n=== JSON brut ===")print(response.message.content)Sortie attendue :
=== Sortie structurée (Pydantic) ===Commande : docker buildDescription : Crée une image Docker à partir d'un fichier Dockerfile.Exemple : docker build -t mon-image .Niveau : debutant
=== JSON brut ==={"command": "docker build", "description": "Crée une image Docker...", ...}Script 04 : Listes et objets imbriqués
Section intitulée « Script 04 : Listes et objets imbriqués »Pour des structures plus complexes avec des listes d’objets :
#!/usr/bin/env python3"""TP 04 : Structured Output avancé (listes et objets imbriqués)Objectif : Extraire plusieurs éléments structurés en une seule requête"""
from ollama import chatfrom pydantic import BaseModel
class Step(BaseModel): """Une étape d'un processus""" number: int action: str command: str
class Tutorial(BaseModel): """Un tutoriel complet""" title: str steps: list[Step] total_time: str
print("🔧 Définition du schéma Pydantic...")
print("\n📤 Envoi de la requête à Ollama (llama3.2:3b)...")
# Demander un tutoriel structuréresponse = chat( model='llama3.2:3b', messages=[ { 'role': 'user', 'content': 'Donne un tutoriel Docker en 2 étapes avec titre, steps (number, action, command) et total_time' } ], format=Tutorial.model_json_schema(), options={'temperature': 0, 'num_predict': 500})
print("✅ Réponse reçue!")print(f" Durée: {response.total_duration / 1e9:.2f}s")
# Parser la réponsetutorial = Tutorial.model_validate_json(response.message.content)
# Afficher le tutoriel formatéprint(f"\n📚 {tutorial.title}")print(f"⏱️ {tutorial.total_time}")for step in tutorial.steps: print(f" {step.number}. {step.action} → {step.command}")Sortie attendue :
📚 Tutoriel Docker en 2 étapes⏱️ > 30 minutes 1. Créer un nouveau projet Docker → docker run -it ubuntu /bin/bash 2. Créer une image personnalisée → docker build -t mon-image .Conversation multi-turn avec historique
Section intitulée « Conversation multi-turn avec historique »Pour maintenir le contexte entre plusieurs échanges, vous devez gérer l’historique des messages.
Script 05 : Conversation
Section intitulée « Script 05 : Conversation »#!/usr/bin/env python3"""TP 05 : Conversation multi-turn avec historiqueObjectif : Maintenir le contexte entre plusieurs échanges"""
from ollama import chat
# Historique de la conversationmessages = []
# Contexte systèmesystem_message = { 'role': 'system', 'content': '''Tu es DevOps Buddy, un assistant spécialisé DevOps.Tu réponds de manière concise et technique.Tu donnes des exemples de commandes quand c'est pertinent.'''}messages.append(system_message)
print("🤖 DevOps Buddy initialisé")print("=" * 50)
def ask(question: str) -> str: """Pose une question et met à jour l'historique""" print(f"\n👤 {question}")
# Ajouter la question à l'historique messages.append({'role': 'user', 'content': question})
# Appeler le modèle avec tout l'historique response = chat( model='llama3.2:3b', messages=messages, options={'temperature': 0.3} )
# Ajouter la réponse à l'historique assistant_message = response.message.content messages.append({'role': 'assistant', 'content': assistant_message})
print(f"🤖 {assistant_message}") return assistant_message
# Simulation d'une conversationask("C'est quoi un Pod Kubernetes en une phrase ?")ask("Et un Deployment, c'est quoi par rapport à un Pod ?")ask("Donne-moi la commande pour créer un Deployment avec 3 réplicas")ask("Comment je scale à 5 réplicas maintenant ?")
print("\n" + "=" * 50)print(f"📊 Total messages dans l'historique : {len(messages)}")Le modèle se souvient du contexte : la 4e question “Comment je scale à 5 réplicas” fait référence au Deployment mentionné précédemment.
Embeddings pour la recherche sémantique
Section intitulée « Embeddings pour la recherche sémantique »Les embeddings transforment du texte en vecteurs numériques. Deux textes similaires auront des vecteurs proches. C’est la base du RAG.
Script 06 : Embeddings et similarité
Section intitulée « Script 06 : Embeddings et similarité »#!/usr/bin/env python3"""TP 06 : Embeddings avec OllamaObjectif : Générer des vecteurs pour la recherche sémantiqueFeature : Disponible depuis Ollama 0.3+"""
from ollama import embedimport math
def cosine_similarity(vec1: list[float], vec2: list[float]) -> float: """Calcule la similarité cosinus entre deux vecteurs""" dot_product = sum(a * b for a, b in zip(vec1, vec2)) norm1 = math.sqrt(sum(a ** 2 for a in vec1)) norm2 = math.sqrt(sum(b ** 2 for b in vec2)) return dot_product / (norm1 * norm2)
# Base de connaissances DevOpsdocuments = [ "Docker est une plateforme de conteneurisation qui permet de packager des applications", "Kubernetes orchestre les conteneurs à grande échelle dans un cluster", "Ansible automatise la configuration des serveurs et le déploiement", "Terraform gère l'infrastructure as code pour le cloud", "GitLab CI/CD permet l'intégration et le déploiement continus"]
print("📦 Génération des embeddings pour 5 documents...")
# Générer les embeddings pour chaque documentdoc_embeddings = []for i, doc in enumerate(documents): response = embed(model='llama3.2:3b', input=doc) doc_embeddings.append(response.embeddings[0]) print(f" ✅ Doc {i+1} : {len(response.embeddings[0])} dimensions")
print(f"\n📊 Dimension des vecteurs : {len(doc_embeddings[0])}")
# Question de testquery = "Comment déployer des conteneurs en production ?"print(f"\n🔍 Requête : {query}")
# Générer l'embedding de la requêtequery_response = embed(model='llama3.2:3b', input=query)query_embedding = query_response.embeddings[0]
# Calculer la similarité avec chaque documentprint("\n📈 Scores de similarité :")scores = []for i, doc_emb in enumerate(doc_embeddings): score = cosine_similarity(query_embedding, doc_emb) scores.append((score, documents[i])) print(f" {score:.4f} → {documents[i][:50]}...")
# Trier par score décroissantscores.sort(reverse=True)print(f"\n🏆 Document le plus pertinent :")print(f" Score: {scores[0][0]:.4f}")print(f" {scores[0][1]}")Sortie attendue :
📦 Génération des embeddings pour 5 documents... ✅ Doc 1 : 3072 dimensions ✅ Doc 2 : 3072 dimensions ...
📈 Scores de similarité : 0.3004 → Docker est une plateforme de conteneurisation... 0.3473 → Kubernetes orchestre les conteneurs à grande... ...
🏆 Document le plus pertinent : Score: 0.4581 Ansible automatise la configuration des serveurs et le déploiementDevOps Buddy : l’assistant complet
Section intitulée « DevOps Buddy : l’assistant complet »Combinons toutes les fonctionnalités dans un assistant DevOps interactif.
Script 07 : DevOps Buddy
Section intitulée « Script 07 : DevOps Buddy »#!/usr/bin/env python3"""TP 07 : DevOps Buddy - Assistant local completObjectif : Combiner toutes les fonctionnalités Ollama dans un assistant interactifFeatures utilisées : chat, streaming, structured output, embeddings"""
from ollama import chat, embedfrom pydantic import BaseModelimport math
# === Configuration ===MODEL = 'llama3.2:3b'SYSTEM_PROMPT = '''Tu es DevOps Buddy, un assistant expert DevOps/DevSecOps.Tu réponds en français de manière concise et technique.Tu donnes des exemples de commandes quand c'est pertinent.Tu structures tes réponses avec des listes à puces.'''
# === Base de connaissances embarquée ===KNOWLEDGE_BASE = [ {"topic": "docker", "content": "Docker utilise des conteneurs pour isoler les applications. Commandes: docker build, docker run, docker compose"}, {"topic": "kubernetes", "content": "Kubernetes orchestre les conteneurs. Concepts: Pod, Deployment, Service, Ingress. Commandes: kubectl get, kubectl apply"}, {"topic": "ansible", "content": "Ansible automatise la configuration. Concepts: playbook, inventory, role, task. Commande: ansible-playbook"}, {"topic": "terraform", "content": "Terraform gère l'infrastructure as code. Concepts: provider, resource, module, state. Commandes: terraform init, plan, apply"}, {"topic": "gitlab", "content": "GitLab CI/CD pipeline avec .gitlab-ci.yml. Concepts: stages, jobs, artifacts, cache, runners"}, {"topic": "prometheus", "content": "Prometheus collecte les métriques. Concepts: scraping, PromQL, alertmanager, exporters"},]
# Pré-calculer les embeddings de la base de connaissancesprint("🔄 Chargement de la base de connaissances...")knowledge_embeddings = []for item in KNOWLEDGE_BASE: response = embed(model=MODEL, input=item["content"]) knowledge_embeddings.append(response.embeddings[0])print(f"✅ {len(KNOWLEDGE_BASE)} documents indexés\n")
# === Fonctions utilitaires ===def cosine_similarity(vec1: list[float], vec2: list[float]) -> float: dot = sum(a * b for a, b in zip(vec1, vec2)) norm1 = math.sqrt(sum(a ** 2 for a in vec1)) norm2 = math.sqrt(sum(b ** 2 for b in vec2)) return dot / (norm1 * norm2) if norm1 and norm2 else 0
def find_relevant_context(query: str, top_k: int = 2) -> str: """Recherche les documents pertinents pour enrichir le contexte""" query_emb = embed(model=MODEL, input=query).embeddings[0]
scores = [] for i, doc_emb in enumerate(knowledge_embeddings): score = cosine_similarity(query_emb, doc_emb) scores.append((score, KNOWLEDGE_BASE[i]))
scores.sort(reverse=True) top_docs = scores[:top_k]
context = "\n".join([f"- {doc['content']}" for _, doc in top_docs]) return context
# === Schéma pour les commandes structurées ===class CommandSuggestion(BaseModel): command: str description: str warning: str | None = None
def get_structured_command(query: str) -> CommandSuggestion | None: """Obtenir une suggestion de commande structurée""" try: response = chat( model=MODEL, messages=[ {'role': 'system', 'content': 'Tu suggères des commandes DevOps. Réponds uniquement en JSON.'}, {'role': 'user', 'content': f'Suggère une commande pour: {query}'} ], format=CommandSuggestion.model_json_schema(), options={'temperature': 0, 'num_predict': 200} ) return CommandSuggestion.model_validate_json(response.message.content) except Exception as e: print(f"⚠️ Erreur structured output: {e}") return None
# === Chat avec RAG ===def chat_with_rag(question: str, history: list) -> str: """Répond à une question avec contexte enrichi""" # Trouver le contexte pertinent context = find_relevant_context(question)
# Construire le prompt enrichi augmented_question = f"""Contexte (base de connaissances):{context}
Question de l'utilisateur: {question}"""
# Ajouter au historique history.append({'role': 'user', 'content': augmented_question})
# Appeler le modèle avec streaming response_text = "" stream = chat( model=MODEL, messages=history, stream=True, options={'temperature': 0.3} )
for chunk in stream: content = chunk.message.content print(content, end='', flush=True) response_text += content
print() # Nouvelle ligne après le streaming
# Ajouter la réponse à l'historique history.append({'role': 'assistant', 'content': response_text})
return response_text
# === Main ===def main(): print("=" * 60) print("🤖 DevOps Buddy - Assistant DevOps Local (Ollama)") print("=" * 60) print(f"Modèle: {MODEL}") print("Commandes spéciales:") print(" /cmd <action> : Obtenir une commande structurée") print(" /quit : Quitter") print("-" * 60)
history = [{'role': 'system', 'content': SYSTEM_PROMPT}]
while True: try: user_input = input("\n👤 Vous: ").strip()
if not user_input: continue
if user_input.lower() == '/quit': print("👋 À bientôt!") break
if user_input.startswith('/cmd '): # Mode commande structurée query = user_input[5:] print("\n🔧 Recherche de commande...") result = get_structured_command(query) if result: print(f"\n📋 Commande: {result.command}") print(f"📝 Description: {result.description}") if result.warning: print(f"⚠️ Attention: {result.warning}") else: print("❌ Impossible de générer la commande") else: # Mode conversation avec RAG print("\n🤖 DevOps Buddy: ", end='') chat_with_rag(user_input, history)
except EOFError: print("\n👋 À bientôt!") break except KeyboardInterrupt: print("\n👋 Interruption - À bientôt!") break
if __name__ == "__main__": main()Utilisation de DevOps Buddy
Section intitulée « Utilisation de DevOps Buddy »python 07_devops_buddy.pyExemple de session :
🔄 Chargement de la base de connaissances...✅ 6 documents indexés
============================================================🤖 DevOps Buddy - Assistant DevOps Local (Ollama)============================================================Modèle: llama3.2:3bCommandes spéciales: /cmd <action> : Obtenir une commande structurée /quit : Quitter------------------------------------------------------------
👤 Vous: C'est quoi un Pod ?
🤖 DevOps Buddy: Un Pod est l'unité de base dans Kubernetes...
👤 Vous: /cmd lister les pods kubernetes
🔧 Recherche de commande...
📋 Commande: kubectl get pods📝 Description: Liste tous les pods dans le namespace courant⚠️ Attention: Ajoutez -A pour lister tous les namespacesÀ retenir
Section intitulée « À retenir »- SDK natif vs LiteLLM : Le SDK ollama offre des fonctionnalités exclusives (structured output, thinking) mais ne supporte que Ollama
format=: Passez un schéma Pydantic pour obtenir du JSON typéstream=True: Affiche la réponse en temps réelembed(): Génère des embeddings pour la recherche sémantique- Historique : Passez tous les messages pour maintenir le contexte
options: Contrôlez la température, le nombre de tokens, etc.- RAG local : Combinez embeddings + contexte pour enrichir les réponses
Checklist
Section intitulée « Checklist »Installation
Section intitulée « Installation »- SDK ollama installé (
pip show ollama) - Pydantic installé (
pip show pydantic) - Modèle téléchargé (
ollama list)
Scripts testés
Section intitulée « Scripts testés »-
01_chat_basique.py: réponse + métadonnées -
02_streaming.py: affichage temps réel -
03_structured_output.py: JSON validé par Pydantic -
04_structured_avance.py: listes d’objets -
05_conversation.py: historique multi-turn -
06_embeddings.py: similarité cosinus -
07_devops_buddy.py: assistant complet