اگر امروز برای هر عملیات سادهی دستهبندی از یک مدل زبانی بزرگ هزینه میپردازید، احتمالاً دارید منابع پردازشی خود را هدر میدهید. تصور کنید سیستمی داشته باشید که در آن هزینه پردازش هر درخواست، دقیقاً با سطح دشواری آن برابر باشد. در چنین سیستمی، پردازش یک ایمیل بدیهی هیچ هزینهای ندارد و تنها پیامهای بسیار مبهم هستند که باعث فراخوانی گرانقیمت مدلهای ابری میشوند. این معماری باعث میشود که مدل زبانی بزرگ (LLM) از موتور اصلی پردازش، به یک «آخرین راه نجات» یا یک سیستم پشتیبان (Fail-safe) تبدیل شود.
جولز روبینو (Jules Robineau)، مهندس ارشد بکاند و فریلنسر ساکن پاریس، با بازطراحی یک عامل دستهبندی ایمیل نشان داد که میتوان وابستگی به GPU را به شدت کاهش داد و زمان استنتاج را به حداقل رساند. او یک دیمون (Daemon) ساخت که پیامهای جدید را میخواند و آنها را در دستههایی نظیر: کاری (Work)، اعلان (Notification)، خبرنامه (Newsletter)، تبلیغاتی (Promo) و چند دستهی دیگر قرار میدهد. این پروژه یک دموی ساده نبود، بلکه یک تغییر در سطح تولید (Production Shift) برای سیستمی بود که باید به صورت مداوم پیامها را ردهبندی و بایگانی کند. طبق گزارش او، این سیستم بهجای استفاده از یک مدل ۷ میلیارد پارامتری برای تفکیک ایمیلها، از یک ساختار طبقهبندی سهلایه استفاده میکند.
این رویکرد، واکنشهای غریزی صنعت در سال ۲۰۲۶ را به چالش میکشد؛ جایی که توسعهدهندگان عادت کردهاند هر قابلیت تولیدی را به یک مدل بزرگ متصل کنند. بسیاری از برنامهنویسان در حال حاضر با هر ورودی به گونهای برخورد میکنند که گویی یک وظیفهی «زاینده» (Generative) است، حتی زمانی که هدف صرفاً انتخاب یک برچسب از یک لیست ثابت و محدود است. طبقهبندی — شبیه به این است که یک نامه را در یکی از پوشههای موجود روی میز بیندازید — کاملاً با تولید متن متفاوت است. تولید متن یک فرآیند خلق است، در حالی که طبقهبندی صرفاً انتخاب یک گزینه از یک لیست کوتاه و پایدار است. این روند مشابه ریسکهای «کدنویسی بر اساس حس» (Vibe Coding) است که پیشتر در مورد اکوسیستم نرمافزارهای آزاد بررسی کردیم؛ جایی که سختگیری معماری فدای راحتیِ نوشتن یک پرامپت ساده میشود و دقت مهندسی قربانی میشود.
سازوکار آبشاری سهلایه
سیستم روبینو هر ایمیل را از سه لایه متوالی عبور میدهد. درخواست در اولین لایهای که پاسخ مطمئن بدهد متوقف میشود. این منطق تضمین میکند که هر لایه فقط مواردی را مدیریت کند که لایههای قبلی و ارزانتر نتوانستهاند درباره آنها تصمیم بگیرند. در واقع، جریان داده تنها زمانی به لایهی بعدی منتقل میشود که لایهی فعلی نتواند پاسخی با سطح اطمینان کافی ارائه دهد.
۱. قوانین قطعی (Deterministic Rules): این لایه فرستندهها و دامنههای شناختهشده را مدیریت میکند. از آنجایی که یک آدرس خبرنامه شناختهشده همیشه خبرنامه است و یک هشدار پلتفرم همیشه یک اعلان است، این موارد در همان دمِ در تصمیمگیری میشوند. این مرحله نیاز به هیچگونه حدس زدن، فراخوانی مدل و هزینه ندارد و سریعترین روش ممکن است. این رویکرد در واقع تکراری از استراتژی استفاده از کدهای قطعی در برابر LLM برای کاهش خطاهای عملیاتی است که پیشتر در محیطهای پیچیدهای چون CRM بررسی شده بود.
۲. مدل ML کوچک: برای موارد مبهم، یک طبقهبند سبک روی CPU اجرا میشود. این لایه حجم اصلی ترافیک میانرده را با استنتاج (Inference) زیر یک میلیثانیه مدیریت میکند. این لایه شبیه به یک دستیار سریع است که فقط نگاهی به کلمات کلیدی میاندازد تا تصمیم بگیرد.
۳. مدل زبانی بزرگ (LLM): تنها ایمیلهای بسیار پیچیده و «دم بلند» (Uncertain Tail) به مدلهای ابری میرسند. این یعنی نیازی به روشن نگه داشتن دائمی GPU نیست و سربار مدیریت یک کانتینر اضافی برای مدلهای محلی حذف میشود.
به نقل از مستندات کد این پروژه، جریان تصمیمگیری دقیقاً به این صورت است:
func (c *Classifier) decide(m Message) Decision {
// 1. Deterministic rules: obvious senders, decided at the door.
if d := preClassify(m); d != nil {
return *d
}
// 2. Small ML model: sub-millisecond, on CPU.
pred := c.ml.Predict(m)
if pred.Confidence >= c.threshold {
return decisionFrom(pred)
}
// 3. LLM last: only the uncertain tail reaches the cloud.
return c.classifyWithLLM(m)
}
مدل کوچک: TF-IDF و رگرسیون لجستیک
روبینو مدل اولیه خود — یک مدل Qwen 2.5 7B که توسط Ollama سرویس میشد — را با دو تکنیک کلاسیک جایگزین کرد: TF-IDF (Term Frequency-Inverse Document Frequency) برای بردارسازی متن و رگرسیون لجستیک (Logistic Regression) برای طبقهبندی.
- سازوکار TF-IDF: این روش متن را به اعداد تبدیل میکند؛ به این صورت که به هر کلمه بر اساس میزان تکرار آن در یک سند و میزان کمیابی آن در کل مجموعه داده، یک وزن اختصاص میدهد. این کار باعث میشود کلمات کلیدی اثرگذارتر شوند.
- رگرسیون لجستیک: این مدل ریاضی ساده یاد میگیرد که بر اساس آن وزنهای عددی حاصل از TF-IDF، مرزهایی بین دستهها رسم کند و آنها را از یکدیگر جدا نماید.
او این مدل را با استفاده از کتابخانه scikit-learn در پایتون، روی مجموعهای از نزدیک به ۵۸۰۰ ایمیل برچسبدار در شش دسته مختلف آموزش داد. خروجی این فرآیند، یک فایل JSON ساده با حجم تنها ۲.۴ مگابایت است. در مقایسه با چندین گیگابایت حافظه مورد نیاز برای یک مدل ۷ میلیارد پارامتری، این ردپای حافظهای (Footprint) بسیار کوچک اجازه میدهد سیستم در یک ایمیج عملیاتی «بدون توزیع» (Distroless) اجرا شود؛ یعنی ایمیجی که هیچ شل (Shell)، ابزار سیستمی یا وابستگی به C ندارد و امنیت و سرعت بسیار بالایی دارد.

صحت و عملکرد
بر اساس دادههای ارائه شده، این مدل کوچک در اعتبارسنجی متقابل ۵-لایه (5-fold cross-validation) به صحت ۸۱٪ رسید. اعتبارسنجی متقابل یک معیار صادقانه است که در آن دادهها به پنج بخش تقسیم میشوند؛ مدل روی چهار بخش آموزش دیده و روی بخش پنجم آزمایش میشود و این چرخه تا زمانی که تمام بخشها تست شوند تکرار میگردد تا اطمینان حاصل شود که مدل بیشبرازش (Overfitting) نکرده است.
- تنوع دستهها: مدل در دستههای شایع و واضح بسیار قوی عمل میکند و دقتی در حدود ۰.۸۸ دارد، اما در دستههای نادر و مبهم که مرزهای کمتری دارند، ضعیفتر است و دقتی حدود ۰.۶۲ ارائه میدهد.
- آستانه اطمینان: روبینو یک آستانه اطمینان (Confidence Threshold) معادل ۰.۶۰ پیاده کرد. اگر اطمینان مدل برای یک پیشبینی خاص بالای ۰.۶۰ باشد، سیستم به آن اعتماد کرده و پاسخ را میپذیرد.
- ارجاع به بالا (Escalation): اگر اطمینان مدل به زیر ۰.۶۰ سقوط کند، ایمیل بهطور خودکار به LLM ارجاع داده میشود تا تصمیم نهایی گرفته شود.
اگرچه ۸۱٪ برای یک مدل مستقل ممکن است متوسط به نظر برسد، اما در یک ساختار آبشاری بسیار کارآمد است. قوانین موارد ساده را بدون خطا مدیریت میکنند، مدل کوچک حجم انبوه موارد متوسط را جذب میکند و LLM تنها با موارد حقیقتاً مبهم روبروست. بدین ترتیب، فراخوانیهای گرانقیمت نادر میشوند زیرا سیستم از ساختاری پیروی میکند که در آن هزینه متناسب با دشواری است و مدلهای گرانقیمت فقط برای سختترین مسائل به کار میروند.
حل چالش توکنسازی
یک مانع فنی جدی در این ساختار ترکیبی، تضمین این است که توکنسازی (Tokenization) — یعنی نحوه تبدیل متن به واحدهای شمارش مدل — بین محیط آموزش پایتون و محیط استنتاج Go دقیقاً یکسان باشد. توکن تکهای از متن است که مدل میشمارد و معمولاً یک کلمه یا بخشی از آن است. اگر نحوه شکستن متن (Split) در Go حتی یک بایت با پایتون تفاوت داشته باشد، مدل توکنهایی را میبیند که هرگز در مرحله آموزش یاد نگرفته است و این منجر به کاهش مخفیانه و خاموشِ دقت مدل میشود که ردیابی آن بسیار دشوار است.
برای جلوگیری از این فاجعه، روبینو یک توکنساز دستی نوشت که در هر دو زبان دقیقاً تکرار شده است. او بهجای استفاده از کتابخانههای خارجی یونیکد که ممکن است رفتار متفاوتی در زبانهای مختلف داشته باشند، از یک عبارت منظم خاص ([a-z0-9]{2,}) و یک جدول نگاشت سفارشی برای حذف اکسانها استفاده کرد (مثلاً تبدیل 'é' و 'è' به 'e' و 'ç' به 'c') تا متن را استاندارد کند.
var fold = map[rune]string{'é': "e", 'è': "e", 'ç': "c" /* full table */}
var wordRE = regexp.MustCompile(`[a-z0-9]{2,}`)
func wordTokens(s string) []string {
var b strings.Builder
for _, r := range strings.ToLower(s) {
if rep, ok := fold[r]; ok {
b.WriteString(rep)
} else {
b.WriteRune(r)
}
}
return wordRE.FindAllString(b.String(), -1)
}
اکنون در هر بار فرآیند ساخت (Build)، یک تست تطابق (Parity Test) اجرا میشود؛ اگر توکنسازهای پایتون و Go واگرا شوند، بیلد بلافاصله متوقف میشود. یک بیلد قرمز (شکستخورده) بسیار ترجیح داده میشود تا دقتی که بدون هشدار ذوب میشود و باعث رفتارهای غیرقابل پیشبینی در تولید گردد.
مقابله با پسرفتهای خاموش
مدل از طریق یک حلقه مداوم بهبود مییابد: وقتی عامل ایمیلی را اشتباه دستهبندی میکند، روبینو آن را دستی به پوشه درست منتقل کرده و یک برچسب جدید برای آموزش مجدد ایجاد میکند. با این حال، او متوجه شد که «پسرفتهای خاموش» (Silent Regressions) خطرناکتر از کرشهای سیستم هستند. در یک جلسه مرتبسازی دستی، تعداد نمونههای یک دسته چنان کاهش یافت که به حداقل تعداد مورد نیاز برای یادگیری رسید و در نتیجه، فرآیند آموزش مجدد آن دسته را در سکوت حذف کرد، بدون اینکه هیچ خطایی صادر شود.
برای حل این مشکل، او یک حفاظ (Guard-rail) به اسکریپت آموزش اضافه کرد که اجازه حذف هیچ دستهای را نمیدهد. اکنون اسکریپت لیستی از برچسبهای مورد انتظار را دریافت میکند:
python train.py --in labeled.jsonl --out model.json --expect-labels "work,notification,newsletter,promo,home"
اگر آموزشدهنده متوجه شود که یک کلاس درخواستی (مانند "personal") به دلیل کمبود داده حذف خواهد شد، فرآیند متوقف شده و به کاربر هشدار میدهد. این تضمین میکند که سیستم در صورت بروز پسرفت، بهطور عمدی «سروصدا» ایجاد کند تا مهندس بتواند سریعاً دادههای آن دسته را تقویت کند.
جایگاه واقعی LLM در معماری
این معماری LLM را حذف نمیکند، بلکه آن را به وظایفی میسپارد که واقعاً برای آنها ساخته شده است. روبینو تفاوت بین «مسائل بسته» و «مسائل باز» را اینگونه تعریف میکند:
- مسائل بسته (Closed Problems): طبقهبندی ایمیل یک مسئله بسته با چند پاسخ محدود و ثابت است. این کار توسط قوانین و مدل کوچک مدیریت میشود زیرا نیاز به خلاقیت ندارد.
- مسائل باز (Open Problems): نوشتن یک پاسخ انسانی، طبیعی و متناسب با لحن کاربر، یک مسئله باز است. تولید زبان آزاد دقیقاً همان کاری است که یک مدل بزرگ بهتر از هر چیز دیگری انجام میدهد.
با رزرو مدلهای بزرگ برای تولید متن و استفاده از یک طبقهبند کوچک برای تفکیک، سیستم هم در هزینه و هم در سرعت بهینهسازی میشود. فشار پردازشی از GPU به CPU منتقل شده و صورتحساب ابری تنها برای تولیدات با ارزش بالا (Generation) هزینه میشود، نه برای برچسبزنیهای کمارزش (Labeling) که میتوان آنها را با مدلهای کلاسیک انجام داد.
این تغییر، فرض بنیادی پشتهی AI را تغییر میدهد: هدف یافتن قدرتمندترین مدل نیست، بلکه تطبیق هزینه ابزار با دشواری وظیفه است. این رویکرد، AI را از یک مرکز هزینه «جعبه سیاه» به یک خط لوله مهندسی لایهبندی شده تبدیل میکند که در آن هر ابزار در جایگاه درست خود قرار دارد.
چکلیست پیادهسازی LLM
روبینو پیشنهاد میکند پیش از آنکه به صورت غریزی از یک مدل بزرگ استفاده کنید، وظیفه خود را با این پرسشها بررسی کنید:
- آیا وظیفه دارای مجموعه کوچکی از پاسخهای ثابت است؟ (اگر بله، این یک طبقهبندی است، نه یک وظیفه LLM).
- آیا میتوانید برای موارد بدیهی قوانینی بنویسید؟ (این کار را ابتدا انجام دهید زیرا رایگان و سریع است).
- آیا نمونههای برچسبدار دارید؟ (اگر بله، ممکن است یک مدل ML کوچک کافی باشد).
- آیا مدل باید زبان آزاد را بفهمد یا فقط یک برچسب را انتخاب کند؟
- آیا میتوانید دقت واقعی را با استفاده از اعتبارسنجی متقابل (Cross-validation) اندازه بگیرید؟
- آیا مدل کوچک بدون نیاز به GPU و روی CPU در داخل ایمیج عملیاتی شما اجرا میشود؟
- آیا توکنساز شما در مرحله آموزش و استنتاج، بایت به بایت یکسان است؟
- آیا حفاظی برای جلوگیری از پسرفت خاموش در هنگام آموزش مجدد وجود دارد؟
- آیا LLM را صرفاً برای تولید زبان و حل مسائل باز نگه داشتهاید؟
برای جلوگیری از هزینههای اضافی در قابلیت AI بعدی خود، وظایفتان را ممیزی کنید: اگر در حال انتخاب یک برچسب از یک لیست ثابت هستید، شما در حال انجام «طبقهبندی» هستید، نه «تولید». با قوانین شروع کنید، به مدل کوچک بروید و LLM را در آخرین مرحله قرار دهید تا فقط زمانی فعال شود که واقعاً به قدرت پردازشیاش نیاز است.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو