Quay lại blog

API & SDKHướng dẫn

Timeout và retry của OpenAI Python SDK: đặt chúng cho CheapTokens

Đặt timeout, max_retries và max_tokens trong OpenAI Python SDK, bắt đúng các lỗi, và giữ một request bị kẹt không chạy trong 10 phút.

Bởi CheapTokens TeamĐã xuất bản 6 phút đọc

Code Python với timeout=30.0 và max_retries=2 trên nền tối
Trên trang này
  1. SDK làm gì khi bạn không đặt gì
  2. Đặt timeout và max_retries trên client
  3. Ghi đè cho một request
  4. Bắt các lỗi quan trọng
  5. Giới hạn max_tokens để một câu trả lời dài không chạy mất kiểm soát
  6. Retry ảnh hưởng gì đến hóa đơn
  7. Trước khi bạn ship

Script của bạn đã gọi model, không có gì trả về, và mười phút sau nó vẫn đang chờ. Đó là mặc định của OpenAI Python SDK: timeout 10 phút và 2 lần retry. Để thay đổi, truyền timeout=30.0 và max_retries=2 cho OpenAI(...), ghi đè chúng theo từng lời gọi bằng client.with_options(...), và giới hạn câu trả lời bằng max_tokens.

README của SDK (kiểm tra ngày 27 tháng 10 năm 2026) ghi lại tất cả những điều này. Dưới đây nó được áp dụng cho endpoint CheapTokens, https://api.cheaptokens.dev/v1, vốn nói định dạng OpenAI Chat Completions. Chi tiết về thiết lập đó nằm trong Yêu cầu API đầu tiên của bạn với CheapTokens và tài liệu.

SDK làm gì khi bạn không đặt gì#

Hai mặc định quan trọng. Timeout là 10 phút. Retry là 2, với exponential backoff ngắn, cho lỗi kết nối và cho các phản hồi 408, 409, 429 và 5xx, và chỉ khi body của request có thể được gửi lại an toàn. Một request bị timeout cũng được retry.

Vậy mỗi lần thử có timeout riêng, và trường hợp xấu nhất đại khái là timeout nhân với số lần thử, cộng thêm khoảng dừng giữa chúng:

  • mặc định: 600 × 3 = 1800 giây, nửa giờ
  • timeout=30.0, max_retries=2: 30 × 3 = 90 giây

Giả sử một endpoint web phải trả lời trong vòng 30 giây. Với các mặc định, nó có thể treo nửa giờ trước khi code của bạn thấy lỗi. Hãy thử với một thiết lập khác trước khi đọc tiếp.

Đoán số

Một client có timeout=45.0 và max_retries=3. Thời gian chờ trong trường hợp xấu nhất tính bằng giây trước khi có exception là bao nhiêu, không tính khoảng dừng giữa các lần thử?

Hiện đáp án

Trả lời: 180

45 × (3 lần retry + 1 lần thử đầu) = 180 giây; khoảng dừng giữa các lần thử cộng thêm một chút.

Đặt timeout và max_retries trên client#

Cách sửa đơn giản nhất là đặt cả hai một lần, khi bạn tạo client:

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 phải là một số nguyên không âm. timeout nhận một float, hoặc một đối tượng httpx Timeout nếu bạn muốn giới hạn riêng cho việc kết nối và đọc. README của nhánh main hiện tại import httpx2 cho việc đó, các phiên bản cũ hơn dùng httpx, nên hãy chạy pip show openai và kiểm tra xem bản cài của bạn dùng cái nào. Một float thuần không cần import và hoạt động với cả hai.

Đường đi của request với timeout=30 và hai lần retry
Một request: một lần thử, một timeout, tối đa hai lần retry, rồi một exception

Dùng id model đầy đủ từ tài liệu CheapTokens, ở đây là deepseek-ai/DeepSeek-V4-Flash-0731: các id khác nhau giữa các provider. Model này nhận tối đa 32.768 output token mỗi câu trả lời và có context 380K, đó là lý do dòng max_tokens ở trên có mặt một cách có chủ đích. Nói thêm về nó bên dưới, và về model trong cheap DeepSeek.

Ghi đè cho một request#

Một client, nhiều công việc khác nhau. Một câu trả lời chat nên bỏ cuộc nhanh, một bản tóm tắt chạy đêm có thể chờ. Thay vì tạo client thứ hai, hãy tạo một client dẫn xuất cho lời gọi:

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 trả về một bản sao với các giá trị mới và để client nguyên vẹn. Đặt max_retries=0 là lựa chọn đúng khi code của bạn đã tự retry, ví dụ một job queue đưa tác vụ trở lại hàng đợi. Nếu không, hai lớp sẽ nhân lên: 3 lần thử của SDK bên trong 3 lần thử của queue là 9 request cho một tác vụ.

Bắt các lỗi quan trọng#

Sau khi các lần retry cạn, SDK ném ra một exception. Mọi lớp lỗi đều kế thừa từ openai.APIError, và các request thất bại mang theo request_id, đó là thứ bạn ghi log:

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

Thứ tự là từ hẹp đến rộng. APIStatusError bao trùm mọi phản hồi 4xx hoặc 5xx, nên nếu nó đứng trước nó sẽ nuốt RateLimitError. Ý tưởng tương tự với timeout: nó nằm trên lỗi kết nối chung. Trong một script batch, return None là toàn bộ mục đích: ghi log prompt, chuyển sang cái tiếp theo, và chạy lại các cái thất bại ở cuối.

Khi timeout kích hoạt và các lần retry đã dùng hết, exception trông như thế này:

Thông báo APITimeoutError sau khi các lần retry đã dùng hết
Một timeout bị bắt trông như thế nào trong log

Để biết chi tiết trường hợp 429, bao gồm chờ giữa các lần thử, xem 429 Too Many Requests từ một AI API.

Giới hạn max_tokens để một câu trả lời dài không chạy mất kiểm soát#

Timeout dừng việc chờ. Nó không làm model viết ít hơn. Không có max_tokens, một model với giới hạn câu trả lời 32.768 token có thể thoải mái viết một câu trả lời rất dài, và một câu trả lời dài là một câu trả lời chậm. Hãy đặt một giới hạn phù hợp với công việc: 800 cho một bản tóm tắt ticket, 5 cho một câu có/không.

Cái giá là một câu trả lời có thể bị cắt. Kiểm tra resp.choices[0].finish_reason: length nghĩa là giới hạn đã chạm trước khi model hoàn thành. Tại sao một câu trả lời AI dừng giữa câu? đề cập điều đó. Nếu bạn stream câu trả lời thay vào đó, những từ đầu tiên đến sớm và cảm giác chờ ngắn hơn, xem Streaming responses trong Python.

Retry ảnh hưởng gì đến hóa đơn#

Một lần retry là một request mới. Nếu một lần thử đã được xử lý, nó là một request riêng với token riêng của nó. Giả sử một bản tóm tắt ticket là 1.500 token vào và 400 ra: một request tốn $0.000063 theo giá hiện tại, và bảng giá đầy đủ có các model khác. Giá cố định theo từng model, không có giờ cao điểm, nên một lần retry lúc 3 giờ sáng tốn bằng một lần lúc giữa trưa.

Di chuyển các thanh trượt theo batch của riêng bạn:

Máy tính chi phí

Một batch gồm 1.000 bản tóm tắt ticket

Token đầu vào mỗi yêu cầu: 1.500 · Token đầu ra mỗi yêu cầu: 400 · Số yêu cầu mỗi ngày: 1.000

Mô hìnhMỗi ngàyMỗi tháng
GLM-5.3-Flash$0.046$1.38
DeepSeek-V4-Flash$0.0625$1.88
MiniMax-M2.7$0.135$4.05

Bảng hiển thị một lượt. Nếu một phần mười batch cần lần thử thứ hai, cộng thêm một phần mười. Retry là bảo hiểm rẻ khi thất bại hiếm, và là một thói quen đắt đỏ khi bạn đặt max_retries=10 với một endpoint liên tục thất bại.

Trước khi bạn ship#

Danh sách kiểm tra

Timeout và retry, sẵn sàng cho production