uttapen

مدل‌های ارزان ولی خوب: DeepSeek، Qwen و Gemini Flash برای پروژه‌های پرحجم

قیمت تومانی واقعی مدل‌های ارزان به‌ازای یک میلیون توکن، هزینهٔ ۱۰٬۰۰۰ درخواست در دو سناریو، و کدی که هزاران درخواست را با کنترل هزینه اجرا می‌کند.

۱۶ شهریور ۱۴۰۵ · ۱۰ دقیقه مطالعه · تیم یوتاپن

وقتی یک بار در روز از مدل سؤال می‌پرسی، قیمت مهم نیست. وقتی هر شب باید ده هزار نظر مشتری را دسته‌بندی کنی، صد هزار عنوان محصول را تمیز کنی یا برای هر تیکت پشتیبانی خلاصه بسازی، قیمت تنها چیزی است که مهم است — به شرطی که کیفیت از یک حداقل قابل قبول پایین نیاید. این مقاله دربارهٔ همین منطقه است: مدل‌هایی که ده تا پنجاه برابر ارزان‌تر از مدل‌های پرچم‌دارند و برای وظیفه‌های تکراری و ساختارمند به‌اندازهٔ کافی خوب.

سه خانواده را بررسی می‌کنیم: DeepSeek، Qwen و Gemini Flash؛ و برای مرجع، چند مدل کوچک OpenAI را هم کنار آن‌ها می‌گذاریم. همهٔ قیمت‌ها قیمت نهایی تومانی از کاتالوگ واقعی مدل‌ها در روز نگارش‌اند؛ چون کاتالوگ روزانه به‌روز می‌شود، عدد دقیق لحظهٔ استفاده را از GET /v1/models یا صفحهٔ هر مدل بگیر.

«ارزان ولی خوب» یعنی چه

ارزانی را با یک عدد می‌سنجیم: تومان به‌ازای یک میلیون توکن، جداگانه برای ورودی و خروجی. خوب بودن را نمی‌شود با یک عدد سنجید، ولی برای وظیفه‌های پرحجم معمولاً این‌ها کافی است:

  • دستور کوتاه را دقیق اجرا کند و از قالب خواسته‌شده (مثلاً فقط یک کلمه، یا JSON) خارج نشود.
  • فارسی را بفهمد و فارسی روان بنویسد، حتی اگر ادبی نباشد.
  • در ورودی‌های بلند (چند هزار توکن) گم نشود.
  • روی هزاران درخواست رفتار یکنواخت داشته باشد؛ نوسان کیفیت برای batch از خودِ کیفیت متوسط بدتر است.

نکتهٔ مهم: ارزان‌ترین مدل هر خانواده معمولاً نسخهٔ «flash» یا «lite» است که برای سرعت و قیمت بهینه شده، نه برای استدلال پیچیده. برای کاری که نیاز به چند مرحله فکر کردن دارد، به‌جای این‌ها به مدل‌های متوسط همان خانواده نگاه کن.

جدول قیمت تومانی به‌ازای یک میلیون توکن

این جدول خروجی مستقیم اسکریپتی است که پایین‌تر می‌بینی. ستون آخر هزینهٔ ده هزار درخواست در سناریوی «دسته‌بندی» است: هر درخواست ۸۰۰ توکن ورودی (system prompt به‌علاوهٔ متن) و ۲۰۰ توکن خروجی.

مدلcontextورودی (تومان / ۱M)خروجی (تومان / ۱M)۱۰٬۰۰۰ درخواست
qwen/qwen3.7-flash۱٬۰۰۰٬۰۰۰۳٬۹۵۶۱۷٬۱۴۴۶۵٬۹۳۸
qwen/qwen3-30b-a3b-instruct-2507۲۶۲٬۱۴۴۶٬۳۵۰۲۵٬۴۵۸۱۰۱٬۷۱۵
deepseek/deepseek-v4-flash۱٬۰۴۸٬۵۷۶۱۰٬۵۷۹۲۱٬۱۵۸۱۲۶٬۹۴۸
openai/gpt-5-nano۴۰۰٬۰۰۰۶٬۵۹۴۵۲٬۷۵۰۱۵۸٬۲۵۰
google/gemini-2.5-flash-lite۱٬۰۴۸٬۵۷۶۱۳٬۱۸۸۵۲٬۷۵۰۲۱۱٬۰۰۰
openai/gpt-4.1-nano۱٬۰۴۷٬۵۷۶۱۳٬۱۸۸۵۲٬۷۵۰۲۱۱٬۰۰۰
deepseek/deepseek-v3.2۱۶۳٬۸۴۰۳۵٬۴۷۴۵۲٬۷۵۰۳۸۹٬۲۹۵
qwen/qwen3-coder-flash۱٬۰۰۰٬۰۰۰۲۵٬۷۱۶۱۲۸٬۵۷۸۴۶۲٬۸۸۱
deepseek/deepseek-chat۱۶۳٬۸۴۰۴۲٬۲۰۰۱۱۷٬۳۶۹۵۷۲٬۳۳۸
google/gemini-3.1-flash-lite۱٬۰۴۸٬۵۷۶۳۲٬۹۶۹۱۹۷٬۸۱۲۶۵۹٬۳۷۵
google/gemini-2.5-flash۱٬۰۴۸٬۵۷۶۳۹٬۵۶۲۳۲۹٬۶۸۸۹۷۵٬۸۷۵

برای اینکه مقیاس دستت بیاید، همان ده هزار درخواست با openai/gpt-5 حدود ۳٬۹۵۶٬۰۰۰ تومان و با anthropic/claude-sonnet-4.5 حدود ۷٬۱۱۷٬۰۰۰ تومان می‌شود. یعنی بین ارزان‌ترین ردیف جدول و یک مدل پرچم‌دار، بیش از صد برابر اختلاف است. این اختلاف آن‌قدر بزرگ است که تقریباً همیشه ارزش دارد اول با مدل ارزان امتحان کنی و فقط برای موارد fail به مدل گران بروی.

سناریوی دوم: خلاصه‌سازی متن بلند

در دسته‌بندی، ورودی و خروجی کوتاه‌اند و قیمت ورودی تعیین‌کننده است. در خلاصه‌سازی یا استخراج از متن بلند (مثلاً ۳٬۰۰۰ توکن ورودی و ۳۰۰ توکن خروجی)، وزن ورودی باز هم بیشتر می‌شود. چند نمونه برای ده هزار درخواست:

  • qwen/qwen3.7-flash: حدود ۱۷۰٬۰۰۰ تومان
  • openai/gpt-5-nano: حدود ۳۵۶٬۰۰۰ تومان
  • deepseek/deepseek-v4-flash: حدود ۳۸۱٬۰۰۰ تومان
  • google/gemini-2.5-flash: حدود ۲٬۱۷۶٬۰۰۰ تومان

جالب اینجاست که ترتیب نسبت به سناریوی اول کمی جابه‌جا شد؛ چون نسبت قیمت ورودی به خروجی در مدل‌ها یکسان نیست. gpt-5-nano ورودی خیلی ارزان ولی خروجی نسبتاً گران دارد، پس برای کارهای «بخوان زیاد، بنویس کم» جذاب‌تر است تا کارهای تولید متن. قبل از انتخاب، سناریوی خودت را با همین فرمول حساب کن؛ اسکریپت زیر این کار را برایت انجام می‌دهد.

اسکریپت جدول قیمت و برآورد هزینه

این اسکریپت بدون کلید کار می‌کند، چون /v1/models عمومی است. فهرست مدل‌ها و اعداد سناریو را عوض کن تا جدول مخصوص خودت را بگیری.

# prices.py — جدول قیمت تومانی مدل‌های ارزان + هزینهٔ ۱۰٬۰۰۰ درخواست
import os, httpx

BASE = os.getenv("UTTAPEN_BASE_URL", "https://api.uttapen.ir/v1")
CHEAP = [
    "deepseek/deepseek-v4-flash", "deepseek/deepseek-chat", "deepseek/deepseek-v3.2",
    "qwen/qwen3.7-flash", "qwen/qwen3-coder-flash", "qwen/qwen3-30b-a3b-instruct-2507",
    "google/gemini-2.5-flash-lite", "google/gemini-2.5-flash", "google/gemini-3.1-flash-lite",
    "openai/gpt-5-nano", "openai/gpt-4.1-nano",
]
# سناریوی نمونه: هر درخواست ۸۰۰ توکن ورودی (system + متن) و ۲۰۰ توکن خروجی
N, IN_TOK, OUT_TOK = 10_000, 800, 200

models = {m["id"]: m for m in httpx.get(f"{BASE}/models", timeout=30).json()["data"]}
rows = []
for mid in CHEAP:
    p = models[mid]["uttapen_pricing"]
    pin, pout = float(p["prompt_toman_per_1m"]), float(p["completion_toman_per_1m"])
    per_req = (IN_TOK * pin + OUT_TOK * pout) / 1_000_000
    rows.append((mid, models[mid]["context_length"], pin, pout, per_req * N))

print(f"{'model':36} {'ctx':>9} {'in/1M':>9} {'out/1M':>9} {'10k req':>12}")
for mid, ctx, pin, pout, total in sorted(rows, key=lambda r: r[-1]):
    print(f"{mid:36} {ctx:>9,} {pin:>9,.0f} {pout:>9,.0f} {total:>12,.0f}")

برآورد قبل از اجرا خوب است، ولی عدد واقعی چیز دیگری است: تعداد توکن فارسی معمولاً بیشتر از چیزی است که حدس می‌زنی و بعضی مدل‌ها پرحرف‌ترند. برای همین در اجرای واقعی، هزینه را از هدر پاسخ بخوان نه از برآورد.

اجرای هزاران درخواست بدون اینکه کیف پول را خالی کنی

اجرای batch سه مشکل عملی دارد: محدودیت نرخ، تمام شدن موجودی وسط کار، و خطاهای گذرای upstream. اسکریپت زیر هر سه را هندل می‌کند. max_retries=0 عمدی است: می‌خواهیم retry دست خودمان باشد تا وقتی 402 (موجودی ناکافی) گرفتیم، ده بار همان درخواست را تکرار نکنیم.

# batch.py — اجرای هم‌زمان هزاران درخواست ارزان با کنترل نرخ و جمع هزینهٔ واقعی از هدرها
import asyncio, os, json
from openai import AsyncOpenAI, RateLimitError, APIStatusError

client = AsyncOpenAI(
    base_url=os.getenv("UTTAPEN_BASE_URL", "https://api.uttapen.ir/v1"),
    api_key=os.environ["UTTAPEN_API_KEY"],
    max_retries=0,  # retry را خودمان کنترل می‌کنیم تا ۴۰۲ را تکرار نکند
)
MODEL = "deepseek/deepseek-v4-flash"
CONCURRENCY = 8          # زیر rate limit کلید (پیش‌فرض ۶۰ درخواست در دقیقه)
SYSTEM = "دسته‌بندی نظر مشتری. فقط یکی از این کلمات را برگردان: مثبت، منفی، خنثی."

async def classify(sem: asyncio.Semaphore, text: str, totals: dict) -> str | None:
    async with sem:
        for attempt in range(4):
            try:
                raw = await client.chat.completions.with_raw_response.create(
                    model=MODEL,
                    messages=[{"role": "system", "content": SYSTEM}, {"role": "user", "content": text}],
                    max_tokens=5, temperature=0,
                )
                totals["toman"] += float(raw.headers.get("X-Uttapen-Cost-Toman", "0"))
                totals["ok"] += 1
                return (raw.parse().choices[0].message.content or "").strip()
            except RateLimitError as e:  # 429 → صبر به اندازهٔ Retry-After
                await asyncio.sleep(int(e.response.headers.get("Retry-After", "5")))
            except APIStatusError as e:
                if e.status_code == 402:      # موجودی تمام شد: ادامه دادن بی‌فایده است
                    totals["stop"] = True
                    return None
                await asyncio.sleep(2 ** attempt)
        totals["failed"] += 1
        return None

async def main():
    reviews = [line.strip() for line in open("reviews.txt", encoding="utf-8") if line.strip()]
    sem = asyncio.Semaphore(CONCURRENCY)
    totals = {"toman": 0.0, "ok": 0, "failed": 0, "stop": False}
    labels = await asyncio.gather(*(classify(sem, r, totals) for r in reviews))
    json.dump(dict(zip(reviews, labels)), open("labels.json", "w"), ensure_ascii=False, indent=1)
    print(f"done={totals['ok']} failed={totals['failed']} cost={totals['toman']:,.1f} toman"
          + ("  (stopped: insufficient balance)" if totals["stop"] else ""))

asyncio.run(main())

چند تصمیم در این کد که ارزش توضیح دارد:

max_tokens=5 — وقتی فقط یک کلمه می‌خواهی، سقف خروجی را همان‌قدر بگذار. این هم هزینه را قطعی می‌کند و هم مقدار hold قبل از ارسال را کوچک نگه می‌دارد. اگر max_tokens نفرستی، سیستم برای hold یک پیش‌فرض بزرگ (۴٬۰۹۶ توکن) در نظر می‌گیرد و با موجودی کم ممکن است 402 بگیری در حالی که هزینهٔ واقعی چند تومان است. جزئیات hold و settle در صفحهٔ قیمت‌ها آمده.

CONCURRENCY=8 — کلید پیش‌فرض ۶۰ درخواست در دقیقه اجازه دارد؛ با هشت درخواست موازی و پاسخ‌های زیر یک ثانیه راحت به این سقف می‌رسی، پس 429 را باید هندل کنی، نه اینکه از آن فرار کنی. سقف بالاتر برای batch های بزرگ قابل درخواست است؛ مستندات محدودیت نرخ را ببین.

جمع هزینه از هدرX-Uttapen-Cost-Toman عدد قطعیِ همان درخواست است که از کیف پول کم شده. جمع این هدرها در پایان اجرا دقیقاً برابر با کاهش موجودی است؛ نیازی به محاسبهٔ توکن نداری.

توقف روی 402 — وقتی موجودی تمام شد، ادامهٔ حلقه فقط لاگ خطا تولید می‌کند. اسکریپت پرچم stop را ست می‌کند تا بدانی خروجی کامل نیست و بعد از شارژ، فقط ردیف‌های None را دوباره بفرستی.

نکته‌هایی برای کیفیت مدل‌های ارزان

تجربهٔ عملی کار با این مدل‌ها چند قاعدهٔ ساده دارد که تفاوت بین «کار می‌کند» و «قابل اتکاست» است:

  1. قالب خروجی را قفل کن. بیشتر این مدل‌ها response_format با json_schema را پشتیبانی می‌کنند. برای دسته‌بندی، enum بده؛ برای استخراج، schema. مدل ارزان با schema اغلب از مدل گران بدون schema قابل اعتمادتر است.
  2. system prompt را کوتاه و فارسی بنویس. این مدل‌ها با دستور بلند و چندبخشی بدتر عمل می‌کنند. یک جمله بگو چه می‌خواهی و یک جمله بگو چه نمی‌خواهی.
  3. چند نمونهٔ واقعی (few-shot) اضافه کن — ولی حواست باشد هر نمونه در همهٔ ده هزار درخواست تکرار می‌شود و به هزینهٔ ورودی اضافه می‌کند. سه نمونهٔ کوتاه معمولاً کافی است.
  4. قبل از batch کامل، صد نمونهٔ تصادفی را با دو مدل اجرا کن و دستی مقایسه کن. اگر مدل ارزان‌تر بالای ۹۰ درصد با مدل گران هم‌نظر بود، بقیه را با همان بفرست و فقط موارد کم‌اطمینان را به مدل گران بده.
  5. context بزرگ را با تعداد توکن اشتباه نگیر. اینکه مدلی یک میلیون توکن context دارد، به این معنی نیست که هر درخواست را با آن پر کنی؛ هر توکن ورودی پول است و کیفیت در انتهای context معمولاً می‌افتد.
  6. تخفیف کش را در کاتالوگ ببین. بعضی مدل‌ها برای توکن‌های ورودیِ تکراری (مثل system prompt ثابت) قیمت جداگانه دارند که در uttapen_pricing با کلید cache_read_toman_per_1m می‌آید. اگر این کلید برای مدلت هست، system prompt و few-shot ثابت را اول پیام بگذار تا از آن بهره ببری.

مدل‌های رایگان کجای این داستان‌اند

در کاتالوگ چند مدل با پسوند :free هست که هزینهٔ صفر دارند و حتی بدون شارژ کیف پول قابل استفاده‌اند. برای نمونه‌سازی و مقایسهٔ کیفیت عالی‌اند، ولی برای batch پرحجم مناسب نیستند: هر کاربر سقف روزانهٔ ۵۰ درخواست دارد و ظرفیت رایگان upstream بین همه مشترک است، پس در ساعت‌های شلوغ 429 می‌گیری. برای کار جدی، همان ردیف اول جدول با چند ده هزار تومان کار تو را راه می‌اندازد.

محدودیت‌هایی که باید بدانی

مدل‌ها از طریق OpenRouter تأمین می‌شوند و مسیر درخواست از ایران به relay خارج از کشور و از آنجا به upstream می‌رود؛ این یعنی حدود صد میلی‌ثانیه تأخیر اضافه نسبت به فراخوانی مستقیم. برای batch شبانه بی‌اهمیت است، برای یک API لحظه‌ای شاید نه. قیمت‌های کاتالوگ روزانه به‌روز می‌شوند و قیمتی که در لحظهٔ شروع هر درخواست دیده‌ای برای همان درخواست ثابت می‌ماند، ولی جدول بالا ممکن است هفتهٔ بعد فرق کند. و در نهایت، مدل ارزان مدل ضعیف‌تری است؛ روی داده‌ات تست کن، نه روی حرف ما.

اگر می‌خواهی همین حالا جدول را روی مدل‌های خودت اجرا کنی، prices.py بدون کلید کار می‌کند. برای batch.py یک کلید از داشبورد بساز و با پنجاه هزار تومان شارژ شروع کن؛ با ارزان‌ترین ردیف جدول، این مبلغ برای چند هزار درخواست دسته‌بندی کافی است.

مقاله‌های مرتبط

می‌خواهی همین کد را اجرا کنی؟

با شماره موبایل ثبت‌نام کن، کیف پول را شارژ کن و کلید بگیر. ساخت کلید API ←