Cách tôi giảm 50% hóa đơn OpenAI nhờ sử dụng Batch API

AI tutorial - IT technology blog
AI tutorial - IT technology blog

Hóa đơn 5.000 USD hàng tháng lẽ ra không nên có

Quý trước, nhóm của tôi đã gặp phải một bài toán khó. Chúng tôi được giao nhiệm vụ phân loại 2,4 triệu phản hồi của khách hàng cho một đối tác bán lẻ lớn. Nếu sử dụng endpoint chat.completions tiêu chuẩn của OpenAI, chi phí dự kiến lên tới 5.100 USD. Tệ hơn nữa, chúng tôi liên tục chạm ngưỡng giới hạn tốc độ (TPM) sau mỗi vài phút, khiến pipeline nạp dữ liệu bị đình trệ liên tục.

Dự án này không yêu cầu khắt khe về thời gian. Chúng tôi không cần phản hồi trong vòng 500 mili giây; chúng tôi chỉ cần dữ liệu được xử lý xong trong vòng 24 giờ. Bằng cách chuyển khối lượng công việc sang OpenAI Batch API, hóa đơn của chúng tôi đã giảm xuống chỉ còn đúng 2.550 USD. Những cơn đau đầu về rate limit cũng biến mất ngay lập tức. Nếu bạn đang mở rộng các tính năng AI với một ngân sách hạn hẹp, đây là sự thay đổi kiến trúc quan trọng nhất mà bạn có thể thực hiện.

“Cái giá của sự tức thì” đang bào mòn lợi nhuận của bạn

Hầu hết các nhà phát triển đều mặc định sử dụng các lệnh gọi API đồng bộ (synchronous). Bạn gửi một yêu cầu, đợi phản hồi và chuyển sang tác vụ tiếp theo. Đây là lựa chọn đúng đắn cho chatbot. Tuy nhiên, nó lại là một gánh nặng lớn đối với các tác vụ chạy ngầm như làm giàu dữ liệu (data enrichment), tạo nội dung SEO quy mô lớn hoặc phân tích dữ liệu lịch sử.

OpenAI tính phí cao hơn cho các phản hồi tức thì vì họ phải dự trữ tài nguyên tính toán cho bạn ngay lập tức. Các lệnh gọi đồng bộ cũng bị ràng buộc bởi các giới hạn Tier nghiêm ngặt. Nếu bạn cố gắng đẩy 1.000.000 prompt qua một đường ống tiêu chuẩn, bạn sẽ mất nhiều thời gian để xử lý lỗi 429: Too Many Requests hơn là tạo ra giá trị thực tế. Batching giải quyết vấn đề này bằng cách cho phép OpenAI chạy các công việc của bạn trong khung giờ “thấp điểm” của họ.

So sánh các lựa chọn: Sync vs. Batch

Trước khi cấu trúc lại pipeline, tôi đã cân nhắc ba chiến lược khác nhau để xử lý 2,4 triệu dòng dữ liệu:

  • API đồng bộ tiêu chuẩn (Standard Sync API): Giá gốc (5,00 USD cho mỗi 1 triệu token với GPT-4o). Khó khăn lớn do giới hạn rate limit. Kết quả trả về tức thì.
  • Multi-threading/AsyncIO: Thông lượng tốt hơn nhưng vẫn phải trả giá gốc. Đòi hỏi logic thử lại (retry logic) phức tạp và cơ chế exponential backoff để xử lý các đợt bùng phát rate limit.
  • OpenAI Batch API: Giảm giá 50% (2,50 USD cho mỗi 1 triệu token với GPT-4o). Có các nhóm rate limit riêng biệt và cực lớn. Kết quả trả về trong vòng 24 giờ.

Triển khai: Quy trình làm việc với Batch

Batch API không sử dụng cấu trúc JSON POST thông thường cho bản thân yêu cầu. Thay vào đó, bạn tải lên một tệp .jsonl, trong đó mỗi dòng là một yêu cầu riêng lẻ. OpenAI sẽ xử lý chúng ở chế độ nền bất cứ khi nào họ có tài nguyên dư thừa.

1. Tạo tệp yêu cầu (.jsonl)

Mỗi yêu cầu cần có một custom_id. Điều này cực kỳ quan trọng vì tệp đầu ra sẽ không nhất thiết phải theo đúng thứ tự như đầu vào của bạn. Bạn sẽ sử dụng ID này để ánh xạ kết quả trở lại cơ sở dữ liệu của mình.

{"custom_id": "user-rev-101", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4o-mini", "messages": [{"role": "system", "content": "Phân loại cảm xúc."}, {"role": "user", "content": "Sản phẩm tuyệt vời!"}]}}
{"custom_id": "user-rev-102", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4o-mini", "messages": [{"role": "system", "content": "Phân loại cảm xúc."}, {"role": "user", "content": "Hàng bị hỏng khi giao đến."}]}}

2. Tải lên và kích hoạt Job

Khi tệp của bạn đã sẵn sàng, hãy sử dụng Python SDK để gửi nó đến OpenAI. Đây là đoạn mã mẫu tôi sử dụng trong môi trường thực tế:

import openai

client = openai.OpenAI(api_key="your_api_key")

# Tải tệp JSONL lên máy chủ của OpenAI
batch_file = client.files.create(
  file=open("tasks.jsonl", "rb"),
  purpose="batch"
)

# Bắt đầu job xử lý batch
batch_job = client.batches.create(
  input_file_id=batch_file.id,
  endpoint="/v1/chat/completions",
  completion_window="24h"
)

print(f"Job đã bắt đầu: {batch_job.id}")

3. Lấy dữ liệu

Bạn không cần phải kiểm tra API từng giây. Tôi thường thiết lập một tập lệnh đơn giản để kiểm tra trạng thái sau mỗi 15 phút. Khi trạng thái là completed, bạn có thể tải xuống tệp kết quả.

# Kiểm tra xem job đã hoàn thành chưa
job_status = client.batches.retrieve(batch_job.id)

if job_status.status == "completed":
    # Lấy dữ liệu phản hồi thực tế
    results = client.files.content(job_status.output_file_id)
    with open("final_results.jsonl", "wb") as f:
        f.write(results.read())
    print("Xử lý hoàn tất. Kết quả đã được lưu.")

Những bài học xương máu từ thực tế

Sau khi xử lý hàng chục triệu token, tôi đã nhận thấy một số quy luật mà tài liệu hướng dẫn thường bỏ qua. Đầu tiên, khung thời gian 24 giờ chỉ là một ước tính thận trọng. Hầu hết các batch gồm 50.000 yêu cầu của tôi đều hoàn thành trong vòng chưa đầy 45 phút. Tuy nhiên, đừng bao giờ hứa với các bên liên quan về thời gian hoàn thành dưới một giờ đối với các batch job.

Thứ hai, kiểm tra tính hợp lệ (validation) là người bạn tốt nhất của bạn. Nếu một dòng duy nhất trong tệp JSONL 500MB của bạn bị thiếu một dấu phẩy, toàn bộ batch job có thể thất bại ngay cả trước khi bắt đầu. Hiện tại, tôi sử dụng một tập lệnh Pydantic cục bộ để xác thực mọi dòng trước khi nhấn nút tải lên. Nó giúp tiết kiệm hàng giờ đồng hồ bực bội.

Xử lý thành công một phần

Không phải mọi yêu cầu trong một batch đều sẽ thành công. Nếu một prompt cụ thể kích hoạt bộ lọc nội dung hoặc gặp lỗi máy chủ nội bộ, OpenAI vẫn sẽ cung cấp kết quả cho 9.999 yêu cầu còn lại. Mã hậu xử lý của bạn phải kiểm tra status_code bên trong tệp đầu ra cho mọi custom_id.

Phân tích Chi phí – Lợi ích

Chỉ số API Tiêu chuẩn (GPT-4o) Batch API (GPT-4o)
Giá mỗi 1 triệu Token đầu vào $5.00 $2.50
Giá mỗi 1 triệu Token đầu ra $15.00 $7.50
Giới hạn Rate Limit Chia sẻ/Hạn chế Riêng biệt/Khối lượng lớn
Thời gian phản hồi Tức thì Lên đến 24 giờ

Lời kết: Kiến trúc Hot vs. Cold

Việc áp dụng Batch API đã thay đổi cách tôi thiết kế các hệ thống AI. Hiện tại, tôi chia các tác vụ thành hai nhóm. Các tác vụ “Hot” (nóng), như người dùng đặt câu hỏi trong hộp chat, sẽ sử dụng API đồng bộ. Các tác vụ “Cold” (lạnh), như tạo báo cáo hàng tuần hoặc gắn thẻ cơ sở dữ liệu, sẽ chuyển sang Batch API. Sự phân chia đơn giản này cho phép chúng tôi tăng gấp 10 lần khả năng xử lý mà không làm tăng ngân sách hàng tháng. Nếu bạn vẫn đang sử dụng các lệnh gọi đồng bộ cho dữ liệu khối lượng lớn, về cơ bản bạn đang lãng phí một nửa ngân sách của mình.

Share: