AI proxy a brama LLM: czym się różnią

12 min

Rozróżnić przekazywanie sieciowe, bramę API i routing modeli, żeby wybrać warstwę, która pasuje do zadania.

Ludzie mówią „AI proxy” na trzy różne pudełka: przekazywacz sieciowy, odwrotną bramę API i router modeli. Ten przewodnik jest dla programistów, którzy muszą wybrać jedną warstwę, nie mieszając ich zadań. Nie sprzedaje adresów IP, nie rozdaje kluczy i nie twierdzi, że proxy zmienia uprawnienie konta albo geografii.

01Co ludzie zwykle mają na myśli, mówiąc „AI proxy”

To sformułowanie nie jest standardem. RFC 9110 (HTTP Semantics, czerwiec 2022, STD 97) już dzieli pośredników na proxy, bramę i tunel, i ostrzega, że jeden proces może zmieniać rolę per żądanie. W copy produktowym to samo słowo jest używane jeszcze do czegoś innego: procesu, który przyjmuje ciało JSON w stylu OpenAI i wybiera model.

Te trzy zadania wyglądają podobnie na diagramie, bo każde siedzi między waszym klientem a czymś upstream. Padają w różnych miejscach. Proxy forward potrafi otworzyć tunel TCP i nadal zostawić was z 401 od dostawcy. Brama LLM potrafi zwrócić HTTP 200 z błędem JSON o nieznanym modelu, podczas gdy ścieżka sieciowa była w porządku. Jeśli debugujecie złą warstwę, będziecie rotować zły sekret.

Ograniczony lokalny przebieg z 2026-10-15 uczynił ten podział konkretnym. Proxy HTTP CONNECT na loopback przekazywało TLS do atrapy origin. Osobny proces na loopback routował fixture-alpha kontra fixture-bravo po lokalnym sprawdzeniu Bearer. Nic nie opuściło 127.0.0.1. Nic nie wołało płatnego API modelu. JSON atrapy jest celowo oznaczony CONTROLLED_FIXTURE_NOT_A_PROVIDER.

Gdy sprzedawca nazywa produkt „AI proxy”, odczytać, które z tych zadań naprawdę implementuje. Nie zakładać ponowień, streamingu ani fallback tylko dlatego, że na stronie głównej pojawia się słowo gateway.

02Proxy forward HTTP i SOCKS, w tym CONNECT

Proxy forward wybiera klient. RFC 9110 §3.7 nazywa je agentem przekazywania komunikatów, który otrzymuje żądania dla niektórych absolutnych URI i stara się je zaspokoić przez HTTP. Dla zwykłych URL http klient wysyła do proxy pełny URL. Dla https zwykły wzorzec jest inny.

RFC 9110 §9.3.6 definiuje CONNECT: klient prosi proxy, żeby zbudowało tunel do host:port i po dowolnym 2xx ograniczyło się do ślepego przekazywania, aż tunel się zamknie. Tunele zwykle potem owija się w TLS. Udany CONNECT nie jest sukcesem aplikacji. Znaczy tylko, że tunel istnieje. Każda inna odpowiedź znaczy, że tunel nie powstał.

SOCKS5 jest starsze i siedzi niżej. RFC 1928 (marzec 1996) opisuje je jako przekładkę między aplikacją a transportem, umownie na TCP 1080. Po negocjacji metody klient wysyła polecenie; CONNECT to X'01'. SOCKS nie parsuje metod HTTP. Przekazuje TCP (i opcjonalnie UDP) do celu, który klient nazwie.

Jeśli biblioteka HTTP mówi HTTPS przez proxy, zwykle robi CONNECT, a potem TLS do origin. Proxy uwierzytelnia tunel, często przez Proxy-Authorization. Ten nagłówek to nie nagłówek Authorization: Bearer, którego oczekuje API modelu. Mieszanie ich to częste źródło „proxy działa w curl, a SDK i tak dostaje 401”.

Jeśli zadanie to tylko „ten proces ma wychodzić innym IP”, zacząć od angielskich list proxy HTTP, proxy HTTPS i proxy SOCKS5. Te strony porównują wyjścia. To nie routery modeli.

03Proxy odwrotne i zwykłe bramy API

Proxy odwrotne, które RFC 9110 w §3.7 też nazywa bramą, przedstawia się klientowi jako origin i przekazuje przychodzące do innego serwera. Klient nie wybrał go jako URL proxy. Klient po prostu otworzył https://api.example.com.

Przewodnik NGINX o reverse-proxy (dostęp 2026-10-15) opisuje mechaniczną pętlę: wysłać żądanie do upstream proxy_pass, pobrać odpowiedź, odesłać ją. Domyślnie NGINX przepisuje Host i Connection. Ten szczegół ma znaczenie dla API, które klucze wirtualnych hostów biorą z Host. Proxy odwrotne może zakończyć TLS na publicznej krawędzi i otworzyć nowe połączenie w głąb. Sesja TLS klienta kończy się wtedy na proxy odwrotnym, nie u sprzedawcy modelu.

Ta warstwa jest właściwym narzędziem do routingu ścieżek (/v1/ kontra /admin/), równoważenia obciążenia, sanityzacji nagłówków i wystawienia jednej nazwy hosta przed kilkoma usługami wewnętrznymi. Może nie wiedzieć, czym jest nazwa modelu. Jeśli tylko przekazuje /v1/chat/completions do jednego upstream, nadal jest bramą API w sensie HTTP, nie bramą LLM.

Nie traktować „postawiliśmy NGINX z przodu” jako dowodu ponowień, budżetów tokenów ani fallback modeli. To dodatkowe cechy, które niektóre produkty dokładają. Strona NGINX, którą czytaliśmy, ich nie twierdzi.

04Bramy LLM: klucze, limity, routing i logi

Brama LLM to usługa aplikacyjna. Klienci wysyłają ciało żądania czatu albo embeddings pod jeden URL. Brama sprawdza wirtualny klucz, może wymuszać budżety, może wybrać dostawcę i model, może logować tokeny, i dopiero wtedy rozmawia ze sprzedawcą — albo z kilkoma.

Strona AI Gateway LiteLLM (HTTP 200 2026-10-15) opisuje self-hosted serwer, który oferuje jeden punkt końcowy zgodny z OpenAI. Udokumentowane zadania obejmują klucze wirtualne z modelami i limitami per klucz, śledzenie wydatków, równoważenie obciążenia, fallback, ponowienia, guardrails i logowanie. Ta sama strona mówi: jeśli klient potrafi użyć API OpenAI, potrafi użyć bramy bez zmiany kodu klienta. To twierdzenie produktowe o kształcie żądania chat completions. To nie dowód, że każdy punkt końcowy, nagłówek i błąd OpenAI mapuje się 1:1. LiteLLM nie instalowaliśmy i nie uruchamialiśmy.

Cloudflare AI Gateway (strona dateModified 2026-04-20, pobrana 2026-10-15) wymienia analitykę, logowanie, cache, limity tempa, ponowienia żądań i fallback modeli jako cechy. Strona dynamic-routing (aktualizacja 2026-10-02) jest precyzyjniejsza o zgodności: trasy dynamiczne przyjmują wyłącznie kształt żądania OpenAI chat completions; Anthropic Messages zwracają HTTP 400. To zdanie jest powodem, dla którego „OpenAI-compatible” musi zostać wąskie. Cloudflare nie wołaliśmy i nie wykonywaliśmy ponowień ani fallback. Jeśli dokument bramy wymienia te cechy, traktować je jako wyłącznie źródłowe, dopóki nie odpalicie ich wobec tego produktu.

Lokalnie zbudowaliśmy malutki router, który nie jest tymi produktami. Przyjmował Authorization: Bearer local-demo-key-not-a-provider-secret, mapował fixture-alpha i fixture-bravo na dwa ładunki atrapy, zwracał 401 bez ważnego lokalnego tokenu i zwracał 400 dla nieznanego modelu. To wystarcza, żeby pokazać, że płaszczyzna sterowania siedzi w HTTP JSON, nie w CONNECT.

05Śledzić żądanie: TLS, Host i kto może czytać ciało

Przejść jedno POST /v1/chat/completions przez oba projekty.

Ścieżka forward użyta w przebiegu na loopback: klient otworzył TCP do 127.0.0.1:54318, wysłał CONNECT 127.0.0.1:54316 HTTP/1.1, dostał 200 Connection Established, a potem dokończył TLS wobec certyfikatu origin. Dopiero po TLS wysłał ciało JSON i nagłówek Bearer przeznaczony dla origin. Proces CONNECT zapisał cel 127.0.0.1:54316. Nie zobaczył JSON atrapy ani tokenu Bearer: lista fragmentów ciała HTTP zaobserwowanych na tunelu była pusta. TLS kończył się na atrapie origin. Origin odpowiedział HTTP 200 z kontrolowaną atrapą.

Ścieżka bramy: klient otworzył TCP do 127.0.0.1:54317 bez CONNECT. TLS nie był użyty na tym przeskoku w demo (zwykły HTTP na loopback). Brama odczytała pole JSON model i lokalny token Bearer zanim dla tego żądania istniał jakikolwiek origin. Brakujące albo złe lokalne auth nigdy nie doszło do atrapy modelu jako udana trasa; oba dały HTTP 401 z invalid_local_gateway_auth. Nieznany model dał HTTP 400 z unsupported_model_or_route po udanym auth. Transport był w górze. Aplikacja odmówiła zadania.

W produkcyjnym HTTPS brama LLM niemal zawsze kończy TLS. Musi, jeśli ma parsować JSON, stosować budżet albo wybierać model. Proxy forward CONNECT, które nie jest middleboxem TLS, nie kończy tego TLS origin. Jeśli ktoś włącza inspekcję HTTPS na korporacyjnym proxy, to inne wdrożenie: proxy staje się klientem TLS wobec origin i serwerem TLS wobec was. Nie zakładać inspekcji tylko dlatego, że CONNECT się udał.

Przegląd API OpenAI (pobrany 2026-10-15; żądany platform.openai.com/docs/api-reference przekierował tutaj) dokumentuje dane Bearer na żądaniu origin. Traktuje x-request-id jako identyfikator korelacji żądania, osobny od nagłówków limitu tempa takich jak x-ratelimit-limit-requests. Te pola należą do rozmowy dostawcy. Brama może dodać własne identyfikatory żądań. Proxy forward zwykle nie.

06Zgodność to kształt klienta, nie całe API

SDK często pozwalają ustawić base_url i nadal używać chat.completions.create. Sprzedawcy nazywają to OpenAI-compatible. Uwaga Cloudflare o dynamic-routing jest ostrożnością: nawet wewnątrz jednego sprzedawcy ścieżka zgodności może przyjąć tylko jeden kształt żądania i 400 inny.

Zgodny punkt końcowy nadal może różnić się uwierzytelnianiem (klucz wirtualny kontra klucz dostawcy), nazwami modeli (gpt-4.1 kontra azure/gpt-4.1 kontra alias bramy), układami zdarzeń streamingu, polami tool-call, uploadami plików, gniazdami realtime i API administracji. Własny przegląd OpenAI traktuje Responses, Realtime i Administration jako osobne powierzchnie. Skopiowanie /v1/chat/completions nie kopiuje całego tego drzewa.

Brama na loopback zwracała JSON, który wygląda jak chat completion (choices[0].message). To była atrapa. Nie czyni demo „OpenAI compatible” w żadnym produktowym sensie.

07Trzy różne rodzaje „zadziałało”

Te trzy wyniki trzymać osobno, gdy czytacie logi.

WarstwaPrzykład sukcesuPrzykład awariiCzego to nie dowodzi
Transport / tunelCONNECT 200 Connection Established; odpowiedź SOCKS CONNECTProxy 403, timeout, błąd handshake TLSŻe model przyjął prompt albo klucz
HTTP do następnego przeskoku200 albo nawet 401/400 z ciałem JSONPusta odpowiedź, RST, HTTP 502 od proxy odwrotnegoŻe JSON jest wynikiem modelu
Aplikacja / trasa modeluBrama mapuje fixture-alpha na atrapę alpha; dostawca zwraca completionBrama 400 nieobsługiwany model; dostawca 401 niepoprawny klucz API; odmowa safety modelu wewnątrz HTTP 200Że inne warstwy są zdrowe, albo że drugi sprzedawca zachowałby się tak samo

2026-10-15 lokalny skrypt zapisał sześć przypadków i szesnaście asercji, wszystkie zaliczone, przy Pythonie 3.9.6 i LibreSSL 3.3.6:

Angielska tabela wyników z atrapy na loopback: przekazywanie CONNECT kontra routing modeli w bramie.

Przekazywanie dało origin HTTP 200. Routing bramy dał dwie różne trasy atrapy. Awarie auth zostały na bramie z 401. Nieobsługiwany model został na bramie z 400. Ponowienia, streaming i fallback nie były wykonywane i nie są twierdzone.

Strona Jaki jest mój adres IP zgłasza adres, który doszedł do tej strony z waszej przeglądarki. Sprawdzanie proxy to sonda po stronie serwera punktu końcowego, który podacie. Żadne z nich nie pokazuje trasy, którą weźmie proces SDK Pythona, i żadne nie ustala uprawnienia do API LLM. Sprawdzanie nie dowodzi, że sprzedawca modelu przyjmie IP, że klucz jest ważny, ani że wolno wam użyć danego konta.

08Wybrać warstwę, która pasuje do zadania

Użyć proxy forward HTTP CONNECT albo SOCKS5, gdy problemem jest skąd wychodzi sesja TCP — korporacyjny egress, laptop deweloperski, który nie dosięga sprzedawcy, albo opublikowane wyjście datacenter albo mobilne. Skonfigurować ustawienia proxy SDK (albo HTTPS_PROXY) i zostawić klucz API dostawcy na żądaniu, które zobaczy origin. Nie oczekiwać, że proxy będzie wybierać modele.

Użyć proxy odwrotnego albo zwykłej bramy API, gdy problemem jest jedna nazwa hosta, wiele usług wewnętrznych, TLS na krawędzi albo polityka nagłówków. Skierować DNS na tę bramę. Nie oczekiwać, że zna wydatek tokenów, chyba że sami to dodaliście.

Użyć bramy LLM, gdy problemem jest wielu klientów, wiele modeli, jedna polityka. Wydawać klucze wirtualne, ustawiać budżety, logować prompty jeśli polityka na to pozwala, i mapować aliasy na modele dostawcy. Klucze dostawcy trzymać na hoście bramy, nie na każdym laptopie. Czytać aktualne uwagi o zgodności sprzedawcy; nie zakładać ponowień ani fallback. Jeśli macie tylko jeden proces Pythona i nie macie wspólnej polityki, brama może być zbędnymi ruchomymi częściami — własne dokumenty LiteLLM dzielą „gateway versus SDK” właśnie na tej osi.

Lista kontrolna przed zakupem albo budową:

  1. Zapisać awarię, którą naprawdę zobaczyliście: TCP, TLS, status HTTP albo błąd JSON.
  2. Nazwać, kto kończy TLS.
  3. Nazwać, który sekret jest dla tunelu, który dla bramy, a który dla dostawcy.
  4. Nazwać ciąg modelu, który wysyła klient, i ciąg, którego oczekuje dostawca.
  5. Potwierdzić, czy streaming, ponowienia i fallback są udokumentowane oraz przetestowane na tym produkcie. Jeśli ich nie odpaliliście, nie twierdzić ich.
  6. Potwierdzić kształt żądania klienta (chat completions kontra inny schemat).
  7. Jeśli potrzebowaliście tylko innego IP wyjścia, zatrzymać się na proxy forward i pominąć router modeli.

Ponumerowana tabela i rysunek WebP powyżej pochodzą z osobnego laboratorium CONNECT+TLS na loopback z tego samego dnia. To laboratorium to rzeczywiste przekazywanie wybrane przez klienta: HTTP CONNECT, potem TLS do origin. To nie przepis copy-paste poniżej.

Ten publiczny przepis to zwykły HTTP wyłącznie na 127.0.0.1. Nie otwiera tunelu CONNECT, nie używa TLS i nie woła sprzedawcy modelu. Jego przeskok Forward zawsze postuje na stały origin_port, a klient używa ścieżki origin-form (/v1/chat/completions). W sensie RFC 9110 to przekazywanie w stylu odwrotnym, ze stałym upstream, nie proxy forward HTTP wybrane przez klienta. Porównać ten przeskok z aplikacyjnym routerem modeli w tym samym skrypcie. Nie czytać forward_http w oczekiwanym wyjściu jako dowodu proxy forward wybranego przez klienta.

Setup: Python 3.9 albo nowszy. Wyłącznie biblioteka standardowa. Bez pakietów. Bez kluczy API.

  1. Zapisać poniższy skrypt jako forwarding_vs_routing.py w dowolnym katalogu, który posiadacie.
  2. Uruchomić: python3 forwarding_vs_routing.py
  3. Wasze stdout powinno zgadzać się z oczekiwanym wyjściem po skrypcie.
#!/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())

Oczekiwane wyjście:

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

Wiersz forward_http to to przekazywanie ze stałym upstream. Laboratorium CONNECT+TLS w tabeli i WebP pozostaje dowodem przekazywania.

Czy proxy sieciowe zmienia klucz API modelu, który trzeba wysłać?

Nie. CONNECT albo SOCKS5 może wziąć Proxy-Authorization albo użytkownika/hasło SOCKS dla tunelu. Sprzedawca modelu nadal oczekuje własnego poświadczenia Bearer (albo innego) na żądaniu origin, chyba że w środku postawicie bramę LLM, która zamienia klucz wirtualny na przechowywany klucz dostawcy.

Czy HTTP CONNECT czyta ciało JSON TLS?

[RFC 9110](https://www.rfc-editor.org/rfc/rfc9110.html#CONNECT) mówi, że po odpowiedzi CONNECT 2xx proxy powinno ślepo przekazywać. W przebiegu na loopback z 2026-10-15 proces CONNECT zobaczył docelowy host:port i nie zobaczył JSON atrapy ani tokenów Bearer na tunelowanych bajtach. Osobny produkt inspekcji HTTPS to nie to zachowanie wyłącznie CONNECT.

Dlaczego uwierzytelnianie bramy różni się od uwierzytelniania dostawcy?

Wirtualny klucz bramy nazywa wasz projekt, budżet i dozwolone modele na bramie. Klucz dostawcy nazywa wasze konto u OpenAI albo innego sprzedawcy. [Przegląd OpenAI](https://developers.openai.com/api/reference/overview) (pobrany 2026-10-15) dokumentuje dane Bearer dla samego API. Utrata jednego sekretu to nie ten sam incydent co utrata drugiego.

Co „OpenAI-compatible” naprawdę znaczy?

Zwykle to, że klient może POST-ować ciało JSON w kształcie chat-completions pod inny base URL. Nie znaczy, że działa każda powierzchnia OpenAI. [Dokumentacja Cloudflare o dynamic-routing](https://developers.cloudflare.com/ai-gateway/features/dynamic-routing/) (aktualizacja 2026-10-02) nawet stwierdza, że formaty inne niż chat-completions mogą dostać 400 na tej cesze. Traktować zgodność jako udokumentowany podzbiór, nie pełną równoważność API.

Czy SOCKS5 może zastąpić HTTP CONNECT dla HTTPS API modeli?

Dla wielu klientów TLS tak, bo SOCKS5 CONNECT ([RFC 1928](https://www.rfc-editor.org/rfc/rfc1928.txt)) to przekaźnik TCP, a HTTPS to TLS-over-TCP. Biblioteka musi mówić SOCKS. Niektóre środowiska wystawiają tylko HTTP CONNECT. Obsługa protokołu to zdolność klienta, nie właściwość modelu.

Czy sprawdzanie proxy dowodzi, że API LLM przyjmie ten IP?

Nie. Angielskie sprawdzanie to sonda po stronie serwera punktu końcowego HTTP, HTTPS albo SOCKS5, który podacie. Nie pokazuje trasy, którą weźmie proces SDK Pythona, nie loguje się u sprzedawcy modelu, nie waliduje kluczy API i nie zmienia uprawnienia konta.

Czy wszystkie bramy LLM ponawiają, robią fallback i streamują tak samo?

Nie. [LiteLLM](https://docs.litellm.ai/docs/simple_proxy) i [Cloudflare AI Gateway](https://developers.cloudflare.com/ai-gateway/) dokumentują część tych cech na stronach pobranych 2026-10-15, z różnymi uwagami o zakresie. Lokalny router tego artykułu nie implementował ponowień, fallback ani streamingu, więc tych zachowań tutaj nie twierdzimy.