diff --git a/ollama_service.py b/ollama_service.py index 79fce8d..16da7de 100644 --- a/ollama_service.py +++ b/ollama_service.py @@ -12,15 +12,19 @@ import httpx from typing import List, Optional import asyncio import os +from elasticsearch import Elasticsearch +from datetime import datetime app = FastAPI() OLLAMA_BASE_URL = "http://ollama:11434" +ES_BASE_URL = "http://elastic:9200" WEAVIATE_URL = "http://weaviate:8080" PROMPT_DIR_PATCH = "./prompts" # Inicjalizacja klientów ollama_client = ollama.Client(host=OLLAMA_BASE_URL) +es = Elasticsearch(ES_BASE_URL) weaviate_client = weaviate.WeaviateClient( connection_params=ConnectionParams.from_params( http_host="weaviate", @@ -113,7 +117,7 @@ def hybrid_search(keywords, limit=100, alpha=0.5): query = " ".join(keywords) - print(f"\nWyszukiwanie hybrydowe dla słowa kluczowego: '{query}'") + #print(f"\nWyszukiwanie hybrydowe dla słowa kluczowego: '{query}'") response = collection.query.hybrid( query=query, alpha=alpha, @@ -136,7 +140,7 @@ def hybrid_search(keywords, limit=100, alpha=0.5): "file_name": obj.properties['fileName'], "keyword": query }) - print(f"Dodano do wyników: {obj.uuid}") + #print(f"Dodano do wyników: {obj.uuid}") if len(results) >= limit: break @@ -183,6 +187,49 @@ async def stream_chat(model, messages, options): except Exception as e: yield json.dumps({"error": str(e)}) + "\n" +def save_to_elasticsearch(index, request_data, response_data, search_data=None): + try: + def message_to_dict(message): + if isinstance(message, dict): + return { + "role": message.get("role"), + "content": message.get("content") + } + return { + "role": getattr(message, "role", None), + "content": getattr(message, "content", None) + } + + if isinstance(request_data.get("messages"), list): + request_data["messages"] = [message_to_dict(msg) for msg in request_data["messages"]] + + if "message" in response_data: + response_data["message"] = message_to_dict(response_data["message"]) + + if "timestamp" in response_data: + response_data["timestamp"] = response_data["timestamp"].isoformat() + + document = { + "request": request_data, + "response": response_data, + "timestamp": datetime.utcnow().isoformat(), + } + + if search_data: + document["vector_search"] = { + "keywords": search_data.get("keywords", []), + "results": search_data.get("results", []) + } + + json_document = json.dumps(document, default=str) + + index_name = index + response = es.index(index=index_name, body=json_document) + + #print(response) + except Exception as e: + print(f"Error saving to Elasticsearch: {e}") + @app.post("/api/chat") async def chat_endpoint(request: ChatRequest): try: @@ -209,9 +256,7 @@ async def chat_endpoint(request: ChatRequest): query = request.messages[-1].content if request.messages else "" keywords = analyze_query(prompt_seach.format(query=query)) weaviate_results = hybrid_search(keywords) - - prompt_data = "\n".join([f"Źródło: {doc['file_name']}\n{doc['relevant_fragment']}\n\n" for doc in weaviate_results]) - print(prompt_data) + prompt_data += "\n".join([f"Źródło: {doc['file_name']}\n{doc['relevant_fragment']}\n\n" for doc in weaviate_results]) messages_with_context =[ {"role": "system", "content": prompt_system}, @@ -228,6 +273,26 @@ async def chat_endpoint(request: ChatRequest): stream=False, options=request.options ) + + request_data = { + "query": query, + "messages": request.messages, + "options": request.options + } + response_data = { + "model": request.model, + "created_at": ollama_response.get('created_at', ''), + "message": ollama_response['message'], + "done": ollama_response.get('done', True), + "total_duration": ollama_response.get('total_duration', 0), + "load_duration": ollama_response.get('load_duration', 0), + "prompt_eval_count": ollama_response.get('prompt_eval_count', 0), + "prompt_eval_duration": ollama_response.get('prompt_eval_duration', 0), + "eval_count": ollama_response.get('eval_count', 0), + "eval_duration": ollama_response.get('eval_duration', 0) + } + save_to_elasticsearch("ably.do", request_data, response_data, {"keywords": keywords, "results": weaviate_results }) + return ChatResponse( model=request.model, created_at=ollama_response.get('created_at', ''), diff --git a/prompts/prompt_seach.txt b/prompts/prompt_seach.txt index 8797301..e5580db 100644 --- a/prompts/prompt_seach.txt +++ b/prompts/prompt_seach.txt @@ -1,15 +1,26 @@ -Jesteś precyzyjnym narzędziem do generowania słów kluczowych z zakresu BHP i prawa pracy. Twoje zadanie to podanie WYŁĄCZNIE najistotniejszych słów do wyszukiwania w bazie dokumentów prawnych. +Jesteś precyzyjnym narzędziem do generowania słów kluczowych z zakresu BHP i prawa pracy. Twoje zadanie to podanie WYŁĄCZNIE najistotniejszych terminów do wyszukiwania w bazie dokumentów prawnych. -Ścisłe zasady: +Ścisłe zasady działania: 1. Jeśli zapytanie dotyczy konkretnego artykułu: - Podaj TYLKO numer artykułu i nazwę kodeksu (np. "Art. 154, Kodeks pracy"). - - NIE dodawaj żadnych innych słów. -2. Jeśli zapytanie nie dotyczy konkretnego artykułu: - - Podaj maksymalnie 3 najbardziej specyficzne terminy związane z zapytaniem. - - Unikaj ogólnych słów jak "praca", "pracownik", "pracodawca", chyba że są częścią specjalistycznego terminu. -3. Używaj wyłącznie terminów, które z pewnością występują w dokumentach prawnych lub specjalistycznych opracowaniach. -4. NIE dodawaj własnych interpretacji ani rozszerzeń zapytania. + - NIE dodawaj żadnych dodatkowych słów. -Odpowiedz TYLKO listą słów kluczowych oddzielonych przecinkami, bez żadnych dodatkowych wyjaśnień czy komentarzy. +2. Jeśli zapytanie nie odnosi się do konkretnego artykułu: + - Wybierz maksymalnie 3 najbardziej precyzyjne i specyficzne terminy związane z treścią zapytania. + - Unikaj ogólników takich jak „praca”, „pracownik”, „pracodawca” – chyba że są częścią specjalistycznego terminu prawnego. -Zapytanie: '{query}' \ No newline at end of file +3. Używaj wyłącznie terminów, które realnie występują w dokumentach prawnych lub specjalistycznych opracowaniach. + +4. Nie dodawaj żadnych interpretacji, rozszerzeń ani dodatkowych komentarzy. + +Format odpowiedzi: +- Odpowiedz wyłącznie listą słów kluczowych oddzielonych przecinkami, bez dodatkowego tekstu, wyjaśnień czy formatowania JSON. + +Przykład: +- Zapytanie: Jak brzmi art. 3 Kodeksu pracy? +- Odpowiedź: Art. 3, Kodeks pracy + +- Zapytanie: Obowiązki pracodawcy w zakresie BHP +- Odpowiedź: obowiązki pracodawcy, BHP, przepisy bezpieczeństwa + +Zapytanie: {query} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 8e207d7..eda4e31 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,5 @@ fastapi uvicorn ollama weaviate-client -unidecode \ No newline at end of file +unidecode +elasticsearch \ No newline at end of file