AI-верификатор фото — защита от фейковых жалоб
Telegram-бот + Mini App для ресторанов, доставки и фудтеха. Сотрудник загружает фото из жалобы клиента — система за секунды проверяет, была ли картинка сгенерирована нейросетью, отредактирована в фотошопе и когда реально снята. На выходе — визуальная ELA-карта манипуляций, PDF-отчёт для разбора спора и защита маржи от ложных компенсаций. Собран в связке с Claude Code.
- 15 000 ₽ предотвращённых убытков всего — в реальной тестовой точке за первые недели работы
- 10 подделок выявлено — AI-фото, пересжатия, отсутствие EXIF
- ~58% доля «модифицировано» среди загруженных фото в пилотном режиме
- 1500 ₽ средний чек ресторана — в настройках задаётся для автоподсчёта сохранённой маржи
- 4 независимых слоя анализа — AI-детектор, ELA-хитмэп, EXIF, давность съёмки
- 2 роли в системе — оператор (проверка) и администратор (управление)
- PDF отчёт по каждому спору — для разбора с клиентом или агрегатором
- 20 МБ потолок файла · JPEG · PNG · WebP — принимает любой формат доставки
* цифры предотвращённых убытков взяты из реального дашборда статистики тестовой точки · средний чек настраивается
Задача
В общепите, доставке и на агрегаторах еды появился новый класс мошенничества: клиент заказывает, получает блюдо, а потом присылает фото — «в моём блюде волос / плесень / посторонний предмет» — и требует компенсацию, возврат или бесплатный повторный заказ. Проблема в том, что фото всё чаще не настоящие: их либо генерирует Midjourney / DALL·E, либо собирают из чужих снимков в фотошопе, либо просто берут из гугла двухлетней давности. Ресторану проще отдать 1 500 ₽, чем спорить — и эти «проще» суммируются в ощутимый минус по маржинальности.
- AI-генерация дешевеет. Любой может за 30 секунд сгенерировать «реалистичное» фото испорченного блюда — и приложить к претензии.
- У менеджера нет инструмента. На глаз AI-фото сегодня почти не отличить от настоящего — особенно в торопливом чате поддержки.
- Старые фото. Часть претензий — это чужое фото из интернета или снимок двухнедельной давности, который выдают за «сейчас из ресторана».
- Нужны доказательства. Спор с клиентом или агрегатором требует не «мнения менеджера», а документа с фактами — PDF, который можно переслать.
Архитектура — один бот, четыре анализа
Система — Telegram-бот на aiogram, который монтирует внутри себя Mini App на Vue 3. Сотрудник загружает фото прямо в окне Telegram, aiohttp-бэкенд прогоняет его через четыре независимых слоя анализа (AI-детектор, ELA, EXIF, timestamp), взвешивает результаты в едином вердикте и сохраняет запись в Postgres. По итогу — визуальный вердикт в Mini App, тепловая карта манипуляций и PDF-отчёт для разбирательства.
- aiogram 3 + aiohttp — в одном процессе: и webhook для Telegram, и REST API для Mini App.
- Vue 3 + Vite + Pinia — Mini App со всеми экранами: проверка, история, статистика, сотрудники, настройки.
- SQLAlchemy async + Alembic — модели Verification / Employee / AppSettings, миграции схемы под версии.
- Pillow + NumPy — ELA-хитмэп и EXIF-разбор делаются локально, без внешних API.
- AI or Not — единственный внешний ML-провайдер, отвечает за вердикт «сгенерировано нейросетью».
- ReportLab — серверная сборка PDF-отчёта с русскими шрифтами и встроенным хитмэпом.
- Ролевая модель — оператор видит «Проверка» + «История»; админ — плюс «Статистика», «Сотрудники», «Настройки».
- Docker · Fly.io — один контейнер, ноль ручной инфраструктуры.
┌─────────────────────────────────────┐ │ Telegram Bot API │ └────────────────┬────────────────────┘ │ webhook · Mini App launch ▼ ┌──────────────────────────────────────────────────────────────────┐ │ aiogram 3 · aiohttp server (один процесс, один контейнер) │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ bot router │ │ REST /api │ │ miniapp (Vue) │ │ │ │ /start │ │ /verify │ │ Pinia · router │ │ │ │ /history │ │ /history │ │ 5 экранов │ │ │ │ /stats │ │ /stats │ └──────────────────┘ │ │ │ /admin │ │ /employees │ │ │ └──────────────┘ │ /settings │ │ │ │ /heatmap │ │ │ │ /pdf │ │ │ └──────┬───────┘ │ │ │ │ │ ┌──── analysis pipeline ┴─────────────────────────────┐ │ │ │ │ │ │ ▼ ▼ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────┐ │ │ │ AI detector │ │ ELA │ │ EXIF │ │Timestamp│ │ │ │ AI or Not │ │ Pillow+NumPy │ │ Pillow/TAGS │ │ datetime │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ └────┬─────┘ │ │ └────────────────┴──────┬─────────┴──────────────┘ │ │ ▼ │ │ ┌───────────────────┐ │ │ │ build_verdict() │ ──► confidence % │ │ └─────────┬─────────┘ │ │ ▼ │ │ ┌────────────────────────┐ │ │ │ Postgres · SQLAlchemy │ ──► audit │ │ └─────────────┬──────────┘ │ │ ▼ │ │ ┌───────────────────┐ │ │ │ ReportLab · PDF │ ──► /tmp cache │ │ └───────────────────┘ │ └──────────────────────────────────────────────────────────────────┘
4 слоя анализа · единый вердикт · ролевая модель · Mini App и бот в одном процессе
Telegram Mini App
Кнопка «Открыть панель» запускает Vue 3 SPA внутри Telegram. Сотрудник тапает по области drag-n-drop, выбирает фото — multipart летит в /api/verify.
4 независимых слоя
AI or Not (вердикт «сгенерировано нейросетью»), ELA-хитмэп на Pillow+NumPy, разбор EXIF-тегов, проверка давности снимка через DateTimeOriginal.
Взвешенная логика
Функция build_verdict() комбинирует результаты в три класса: ORIGINAL / MODIFIED / AI_GENERATED — и возвращает confidence %.
PDF + история
Каждая проверка пишется в Postgres, ELA кэшируется в /tmp, ReportLab собирает PDF с русским шрифтом — сотрудник может скачать и переслать.
Mini App в Telegram
Сотрудник общепита не ставит приложение, не заводит аккаунт, не помнит пароль. Он открывает Telegram — Verificator_eat_bot уже добавлен в корпоративный чат, нажимает «Открыть панель» — и попадает в полноценный SPA с пятью экранами прямо в клиенте.
- /start → приветствие бота, подсказка отправить фото как документ для максимальной точности анализа
- Mini App → drag-n-drop зона «Нажмите для загрузки», JPEG / PNG / WebP до 20 МБ
- Bottom nav → 2 пункта для оператора, 5 для администратора
-
Auth →
через
initDataTelegram, без отдельных логинов
Две роли — оператор и админ
Нижняя навигация Mini App динамически перестраивается под роль. Оператор (линейный сотрудник зала или колл-центра) видит только проверку и свою историю — ничего лишнего. Администратор — плюс сводную статистику по точке, список сотрудников с ролями и настройки среднего чека для расчёта сохранённой маржи.
- Оператор → Проверка · История (2 таба)
- Админ → Проверка · История · Статистика · Сотрудники · Настройки (5 табов)
- Назначение ролей — одной кнопкой из админского экрана, без SQL
- Деактивация — увольнение сотрудника закрывает ему Mini App
Ядро проверки — 4 слоя анализа
Каждое фото проходит четыре независимых проверки, результаты которых затем сливаются в один вердикт с процентом уверенности. Важно, что три из четырёх слоёв работают локально — без интернета и без платных API — это даёт базовую защиту даже при сбое внешнего сервиса.
- Verdict — ai / human / unknown
- Confidence — вероятность AI-генерации 0..1
- Generators — детектируется источник: Midjourney, SDXL, DALL·E, Flux
- Graceful degrade — при падении API вердикт по остальным 3 слоям
- Пересжатие изображения в JPEG q=90
- Попиксельная разность с оригиналом × 20
- JET-colormap без matplotlib — чистый NumPy
- Тёплые зоны = область, которая перекомпрессовалась дважды — типичный признак редактирования
- Камера — Make / Model (iPhone 15, Canon EOS и т.д.)
- Software — сверяется с списком из 18 редакторов (Photoshop, Lightroom, Snapseed, Facetune…)
- GPS — присутствует / нет
- Флаги — NO_EXIF · HAS_CAMERA · HAS_DATE · HAS_GPS · EDITING_SOFTWARE
- Парс даты из
DateTimeOriginal - Вердикт — recent / suspicious / old / no_data
- Пороги — WARN (по умолчанию 30 мин.) и MAX (24 ч)
- Защита — дата в будущем → suspicious
ELA — тепловая карта манипуляций
Error Level Analysis — классический forensic-алгоритм, который превращает невидимые глазом следы редактирования в яркую тепловую карту. Система пересжимает фото в JPEG с тем же качеством, вычитает из оригинала — и зоны, где разность велика, подсвечиваются тёплыми цветами. Если на фото блюда «инородный предмет» — он почти всегда вставлен отдельным слоем и на ELA подсвечивается ярче фона.
История и статистика
Каждая проверка сохраняется — и сотрудник, и администратор могут вернуться к ней, скачать PDF, переслать клиенту. Админ видит сводку по точке: сколько всего проверок, сколько сегодня / за неделю, какая доля — «модифицировано» vs «оригинал», и сколько рублей маржи уже спасено (подсчитывается автоматически из среднего чека в настройках).
Управление — сотрудники и настройки
Админ добавляет сотрудников по Telegram ID, назначает роль — оператор или администратор — и в любой момент может повысить, понизить или деактивировать аккаунт. Средний чек в настройках — единственный экономический параметр, от которого зависит расчёт «предотвращённых убытков» на дашборде.
Deep dive — данные, ELA, вердикт
Три технических куска, ради которых случаи вроде этого стоит собирать по-инженерному, а не склеивать no-code. Ниже — схема данных под аудит, ядро ELA-алгоритма и формула взвешенного вердикта.
Полный след проверки
В таблице verifications хранится не только итоговый вердикт, но и каждый промежуточный сигнал: вердикт AI-детектора отдельно, JSON-словарь детектированных генераторов, все EXIF-флаги, camera_info, software, timestamp-вердикт. При споре можно поднять одну запись и разложить логику на составляющие.
3 из 4 слоёв — локально
ELA, EXIF и timestamp-чекер не ходят в интернет. Это значит, что даже при сбое AI or Not или при отключении интернета — базовая проверка по метаданным и ELA продолжает работать, а сотрудник получает хоть какой-то вердикт.
UUID для каждой записи
Первичный ключ — uuid4. В URL-ах PDF и хитмэпов нет угадываемых id-шников. ELA-png кэшируется в /tmp/ela_{uuid}.png и отдаётся только авторизованному сотруднику той же точки.
initData для Mini App
Аутентификация — через подпись initData, которую Telegram выдаёт при запуске Mini App. Middleware проверяет HMAC, достаёт telegram_id, матчит его к строке в employees, внутрь хендлера уже приходит объект Employee с ролью.
# ядро ELA — чистый Pillow + NumPy, без matplotlib orig = Image.open(buf).convert("RGB") recomp = _save_as_jpeg(orig, quality=ELA_QUALITY) # 90 diff = np.abs(np.array(orig, int16) - np.array(recomp, int16)) diff_gray = np.clip(diff * ELA_AMPLIFY, 0, 255).max(axis=2) # ×20 # JET-colormap без matplotlib — синий → зелёный → жёлтый → красный n = diff_gray / 255.0 r = np.clip(1.5 - np.abs(4 * n - 3), 0, 1) g = np.clip(1.5 - np.abs(4 * n - 2), 0, 1) b = np.clip(1.5 - np.abs(4 * n - 1), 0, 1) heatmap = Image.fromarray((np.stack([r, g, b], -1) * 255).astype(uint8))
-- verifications · одна запись на каждое загруженное фото CREATE TABLE verifications ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), employee_id INT REFERENCES employees(id), -- итоговый вердикт verdict verdict_type NOT NULL, -- ORIGINAL | MODIFIED | AI_GENERATED | UNKNOWN confidence INT NOT NULL, -- 0..100 % verdict_text TEXT NOT NULL, -- AI-детектор ai_verdict VARCHAR(20), ai_confidence FLOAT, ai_generators JSON, -- { midjourney: 0.87, sdxl: 0.12 } ai_error BOOLEAN DEFAULT FALSE, -- EXIF has_exif BOOLEAN DEFAULT FALSE, camera_info VARCHAR(255), software VARCHAR(255), photo_datetime VARCHAR(50), gps_present BOOLEAN DEFAULT FALSE, editing_detected BOOLEAN DEFAULT FALSE, exif_flags JSON, -- [NO_EXIF, EDITING_SOFTWARE, HAS_GPS] -- timestamp photo_age_minutes INT, timestamp_verdict VARCHAR(20), -- recent | suspicious | old | no_data -- артефакты ela_heatmap_available BOOLEAN DEFAULT FALSE, original_filename VARCHAR(255), complaint_note TEXT, created_at TIMESTAMPTZ DEFAULT now() );
схема · каждый промежуточный сигнал сохранён рядом с итоговым вердиктом · UUID для приватности ссылок
Результат
- Ресторан получает доказательство. За секунды — вердикт, тепловая карта и PDF, который можно переслать клиенту или агрегатору. «Ощущение менеджера» превращается в документ.
- Маржа защищена. В пилотной точке за первые недели — 15 000 ₽ спасённой маржи, 10 выявленных подделок и доля "модифицировано" ~58%.
- Сотрудник не учится. Вход — через Telegram, который уже у всех в корпоративном чате. Никаких отдельных приложений и паролей.
- Роли и управление из Mini App. Админ добавляет, назначает и деактивирует сотрудников одной кнопкой.
- Архитектура с запасом прочности. 3 из 4 слоёв анализа работают локально — базовая защита остаётся даже при падении внешнего ML-провайдера.
Нужен похожий AI-инструмент?
Если в вашем бизнесе фото, документы или скриншоты становятся инструментом мошенничества — напишите. Соберём бота, Mini App или SaaS, который превратит «доверие на слово» в аудируемый процесс.
← все работы