IA - Intermédiaire : LLMs, embeddings, RAG et APIs
🎯 Objectifs
- Comprendre l'architecture Transformer et le mécanisme d'attention (intuition)
- Saisir ce que sont les embeddings et leur utilité
- Mettre en place un pipeline RAG (Retrieval-Augmented Generation)
- Appeler l'API OpenAI et Ollama (LLM local) en Python
- Distinguer fine-tuning, prompt engineering et RAG
📋 Prérequis
- IA - Débutant - bases des modèles d'IA
- Python : variables, fonctions, manipulation de listes et dictionnaires
- Notions de base en HTTP / APIs REST
🤔 Pourquoi aller plus loin ?
Le module débutant t'a donné l'intuition. Maintenant tu veux savoir : comment ça marche vraiment sous le capot ? Et surtout : comment intégrer un LLM dans mes propres applications ?
Dans ce module, on va ouvrir le capot. Pas pour faire de la recherche académique, mais pour que tu puisses :
- Comprendre pourquoi GPT-4 est "meilleur" que GPT-3 sur le raisonnement
- Choisir entre RAG et fine-tuning selon le cas d'usage
- Appeler une API d'IA depuis ton propre code Python
🏗️ L'architecture Transformer (intuition)
Le problème qu'elle résout
Avant les Transformers (2017), les modèles lisaient le texte séquentiellement - comme tu lis un livre. Pour comprendre le mot "banque" dans "il est allé à la banque", le modèle devait attendre d'avoir lu tous les mots précédents.
Le Transformer a révolutionné ça avec une idée simple : regarder tous les mots en même temps et calculer les relations entre eux.
Le mécanisme d'attention
L'attention est le coeur du Transformer. C'est l'algorithme qui permet au modèle de savoir que dans "Le chat mange la souris parce qu'elle avait faim", le "elle" se réfère au chat et pas à la souris.
"Le chat mange la souris parce qu'elle avait faim"
↑ ↑
|___________________________________|
forte attention : "elle" → "chat"💡 Analogie : imagine que tu surligneras en jaune les mots importants pour comprendre chaque mot. L'attention fait exactement ça, automatiquement, pour chaque mot du texte.
La pile de couches
Un modèle comme GPT-4 empile des centaines de couches d'attention. Chaque couche affine la compréhension :
- Couches basses : relations syntaxiques (sujet/verbe)
- Couches hautes : relations sémantiques (concepts abstraits)
Ce qui explique les différences de capacité entre les modèles : plus de couches + plus de paramètres = comprend des relations plus complexes.
📐 Les Embeddings
C'est quoi un embedding ?
Un embedding est une représentation vectorielle d'un texte. En clair : transformer du texte en une liste de nombres qui "capture" le sens.
"chat" → [0.21, -0.45, 0.87, 0.12, ...] # vecteur de 1536 dimensions
"chien" → [0.18, -0.41, 0.83, 0.15, ...] # vecteur proche du précédent
"voiture" → [0.85, 0.23, -0.31, 0.67, ...] # vecteur éloignéLes textes au sens proche ont des vecteurs proches dans cet espace mathématique. C'est ce qui permet de faire des recherches sémantiques.
Similarité cosinus
Pour mesurer si deux textes veulent dire la même chose, on calcule la similarité cosinus entre leurs vecteurs :
from openai import OpenAI
import numpy as np
client = OpenAI()
def get_embedding(text: str) -> list[float]:
response = client.embeddings.create(
model="text-embedding-3-small",
input=text
)
return response.data[0].embedding
def cosine_similarity(a: list[float], b: list[float]) -> float:
a, b = np.array(a), np.array(b)
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
emb1 = get_embedding("comment déployer avec Docker ?")
emb2 = get_embedding("utiliser Docker pour mettre en production")
emb3 = get_embedding("recette de tarte aux pommes")
print(cosine_similarity(emb1, emb2)) # → ~0.92 (très proches)
print(cosine_similarity(emb1, emb3)) # → ~0.21 (très différents)Pourquoi c'est utile ?
Les embeddings sont la brique de base de nombreux systèmes :
- Recherche sémantique : trouver des documents pertinents même avec des mots différents
- RAG (on en parle juste après)
- Classification de texte
- Détection de doublons
🔍 Le RAG (Retrieval-Augmented Generation)
Le problème
Un LLM a une date de coupure. Il ne sait pas ce qui est dans ta documentation interne, tes tickets Jira, ta base de connaissances. Si tu lui poses une question sur un document qu'il n'a pas vu, il invente.
La solution : RAG
Le RAG résout ce problème en 3 étapes :
1. INDEXATION (une fois)
Tes docs → découper en chunks → embeddings → stocker dans une base vectorielle
2. RÉCUPÉRATION (à chaque question)
Question utilisateur → embedding → chercher les chunks les plus proches
3. GÉNÉRATION
LLM reçoit : [question + chunks pertinents] → génère une réponse ancréeImplémentation minimale en Python
from openai import OpenAI
import numpy as np
client = OpenAI()
# --- PHASE 1 : Indexation ---
documents = [
"Pour déployer sur Kubernetes, utilise kubectl apply -f deployment.yaml",
"Pour voir les pods en cours : kubectl get pods -n mon-namespace",
"Un Deployment gère la réplication et le rolling update des pods",
"La recette de la tarte aux pommes nécessite 500g de farine",
]
def embed(text: str) -> np.ndarray:
resp = client.embeddings.create(model="text-embedding-3-small", input=text)
return np.array(resp.data[0].embedding)
# Créer l'index : liste de (texte, vecteur)
index = [(doc, embed(doc)) for doc in documents]
# --- PHASE 2 : Récupération ---
def retrieve(question: str, top_k: int = 2) -> list[str]:
q_emb = embed(question)
scores = [
(doc, float(np.dot(q_emb, emb) / (np.linalg.norm(q_emb) * np.linalg.norm(emb))))
for doc, emb in index
]
scores.sort(key=lambda x: x[1], reverse=True)
return [doc for doc, _ in scores[:top_k]]
# --- PHASE 3 : Génération augmentée ---
def rag_query(question: str) -> str:
contexte = "\n".join(retrieve(question))
prompt = f"""Tu es un assistant DevOps. Réponds uniquement en te basant sur le contexte fourni.
Si la réponse n'est pas dans le contexte, dis-le clairement.
Contexte :
{contexte}
Question : {question}"""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}]
)
return response.choices[0].message.content
print(rag_query("Comment voir mes pods Kubernetes ?"))
# → "Pour voir les pods en cours, utilise kubectl get pods -n mon-namespace"💡 En production, on utilise une vraie base vectorielle (Chroma, Pinecone, Weaviate, pgvector) plutôt qu'une liste Python. Mais le principe est exactement le même.
🔧 Appeler des APIs d'IA
OpenAI API
pip install openai
export OPENAI_API_KEY="sk-..."from openai import OpenAI
client = OpenAI()
# Appel simple
response = client.chat.completions.create(
model="gpt-4o-mini", # modèle moins coûteux
messages=[
{"role": "system", "content": "Tu es un expert DevOps."},
{"role": "user", "content": "Explique la différence entre un pod et un deployment Kubernetes."}
],
max_tokens=500,
temperature=0.7 # 0 = déterministe, 1 = créatif
)
print(response.choices[0].message.content)Paramètres importants
| Paramètre | Effet | Valeur recommandée |
|---|---|---|
temperature | Variété des réponses (0=prévisible, 1=créatif) | 0.2 pour du code, 0.7 pour du texte |
max_tokens | Limite de longueur de la réponse | Selon le cas d'usage |
model | Quel modèle utiliser | gpt-4o-mini pour tester, gpt-4o pour la prod |
stream | Recevoir la réponse token par token | True pour les UIs |
Ollama : LLM en local
Tu veux expérimenter sans payer d'API ? Ollama fait tourner des LLM open-source sur ta machine.
# Installation (Linux/macOS)
curl -fsSL https://ollama.ai/install.sh | sh
# Télécharger et lancer un modèle
ollama pull llama3.2
ollama run llama3.2# Ollama expose une API compatible OpenAI
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama" # obligatoire mais ignoré
)
response = client.chat.completions.create(
model="llama3.2",
messages=[{"role": "user", "content": "Explique les conteneurs Docker en 3 phrases."}]
)
print(response.choices[0].message.content)🆚 Fine-tuning vs RAG vs Prompt Engineering
La question que tout le monde se pose : quand utiliser quoi ?
| Approche | Principe | Avantages | Limites | Quand l'utiliser |
|---|---|---|---|---|
| Prompt Engineering | Bien formuler la question | Rapide, gratuit, aucun code | Limité au contexte de la fenêtre | En premier lieu, toujours |
| RAG | Injecter des docs au moment de la question | Connaissances à jour, traçable | Latence + coût d'embedding | Docs internes, FAQ, base de connaissances |
| Fine-tuning | Ré-entraîner le modèle sur tes données | Style/format très précis, rapide à l'inférence | Coûteux, données nécessaires, modèle figé | Ton spécifique, format de sortie très contraint |
💡 Règle pratique : essaie d'abord le prompt engineering. Si c'est insuffisant, ajoute du RAG. Le fine-tuning n'est justifié que dans des cas très spécifiques.
📊 Récapitulatif
| Concept | À retenir |
|---|---|
| Transformer | Architecture qui lit tous les mots en parallèle via l'attention |
| Attention | Mécanisme qui calcule les relations entre tous les mots d'un texte |
| Embedding | Représentation vectorielle d'un texte qui capture son sens |
| Similarité cosinus | Mesure à quel point deux vecteurs (donc deux textes) se ressemblent |
| RAG | Injecter des docs pertinents dans le prompt au moment de la question |
| Temperature | Contrôle la créativité du modèle (0 = prévisible, 1 = créatif) |
| Ollama | Faire tourner des LLM open-source en local, zéro coût |
🚀 Prochaines Étapes
- Module suivant : IA - Avancé - MLOps, déploiement de modèles en production, Docker pour l'IA, monitoring
- En pratique : installe Ollama, fais tourner
llama3.2en local et expérimente avec les paramètrestemperature