AI proxy e gateway LLM: qual è la differenza?

13 min

Distinguere l'inoltro di rete, un API gateway e il routing dei modelli, così si sceglie il livello che corrisponde al lavoro.

Si dice «AI proxy» per tre scatole diverse: un forwarder di rete, un gateway API reverse e un router di modelli. Questa guida è per gli sviluppatori che devono scegliere un livello senza mescolare i lavori. Non vende indirizzi IP, non distribuisce chiavi e non pretende che un proxy cambi l'idoneità di account o di geografia.

01Che cosa di solito si intende per «AI proxy»

La frase non è uno standard. RFC 9110 (HTTP Semantics, giugno 2022, STD 97) divide già gli intermediari in un proxy, un gateway e un tunnel, e avverte che un processo può cambiare ruolo per richiesta. Nel copy di prodotto la stessa parola viene riusata per un'altra cosa ancora: un processo che accetta un body JSON in stile OpenAI e sceglie un modello.

Quei tre lavori sembrano simili in un diagramma perché ciascuno sta tra il suo client e qualcosa a monte. Falliscono in posti diversi. Un forward proxy può aprire un tunnel TCP e lasciarla comunque con un 401 del fornitore. Un gateway LLM può restituire HTTP 200 con un errore JSON su un modello sconosciuto mentre il percorso di rete era a posto. Se si debugga il livello sbagliato, si ruota il segreto sbagliato.

Un'esecuzione locale delimitata il 2026-10-15 ha reso concreto quello spacco. Un proxy HTTP CONNECT in loopback ha inoltrato il TLS a un'origine fixture. Un processo loopback distinto ha instradato fixture-alpha contro fixture-bravo dopo un controllo Bearer locale. Niente è uscito da 127.0.0.1. Niente ha chiamato un'API di modello a pagamento. Il JSON della fixture è etichettato CONTROLLED_FIXTURE_NOT_A_PROVIDER di proposito.

Quando un vendor chiama un prodotto «AI proxy», leggere quale di quei lavori implementa davvero. Non assumere che esistano retry, streaming o fallback perché la parola gateway compare in homepage.

02Forward proxy HTTP e SOCKS, compreso CONNECT

Un forward proxy è scelto dal client. RFC 9110 §3.7 lo chiama un agente di inoltro messaggi che riceve richieste per alcuni URI assoluti e cerca di soddisfarle attraverso HTTP. Per gli URL http ordinari il client invia l'URL completo al proxy. Per https il pattern usuale è diverso.

RFC 9110 §9.3.6 definisce CONNECT: il client chiede al proxy di costruire un tunnel verso host:port e, dopo qualsiasi 2xx, di limitarsi a un inoltro cieco finché il tunnel non si chiude. I tunnel sono di solito avvolti in TLS dopo di quello. Un CONNECT riuscito non è un successo applicativo. Significa solo che il tunnel esiste. Qualsiasi altra risposta significa che il tunnel non è stato formato.

SOCKS5 è più vecchio e sta più in basso. RFC 1928 (marzo 1996) lo descrive come uno shim tra applicazione e trasporto, convenzionalmente su TCP 1080. Dopo la negoziazione del metodo il client invia un comando; CONNECT è X'01'. SOCKS non analizza i metodi HTTP. Inoltra TCP (e opzionalmente UDP) a una destinazione che il client nomina.

Se la libreria HTTP parla HTTPS attraverso un proxy, di solito sta facendo CONNECT, poi TLS verso l'origine. Il proxy autentica il tunnel, spesso con Proxy-Authorization. Quell'header non è l'header Authorization: Bearer che l'API del modello si aspetta. Mescolarli è una fonte comune di «il proxy funziona in curl ma l'SDK dà ancora 401».

Se il lavoro è solo «questo processo deve uscire da un altro IP», partire dagli elenchi di proxy HTTP, proxy HTTPS e proxy SOCKS5. Quelle pagine confrontano le uscite. Non sono router di modelli.

03Reverse proxy e API gateway ordinari

Un reverse proxy, che RFC 9110 chiama anche gateway in §3.7, si presenta al client come l'origine e inoltra in ingresso a un altro server. Il client non l'ha scelto come URL di proxy. Il client ha solo aperto https://api.example.com.

La guida reverse-proxy di NGINX (consultata il 2026-10-15) descrive il ciclo meccanico: inviare la richiesta a un upstream proxy_pass, prendere la risposta, rimandarla indietro. Di default NGINX riscrive Host e Connection. Quel dettaglio conta per le API che agganciano i virtual host a Host. Un reverse proxy può terminare il TLS sul bordo pubblico e aprire una nuova connessione all'interno. La sessione TLS del client finisce allora al reverse proxy, non al vendor del modello.

Questo livello è lo strumento giusto per il routing di path (/v1/ contro /admin/), il load balancing, la sanificazione degli header, e l'esposizione di un hostname davanti a diversi servizi interni. Può non sapere che cosa è un nome di modello. Se inoltra solo /v1/chat/completions a un unico upstream, è comunque un API gateway nel senso HTTP, non un gateway LLM.

Non trattare «abbiamo messo NGINX davanti» come prova di retry, budget di token o fallback di modello. Sono funzioni extra che alcuni prodotti aggiungono. La pagina NGINX che abbiamo letto non le pretende.

04Gateway LLM: chiavi, limiti, routing e log

Un gateway LLM è un servizio applicativo. I client inviano un body di chat o embeddings a un URL. Il gateway controlla una chiave virtuale, può imporre budget, può scegliere un fornitore e un modello, può registrare i token, e solo allora parla a un vendor — o a diversi vendor.

La pagina AI Gateway di LiteLLM (HTTP 200 il 2026-10-15) descrive un server self-hosted che offre un endpoint compatibile OpenAI. I lavori documentati includono chiavi virtuali con modelli e rate limit per chiave, spend tracking, load balancing, fallback, retry, guardrail e logging. La stessa pagina dice: se un client può usare l'API OpenAI, può usare il gateway senza cambiare il codice client. È una pretesa di prodotto sulla forma della richiesta chat completions. Non è una prova che ogni endpoint, header ed errore OpenAI mappi 1:1. Non abbiamo installato né eseguito LiteLLM.

Cloudflare AI Gateway (dateModified della pagina 2026-04-20, recuperata il 2026-10-15) elenca analytics, logging, caching, rate limiting, retry delle richieste e fallback di modello come funzioni. La pagina dynamic-routing (aggiornata il 2026-10-02) è più precisa sulla compatibilità: le route dinamiche accettano solo la forma della richiesta chat completions di OpenAI; Anthropic Messages restituiscono HTTP 400. Quella frase è il motivo per cui «compatibile OpenAI» deve restare stretto. Non abbiamo chiamato Cloudflare e non abbiamo eseguito retry o fallback. Se un documento di gateway elenca quelle funzioni, trattarle come solo-fonte finché non le si esegue contro quel prodotto.

In locale abbiamo costruito un router minuscolo che non è quei prodotti. Accettava Authorization: Bearer local-demo-key-not-a-provider-secret, mappava fixture-alpha e fixture-bravo su due payload fixture, restituiva 401 senza un token locale valido, e restituiva 400 per un modello sconosciuto. Basta a mostrare che il piano di controllo sta nell'HTTP JSON, non in CONNECT.

05Seguire una richiesta: TLS, Host, e chi può leggere il body

Seguire un POST /v1/chat/completions attraverso entrambi gli schemi.

Percorso forward usato nell'esecuzione loopback: il client ha aperto TCP verso 127.0.0.1:54318, ha inviato CONNECT 127.0.0.1:54316 HTTP/1.1, ha ricevuto 200 Connection Established, poi ha completato il TLS verso il certificato dell'origine. Solo dopo il TLS ha inviato il body JSON e un header Bearer destinato all'origine. Il processo CONNECT ha registrato la destinazione 127.0.0.1:54316. Non ha visto il JSON della fixture né il token Bearer: l'elenco di frammenti di body HTTP osservati sul tunnel era vuoto. Il TLS è finito all'origine fixture. L'origine ha risposto HTTP 200 con la fixture controllata.

Percorso gateway: il client ha aperto TCP verso 127.0.0.1:54317 senza CONNECT. Il TLS non è stato usato su quell'hop nella demo (HTTP in chiaro su loopback). Il gateway ha letto il campo JSON model e il token Bearer locale prima che esistesse un'origine per quella richiesta. Auth locale mancante o sbagliata non ha mai raggiunto una fixture di modello come route riuscita; entrambe hanno prodotto HTTP 401 con invalid_local_gateway_auth. Un modello sconosciuto ha prodotto HTTP 400 con unsupported_model_or_route dopo che l'auth era riuscita. Il trasporto era su. L'applicazione ha rifiutato il lavoro.

In HTTPS di produzione, un gateway LLM termina quasi sempre il TLS. Deve, se sta per analizzare JSON, applicare un budget, o scegliere un modello. Un forward proxy CONNECT che non è un middlebox TLS non termina quel TLS di origine. Se qualcuno abilita l'ispezione HTTPS su un proxy aziendale, è un deployment diverso: il proxy diventa un client TLS verso l'origine e un server TLS verso di lei. Non assumere l'ispezione solo perché CONNECT è riuscito.

La panoramica API di OpenAI (recuperata il 2026-10-15; il richiesto platform.openai.com/docs/api-reference ha reindirizzato qui) documenta le credenziali Bearer sulla richiesta di origine. Tratta x-request-id come identificatore di correlazione della richiesta, distinto dagli header di rate-limit come x-ratelimit-limit-requests. Quei campi appartengono alla conversazione del fornitore. Un gateway può aggiungere i propri request ID. Un forward proxy di solito no.

06La compatibilità è una forma client, non l'intera API

Gli SDK spesso lasciano impostare base_url e continuare a usare chat.completions.create. I vendor lo chiamano compatibile OpenAI. La nota dynamic-routing di Cloudflare è la cautela: anche dentro un solo vendor, un percorso di compatibilità può accettare una sola forma di richiesta e dare 400 a un'altra.

Un endpoint compatibile può comunque differire in autenticazione (chiave virtuale contro chiave del fornitore), nomi di modello (gpt-4.1 contro azure/gpt-4.1 contro un alias di gateway), layout degli eventi di streaming, campi tool-call, upload di file, socket realtime e API di amministrazione. La panoramica di OpenAI stessa tratta Responses, Realtime e Administration come superfici distinte. Copiare /v1/chat/completions non copia quell'intero albero.

Il gateway loopback ha restituito JSON che sembra una chat completion (choices[0].message). Era una fixture. Non rende la demo «compatibile OpenAI» in nessun senso di prodotto.

07Tre tipi diversi di «ha funzionato»

Tenere questi tre esiti distinti quando si leggono i log.

LivelloEsempio di successoEsempio di fallimentoChe cosa non dimostra
Trasporto / tunnelCONNECT 200 Connection Established; risposta SOCKS CONNECTProxy 403, timeout, errore di handshake TLSIl modello ha accettato il prompt o la chiave
HTTP verso l'hop successivo200 o anche 401/400 con un body JSONRisposta vuota, RST, HTTP 502 da un reverse proxyIl JSON è un risultato di modello
Applicazione / route di modelloIl gateway mappa fixture-alpha sulla fixture alpha; il fornitore restituisce una completionGateway 400 modello non supportato; fornitore 401 API key non valida; rifiuto di safety del modello dentro HTTP 200Gli altri livelli sono sani, o che un secondo vendor si comporterebbe allo stesso modo

Il 2026-10-15 lo script locale ha registrato sei casi e sedici asserzioni, tutte passate, con Python 3.9.6 e LibreSSL 3.3.6:

Tabella dei risultati in inglese dalla fixture loopback: inoltro CONNECT contro routing di modello del gateway.

L'inoltro ha prodotto HTTP 200 dell'origine. Il routing del gateway ha prodotto due route fixture diverse. I fallimenti di auth sono restati al gateway con 401. Il modello non supportato è restato al gateway con 400. Retry, streaming e fallback non sono stati eseguiti e non sono pretesi.

La pagina Il mio IP riporta l'indirizzo che ha raggiunto quella pagina dal suo browser. Il controllo proxy è una sonda lato server di un endpoint che lei fornisce. Nessuno dei due mostra il percorso che prenderà un processo SDK Python, e nessuno dei due stabilisce l'idoneità all'API LLM. Il controllo non dimostra che un vendor di modelli accetterà un IP, che una chiave è valida, o che le è permesso usare un dato account.

08Scegliere il livello che corrisponde al lavoro

Usare un forward proxy HTTP CONNECT o SOCKS5 quando il problema è dove esce la sessione TCP — un egress aziendale, un portatile di sviluppo che non raggiunge un vendor, o un'uscita pubblicata datacenter o mobile. Configurare le impostazioni proxy dell'SDK (o HTTPS_PROXY) e tenere la chiave API del fornitore sulla richiesta che l'origine vedrà. Non aspettarsi che il proxy scelga i modelli.

Usare un reverse proxy o un API gateway generico quando il problema è un hostname, tanti servizi interni, TLS al bordo, o policy sugli header. Puntare il DNS a quel gateway. Non aspettarsi che conosca lo spend di token a meno che non lo abbia aggiunto lei.

Usare un gateway LLM quando il problema è tanti client, tanti modelli, una policy. Emettere chiavi virtuali, impostare budget, registrare i prompt se la sua policy lo consente, e mappare alias sui modelli del fornitore. Tenere le chiavi del fornitore sull'host del gateway, non in ogni portatile. Leggere le note di compatibilità attuali del vendor; non assumere retry o fallback. Se ha un solo processo Python e nessuna policy condivisa, un gateway può essere parti in movimento di troppo — la documentazione di LiteLLM stessa spezza «gateway contro SDK» su quell'asse.

Lista di controllo prima di comprare o costruire:

  1. Scrivere il fallimento che ha visto davvero: TCP, TLS, stato HTTP, o errore JSON.
  2. Nominare chi termina il TLS.
  3. Nominare quale segreto è per il tunnel, quale per il gateway, e quale per il fornitore.
  4. Nominare la stringa di modello che il client invia e la stringa che il fornitore si aspetta.
  5. Confermare se streaming, retry e fallback sono documentati e testati su quel prodotto. Se non li ha eseguiti, non pretenderli.
  6. Confermare la forma della richiesta client (chat completions contro un altro schema).
  7. Se serviva solo un IP di uscita diverso, fermarsi al forward proxy e saltare il router di modelli.

La tabella numerata e la figura WebP sopra vengono da un laboratorio loopback CONNECT+TLS distinto, eseguito lo stesso giorno. Quel laboratorio è inoltro vero scelto dal client: HTTP CONNECT, poi TLS verso l'origine. Non è la ricetta copia-incolla sotto.

Questa ricetta pubblica è HTTP in chiaro solo su 127.0.0.1. Non apre un tunnel CONNECT, non usa TLS, e non chiama un vendor di modelli. Il suo hop Forward fa sempre POST a un origin_port fisso, e il client usa un path in origin-form (/v1/chat/completions). Nel senso di RFC 9110 è un pass-through reverse-style a upstream fisso, non un forward proxy HTTP scelto dal client. Confrontare quell'hop con il router applicativo di modelli nello stesso script. Non leggere forward_http nell'output atteso come prova di un forward proxy scelto dal client.

Setup: Python 3.9 o più recente. Solo libreria standard. Nessun pacchetto. Nessuna chiave API.

  1. Salvare lo script seguente come forwarding_vs_routing.py in una directory di sua proprietà.
  2. Eseguire: python3 forwarding_vs_routing.py
  3. Lo stdout dovrebbe coincidere con l'output atteso dopo lo script.
#!/usr/bin/env python3
"""Plain-HTTP loopback demo: forwarding vs model routing. Stdlib only."""
from __future__ import annotations

import json
import threading
from http.client import HTTPConnection
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

BIND = "127.0.0.1"
KEY = "***"
LABEL = "CONTROLLED_FIXTURE_NOT_A_PROVIDER"
TIMEOUT = 5


class Origin(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"

    def log_message(self, *_args):
        return

    def do_POST(self):
        n = int(self.headers.get("Content-Length") or 0)
        self.rfile.read(n)
        body = json.dumps(
            {
                "label": LABEL,
                "origin": "loopback-http-origin",
                "choices": [
                    {
                        "index": 0,
                        "message": {
                            "role": "assistant",
                            "content": "FIXTURE: forwarded origin",
                        },
                    }
                ],
            }
        ).encode()
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(body)))
        self.send_header("Connection", "close")
        self.end_headers()
        self.wfile.write(body)


class Gateway(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"
    ROUTES = {
        "fixture-alpha": {"label": LABEL, "route": "alpha"},
        "fixture-bravo": {"label": LABEL, "route": "bravo"},
    }

    def log_message(self, *_args):
        return

    def _send(self, code, obj):
        raw = json.dumps(obj).encode()
        self.send_response(code)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(raw)))
        self.send_header("Connection", "close")
        self.end_headers()
        self.wfile.write(raw)

    def do_POST(self):
        n = int(self.headers.get("Content-Length") or 0)
        raw = self.rfile.read(n)
        auth = self.headers.get("Authorization") or ""
        try:
            model = json.loads(raw.decode() or "{}").get("model")
        except json.JSONDecodeError:
            model = None
        if auth != "Bearer " + KEY:
            self._send(401, {"error": "invalid_local_gateway_auth", "label": LABEL})
            return
        route = self.ROUTES.get(model)
        if route is None:
            self._send(
                400,
                {
                    "error": "unsupported_model_or_route",
                    "requested": model,
                    "label": LABEL,
                },
            )
            return
        self._send(200, route)


class Forward(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"
    origin_port = 0

    def log_message(self, *_args):
        return

    def do_POST(self):
        n = int(self.headers.get("Content-Length") or 0)
        body = self.rfile.read(n)
        upstream = HTTPConnection(BIND, self.origin_port, timeout=TIMEOUT)
        try:
            skip = {"host", "proxy-connection"}
            headers = {k: v for k, v in self.headers.items() if k.lower() not in skip}
            upstream.request("POST", self.path, body=body, headers=headers)
            resp = upstream.getresponse()
            data = resp.read()
            status = resp.status
            ctype = resp.getheader("Content-Type") or "application/json"
        finally:
            upstream.close()
        self.send_response(status)
        self.send_header("Content-Type", ctype)
        self.send_header("Content-Length", str(len(data)))
        self.send_header("Connection", "close")
        self.end_headers()
        self.wfile.write(data)


def start(handler):
    httpd = ThreadingHTTPServer((BIND, 0), handler)
    httpd.daemon_threads = False
    httpd.timeout = 0.3
    thread = threading.Thread(
        target=httpd.serve_forever,
        name="srv-%s" % httpd.server_address[1],
        daemon=False,
    )
    thread.start()
    return httpd, thread


def post(port, path, payload, headers):
    conn = HTTPConnection(BIND, port, timeout=TIMEOUT)
    try:
        raw = json.dumps(payload).encode()
        hdrs = {"Content-Type": "application/json"}
        hdrs.update(headers)
        conn.request("POST", path, body=raw, headers=hdrs)
        resp = conn.getresponse()
        return resp.status, json.loads(resp.read().decode())
    finally:
        conn.close()


def main():
    origin_httpd = gw_httpd = fwd_httpd = None
    threads = []
    try:
        origin_httpd, origin_thread = start(Origin)
        threads.append(origin_thread)
        Forward.origin_port = origin_httpd.server_address[1]
        fwd_httpd, fwd_thread = start(Forward)
        threads.append(fwd_thread)
        gw_httpd, gw_thread = start(Gateway)
        threads.append(gw_thread)

        ping = {
            "model": "ignored-by-forward",
            "messages": [{"role": "user", "content": "ping"}],
        }
        rows = []
        status, body = post(
            fwd_httpd.server_address[1],
            "/v1/chat/completions",
            ping,
            {"Authorization": "***"},
        )
        rows.append(("forward_http", status, body.get("origin") or body.get("error")))
        assert status == 200
        assert body.get("label") == LABEL
        assert body.get("origin") == "loopback-http-origin"

        gw = gw_httpd.server_address[1]
        status, body = post(
            gw,
            "/v1/chat/completions",
            {"model": "fixture-alpha", "messages": []},
            {"Authorization": "Bearer " + KEY},
        )
        rows.append(("gateway_route_alpha", status, body.get("route") or body.get("error")))
        assert status == 200 and body.get("route") == "alpha"

        status, body = post(
            gw,
            "/v1/chat/completions",
            {"model": "fixture-bravo", "messages": []},
            {"Authorization": "Bearer " + KEY},
        )
        rows.append(("gateway_route_bravo", status, body.get("route") or body.get("error")))
        assert status == 200 and body.get("route") == "bravo"

        status, body = post(
            gw,
            "/v1/chat/completions",
            {"model": "fixture-alpha", "messages": []},
            {},
        )
        rows.append(("gateway_auth_missing", status, body.get("error")))
        assert status == 401

        status, body = post(
            gw,
            "/v1/chat/completions",
            {"model": "fixture-alpha", "messages": []},
            {"Authorization": "***"},
        )
        rows.append(("gateway_auth_invalid", status, body.get("error")))
        assert status == 401

        status, body = post(
            gw,
            "/v1/chat/completions",
            {"model": "no-such-model", "messages": []},
            {"Authorization": "Bearer " + KEY},
        )
        rows.append(("gateway_unsupported_model", status, body.get("error")))
        assert status == 400

        print("CONTROLLED_FIXTURE public recipe — not a provider call, not TLS")
        for name, status, extra in rows:
            print("%s HTTP %s %s" % (name, status, extra))
        print("assertions=6 ok")
        return 0
    finally:
        for httpd in (origin_httpd, gw_httpd, fwd_httpd):
            if httpd is not None:
                httpd.shutdown()
                httpd.server_close()
        for thread in threads:
            thread.join(timeout=TIMEOUT)


if __name__ == "__main__":
    raise SystemExit(main())

Output atteso:

CONTROLLED_FIXTURE public recipe — not a provider call, not TLS
forward_http HTTP 200 loopback-http-origin
gateway_route_alpha HTTP 200 alpha
gateway_route_bravo HTTP 200 bravo
gateway_auth_missing HTTP 401 invalid_local_gateway_auth
gateway_auth_invalid HTTP 401 invalid_local_gateway_auth
gateway_unsupported_model HTTP 400 unsupported_model_or_route
assertions=6 ok

La riga forward_http è quel pass-through a upstream fisso. Il laboratorio CONNECT+TLS nella tabella e nel WebP resta la prova dell'inoltro.

Un proxy di rete cambia la chiave API del modello che devo inviare?

No. CONNECT o SOCKS5 possono prendere Proxy-Authorization o nome utente/password SOCKS per il tunnel. Il vendor del modello si aspetta comunque la propria credenziale Bearer (o altra) sulla richiesta di origine, a meno che in mezzo non metta un gateway LLM che scambia una chiave virtuale con una chiave di fornitore memorizzata.

HTTP CONNECT legge il body JSON del TLS?

[RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#CONNECT) dice che dopo una risposta CONNECT 2xx il proxy dovrebbe inoltrare alla cieca. Nell'esecuzione loopback del 2026-10-15 il processo CONNECT ha visto l'host:port di destinazione e non ha visto JSON della fixture né token Bearer sui byte tunnellati. Un prodotto distinto di ispezione HTTPS non è quel comportamento solo-CONNECT.

Perché l'auth del gateway è diversa dall'auth del fornitore?

Una chiave virtuale di gateway nomina il suo progetto, il budget e i modelli consentiti sul gateway. Una chiave di fornitore nomina il suo account presso OpenAI o un altro vendor. La [panoramica di OpenAI](https://developers.openai.com/api/reference/overview) (recuperata il 2026-10-15) documenta le credenziali Bearer per l'API stessa. Perdere un segreto non è lo stesso incidente che perdere l'altro.

Che cosa significa davvero «compatibile OpenAI»?

Di solito che un client può fare POST di un body JSON a forma di chat-completions verso un altro base URL. Non significa che ogni superficie OpenAI funzioni. La [documentazione dynamic-routing di Cloudflare](https://developers.cloudflare.com/ai-gateway/features/dynamic-routing/) (aggiornata il 2026-10-02) afferma persino che i formati non-chat-completions possono dare 400 su quella funzione. Trattare la compatibilità come un sottoinsieme documentato, non come equivalenza API piena.

SOCKS5 può sostituire HTTP CONNECT per le API HTTPS di modelli?

Per molti client TLS, sì, perché SOCKS5 CONNECT ([RFC 1928](https://www.rfc-editor.org/rfc/rfc1928.txt)) è un relay TCP e HTTPS è TLS-over-TCP. La libreria deve parlare SOCKS. Alcuni ambienti espongono solo HTTP CONNECT. Il supporto di protocollo è una capacità del client, non una proprietà del modello.

Il controllo proxy dimostra che un'API LLM accetterà quell'IP?

No. Il controllo è una sonda lato server di un endpoint HTTP, HTTPS o SOCKS5 che lei fornisce. Non mostra il percorso che prenderà un processo SDK Python, non accede a un vendor di modelli, non valida le chiavi API, e non cambia l'idoneità dell'account.

Tutti i gateway LLM fanno retry, fallback e streaming allo stesso modo?

No. [LiteLLM](https://docs.litellm.ai/docs/simple_proxy) e [Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/) documentano alcune di quelle funzioni su pagine recuperate il 2026-10-15, con note di ambito diverse. Il router locale di questo articolo non ha implementato retry, fallback o streaming, quindi quei comportamenti non sono pretesi qui.