* Delete legacy from bot * Clear old models * Единый http клиент * РАГ полечен
This commit is contained in:
@@ -1,137 +1,130 @@
|
||||
import aiohttp
|
||||
from tg_bot.infrastructure.external.deepseek_client import DeepSeekClient
|
||||
"""
|
||||
RAG сервис для бота - вызывает API бэкенда
|
||||
"""
|
||||
from tg_bot.config.settings import settings
|
||||
from tg_bot.infrastructure.http_client import create_http_session
|
||||
|
||||
|
||||
class RAGService:
|
||||
"""Сервис для работы с RAG через API бэкенда"""
|
||||
|
||||
def __init__(self):
|
||||
self.deepseek_client = DeepSeekClient()
|
||||
|
||||
async def search_documents_in_collections(
|
||||
self,
|
||||
user_telegram_id: str,
|
||||
query: str,
|
||||
limit_per_collection: int = 5
|
||||
) -> list[dict]:
|
||||
async def get_or_create_conversation(
|
||||
self,
|
||||
user_telegram_id: str,
|
||||
collection_id: str = None
|
||||
) -> str | None:
|
||||
"""Получить или создать беседу для пользователя"""
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with create_http_session() as session:
|
||||
async with session.get(
|
||||
f"{settings.BACKEND_URL}/users/telegram/{user_telegram_id}"
|
||||
) as user_response:
|
||||
if user_response.status != 200:
|
||||
return []
|
||||
|
||||
user_data = await user_response.json()
|
||||
user_uuid = str(user_data.get("user_id"))
|
||||
|
||||
if not user_uuid:
|
||||
return []
|
||||
|
||||
async with session.get(
|
||||
f"{settings.BACKEND_URL}/collections/",
|
||||
headers={"X-Telegram-ID": user_telegram_id}
|
||||
f"{settings.BACKEND_URL}/collections/",
|
||||
headers={"X-Telegram-ID": user_telegram_id}
|
||||
) as collections_response:
|
||||
if collections_response.status != 200:
|
||||
return []
|
||||
|
||||
return None
|
||||
collections = await collections_response.json()
|
||||
|
||||
all_documents = []
|
||||
for collection in collections:
|
||||
collection_id = collection.get("collection_id")
|
||||
if not collections:
|
||||
if not collection_id:
|
||||
async with session.post(
|
||||
f"{settings.BACKEND_URL}/collections",
|
||||
json={
|
||||
"name": "Основная коллекция",
|
||||
"description": "Коллекция по умолчанию",
|
||||
"is_public": False
|
||||
},
|
||||
headers={"X-Telegram-ID": user_telegram_id}
|
||||
) as create_collection_response:
|
||||
if create_collection_response.status in [200, 201]:
|
||||
collection_data = await create_collection_response.json()
|
||||
collection_id = collection_data.get("collection_id")
|
||||
else:
|
||||
collection_id = collection_id
|
||||
else:
|
||||
collection_id = collections[0].get("collection_id")
|
||||
|
||||
if not collection_id:
|
||||
continue
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as search_session:
|
||||
async with search_session.get(
|
||||
f"{settings.BACKEND_URL}/documents/collection/{collection_id}",
|
||||
params={"search": query, "limit": limit_per_collection},
|
||||
headers={"X-Telegram-ID": user_telegram_id}
|
||||
) as search_response:
|
||||
if search_response.status == 200:
|
||||
documents = await search_response.json()
|
||||
for doc in documents:
|
||||
doc["collection_name"] = collection.get("name", "Unknown")
|
||||
all_documents.append(doc)
|
||||
except Exception as e:
|
||||
print(f"Error searching collection {collection_id}: {e}")
|
||||
continue
|
||||
|
||||
return all_documents[:20]
|
||||
|
||||
return None
|
||||
|
||||
async with session.get(
|
||||
f"{settings.BACKEND_URL}/conversations",
|
||||
headers={"X-Telegram-ID": user_telegram_id}
|
||||
) as conversations_response:
|
||||
if conversations_response.status == 200:
|
||||
conversations = await conversations_response.json()
|
||||
for conv in conversations:
|
||||
if conv.get("collection_id") == str(collection_id):
|
||||
return conv.get("conversation_id")
|
||||
|
||||
async with session.post(
|
||||
f"{settings.BACKEND_URL}/conversations",
|
||||
json={"collection_id": str(collection_id)},
|
||||
headers={"X-Telegram-ID": user_telegram_id}
|
||||
) as create_conversation_response:
|
||||
if create_conversation_response.status in [200, 201]:
|
||||
conversation_data = await create_conversation_response.json()
|
||||
return conversation_data.get("conversation_id")
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error searching documents: {e}")
|
||||
return []
|
||||
print(f"Error getting/creating conversation: {e}")
|
||||
return None
|
||||
|
||||
async def generate_answer_with_rag(
|
||||
self,
|
||||
question: str,
|
||||
user_telegram_id: str
|
||||
self,
|
||||
question: str,
|
||||
user_telegram_id: str
|
||||
) -> dict:
|
||||
documents = await self.search_documents_in_collections(
|
||||
user_telegram_id,
|
||||
question
|
||||
)
|
||||
|
||||
context_parts = []
|
||||
sources = []
|
||||
|
||||
for doc in documents[:5]:
|
||||
title = doc.get("title", "Без названия")
|
||||
content = doc.get("content", "")[:1000]
|
||||
collection_name = doc.get("collection_name", "Unknown")
|
||||
|
||||
context_parts.append(f"Документ: {title}\nКоллекция: {collection_name}\nСодержание: {content[:500]}...")
|
||||
sources.append({
|
||||
"title": title,
|
||||
"collection": collection_name,
|
||||
"document_id": doc.get("document_id")
|
||||
})
|
||||
|
||||
context = "\n\n".join(context_parts) if context_parts else "Релевантные документы не найдены."
|
||||
|
||||
system_prompt = """Ты - помощник-юрист, который отвечает на вопросы на основе предоставленных документов.
|
||||
Используй информацию из документов для формирования точного и полезного ответа.
|
||||
Если в документах нет информации для ответа, честно скажи об этом."""
|
||||
|
||||
user_prompt = f"""Контекст из документов:
|
||||
{context}
|
||||
|
||||
Вопрос пользователя: {question}
|
||||
|
||||
Ответь на вопрос, используя информацию из предоставленных документов. Если информации недостаточно, укажи это."""
|
||||
|
||||
"""Генерирует ответ используя RAG через API бэкенда"""
|
||||
try:
|
||||
messages = [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt}
|
||||
]
|
||||
|
||||
response = await self.deepseek_client.chat_completion(
|
||||
messages=messages,
|
||||
temperature=0.7,
|
||||
max_tokens=2000
|
||||
)
|
||||
|
||||
return {
|
||||
"answer": response.get("content", "Failed to generate answer"),
|
||||
"sources": sources,
|
||||
"usage": response.get("usage", {})
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error generating answer: {e}")
|
||||
if documents:
|
||||
conversation_id = await self.get_or_create_conversation(user_telegram_id)
|
||||
if not conversation_id:
|
||||
return {
|
||||
"answer": f"Found {len(documents)} documents but failed to generate answer",
|
||||
"sources": sources[:3],
|
||||
"usage": {}
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"answer": "No relevant documents found",
|
||||
"answer": "Не удалось создать беседу. Попробуйте позже.",
|
||||
"sources": [],
|
||||
"usage": {}
|
||||
}
|
||||
|
||||
async with create_http_session() as session:
|
||||
async with session.post(
|
||||
f"{settings.BACKEND_URL}/rag/question",
|
||||
json={
|
||||
"conversation_id": str(conversation_id),
|
||||
"question": question,
|
||||
"top_k": 20,
|
||||
"rerank_top_n": 5
|
||||
},
|
||||
headers={"X-Telegram-ID": user_telegram_id}
|
||||
) as response:
|
||||
if response.status == 200:
|
||||
result = await response.json()
|
||||
sources = []
|
||||
for source in result.get("sources", []):
|
||||
sources.append({
|
||||
"title": source.get("title", "Без названия"),
|
||||
"document_id": source.get("document_id"),
|
||||
"chunk_id": source.get("chunk_id"),
|
||||
"index": source.get("index", 0)
|
||||
})
|
||||
|
||||
return {
|
||||
"answer": result.get("answer", "Не удалось сгенерировать ответ."),
|
||||
"sources": sources,
|
||||
"usage": result.get("usage", {}),
|
||||
"conversation_id": str(conversation_id)
|
||||
}
|
||||
else:
|
||||
error_text = await response.text()
|
||||
print(f"RAG API error: {response.status} - {error_text}")
|
||||
return {
|
||||
"answer": "Ошибка при генерации ответа. Попробуйте позже.",
|
||||
"sources": [],
|
||||
"usage": {}
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"Error generating answer with RAG: {e}")
|
||||
return {
|
||||
"answer": "Произошла ошибка при генерации ответа. Попробуйте позже.",
|
||||
"sources": [],
|
||||
"usage": {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user