197 lines
6.6 KiB
Python
197 lines
6.6 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Скрипт для создания коллекции в Qdrant для хранения эмбеддингов книг.
|
||
|
||
Структура данных основана на epub-parser/app.py:
|
||
- Каждая точка содержит эмбеддинг чанка текста
|
||
- Payload содержит метаданные книги, главы и чанка
|
||
"""
|
||
import requests
|
||
import json
|
||
from typing import Dict, Any
|
||
|
||
# Конфигурация Qdrant
|
||
QDRANT_URL = "http://localhost:6333"
|
||
COLLECTION_NAME = "epub_books"
|
||
VECTOR_SIZE = 768 # Размерность эмбеддингов nomic-embed-text
|
||
|
||
|
||
def create_collection() -> Dict[str, Any]:
|
||
"""Создает коллекцию в Qdrant для хранения эмбеддингов книг.
|
||
|
||
Returns:
|
||
Ответ от Qdrant API с результатом создания коллекции.
|
||
"""
|
||
url = f"{QDRANT_URL}/collections/{COLLECTION_NAME}"
|
||
|
||
payload = {
|
||
"vectors": {
|
||
"size": VECTOR_SIZE,
|
||
"distance": "Cosine" # Косинусное расстояние для семантического поиска
|
||
},
|
||
"optimizers_config": {
|
||
"default_segment_number": 2
|
||
},
|
||
"replication_factor": 1,
|
||
"write_consistency_factor": 1
|
||
}
|
||
|
||
response = requests.put(url, json=payload)
|
||
response.raise_for_status()
|
||
return response.json()
|
||
|
||
|
||
def create_payload_indexes() -> Dict[str, Any]:
|
||
"""Создает индексы для полей payload для быстрого поиска.
|
||
|
||
Returns:
|
||
Список ответов от Qdrant API.
|
||
"""
|
||
results = []
|
||
|
||
# Индексы для фильтрации и поиска
|
||
indexes = [
|
||
{
|
||
"field_name": "bookId",
|
||
"field_schema": "keyword"
|
||
},
|
||
{
|
||
"field_name": "title",
|
||
"field_schema": "keyword"
|
||
},
|
||
{
|
||
"field_name": "author",
|
||
"field_schema": "keyword"
|
||
},
|
||
{
|
||
"field_name": "chapterNumber",
|
||
"field_schema": "integer"
|
||
},
|
||
{
|
||
"field_name": "chapterTitle",
|
||
"field_schema": "text"
|
||
},
|
||
{
|
||
"field_name": "chapterId",
|
||
"field_schema": "keyword"
|
||
},
|
||
{
|
||
"field_name": "chunkIndex",
|
||
"field_schema": "integer"
|
||
}
|
||
]
|
||
|
||
for index in indexes:
|
||
url = f"{QDRANT_URL}/collections/{COLLECTION_NAME}/index"
|
||
payload = {
|
||
"field_name": index["field_name"],
|
||
"field_schema": {
|
||
"type": index["field_schema"]
|
||
}
|
||
}
|
||
try:
|
||
response = requests.post(url, json=payload)
|
||
response.raise_for_status()
|
||
results.append({
|
||
"field": index["field_name"],
|
||
"status": "created",
|
||
"response": response.json()
|
||
})
|
||
except requests.exceptions.HTTPError as e:
|
||
if e.response.status_code == 400:
|
||
# Индекс уже существует
|
||
results.append({
|
||
"field": index["field_name"],
|
||
"status": "already_exists"
|
||
})
|
||
else:
|
||
raise
|
||
|
||
return results
|
||
|
||
|
||
def check_collection_exists() -> bool:
|
||
"""Проверяет, существует ли коллекция.
|
||
|
||
Returns:
|
||
True если коллекция существует, False иначе.
|
||
"""
|
||
url = f"{QDRANT_URL}/collections/{COLLECTION_NAME}"
|
||
try:
|
||
response = requests.get(url)
|
||
return response.status_code == 200
|
||
except:
|
||
return False
|
||
|
||
|
||
def get_collection_info() -> Dict[str, Any]:
|
||
"""Получает информацию о коллекции.
|
||
|
||
Returns:
|
||
Информация о коллекции от Qdrant API.
|
||
"""
|
||
url = f"{QDRANT_URL}/collections/{COLLECTION_NAME}"
|
||
response = requests.get(url)
|
||
response.raise_for_status()
|
||
return response.json()
|
||
|
||
|
||
def main():
|
||
"""Основная функция для создания коллекции и индексов."""
|
||
print(f"Создание коллекции '{COLLECTION_NAME}' в Qdrant...")
|
||
print(f"URL: {QDRANT_URL}")
|
||
print(f"Размерность векторов: {VECTOR_SIZE}")
|
||
print()
|
||
|
||
# Проверяем, существует ли коллекция
|
||
if check_collection_exists():
|
||
print(f"⚠️ Коллекция '{COLLECTION_NAME}' уже существует.")
|
||
response = input("Удалить и создать заново? (y/n): ")
|
||
if response.lower() == 'y':
|
||
url = f"{QDRANT_URL}/collections/{COLLECTION_NAME}"
|
||
requests.delete(url)
|
||
print("✅ Коллекция удалена.")
|
||
else:
|
||
print("Используем существующую коллекцию.")
|
||
info = get_collection_info()
|
||
print(json.dumps(info, indent=2, ensure_ascii=False))
|
||
return
|
||
|
||
# Создаем коллекцию
|
||
try:
|
||
result = create_collection()
|
||
print("✅ Коллекция успешно создана!")
|
||
print(json.dumps(result, indent=2, ensure_ascii=False))
|
||
print()
|
||
|
||
# Создаем индексы
|
||
print("Создание индексов для payload...")
|
||
index_results = create_payload_indexes()
|
||
for idx_result in index_results:
|
||
status = idx_result.get("status", "unknown")
|
||
field = idx_result.get("field", "unknown")
|
||
if status == "created":
|
||
print(f" ✅ Индекс для '{field}' создан")
|
||
elif status == "already_exists":
|
||
print(f" ⚠️ Индекс для '{field}' уже существует")
|
||
print()
|
||
|
||
# Получаем информацию о коллекции
|
||
print("Информация о коллекции:")
|
||
info = get_collection_info()
|
||
print(json.dumps(info, indent=2, ensure_ascii=False))
|
||
|
||
except requests.exceptions.ConnectionError:
|
||
print("❌ Ошибка: Не удалось подключиться к Qdrant.")
|
||
print(f" Убедитесь, что Qdrant запущен на {QDRANT_URL}")
|
||
except requests.exceptions.HTTPError as e:
|
||
print(f"❌ Ошибка HTTP: {e}")
|
||
if e.response.text:
|
||
print(f" Ответ сервера: {e.response.text}")
|
||
except Exception as e:
|
||
print(f"❌ Неожиданная ошибка: {e}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|