PackUp یک سوئیت تصمیمگیری لجستیک برای تجارت الکترونیک است که سه مسئلهی هستهای زنجیرهی تأمین را در یک معماری ماژولار حل میکند: (1) تخصیص بهینهی سفارش از انبارهای مرکزی به مراکز توزیع با یک مدل برنامهریزی خطیِ عددصحیح مختلط (حلشده با PuLP/CBC) که علاوه بر جواب بهینه، ارزشهای سایهای و تحلیل حساسیت را نیز محاسبه میکند؛ (2) مسیریابی ناوگان با پنجرهی زمانی (VRPTW) با یک الگوریتم ژنتیکِ خودنوشته شامل رمزگذاری تور-غولآسا، رمزگشای Split و اپراتورهای OX/PMX؛ و (3) چیدمان سهبعدیِ بار با قید تخلیهی چندمقصدی. یک چتبات تحلیلی نتایج را به زبان طبیعی تفسیر میکند و میتواند سناریوهای «چهمیشد-اگر» را زنده اجرا کند.
در تجارت الکترونیک، هزینه و کیفیتِ خدمت تا حد زیادی در «لجستیک» تعیین میشود: کالا باید از انبارهای مرکزی به مراکز توزیع برسد، سپس با ناوگانی محدود و در بازههای زمانیِ توافقشده به درِ منزل مشتری تحویل شود و در هر وانت بهگونهای چیده شود که در هر توقف بدون جابهجاییِ بقیه قابل تخلیه باشد. این پروژه سه لایهی تصمیم را بهصورت یکپارچه مدل و حل میکند.
تخصیصِ سفارشات از انبارهای مرکزی به مراکز توزیعِ منطقهای جهت حداقلسازیِ کل هزینههای حملونقل.
مسیریابیِ ناوگانِ تحویلِ کالا دربِ منزل با پنجرهی زمانی (VRPTW) و ظرفیتِ محدودِ وانتبارها.
مسئلهی تخصیص یک برنامهی خطیِ عددصحیح مختلط است که با متغیرهای پیوستهی جریان و متغیرهای دودوییِ فعالسازیِ مسیر مدل میشود؛ برای این دسته، حلگرهای دقیق (شاخهوکران) جوابِ بهینهی سراسری و اطلاعاتِ دوگان (ارزش سایهای) میدهند. در مقابل، مسیریابیِ VRPTW از خانوادهی مسائلِ NP-Hard است؛ فضای جواب فاکتوریل رشد میکند و حلِ دقیق برای 50 مشتری عملی نیست. بنابراین از یک فراابتکارِ تکاملی (الگوریتم ژنتیک) استفاده میکنیم که در زمانِ معقول به جوابی نزدیکبهبهینه میرسد.
سناریوی نمونه بر پایهی جغرافیای واقعیِ ایران ساخته شده است: 3 انبار مرکزی (تهران، کرج، قم) و 8 مرکز توزیع (تهرانشرق، تهرانغرب، اصفهان، شیراز، مشهد، تبریز، رشت، اهواز) برای LP، و 50 مشتریِ تهران با پنجرههای زمانیِ متفاوت برای GA. فاصلهها با فرمول Haversine و هزینهها با نرخنامهی مستند محاسبه میشوند.
| بخش ارزیابی | بارم | تحققِ آن در PackUp |
|---|---|---|
| مدلسازی مهندسی صنایع | 3 | فرمولبندیِ کاملِ LP و GA در همین گزارش (بخشهای 3 و 4) |
| کدنویسی ماژول LP | 4 | backend/app/lp — مدل FCTP، دوگان، حساسیت، تورنادو |
| کدنویسی ماژول GA | 4 | backend/app/ga — تور-غولآسا، Split، OX/PMX، جستوجوی محلی |
| رابط کاربری (APP) | 3 | برنامهی React با نمودار، نقشه و صحنهی سهبعدی |
| چتبات هوشمند | 6 | backend/app/chat — 5 ابزارِ زنده روی نتایج |
سامانه بر پایهی برنامهنویسیِ ماژولار و جداسازیِ صریحِ «منطقِ نمرهدار» از «رابط» طراحی شده است. کلِ ماژولهای نمرهدار (LP، GA، چتبات) در پایتون و در بکاند پیاده شدهاند؛ رابط کاربری یک برنامهی React است که تنها از طریق یک API قراردادی با بکاند سخن میگوید.
ارزشِ سامانه در «اتصالِ» ماژولهاست نه در تکتکِ آنها. شکلِ زیر نشان میدهد دادهی سفارش از کجا وارد میشود، هر ماژول چه خروجیِ مشخصی به ماژولِ بعدی تحویل میدهد، و در انتها چه شاخصِ تصمیمِ مدیریتی تولید میشود. پیکانِ چیندارِ سمتِ راست، حلقهی بازخوردِ «چهمیشداگر» است: چتبات میتواند مدل را با ورودیِ جدید دوباره حل کند و کلِ زنجیره را بهروز کند.
زنجیرهی بالا در انتها به چهار عددی میرسد که یک مدیرِ عملیات با آنها تصمیم میگیرد. این اعداد مشتقِ مستقیمِ خروجیِ حلگرهایند و در داشبوردِ برنامه نیز دقیقاً به همین شکل نمایش داده میشوند؛ بنابراین گزارش و سامانه یک عدد واحد را روایت میکنند.
این ماژول تعیین میکند هر مرکز توزیع، تقاضای خود را از کدام انبار و به چه میزان تأمین کند تا کل هزینهی شبکه کمینه شود. مدل از نوع مسئلهی حملونقل با هزینهی ثابت (Fixed-Charge Transportation Problem) است که هم هزینهی متغیرِ حمل و هم هزینهی ثابتِ فعالسازیِ هر مسیر را در بر میگیرد.
| نماد | تعریف | نماد | تعریف |
|---|---|---|---|
| $i \in I$ | انبارهای مرکزی | $c_{ij}$ | هزینهی حملِ هر واحد در مسیر (i,j) |
| $j \in J$ | مراکز توزیع | $f_{ij}$ | هزینهی ثابتِ فعالسازیِ مسیر (i,j) |
| $S_i$ | ظرفیتِ عرضهی انبار i | $u_{ij}$ | سقفِ حملِ مسیر (اختیاری) |
| $D_j$ | تقاضای مرکز j | $\pi$ | جریمهی هر واحد کمبودِ پوشش |
متغیرِ کمکیِ کمبود $z_j$ تضمین میکند مدل همیشه شدنی بماند؛ اگر عرضه کفافِ تقاضا را ندهد، کمبود با جریمهی $\pi$ در تابع هدف ظاهر میشود و پیام روشنی به کاربر میدهد بهجای «ناشدنی» شدنِ مدل.
قیدِ (4) یک Big-M سفت است: بهجای یک مقدارِ بزرگِ دلخواه، از کوچکترین کرانِ ممکن ($\min$ ظرفیت، تقاضا و سقفِ مسیر) استفاده میشود تا شکافِ آزادسازیِ خطی کم و حل سریعتر شود. این قید جریان را تنها روی مسیرهای «فعال» ($y_{ij}{=}1$) مجاز میکند و هزینهی ثابت را واقعی میسازد.
مدل با کتابخانهی PuLP ساخته و با حلگرِ CBC (شاخهوکران) حل میشود. برای استخراجِ ارزشِ سایهای، پس از یافتنِ جوابِ صحیحِ بهینه، متغیرهای دودویی $y_{ij}$ تثبیت شده و مدل یکبار دیگر بهصورتِ یک برنامهی خطیِ محض حل میشود؛ آنگاه ضریبِ دوگانِ هر قید، ارزشِ سایهایِ آن است.
در بازحلِ دوگان، جملهی Big-M از قیدِ تقاضا حذف میشود؛ در غیر اینصورت ضریبِ دوگانِ تقاضا بهجای هزینهی نهاییِ واقعی، برابرِ جریمهی کمبود $\pi$ میشد. این جزئیات تفاوتِ یک ارزشِ سایهایِ درست با یک عددِ گمراهکننده است.
تفسیرِ اقتصادی طبق قضیهی مکملی (Complementary Slackness): قیدِ فعال (با لختیِ صفر) ارزشِ سایهایِ ناصفر دارد و گلوگاه است؛ قیدِ غیرفعال ارزشِ سایهایِ صفر دارد. ارزشِ سایهایِ ظرفیتِ یک انبار یعنی «صرفهجوییِ هر واحد ظرفیتِ اضافه»، و ارزشِ سایهایِ تقاضا یعنی «هزینهی نهاییِ سرویسِ یک واحدِ تقاضای بیشتر».
دو تحلیلِ پسازبهینه ارائه میشود: (الف) حساسیتِ تکپارامتری که یک پارامتر را در بازهی $\pm 20\%$ میچرخاند و اثرش را بر هزینهی کل رسم میکند؛ (ب) تحلیلِ تورنادو که همهی پارامترها را همزمان $\pm\delta$ تکان داده و بر اساسِ بزرگیِ اثرشان بر هزینه رتبهبندی میکند تا پرتأثیرترین اهرم شناسایی شود.
شکلِ زیر همان مدلِ ریاضیِ بالا را در قالبِ جوابِ واقعی نشان میدهد: کدام انبار کدام مرکز را و با چه مقداری تغذیه میکند. ضخامتِ هر خط با مقدارِ جریان متناسب است و نوارِ زیر هر انبار، درصدِ بهرهبرداری از ظرفیت را نشان میدهد. انبارِ قم با ۱۰۰٪ بهرهبرداری گلوگاهِ شبکه است — همان قیدی که ارزشِ سایهایِ آن در بخشِ بعد تفسیر میشود.
| یافته | مقدار | تفسیر |
|---|---|---|
| ارزشِ سایهایِ ظرفیتِ قم | 6,527 تومان/واحد | گلوگاه؛ هر واحد ظرفیتِ اضافه در قم بیشترین صرفهجویی را دارد |
| ظرفیتِ آزادِ کرج | 53% بلااستفاده | کمبهرهورترین انبار — ظرفیتِ رزرو |
| پرتأثیرترین اهرمِ تورنادو | تقاضای مشهد (±19.9M) | حساسترین پارامترِ هزینه در کلِ شبکه |
| چهمیشد-اگر: قم +20% ظرفیت | صرفهجوییِ 2,592,299 تومان | هزینه از 154.76M به 152.17M کاهش مییابد |
مدل نهتنها جوابِ بهینه بلکه دلیلِ اقتصادیِ آن را میدهد: کدام انبار گلوگاه است، سرمایهگذاری کجا بیشترین بازده را دارد و کدام پارامتر حساسترین است. این ماژول با 47 آزمونِ پایتون پوشش داده شده است.
این ماژول مسیرِ ناوگانِ تحویلِ درِ منزل را بهینه میکند بهگونهای که هر مشتری دقیقاً یکبار در پنجرهی زمانیِ خود سرویس شود و بارِ هر وانت از ظرفیتِ وزنی و حجمی فراتر نرود. چون این مسئله NP-Hard است، از یک الگوریتمِ ژنتیکِ خودنوشته (بدونِ کتابخانهی آماده) استفاده میشود.
قیود: هر مشتری دقیقاً یکبار سرویس شود؛ بارِ هر مسیر از ظرفیتِ وزنی $Q_w$ و حجمی $Q_v$ فراتر نرود؛ و زمانِ شروعِ سرویس در پنجرهی زمانی بماند:
در هر توقف، رسیدنِ زودتر از $e_i$ به انتظار میانجامد (مجاز) و رسیدنِ دیرتر از $\ell_i$ نقضِ قید است. کمینهسازیِ لغوی با یک جریمهی وانت در تابعِ برازش پیاده میشود: $\text{fitness} = \lambda_{\text{veh}}\!\cdot\! K + \sum d + \text{جریمهها}$.
هر کروموزوم یک جایگشتِ ساده از مشتریها است (یک «تورِ غولآسا» بدونِ مرزِ وانت). این جایگشت با یک رمزگشای بهینه به مسیرها تقسیم میشود — روشِ Split (پرینس، 2004) که با برنامهریزیِ پویا روی یک گرافِ کمکی، کوتاهترین مسیر از گرهِ 0 تا n را مییابد و بدینترتیب بهترین برشِ همان جایگشت به وانتها را میدهد:
این جداسازیِ «رمزگذاری از رمزگشایی» مزیتِ بزرگی دارد: اپراتورهای ژنتیک روی یک جایگشتِ ساده کار میکنند (بیدردسر)، و ساختارِ پیچیدهی مسیرها بهصورتِ بهینه از خودِ جایگشت استخراج میشود.
| اپراتور | نقش |
|---|---|
| OX (Order Crossover) | تقاطعِ ترتیبمحور؛ برشی از والدِ اول را حفظ و بقیه را بهترتیبِ والدِ دوم پر میکند — پیوستگیِ توالی را نگه میدارد. |
| PMX (Partially-Mapped) | تقاطعِ نگاشتِ جزئی؛ ساختارِ موقعیتی را با یک نگاشتِ دوسویه حفظ میکند. |
| جهش (سهگانه) | جابهجایی، درج و وارونهسازیِ زیربخش — تنوعِ جمعیت را حفظ میکند. |
| 2-opt (جستوجوی محلی) | الگوریتمِ ممتیک: بازآراییِ یالها با انتقالِ بینمسیری برای بهبودِ محلیِ بهترینها. |
(1) جستوجوی محلی باید جریمههای تطبیقیِ جاری را ببیند نه ثابت؛ (2) تطبیقِ جریمه بر مبنای «شدنیبودنِ بهترین جواب» انجام شود نه نسبتِ جمعیت؛ (3) یک آرشیوِ جداگانه از بهترین جوابِ شدنی با معیارِ (وانت، مسافت) نگهداری شود تا نوسانِ جریمه نتیجه را نپراند.
برای اعتبارسنجیِ جهانی، الگوریتم روی نمونههای استانداردِ Solomon اجرا شد و فاصله تا بهترین جوابِ شناختهشده (BKS) اندازهگیری شد:
| نمونه | فاصله تا BKS | تعداد وانت |
|---|---|---|
| Solomon C101-25 | +0.3% | برابرِ BKS |
| Solomon R101-25 | +0.2% | 8 = BKS |
جوابِ کاملاً شدنی برای 50 مشتری در کمتر از یک دقیقه، با پوششِ همهی پنجرههای زمانی و قیود ظرفیت؛ نتیجه با بذرِ ثابت تکرارپذیر و روی محکِ جهانیِ Solomon معتبر است. این ماژول با آزمونهای اختصاصیِ همگرایی و بنچمارک پوشش داده شده است.
فراتر از حداقلِ خواستهی موضوع، خروجیِ مسیریابی به یک موتور چیدمان سهبعدیِ بار متصل میشود: توقفِ k-امِ مسیر به بستههای همان مشتری نگاشت میشود و بستهها با قیدِ دسترسپذیریِ چندمقصدی چیده میشوند تا در هر توقف بدونِ جابهجاییِ بقیه قابلِ تخلیه باشند (تخلیهی LIFO).
این فرمولها مستقیماً منطقِ «پرداخت بر اساس فضا»ی سرویسهای حملِ اشتراکی (مانند باکسلاینِ تیپاکس) را مدل میکنند: مشتری بابتِ فضایی که واقعاً اشغال میکند هزینه میدهد، و بارِ سبک اما حجیم بر مبنای وزنِ حجمی قیمتگذاری میشود. کاربر میتواند از کاتالوگِ 12 کارتنِ استانداردِ تیپاکس یا جعبهی سفارشی بسته اضافه کند و اثرش را بر پرشدگی و وزنِ صورتحساب زنده ببیند.
بستههای توقفهای آخر، عقبتر یا پایینتر بار میشوند تا هنگامِ رسیدن به توقفِ زودتر، بدونِ برداشتنِ سایرِ بستهها قابلِ تخلیه باشند. موتور این قید را تضمین و پایداریِ جانبی و شکنندگیِ هر بسته را نیز بررسی میکند.
چتبات یک تحلیلگرِ فارسیِ نتایج است که به خروجیِ همان جلسه متصل میشود. برخلافِ یک دستیارِ عمومی، این بات ابزارهای واقعی اجرا میکند: برای پرسشهای «چهمیشد-اگر» مدل را دوباره حل میکند و پاسخ را با اعدادِ واقعیِ قبل/بعد میدهد.
| ابزار | کارکرد |
|---|---|
| run_lp_whatif | تغییرِ ظرفیت/تقاضا و بازحلِ MILP؛ پاسخ با هزینهی قبل/بعد |
| rank_cost_drivers | تحلیلِ تورنادو — رتبهبندیِ پرتأثیرترین اهرمهای هزینه |
| explain_shadow_price | تفسیرِ ارزشِ سایهایِ یک قید با دادهی دقیق |
| get_route_detail | جزئیاتِ توالیِ توقفهای یک وانت + نمایشِ نقشهی درونگفتگو |
| run_ga_quick | اجرای سریعِ مسیریابی با بذرِ متفاوت برای سنجشِ حساسیت |
پرسش: «اگر ظرفیتِ انبار قم 20% بیشتر شود چه میشود؟» — بات ابزارِ run_lp_whatif را صدا زد، مدل را واقعاً بازحل کرد و پاسخ داد: هزینهی کل از 154.76M به 152.17M تومان کاهش مییابد (صرفهجوییِ 2,592,299 تومان)، و جدولِ قبل/بعد را نمایش داد. این عدد دقیقاً با محاسبهی مستقلِ پنلِ «مقایسهی سناریو» همخوان است.
دو نماگرفتِ زیر مستقیماً از سامانهی زنده روی ap.packup.ir گرفته شدهاند و دو توانِ متفاوتِ دستیار را نشان میدهند: بازگوییِ صوریِ مدل و گزارشِ عملیاتیِ یک مسیرِ حلشده.
پاسخِ دوم فقط داده را بازنمیگوید؛ آن را تشخیص میدهد. دستیار از همان جدول نتیجه میگیرد که قیدِ فعالِ وانت ۱ حجم است نه وزن (پرشدگیِ حجمی 93.2٪ در برابر وزنیِ 41.1٪) — یعنی بارِ این مسیر «چگالیِ پایین» دارد — و اینکه 503.1 دقیقه از 666.1 دقیقهی مأموریت صرفِ انتظارِ پشتِ پنجرهی زمانی میشود.
اقدامِ پیشنهادیاش هم عددی است: تعویقِ 100 تا 150 دقیقهای زمانِ خروج از انبار، با اثرِ تخمینیِ کاهشِ حدود 60٪ از زمانِ معطلی. این دقیقاً همان چیزی است که یک شاخصِ خام نمیدهد: عدد بهعلاوهی علت بهعلاوهی کارِ بعدی.
رابط یک برنامهی تکصفحهای React با شش صفحه است: داشبورد، تخصیصِ سفارش، مسیریابی، بارگیریِ سهبعدی، چتبات و راهنما. تجربهی کاربر بر «سادگیِ ورودِ اطلاعات» و «رسمِ نمودارهای تحلیلیِ خروجی» متمرکز است — دو شاخصِ صریحِ ارزیابی.
کلِ رابط دوزبانه (fa/en)، راستچین/چپچینِ خودکار، واکنشگرا (از 320 پیکسل) و دارای دارکمودِ سهحالته است. آیکنها برداری (lucide) و بدونِ هیچ ایموجی هستند؛ اعداد در متنِ راستچین با ایزولهسازیِ جهت نمایش داده میشوند.
تصاویر زیر از اجرای زندهی برنامه روی سناریوی نمونه گرفته شدهاند: نقشهی تعاملی، نمودار تحلیلی، مسیرهای بهینه و صحنهی سهبعدیِ چیدمان بار.
سامانه کاتالوگی از ۲۲ وسیلهی نقلیهی واقعیِ ایرانی دارد — از موتورِ پیک تا کشندهی تریلی. هر وسیله با ابعادِ واقعیِ اتاقِ بار، ظرفیتِ وزنی، و مدلِ سهبعدیِ اختصاصی (پلاکِ ایرانی، جلوپنجره، چراغهای خودتاب، چرخهای لایهای) در موتورِ چیدمانِ بارچین رندر میشود. کاربر وسیله را دستی برمیگزیند یا «انتخابِ خودکار»، کوچکترین وسیلهی جاگیرِ بار را پیشنهاد میدهد و بار را همانجا با رعایتِ قیدِ تخلیهی LIFO میچیند.
کیفیت با آزمونهای خودکار و گیتهای ساختاری تضمین میشود. منطقِ نمرهدار بهصورتِ توابعِ خالص از رابط جدا شده تا مستقل و دقیق آزموده شود.
| حوزه | پوششِ آزمون |
|---|---|
| LP | بهینهی دستی 2×2، تعادلِ قیود، مکملی، کمبود، رگرسیونِ حساسیت و تورنادو |
| GA | صحتِ اپراتورها، رمزگشای Split، قطعیتِ بذر، شدنیبودنِ تهران، فاصله تا BKS روی Solomon |
| چتبات | ابزارها بدونِ تماسِ واقعی با مدل، اعتبارسنجی، استریمِ endpoint |
| منطقِ فرانت | تخصیصِ ناوگان، تورنادو، شبیهسازی، پارسِ بسته، ویرایشِ سناریو — همه توابعِ خالص |
علاوه بر آزمونها، تغییراتِ کلیدی با یک فرایندِ مرورِ متخاصمِ چندعاملی بازبینی شدند: عاملهای مستقل بهدنبالِ باگ میگشتند و یافتهها با عاملهای راستیآزمای دیگر تأیید یا رد میشدند. این فرایند چند باگِ واقعی (از جمله یک باگِ نامرئیِ سرریزِ چیدمان و یک باگِ جهتِ فلش) را پیش از تحویل شناسایی و اصلاح کرد.
برنامه فقط لوکال نیست؛ بهصورتِ زنده و عمومی روی نشانیِ https://packup.ir مستقر است و از هر جا در دسترس است.
API گوگل از آیپیِ ایران بسته است. برای آنکه چتبات بدونِ هیچ VPN برای کاربر و استاد کار کند، برنامه روی یک سرورِ اروپایی (آمستردام) مستقر شد؛ چون تماس با Gemini سمتِ سرور انجام میشود، آیپیِ سرور مجاز است و پاسخ میگیرد. نکتهی مهندسیِ ظریف: بلاکِ گوگل بر پایهی اعتبارِ آیپیِ دیتاسنتر است نه صرفاً کشور — یک آیپیِ اولِ اروپایی بلاک بود و با جابهجایی به آیپیِ تمیز حل شد. مدلِ فعال: gemini-3.5-flash.
کدِ کامل روی گیتهاب؛ اجرا با Docker Compose (بکاندِ FastAPI + فرانتِ nginx)؛ گواهیِ HTTPSِ خودکار (Let's Encrypt) با ریورسپروکسیِ Caddy که استریمِ SSE را سالم عبور میدهد. یک خطِ CI/CD (GitHub Actions) روی هر تغییر، آزمونهای بکاند (ruff · mypy · pytest) و فرانت (typecheck · i18n · build · ۱۰۴ آزمون) را اجرا و در صورتِ سبزشدن، برنامه را خودکار روی سرور منتشر میکند.
محدودیتِ شناختهشده: پردازندهی سرورِ اقتصادی (Burstable) حدودِ سه برابر کندتر از یک هستهی دسکتاپ است، پس همگراییِ الگوریتم ژنتیک روی سرور کندتر از لوکال دیده میشود — این محدودیتِ سختافزار است و با پینِ تردهای محاسباتی تا حدی بهبود یافت.
PackUp نشان میدهد که سه پارادایمِ متفاوتِ بهینهسازی — برنامهریزیِ خطیِ دقیق، فراابتکارِ تکاملی و چیدمانِ هندسی — چگونه میتوانند در یک معماریِ ماژولار و یک تجربهی کاربریِ یکپارچه کنار هم بنشینند و یک دستیارِ تصمیمِ واقعی بسازند، نه صرفاً یک ماشینحساب. مدلِ LP دلیلِ اقتصادیِ جواب را میدهد، GA مسیرِ عملیاتی را میسازد، موتورِ چیدمان بارگیریِ اجرایی را تضمین میکند و چتبات همه را به زبانِ مدیر ترجمه میکند.
ماژولهای LP، GA و رابط کاربری کامل و آزموده هستند (154 آزمونِ سبز در مجموع). گزارشِ حاضر فرمولبندیِ ریاضیِ کامل را پوشش میدهد. کدِ چتبات کامل است و برای اجرای زنده تنها به یک کلیدِ معتبرِ مدلِ زبانی نیاز دارد. کلِ سامانه با docker compose بهصورتِ محلی اجرا میشود و آمادهی استقرار است.