SsebaA commited on
Commit
806edf7
·
verified ·
1 Parent(s): 62dd69c

Update vips_classifier.py

Browse files
Files changed (1) hide show
  1. vips_classifier.py +137 -152
vips_classifier.py CHANGED
@@ -1,181 +1,166 @@
1
  """
2
  VoiceNote AI - VIPS Classifier
3
- ===============================
4
- VIPS classification with dual GDPR protection.
5
- Supports both Few-shot and Chain-of-Thought prompting.
6
  """
7
 
8
  import logging
9
- from typing import Dict
10
- from config import Config
11
- from gdpr_filter import GDPRFilter
12
- from models import MistralClient
13
 
14
  logger = logging.getLogger(__name__)
15
 
16
 
17
- class VIPSClassifier:
18
- """VIPS classification with support for different prompt techniques"""
 
19
 
20
- def __init__(self, llm_client: MistralClient):
21
- self.llm = llm_client
22
-
23
- @staticmethod
24
- def build_prompt_few_shot(text: str) -> str:
25
- """
26
- Few-shot prompting: Ger 2-3 konkreta exempel före den verkliga uppgiften.
27
-
28
- Fördelar:
29
- - Tydliga mönster för modellen att följa
30
- - Bättre formatering
31
- - Konsekvent output
32
-
33
- Forskningsreferens: Brown et al. (2020) - Language Models are Few-Shot Learners
34
- """
35
- return f"""Du är ett dokumentationssystem för sjuksköterskor.
36
- Din uppgift är att klassificera patientinformation enligt VIPS-modellen.
37
-
38
- OBLIGATORISKA REGLER:
39
- 1. Skriv ALDRIG namn, personnummer, telefonnummer eller adresser.
40
- 2. Hitta INTE på information. Dokumentera ENBART vad patienten faktiskt säger.
41
- 3. Lägg INTE till medicinska råd, diagnoser eller rekommendationer.
42
- 4. Om en kategori saknar information, skriv exakt: Ingen relevant information.
43
- 5. Svara i exakt VIPS-format — fyra rader, en per kategori, inget annat.
44
-
45
- VIPS-KATEGORIER:
46
- V (Välbefinnande) — Symtom, smärta, mående och känslor
47
- I (Integritet) — Vanor, önskemål och preferenser
48
- P (Prevention) — Förebyggande åtgärder som nämnts
49
- S (Säkerhet) — Risker, mediciner och säkerhetsaspekter
50
 
51
  EXEMPEL 1:
52
- Samtal: "Jag har haft huvudvärk i två dagar. Jag tar Metoprolol dagligen."
53
- Svar:
54
- V: Patienten rapporterar huvudvärk sedan två dagar.
55
  I: Ingen relevant information.
56
  P: Ingen relevant information.
57
- S: Patienten tar Metoprolol dagligen.
58
 
59
  EXEMPEL 2:
60
- Samtal: "Jag sover dåligt nätterna och känner mig orolig. Jag brukar gå på promenader varje dag."
61
- Svar:
62
- V: Patienten rapporterar sömnsvårigheter och känslor av oro.
63
- I: Patienten promenerar dagligen.
64
- P: Ingen relevant information.
65
- S: Ingen relevant information.
66
 
67
  EXEMPEL 3:
68
- Samtal: "Jag har ont i bröstet när jag går i trappor. Jag röker 10 cigaretter om dagen."
69
- Svar:
70
- V: Patienten rapporterar bröstsmärta vid ansträngning.
71
  I: Ingen relevant information.
72
  P: Ingen relevant information.
73
- S: Patienten röker 10 cigaretter dagligen. Risk för hjärt-kärlsjukdom.
74
 
75
- NU ÄR DET DIN TUR. Klassificera följande samtal:
76
- SAMTAL:
77
- "{text}"
78
 
79
- Klassificera i exakt VIPS-format (V:, I:, P:, S:):"""
 
 
80
 
81
- @staticmethod
82
- def build_prompt_chain_of_thought(text: str) -> str:
83
- """
84
- Chain-of-Thought prompting: Be modellen att tänka steg-för-steg.
85
-
86
- Fördelar:
87
- - Bättre resonemang
88
- - Minskad hallucination
89
- - Tydligare logik
90
-
91
- Forskningsreferens: Wei et al. (2022) - Chain-of-Thought Prompting
92
- Elicits Reasoning in Large Language Models
93
- """
94
- return f"""Du är ett dokumentationssystem för sjuksköterskor.
95
- Din uppgift är att klassificera patientinformation enligt VIPS-modellen.
96
-
97
- OBLIGATORISKA REGLER:
98
- 1. Skriv ALDRIG namn, personnummer, telefonnummer eller adresser.
99
- 2. Hitta INTE på information. Dokumentera ENBART vad patienten faktiskt säger.
100
- 3. Lägg INTE till medicinska råd, diagnoser eller rekommendationer.
101
- 4. Om en kategori saknar information, skriv exakt: Ingen relevant information.
102
-
103
- VIPS-KATEGORIER:
104
- V (Välbefinnande) — Symtom, smärta, mående och känslor
105
- I (Integritet) — Vanor, önskemål och preferenser
106
- P (Prevention) — Förebyggande åtgärder som nämnts
107
- S (Säkerhet) — Risker, mediciner och säkerhetsaspekter
108
-
109
- SAMTAL:
110
- "{text}"
111
 
112
- STEG-FÖR-STEG ANALYS:
113
- Tänk igenom detta systematiskt:
114
-
115
- 1. Läs igenom samtalet noggrant.
116
- 2. Identifiera ALL information som nämnts (symtom, känslor, vanor, mediciner, risker).
117
- 3. Sortera varje informationsdel:
118
- - Handlar det om hur patienten MÅR? → V (Välbefinnande)
119
- - Handlar det om patientens VANOR/PREFERENSER? → I (Integritet)
120
- - Handlar det om FÖREBYGGANDE åtgärder? → P (Prevention)
121
- - Handlar det om RISKER/MEDICINER/SÄKERHET? → S (Säkerhet)
122
- 4. För varje kategori: Formulera en kort, professionell mening.
123
- 5. Om kategorin är tom: Skriv "Ingen relevant information."
124
-
125
- Genomför analysen steg-för-steg, sedan ge ditt svar i exakt VIPS-format:
126
- V: [din analys]
127
- I: [din analys]
128
- P: [din analys]
129
- S: [din analys]"""
130
 
131
- def build_prompt(self, text: str) -> str:
132
- """
133
- Build prompt based on configured technique.
134
- Used for research comparison between Few-shot and Chain-of-Thought.
135
- """
136
- if Config.PROMPT_TECHNIQUE == "chain_of_thought":
137
- logger.info("Using Chain-of-Thought prompting")
138
- return self.build_prompt_chain_of_thought(text)
139
- else: # Default to few_shot
140
- logger.info("Using Few-shot prompting")
141
- return self.build_prompt_few_shot(text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
- def classify(self, text: str) -> str:
144
- """
145
- Classify text according to VIPS model with dual GDPR protection.
146
- Returns VIPS-formatted text.
147
- """
148
- try:
149
- # Layer 1: Anonymize input
150
- anonymized_input = GDPRFilter.anonymize(text)
151
- logger.info("Input anonymized (Layer 1)")
152
-
153
- # Build prompt using selected technique
154
- prompt = self.build_prompt(anonymized_input)
155
- response = self.llm.chat(prompt)
156
-
157
- # Layer 2: Anonymize output
158
- anonymized_output = GDPRFilter.anonymize(response)
159
- logger.info("Output anonymized (Layer 2)")
160
-
161
- # Validate anonymization
162
- if not GDPRFilter.validate_anonymization(anonymized_output):
163
- logger.error("GDPR validation failed!")
164
-
165
- return anonymized_output
166
- except Exception as e:
167
- logger.error(f"VIPS classification error: {e}")
168
- raise
169
 
170
- @staticmethod
171
- def parse_vips(vips_text: str) -> Dict[str, str]:
172
- """Parse VIPS text into dictionary"""
173
- vips = {k: "Ingen relevant information." for k in ["V", "I", "P", "S"]}
174
 
175
- for line in vips_text.strip().split("\n"):
176
- line = line.strip()
177
- for key in vips:
178
- if line.startswith(f"{key}:"):
179
- vips[key] = line[2:].strip()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
180
 
181
- return vips
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
  VoiceNote AI - VIPS Classifier
3
+ Classifies patient information into VIPS categories using prompt engineering
 
 
4
  """
5
 
6
  import logging
7
+ from config import Config, VIPS_CATEGORIES
8
+ from gdpr_filter import apply_dual_layer_gdpr
 
 
9
 
10
  logger = logging.getLogger(__name__)
11
 
12
 
13
+ def build_prompt_few_shot(text: str) -> str:
14
+ """
15
+ Build Few-shot prompting with 3 concrete examples
16
 
17
+ Reference: Brown et al. (2020) - Language Models are Few-Shot Learners
18
+ """
19
+ prompt = f"""Du är en AI-assistent som hjälper sjuksköterskor att strukturera journalanteckningar enligt VIPS-modellen.
20
+
21
+ VIPS står för:
22
+ - V (Välbefinnande): Fysiska och psykiska symtom, smärta, känslor
23
+ - I (Integritet): Vanor, preferenser, sociala relationer
24
+ - P (Prevention): Förebyggande åtgärder, hälsofrämjande aktiviteter
25
+ - S (Säkerhet): Risker, läkemedel, säkerhetsåtgärder
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
 
27
  EXEMPEL 1:
28
+ Input: "Jag har ont i huvudet och känner mig trött."
29
+ Output:
30
+ V: Patienten rapporterar huvudvärk och trötthet.
31
  I: Ingen relevant information.
32
  P: Ingen relevant information.
33
+ S: Ingen relevant information.
34
 
35
  EXEMPEL 2:
36
+ Input: "Jag tar Metoprolol dagligen och röker 10 cigaretter per dag."
37
+ Output:
38
+ V: Ingen relevant information.
39
+ I: Patienten röker 10 cigaretter dagligen.
40
+ P: Rökavvänjning kan diskuteras.
41
+ S: Patienten tar Metoprolol dagligen.
42
 
43
  EXEMPEL 3:
44
+ Input: "Jag har ont i bröstet, känner mig yr, och har svårt att andas."
45
+ Output:
46
+ V: Patienten rapporterar bröstsmärta, yrsel och andningssvårigheter.
47
  I: Ingen relevant information.
48
  P: Ingen relevant information.
49
+ S: Akuta symtom som kräver omedelbar bedömning.
50
 
51
+ NU ÄR DET DIN TUR:
52
+ Input: "{text}"
 
53
 
54
+ Ge ENDAST svaret i VIPS-format. Var kortfattad och professionell. Om ingen information finns för en kategori, skriv "Ingen relevant information."
55
+
56
+ Output:"""
57
 
58
+ return prompt
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+
61
+ def build_prompt_chain_of_thought(text: str) -> str:
62
+ """
63
+ Build Chain-of-Thought prompting with step-by-step reasoning
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
+ Reference: Wei et al. (2022) - Chain-of-Thought Prompting Elicits Reasoning
66
+ """
67
+ prompt = f"""Du är en AI-assistent som hjälper sjuksköterskor att strukturera journalanteckningar enligt VIPS-modellen.
68
+
69
+ VIPS står för:
70
+ - V (Välbefinnande): Fysiska och psykiska symtom, smärta, känslor
71
+ - I (Integritet): Vanor, preferenser, sociala relationer
72
+ - P (Prevention): Förebyggande åtgärder, hälsofrämjande aktiviteter
73
+ - S (Säkerhet): Risker, läkemedel, säkerhetsåtgärder
74
+
75
+ STEG-FÖR-STEG ANALYS:
76
+
77
+ Följ dessa steg för att klassificera informationen:
78
+
79
+ Steg 1: Läs patientens berättelse noggrant
80
+ Steg 2: Identifiera alla symtom och känslor → placera under V
81
+ Steg 3: Identifiera vanor och preferenser → placera under I
82
+ Steg 4: Identifiera förebyggande åtgärder → placera under P
83
+ Steg 5: Identifiera risker och läkemedel → placera under S
84
+
85
+ Patientens berättelse:
86
+ "{text}"
87
+
88
+ Analysera texten steg för steg och ge sedan svaret i VIPS-format. Var kortfattad och professionell. Om ingen information finns för en kategori, skriv "Ingen relevant information."
89
+
90
+ Output:"""
91
 
92
+ return prompt
93
+
94
+
95
+ def classify_vips(text: str, mistral_client) -> dict:
96
+ """
97
+ Classify text into VIPS categories
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
 
99
+ Args:
100
+ text: Input text to classify
101
+ mistral_client: Mistral AI client instance
 
102
 
103
+ Returns:
104
+ Dictionary with VIPS classifications
105
+ """
106
+ # Apply GDPR Layer 1: Anonymize input
107
+ anonymized_input, _ = apply_dual_layer_gdpr(text, "")
108
+
109
+ # Select prompt technique
110
+ technique = Config.PROMPT_TECHNIQUE
111
+ logger.info(f"Using {technique} prompting")
112
+
113
+ if technique == "few_shot":
114
+ prompt = build_prompt_few_shot(anonymized_input)
115
+ max_tokens = Config.LLM_MAX_TOKENS_FEW_SHOT
116
+ else: # chain_of_thought
117
+ prompt = build_prompt_chain_of_thought(anonymized_input)
118
+ max_tokens = Config.LLM_MAX_TOKENS_CHAIN_OF_THOUGHT
119
+
120
+ # Generate VIPS classification
121
+ response = mistral_client.generate(
122
+ prompt=prompt,
123
+ max_tokens=max_tokens,
124
+ temperature=Config.LLM_TEMPERATURE
125
+ )
126
+
127
+ # Apply GDPR Layer 2: Anonymize output
128
+ _, anonymized_output = apply_dual_layer_gdpr("", response)
129
+
130
+ # Parse VIPS categories from response
131
+ vips = parse_vips_response(anonymized_output)
132
+
133
+ return vips
134
+
135
+
136
+ def parse_vips_response(response: str) -> dict:
137
+ """
138
+ Parse VIPS categories from LLM response
139
+
140
+ Args:
141
+ response: Raw LLM response
142
 
143
+ Returns:
144
+ Dictionary with parsed VIPS categories
145
+ """
146
+ vips = {
147
+ "V": "Ingen relevant information.",
148
+ "I": "Ingen relevant information.",
149
+ "P": "Ingen relevant information.",
150
+ "S": "Ingen relevant information."
151
+ }
152
+
153
+ lines = response.strip().split('\n')
154
+
155
+ for line in lines:
156
+ line = line.strip()
157
+ if line.startswith("V:"):
158
+ vips["V"] = line[2:].strip()
159
+ elif line.startswith("I:"):
160
+ vips["I"] = line[2:].strip()
161
+ elif line.startswith("P:"):
162
+ vips["P"] = line[2:].strip()
163
+ elif line.startswith("S:"):
164
+ vips["S"] = line[2:].strip()
165
+
166
+ return vips