Проект аудита отелей: основные скрипты и документация

- Краулеры: smart_crawler.py, regional_crawler.py
- Аудит: audit_orel_to_excel.py, audit_chukotka_to_excel.py
- РКН проверка: check_rkn_registry.py, recheck_unclear_rkn.py
- Отчёты: create_orel_horizontal_report.py
- Обработка: process_all_hotels_embeddings.py
- Документация: README.md, DB_SCHEMA_REFERENCE.md
This commit is contained in:
Фёдор
2025-10-16 10:52:09 +03:00
parent 545e199389
commit 0cf3297290
105 changed files with 28743 additions and 0 deletions

289
merge_audit_results.py Normal file
View File

@@ -0,0 +1,289 @@
#!/usr/bin/env python3
"""
Объединение результатов AI Agent и Regex в единую структуру
"""
import json
# Определяем 17 критериев
CRITERIA = [
{
"id": 1,
"name": "Юридическая идентификация и верификация",
"description": "ИНН, ОГРН, полное наименование организации"
},
{
"id": 2,
"name": "Адрес",
"description": "Юридический и фактический адрес, местонахождение"
},
{
"id": 3,
"name": "Контакты",
"description": "Телефон, email, форма обратной связи"
},
{
"id": 4,
"name": "Режим работы",
"description": "Часы работы, график приема, колл-центр"
},
{
"id": 5,
"name": "Политика ПДн (152-ФЗ)",
"description": "Политика персональных данных, обработка ПДн"
},
{
"id": 7,
"name": "Договор-оферта / Правила оказания услуг",
"description": "Публичная оферта, пользовательское соглашение, условия"
},
{
"id": 8,
"name": "Рекламации и споры",
"description": "Претензии, возврат, обмен, жалобы"
},
{
"id": 9,
"name": "Цены/прайс",
"description": "Цены, стоимость, тарифы"
},
{
"id": 10,
"name": "Способы оплаты",
"description": "Наличные, карта, СБП"
},
{
"id": 11,
"name": "Онлайн-оплата",
"description": "Эквайринг, оплата онлайн"
},
{
"id": 12,
"name": "Онлайн-бронирование",
"description": "Забронировать, booking"
},
{
"id": 13,
"name": "FAQ",
"description": "Частые вопросы, вопрос-ответ"
},
{
"id": 14,
"name": "Доступность для ЛОВЗ",
"description": "Инвалиды, безбарьерная среда, маломобильные"
},
{
"id": 15,
"name": "Партнёры/бренды",
"description": "Партнеры, поставщики, сотрудничество"
},
{
"id": 16,
"name": "Команда/сотрудники",
"description": "Команда, персонал, руководство"
},
{
"id": 17,
"name": "Уголок потребителя",
"description": "Права потребителей, защита"
},
{
"id": 18,
"name": "Актуальность документов",
"description": "Дата обновления, версия"
}
]
def merge_results(ai_results, regex_results):
"""
Объединяет результаты AI Agent и Regex
ai_results: список из 17 элементов с детальными ответами AI
regex_results: список из 17 элементов с простыми ДА/НЕТ от regex
"""
merged = []
for i, criterion in enumerate(CRITERIA):
# Берём результаты AI и Regex для этого критерия
ai_result = ai_results[i] if i < len(ai_results) else {}
regex_result = regex_results[i] if i < len(regex_results) else {}
# Извлекаем данные из AI результата
ai_output = ai_result.get('output', {})
ai_found = ai_output.get('found', False)
ai_score = ai_output.get('score', 0)
ai_quote = ai_output.get('quote', '')
ai_url = ai_output.get('url', '')
ai_details = ai_output.get('details', '')
ai_confidence = ai_output.get('confidence', 'Не определена')
ai_checked_pages = ai_output.get('checked_pages', 0)
# Извлекаем данные из Regex результата
regex_output = regex_result.get('output', {})
regex_found = regex_output.get('found', False)
regex_answer = regex_output.get('answer', 'НЕТ')
regex_extracted = regex_output.get('extracted', '')
regex_confidence = regex_output.get('confidence', 'Не определена')
# Объединяем результаты
merged_item = {
"criterion_id": criterion['id'],
"criterion_name": criterion['name'],
"criterion_description": criterion['description'],
# Общий результат (ДА если хотя бы один метод нашёл)
"found": ai_found or regex_found,
"status": "НАЙДЕНО" if (ai_found or regex_found) else "НЕ НАЙДЕНО",
# Оценка (0-1)
"score": max(ai_score, 1 if regex_found else 0),
# AI Agent результаты
"ai_agent": {
"found": ai_found,
"score": ai_score,
"quote": ai_quote,
"url": ai_url,
"details": ai_details,
"confidence": ai_confidence,
"checked_pages": ai_checked_pages
},
# Regex результаты
"regex": {
"found": regex_found,
"answer": regex_answer,
"extracted": regex_extracted,
"confidence": regex_confidence
},
# Итоговая уверенность
"final_confidence": _calculate_final_confidence(ai_confidence, regex_confidence, ai_found, regex_found)
}
merged.append(merged_item)
return merged
def _calculate_final_confidence(ai_conf, regex_conf, ai_found, regex_found):
"""Рассчитывает итоговую уверенность"""
# Если оба нашли - высокая
if ai_found and regex_found:
return "Очень высокая"
# Если один нашёл с высокой уверенностью
if (ai_found and ai_conf == "Высокая") or (regex_found and regex_conf == "Высокая"):
return "Высокая"
# Если один нашёл со средней уверенностью
if (ai_found and ai_conf == "Средняя") or (regex_found and regex_conf == "Средняя"):
return "Средняя"
# Если оба не нашли с высокой уверенностью - точно нет
if not ai_found and not regex_found and ai_conf == "Высокая" and regex_conf == "Высокая":
return "Высокая (не найдено)"
# Иначе - низкая
return "Низкая"
def format_summary(merged_results):
"""Форматирует краткую сводку"""
total = len(merged_results)
found_count = sum(1 for r in merged_results if r['found'])
not_found_count = total - found_count
summary = {
"hotel_name": "Городской отель \"Комфорт\"",
"region": "Камчатский край",
"audit_date": "2025-10-14",
"total_criteria": total,
"found": found_count,
"not_found": not_found_count,
"compliance_percentage": round(found_count / total * 100, 1),
"criteria_results": merged_results
}
return summary
def print_summary(summary):
"""Выводит красивую сводку"""
print(f"\n{'='*80}")
print(f"📊 СВОДКА АУДИТА: {summary['hotel_name']}")
print(f"{'='*80}")
print(f"\n📍 Регион: {summary['region']}")
print(f"📅 Дата аудита: {summary['audit_date']}")
print(f"\n✅ Найдено: {summary['found']}/{summary['total_criteria']} ({summary['compliance_percentage']}%)")
print(f"Не найдено: {summary['not_found']}/{summary['total_criteria']}")
print(f"\n{'='*80}")
print("ДЕТАЛЬНЫЕ РЕЗУЛЬТАТЫ:")
print(f"{'='*80}\n")
for result in summary['criteria_results']:
status_icon = "" if result['found'] else ""
print(f"{status_icon} [{result['criterion_id']}] {result['criterion_name']}")
print(f" Статус: {result['status']}")
print(f" Оценка: {result['score']}")
print(f" Уверенность: {result['final_confidence']}")
if result['ai_agent']['found']:
print(f" 🤖 AI Agent: найдено")
if result['ai_agent']['url']:
print(f" URL: {result['ai_agent']['url']}")
if result['ai_agent']['details']:
print(f" Детали: {result['ai_agent']['details'][:100]}...")
if result['regex']['found']:
print(f" 🔍 Regex: найдено")
if result['regex']['extracted']:
print(f" Извлечено: {result['regex']['extracted'][:100]}...")
print()
# Пример использования
if __name__ == "__main__":
# Загружаем данные из файла или используем переданные
import sys
if len(sys.argv) > 1:
# Загружаем из файла
with open(sys.argv[1], 'r', encoding='utf-8') as f:
data = json.load(f)
else:
# Используем тестовые данные
print("⚠️ Использование: python merge_audit_results.py <файл_с_результатами.json>")
print(" Или передайте данные через stdin")
sys.exit(1)
# Разделяем на AI и Regex результаты
# Первые 17 - от AI Agent, последние 17 - от Regex
ai_results = data[:17]
regex_results = data[17:34]
# Объединяем
merged = merge_results(ai_results, regex_results)
# Формируем сводку
summary = format_summary(merged)
# Выводим
print_summary(summary)
# Сохраняем в файл
output_file = "audit_merged_results.json"
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(summary, f, ensure_ascii=False, indent=2)
print(f"\n💾 Результаты сохранены в {output_file}")