Batching, routing dei modelli e dashboard dei costi
Indirizzi le richieste semplici verso modelli più economici come GPT-4o-mini e quelle complesse verso GPT-4o, raggruppi le richieste non urgenti in batch e crei una dashboard dei costi che monitori la spesa per funzionalità.
Batching, routing dei modelli e dashboard dei costi è una lezione AI Engineering Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Engineering Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Engineering Academy include 4 lezioni in totale.
Altri tre strumenti per ottimizzare i costi
Dopo il caching, tre strategie aggiuntive riducono drasticamente i costi operativi degli LLM: il batching (rinviare le richieste non urgenti e inviarle in blocco a una tariffa API inferiore), il model routing (indirizzare le query semplici verso modelli economici e quelle complesse verso modelli più potenti) e i dashboard dei costi (monitorare la spesa per funzionalità per individuare gli ambiti in cui le ottimizzazioni offrono il ROI più elevato). Insieme, queste strategie possono ridurre i costi di un ulteriore 40-60 percento oltre ai risparmi ottenuti con il caching.
OpenAI Batch API: 50% di sconto per i carichi di lavoro asincroni
La Batch API di OpenAI accetta un file JSONL contenente fino a 50.000 richieste ed esegue l'elaborazione in modo asincrono entro 24 ore al 50 percento del prezzo standard. È ideale per i carichi di lavoro non interattivi: creare gli embedding di grandi raccolte di documenti, generare descrizioni di prodotti, eseguire valutazioni notturne o pre-elaborare dati di addestramento. Il compromesso è la latenza: i risultati sono disponibili dopo alcune ore, non immediatamente.
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}')Eseguire il polling dei risultati del batch
Dopo aver inviato un batch, controllarne lo stato finché non viene completato (lo stato passa da in_progress a completed). Al termine, scaricare il file di output contenente i risultati di tutte le richieste. Ogni riga dell'output è un oggetto JSON con il campo custom_id della richiesta e un campo response oppure error: gestire sempre entrambi, poiché le singole richieste all'interno di un batch possono fallire indipendentemente.
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])Model routing: associare la complessità alle dimensioni del modello
Il routing dei modelli assegna ogni richiesta al modello meno costoso in grado di gestirla adeguatamente. GPT-4o-mini costa circa 30 volte meno di GPT-4o, ma gestisce altrettanto bene le attività semplici di classificazione, estrazione e domande e risposte brevi. Indirizzi le attività semplici e strutturate verso modelli piccoli ed economici e il ragionamento complesso, la sintesi di contesti estesi e la generazione con sfumature verso modelli grandi e potenti. Anche indirizzare il 60 percento del traffico verso un modello economico consente di risparmiare notevolmente.
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.contentRouting basato su LLM per una maggiore accuratezza
Il routing basato su euristiche è rapido, ma poco robusto. Un approccio più accurato utilizza un modello di classificazione piccolo ed economico per decidere a quale modello indirizzare la richiesta. Esegua il fine-tuning di un modello piccolo su esempi di query semplici e complesse nel proprio dominio oppure utilizzi il prompting few-shot con GPT-4o-mini stesso. La chiamata al classificatore costa poche centinaia di token di input, molto meno del costo di indirizzare per errore una query complessa a un modello economico che produce una risposta errata.
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_MODELMonitoraggio dei costi per funzionalità
Per sapere su cosa concentrare gli sforzi di ottimizzazione, deve monitorare il costo per funzionalità dell'applicazione, non solo la spesa totale. Racchiuda ogni chiamata LLM in un tag della funzionalità e accumuli i costi dei token per tag. 'search_summarization' potrebbe consumare il 40 percento del budget servendo solo il 5 percento del traffico, diventando così un obiettivo prioritario per l'ottimizzazione. 'user_onboarding' potrebbe essere costoso, ma servire un flusso di grande valore che non desidera compromettere.
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}')Creazione di una semplice dashboard dei costi
Una dashboard pratica dei costi aggrega i dati di spesa a livello di funzionalità e li espone tramite un semplice endpoint HTTP. Memorizzi i costi cumulativi in Redis utilizzando chiavi per i riepiloghi giornalieri, in modo da poter analizzare l'andamento della spesa nel tempo. Aggiunga questa dashboard agli strumenti interni per sviluppatori, così il team potrà vedere quasi in tempo reale l'impatto sui costi dei rilasci delle funzionalità e individuare una spesa fuori controllo prima che si trasformi in una fattura elevata.
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())}Avvisi sul budget mensile
Imposti avvisi sul budget mensile per individuare aumenti imprevisti dei costi prima che si trasformino in fatture elevate. Calcoli una spesa giornaliera progressiva a partire dal sistema di monitoraggio dei costi, la proietti fino alla fine del mese e invii un avviso su Slack quando la proiezione supera la soglia del budget. Una semplice proiezione — daily_spend * days_remaining — individua tempestivamente le richieste fuori controllo, anche se gli andamenti effettivi non sono lineari.
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})Coda delle richieste per la gestione dei limiti di frequenza
Quando il traffico aumenta improvvisamente, le richieste raggiungono i limiti di frequenza di OpenAI e falliscono con 429 Too Many Requests. Una coda delle richieste memorizza temporaneamente le richieste in arrivo e le invia a una velocità controllata, attenuando i picchi di traffico. In produzione, utilizzi una coda asincrona basata su Redis o su un message broker come RabbitMQ e implementi una logica di ritentativo con backoff esponenziale per gli errori 429 temporanei.
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 futureCombinare tutto: stack per l'ottimizzazione dei costi
Uno stack completo per l'ottimizzazione dei costi degli LLM opera su più livelli: la cache esatta elimina le chiamate per le query identiche ripetute, la cache semantica elimina le chiamate per le query simili, il caching del prefisso riduce il costo dell'input per tutte le chiamate rimanenti, il routing dei modelli utilizza modelli economici per le query semplici, il batching rimanda le attività non urgenti ottenendo uno sconto del 50 percento e dashboard e avvisi mantengono i costi visibili e sotto controllo. Li implementi gradualmente, in ordine di impatto per la propria applicazione.
# 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 visibleFallback a cascata in caso di errore del modello economico
Quando indirizza una richiesta a un modello economico, deve gestire i casi in cui questo produca una risposta insoddisfacente. Implementi un controllo della qualità dell'output del modello economico: verifichi la lunghezza della risposta e la presenza dei campi obbligatori oppure esegua rapidamente una valutazione LLM-as-judge, quindi passi automaticamente al modello potente se la qualità è insufficiente. Questa rete di sicurezza consente di indirizzare con decisione le richieste verso modelli economici senza rischiare di compromettere l'esperienza utente.
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 answerVerifica rapida
Verifichi la propria comprensione del batching, del routing dei modelli e delle dashboard dei costi presentati in questa lezione.
Riepilogo della lezione
In questa lezione ha imparato che: OpenAI Batch API offre uno sconto del 50 percento per i carichi di lavoro asincroni non in tempo reale, il routing dei modelli utilizza modelli economici come GPT-4o-mini per le attività semplici e modelli costosi per quelle complesse, mentre il monitoraggio dei costi per funzionalità mostra quali parti dell'applicazione consumano la maggior parte del budget, consentendo di stabilire efficacemente le priorità di ottimizzazione. Insieme alle strategie di caching delle lezioni precedenti, queste tecniche possono ridurre del 60-80 percento i costi dell'infrastruttura LLM. Ha completato il corso sul caching degli LLM e sull'ottimizzazione dei costi.
Impara Python con un tutor IA — gratis
Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.
- Corsi
- 30
- Lezioni
- 120
Domande Frequenti
La lezione «Batching, routing dei modelli e dashboard dei costi» è gratuita?
Sì — il testo completo di «Batching, routing dei modelli e dashboard dei costi» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Engineering Academy, passa a CoddyKit PRO. Il corso AI Engineering Academy include 4 lezioni in totale.
Cosa imparerò in «Batching, routing dei modelli e dashboard dei costi»?
Indirizzi le richieste semplici verso modelli più economici come GPT-4o-mini e quelle complesse verso GPT-4o, raggruppi le richieste non urgenti in batch e crei una dashboard dei costi che monitori l… Eserciti AI Engineering Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.
Ho bisogno di esperienza per iniziare AI Engineering Academy?
Non è richiesta alcuna esperienza precedente. AI Engineering Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.
Quanto tempo richiede la lezione «Batching, routing dei modelli e dashboard dei costi»?
La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.
Posso scrivere ed eseguire codice in questa lezione AI Engineering Academy?
Sì. Ogni lezione AI Engineering Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.
Tutte le lezioni di questo corso
- Caching esatto con Redis
- Caching semantico con gli embedding
- Caching dei prefissi dei prompt con OpenAI
- Batching, routing dei modelli e dashboard dei costi