Product
کار دستهای نباید به یکپارچهسازی تازه نیاز داشته باشد
یک نقطهی پایانی، چه فهرست بفرستید چه فایل.
By Ehsan Gazar, ۲۸ شهریور ۱۴۰۵
ده هزار سند دارید که باید طبقهبندی شوند و کسی هم منتظر نتیجه نیست. اگر به ارائهدهندهها فرصت بدهید، هر کدام این کار را ارزانتر انجام میدهند. بعد مستندات دستهای (batch) آنها را میخوانید و میبینید هر بار یک یکپارچهسازی تازه است.
تخفیف بخش آسان ماجراست. آنچه برایتان گران تمام میشود، شکل کار است.
هر ارائهدهنده، راه ورود خودش
یک ارائهدهنده میخواهد اول یک فایل JSONL بارگذاری کنید و بعد با شناسهی همان فایل یک دسته بسازید. دیگری درخواستها را مستقیم در بدنهی یک JSON میگیرد و اصلاً مرحلهی فایل ندارد. نام وضعیتها فرق میکند، قالب نتایج فرق میکند و شیوهی گزارش شکستِ یک مورد تکی هم فرق میکند.
به این ترتیب، اجرای دستهای دیگر کلیدی نیست که برای کارهای غیرفوری روشنش کنید. برای هر ارائهدهنده به پروژهای جداگانه تبدیل میشود، با کد کلاینت خودش، حلقهی پرسوجوی وضعیت (polling) خودش و تجزیهگر خودش برای نتایج.
بیشتر تیمها این کار را یک بار، برای یک ارائهدهنده، انجام میدهند و دیگر سراغش نمیروند. کارهایی که میتوانستند با قیمت دستهای اجرا شوند همچنان با قیمت زنده اجرا میشوند، چون انتقالشان یعنی یک یکپارچهسازی دیگر.
مسئلهای که ارزش حل کردن دارد همین است، و قیمت پایینتر آن را حل نمیکند.
یک نقطهی پایانی، دو راه ورود
درگاه ما همهی دستهها را در یک نشانی میپذیرد، POST /v1/batches، و با همان کلیدی که همین حالا برای چت به کار میبرید. برای اینکه بگویید چه چیزی اجرا شود دو راه هست، و هر دسته دقیقاً از یکی از آنها استفاده میکند.
راه اول فهرستی در بدنهی درخواست است. این راه به کار اسکریپتی میآید که درخواستها را از قبل در حافظه دارد.
curl https://gateway.vatan.one/v1/batches \
-H "authorization: Bearer $VATAN_API_KEY" \
-H "content-type: application/json" \
-d '{
"model": "openrouter/openai/gpt-5-nano:batch",
"requests": [
{"messages": [{"role": "user", "content": "Classify: the invoice is wrong"}], "max_tokens": 50},
{"messages": [{"role": "user", "content": "Classify: my parcel never came"}], "max_tokens": 50}
]
}'
راه دوم فایل است. یک فایل JSONL بارگذاری میکنید که هر خطش یک درخواست است، و سپس آن را نام میبرید. این همان شکلی است که هر SDK شرکت OpenAI از طریق files.create و batches.create میفرستد. بنابراین کد دستهای موجود شما میتواند base URL خود را به سمت ما تغییر دهد و همان فراخوانیها را نگه دارد.
{"custom_id": "ticket-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "openrouter/openai/gpt-5-nano:batch", "messages": [{"role": "user", "content": "Classify: the invoice is wrong"}], "max_tokens": 50}}
{"custom_id": "ticket-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "openrouter/openai/gpt-5-nano:batch", "messages": [{"role": "user", "content": "Classify: my parcel never came"}], "max_tokens": 50}}
curl https://gateway.vatan.one/v1/files \
-H "authorization: Bearer $VATAN_API_KEY" \
-F purpose=batch -F file=@tickets.jsonl
# {"id": "file-...", "lines": 2, "status": "processed"}
curl https://gateway.vatan.one/v1/batches \
-H "authorization: Bearer $VATAN_API_KEY" \
-H "content-type: application/json" \
-d '{"input_file_id": "file-...", "endpoint": "/v1/chat/completions", "completion_window": "24h"}'
اگر هم فهرست بفرستید و هم فایل، درخواست رد میشود و سامانه یکی را بهجای شما انتخاب نمیکند. هر انتخابی نیمی از کسانی را که بهاشتباه هر دو را فرستادهاند غافلگیر میکرد.
آنچه پشت آن یکسان میماند
کار از هر راهی وارد شده باشد، به همان شیء دسته تبدیل میشود: همان وضعیتها، همان شمارشها و همان نتایج، که صفحهبهصفحه از /v1/batches/{id}/results خوانده میشوند.
دستهی تخفیفدار یکجا برای ارائهدهنده فرستاده میشود و هزینهاش یک بار کسر میشود. در میانهی کار هیچ لحظهای نیست که بودجهی شما بتواند «نه» بگوید؛ برای همین بدترین حالت پیش از ارسال هر چیزی قیمتگذاری و بررسی میشود. API دستهای خودِ ارائهدهنده این بخش را به عهدهی شما میگذارد.
قیمت در شناسهی مدل است. فقط شناسهای پذیرفته میشود که به :batch ختم شود، پس هیچ دستهای بیسروصدا با نرخ زنده اجرا نمیشود. اگر شناسهی عادی بفرستید، پاسخ یک رد است که شناسهی دستهای مناسب را نام میبرد.
نقطهی پایانی اعلام میشود، نه حدس زده. همین فراخوانی chat completion، بردارسازی (embedding)، تصویر، گفتار، رونویسی و ویدیو را میپذیرد و اعلام میکند کدام را اجرا میکند.
خبردار شدن از پایان کار
دستهای که باید مدام وضعیتش را بپرسید، دستهای است که باید به خاطر بسپارید. وبهوکی که batch.finished را دریافت میکند، هر بار که دستهای تمام شود خبردار میشود، همراه با شمارشها و اینکه کار چگونه تمام شد.
{
"id": "evt_...",
"event": "batch.finished",
"data": {"batch_id": "...", "api_status": "completed", "total": 2, "completed": 2, "failed": 0}
}
این پیام امضاشده میرسد و اگر گیرندهی شما در دسترس نباشد، دوباره فرستاده میشود. کد شما دسته را آغاز میکند، فراموشش میکند و وقتی خبر رسید نتایج را برمیدارد.
یکپارچهسازیای که یک بار مینویسید باید آخرین یکپارچهسازیای باشد که ارائهدهندهی بعدی از شما میخواهد.