From b9acc30bf4a34b97a2885c0392f917064525c85b Mon Sep 17 00:00:00 2001 From: Admin Date: Thu, 15 Oct 2026 20:11:23 +0400 Subject: [PATCH] style(legal): purge internal labels, remove question echo, clean status messages --- handlers/legal_advisor.py | 52 ++++++++++++++++++------------------- services/ai_legal_engine.py | 7 +---- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/handlers/legal_advisor.py b/handlers/legal_advisor.py index 04c83d8..4e76336 100644 --- a/handlers/legal_advisor.py +++ b/handlers/legal_advisor.py @@ -73,7 +73,10 @@ async def send_split_message(message: Message, header: str, body: str): for idx, chunk in enumerate(chunks, 1): if idx == 1: - part_header = f"{header}\n\n" if len(chunks) == 1 else f"{header} (Часть 1/{len(chunks)}):\n\n" + if header: + part_header = f"{header}\n\n" if len(chunks) == 1 else f"{header} (Часть 1/{len(chunks)}):\n\n" + else: + part_header = "" if len(chunks) == 1 else f"(Часть 1/{len(chunks)}):\n\n" else: part_header = f"(Продолжение {idx}/{len(chunks)}):\n\n" @@ -84,8 +87,12 @@ async def send_split_message(message: Message, header: str, body: str): await message.answer(part_text, parse_mode='HTML', reply_markup=reply_markup) except Exception as e: log.warning(f"HTML send failed ({e}), falling back to plain text") - plain_header = header if idx == 1 else f"(Продолжение {idx}/{len(chunks)}):" - await message.answer(f"{plain_header}\n\n{chunk}", reply_markup=reply_markup) + if header: + plain_header = header if idx == 1 else f"(Продолжение {idx}/{len(chunks)}):" + await message.answer(f"{plain_header}\n\n{chunk}", reply_markup=reply_markup) + else: + plain_header = "" if idx == 1 else f"(Продолжение {idx}/{len(chunks)}):\n\n" + await message.answer(f"{plain_header}{chunk}", reply_markup=reply_markup) def get_legal_nav_keyboard() -> InlineKeyboardMarkup: """Инлайн-кнопки навигации после ответа юриста.""" @@ -130,12 +137,11 @@ async def cmd_legal_advisor(message: Message, state: FSMContext, bot: Bot): if len(parts) > 1 and parts[0].casefold() in ("/юрист", "/jurist", "/вопрос"): inline_question = parts[1].strip() if len(inline_question) > 3: - status_msg = await message.answer("⏳ Изучаю документы, сметы и решения ОСС дома № 37 в блокноте NotebookLM...", parse_mode='HTML') + status_msg = await message.answer("⏳ Изучаю документы, сметы, отчеты и решения дома № 37...", parse_mode='HTML') result = await legal_engine.query(prompt=inline_question, role=role) await status_msg.delete() if result.get("success"): - header = "⚖️ Юридический ответ по документам дома № 37:" - await send_split_message(message, header, result["text"]) + await send_split_message(message, "", result["text"]) await state.set_state(LegalQuestionState.waiting_for_question) else: await message.answer(f"{result.get('text')}", parse_mode='HTML') @@ -143,8 +149,8 @@ async def cmd_legal_advisor(message: Message, state: FSMContext, bot: Bot): text = ( "⚖️ Юридический помощник (Дом № 37)\n\n" - "Напишите ваш вопрос (текстом или отправьте голосовое сообщение).\n" - "Ответ формируется строго по сметам, договорам, протоколам ОСС и нормам ЖК РФ в блокноте NotebookLM." + "Напишите ваш вопрос или отправьте голосовое сообщение.\n" + "Ответ формируется по сметам, отчетам, договорам и решениям ОСС дома." ) await message.answer(text, parse_mode='HTML') await state.set_state(LegalQuestionState.waiting_for_question) @@ -162,8 +168,8 @@ async def cb_legal_advisor(callback: CallbackQuery, state: FSMContext): text = ( "⚖️ Юридический помощник (Дом № 37)\n\n" - "Напишите ваш вопрос (текстом или отправьте голосовое сообщение).\n" - "Ответ формируется строго по сметам, договорам, протоколам ОСС и нормам ЖК РФ в блокноте NotebookLM." + "Напишите ваш вопрос или отправьте голосовое сообщение.\n" + "Ответ формируется по сметам, отчетам, договорам и решениям ОСС дома." ) await callback.message.answer(text, parse_mode='HTML') await state.set_state(LegalQuestionState.waiting_for_question) @@ -239,7 +245,7 @@ async def process_text_legal_question(message: Message, state: FSMContext, bot: question = message.text status_msg = await message.answer( - "⏳ Изучаю документы, сметы и решения ОСС дома № 37 в блокноте NotebookLM...", + "⏳ Изучаю документы, сметы, отчеты и решения дома № 37...", parse_mode='HTML' ) @@ -256,23 +262,22 @@ async def process_text_legal_question(message: Message, state: FSMContext, bot: try: await bot.send_message( chat_id=user_id, - text=f"⚖️ Юридический ответ по документам дома № 37:\n\n{format_telegram_html(result['text'])}", + text=format_telegram_html(result['text']), parse_mode='HTML', reply_markup=get_legal_nav_keyboard() ) await message.reply( - "💬 Ответ сформирован и отправлен вам в личные сообщения с ботом, чтобы не перегружать общий чат дома.", + "💬 Ответ отправлен вам в личные сообщения, чтобы не перегружать общий чат дома.", parse_mode='HTML' ) except Exception: await message.reply( - "💬 Пожалуйста, откройте личные сообщения с ботом (@lkmthreeseven_bot) и нажмите /start, чтобы получить ответ конфиденциально.", + "💬 Пожалуйста, откройте диалог с ботом (@lkmthreeseven_bot) и нажмите /start, чтобы получить ответ.", parse_mode='HTML' ) else: - # В личном диалоге отвечаем напрямую - header = "⚖️ Юридический ответ по документам дома № 37:" - await send_split_message(message, header, result["text"]) + # В личном диалоге отвечаем напрямую без лишних префиксов + await send_split_message(message, "", result["text"]) # Оставляем состояние активным, чтобы жилец мог сразу задать следующий вопрос без клика await state.set_state(LegalQuestionState.waiting_for_question) @@ -293,7 +298,7 @@ async def process_voice_legal_question(message: Message, state: FSMContext, bot: return status_msg = await message.answer( - "🎙 Распознаю аудио и сверяю с документами дома № 37 в NotebookLM...", + "🎙 Распознаю аудио и сверяю с документами дома № 37...", parse_mode='HTML' ) @@ -304,7 +309,7 @@ async def process_voice_legal_question(message: Message, state: FSMContext, bot: voice_file_path = str(TEMP_VOICE_DIR / f"voice_{user_id}_{int(message.date.timestamp())}.ogg") await bot.download_file(file_info.file_path, destination=voice_file_path) - # Передаем в legal_engine (Gemini транскрибирует аудио в текст, затем NotebookLM ищет по базе дома) + # Передаем в legal_engine (Gemini транскрибирует аудио в текст, затем поиск по базе дома) result = await legal_engine.query(prompt="", role=role, audio_path=voice_file_path) await status_msg.delete() @@ -313,13 +318,8 @@ async def process_voice_legal_question(message: Message, state: FSMContext, bot: await message.answer(f"{result.get('text')}", parse_mode='HTML') return - transcribed = result.get("transcribed_question") - if transcribed: - header = f"⚖️ Юридический ответ по документам дома № 37:\n(Вопрос: «{format_telegram_html(transcribed)}»)" - else: - header = "⚖️ Юридический ответ по документам дома № 37:" - - await send_split_message(message, header, result["text"]) + # Отправляем чистый ответ без дублирования вопроса и без лишних плашек + await send_split_message(message, "", result["text"]) await state.set_state(LegalQuestionState.waiting_for_question) except Exception as e: diff --git a/services/ai_legal_engine.py b/services/ai_legal_engine.py index 0c3379b..75640c9 100644 --- a/services/ai_legal_engine.py +++ b/services/ai_legal_engine.py @@ -255,12 +255,7 @@ class AILegalEngine: # Никакого fallback на обычный Gemini! Выдаем честное уведомление об обновлении базы return { "success": False, - "text": ( - "❌ База документов дома № 37 временно обновляет сессию.\n\n" - "Юридический помощник не выдает выдуманных общих ответов нейросетей, " - "а обращается строго к протоколам ОСС, сметам и договорам нашего дома в NotebookLM. " - "Пожалуйста, повторите вопрос через минуту." - ), + "text": "❌ База документов дома № 37 временно обновляет сессию.", "role": role, "latency_sec": latency, "error_detail": err_msg