Powrót do bloga

API i SDKPrzewodnik

Limit czasu i ponowienia w OpenAI Python SDK: ustaw je dla CheapTokens

Ustaw timeout, max_retries i max_tokens w OpenAI Python SDK, przechwytuj właściwe błędy i nie pozwól, by zawieszone żądanie działało przez 10 minut.

Autor: CheapTokens TeamOpublikowano 4 min czytania

Kod w Pythonie z timeout=30.0 i max_retries=2 na ciemnym tle
Na tej stronie
  1. Co robi SDK, gdy nic nie ustawisz
  2. Ustaw timeout i max_retries na kliencie
  3. Nadpisz to dla jednego żądania
  4. Łap błędy, które mają znaczenie
  5. Ogranicz max_tokens, aby długa odpowiedź nie wymknęła się spod kontroli
  6. Co ponowienia robią z rachunkiem
  7. Zanim wdrożysz

Twój skrypt wywołał model, nic nie wróciło, a dziesięć minut później wciąż czeka. Taki jest domyślny stan OpenAI Python SDK: 10-minutowy limit czasu i 2 ponowienia. Aby to zmienić, przekaż timeout=30.0 i max_retries=2 do OpenAI(...), nadpisz je dla pojedynczego wywołania przez client.with_options(...) i ogranicz odpowiedź przez max_tokens.

README SDK (sprawdzone 27 października 2026) dokumentuje to wszystko. Poniżej zastosowano to do endpointu CheapTokens, https://api.cheaptokens.dev/v1, który mówi w formacie OpenAI Chat Completions. Szczegóły tej konfiguracji są w Twoje pierwsze żądanie API z CheapTokens oraz w dokumentacji.

Co robi SDK, gdy nic nie ustawisz#

Znaczenie mają dwa ustawienia domyślne. Limit czasu to 10 minut. Ponowienia to 2, z krótkim wykładniczym backoffem, dla błędów połączenia oraz odpowiedzi 408, 409, 429 i 5xx, i tylko wtedy, gdy treść żądania można bezpiecznie wysłać ponownie. Żądanie, które przekroczy limit czasu, również jest ponawiane.

Zatem każda próba ma własny limit czasu, a najgorszy przypadek to w przybliżeniu limit czasu pomnożony przez liczbę prób plus przerwy między nimi:

  • domyślnie: 600 × 3 = 1800 sekund, pół godziny
  • timeout=30.0, max_retries=2: 30 × 3 = 90 sekund

Załóżmy, że endpoint webowy musi odpowiedzieć w ciągu 30 sekund. Przy ustawieniach domyślnych może wisieć przez pół godziny, zanim twój kod zobaczy błąd. Wypróbuj to na innym ustawieniu, zanim przeczytasz dalej.

Zgadnij liczbę

Klient ma timeout=45.0 i max_retries=3. Jaki jest najgorszy czas oczekiwania w sekundach przed wyjątkiem, nie licząc przerw między próbami?

Pokaż odpowiedź

Odpowiedź: 180

45 × (3 ponowienia + 1 pierwsza próba) = 180 sekund; przerwy między próbami dodają jeszcze trochę.

Ustaw timeout i max_retries na kliencie#

Najprostsza poprawka to ustawienie obu raz, przy tworzeniu klienta:

python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.cheaptokens.dev/v1",
    api_key=os.environ["CHEAPTOKENS_API_KEY"],
    timeout=30.0,      # seconds per attempt
    max_retries=2,     # 0 turns retries off
)

resp = client.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    messages=[{"role": "user", "content": "Summarize this ticket in two sentences: ..."}],
    max_tokens=800,
)
print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)

max_retries musi być nieujemną liczbą całkowitą. timeout przyjmuje float albo obiekt httpx Timeout, jeśli chcesz osobne limity dla łączenia i odczytu. README bieżącej gałęzi main importuje w tym celu httpx2, starsze wersje używały httpx, więc uruchom pip show openai i sprawdź, której używa twoja instalacja. Zwykły float nie wymaga importu i działa w obu przypadkach.

Ścieżka żądania z timeout=30 i dwoma ponowieniami
Jedno żądanie: próba, przekroczenie limitu czasu, do dwóch ponowień, potem wyjątek

Użyj pełnego identyfikatora modelu z dokumentacji CheapTokens, tutaj deepseek-ai/DeepSeek-V4-Flash-0731: identyfikatory różnią się między dostawcami. Ten model przyjmuje do 32 768 tokenów wyjściowych na odpowiedź i ma kontekst 380K, dlatego powyższa linia max_tokens jest tam celowo. Więcej o tym poniżej, a o modelu w tani DeepSeek.

Nadpisz to dla jednego żądania#

Jeden klient, różne zadania. Odpowiedź czatu powinna szybko się poddać, nocne podsumowanie może poczekać. Zamiast tworzyć drugiego klienta, wyprowadź go dla wywołania:

python
quick = client.with_options(timeout=5.0, max_retries=0)
resp = quick.chat.completions.create(
    model="deepseek-ai/DeepSeek-V4-Flash-0731",
    messages=[{"role": "user", "content": "Reply with one word: ok"}],
    max_tokens=5,
)

with_options zwraca kopię z nowymi wartościami i pozostawia client bez zmian. Ustawienie max_retries=0 jest właściwym wyborem, gdy twój własny kod już ponawia, na przykład kolejka zadań, która ponownie dodaje zadanie do kolejki. W przeciwnym razie obie warstwy się mnożą: 3 próby SDK wewnątrz 3 prób kolejki to 9 żądań dla jednego zadania.

Łap błędy, które mają znaczenie#

Gdy ponowienia się wyczerpią, SDK zgłasza wyjątek. Każda klasa błędu dziedziczy po openai.APIError, a nieudane żądania niosą request_id, co właśnie logujesz:

python
import openai

def ask(prompt: str) -> str | None:
    try:
        r = client.chat.completions.create(
            model="deepseek-ai/DeepSeek-V4-Flash-0731",
            messages=[{"role": "user", "content": prompt}],
            max_tokens=800,
        )
        return r.choices[0].message.content
    except openai.APITimeoutError:
        print("timed out after retries")
    except openai.RateLimitError as e:
        print("429, slow down:", e.request_id)
    except openai.APIConnectionError:
        print("cannot reach the API")
    except openai.APIStatusError as e:
        print("HTTP", e.status_code, e.request_id)
    return None

Kolejność jest od wąskich do szerokich. APIStatusError obejmuje każdą odpowiedź 4xx lub 5xx, więc gdyby był pierwszy, połknąłby RateLimitError. To samo dotyczy limitu czasu: znajduje się powyżej ogólnego błędu połączenia. W skrypcie wsadowym return None jest sednem sprawy: zaloguj prompt, przejdź do następnego i uruchom ponownie nieudane na końcu.

Gdy limit czasu zadziała, a ponowienia się wyczerpią, wyjątek wygląda tak:

Komunikat APITimeoutError po wyczerpaniu ponowień
Jak wygląda przechwycony limit czasu w logu

Szczegóły przypadku 429, w tym oczekiwanie między próbami, znajdziesz w 429 Too Many Requests z API AI.

Ogranicz max_tokens, aby długa odpowiedź nie wymknęła się spod kontroli#

Limit czasu przestaje czekać. Nie sprawia, że model pisze mniej. Bez max_tokens model z limitem odpowiedzi 32 768 tokenów może swobodnie napisać bardzo długą odpowiedź, a długa odpowiedź jest wolna. Ustaw limit pasujący do zadania: 800 dla podsumowania zgłoszenia, 5 dla tak/nie.

Koszt jest taki, że odpowiedź może zostać ucięta. Sprawdź resp.choices[0].finish_reason: length oznacza, że limit zadziałał, zanim model skończył. Omawia to Dlaczego odpowiedź AI urywa się w połowie zdania?. Jeśli zamiast tego strumieniujesz odpowiedź, pierwsze słowa pojawiają się wcześnie i oczekiwanie wydaje się krótsze, zobacz Strumieniowanie odpowiedzi w Pythonie.

Co ponowienia robią z rachunkiem#

Ponowienie to nowe żądanie. Jeśli próba została przetworzona, to osobne żądanie z własnymi tokenami. Załóżmy, że podsumowanie zgłoszenia to 1500 tokenów na wejściu i 400 na wyjściu: jedno żądanie kosztuje $0.000063 przy dzisiejszych cenach, a pełna tabela cen zawiera pozostałe modele. Ceny są stałe dla modelu, bez godzin szczytu, więc ponowienie o 3 w nocy kosztuje tyle samo co o południu.

Przesuń suwaki na własną partię:

Kalkulator kosztów

Partia 1000 podsumowań zgłoszeń

Tokeny wejściowe na żądanie: 1500 · Tokeny wyjściowe na żądanie: 400 · Żądania dziennie: 1000

ModelDziennieMiesięcznie
GLM-5.3-Flash$0.046$1.38
DeepSeek-V4-Flash$0.0625$1.88
MiniMax-M2.7$0.135$4.05

Tabela pokazuje jedno przejście. Jeśli dziesiąta część partii wymaga drugiej próby, dodaj jedną dziesiątą. Ponowienia to tanie ubezpieczenie, gdy awarie są rzadkie, i kosztowny nawyk, gdy ustawisz max_retries=10 przeciwko endpointowi, który ciągle zawodzi.

Zanim wdrożysz#

Lista kontrolna

Limity czasu i ponowienia gotowe do produkcji