Инженерлік блог

Донердегі фудкостпен күрес: 4 фастфуд нүктесі сұранысты болжау арқылы ет пен лавашты рәсуа етуді қалай жеңді

20.08.2026
Шарафутдинов Р.

Мейрамхана бизнесінде қатал қағида бар: егер сіз керемет шырынды донер дайындасаңыз да, бірақ таңертең еттің қаншасын шашқа (вертелге) ілу керектігін дұрыс есептей алмасаңыз — сіз өз пайдаңыз үшін емес, бұрыштағы қоқыс жәшігі үшін жұмыс істеп жатырсыз.

Алматыдағы танымал 4 нүктеден тұратын фастфуд желісінің әдеттегі таңғы тыныс-тіршілігін елестетіп көріңізші (бір нүктесі Саяхат автобекетінде, екеуі Тимирязев пен Сәтбаев көшелеріндегі ҚазҰУ мен ҚазҰТЗУ студенттер қалашығында, тағы біреуі Абай даңғылындағы Орталық стадионның жанында орналасқан). Таңғы сағат алты жарымда бас аспаз Ермек асүйге кіреді. Оның алдында екі таңдау тұр, және екеуі де қаржылық шығынға апарады:

Егер Ермек сақтық үшін тік грильге 45 килограммдық алып ет шашын іліп қойса, ал түске қарай Алматыда кенеттен нөсер жауын құйып, студенттер аудиторияда қалып қойса — түн ортасына қарай 18 килограмм қуырылған, кеуіп кеткен ет қоқысқа тасталады (санитарлық ереже бойынша дайын етті қайта қыздыруға немесе тоңазытуға қатаң тыйым салынған). Онымен бірге 80 дана кеуіп, үгітіліп қалған лаваш та рәсуа болады.

Ал егер Ермек үнемдеймін деп бар болғаны 20 килограммдық шағын ет қойса, ал Орталық стадионда «Қайраттың» кешкі ойыны болып немесе студенттердің емтихан марафоны басталса — кешкі сағат 18:30-да ет бітіп, темір таяқ қана қалады. Аш қонақтар ашуланып бәсекелестерге кетеді, ал желі бір кеште 160 000 теңге таза пайдадан қағылады.

Желі иесі ай сайын 1 850 000 теңгенің өнімін қоқысқа есептен шығарып (списание) отырды, ал фудкост (табыстағы өнімнің өзіндік құн үлесі) фастфуд үшін қалыпты 28–30% орнына 41.8%-ға дейін шарықтап кеткен еді.

Біз бұл мәселені нағыз инженерлік тұрғыдан шештік: аспаздардың болжамына сенудің орнына Google Cloud BigQuery ML (ARIMA_PLUS_XREG) уақыттық қатарларды машиналық оқыту жүйесін енгіздік. Оны ауа райы болжамымен, қалалық іс-шаралар күнтізбесімен және асүйдің Telegram-ботымен байланыстырдық. Бұлттық инфрақұрылымның айлық шығыны бар болғаны $4.15 (шамамен 2 100 теңге) болды, ал желінің фудкосты 29.2%-ға дейін төмендеді. Төменде — бұл жүйені қалай құрғанымыз туралы толық инженерлік талдау.

1. Фастфуд миллиондарды неліктен жоғалтады: Дайындықтың «ұзақ уақыттық» мәселесі

Донер бизнесінің басты қиындығы етті маринадтау мен еріту технологиялық процесінде жатыр:

  1. Етті маринадтаудың Lead Time уақыты (12–14 сағат): Етті тоңазытқыштан шығара салып, 10 минутта шашқа іле салу мүмкін емес. Ет жұқа тілімдерге туралып, дәмдеуіштермен кем дегенде 12 сағат тұруы керек. Ертең қанша ет қуырылатынын аспаз бүгін кешке шешуге мәжбүр;
  2. Жұқа армян лавашының нәзіктігі: Жаңа лаваш ауа тигенде 6–8 сағатта созылғыштығын жоғалтады. Артық бума ашылса, донерді орағанда лаваш жыртылып, тұздығы ағып, клиент ақшасын қайтаруды талап етеді;
  3. Алматының локациялық ерекшеліктері: Саяхаттағы нүкте қала маңындағы автобустар мен Райымбектегі кептеліске тәуелді; Тимирязевте — сабақ пен емтихан кестесіне; Орталық стадионда — футбол лигасы мен концерттер кестесіне байланысты.

Кәдімгі сызықтық орташа есептер («өткен сейсенбіде 180 донер сатылды, демек бүгін де 180 болады») бұл жерде мүлдем жұмыс істемейді. Сыртқы факторларды (экзогенді регрессорларды) ескеретін жүйе қажет болды.

2. Болжау жүйесінің архитектурасы: BigQuery ML + Cloud Scheduler + Telegram

Pandas және Scikit-learn орнатылған ауыр виртуалды серверлерді ұстаудың орнына біз бүкіл деректер аналитикасы мен машиналық оқытуды тікелей Google Cloud BigQuery қоймасында жүзеге асырдық:

┌─────────────────────────────────────────────────────────────┐
│ 1. Деректерді қабылдау көздері (Data Ingestion)             │
│ - Кассалық жүйеден чектік ақпараттар (iiko / Syrve)         │
│ - OpenWeather API (Жауын-шашын, температура, жел күші)      │
│ - Матчтар, концерттер мен ЖОО сессияларының күнтізбесі      │
└──────────────────────────────┬──────────────────────────────┘
                               │ Ағынды жүктеу (Streaming)
                               ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Деректер көлі және BigQuery ML моделі                    │
│ - Модель: ARIMA_PLUS_XREG (Мерекелер мен маусымдылықты тану)│
│ - Ажыратымдылық: 4 филиал бойынша сағаттық (Hourly) болжам  │
│ - Оқыту: 180 күндік сатылым тарихы (1.4 млн транзакция)     │
└──────────────────────────────┬──────────────────────────────┘
                               │
            ┌──────────────────┴──────────────────┐
            │ Күн сайын 06:00-де іске қосу (Cron) │ Техкартаны есептеу
            ▼                                     ▼
┌──────────────────────────────┐    ┌──────────────────────────────┐
│ Google Cloud Scheduler       │    │ Cloud Run Functions (Python) │
│ - Модель инференсін қосу     │    │ - Донерді ет салмағына айнал │
│ - JSON жоспарын қалыптастыру │    │ - 30% ет қуырылу шығынын қосу│
│ - Сұраныс аномалиясын тексеру│    │ - Аспаздардың Telegram боты  │
└──────────────────────────────┘    └──────────────────────────────┘

3. ARIMA_PLUS_XREG SQL моделі мен Python генераторының бастапқы коды

BigQuery ML құрамындағы ARIMA_PLUS_XREG алгоритмі уақыттық қатарды трендке, апталық және тәуліктік маусымдылыққа бөліп, Қазақстанның ресми мерекелерін және сыртқы факторларын автоматты түрде есептейді:

import os
import json
import logging
from datetime import datetime, timedelta
from typing import Dict, Any, List
from google.cloud import bigquery
from google.cloud import secretmanager
import requests

# Инициализация BigQuery клиента для прогнозирования спроса в общепите
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] [DONER-ML] %(message)s")
logger = logging.getLogger("doner_forecasting")

PROJECT_ID = os.getenv("GCP_PROJECT_ID", "almaty-doner-fastfood-ml")
DATASET_ID = "fastfood_analytics"
bq_client = bigquery.Client(project=PROJECT_ID)

SQL_TRAIN_ARIMA_MODEL = """
CREATE OR REPLACE MODEL `fastfood_analytics.doner_demand_arima_model`
OPTIONS(
  model_type = 'ARIMA_PLUS_XREG',
  time_series_timestamp_col = 'order_hour',
  time_series_data_col = 'doner_portions_sold',
  time_series_id_col = 'branch_id',
  auto_arima = TRUE,
  data_frequency = 'HOURLY',
  holiday_region = 'KZ'
) AS
SELECT
  TIMESTAMP_TRUNC(order_created_at, HOUR) AS order_hour,
  branch_id,
  SUM(quantity) AS doner_portions_sold,
  ANY_VALUE(is_rainy) AS is_rainy,
  ANY_VALUE(is_student_exam_period) AS is_student_exam_period,
  ANY_VALUE(is_stadium_match_day) AS is_stadium_match_day,
  ANY_VALUE(traffic_congestion_index) AS traffic_congestion_index
FROM
  `fastfood_analytics.pos_orders_enriched`
WHERE
  order_created_at >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 180 DAY)
GROUP BY
  order_hour, branch_id;
"""

SQL_FORECAST_TOMORROW = """
SELECT
  branch_id,
  forecast_timestamp,
  ROUND(forecast_value, 0) AS predicted_portions,
  ROUND(prediction_interval_lower_bound, 0) AS safe_min_portions,
  ROUND(prediction_interval_upper_bound, 0) AS peak_max_portions
FROM
  ML.FORECAST(
    MODEL `fastfood_analytics.doner_demand_arima_model`,
    STRUCT(24 AS horizon, 0.90 AS confidence_level),
    (
      SELECT
        branch_id,
        future_hour AS order_hour,
        is_rainy,
        is_student_exam_period,
        is_stadium_match_day,
        traffic_congestion_index
      FROM
        `fastfood_analytics.future_exogenous_factors`
      WHERE
        future_hour BETWEEN TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 1 DAY)
        AND TIMESTAMP_ADD(CURRENT_TIMESTAMP(), INTERVAL 2 DAY)
    )
  )
ORDER BY
  branch_id, forecast_timestamp ASC;
"""

def generate_morning_prep_plan(forecast_records: List[Dict[str, Any]]) -> Dict[str, Any]:
    """
    Расчет технологической карты заготовок мяса и лаваша на смену:
    - 1 классический донер = 120 г мяса (с учетом упека 30% требуется 172 г сырого маринованного мяса)
    - 1 донер = 1 тонкий лаваш + 10% резерв на брак/рваный лаваш
    """
    total_portions = sum(item["predicted_portions"] for item in forecast_records)
    raw_meat_kg_required = round(total_portions * 0.172, 1)
    lavash_packs_required = int(total_portions * 1.10)
    
    plan = {
        "calculated_at": datetime.utcnow().isoformat(),
        "total_predicted_doners": int(total_portions),
        "raw_chicken_meat_kg": raw_meat_kg_required,
        "lavash_units_to_defrost": lavash_packs_required,
        "peak_rush_hours": [
            item["forecast_timestamp"].strftime("%H:00") 
            for item in forecast_records if item["predicted_portions"] > 35
        ]
    }
    logger.info(f"📊 План заготовок сформирован: {plan['total_predicted_doners']} донеров, {plan['raw_chicken_meat_kg']} кг мяса, {plan['lavash_units_to_defrost']} лавашей.")
    return plan

def send_telegram_dispatch_to_kitchen(telegram_bot_token: str, chat_id: str, plan: Dict[str, Any]):
    """
    Отправка утреннего задания шеф-повару донерной в 07:00 утра.
    """
    message = (
        f"🌯 *УТРЕННИЙ ПЛАН ЗАГОТОВОК (ИИ BIGQUERY ML)*\n"
        f"📅 Смена: {datetime.now().strftime('%d.%m.%Y')}\n\n"
        f"🎯 Прогноз продаж: *{plan['total_predicted_doners']} шт.*\n"
        f"🥩 Насадить на вертел (сырое мясо): *{plan['raw_chicken_meat_kg']} кг*\n"
        f"🫓 Разморозить лаваша: *{plan['lavash_units_to_defrost']} шт.*\n"
        f"🔥 Часы пик (наплыв гостей): {', '.join(plan['peak_rush_hours']) or 'Равномерно'}\n\n"
        f"💡 _Рекомендация:_ Замариновать второй вертел к 16:30 из-за вечернего матча на стадионе!"
    )
    url = f"https://api.telegram.org/bot{telegram_bot_token}/sendMessage"
    requests.post(url, json={"chat_id": chat_id, "text": message, "parse_mode": "Markdown"})
    logger.info("✅ Утренний план отправлен в Telegram кухни.")

GitHub-тағы кодты көру (OZAT-kz)

Алгоритмнің техникалық ерекшеліктері:

  • holiday_region = 'KZ' параметрі: BigQuery Қазақстандағы Наурыз, Құрбан айт және демалыс күндерінің кестесін автоматты біледі және келушілер ағынын алдын ала бейімдейді;
  • Сыртқы факторлар (XREG): is_rainy (жаңбыр жаяу трафикті 38%-ға төмендетеді), is_student_exam_period (түнгі тапсырыстарды 65%-ға арттырады) және is_stadium_match_day (ойынға дейін 2 сағат бұрын 140%-ға өседі);
  • Өнім шығынының коэффициенті: Модель дайын донерлер саны бойынша болжам жасайды. Python скрипті грильде қуырылғанда болатын 30% ет массасының азаюын ескеріп, шикі ет салмағын және 10% сақтық қоры бар лаваш санын есептеп шығарады.

4. Cloud Scheduler және Cloud Run баптаулары

Таңғы автоматты есептеу үшін Cloud Scheduler және нөлдік масштабталуы бар Cloud Run серверсіз контейнері қолданылады:

apiVersion: v1
kind: ConfigMap
metadata:
  name: doner-ml-forecast-cron-config
  namespace: fastfood-production
data:
  CRON_SCHEDULE: "0 6 * * *" # Запуск ежедневно в 06:00 утра по времени Алматы (UTC+5)
---
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: doner-demand-predictor-service
  namespace: fastfood-production
  labels:
    cloud.googleapis.com/location: asia-southeast1
    app: almaty-doner-demand-predictor
spec:
  template:
    metadata:
      annotations:
        autoscaling.knative.dev/minScale: "0"
        autoscaling.knative.dev/maxScale: "3"
        run.googleapis.com/execution-environment: "gen2"
    spec:
      containerConcurrency: 10
      timeoutSeconds: 300
      containers:
        - image: gcr.io/almaty-doner-fastfood-ml/predictor:v2.1.0
          resources:
            limits:
              cpu: "1000m"
              memory: "1024Mi"
          env:
            - name: GCP_PROJECT_ID
              value: "almaty-doner-fastfood-ml"
            - name: TELEGRAM_CHAT_ID
              value: "-1002348571290"
            - name: BIGQUERY_DATASET
              value: "fastfood_analytics"

Food Cost көрсеткіші (%) мен өнімді қоқысқа есептен шығару үлесі

Аспаздардың болжамына сүйенген 4 нүктенің жұмысы vs ARIMA_PLUS_XREG арқылы сұранысты болжаудың салыстырмасы.

Сенімділік және қауіпсіздік параметрлері:

  • Серверсіз үнемділік: Cloud Run контейнері таңғы сағат 06:00-де бар болғаны 8 секундқа оянып, BigQuery сұранысын жібереді де, Telegram-ға жоспарды салып, қайтадан minScale: 0 күйінде ұйықтайды. Артық серверге ақы төленбейді;
  • Сенімділік аралықтары (Confidence Intervals 90%): Жүйе орташа мәнді ғана емес, жоғарғы және төменгі шекті көрсетеді. Егер белгісіздік жоғары болса, аспазға 10 кг қосымша резервтік шағын шаш дайындау ұсынылады;
  • Деректерді сақтау: Барлық өткен болжамдар қателіктерді талдау үшін Google Cloud Storage қауіпсіз қоймасына жинақталады.

Сағаттық сұраныс болжамы (порция/сағат) vs Матч пен жаңбыр күніндегі нақты сатылым

Орталық стадион маңындағы филиалда BigQuery ML сағаттық сатылым графигінің нақтылығы (дәлдік 96.2%).

5. Нақты нәтижелер: Желі кубок финалы мен күзгі жауынды қалай өткерді

Алгоритмнің төрт айлық жұмысында бірнеше маңызды жетістікке қол жеткіздік:

  1. Орталық стадиондағы «Қайрат — Астана» матчы: Әдеттегі аспаз шамамен 30 кг ет ілер еді. BigQuery ML толық стадион мен ашық ауа райын (+22°C) ескеріп, 82 кг тауық еті мен 480 лавашқа жоспар берді. Сағат 22:00-ге қарай нүкте 474 донер сатып, артық қалдықсыз рекордтық касса жинады;
  2. Қарашадағы қарлы күндер: Алматыда таңертең 25 см қар жауып, қалада 10 балдық кептеліс болды. Модель студенттік нүктелердің жоспарын лезде 45%-ға азайтты. Қоқысқа бірде-бір килограмм ет тасталмады;
  3. Асүй тәртібі: Аспаздар арасындағы «еттің ерте бітіп қалуына кім кінәлі» деген дау тоқтады. Таңертең Telegram-ға нақты тапсырма келеді: «Ермек, бүгін тура 36.5 кг ет ілеміз».

6. 4 нүктенің қаржылық нәтижелері

Желінің предиктивті аналитиканы енгізгенге дейінгі және кейінгі көрсеткіштері:

  • Ет пен лавашты қоқысқа есептен шығару: Сатып алу көлемінің 18.4%-ынан 2.1%-ға дейін азайды (айына таза 1 400 000 теңге үнемделді);
  • Food Cost көрсеткіші: Қауіпті 41.8%-дан қолайлы 29.2%-ға дейін төмендеді;
  • Еттің жетпей қалуынан жоғалған сауда: 88%-ға қысқарды;
  • Google Cloud үшін айлық шығын: BigQuery және Cloud Run үшін бар болғаны $4.15 (шамамен 2 100 теңге).

Уақыттық қатарларды машиналық оқыту және оңтайландыру туралы толығырақ Cloud Tasks арқылы Kaspi-де автодемпинг жасау және ESP32 мен Firebase арқылы ақылды жылыжай құру мақалаларынан оқи аласыз. Инфрақұрылымды оңтайландыру және шығындарды қысқарту тәсілдері бұлттық инфрақұрылымды оңтайландыру бөлімінде, ал жаңа аналитикалық шешімдер бұлттық микросервистер бетінде берілген.

💡 ОЗАТ кеңесі: Енгізуге дайынсыз ба? Архитектура мен бюджетті Scope Builder арқылы есептеңіз немесе тегін ЖИ-аудиттен өтіңіз.

Рустам Шарафутдинов

Рустам Шарафутдинов

Инженерлік блог авторы

Google Cloud архитектурасы саласындағы сарапшы және 15 жылдан астам тәжірибесі бар Senior Full-Stack әзірлеушісі. Ақауға төзімді архитектураларға, жоғары жүктемелі жобаларды оңтайландыруға және AI (Vertex AI) интеграциясына маманданған.

Сараптама: GCP, Kubernetes, Микросервистер, React, Node.js

Пікірлер (0)