Редактировать kb_service.py

This commit is contained in:
Markov Andrey
2026-06-30 22:50:36 +00:00
parent e60318cbd6
commit 95165b7f37

View File

@@ -166,16 +166,7 @@ class KBService:
update_if_exists: bool = True update_if_exists: bool = True
) -> Tuple[int, int]: ) -> Tuple[int, int]:
""" """
Индексирует документ в базу знаний с транзакционностью и атомарным обновлением: Индексирует документ в базу знаний с сохранением исходного текста в БД.
1. Если документ уже существует в комнате и update_if_exists=True,
запоминаем старый doc_id, но НЕ удаляем его сразу.
2. Создаём новую запись с indexed=False и новым doc_id.
3. Разбиваем на чанки, получаем эмбеддинги, загружаем в Qdrant.
4. Если успешно обновляем indexed=True у новой записи.
5. Удаляем старый документ (если он был) из Qdrant и PostgreSQL.
6. При ошибке оставляем старый документ нетронутым, новую запись удаляем (или оставляем с indexed=False для диагностики).
Это гарантирует, что в любой момент времени либо старая версия доступна, либо новая.
""" """
source_name = title if title else file_name source_name = title if title else file_name
doc_meta = metadata or {} doc_meta = metadata or {}
@@ -184,7 +175,7 @@ class KBService:
old_doc_id = None old_doc_id = None
# ---- 1. Поиск существующего документа в комнате (если нужно обновление) ---- # ---- 1. Поиск существующего документа в комнате ----
if update_if_exists and room_jid and not is_global: if update_if_exists and room_jid and not is_global:
existing = await self.db.find_document_by_name_and_room(source_name, room_jid) existing = await self.db.find_document_by_name_and_room(source_name, room_jid)
if existing: if existing:
@@ -195,7 +186,7 @@ class KBService:
return old_doc_id, 0 return old_doc_id, 0
logger.info(f"Обновление документа {source_name} в комнате {room_jid}: старая версия doc_id={old_doc_id}") logger.info(f"Обновление документа {source_name} в комнате {room_jid}: старая версия doc_id={old_doc_id}")
# ---- 2. Создание НОВОЙ записи в PostgreSQL с indexed=False ---- # ---- 2. Создание НОВОЙ записи в PostgreSQL с indexed=False и сохранением текста ----
new_doc_id = await self.db.add_document( new_doc_id = await self.db.add_document(
source_name=source_name, source_name=source_name,
owner_jid=user_jid, owner_jid=user_jid,
@@ -204,7 +195,8 @@ class KBService:
metadata=doc_meta, metadata=doc_meta,
room_jid=room_jid, room_jid=room_jid,
file_hash=file_hash, file_hash=file_hash,
indexed=False # начальное состояние не проиндексирован indexed=False,
file_text=file_text # <-- сохраняем исходный текст
) )
logger.info(f"Создана новая запись документа doc_id={new_doc_id} для {source_name}") logger.info(f"Создана новая запись документа doc_id={new_doc_id} для {source_name}")
@@ -231,7 +223,7 @@ class KBService:
sparse_vectors = await self.embedding.embed_sparse_batch(chunks) sparse_vectors = await self.embedding.embed_sparse_batch(chunks)
# ---- 5. Сохранение чанков в Qdrant (обёрнуто в to_thread) ---- # ---- 5. Сохранение чанков в Qdrant ----
chunk_count = 0 chunk_count = 0
success = True success = True
try: try:
@@ -269,7 +261,6 @@ class KBService:
# ---- 6. Если успешно обновляем флаг indexed у НОВОЙ записи ---- # ---- 6. Если успешно обновляем флаг indexed у НОВОЙ записи ----
if success and chunk_count > 0: if success and chunk_count > 0:
await self.db.update_document_indexed(new_doc_id, True) await self.db.update_document_indexed(new_doc_id, True)
# Для личных документов предоставляем доступ владельцу
if not is_global and not room_jid: if not is_global and not room_jid:
await self.db.add_access(new_doc_id, user_jid) await self.db.add_access(new_doc_id, user_jid)
logger.info(f"Новый документ {source_name} (doc_id={new_doc_id}) проиндексирован, чанков: {chunk_count}") logger.info(f"Новый документ {source_name} (doc_id={new_doc_id}) проиндексирован, чанков: {chunk_count}")
@@ -277,15 +268,11 @@ class KBService:
# ---- 7. Удаляем СТАРЫЙ документ (если был) ---- # ---- 7. Удаляем СТАРЫЙ документ (если был) ----
if old_doc_id is not None: if old_doc_id is not None:
logger.info(f"Удаление старой версии документа doc_id={old_doc_id}") logger.info(f"Удаление старой версии документа doc_id={old_doc_id}")
# Удаление из Qdrant
await asyncio.to_thread(self.qdrant.delete_by_doc_id, old_doc_id) await asyncio.to_thread(self.qdrant.delete_by_doc_id, old_doc_id)
# Удаление из PostgreSQL (включая права доступа)
await self.db.delete_documents_by_ids([old_doc_id]) await self.db.delete_documents_by_ids([old_doc_id])
logger.info(f"Старая версия doc_id={old_doc_id} удалена") logger.info(f"Старая версия doc_id={old_doc_id} удалена")
else: else:
# Не удалось загрузить все чанки оставляем новую запись с indexed=False для диагностики
logger.error(f"Документ {source_name} (doc_id={new_doc_id}) не проиндексирован (чанков: {chunk_count})") logger.error(f"Документ {source_name} (doc_id={new_doc_id}) не проиндексирован (чанков: {chunk_count})")
# Старый документ (если был) остаётся нетронутым
return new_doc_id, chunk_count return new_doc_id, chunk_count