- Краулеры: 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
249 lines
9.5 KiB
Python
249 lines
9.5 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Тест семантического поиска по Чукотскому автономному округу
|
||
на основе готовой базы с эмбеддингами
|
||
"""
|
||
|
||
import psycopg2
|
||
from urllib.parse import unquote
|
||
import requests
|
||
import json
|
||
import time
|
||
|
||
# API настройки
|
||
BGE_API_URL = "http://147.45.146.17:8002/embed"
|
||
BGE_API_KEY = "22564b177aa73b6ac0b8642d7773350ff4c01d4983f028beff15ea247f09fa89"
|
||
|
||
class ChukotkaAnalyzer:
|
||
def __init__(self):
|
||
self.conn = None
|
||
self.cur = None
|
||
self.connect_db()
|
||
|
||
def connect_db(self):
|
||
"""Подключение к базе данных"""
|
||
try:
|
||
self.conn = psycopg2.connect(
|
||
host='147.45.189.234',
|
||
port=5432,
|
||
database='default_db',
|
||
user='gen_user',
|
||
password=unquote('2~~9_%5EkVsU%3F2%5CS')
|
||
)
|
||
self.conn.autocommit = True
|
||
self.cur = self.conn.cursor()
|
||
print("✅ Подключение к БД установлено")
|
||
except Exception as e:
|
||
print(f"❌ Ошибка подключения к БД: {e}")
|
||
raise
|
||
|
||
def get_chukotka_stats(self):
|
||
"""Получение статистики по Чукотке"""
|
||
self.cur.execute("""
|
||
SELECT
|
||
COUNT(DISTINCT metadata->>'hotel_id') as hotels_count,
|
||
COUNT(*) as total_chunks,
|
||
AVG(LENGTH(text)) as avg_chunk_length
|
||
FROM hotel_website_chunks
|
||
WHERE metadata->>'region_name' = 'Чукотский автономный округ';
|
||
""")
|
||
|
||
result = self.cur.fetchone()
|
||
return {
|
||
'hotels_count': result[0],
|
||
'total_chunks': result[1],
|
||
'avg_chunk_length': result[2]
|
||
}
|
||
|
||
def get_chukotka_hotels(self):
|
||
"""Получение списка отелей Чукотки"""
|
||
self.cur.execute("""
|
||
SELECT DISTINCT
|
||
metadata->>'hotel_id' as hotel_id,
|
||
metadata->>'hotel_name' as hotel_name,
|
||
COUNT(*) as chunks_count
|
||
FROM hotel_website_chunks
|
||
WHERE metadata->>'region_name' = 'Чукотский автономный округ'
|
||
GROUP BY metadata->>'hotel_id', metadata->>'hotel_name'
|
||
ORDER BY chunks_count DESC;
|
||
""")
|
||
|
||
return self.cur.fetchall()
|
||
|
||
def generate_query_embedding(self, query: str):
|
||
"""Генерация эмбеддинга для поискового запроса"""
|
||
try:
|
||
headers = {
|
||
"X-API-Key": BGE_API_KEY,
|
||
"Content-Type": "application/json"
|
||
}
|
||
|
||
payload = {"text": query}
|
||
response = requests.post(BGE_API_URL, json=payload, headers=headers, timeout=30)
|
||
|
||
if response.status_code == 200:
|
||
result = response.json()
|
||
return result.get('embeddings', [[]])[0]
|
||
else:
|
||
print(f"❌ Ошибка API: {response.status_code}")
|
||
return None
|
||
|
||
except Exception as e:
|
||
print(f"❌ Ошибка генерации эмбеддинга: {e}")
|
||
return None
|
||
|
||
def search_chukotka(self, query: str, limit: int = 5):
|
||
"""Семантический поиск по Чукотке"""
|
||
query_embedding = self.generate_query_embedding(query)
|
||
if not query_embedding:
|
||
return []
|
||
|
||
embedding_str = json.dumps(query_embedding)
|
||
|
||
self.cur.execute("""
|
||
SELECT
|
||
metadata->>'hotel_name' as hotel_name,
|
||
metadata->>'url' as url,
|
||
LEFT(text, 150) as sample_text,
|
||
LENGTH(text) as text_length,
|
||
embedding <-> %s::vector as distance
|
||
FROM hotel_website_chunks
|
||
WHERE metadata->>'region_name' = 'Чукотский автономный округ'
|
||
AND embedding IS NOT NULL
|
||
ORDER BY embedding <-> %s::vector
|
||
LIMIT %s;
|
||
""", (embedding_str, embedding_str, limit))
|
||
|
||
return self.cur.fetchall()
|
||
|
||
def analyze_hotel_criteria(self, hotel_id: str):
|
||
"""Анализ отеля по критериям аудита"""
|
||
criteria_queries = {
|
||
'Юридическая идентификация': 'инн огрн егрюл организация',
|
||
'Контактная информация': 'телефон адрес email контакты',
|
||
'Политика конфиденциальности': 'политика конфиденциальности персональные данные',
|
||
'Условия бронирования': 'бронирование условия отмена возврат',
|
||
'Услуги отеля': 'услуги сервис завтрак wi-fi парковка',
|
||
'Доступность': 'доступность инвалиды коляска лифт'
|
||
}
|
||
|
||
results = {}
|
||
|
||
for criteria, query in criteria_queries.items():
|
||
self.cur.execute("""
|
||
SELECT
|
||
embedding <-> %s::vector as distance,
|
||
LEFT(text, 200) as sample_text
|
||
FROM hotel_website_chunks
|
||
WHERE metadata->>'hotel_id' = %s
|
||
AND embedding IS NOT NULL
|
||
ORDER BY embedding <-> %s::vector
|
||
LIMIT 1;
|
||
""", (json.dumps(self.generate_query_embedding(query)), hotel_id, json.dumps(self.generate_query_embedding(query))))
|
||
|
||
result = self.cur.fetchone()
|
||
if result:
|
||
distance, sample_text = result
|
||
relevance = "🟢 Высокая" if distance < 0.9 else "🟡 Средняя" if distance < 1.0 else "🔴 Низкая"
|
||
results[criteria] = {
|
||
'distance': distance,
|
||
'relevance': relevance,
|
||
'sample_text': sample_text
|
||
}
|
||
|
||
return results
|
||
|
||
def close(self):
|
||
"""Закрытие соединения с БД"""
|
||
if self.cur:
|
||
self.cur.close()
|
||
if self.conn:
|
||
self.conn.close()
|
||
|
||
def main():
|
||
print("="*70)
|
||
print("🏔️ АНАЛИЗ ЧУКОТСКОГО АВТОНОМНОГО ОКРУГА")
|
||
print("="*70)
|
||
|
||
analyzer = ChukotkaAnalyzer()
|
||
|
||
try:
|
||
# Статистика по региону
|
||
stats = analyzer.get_chukotka_stats()
|
||
print(f"\n📊 СТАТИСТИКА ПО ЧУКОТКЕ:")
|
||
print(f" Отелей: {stats['hotels_count']}")
|
||
print(f" Chunks: {stats['total_chunks']}")
|
||
print(f" Средняя длина chunk: {stats['avg_chunk_length']:.0f} символов")
|
||
|
||
# Список отелей
|
||
hotels = analyzer.get_chukotka_hotels()
|
||
print(f"\n🏨 ОТЕЛИ ЧУКОТКИ:")
|
||
print("-" * 70)
|
||
for hotel_id, hotel_name, chunks_count in hotels:
|
||
print(f" 🏨 {hotel_name}")
|
||
print(f" ID: {hotel_id}")
|
||
print(f" Chunks: {chunks_count}")
|
||
print()
|
||
|
||
# Тестовые поисковые запросы
|
||
test_queries = [
|
||
"телефон отеля",
|
||
"услуги и сервисы",
|
||
"бронирование номеров",
|
||
"адрес и контакты",
|
||
"политика конфиденциальности",
|
||
"завтрак и питание"
|
||
]
|
||
|
||
print("🔍 ТЕСТИРОВАНИЕ СЕМАНТИЧЕСКОГО ПОИСКА:")
|
||
print("-" * 70)
|
||
|
||
for query in test_queries:
|
||
print(f"\n🔍 Запрос: '{query}'")
|
||
results = analyzer.search_chukotka(query, 3)
|
||
|
||
for i, (hotel_name, url, sample_text, text_length, distance) in enumerate(results, 1):
|
||
if distance < 0.9:
|
||
relevance = "🟢 Отлично"
|
||
elif distance < 1.0:
|
||
relevance = "🟡 Хорошо"
|
||
else:
|
||
relevance = "🔴 Слабо"
|
||
|
||
print(f" {i}. Distance: {distance:.4f} {relevance}")
|
||
print(f" Отель: {hotel_name[:50]}...")
|
||
print(f" Текст: {sample_text}...")
|
||
print()
|
||
|
||
# Анализ одного отеля по критериям
|
||
if hotels:
|
||
test_hotel_id, test_hotel_name, _ = hotels[0]
|
||
print(f"\n📋 АНАЛИЗ ОТЕЛЯ ПО КРИТЕРИЯМ:")
|
||
print(f"🏨 {test_hotel_name}")
|
||
print("-" * 70)
|
||
|
||
criteria_results = analyzer.analyze_hotel_criteria(test_hotel_id)
|
||
|
||
for criteria, data in criteria_results.items():
|
||
print(f"{data['relevance']} {criteria}")
|
||
print(f" Distance: {data['distance']:.4f}")
|
||
print(f" Найденный текст: {data['sample_text'][:100]}...")
|
||
print()
|
||
|
||
print("="*70)
|
||
print("✅ АНАЛИЗ ЗАВЕРШЁН!")
|
||
print("="*70)
|
||
|
||
except Exception as e:
|
||
print(f"❌ Ошибка анализа: {e}")
|
||
finally:
|
||
analyzer.close()
|
||
|
||
if __name__ == "__main__":
|
||
main()
|
||
|
||
|
||
|
||
|
||
|