Una grande organizzazione IT aziendale ha cinque applicazioni di AI generativa in produzione, undici deployment di modelli sparsi su tre sottoscrizioni Azure, una fattura mensile intorno ai 38.000 euro, e nessun modo affidabile di dire quale delle cinque li abbia spesi. Non c'è nulla di rotto. Ogni app funziona, ogni team ha consegnato, ogni security review era passata a suo tempo. Quello che non esiste è un unico punto dove un token viene contato, un chiamante viene identificato o un modello viene ritirato, e quella assenza è ciò che questa serie in dieci parti costruisce: un gateway GenAI governato su Azure attraverso cui passa ogni applicazione, e attorno a cui non passa nulla.

L'azienda, e cosa sta davvero pagando

Chiamiamola per quello che è: un'organizzazione IT aziendale consolidata, circa 400 ingegneri distribuiti su una quarantina di team di prodotto, la maggior parte dei workload su Azure, un gruppo platform che già possiede le landing zone e i template CI. L'AI generativa è arrivata come arriva di solito, dai margini verso il centro. Un assistente per il customer service è uscito per primo perché un product owner aveva budget. Poi un summarizer per le IT operations, un code assistant interno, una ricerca di conoscenza retail sulla documentazione prodotto e uno strumento per i testi di marketing. Cinque team, cinque decisioni, cinque integrazioni dirette contro Azure OpenAI.

Ognuna di quelle decisioni era corretta presa da sola. Insieme producono una forma specifica e ormai familiare:

  • Undici deployment di modelli su tre sottoscrizioni. Due sono duplicati dello stesso modello nella stessa region, creati perché un team non riusciva a ottenere quota sulla risorsa che già aveva.
  • Circa 38.000 euro al mese di spesa sui modelli che il cost management riporta con granularità di sottoscrizione, il che significa che può dire al finance il totale e non può dire a nessun product owner il proprio numero.
  • Quattro delle cinque app tengono una chiave nelle application settings. Una la teneva in un repository finché un secret scanner non l'ha trovata. Nessuna app può essere revocata singolarmente senza che un ingegnere modifichi la configurazione di quell'app.
  • Un preavviso di ritiro modello a 60 giorni è arrivato in una email, e il gruppo platform ha passato due giorni a fare grep nei repository per capire quali applicazioni si sarebbero rotte, perché nulla registrava quale app chiamasse quale versione del modello.

Poi l'incidente che ha davvero forzato la decisione. Un job batch nello strumento dei testi di marketing ha consumato per una quarantina di minuti gran parte della quota tokens-per-minute su un deployment condiviso. L'assistente del customer service, che condivideva quel deployment, ha iniziato a restituire HTTP 429 a clienti veri. Nessuno dei due team ha fatto qualcosa di sbagliato. Non esisteva nel percorso alcun meccanismo in cui esprimere "un chiamante non deve esaurire la quota da cui dipende un altro chiamante", perché non esisteva alcun percorso condiviso.

Questo è l'argomento onesto a favore di un gateway. Non "il controllo centrale è buona architettura". È che quota, attribuzione dei costi, rotazione delle credenziali e ciclo di vita dei modelli sono tutte proprietà della chiamata, e se nessun componente vede ogni chiamata, nessuna di esse può essere imposta da nessuna parte se non per convenzione.

Quanto costa davvero il pattern per-app

L'integrazione diretta è genuinamente più economica il primo giorno. Un client SDK, una chiave, un endpoint, nessun platform team nel percorso critico. Il costo arriva più tardi e atterra da qualche parte diversa dal team che l'ha scelta, ed è esattamente per questo che continua a essere scelta.

I costi che si accumulano sono tre, e sono tutti lo stesso costo con vestiti diversi. Costo del cambiamento: un ritiro di modello, un cambio di prezzo, una migrazione di region o un'interruzione del provider va gestita una volta per applicazione, da cinque team con cinque backlog e cinque livelli di interesse diversi. Costo dell'attribuzione: la spesa è visibile per risorsa e invisibile per consumatore, quindi a nessuno può essere assegnato un budget, il che significa che a nessuno può essere assegnato un limite. Costo della governance: ogni nuovo controllo, il logging dei prompt, la redazione dei dati PII, un requisito di trasparenza dell'EU AI Act, va implementato cinque volte e verificato cinque volte, e lo sforzo di revisione scala con il numero di applicazioni invece che con il numero di controlli.

Un gateway non elimina quei costi. Li sposta in un unico posto dove un platform team può pagarli una volta sola. Questo è l'intero argomento, e vale la pena diffidare di qualsiasi versione più ricca.

Costruire contro comprare su Azure

Il team ha valutato tre forme, e la risposta non è quella che un ingegnere vorrebbe.

Azure API Management come AI gateway. API Management include un insieme di capacità esplicitamente pensate per il traffico dei language model, e la lista è più lunga di quanto la maggior parte delle persone immagini prima di leggerla. La policy llm-token-limit impone tokens-per-minute o una quota di token per ora, giorno, settimana, mese o anno contro qualsiasi chiave di conteggio, inclusa una subscription key, l'IP del chiamante o un'espressione di policy arbitraria, e può precalcolare i token del prompt sul gateway così che una richiesta oltre il limite non raggiunga mai il backend. Le policy llm-semantic-cache-store e llm-semantic-cache-lookup fanno caching per similarità vettoriale delle completion contro Azure Managed Redis o un'altra cache compatibile RediSearch. La policy llm-emit-token-metric emette conteggi di token verso Application Insights con dimensioni custom, che sono la materia prima per l'attribuzione per consumatore. I backend supportano load balancing round-robin, pesato, per priorità e session-aware, e un circuit breaker la cui durata di apertura rispetta l'header Retry-After del backend stesso, così un endpoint pay-as-you-go può assorbire l'overflow da un deployment Provisioned Throughput Unit saturo senza che nessuno scriva codice di retry.

La copertura dei provider qui conta ed è facile sbagliarla. API Management media API di language model conformi agli schemi OpenAI Chat Completions e Responses, alla Anthropic Messages API (nei tier v2) e alla Google Vertex AI API, verso modelli deployati in Microsoft Foundry o presso provider non Microsoft, incluso Amazon Bedrock. Una unified model API, in preview al momento della scrittura, espone diversi backend dietro un singolo endpoint compatibile OpenAI e traduce i formati, così un unico set di policy di governance si applica a tutti i provider. Anthropic Claude, Mistral e Llama sono raggiungibili attraverso questo layer invece di richiedere ciascuno un'integrazione separata.

Un proxy open source su AKS. LiteLLM e progetti simili offrono traduzione dei provider e routing in un container che controlli completamente, con un percorso più rapido verso la feature che vuoi e nessuna dipendenza da una preview. Erediti una rotazione on-call per un componente che ora sta davanti a ogni richiesta AI dell'azienda, più il lavoro su identità, rete e audit che API Management ha già certificato.

Un servizio FastAPI su misura. Massimo controllo, e l'unica opzione in cui ogni fallimento è tuo. Ragionevole per un componente con requisiti genuinamente insoliti, cosa che un reverse proxy che conta token non è.

La decisione che questa serie segue: API Management è il data plane, e le cose che API Management non fa vengono costruite come un piccolo control plane Python su AKS. È questa la separazione che vale la pena interiorizzare, perché è dove la maggior parte dei progetti gateway va storta. I team o provano a esprimere tutto come policy XML, il che produce un artefatto immantenibile che nessuno può testare, o instradano il traffico attraverso il proprio servizio e reimplementano male il rate limiting. Il gateway porta la richiesta. Il control plane porta lo stato: quali modelli esistono, chi può chiamarli, quanto costa una chiamata e cosa succede quando un modello viene ritirato.

Cosa costruisci comunque

Cinque cose, e ognuna è una parte successiva di questa serie.

  • Un catalogo dei modelli e il loro ciclo di vita. Quali nomi logici di modello esistono, quali deployment fisici li sostengono, quali sono deprecati e quando spariscono. Le applicazioni devono chiamare chat-default, mai il nome di un deployment, o l'email di ritiro a 60 giorni torna a essere un progetto di migrazione da 60 giorni.
  • Identità dei tenant e onboarding. Un team riceve una client credential, una quota, un allowlist di modelli e un centro di costo, provisionati come codice invece che come ticket.
  • Attribuzione dei costi e chargeback. Le metriche di token con una dimensione tenant sono solo l'input. Trasformarle in una cifra mensile per team che il finance accetti è una data pipeline con i propri test.
  • Cancelli di valutazione nella pipeline. Promuovere una nuova versione di modello è un deployment. Un deployment che cambia il comportamento del modello senza un risultato di eval allegato è un'interruzione in attesa che un cliente la trovi.
  • Lavoro asincrono e di lunga durata. La summarization batch dei ticket di supporto non appartiene allo stesso percorso sincrono di un cliente che aspetta il primo token. Quella separazione è una coda, non un'impostazione di timeout.

Architettura target

La piattaforma è deliberatamente noiosa e deliberatamente additiva. Se il control plane è giù, il data plane continua a servire traffico sull'ultima configurazione nota, perché un gateway che fallisce chiuso durante un'interruzione del control plane è un'interruzione a livello di intera azienda.

graph TD
  subgraph consumers["Consumer apps"]
    A1["customer-service assistant"]
    A2["IT-ops summarizer"]
    A3["code assistant"]
    A4["retail knowledge search"]
    A5["marketing copy tool"]
  end

  subgraph gw["Data plane: API Management"]
    POL["policies:
llm-token-limit, semantic cache,
emit-token-metric, JWT validate"] LB["backend pool:
priority + weighted, circuit breaker"] end subgraph cp["Control plane: Python on AKS"] CAT["model catalog"] TEN["tenant registry"] COST["cost attribution job"] end subgraph models["Model layer"] FDRY["Microsoft Foundry:
GPT deployments, PTU + PAYG"] ANTH["Anthropic Claude"] OSS["Mistral, Llama"] end ID["Microsoft Entra ID"] RED["Azure Managed Redis
semantic cache"] SRCH["Azure AI Search
retrieval"] MON["Azure Monitor +
Application Insights"] A1 --> POL A2 --> POL A3 --> POL A4 --> POL A5 --> POL ID -. "OAuth2 client credentials" .-> POL POL --> LB POL <--> RED LB --> FDRY LB --> ANTH LB --> OSS POL -- token metrics, prompt logs --> MON CAT -. "backend + policy config" .-> gw TEN -. "subscriptions, quotas" .-> gw MON --> COST A4 -. "grounding data" .-> SRCH

Due proprietà di quel diagramma sono portanti. Primo, ogni freccia verso il layer dei modelli origina dentro API Management, il che significa che le regole di rete sulle risorse Foundry negano tutto il resto: il gateway non è un suggerimento, è l'unica rotta con un percorso. Secondo, il control plane scrive configurazione nel gateway e legge telemetria da Azure Monitor, ma non sta mai nel percorso della richiesta. Può essere rideployato a mezzogiorno.

Lo stack, in un unico posto

Cosa toccano le dieci parti, così la forma della serie è visibile fin da qui. Infrastruttura come codice in Terraform, con il confronto con Bicep e ARM fatto onestamente nella Parte 2 invece che dato per scontato. Immagini Docker e AKS per il control plane. Azure DevOps per pipeline, versionamento e release management. Python ovunque viva la logica. Microsoft Entra ID con OAuth2 client credentials e API key dove un chiamante legacy non può fare di meglio. Azure AI Search e un vector database dedicato per il retrieval, confrontati invece che dichiarati. Azure Managed Redis per il semantic caching. Azure Monitor e Application Insights per la telemetria, accanto a osservabilità specifica per LLM in LangSmith e Openlayer, e Dataiku dove un team di data science già vive. LangChain per l'orchestrazione nell'unica parte in cui un framework si guadagna il posto e nelle parti in cui non se lo guadagna. Streaming su server-sent events, WebSockets dove una sessione è genuinamente bidirezionale, e una coda di messaggi per tutto ciò che non ha bisogno di una risposta adesso.

Le dieci parti, in ordine: le ragioni di un gateway; Terraform contro Bicep e ARM; l'astrazione dei provider e lo streaming; lavoro asincrono ed event-driven; identità, quota e chargeback; semantic caching; retrieval con Azure AI Search contro un vero vector database; orchestrazione sopra il gateway; CI/CD e ciclo di vita dei modelli in Azure DevOps; e osservabilità con governance dell'AI.

Come si presenta il successo

La parte finale torna a questi punti, che sono i quattro numeri dell'inventario del lavoro ripetitivo resi misurabili:

  • Ogni product owner può vedere la propria spesa di token per il mese corrente, entro un giorno da quando è stata sostenuta, senza chiedere al platform team.
  • Nessuna applicazione detiene una credenziale di modello a lunga vita. Revocare l'accesso di un team è un cambio di configurazione nel tenant registry, non una modifica alle app settings di quel team.
  • Un preavviso di ritiro modello produce una query, non un'indagine: il catalogo sa già quali tenant si sono risolti su quel deployment negli ultimi 30 giorni.
  • Un tenant non può esaurire la quota di un altro tenant, verificato riproducendo l'incidente 429 di cui sopra come load test contro il gateway.
  • La latenza mediana aggiunta dal gateway resta sotto i 60 millisecondi al 95esimo percentile per le chiamate non in cache, e gli hit della semantic cache tornano in una frazione del tempo di una chiamata al modello.

Modalità di fallimento da tenere d'occhio fin dal primo giorno

Tre, e vale la pena nominarle prima che qualsiasi Terraform venga scritto. Il gateway diventa un single point of failure nel momento in cui è l'unica rotta, che è esattamente lo scopo, quindi il suo obiettivo di disponibilità deve essere più alto di quello dell'applicazione più esigente dietro di esso, e il suo deployment deve essere ridondante tra zone fin dal primo apply invece che dopo il primo incidente. La proliferazione delle policy arriva in silenzio: la policy XML è codice, viene eseguita a ogni richiesta, ed è facile scriverla in un posto dove nessuno la revisiona. Appartiene al version control con il resto della piattaforma, applicata da pipeline, mai modificata nel portale. E il percorso di bypass vanifica tutto il resto, perché un singolo team che tiene una chiave diretta verso un endpoint Foundry rende approssimativi ogni numero di attribuzione e ogni garanzia di quota. Quella è una regola di rete e un alert, non un documento di policy.

Leggi questo dopo

Per il lato infrastruttura e piattaforma di come far girare tutto questo su scala, gli appunti tecnici sono su ercan.cloud, e l'hub è su ercanermis.com.

Riferimenti