From 0d681ea2ba6bea776b037bd36a39640c0550a7f7 Mon Sep 17 00:00:00 2001 From: oleh Date: Sat, 5 Apr 2025 23:39:54 +0200 Subject: [PATCH] upd docker prepare script --- Backend/__pycache__/model.cpython-311.pyc | Bin 27235 -> 28081 bytes Backend/arch.py | 122 ++++++++++++++++++++++ Backend/model.py | 69 +++++++++--- Backend/tablepresent.py | 56 ++++++++++ prepare.sh | 7 +- 5 files changed, 238 insertions(+), 16 deletions(-) create mode 100644 Backend/arch.py create mode 100644 Backend/tablepresent.py diff --git a/Backend/__pycache__/model.cpython-311.pyc b/Backend/__pycache__/model.cpython-311.pyc index 5463232037e889aa874ac60af65d763e24fe146f..59d37d88fa8d17f4030851bb9576706a5af5c41b 100644 GIT binary patch delta 1782 zcmbVKe{54#6u!6bb^Wof8;p)VZ0j4_+Ld){3j(q^gl&wCb->uhU?BL```Q({cD8S0 zt8GS1g!uzUxEP3FQey&t7}VvdAsP`jOfXSwXA_zlwTZ^R3=We;jR|@m-CPV}h$ruU z=bm%Ecg}b3d*7$v$)&!o~9y3SHXfv}w0x=7T8y92L zw78E|lA3EVj$5Qz%sNzjK}IH#m9v<2UP*^bx6Tr+j9ULq$;&$kt39Vh=va6cfM_|H zf#Z$(!c_v1ykLi{_>;CWrV4G3R-FTA$Lmzg7F~sQMhGER9j(THw5^97c#Ug^f?SFR zqFp%FS}pHHdenvAXb)(&=2dzC%cLM7kaiaYVIh?}I z&b#m>?C5fBR__c2!_$`rY-|t>28VgX9b&^mXz2Qh0MBwP3Wfzv2uhKS?c*bCfI|a( zD8L_L(e)2My>fUIKil;=`~kOgb7Mmma*X==dUp5p$6-H@4v(_^K@WyxmUf_KE!8F_mU@%~KSE>7?Tyb}+`4in*t)`zv)hb7*}Q{%V0zmw52JhA&UT!qVfetk5P=Lv=eI3c45@q>QO z&4-o~JsCL~8W;>)ouYNmD{yTQpSv1gFmO=d`uu!G<>ms&hsbS(x(Gj1CcUJcr}5~Z zz>nbf_qu5($>Y*qGj{Z<@z&llvl0T(LsmGh0^ql!;2TgXBJA`?7YM+J5GLPWf`xq)qtFUH&xhk7)yaP(*+O$}=Unhe_xZ^-aepaD5shv@n z5(*REbZ{A-K479qPWoLJyS;7sl3u8g5#}e zW@UVRim4jkme!fh4ySbH_()3U7;jD+&GFHc(Rs_bVaB-Oa`{y%X>3Xvo5r`NHHHLR zcgw~$%&-ktYi|rEdiW&kNwJ=!#*-jLjp3f6`H^!Pps&MsxdEuf{=TsqTIGT#w@C9{ z_f)uGHot_N_wR7NL;?AdIjzXJyJgmIrLR~aK~{p}61*q@mf%$hG7>298Q!QikuOLd z5Pkt);Zsz$n8jy2=QJ7!i_W@d0daBsw6_E*aksaWs5;`cLmR&6tt5Y4f}k1yPVy`+ z?JvR3eg+QWw*EIs?dSey;4;kls^MdJ!dD4b4-y4k#D3g*oM m3FBm%nQsnxvgotw>W6qW?tms7Jcv`kvQnQjx!EkD#NWgoH& zmGId8LeYo56e$NHDx$1DL>IIqqOydD2nrTqMbYKF%!&v)ocH&C&T}6A?|Gh{r|@zB zq%&r-k+5|nW1n8v?To-tFN_Tp$1NVW@K>lR_ zf_mQ#FyLL^Z7|UT1wS~j&;wH*3Sg(98DSpW^wG>JfT>tew4M7w=*1#8=+8=oba)OD z1b-Ga&6blygK7+=;n(Exv~ocv&wF-wXq#W+tg#ZUtHe^Xl?8TGb?KIznE6+dkdO;| z0$KK4WucmTlqidi^KC9QNAYiBC2g`Qy5SyW$SqMs-C{Z0TnY zKwISt?g`+>$}W&`chx7UC5@$@h|0#s=BV3CPp#YwSXUhsyjpdCbsm1H&X(C=$t?Co z1p^_Uw~|4@*ipQ#xNnrFU+@kH-af$_*O4))blz$ofF5HtE|DcN#^Q+@yB3K?yi}8& z%p#NHJ;@ZMA^N(;#z6{xTT`wT=*?IjEKEdnZ&1%oL%YBUd~z z*oIAbv$l)AFT3g;&;XnaCTKLg#!|%QV5E?VoRp+q8!-L^ DEYT5O diff --git a/Backend/arch.py b/Backend/arch.py new file mode 100644 index 0000000..90e92ce --- /dev/null +++ b/Backend/arch.py @@ -0,0 +1,122 @@ +from pptx import Presentation +from pptx.util import Inches +from pptx.enum.shapes import MSO_AUTO_SHAPE_TYPE, MSO_CONNECTOR + +# Vytvorenie novej prezentácie +prs = Presentation() +slide_layout = prs.slide_layouts[5] # Prázdny slide +slide = prs.slides.add_slide(slide_layout) + +# Definícia základných rozmerov a pozícií +left_margin = Inches(0.5) +top_margin = Inches(0.5) +block_width = Inches(3) +block_height = Inches(0.7) +vertical_gap = Inches(0.3) +horizontal_gap = Inches(0.5) + +# Blok 1: Používateľský dotaz & Chat history +box1 = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, left_margin, top_margin, block_width, block_height) +box1.text = "Používateľský dotaz\n& Chat history" + +# Blok 2: ConversationalAgent (pod box1) +box2_top = top_margin + block_height + vertical_gap +box2 = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, left_margin, box2_top, block_width, block_height) +box2.text = "ConversationalAgent" + +# Blok 3: Klasifikácia dotazu (pod box2) +box3_top = box2_top + block_height + vertical_gap +box3 = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, left_margin, box3_top, block_width, block_height) +box3.text = "Klasifikácia dotazu" + +# Vetvenie: Pozície pre dve vetvy +branch_top = box3_top + block_height + vertical_gap + +# Ľavá vetva ("Vyhladavanie") +left_branch_left = left_margin - Inches(0.2) +box4A = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, left_branch_left, branch_top, block_width, block_height) +box4A.text = "ElasticsearchStore\nvyhľadávanie" + +box5A_top = branch_top + block_height + vertical_gap +box5A = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, left_branch_left, box5A_top, block_width, block_height) +box5A.text = "Generovanie\ndynamického promptu" + +box6A_top = box5A_top + block_height + vertical_gap +box6A = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, left_branch_left, box6A_top, block_width, block_height) +box6A.text = "Generovanie\nodpovede" + +box7A_top = box6A_top + block_height + vertical_gap +box7A = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, left_branch_left, box7A_top, block_width, block_height) +box7A.text = "Finalizácia\nodpovede" + +# Pravá vetva ("Upresnenie") +right_branch_left = left_margin + block_width + horizontal_gap +box4B = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, right_branch_left, branch_top, block_width, block_height) +box4B.text = "Kombinovanie\ndotazov" + +box5B_top = branch_top + block_height + vertical_gap +box5B = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, right_branch_left, box5B_top, block_width, block_height) +box5B.text = "ElasticsearchStore\nvyhľadávanie" + +box6B_top = box5B_top + block_height + vertical_gap +box6B = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, right_branch_left, box6B_top, block_width, block_height) +box6B.text = "Generovanie\ndynamického promptu" + +box7B_top = box6B_top + block_height + vertical_gap +box7B = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, right_branch_left, box7B_top, block_width, block_height) +box7B.text = "Generovanie\nodpovedí (2 modely)" + +box8B_top = box7B_top + block_height + vertical_gap +box8B = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, right_branch_left, box8B_top, block_width, block_height) +box8B.text = "Validácia a\nhodnotenie odpovedí" + +box9B_top = box8B_top + block_height + vertical_gap +box9B = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, right_branch_left, box9B_top, block_width, block_height) +box9B.text = "Finalizácia\nodpovede" + +# Finálny blok: Výstup (zlúčenie vetiev) +final_box_top = max(box7A_top, box9B_top) + block_height + vertical_gap +final_box = slide.shapes.add_shape(MSO_AUTO_SHAPE_TYPE.RECTANGLE, left_margin, final_box_top, block_width, block_height) +final_box.text = "Výstup" + +# Funkcia na pridanie šípok medzi blokmi +def add_connector(slide, start_shape, end_shape): + start_x = start_shape.left + start_shape.width / 2 + start_y = start_shape.top + start_shape.height + end_x = end_shape.left + end_shape.width / 2 + end_y = end_shape.top + connector = slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, start_x, start_y, end_x, end_y) + # Aktuálna verzia python-pptx nepodporuje nastavenie šípky, preto tento riadok odstraňte alebo zakomentujte: + # connector.line.end_arrowhead.style = 1 + return connector + +# Prepojenie blokov +add_connector(slide, box1, box2) +add_connector(slide, box2, box3) + +# Vetvenie z Box3 do oboch vetiev +mid_point = box3.left + box3.width / 2 +branch_mid_y = box3.top + box3.height + vertical_gap/2 +# Do ľavej vetvy: +connector_left = slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, mid_point, box3.top + box3.height, left_branch_left + block_width/2, branch_mid_y) +# Do pravej vetvy: +connector_right = slide.shapes.add_connector(MSO_CONNECTOR.STRAIGHT, mid_point, box3.top + box3.height, right_branch_left + block_width/2, branch_mid_y) + +# Prepojenie blokov v ľavej vetve +add_connector(slide, box4A, box5A) +add_connector(slide, box5A, box6A) +add_connector(slide, box6A, box7A) + +# Prepojenie blokov v pravej vetve +add_connector(slide, box4B, box5B) +add_connector(slide, box5B, box6B) +add_connector(slide, box6B, box7B) +add_connector(slide, box7B, box8B) +add_connector(slide, box8B, box9B) + +# Spojenie oboch vetiev s finálnym blokom "Výstup" +add_connector(slide, box7A, final_box) +add_connector(slide, box9B, final_box) + +# Uloženie prezentácie +prs.save("architecture_diagram.pptx") diff --git a/Backend/model.py b/Backend/model.py index 474f402..8bae8ef 100644 --- a/Backend/model.py +++ b/Backend/model.py @@ -14,7 +14,6 @@ from psycopg2.extras import RealDictCursor logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) - config_file_path = "config.json" with open(config_file_path, 'r') as config_file: config = json.load(config_file) @@ -23,15 +22,18 @@ mistral_api_key = "hXDC4RBJk1qy5pOlrgr01GtOlmyCBaNs" if not mistral_api_key: raise ValueError("Mistral API key not found in configuration.") + ############################################################################### # Simple functions for translation (stub) ############################################################################### def translate_to_slovak(text: str) -> str: return text + def translate_preserving_medicine_names(text: str) -> str: return text + ############################################################################### # Function for evaluating the completeness of the answer ############################################################################### @@ -53,6 +55,7 @@ def evaluate_complete_answer(query: str, answer: str) -> dict: score = 0.0 return {"rating": round(score, 2), "explanation": "Evaluation based on required criteria."} + ############################################################################### # Function for validating the response logic ############################################################################### @@ -73,6 +76,7 @@ def validate_answer_logic(query: str, answer: str) -> str: logger.error(f"Error during answer validation: {e}") return answer + ############################################################################### # Function for creating a dynamic prompt with information from documents ############################################################################### @@ -91,6 +95,7 @@ def build_dynamic_prompt(query: str, documents: list) -> str: ) return prompt + ############################################################################### # Function to get user data from the database via endpoint /api/get_user_data ############################################################################### @@ -106,6 +111,7 @@ def get_user_data_from_db(chat_id: str) -> str: logger.error(f"Error retrieving user_data from DB: {e}", exc_info=True) return "" + ############################################################################### # Class for calling Mistral LLM ############################################################################### @@ -147,6 +153,21 @@ class CustomMistralLLM: raise ex raise Exception("Reached maximum number of retries for API request") + +############################################################################### +# Function for generating a detailed evaluation description +############################################################################### +# def detailed_evaluation_description(query: str, answer: str, rating: float) -> str: +# prompt = ( +# f"Podrobne opíš, prečo odpoveď: '{answer}' na otázku: '{query}' dosiahla hodnotenie {rating} zo 10. " +# "Uveď relevantné aspekty, ktoré ovplyvnili toto hodnotenie, vrátane úplnosti, presnosti a kvality vysvetlenia." +# ) +# description = llm_small.generate_text(prompt=prompt, max_tokens=150, temperature=0.5) +# return description.strip() +# +# Ak chcete vidieť podrobné hodnotenie, odkomentujte funkciu detailed_evaluation_description a príslušné časti kódu. + + ############################################################################### # Initialisation of Embeddings and Elasticsearch ############################################################################### @@ -188,6 +209,7 @@ llm_large = CustomMistralLLM( model_name="mistral-large-latest" ) + ############################################################################### # Request classification function: vyhladavanie vs. upresnenie ############################################################################### @@ -213,6 +235,7 @@ def classify_query(query: str, chat_history: str = "") -> str: return "upresnenie" return "vyhladavanie" + ############################################################################### # Template for upresnenie dopytu ############################################################################### @@ -237,6 +260,7 @@ Upresňujúca otázka od používateľa: """ return prompt + ############################################################################### # Function for retrieving the last vyhladavacieho dopytu z histórie ############################################################################### @@ -249,6 +273,7 @@ def extract_last_vyhladavacie_query(chat_history: str) -> str: break return last_query + ############################################################################### # Agent class for data storage: vek, anamneza, predpis, user_data, search_query ############################################################################### @@ -308,15 +333,16 @@ class ConversationalAgent: def ask_follow_up(self, missing_info: dict) -> str: return " ".join(missing_info.values()) + ############################################################################### # Main function process_query_with_mistral with updated logic ############################################################################### CHAT_HISTORY_ENDPOINT = "http://localhost:5000/api/chat_history_detail" + def process_query_with_mistral(query: str, chat_id: str, chat_context: str, k=10): logger.info("Processing query started.") - chat_history = "" if chat_context: chat_history = chat_context @@ -337,23 +363,18 @@ def process_query_with_mistral(query: str, chat_id: str, chat_context: str, k=10 except Exception as e: logger.error(f"Chyba pri načítaní histórie: {e}") - agent = ConversationalAgent() if chat_history: agent.load_memory_from_history(chat_history) - existing_user_data = "" if chat_id: existing_user_data = get_user_data_from_db(chat_id) - agent.parse_user_info(query) missing_info = agent.analyze_input(query) - if not existing_user_data: - if "Prosím, uveďte vek pacienta" in chat_history: if chat_id: update_payload = {"chatId": chat_id, "userData": query} @@ -388,19 +409,16 @@ def process_query_with_mistral(query: str, chat_id: str, chat_context: str, k=10 "patient_data": query } - qtype = classify_query(query, chat_history) logger.info(f"Typ dopytu: {qtype}") logger.info(f"Chat context (snippet): {chat_history[:200]}...") - if qtype == "vyhladavanie": user_data_db = get_user_data_from_db(chat_id) if user_data_db: query = query + " Udaje cloveka: " + user_data_db agent.long_term_memory["search_query"] = query - if qtype == "upresnenie": original_search = agent.long_term_memory.get("search_query") if not original_search: @@ -442,7 +460,8 @@ def process_query_with_mistral(query: str, chat_id: str, chat_context: str, k=10 f"Otázka: {combined_query}\n\n" "Na základe týchto informácií:\n" f"{joined_docs}\n\n" - "Vygeneruj odporúčanie liekov alebo vysvetlenie, ak je to relevantné." + "Vygeneruj odporúčanie liekov alebo vysvetlenie, ak je to relevantné.\n" + "Prosím, odpovedaj stručne a dostatočne, bez nadmernej dĺžky." ) ans_small = llm_small.generate_text(final_prompt, max_tokens=1200, temperature=0.7) ans_large = llm_large.generate_text(final_prompt, max_tokens=1200, temperature=0.7) @@ -454,8 +473,32 @@ def process_query_with_mistral(query: str, chat_id: str, chat_context: str, k=10 {"summary": val_small, "eval": eval_small, "model": "Mistral Small"}, {"summary": val_large, "eval": eval_large, "model": "Mistral Large"}, ] + + # + # for candidate in candidates: + # detailed_desc = detailed_evaluation_description(combined_query, candidate["summary"], candidate["eval"]["rating"]) + # candidate["eval"]["detailed_description"] = detailed_desc + # + + best = max(candidates, key=lambda x: x["eval"]["rating"]) logger.info(f"Odpoveď od modelu {best['model']} má rating: {best['eval']['rating']}/10") + + + evaluation_table = "=== Výsledky hodnotenia odpovedí ===\n" + evaluation_table += "{:<15} | {:<6} | {:<60}\n".format("Model", "Rating", "Evaluated Text") + evaluation_table += "-" * 100 + "\n" + for candidate in candidates: + model_name = candidate["model"] + rating = candidate["eval"]["rating"] + evaluated_text = candidate["summary"].replace("\n", " ") + evaluation_table += "{:<15} | {:<6} | {:<60}\n".format(model_name, rating, evaluated_text) + evaluation_table += "=" * 100 + "\n" + + # with open("evaluation.txt", "w", encoding="utf-8") as f: + # f.write(evaluation_table) + # logger.info("Evaluation table записана в evaluation.txt") + final_answer = translate_preserving_medicine_names(best["summary"]) memory_json = json.dumps(agent.long_term_memory) memory_block = f"[MEMORY]{memory_json}[/MEMORY]" @@ -467,7 +510,6 @@ def process_query_with_mistral(query: str, chat_id: str, chat_context: str, k=10 "explanation": best["eval"]["explanation"] } - vector_results = vectorstore.similarity_search(query, k=k) max_docs = 5 max_len = 1000 @@ -484,7 +526,8 @@ def process_query_with_mistral(query: str, chat_id: str, chat_context: str, k=10 f"Otázka: {query}\n\n" "Na základe týchto informácií:\n" f"{joined_docs}\n\n" - "Vygeneruj odporúčanie liekov alebo vysvetlenie, ak je to relevantné." + "Vygeneruj odporúčanie liekov alebo vysvetlenie, ak je to relevantné.\n" + "Prosím, odpovedaj stručne a dostatočne, bez nadmernej dĺžky." ) answer = llm_small.generate_text(final_prompt, max_tokens=1200, temperature=0.7) memory_json = json.dumps(agent.long_term_memory) diff --git a/Backend/tablepresent.py b/Backend/tablepresent.py new file mode 100644 index 0000000..425a0d2 --- /dev/null +++ b/Backend/tablepresent.py @@ -0,0 +1,56 @@ +from pptx import Presentation +from pptx.util import Inches + +# Создание новой презентации +prs = Presentation() + +# Добавляем пустой слайд (layout с индексом 5 обычно является пустым) +slide_layout = prs.slide_layouts[5] +slide = prs.slides.add_slide(slide_layout) + +# Определяем позицию и размер таблицы +left = Inches(0.5) +top = Inches(1.5) +width = Inches(9) +height = Inches(3) + +# Количество строк: 1 заголовок + 2 строки с данными +rows = 3 +cols = 4 + +# Добавляем таблицу на слайд +table = slide.shapes.add_table(rows, cols, left, top, width, height).table + +# Устанавливаем ширину столбцов (при необходимости можно настроить отдельно) +table.columns[0].width = Inches(1.5) # Модель +table.columns[1].width = Inches(1) # Оценка +table.columns[2].width = Inches(4) # Текст +table.columns[3].width = Inches(2.5) # Описание + +# Заполняем заголовки +table.cell(0, 0).text = "Модель" +table.cell(0, 1).text = "Оценка" +table.cell(0, 2).text = "Текст" +table.cell(0, 3).text = "Описание" + +# Данные для первого кандидата +table.cell(1, 0).text = "Mistral Small" +table.cell(1, 1).text = "9.0" +table.cell(1, 2).text = ( + "Nevolnosť môže byť spôsobená rôznymi príčinami, ako sú napríklad gastrointestinálne problémy, infekcie, alebo vedľajšie účinky liekov. " + "Pre ľudí, ktorí hľadajú voľnopredajný liek na nevolnosť, sú dostupné niekoľko možností: 1. Dimedrol (Dramin) – Antihistaminikum; " + "2. Bismut subsalicylát (Pepto-Bismol); 3. Ginger (Zázvor); 4. Meclizin (Bonine). Pred použitím lieku je dôležité konzultovať s lekárom." +) +table.cell(1, 3).text = "Evaluation based on required criteria." + +# Данные для второго кандидата +table.cell(2, 0).text = "Mistral Large" +table.cell(2, 1).text = "8.0" +table.cell(2, 2).text = ( + "Pre nevolnosť sa dajú použiť niektoré voľne predávané lieky, ale dôležité je poradiť sa s lekárom, najmä ak má pacient 20 rokov. " + "Medzi bežné voľne predávané lieky patria: 1. Dimenhydrinát; 2. Meclozín. Tieto lieky môžu spôsobiť spánkovosť a dávkovanie je nutné konzultovať." +) +table.cell(2, 3).text = "Evaluation based on required criteria." + +# Сохраняем презентацию в файл +prs.save("evaluation_table.pptx") diff --git a/prepare.sh b/prepare.sh index 41e3132..d22742e 100644 --- a/prepare.sh +++ b/prepare.sh @@ -1,4 +1,5 @@ #!/bin/bash -echo "Подготовка окружения: сборка Docker образов..." -docker-compose build -echo "Подготовка завершена." +echo "Prepearing Docker images..." +docker-compose up --build + +echo "Preparation ended."