Traitement par lots, routage des modèles et tableaux de bord des coûts
Acheminez les requêtes simples vers des modèles moins coûteux comme GPT-4o-mini et les requêtes complexes vers GPT-4o, regroupez les requêtes non urgentes et créez un tableau de bord des coûts qui suit les dépenses par fonctionnalité.
Traitement par lots, routage des modèles et tableaux de bord des coûts est une leçon AI Engineering Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage AI Engineering Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours AI Engineering Academy comprend 4 leçons au total.
Trois leviers supplémentaires pour optimiser les coûts
Après la mise en cache, trois stratégies supplémentaires réduisent considérablement les coûts d’exploitation des LLM : le traitement par lots (différer les requêtes non urgentes et les envoyer en une seule fois à un tarif d’API inférieur), le routage des modèles (diriger les requêtes simples vers des modèles peu coûteux et les requêtes complexes vers des modèles puissants) et les tableaux de bord des coûts (suivre les dépenses par fonctionnalité afin d’identifier les domaines où les optimisations ont le meilleur ROI). Ensemble, ces stratégies peuvent réduire les coûts de 40 à 60 % supplémentaires au-delà des économies réalisées grâce à la mise en cache.
API Batch d’OpenAI : 50 % de réduction pour les traitements asynchrones
L’API Batch d’OpenAI accepte un fichier JSONL contenant jusqu’à 50 000 requêtes et les traite de manière asynchrone dans un délai de 24 heures, à 50 % du prix standard. Elle convient parfaitement aux traitements non interactifs : générer les représentations vectorielles de grands corpus de documents, produire des descriptions de produits, exécuter des évaluations nocturnes ou prétraiter des données d’entraînement. Le compromis concerne la latence : les résultats sont disponibles plusieurs heures plus tard, et non immédiatement.
import json
from openai import OpenAI
client = OpenAI()
# Prepare batch file
requests = [
{
'custom_id': f'req_{i}',
'method': 'POST',
'url': '/v1/chat/completions',
'body': {
'model': 'gpt-4o-mini',
'messages': [
{'role': 'user', 'content': f'Summarize: {document}'}
],
'max_tokens': 150,
}
}
for i, document in enumerate(documents_to_process)
]
# Write JSONL batch file
with open('/tmp/batch_requests.jsonl', 'w') as f:
for req in requests:
f.write(json.dumps(req) + '\n')
# Upload and submit batch
with open('/tmp/batch_requests.jsonl', 'rb') as f:
batch_file = client.files.create(file=f, purpose='batch')
batch = client.batches.create(
input_file_id=batch_file.id,
endpoint='/v1/chat/completions',
completion_window='24h',
)
print(f'Batch {batch.id} submitted, status: {batch.status}')Interroger les résultats d’un traitement par lots
Après avoir envoyé un lot, interrogez son état jusqu’à sa fin (l’état passe de in_progress à completed). Une fois le traitement terminé, téléchargez le fichier de sortie contenant les résultats de toutes les requêtes. Chaque ligne de sortie est un objet JSON contenant le custom_id de la requête ainsi qu’un champ response ou error ; gérez toujours les deux cas, car certaines requêtes d’un même lot peuvent échouer indépendamment.
import time
def wait_for_batch(batch_id: str, poll_interval: int = 60) -> str:
while True:
batch = client.batches.retrieve(batch_id)
print(f'Status: {batch.status}, completed: {batch.request_counts.completed}')
if batch.status == 'completed':
return batch.output_file_id
elif batch.status == 'failed':
raise RuntimeError(f'Batch failed: {batch.errors}')
time.sleep(poll_interval)
def download_batch_results(output_file_id: str) -> list[dict]:
content = client.files.content(output_file_id)
results = []
for line in content.text.strip().split('\n'):
results.append(json.loads(line))
return results
output_file_id = wait_for_batch(batch.id)
results = download_batch_results(output_file_id)
for result in results[:3]:
print(result['custom_id'], result.get('response', {}).get('body', {}).get('choices', [{}])[0])Routage des modèles : adapter la complexité à la taille du modèle
Le routage des modèles attribue chaque requête au modèle le moins coûteux capable de la traiter correctement. GPT-4o-mini coûte environ 30 fois moins cher que GPT-4o, tout en étant aussi performant pour les tâches simples de classification, d’extraction et de questions-réponses courtes. Acheminez les tâches simples et structurées vers de petits modèles peu coûteux, et le raisonnement complexe, la synthèse de longs contextes et la génération nuancée vers de grands modèles puissants. Même le routage de 60 % du trafic vers un modèle peu coûteux permet de réaliser des économies importantes.
CHEAP_MODEL = 'gpt-4o-mini'
POWERFUL_MODEL = 'gpt-4o'
def classify_query_complexity(query: str) -> str:
# Heuristic-based routing (replace with ML classifier for production)
words = query.split()
has_code = any(c in query for c in ['```', 'def ', 'class ', 'SELECT ', 'function '])
is_multi_step = any(w in query.lower() for w in ['compare', 'analyze', 'explain why', 'evaluate'])
is_long = len(words) > 50
if has_code or is_multi_step or is_long:
return POWERFUL_MODEL
return CHEAP_MODEL
def routed_completion(messages: list[dict]) -> str:
user_query = messages[-1].get('content', '')
model = classify_query_complexity(user_query)
print(f'Routing to: {model}')
response = client.chat.completions.create(model=model, messages=messages)
return response.choices[0].message.contentRoutage fondé sur un LLM pour une meilleure précision
Le routage heuristique est rapide, mais fragile. Une approche plus précise consiste à utiliser un petit modèle de classification peu coûteux pour décider vers quel modèle acheminer la requête. Effectuez un réglage fin d’un petit modèle à partir d’exemples de requêtes simples et complexes dans votre domaine, ou utilisez un amorçage à partir de quelques exemples avec GPT-4o-mini lui-même. L’appel au classificateur coûte quelques centaines de jetons d’entrée, soit bien moins qu’un routage incorrect d’une requête complexe vers un modèle peu coûteux qui produirait une mauvaise réponse.
CLASSIFIER_SYSTEM = '''You are a query complexity classifier.
Classify the user query as SIMPLE or COMPLEX.
SIMPLE: factual lookup, extraction, classification with clear answer.
COMPLEX: multi-step reasoning, synthesis, comparison, code generation, long-form writing.
Reply with just SIMPLE or COMPLEX.'''
def llm_classify_complexity(query: str) -> str:
response = client.chat.completions.create(
model='gpt-4o-mini', # use cheap model for routing
messages=[
{'role': 'system', 'content': CLASSIFIER_SYSTEM},
{'role': 'user', 'content': query},
],
max_tokens=10,
temperature=0,
)
label = response.choices[0].message.content.strip()
return POWERFUL_MODEL if label == 'COMPLEX' else CHEAP_MODELSuivi du coût par fonctionnalité
Pour savoir où concentrer vos efforts d’optimisation, vous devez suivre le coût par fonctionnalité de l’application, et pas seulement les dépenses totales. Entourez chaque appel à un LLM d’une étiquette de fonctionnalité et cumulez les coûts en jetons par étiquette. « search_summarization » pourrait consommer 40 % de votre budget tout en ne servant que 5 % du trafic, ce qui en ferait une cible d’optimisation prioritaire. « user_onboarding » pourrait être coûteux, mais servir un parcours à forte valeur que vous ne souhaitez pas dégrader.
from collections import defaultdict
cost_tracker = defaultdict(lambda: {'prompt_tokens': 0, 'completion_tokens': 0, 'cost_usd': 0.0})
MODEL_PRICING = {
'gpt-4o-mini': {'input': 0.15 / 1e6, 'output': 0.60 / 1e6},
'gpt-4o': {'input': 2.50 / 1e6, 'output': 10.00 / 1e6},
}
def tracked_completion(feature: str, messages: list[dict], model: str = 'gpt-4o-mini') -> str:
response = client.chat.completions.create(model=model, messages=messages)
usage = response.usage
pricing = MODEL_PRICING.get(model, {'input': 0, 'output': 0})
cost = usage.prompt_tokens * pricing['input'] + usage.completion_tokens * pricing['output']
cost_tracker[feature]['prompt_tokens'] += usage.prompt_tokens
cost_tracker[feature]['completion_tokens'] += usage.completion_tokens
cost_tracker[feature]['cost_usd'] += cost
return response.choices[0].message.content
def print_cost_report():
print(f'{"Feature":<30} {"Prompt":<10} {"Completion":<12} {"Cost USD":<12}')
for feature, stats in sorted(cost_tracker.items(), key=lambda x: -x[1]['cost_usd']):
print(f'{feature:<30} {stats["prompt_tokens"]:<10} {stats["completion_tokens"]:<12} ${stats["cost_usd"]:.4f}')Créer un tableau de bord des coûts simple
Un tableau de bord pratique des coûts agrège les données de dépenses au niveau des fonctionnalités et les expose via un point de terminaison HTTP simple. Stockez les coûts cumulés dans Redis à l’aide de clés de regroupement quotidiennes afin de suivre l’évolution des dépenses dans le temps. Ajoutez ce tableau de bord à vos outils internes pour développeurs afin que l’équipe puisse voir presque en temps réel l’impact financier des mises en production de fonctionnalités et détecter les dépenses incontrôlées avant qu’elles ne deviennent une facture élevée.
from fastapi import FastAPI
import datetime
app = FastAPI()
async def record_cost(feature: str, model: str, prompt_tokens: int, completion_tokens: int):
pricing = MODEL_PRICING.get(model, {'input': 0, 'output': 0})
cost = prompt_tokens * pricing['input'] + completion_tokens * pricing['output']
today = datetime.date.today().isoformat()
key = f'cost:{today}:{feature}:{model}'
await async_r.incrbyfloat(key, cost)
await async_r.expire(key, 86400 * 30) # keep 30 days
@app.get('/dashboard/costs')
async def cost_dashboard():
today = datetime.date.today().isoformat()
pattern = f'cost:{today}:*'
costs = {}
async for key in async_r.scan_iter(match=pattern):
value = await async_r.get(key)
parts = key.split(':')
feature_model = ':'.join(parts[2:])
costs[feature_model] = float(value or 0)
return {'date': today, 'costs': costs, 'total': sum(costs.values())}Alertes budgétaires mensuelles
Configurez des alertes budgétaires mensuelles pour détecter les hausses de coûts inattendues avant qu’elles ne se transforment en factures élevées. Calculez les dépenses quotidiennes glissantes à partir de votre outil de suivi des coûts, projetez-les jusqu’à la fin du mois et déclenchez une alerte Slack lorsque la projection dépasse le seuil budgétaire. Une projection simple — daily_spend * days_remaining — détecte rapidement les requêtes incontrôlées, même lorsque les tendances réelles ne sont pas linéaires.
import datetime
import httpx
SLACK_WEBHOOK = 'https://hooks.slack.com/services/YOUR/WEBHOOK'
MONTHLY_BUDGET_USD = 500.0
async def check_budget_alert():
today = datetime.date.today()
days_in_month = 30
day_of_month = today.day
days_remaining = days_in_month - day_of_month
# Sum today's costs
today_total = sum(cost_tracker[f]['cost_usd'] for f in cost_tracker)
avg_daily = today_total # simplified: just today's spend
projected_month = avg_daily * days_in_month
if projected_month > MONTHLY_BUDGET_USD:
message = (
f'LLM Budget Alert: Projected monthly spend ${projected_month:.2f} '
f'exceeds budget ${MONTHLY_BUDGET_USD:.2f}. '
f'Today spend: ${today_total:.2f}'
)
async with httpx.AsyncClient() as client:
await client.post(SLACK_WEBHOOK, json={'text': message})File d’attente des requêtes pour gérer les limites de débit
Lorsque le trafic augmente fortement, les requêtes atteignent les limites de débit d’OpenAI et échouent avec 429 Too Many Requests. Une file d’attente des requêtes met en mémoire tampon les requêtes entrantes et les soumet à un débit contrôlé, ce qui lisse les pics de trafic. En production, utilisez une file d’attente asynchrone reposant sur Redis ou sur un courtier de messages comme RabbitMQ, et implémentez une logique de nouvelle tentative avec temporisation exponentielle pour les erreurs 429 temporaires.
import asyncio
from asyncio import Queue
class RateLimitedLLMClient:
def __init__(self, requests_per_minute: int = 500):
self.rpm = requests_per_minute
self.queue: Queue = Queue(maxsize=1000)
self.interval = 60.0 / requests_per_minute
async def start(self):
asyncio.create_task(self._worker())
async def _worker(self):
while True:
request_fn, future = await self.queue.get()
try:
result = await request_fn()
future.set_result(result)
except Exception as e:
future.set_exception(e)
await asyncio.sleep(self.interval)
async def submit(self, request_fn) -> str:
loop = asyncio.get_event_loop()
future = loop.create_future()
await self.queue.put((request_fn, future))
return await futureTout mettre en place : pile d’optimisation des coûts
Une pile complète d’optimisation des coûts des LLM fonctionne par couches : le cache exact élimine les appels pour les requêtes identiques répétées, le cache sémantique élimine les appels pour les requêtes similaires, la mise en cache des préfixes réduit le coût d’entrée de tous les appels restants, le routage des modèles utilise des modèles peu coûteux pour les requêtes simples, le traitement par lots reporte les tâches non urgentes pour bénéficier d’une remise de 50 %, et les tableaux de bord et alertes rendent les coûts visibles et les maintiennent sous contrôle. Mettez ces mécanismes en œuvre progressivement, selon leur impact sur votre application.
# Decision framework for cost optimization priority:
#
# 1. Enable prefix caching (free, zero effort, automatic)
# 2. Add exact caching (high hit rate for FAQ/support bots)
# 3. Add model routing (simple heuristics first, ML classifier later)
# 4. Add semantic caching (complex, high ROI for paraphrase-heavy use cases)
# 5. Enable batch API (only for non-real-time pipelines)
# 6. Build cost dashboard (essential for ongoing monitoring)
#
# Typical combined result in a customer support bot:
# Before: $1,000/month
# After step 1-2: $400/month (-60%)
# After step 3-4: $200/month (-50% of remaining)
# After step 5-6: $150/month and visibleRepli en cascade en cas d’échec du modèle peu coûteux
Lorsque vous acheminez une requête vers un modèle peu coûteux, vous devez gérer les cas où celui-ci produit une réponse insatisfaisante. Implémentez un contrôle de qualité de la sortie du modèle peu coûteux : vérifiez la longueur de la réponse, la présence des champs requis ou exécutez rapidement une évaluation par un LLM jouant le rôle de juge, puis revenez automatiquement au modèle puissant si la qualité est insuffisante. Ce filet de sécurité vous permet d’acheminer davantage de requêtes vers des modèles peu coûteux sans risquer de dégrader l’expérience utilisateur.
async def routing_with_fallback(messages: list[dict], min_length: int = 50) -> str:
# Try cheap model first
cheap_response = await async_client.chat.completions.create(
model=CHEAP_MODEL, messages=messages, temperature=0.0
)
answer = cheap_response.choices[0].message.content
# Quality check: response too short indicates poor answer
if len(answer.strip()) < min_length:
print(f'Cheap model answer too short ({len(answer)} chars), escalating...')
powerful_response = await async_client.chat.completions.create(
model=POWERFUL_MODEL, messages=messages, temperature=0.0
)
return powerful_response.choices[0].message.content
return answerVérification rapide
Vérifiez votre compréhension du traitement par lots, du routage des modèles et des tableaux de bord des coûts présentés dans cette leçon.
Récapitulatif de la leçon
Dans cette leçon, vous avez appris que l’API Batch d’OpenAI offre une remise de 50 % pour les charges de travail asynchrones qui ne sont pas en temps réel, que le routage des modèles utilise des modèles peu coûteux comme GPT-4o-mini pour les tâches simples et des modèles coûteux pour les tâches complexes, et que le suivi des coûts par fonctionnalité révèle quelles parties de votre application consomment le plus de budget afin de vous aider à hiérarchiser efficacement les optimisations. Associées aux stratégies de mise en cache des leçons précédentes, ces techniques peuvent réduire de 60 à 80 % les coûts d’infrastructure des LLM. Vous avez maintenant terminé le cours sur la mise en cache des LLM et l’optimisation des coûts.
Apprends Python avec un tuteur IA — gratuit
Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.
- Cours
- 30
- Leçons
- 120
Questions Fréquemment Posées
La leçon « Traitement par lots, routage des modèles et tableaux de bord des coûts » est-elle gratuite ?
Oui — le texte complet de « Traitement par lots, routage des modèles et tableaux de bord des coûts » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours AI Engineering Academy, passe à CoddyKit PRO. Le cours AI Engineering Academy comprend 4 leçons au total.
Qu'est-ce que j'apprendrai dans « Traitement par lots, routage des modèles et tableaux de bord des coûts » ?
Acheminez les requêtes simples vers des modèles moins coûteux comme GPT-4o-mini et les requêtes complexes vers GPT-4o, regroupez les requêtes non urgentes et créez un tableau de bord des coûts qui su… Tu pratiques AI Engineering Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.
Dois-je avoir de l'expérience pour commencer AI Engineering Academy ?
Aucune expérience préalable n'est requise. AI Engineering Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.
Combien de temps prend la leçon « Traitement par lots, routage des modèles et tableaux de bord des coûts » ?
La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.
Peux-tu écrire et exécuter du code dans cette leçon AI Engineering Academy ?
Oui. Chaque leçon AI Engineering Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.
Toutes les leçons de ce cours
- Mise en cache exacte avec Redis
- Mise en cache sémantique avec des plongements
- Mise en cache des préfixes d’invites d’OpenAI
- Traitement par lots, routage des modèles et tableaux de bord des coûts