From 0941a28da48f6e68e41249fb82b61a850ea567a4 Mon Sep 17 00:00:00 2001 From: Denis Krivochenko Date: Mon, 17 Aug 2026 17:10:37 +0700 Subject: [PATCH] =?UTF-8?q?photo=5Fbot:=20=D0=B6=D1=91=D1=81=D1=82=D0=BA?= =?UTF-8?q?=D0=B8=D0=B9=20=D0=BF=D1=80=D0=BE=D0=BC=D0=BF=D1=82=20=E2=80=94?= =?UTF-8?q?=20=D1=87=D0=B5=D0=BA-=D0=BB=D0=B8=D1=81=D1=82=20A-E,=20=D1=84?= =?UTF-8?q?=D0=B8=D0=BA=D1=81=D0=B0=D1=86=D0=B8=D1=8F=20=D0=BC=D0=B8=D0=BC?= =?UTF-8?q?=D0=B8=D0=BA=D0=B8/=D0=B3=D1=80=D0=B0=D1=84=D0=B8=D0=BA=D0=B8,?= =?UTF-8?q?=20=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=D0=B0=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BA=D0=B0=D0=B7=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- photo_bot/ai_validator.py | 48 +++++++++++++++++++++++---------------- photo_bot/handlers.py | 7 +++--- utils/texts.py | 21 +++++++++++++---- 3 files changed, 48 insertions(+), 28 deletions(-) diff --git a/photo_bot/ai_validator.py b/photo_bot/ai_validator.py index 6a8c798..a1c1cda 100644 --- a/photo_bot/ai_validator.py +++ b/photo_bot/ai_validator.py @@ -21,32 +21,33 @@ def _build_prompt() -> str: """Промпт проверки фото (без подсказки MediaPipe — в тест-моде геометрия не считается).""" cv_hint = 'ℹ️ ПОДСКАЗКА ОТ ALGORITHM: Геометрическая проверка в тестовом режиме не выполняется, проверяй визуально.' - return ('''Ты — автоматический эксперт пропускной системы. Проверь фотографию сотрудника на соответствие регламенту. + return ('''Ты — строгий инспектор пропускной системы. Проверь фотографию сотрудника на соответствие регламенту. %s ЗАПРЕЩЕНО выводить внутренние рассуждения и теги размышлений. Сразу пиши финальный ответ. -БЛОК 0. КРИТИЧЕСКИЕ СТОП-ФАКТОРЫ: -1. Не реальное фото: ИИ-генерация, 3D-арт, цифровой рисунок, мультфильм, стилизованный портрет, сильная обработка фильтрами. -2. Графика и текст: Любые надписи, водяные знаки, рамки, декоративный фон с буквами, графическими элементами, коллажи. -3. Костюмы и атрибуты: Исторические костюмы, короны, маски, каски (кроме строительных). -4. Посторонние предметы: Оружие, предметы перед лицом. -5. Неправильный ракурс и взгляд: Строгий боковой профиль, затылок, человек смотрит совсем в сторону. -6. Гримасы и мимика: Гримасы, смешные рожи, высунутый язык, вытянутые щёки, скривленные губы, смех/улыбка с закрытым ртом, любой невынужденный и неспокойный вид. Выражение лица должно быть нейтральным или слегка спокойным. +ПРЯМО СЕЙЧАС ВНИМАТЕЛЬНО РАССМОТРИ ФОТО И ПОСЛЕДОВАТЕЛЬНО ЗАФИКСИРУЙ: +A) РЕАЛЬНОСТЬ: это живое фото, а не ИИ-генерация, рисунок, 3D, мультфильм, фильтр? +B) ГРАФИКА: есть ли рамка, любые буквы/надписи/подписи (включая подпись с именем внизу), водяные знаки, декоративный фон с буквами, коллаж? +C) МИМИКА: высунут ли язык? Скривлены ли губы? Есть ли гримаса, смешная рожа, щёлканье зубами, смех, скос глаз, закатанные глаза? +D) РАКУРС: профиль, затылок, взгляд в сторону? +E) МАСШТАБ: ниже линии груди видны ли живот, талия, подмышки? -БЛОК 1. МАСШТАБ И КАДРИРОВАНИЕ: -- подбородок НИЖЕ середины кадра → тест пройден. -- «Не прошел», если видны талия, живот, подмышки, одежда ниже линии груди. +ПРАВИЛО ОТКАЗА: если по любому пункту A–E есть хоть малейшее отклонение — вердикт «Не прошел». +Смех, ржа, рожи, высунутый язык, скривлённые губы — фото НЕ ПРОХОДИТ, даже если лицо анфас и хорошо освещено. +Рамка или буквы/подпись на фоне или по краю кадра — фото НЕ ПРОХОДИТ. ИТОГОВЫЙ ВЕРДИКТ: -- «Прошел» — нормальные портретные фотографии. -- «Не прошел» — только явные нарушения. -- «На модерацию» — если фото качественное, но масштаб на грани. +- «Прошел» — только если все пункты A–E идеальны: нейтральное спокойное лицо, чистый кадр без рамки и букв. +- «Не прошел» — любое отклонение, описанное выше. +- «На модерацию» — только если фото реальное и лицо нейтральное, но масштаб или ракурс на грани нормы. -ФОРМАТ ОТВЕТА — СТРОГО 2 строки: +ФОРМАТ ОТВЕТА — СТРОГО 4 строки, без других слов: +Мимика: <нейтрально / высунут язык / гримаса / скривленная улыбка / смех — что именно видно> +Графика: <нет / рамка / буквы / подпись / коллаж — что именно видно> Вердикт: [Прошел / Не прошел / На модерацию] -Причина: [1–2 предложения с указанием конкретной причины] +Причина: [1–2 предложения с указанием конкретного пункта A–E] ''') % cv_hint @@ -99,15 +100,22 @@ def validate_photo_with_ai(file_bytes: bytes) -> dict: verdict = 'Не прошел' reason = '' + mimi = '' + grafika = '' for line in clean_ans.splitlines(): - if line.strip().lower().startswith('причина:'): + ls = line.strip().lower() + if ls.startswith('причина:'): reason = line.split(':', 1)[1].strip() - break + elif ls.startswith('мимика:'): + mimi = line.split(':', 1)[1].strip() + elif ls.startswith('графика:'): + grafika = line.split(':', 1)[1].strip() if not reason: reason = clean_ans.replace('\n', ' ').strip() - logger.info(f"[TEST MODE] ИИ-вердикт: {verdict} | {reason}") - return {'ok': True, 'verdict': verdict, 'reason': reason, 'error': ''} + logger.info(f"[TEST MODE] ИИ-вердикт: {verdict} | мимика={mimi} | графика={grafika} | {reason}") + return {'ok': True, 'verdict': verdict, 'reason': reason, 'error': '', + 'mimi': mimi, 'grafika': grafika} except Exception as e: logger.error(f"[TEST MODE] Ошибка обращения к ИИ: {e}") diff --git a/photo_bot/handlers.py b/photo_bot/handlers.py index b5c2498..77f9094 100644 --- a/photo_bot/handlers.py +++ b/photo_bot/handlers.py @@ -220,10 +220,12 @@ async def photo_module_handler(msg: Message): verdict = ai_res['verdict'] reason = ai_res['reason'] or 'Причина не указана.' + mimi = ai_res.get('mimi', '') + grafika = ai_res.get('grafika', '') log_menu_stats(user_id, "Photo Bot (TEST)", f"ИИ-вердикт: {verdict}") # Фото остаётся в pending — можно повторить проверку другим фото (кнопка 2) - await msg.answer(photo_test_result(verdict, reason), parse_mode="html") + await msg.answer(photo_test_result(verdict, reason, mimi, grafika), parse_mode="html") return elif cmd == "2": @@ -314,8 +316,7 @@ async def photo_module_handler(msg: Message): file_id = getattr(msg.content, "file_id", None) raw_bytes = None - face_text = PHOTO_TEST_RECEIVING_TEXT if config.PHOTO_TEST_MODE else PHOTO_PROCESSING_FACE_TEXT - await msg.answer(face_text, parse_mode="html") + await msg.answer(PHOTO_PROCESSING_FACE_TEXT, parse_mode="html") try: downloaded_file = await states.tc_bot.download_file_by_id(file_id) diff --git a/utils/texts.py b/utils/texts.py index 1e36897..1615f75 100644 --- a/utils/texts.py +++ b/utils/texts.py @@ -269,16 +269,27 @@ def _photo_test_ai_checking(emojis: dict) -> str: def _photo_test_receiving(emojis: dict) -> str: return "🧪 Ищу лицо на фото (тестовый режим), пожалуйста, подождите..." -def _photo_test_result(verdict: str, reason: str, emojis: dict) -> str: +def _photo_test_result(verdict: str, reason: str, mimi: str = '', grafika: str = '', emojis: dict = None) -> str: + emojis = emojis or {} icons = {"Прошел": "✅", "Не прошел": "❌", "На модерацию": "⏳"} icon = icons.get(verdict, "ℹ️") + lines = [] + if mimi: + lines.append(f"🙂 Мимика: {mimi}") + if grafika: + lines.append(f"🖼 Графика: {grafika}") + if lines: + detail = "\n".join(lines) + "\n\n" + else: + detail = "" return ( f"🧪 Тестовый режим: вердикт ИИ\n\n" f"{icon} Вердикт: {verdict}\n" + f"{detail}" f"📝 Причина: {reason}\n\n" f"⚠️ Тестовый режим: фото не публиковалось и никуда не отправлялось.\n\n" - f"{emojis['2']} — Загрузить другое фото\n" - f"{emojis['0']} — В главное меню" + f"{emojis.get('2', '2')} — Загрузить другое фото\n" + f"{emojis.get('0', '0')} — В главное меню" ) def _photo_test_ai_error(emojis: dict) -> str: @@ -362,8 +373,8 @@ PHOTO_TEST_PREVIEW_TEXT = _photo_test_preview(EMOJI_DIGITS) PHOTO_TEST_AI_CHECKING_TEXT = _photo_test_ai_checking(EMOJI_DIGITS) PHOTO_TEST_RECEIVING_TEXT = _photo_test_receiving(EMOJI_DIGITS) -def photo_test_result(verdict: str, reason: str) -> str: - return _photo_test_result(verdict, reason, EMOJI_DIGITS) +def photo_test_result(verdict: str, reason: str, mimi: str = '', grafika: str = '') -> str: + return _photo_test_result(verdict, reason, mimi, grafika, EMOJI_DIGITS) def photo_test_ai_error() -> str: return _photo_test_ai_error(EMOJI_DIGITS)