From 1d4a68b091200f72429c606bbbc6aada21ccb697 Mon Sep 17 00:00:00 2001 From: Stijn Goossens <22433228+StijnGoossens@users.noreply.github.com> Date: Wed, 8 Nov 2023 15:53:57 +0000 Subject: [PATCH] chore: remove unnecessary files --- README.md | 6 +- src/llm_app_eval/app.py | 101 ------- src/llm_app_eval/example.ipynb | 58 ++-- .../20230930_question_answer_pairs.ipynb | 143 ---------- .../20231001_evaluate_properties.ipynb | 252 ------------------ src/llm_app_eval/qa_extraction.py | 55 ---- 6 files changed, 31 insertions(+), 584 deletions(-) delete mode 100644 src/llm_app_eval/app.py delete mode 100644 src/llm_app_eval/notebooks/20230930_question_answer_pairs.ipynb delete mode 100644 src/llm_app_eval/notebooks/20231001_evaluate_properties.ipynb delete mode 100644 src/llm_app_eval/qa_extraction.py diff --git a/README.md b/README.md index 95cab95..fa07669 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ An implementation of the principles of evaluating LLM-based applications. This repository accompanies the blog post ['Steady the Course: Navigating the Evaluation of LLM-based Applications'](https://medium.com/@stijn.sg.goossens/steady-the-course-navigating-the-evaluation-of-llm-based-applications-8b7a22734fc9). -πŸ’‘ Check out the [example notebook](src/llm_app_eval/example.ipynb) for an end-to-end illustration of the most important concepts (LLM app, test case, test properties and Evaluator), including the integration with MLflow. +πŸ’‘ Check out the [example notebook](src/llm_app_eval/example.ipynb) for an end-to-end illustration of the most important concepts (LLM app, test case, test properties and Evaluator), including the integration with [MLflow](https://github.com/mlflow/mlflow/tree/master). πŸ”‘ Add your OpenAI API key to a file named `openai_key` in the root directory before running the notebook. -The image below shows an architectural overview of the evaluation framework and illustrates an important feedback loop. See the aforementioned blog post for more information. The scope of this repository is indicated by the green box. +The image below shows the evaluation framework and illustrates an important feedback loop to improve your LLM app further. See the aforementioned blog post for more information. The scope of this repository is indicated by the green box. ![Evaluation feedback loop](images/evaluation_feedback_loop.png) @@ -16,8 +16,6 @@ The image below shows an architectural overview of the evaluation framework and _Python package_: to add and install this package as a dependency of your project, run `poetry add llm-app-eval`. -_Python application_: to serve this Streamlit app, run `docker compose up app` and open [localhost:8000](http://localhost:8000) in your browser. Within the Dev Container, this is equivalent to running `poe app`. - ## Contributing
diff --git a/src/llm_app_eval/app.py b/src/llm_app_eval/app.py deleted file mode 100644 index a57b203..0000000 --- a/src/llm_app_eval/app.py +++ /dev/null @@ -1,101 +0,0 @@ -"""Streamlit app.""" - -import json -import os -from importlib.metadata import version - -import numpy as np -import pandas as pd -import streamlit as st - -from llm_app_eval.eval_properties import properties - -st.title(f"llm-app-eval v{version('llm-app-eval')}") # type: ignore[no-untyped-call] - -# Define the paths to the test cases and evaluation results -TEST_SET_FOLDER = "src/llm_app_eval/data/test_cases" -EVAL_FOLDER = "src/llm_app_eval/data/eval_results" - -# Create folders if they don't exist -os.makedirs(TEST_SET_FOLDER, exist_ok=True) -os.makedirs(EVAL_FOLDER, exist_ok=True) - -# Get the list of evaluation runs -EVAL_RUNS = os.listdir(EVAL_FOLDER) - -# Load all the test cases JSON files -test_cases = {} # type: ignore -for test_case in os.listdir(TEST_SET_FOLDER): - test_case_path = os.path.join(TEST_SET_FOLDER, test_case) - with open(test_case_path) as f: - test_cases[test_case] = json.load(f) - -# Load all the evaluation results JSON files -eval_results = {} # type: ignore -for eval_run in EVAL_RUNS: - eval_results[eval_run] = {} - eval_run_folder = os.path.join(EVAL_FOLDER, eval_run) - for eval_file in os.listdir(eval_run_folder): - eval_file_path = os.path.join(eval_run_folder, eval_file) - with open(eval_file_path) as f: - eval_results[eval_run][eval_file] = json.load(f) - -# Build a matrix for each evaluation run -# Each row is a test case. Each column is a property. -eval_matrices = {} # type: ignore -for eval_run in EVAL_RUNS: - eval_matrices[eval_run] = np.zeros((len(test_cases), len(properties))) - for test_case_idx, test_case in enumerate(test_cases): - for property_idx, prop in enumerate(properties): - r = eval_results[eval_run][test_case] - for property_result in r["property_results"]: - if property_result["property_name"] == prop.property_name: - eval_matrices[eval_run][test_case_idx, property_idx] = property_result[ - "pass_fail" - ] - break - # Turn the matrix into a dataframe - eval_matrices[eval_run] = pd.DataFrame( - eval_matrices[eval_run], - columns=[prop.property_name for prop in properties], - index=list(test_cases), - ) - -st.write(eval_matrices[eval_run]) - -# Select a specific test case -test_case = st.selectbox("Test case", list(test_cases.keys())) # type: ignore - -# Select a specific evaluation run -eval_run = st.selectbox("Evaluation run", EVAL_RUNS) # type: ignore - -# Show the test case input -st.markdown("**Test case input:**") -st.write(test_cases[test_case]["test_input"]["question"]) -# Show the reference_output, historical_output, and historical_feedback, if available -if test_cases[test_case]["reference_output"]: - st.markdown("**Reference output:**") - st.write(test_cases[test_case]["reference_output"]["answer"]) -if test_cases[test_case]["historical_output"]: - st.markdown("**Historical output:**") - st.write(test_cases[test_case]["historical_output"]["answer"]) -if test_cases[test_case]["historical_feedback"]: - st.markdown("**Historical feedback:**") - st.write(test_cases[test_case]["historical_feedback"]) - -# Show the model output -st.markdown("**Model response:**") -st.write(eval_results[eval_run][test_case]["output"]["answer"]) - -# Show the evaluation results -st.markdown("**Evaluation results:**") -# Loop over the properties -for prop in properties: - # Loop over the evaluation runs - for eval_run in EVAL_RUNS: - # Loop over the evaluation results - for property_result in eval_results[eval_run][test_case]["property_results"]: - # If the property name matches the current property, show the result - if property_result["property_name"] == prop.property_name: - st.write(f"{prop.property_name}: {'βœ…' if property_result['pass_fail'] else '❌'}") - st.write(property_result["feedback"]) diff --git a/src/llm_app_eval/example.ipynb b/src/llm_app_eval/example.ipynb index 7bec956..84fb3ff 100644 --- a/src/llm_app_eval/example.ipynb +++ b/src/llm_app_eval/example.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 8, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -35,7 +35,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -61,7 +61,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -103,7 +103,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -125,16 +125,16 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Evaluating test cases: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 3/3 [00:23<00:00, 7.75s/test case]\n", - "Evaluating test cases: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 3/3 [00:20<00:00, 6.79s/test case]\n", - "Evaluating test cases: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 3/3 [00:29<00:00, 9.91s/test case]\n" + "Evaluating test cases: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 3/3 [00:30<00:00, 10.06s/test case]\n", + "Evaluating test cases: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 3/3 [00:19<00:00, 6.37s/test case]\n", + "Evaluating test cases: 100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 3/3 [00:43<00:00, 14.59s/test case]\n" ] } ], @@ -147,7 +147,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -184,28 +184,28 @@ " 0\n", " gpt-3.5-turbo-0613\n", " Answer the question.\n", - " 0.904431\n", - " 0.666667\n", - " 8.370790\n", - " 2.814149\n", + " 0.891321\n", + " 1.0\n", + " 8.454041\n", + " 2.887883\n", " \n", " \n", " 0\n", " gpt-3.5-turbo-0613\n", " You are a first-aid expert. Answer the questio...\n", - " 0.903006\n", - " 0.666667\n", - " 2.031130\n", - " 1.684336\n", + " 0.895463\n", + " 1.0\n", + " 5.316218\n", + " 2.421593\n", " \n", " \n", " 0\n", " gpt-4\n", " You are a first-aid expert. Answer the questio...\n", - " 0.907844\n", - " 1.000000\n", - " 5.116838\n", - " 5.495382\n", + " 0.899324\n", + " 1.0\n", + " 6.696650\n", + " 9.665427\n", " \n", " \n", "\n", @@ -218,17 +218,17 @@ "0 gpt-4 You are a first-aid expert. Answer the questio... \n", "\n", " CosineSimilarity.score FactuallyConsistent.score Verbosity.score \\\n", - "0 0.904431 0.666667 8.370790 \n", - "0 0.903006 0.666667 2.031130 \n", - "0 0.907844 1.000000 5.116838 \n", + "0 0.891321 1.0 8.454041 \n", + "0 0.895463 1.0 5.316218 \n", + "0 0.899324 1.0 6.696650 \n", "\n", " latency \n", - "0 2.814149 \n", - "0 1.684336 \n", - "0 5.495382 " + "0 2.887883 \n", + "0 2.421593 \n", + "0 9.665427 " ] }, - "execution_count": 15, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } diff --git a/src/llm_app_eval/notebooks/20230930_question_answer_pairs.ipynb b/src/llm_app_eval/notebooks/20230930_question_answer_pairs.ipynb deleted file mode 100644 index acecb31..0000000 --- a/src/llm_app_eval/notebooks/20230930_question_answer_pairs.ipynb +++ /dev/null @@ -1,143 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 100, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The autoreload extension is already loaded. To reload it, use:\n", - " %reload_ext autoreload\n" - ] - } - ], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": 101, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from typing import List\n", - "import sys\n", - "import uuid\n", - "\n", - "sys.path.append(\"../../llm_app_eval\")\n", - "from qa_extraction import QuestionAnswerPairs, extract_question_answer_pairs, save_qa_pairs\n", - "from llm_app import BaseApp, InputFormat, OutputFormat\n", - "from evaluator import TestCase, EvalProperty, PropertyResult, Evaluator\n", - "\n", - "import openai\n", - "import instructor\n", - "\n", - "openai.api_key_path = \"../../../openai_key\"\n", - "instructor.patch()" - ] - }, - { - "cell_type": "code", - "execution_count": 51, - "metadata": {}, - "outputs": [], - "source": [ - "# Load documents\n", - "def load_documents(path):\n", - " docs = []\n", - " for filename in os.listdir(path):\n", - " with open(path + filename) as f:\n", - " docs.append(f.read())\n", - " return docs\n", - "\n", - "def chunk_markdown(md_doc: str) -> List[str]:\n", - " \"\"\"Split markdown document into chunks\"\"\"\n", - " chunks = []\n", - " chunk = \"\"\n", - " for line in md_doc.splitlines():\n", - " if line.startswith(\"# \"):\n", - " chunks.append(chunk)\n", - " chunk = \"\"\n", - " chunk += line + \"\\n\"\n", - " chunks.append(chunk)\n", - " return chunks[1:]" - ] - }, - { - "cell_type": "code", - "execution_count": 52, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Loaded 1 documents\n", - "['# 2021 - EHBO\\n\\n# Vier stappen van eerste hulp\\n\\n1. Zorg voor veiligheid, van jezelf, het slachtoffer (SO) en de omstaanders.\\n2. Beoordeel de toestand van het SO.\\n 1. Bewustzijn: verbaal + schudden met schouders\\n 1. Bewust? β†’ laten liggen, vragen wat er gebeurd is\\n 2. Bewusteloos? β†’ Controleer ademhaling: open de luchtweg met kinlift + 10 tellen ademhaling observeren (kijken naar borst, horen en voelen aan mond)\\n 1. Ademhaling β†’ 112 bellenβ†’ stabiele zijligging + ademhaling blijven controleren\\n 2. Geen ademhaling β†’ 112 bellen β†’ reanimeren\\n3. Hulpdiensten verwittigen indien nodig:\\n 1. 112, 101, antigifcentrum (070 245 245), huisartsenwachtpost (1733)\\n 2. 112: vermeld waar, wat en wie\\n4. Verleen verdere eerste hulp\\n\\n# Algemeen\\n\\n- Vermijd het zeggen van \"het komt allemaal goed\". Maak **geen valse beloftes**. Zeg wel: \"de hulpdiensten zijn verwittigd en komen eraan\", \"ik blijf bij u tot de hulpdiensten er zijn\", \"we gaan u zo goed mogelijk helpen\", \"kan ik iemand verwittigen?\"\\n- **Geen eten of drinken** toedienen want het zou kunnen dat er geopereerd moet worden. Behalve bij:\\n - Hypo\\n - Hitte- en zonneslag: geef (sport)drank\\n- **Geen pijnstillers of medicatie** geven want een eerste hulpverlener is geen dokter. Wat wel kan, is dat je het SO vraagt of ze daar medicatie voor bij hebben (bvb bij suikerziekte of hartaanval) en ze hen dat laten innemen. Als een collega je eens heeft uitgelegd hoe je bij hem/haar een EPI-pen moet toedienen bij een allergische aanval dan mag je dat doen want dan ben je daarvoor \\'opgeleid\\'.\\n- VLAM:\\n - Voorgeschiedenis: Heb je dit nog al eens gehad?\\n - Laatste maaltijd: Wanneer heb je voor het laatst gegeten?\\n - Allergie: Ben je voor iets allergisch?\\n - Medicatie: Neem jij medicatie? Waarom neem jij ze?\\n\\n# Reanimatie\\n\\n30 borstcompressies + 2 pogingen tot beademing.\\n\\nBeademen = luchtweg vrijmaken + neus toeknijpen + blazen in mond\\n\\nLaat iemand een AED halen.\\n\\nVerwijder alle bovenkleding (ook BH\\'s).\\n\\n## Baby (0 - 1 jaar)\\n\\n[Ademhalingsfalen zorgt voor hartfalen. Omgekeerde van volwassenen.]\\n\\nStart met vijf beademingen (pufjes), mond volledig over neus en mond van de baby. Daarna 30 borstcompressies + 2 beademingen.\\n\\n112 bellen na de vijf beademingen. Vaak komt de baby er al na die beademingen door.\\n\\nBorstcompressies met twee vingers ipv twee handen.\\n\\nGeen AED.\\n\\nLeg baby op een hard oppervlak, niet in bedje laten liggen.\\n\\nVoorzichtig met kinlift, niet veel nodig\\n\\n## Kind (1 - 12 jaar)\\n\\n[Ademhalingsfalen zorgt voor hartfalen. Omgekeerde van volwassenen.]\\n\\nStart met vijf beademingen. Daarna 30 borstcompressies + 2 beademingen.\\n\\nBorstcompressies met één hand ipv twee.\\n\\nAED kan, maar plak het dan op de borstkas en rug.\\n\\n# Verslikking\\n\\nSO kan nog ademen/hoesten/huilen/spreken, bvb een beetje vocht β†’ goed laten hoesten\\n\\nSO kan niet meer ademen/hoesten/huilen/spreken, bvb grote voedselbrok\\n\\n1. SO laten zitten en voorover laten buigen.\\n2. Tussen schouderbladen kloppen x5 (hard en met tempo)\\n3. Heimlich: Onder oksels met twee armen, vuist vastpakken boven de navel en onder de ribben. 5x samendrukken. Dicht bij persoon staan om alle kracht te kunnen overbrengen.\\n\\nBlijf stap 2 en 3 herhalen.\\n\\nDaarna SO naar dokter laten gaan omdat Heimlich een bloeding kan hebben veroorzaakt.\\n\\n# Beroerte\\n\\n## FAST test\\n\\n- Face: Vraag het SO om te lachen en kijk of er een mondhoek niet mee omhoog gaat.\\n- Arms: Vraag het SO om zijn/haar armen tegelijkertijd op te heffen met de ogen toe en kijk of er één blijft hangen.\\n- Speech: Onverstaanbaar brabbelen, geen woorden meer kunnen volgen.\\n- Time: Hoelang is het al bezig. Belangrijk om weten voor de hulpdiensten.\\n\\nSTAP 3: 112 bellen en zeggen dat de FAST test positief is.\\n\\nSTAP 4: laat het SO rusten, geen eten of drinken geven, blijf rustig\\n\\n# 🩹 Wondverzorging ⚠️\\n\\n## **Brandwonden**\\n\\n- Koelen met (lauw) water om pijn te stillen. Tot pijn verzacht of 15-20 min bij grote brandwonde (brandwonden kunnen onderhuids nog verder branden).\\n- Voorzichtig met kledij en schoenen. Ze uitdoen kan ervoor zorgen dat je verbrande huid er af trekt.\\n- Zware wonden inpakken om naar dokter te gaan:\\n - met doorweekte windel of compressen (kletsnat anders kan het in de wonde blijven hangen)\\n - huishoudfolie (!) (is redelijk steriel en plakt niet in de wonde)\\n - driehoeksverband\\n- Ringen, horloges, kettingen,... uitdoen want het lichaamsdeel kan opzwellen en dan krijg je ze niet meer uit.\\n- Flamigel β†’ JA. Ook voor gewone wonden. Dit voorkomt korstvorming waardoor de wonde sneller geneest.\\n- Flamazine β†’ OPPASSEN. Niet mee in de zon komen want dat verminkt de huid.\\n\\n# πŸ«€ Hartproblemen/hartaanval\\n\\nSymptomen: pijn i/h midden van de borstkas, koorts/zweten/bibberen, grauwe huidskleur, **ontkenning** (!),...\\n\\nNooit volledig plat neerleggen!\\n\\nNiet naar WC laten gaan! (Persen op WC kan de inspanning te veel zijn...)\\n\\nSO mag zich niet druk maken.\\n\\n# ⚑️Electriciteitsongevallen\\n\\n## Laagspanning\\n\\nStroom door lichaam\\n\\n- STAP 1: SO niet onmiddellijk aanraken. Verbreek verbinding met stroombron op een veilige manier, bvb met een bezem. Hoofdschakelaar afzetten.\\n- STAP 2: SO aanraken met handrug.\\n- STAP 3: Altijd hulp zoeken (bvb huisarts). Beter te veel dan te weinig.\\n- STAP 4: Naargelang de letsels.\\n\\n## Hoogspanning\\n\\n> 1000V stroom over lichaam\\n\\n- STAP 1: Blijf op 10 meter van de stroombron!\\n- STAP 3: Bel 112 en vermeld \\'hoogspanning\\' β†’ extra hulpdiensten\\n\\n# 🦡 Kneuzing/Verstuiking/Verzwikking\\n\\n- Kneuzing = oorzaak van buitenaf\\n- Verstuiking = Verzwikking = uitgerokken ligamenten\\n\\nJe kan het verschil tussen beide niet zien β‡’ dezelfde behandeling\\n\\nProbeer schoen uit te doen om te kijken wat er scheelt. Als het te veel pijn doet, zou het een breuk kunnen zijn.\\n\\nGebruik ijs/coldpack om zwelling tegen te gaan.\\n\\n# 🦴 Botbreuk\\n\\nSymptomen: heel veel pijn, lichaamsdeel werkt niet meer, zwelling.\\n\\nOpen botbreuk:\\n- SO niet laten bewegen\\n- Handschoenen aandoen\\n- Lidmaat niet bewegen\\n- Altijd 112 bellen\\n- bloeding stelpen\\n- ringen verwijderen\\n- steriel kompres, bevochtigd met ontsmettingsmiddel\\n\\nGesloten botbreuk:\\n- SO niet laten bewegen\\n- Lidmaat niet bewegen\\n- bovenste ledematen β†’ evt zelf vervoeren\\n- onderste ledematen β†’ altijd 112\\n- ringen verwijderen\\n- ijs (max. 20 min)\\n- immobiliseer met draagdoek\\n\\n# Ontwrichting\\n\\nZelfde symptomen als een breuk, maar dan op de plaats van een gewricht.\\n\\nKan ook open of gesloten zijn.\\n\\nEnige verschil bij behandeling: geen draagdoek (die techniek bestaat wel, maar is buiten scope).\\n\\nKoeling niet vergeten.\\n\\nAls je het SO moet ondersteunen β‡’ ga aan de goede kant van het SO staan.\\n\\n# 🫁 Ademhalingsmoeilijkheden\\n\\n## Hyperventilatie\\n\\n= foute gasbalans in het bloed\\n\\noorzaak: paniek, pijn, medicatie,...\\n\\nsymptomen: snel ademhalen, duizelig, tintelingen in vingers/lippen, ... (vicieuze cirkel)\\n\\nSTAP 2: Wat is de reden van de paniek?\\n\\nSTAP 3: Enkel 112 als de aanval niet stop, SO wordt blauw-paars, twijfel.\\n\\nSTAP 4: SO kalmeren en vooral zelf rustig blijven. Rustig mee ademen met SO. Bij paniekaanval: SO in handen of zakje laten ademen.\\n\\n## Kortademigheid\\n\\noorzaak: astma, verslikking, fysieke inspanning, klaplong (vb door slag/stoot of wonde, gelukkig heb je twee longen en is dit vaak beperkt tot 1 long), longontsteking.\\n\\nSTAP 4: kalmeren, rustig ademen, evt medicatie laten innemen\\n\\n# πŸ—£ Hoofd en hals\\n\\n## Vreemd voorwerp in oog\\n\\nSTAP 4:\\n\\n- Niet wrijven! Anders kan je het oog beschadigen met krassen.\\n- SO weinig oogbewegingen laten maken. Bvb door naar een vast punt laten kijken OF door beide ogen afdekken met een compres of schone droge doek.\\n- Voorwerp niet zelf verwijderen.\\n- Bij een vuiltje: oog spoelen onder water, van het midden naar buiten.\\n\\n## Vreemd voorwerp in oor\\n\\nSTAP 3: enkel 112 bij extreme gevallen, anders huisarts\\n\\nSTAP 4: voorwerp niet zelf verwijderen. Risico van het zelf te verwijderen en schade toebrengen is groter dan het langer te laten zitten.\\n\\n## Vreemd voorwerp in neus\\n\\nSTAP 4:\\n\\n- voorwerp niet zelf verwijderen. Risico van het zelf te verwijderen en schade toebrengen is groter dan het langer te laten zitten.\\n- bij klein voorwerp (bvb TicTac), SO laten snuiten, of in geval van klein kind β†’ blaaskus = luchtstoot in mond en goede neusgat dicht houden\\n\\n## Epilepsie\\n\\n= overprikkeling van de hersenen\\n\\nSymptomen: verlies van bewustzijn, schokken, evt urineverlies,...\\n\\nSTAP 1: verwijder voorwerpen, kussen onder hoofd, niets in mond steken (ook geen riem zoals vroeger werd gedaan). SO niet vasthouden of proberen tegen te houden!\\n\\nSTAP 2: blijf toestand (bewustzijn en ademhaling) in het oog houden. Ook als het SO stopt met schokken.\\n\\nSTAP 3: 112 indien:\\n\\n- Je niet weet of het SO een epilepsie geschiedenis heeft.\\n- Aanval langer duurt dan 5 minuten β‡’ mug wordt meegestuurd.\\n- SO hervalt telkens in een aanval.\\n- SO is zwanger.\\n- SO heeft diabetes.\\n\\nSTAP 4:\\n\\n- rust brengen\\n- registreren wat er gebeurt en hoelang en deze informatie meegeven aan de ziekenwagen of dokter\\n\\n## Koortsstuipen\\n\\n= overprikkeling v/d hersenen bij koorts (>40 graden), vaak bij kinderen\\n\\nSTAP 1: idem epilepsie\\n\\nSTAP 3: altijd 112 (omwille van hoge koorts)\\n\\nSTAP 4:\\n\\n- temperatuur meten\\n- koelen (kleding uitdoen, deppen met lauw water)\\n- ouders mogen een koortswerend middel geven (maar geen eten of drinken geven)\\n\\n# Hoofdletsels\\n\\nSoorten:\\n\\n- hoofdwonde\\n- schedelbreuk\\n - bloed of helder vocht uit neus, mond en/of oren\\n - blauwe verkleuring rond de ogen of achter de oren\\n - suf, verward\\n - uitval van zicht, reuk, gehoor en/of evenwicht\\n- hersenschudding\\n - hoofdpijn\\n - vaak geeuwen\\n - bloed uit neus/oor\\n - epileptisch beeld\\n - hoofdwonde,\\n - misselijk\\n\\nSTAP 3: vaak 112 nodig, tenzij bij zeer lichte hersenschudding (bvb voetbal tegen hoofd) of van voorbijgaande aard\\n\\nSTAP 4:\\n\\n- indien bewusteloos: op rug laten liggen + kinlift!\\n- evt hoofd immobiliseren met handen (steun je armen op je dijbenen) of hoofd tussen knieΓ«n houden.\\n\\n# Wervelletsels\\n\\nbijv. bij val van >2 meter, verkeersongeluk, duikongevallen in ondiep water\\n\\nsymptomen: gevoelsstoringen en verlamming, verminderd of tintelend gevoel, pijn in nek, hals, rug of achteraan in bekken,..\\n\\nSTAP 1:\\n\\n- indien verkeersongeval goed opletten!\\n- verplaats SO niet, ook niet als het een motorrijder is en nog op de weg ligt\\n\\nSTAP 2: kinlift indien je niet onmiddellijk een ademhaling opmerkt. Indien SO op zijn/haar zij ligt en ademt β†’ laten liggen + ademhaling blijven controleren\\n\\nSTAP 3: sowieso 112\\n\\nSTAP 4:\\n\\n- kalmeer het SO\\n- overtuig SO om niet te bewegen\\n- hoofd immobiliseren\\n- reanimeer indien nodig!!!\\n\\n# 🧁 Diabetes\\n\\nSoorten:\\n\\n- Type 1\\n - tekort aan insuline β†’ moeten insuline spuiten\\n - vanaf jonge leeftijd\\n- Type 2\\n - insuline werkt minder goed\\n - ouderdom, overgewicht β†’ dieet, bewegen,...\\n- Zwangerschapsdiabetes\\n - tekort aan insuline\\n\\n## Hyperglycemie\\n\\n= te veel suiker in het bloed\\n\\nsymptomen: dorst, droge mond, gebrek aan eetlust, hyperventilatie\\n\\nGevaarlijk op lange termijn: wondes die minder goed helen op extreme delen, oogproblemen (in de fijne bloedvatten)\\n\\n## Hypoglycemie\\n\\n= te weinig suiker in het bloed\\n\\nsymptomen: onrustig, afwijkend gedrag, \\'dronken\\', flauwte, bewusteloos,...\\n\\nMoeilijk te herkennen!\\n\\nAccuut en gevaarlijk!\\n\\nAlgemeen EHBO advies: **diabetes patient met disbalans (hypo of hyper) β‡’ suiker geven**. (Ja, dat maakt een hyperglycemie erger, maar dat is geen probleem op de korte termijn)\\n\\nSTAP 3: 112 indien verminderd bewustzijn\\n\\nSTAP 4:\\n\\n- achterhalen of het effectief diabetes is\\n- snelwerkende suikers geven (bvb druivensuiker, echte cola)\\n- koolhydraten voor langere werking (bvb boterham)\\n- SO bloedsuikerspiegel laten meten\\n- hyper: medicatie laten nemen (i.e. insuline spuiten, dat verlaagt de suiker in het bloed)\\n- indien bewustzijnsverlies: geen eten geven want gevaar op verslikking\\n\\n# 🀒 Vergiftiging\\n\\nOorzaken:\\n\\n- inslikken (vb alcohol)\\n- inademen (vb CO, ammoniak, H2S (kan zich opbouwen in regenput, beerput,...), CH4, stikstof,...)\\n- inspuiten (vb overdosis drugs)\\n- contact met chemische producten\\n\\nSTAP 1:\\n\\n- Zien dat je jezelf niet vergiftigt.\\n- Ingeval van CO: elektrische toestellen niet uit/aan schakelen.\\n\\nSTAP 3:\\n\\n- ernstig: 112 en daarna antigifcentrum\\n- niet zo ernstig: antigifcentrum\\n']\n" - ] - } - ], - "source": [ - "# Load documents\n", - "docs = load_documents(\"../data/documents/\")\n", - "print(f\"Loaded {len(docs)} documents\")\n", - "print(docs)\n", - "\n", - "# Chunk documents\n", - "chunks = []\n", - "for doc in docs:\n", - " chunks.extend(chunk_markdown(doc))" - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "metadata": {}, - "outputs": [], - "source": [ - "# Extract question answer pairs\n", - "question_answer_pairs = []\n", - "for chunk in chunks:\n", - " qas = extract_question_answer_pairs(chunk)\n", - " question_answer_pairs.extend(qas)" - ] - }, - { - "cell_type": "code", - "execution_count": 46, - "metadata": {}, - "outputs": [], - "source": [ - "save_qa_pairs(question_answer_pairs, \"../data/question_answer_pairs.csv\")" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "llm-app-eval-env", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.18" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/src/llm_app_eval/notebooks/20231001_evaluate_properties.ipynb b/src/llm_app_eval/notebooks/20231001_evaluate_properties.ipynb deleted file mode 100644 index f155815..0000000 --- a/src/llm_app_eval/notebooks/20231001_evaluate_properties.ipynb +++ /dev/null @@ -1,252 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "from typing import List\n", - "import sys\n", - "import uuid\n", - "\n", - "sys.path.append(\"../../llm_app_eval\")\n", - "from llm_app import BaseApp, InputFormat, OutputFormat, GptBaseApp\n", - "from evaluator import TestCase, EvalProperty, PropertyResult, Evaluator\n", - "from eval_properties import properties\n", - "\n", - "import openai\n", - "import instructor\n", - "\n", - "openai.api_key_path = \"../../../openai_key\"\n", - "instructor.patch()" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "test_cases = [\n", - " TestCase(\n", - " test_id=1,\n", - " test_input={\"question\": \"Waarom zou het slachtoffer naar de dokter moeten gaan na het Heimlich-manoeuvre?\"},\n", - " reference_output={\"answer\": \"Omdat het Heimlich-manoeuvre een interne bloeding kan hebben veroorzaakt.\"},\n", - " ),\n", - " TestCase(\n", - " test_id=2,\n", - " test_input={\"question\": \"Wat zijn de vier stappen van eerste hulp?\"},\n", - " reference_output={\"answer\": \"1. Zorg voor veiligheid, 2. Beoordeel de toestand van het slachtoffer, 3. Hulpdiensten verwittigen indien nodig, 4. Verleen verdere eerste hulp.\"},\n", - " ),\n", - " TestCase(\n", - " test_id=3,\n", - " test_input={\"question\": \"Wat is de eerste stap van eerste hulp?\"},\n", - " historical_output={\"answer\": \"Zorg voor de veiligheid van het slachtoffer.\"},\n", - " historical_feedback=\"Het is belangrijk om ook voor de veiligheid van jezelf en omstaanders te zorgen.\",\n", - " ),\n", - " TestCase(\n", - " test_id=4,\n", - " test_input={\"question\": \"Wat moet je doen als het slachtoffer geen ademhaling heeft?\"},\n", - " historical_output={\"answer\": \"Bel 112\"},\n", - " ),\n", - " TestCase(\n", - " test_id=5,\n", - " test_input={\"question\": \"Moet je eten of drinken toedienen in een noodsituatie?\"},\n", - " reference_output={\"answer\": \"Nee, behalve bij een hypo (lage bloedsuiker) of hitte- en zonneslag\"},\n", - " ),\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "# Store the test cases in a folder with a JSON per test case\n", - "os.makedirs(\"../data/test_cases\", exist_ok=True)\n", - "for i, test_case in enumerate(test_cases):\n", - " test_case_json = test_case.model_dump_json()\n", - " with open(f\"../data/test_cases/{test_case.test_id}.json\", \"w\") as f:\n", - " f.write(test_case_json)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "llm_app = GptBaseApp()\n", - "ev = Evaluator(test_set=test_cases, properties=properties, results_dir=\"../data/eval_results\")" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Evaluating test cases: 0%| | 0/5 [00:00 list[QuestionAnswer]: - qas: QuestionAnswerPairs = openai.ChatCompletion.create( - model="gpt-4", - response_model=QuestionAnswerPairs, - messages=[ - { - "role": "system", - "content": "Turn the provided text into a set of question and answer pairs. Use only the information provided in the text. Make the answers as short as possible.", - }, - {"role": "user", "content": f"TEXT: {chunk}"}, - ], - ) - return qas.qa_pairs - - -def save_qa_pairs(qa_pairs, file_path: str): - """Store the question and answers into a CSV file.""" - with open(file_path, "w") as f: - f.write("question,answer\n") - for qa in qa_pairs: - # Replace double quotes with single quotes - answer = qa.answer.replace('"', "'") - f.write(f'"{qa.question}","{answer}"\n') - - -def load_qa_pairs(file_path: str): - """Load the question and answers from a CSV file.""" - qa_pairs = [] - with open(file_path) as f: - f.readline() # Skip header - for line in f.readlines(): - # Split on the comma between the question and answer, indicated by the double quotes - question, answer = line.split('","') - # Remove the double quotes from the question and answer - question = question.replace('"', "") - answer = answer.replace('"', "") - qa_pairs.append(QuestionAnswer(question=question, answer=answer)) - return qa_pairs