Context Engineering — Section 7 : Caching & Long Contexte
Optimiser la vitesse et les coûts en réutilisant les calculs et en gérant efficacement la mémoire.
7. Caching & Gestion des Longs Contextes
- A. Panorama
- B. Cache Applicatif
- C. Cache d'Inférence (KV)
- D. Optimisations (PagedAttention)
- E. Gestion de Fenêtre
- F. Évaluation
- G. Pitfalls
- H. Snippets
- I. Playbook
Panorama : Le Coût de la Répétition
L'inférence des LLMs est un processus coûteux, dominé par deux phases : le **prefill** (traitement du prompt initial) et le **decode** (génération des tokens de réponse). Le prefill, surtout avec un long contexte, est lent. Le caching vise à stocker les résultats de ces calculs pour les réutiliser, que ce soit au niveau de la réponse complète ou au niveau des états internes du modèle.
- Niveau 2 : Inférence (KV Cache): Stocker l'état interne de l'attention du Transformer (les matrices Clé/Valeur) dans la VRAM du GPU pour accélérer la génération token par token. Essentiel pour la performance.
Niveau 1 : Le Cache Applicatif
Ce sont des stratégies de mise en cache que vous implémentez dans votre propre code, en amont de l'appel à l'API du LLM.
| Stratégie | Principe | Avantages | Inconvénients |
|---|---|---|---|
| Exact Match Cache | Clé = hash du prompt. Valeur = réponse du LLM. | Très rapide. Idéal pour les questions les plus fréquentes (FAQs). | Très rigide. Une virgule de différence = cache miss. |
| Cache Sémantique | Clé = embedding du prompt. On cherche si une question sémantiquement similaire (< 0.1 de distance cosinus) est déjà en cache. | Robuste aux reformulations. | Risque de faux positifs (questions proches mais avec une nuance critique). Nécessite une DB vectorielle. |
| Cache d'Artefacts | (Recommandé) On ne cache pas la réponse finale, mais le résultat d'une étape intermédiaire coûteuse (ex: le "paquet de contexte distillé"). | Le meilleur équilibre. Permet de réutiliser le contexte tout en laissant le LLM générer une réponse finale dynamique. | Nécessite une architecture modulaire (ex: un pipeline de distillation clair). |
Niveau 2 : Le Cache d'Inférence (KV Cache)
Le KV Cache est le mécanisme fondamental qui rend la génération de texte par les Transformers efficace. Sans lui, chaque nouveau token nécessiterait de recalculer l'attention sur l'intégralité du texte précédent, une opération quadratique (O(n²)) insoutenable.
- Il calcule les paires de vecteurs Clé (K) et Valeur (V) pour chaque token du prompt.
- Ces paires (le KV Cache) sont stockées en VRAM.
- Cette phase est limitée par la puissance de calcul (compute-bound) et est lente pour les longs prompts.
- Il compare son vecteur Query (Q) à son propre vecteur K et à **tous les K du cache** pour calculer les scores d'attention.
- Cette phase est limitée par la bande passante mémoire du GPU (memory-bound), mais bien plus rapide que le prefill.
Optimisations pour Gérer le KV Cache : PagedAttention
Avec de longs contextes (ex: 100k tokens) et de grands batchs, le KV Cache peut consommer des dizaines de Go de VRAM. Sa gestion devient un problème majeur, résolu par des techniques innovantes comme PagedAttention (popularisé par vLLM).
- Une "table des pages" mappe les tokens logiques aux pages physiques, qui peuvent être non-contiguës en mémoire.
- Résultat : Élimine quasi-totalement la fragmentation interne et externe. Permet d'utiliser la VRAM à >90% de son potentiel.
- C'est une forme de caching de prefill automatique et très efficace.
Gestion de Fenêtre pour Contexte "Infini"
Même avec 1M de tokens, il y aura toujours des cas d'usage nécessitant un contexte plus long (analyse de codebases, assistants sur le long terme). La gestion de fenêtre est une approche pour simuler un contexte infini.
- Sliding Window Attention : Une caractéristique architecturale (ex: Mistral 7B) où l'attention de chaque token est limitée aux `k` tokens précédents (ex: 4096). Permet des contextes théoriques longs avec un coût de calcul constant.
- StreamingLLM : Une technique d'inférence qui gère un cache glissant. Elle observe que les premiers tokens ("attention sinks") sont cruciaux pour la stabilité. Le cache garde donc les 4 premiers tokens + les `k` tokens récents, permettant des conversations de millions de tokens sans dégradation.
- Le test "Needle in a Haystack" : Un benchmark célèbre qui mesure la capacité d'un modèle à retrouver une information précise ("l'aiguille") noyée dans un long contexte ("la botte de foin"). Il montre que même les meilleurs modèles à long contexte ont des "angles morts" et que la performance diminue lorsque le contexte s'allonge.
Évaluation des Performances
Les métriques pour le caching et les systèmes d'inférence sont centrées sur la vitesse et le débit.
| Métrique | Description | Ce qu'elle mesure |
|---|---|---|
| Cache Hit Rate (%) | Pourcentage de requêtes résolues par le cache applicatif. | L'efficacité de votre stratégie de cache applicatif. |
| Time To First Token (TTFT) | Temps entre l'envoi de la requête et la réception du premier token. | La performance de la phase de **prefill**. Une métrique cruciale pour l'expérience utilisateur en temps réel. |
| Time Per Output Token (TPOT) | Temps moyen pour générer chaque token après le premier. | La performance de la phase de **decode**. Doit être faible et stable. |
| Throughput (tokens/s) | Nombre total de tokens de sortie que le serveur d'inférence peut produire par seconde, toutes requêtes confondues. | La capacité globale de votre système à gérer la charge. |
Pièges & Erreurs à Éviter
C'est l'un des deux problèmes les plus difficiles en informatique. Pour un cache applicatif, si vous mettez en cache une réponse basée sur un document, que se passe-t-il si le document est mis à jour ? Votre cache servira une information obsolète. Une stratégie d'invalidation (ex: basée sur un hash du contenu ou un timestamp) est absolument essentielle.
- Cache sémantique trop laxiste : Un seuil de similarité trop élevé (> 0.2) peut retourner des réponses pour des questions qui ne sont que vaguement similaires.
- Ignorer le coût du cache : Le KV Cache consomme énormément de VRAM. Sans des systèmes comme PagedAttention, héberger un modèle à long contexte peut devenir prohibitif.
- Croire que "long contexte" remplace RAG : Même avec 1M de tokens, le RAG (Retrieve-Augment-Generate) est souvent plus performant et moins cher. Il effectue un premier filtrage pour ne donner au LLM que les informations les plus pertinentes, ce qui est une forme de compression contextuelle.
Snippet 1 : Cache Sémantique Simplifié
from sentence_transformers import SentenceTransformer, util
import numpy as np
class SemanticCache:
def __init__(self, threshold=0.9):
self.encoder = SentenceTransformer('all-MiniLM-L6-v2')
self.cached_embeddings = []
self.cached_responses = []
self.threshold = threshold
def get(self, query):
if not self.cached_responses:
return None
query_embedding = self.encoder.encode(query, convert_to_tensor=True)
# Compare against all cached embeddings
cos_scores = util.cos_sim(query_embedding, self.cached_embeddings)[0]
best_match_idx = np.argmax(cos_scores)
if cos_scores[best_match_idx] > self.threshold:
print(f"Cache hit! Similarity: {cos_scores[best_match_idx]:.2f}")
return self.cached_responses[best_match_idx]
return None
def put(self, query, response):
query_embedding = self.encoder.encode(query, convert_to_tensor=True)
self.cached_embeddings.append(query_embedding)
self.cached_responses.append(response)
# Utilisation
# cache = SemanticCache()
# cache.put("What is the capital of France?", "The capital of France is Paris.")
# response = cache.get("Tell me the capital city of France") # -> Cache hit!
Snippet 2 : Démarrer un serveur vLLM (Concept)
# vLLM active PagedAttention par défaut.
# Ce simple script Python lance un serveur d'inférence OpenAI-compatible
# pour le modèle Mistral 7B, prêt à gérer de larges batchs efficacement.
from vllm.engine.arg_utils import AsyncEngineArgs
from vllm.engine.async_llm_engine import AsyncLLMEngine
# Configuration du moteur
engine_args = AsyncEngineArgs(
model='mistralai/Mistral-7B-Instruct-v0.1',
# max_num_seqs: nombre de requêtes simultanées que le moteur peut gérer
max_num_seqs=256,
# max_model_len: contexte maximal supporté
max_model_len=8192,
# gpu_memory_utilization: % de VRAM à allouer au KV Cache
gpu_memory_utilization=0.90
)
engine = AsyncLLMEngine.from_engine_args(engine_args)
# Le moteur peut maintenant être intégré dans un serveur web (ex: FastAPI)
# pour servir des requêtes d'inférence à haute performance.
Playbook de Déploiement
- Étape 1 : Implémenter le Cache d'Artefacts. C'est le plus rentable. Si vous avez un pipeline RAG, mettez en cache le résultat de l'étape de distillation/compression. La clé de cache doit être un hash de `(user_query, sorted(list_of_doc_ids))`.
- Étape 2 : Ajouter un Cache Sémantique pour les requêtes sans RAG. Pour les cas d'usage conversationnels ou Q&A simples, un cache sémantique peut grandement réduire le nombre d'appels LLM pour les questions récurrentes. Fixez un seuil de similarité strict (ex: 0.95) pour commencer.
- Étape 3 (Auto-hébergement) : Utiliser un Serveur d'Inférence moderne. N'essayez pas de réinventer la roue. Pour l'hébergement de modèles open-source, utilisez des serveurs comme **vLLM** ou **Text Generation Inference (TGI)**. Ils implémentent PagedAttention et d'autres optimisations de pointe "out-of-the-box".
- Étape 4 : Benchmarker votre système. Mesurez le TTFT et le TPOT de votre serveur d'inférence avec différentes longueurs de prompt et tailles de batch. Cela vous donnera une compréhension claire des limites de performance de votre infrastructure.
- Étape 5 : Ne pas négliger la gestion de fenêtre. Pour les conversations très longues, ne vous fiez pas uniquement à une fenêtre de contexte native. Implémentez une stratégie de gestion de mémoire de session (ex: résumé + fenêtre glissante) au niveau applicatif pour garantir la robustesse.
