Проект аудита отелей: основные скрипты и документация
- Краулеры: 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:
289
merge_audit_results.py
Normal file
289
merge_audit_results.py
Normal 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}")
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user