Files
hotels/merge_audit_results.py
Фёдор 684fada337 🚀 Full project sync: Hotels RAG & Audit System
 Major Features:
- Complete RAG system for hotel website analysis
- Hybrid audit with BGE-M3 embeddings + Natasha NER
- Universal horizontal Excel reports with dashboards
- Multi-region processing (SPb, Orel, Chukotka, Kamchatka)

📊 Completed Regions:
- Орловская область: 100% (36/36)
- Чукотский АО: 100% (4/4)
- г. Санкт-Петербург: 93% (893/960)
- Камчатский край: 87% (89/102)

🔧 Infrastructure:
- PostgreSQL with pgvector extension
- BGE-M3 embeddings API
- Browserless for web scraping
- N8N workflows for automation
- S3/Nextcloud file storage

📝 Documentation:
- Complete DB schemas
- API documentation
- Setup guides
- Status reports
2025-10-27 22:49:42 +03:00

291 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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}")