style(legal): purge internal labels, remove question echo, clean status messages

This commit is contained in:
Admin 2026-10-15 20:11:23 +04:00
parent 1abadd9081
commit b9acc30bf4
2 changed files with 27 additions and 32 deletions

View file

@ -73,7 +73,10 @@ async def send_split_message(message: Message, header: str, body: str):
for idx, chunk in enumerate(chunks, 1): for idx, chunk in enumerate(chunks, 1):
if idx == 1: if idx == 1:
if header:
part_header = f"{header}\n\n" if len(chunks) == 1 else f"{header} (Часть 1/{len(chunks)}):\n\n" 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"<i>(Часть 1/{len(chunks)}):</i>\n\n"
else: else:
part_header = f"<i>(Продолжение {idx}/{len(chunks)}):</i>\n\n" part_header = f"<i>(Продолжение {idx}/{len(chunks)}):</i>\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) await message.answer(part_text, parse_mode='HTML', reply_markup=reply_markup)
except Exception as e: except Exception as e:
log.warning(f"HTML send failed ({e}), falling back to plain text") log.warning(f"HTML send failed ({e}), falling back to plain text")
if header:
plain_header = header if idx == 1 else f"(Продолжение {idx}/{len(chunks)}):" plain_header = header if idx == 1 else f"(Продолжение {idx}/{len(chunks)}):"
await message.answer(f"{plain_header}\n\n{chunk}", reply_markup=reply_markup) 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: 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", "/вопрос"): if len(parts) > 1 and parts[0].casefold() in ("/юрист", "/jurist", "/вопрос"):
inline_question = parts[1].strip() inline_question = parts[1].strip()
if len(inline_question) > 3: if len(inline_question) > 3:
status_msg = await message.answer("⏳ <i>Изучаю документы, сметы и решения ОСС дома № 37 в блокноте NotebookLM...</i>", parse_mode='HTML') status_msg = await message.answer("⏳ <i>Изучаю документы, сметы, отчеты и решения дома № 37...</i>", parse_mode='HTML')
result = await legal_engine.query(prompt=inline_question, role=role) result = await legal_engine.query(prompt=inline_question, role=role)
await status_msg.delete() await status_msg.delete()
if result.get("success"): if result.get("success"):
header = "⚖️ <b>Юридический ответ по документам дома № 37:</b>" await send_split_message(message, "", result["text"])
await send_split_message(message, header, result["text"])
await state.set_state(LegalQuestionState.waiting_for_question) await state.set_state(LegalQuestionState.waiting_for_question)
else: else:
await message.answer(f"{result.get('text')}", parse_mode='HTML') 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 = ( text = (
"⚖️ <b>Юридический помощник (Дом № 37)</b>\n\n" "⚖️ <b>Юридический помощник (Дом № 37)</b>\n\n"
"Напишите ваш вопрос (текстом или отправьте голосовое сообщение).\n" "Напишите ваш вопрос или отправьте голосовое сообщение.\n"
"<i>Ответ формируется строго по сметам, договорам, протоколам ОСС и нормам ЖК РФ в блокноте NotebookLM.</i>" "<i>Ответ формируется по сметам, отчетам, договорам и решениям ОСС дома.</i>"
) )
await message.answer(text, parse_mode='HTML') await message.answer(text, parse_mode='HTML')
await state.set_state(LegalQuestionState.waiting_for_question) await state.set_state(LegalQuestionState.waiting_for_question)
@ -162,8 +168,8 @@ async def cb_legal_advisor(callback: CallbackQuery, state: FSMContext):
text = ( text = (
"⚖️ <b>Юридический помощник (Дом № 37)</b>\n\n" "⚖️ <b>Юридический помощник (Дом № 37)</b>\n\n"
"Напишите ваш вопрос (текстом или отправьте голосовое сообщение).\n" "Напишите ваш вопрос или отправьте голосовое сообщение.\n"
"<i>Ответ формируется строго по сметам, договорам, протоколам ОСС и нормам ЖК РФ в блокноте NotebookLM.</i>" "<i>Ответ формируется по сметам, отчетам, договорам и решениям ОСС дома.</i>"
) )
await callback.message.answer(text, parse_mode='HTML') await callback.message.answer(text, parse_mode='HTML')
await state.set_state(LegalQuestionState.waiting_for_question) 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 question = message.text
status_msg = await message.answer( status_msg = await message.answer(
"⏳ <i>Изучаю документы, сметы и решения ОСС дома № 37 в блокноте NotebookLM...</i>", "⏳ <i>Изучаю документы, сметы, отчеты и решения дома № 37...</i>",
parse_mode='HTML' parse_mode='HTML'
) )
@ -256,23 +262,22 @@ async def process_text_legal_question(message: Message, state: FSMContext, bot:
try: try:
await bot.send_message( await bot.send_message(
chat_id=user_id, chat_id=user_id,
text=f"⚖️ <b>Юридический ответ по документам дома № 37:</b>\n\n{format_telegram_html(result['text'])}", text=format_telegram_html(result['text']),
parse_mode='HTML', parse_mode='HTML',
reply_markup=get_legal_nav_keyboard() reply_markup=get_legal_nav_keyboard()
) )
await message.reply( await message.reply(
"💬 <b>Ответ сформирован и отправлен вам в личные сообщения с ботом</b>, чтобы не перегружать общий чат дома.", "💬 <b>Ответ отправлен вам в личные сообщения</b>, чтобы не перегружать общий чат дома.",
parse_mode='HTML' parse_mode='HTML'
) )
except Exception: except Exception:
await message.reply( await message.reply(
"💬 Пожалуйста, откройте личные сообщения с ботом (@lkmthreeseven_bot) и нажмите /start, чтобы получить ответ конфиденциально.", "💬 Пожалуйста, откройте диалог с ботом (@lkmthreeseven_bot) и нажмите /start, чтобы получить ответ.",
parse_mode='HTML' parse_mode='HTML'
) )
else: else:
# В личном диалоге отвечаем напрямую # В личном диалоге отвечаем напрямую без лишних префиксов
header = "⚖️ <b>Юридический ответ по документам дома № 37:</b>" await send_split_message(message, "", result["text"])
await send_split_message(message, header, result["text"])
# Оставляем состояние активным, чтобы жилец мог сразу задать следующий вопрос без клика # Оставляем состояние активным, чтобы жилец мог сразу задать следующий вопрос без клика
await state.set_state(LegalQuestionState.waiting_for_question) await state.set_state(LegalQuestionState.waiting_for_question)
@ -293,7 +298,7 @@ async def process_voice_legal_question(message: Message, state: FSMContext, bot:
return return
status_msg = await message.answer( status_msg = await message.answer(
"🎙 <i>Распознаю аудио и сверяю с документами дома № 37 в NotebookLM...</i>", "🎙 <i>Распознаю аудио и сверяю с документами дома № 37...</i>",
parse_mode='HTML' 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") 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) 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) result = await legal_engine.query(prompt="", role=role, audio_path=voice_file_path)
await status_msg.delete() 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') await message.answer(f"{result.get('text')}", parse_mode='HTML')
return return
transcribed = result.get("transcribed_question") # Отправляем чистый ответ без дублирования вопроса и без лишних плашек
if transcribed: await send_split_message(message, "", result["text"])
header = f"⚖️ <b>Юридический ответ по документам дома № 37:</b>\n<i>(Вопрос: «{format_telegram_html(transcribed)}»)</i>"
else:
header = "⚖️ <b>Юридический ответ по документам дома № 37:</b>"
await send_split_message(message, header, result["text"])
await state.set_state(LegalQuestionState.waiting_for_question) await state.set_state(LegalQuestionState.waiting_for_question)
except Exception as e: except Exception as e:

View file

@ -255,12 +255,7 @@ class AILegalEngine:
# Никакого fallback на обычный Gemini! Выдаем честное уведомление об обновлении базы # Никакого fallback на обычный Gemini! Выдаем честное уведомление об обновлении базы
return { return {
"success": False, "success": False,
"text": ( "text": "❌ <b>База документов дома № 37 временно обновляет сессию.</b>",
"❌ <b>База документов дома № 37 временно обновляет сессию.</b>\n\n"
"Юридический помощник не выдает выдуманных общих ответов нейросетей, "
"а обращается строго к протоколам ОСС, сметам и договорам нашего дома в NotebookLM. "
"Пожалуйста, повторите вопрос через минуту."
),
"role": role, "role": role,
"latency_sec": latency, "latency_sec": latency,
"error_detail": err_msg "error_detail": err_msg