k96beni commited on
Commit
42e1582
·
verified ·
1 Parent(s): 903a885

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +169 -40
app.py CHANGED
@@ -24,7 +24,7 @@ CHUNK_OVERLAP = 200 # Nytt: Overlapping chunks för att inte tappa kontext
24
  RETRIEVAL_K = 5 # Antal chunker att hämta vid varje sökning
25
 
26
  # Uppdaterad modell till Sonnet 4
27
- MODEL_NAME = "claude-sonnet-4-20250514" # Behåller din specificerade Sonnet, Claude-3.5-Sonnet är "claude-3-5-sonnet-20240620"
28
 
29
  # Kontrollera om vi kör i Hugging Face-miljön
30
  IS_HUGGINGFACE = os.environ.get("SPACE_ID") is not None
@@ -77,15 +77,30 @@ chunk_sources = []
77
  chunk_priorities = [] # Ny: Prioritet för varje chunk
78
  faq_dict = {} # Dictionary för direktmatchning av vanliga frågor
79
 
80
- # Nya globala variabler för källprioriterng
81
  source_priorities = {
82
- "FAQ": 1.0, # Högsta prioritet
83
  "ChargeNode App": 0.8,
84
  "ChargeNode Portal": 0.8,
85
  "Företagskonto": 0.8,
86
  "local_files": 0.6 # Lägsta prioritet för övriga filer
87
  }
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  # Förbättrade nyckelord med organisationskontext
90
  system_keywords = {
91
  "app": ["app", "mobil", "telefon", "ladda bil", "ladda fordon", "qr-kod", "skanna", "kartvy", "favoriter", "laddningar", "nedre meny", "mitt privata"],
@@ -93,24 +108,66 @@ system_keywords = {
93
  "portal", "dashboard", "medlemmar", "statistik", "priser", "avtal", "felanmälan", "hjälpcenter",
94
  "samfällighet", "samfällighetsförening", "bostadsrättsförening", "förening", "organisation",
95
  "vi är", "vi har avtal", "våra uppgifter", "vårt avtal", "vår förening", "adminpanel",
96
- "administrera", "hantera medlemmar", "logga in portal", "portal.chargenode"
 
97
  ],
98
  "företagskonto": [
99
  "företag", "företagskonto", "administratör", "fakturor", "behörighet", "användare",
100
  "org.nummer", "organisationsnummer", "företagsavtal", "mitt företag", "vårt företag"
101
  ],
102
  "betalning_privat": [
103
- "mitt betalsätt", "min betalmetod", "mitt kort", "min betalning", "privat betalkort",
104
  "personlig betalning", "mitt privata kort"
105
  ]
106
  }
107
 
108
- # Nya organisationsspecifika regler
109
  organization_types = [
110
  "samfällighet", "samfällighetsförening", "bostadsrättsförening", "förening",
111
- "organisation", "kommun", "kommunal", "offentlig", "vi är", "vi har avtal"
 
112
  ]
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  # --- Förbättrad loggfunktion ---
115
  def safe_append_to_log(log_entry):
116
  """Säker metod för att lägga till loggdata utan att förlora historisk information."""
@@ -152,7 +209,16 @@ def identify_source_context(query):
152
  has_collective_pronouns = any(word in query_lower for word in ["vi", "våra", "vårt", "vår"])
153
  mentions_agreement = any(word in query_lower for word in ["avtal", "avtalet", "överenskommelse"])
154
 
155
- if is_organization or (has_collective_pronouns and mentions_agreement):
 
 
 
 
 
 
 
 
 
156
  print(f"🏢 Identifierad som organisationsfråga: {query}")
157
 
158
  # Om det handlar om fakturor/betalningar för företag -> Företagskonto
@@ -180,7 +246,7 @@ def identify_source_context(query):
180
  return "Företagskonto"
181
 
182
  # Standard fallback - men inte FAQ för organisationer
183
- if is_organization or has_collective_pronouns:
184
  return "ChargeNode Portal"
185
  else:
186
  return "FAQ"
@@ -348,34 +414,53 @@ def enhanced_prepare_chunks(text_data):
348
  return chunks_list, sources_list, chunk_priorities_list
349
 
350
  def add_organization_faqs():
351
- """Lägger till specifika FAQ-svar för organisationer."""
352
  global faq_dict
353
 
354
- # Samfällighets- och föreningsfrågor
355
- samfallighet_svar = """För samfällighetsföreningar och andra organisationer med avtal:
 
 
356
 
357
- 1. Gå till portal.chargenode.eu
358
- 2. Logga in med de uppgifter ni fick när avtalet tecknades
359
- 3. I portalen kan ni:
360
- - Se Dashboard med översikt
361
- - Hantera medlemmar under 'Medlemmar'
362
- - Se statistik under 'Statistik'
363
- - Ändra priser under 'Priser'
364
- - Hantera avtal under 'Avtal'
365
- - Se fakturering under 'Fakturering'
366
 
367
- Om ni inte har inloggningsuppgifter, kontakta support@chargenode.eu eller 010-2051055."""
 
 
 
 
 
 
368
 
 
 
 
 
 
 
 
 
369
  organization_keys = [
370
  "samfällighetsförening logga in",
371
- "förening logga in portal",
372
  "vi har avtal hur loggar vi in",
373
  "ändra våra uppgifter",
374
  "hantera vår organisation",
375
  "organisationsuppgifter",
376
  "samfällighet portal",
377
  "bostadsrättsförening portal",
378
- "vi är en förening"
 
 
 
 
 
 
 
 
 
379
  ]
380
 
381
  for key in organization_keys:
@@ -389,9 +474,37 @@ def enhanced_check_direct_match(query):
389
  is_organization = any(org_type in query_lower for org_type in organization_types)
390
  has_collective_pronouns = any(word in query_lower for word in ["vi", "våra", "vårt", "vår"])
391
 
392
- if is_organization or has_collective_pronouns:
393
- print(f"🏢 Organisationsfråga detekterad - hoppar över FAQ direktmatchning")
394
- return None # Låt RAG hantera detta istället
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
395
 
396
  # Endast för PRIVATA betalningsfrågor
397
  payment_prefixes = [
@@ -409,7 +522,7 @@ def enhanced_check_direct_match(query):
409
  is_general_payment = any(term in query_lower for term in general_payment_terms)
410
 
411
  if any(query_lower.startswith(prefix) for prefix in payment_prefixes) and \
412
- (is_private_payment or (is_general_payment and not has_collective_pronouns)):
413
 
414
  payment_answer = """Så här lägger du till/ändrar betalkort:
415
  1. Öppna ChargeNode-appen
@@ -444,7 +557,7 @@ OBS! Se till att kortet har pengar och att det är upplåst för internetbetalni
444
  return None
445
 
446
  def enhanced_retrieve_context(query, k=RETRIEVAL_K):
447
- """Förbättrad kontexthämtning med källprioriterng och smart viktning."""
448
  initialize_enhanced_embeddings()
449
 
450
  # Steg 1: Kontrollera direktmatchning (högsta prioritet)
@@ -457,9 +570,14 @@ def enhanced_retrieve_context(query, k=RETRIEVAL_K):
457
  print("Varning: Embedder eller FAISS-index inte tillgängligt")
458
  return "", []
459
 
460
- # Steg 2: Identifiera förväntad källkontext
461
  preferred_source = identify_source_context(query)
462
- print(f"📍 Identifierad förväntad källa: {preferred_source}")
 
 
 
 
 
463
 
464
  # Steg 3: Utför embedding-sökning
465
  query_embedding = embedder.encode([query], convert_to_numpy=True)
@@ -472,34 +590,44 @@ def enhanced_retrieve_context(query, k=RETRIEVAL_K):
472
  search_k = min(k * 3, len(chunks))
473
  D, I = index.search(query_embedding, search_k)
474
 
475
- # Steg 4: Viktad ranking baserat både relevans och källprioritet
476
  candidates = []
477
  for idx, score in zip(I[0], D[0]):
478
  if 0 <= idx < len(chunks):
479
  source = chunk_sources[idx]
480
- chunk_priority = chunk_priorities[idx] if idx < len(chunk_priorities) else 0.5
481
 
482
- # Bonuspoäng om det matchar förväntad källa
 
 
 
483
  source_bonus = 0.3 if source == preferred_source else 0.0
484
 
485
- # Kombinera semantic similarity med source priority
486
- combined_score = score + chunk_priority + source_bonus
 
 
 
 
 
487
 
488
  candidates.append({
489
  'chunk': chunks[idx],
490
  'source': source,
491
  'score': combined_score,
492
  'semantic_score': score,
493
- 'priority': chunk_priority
 
494
  })
495
 
496
  # Steg 5: Sortera och välj topp k resultat
497
  candidates.sort(key=lambda x: x['score'], reverse=True)
498
  top_candidates = candidates[:k]
499
 
500
- # Steg 6: Logga vad som valdes för debugging
501
  print(f"🔍 Valda källor: {[c['source'] for c in top_candidates]}")
502
- print(f"📊 Scores: {[(c['source'], round(c['score'], 3)) for c in top_candidates]}")
 
 
503
 
504
  # Steg 7: Bygg kontextsvar
505
  retrieved_chunks = [c['chunk'] for c in top_candidates]
@@ -513,7 +641,7 @@ def initialize_enhanced_embeddings():
513
  global embedder, embeddings, index, chunks, chunk_sources, chunk_priorities, faq_dict
514
 
515
  if embedder is None:
516
- print("🚀 Initierar förbättrad RAG med källprioriterng...")
517
 
518
  # Ladda data med källseparation
519
  print("📁 Laddar källseparerad textdata...")
@@ -526,6 +654,7 @@ def initialize_enhanced_embeddings():
526
  print(f"📦 {len(chunks)} chunks skapade")
527
 
528
  # NYTT: Lägg till organisationsspecifika FAQ
 
529
  add_organization_faqs()
530
 
531
  if not chunks:
 
24
  RETRIEVAL_K = 5 # Antal chunker att hämta vid varje sökning
25
 
26
  # Uppdaterad modell till Sonnet 4
27
+ MODEL_NAME = "claude-sonnet-4-20250514"
28
 
29
  # Kontrollera om vi kör i Hugging Face-miljön
30
  IS_HUGGINGFACE = os.environ.get("SPACE_ID") is not None
 
77
  chunk_priorities = [] # Ny: Prioritet för varje chunk
78
  faq_dict = {} # Dictionary för direktmatchning av vanliga frågor
79
 
80
+ # FÖRBÄTTRADE källprioriteringar - nu som standard, kommer att överskridas av dynamisk prioritering
81
  source_priorities = {
82
+ "FAQ": 1.0, # Standard högsta prioritet
83
  "ChargeNode App": 0.8,
84
  "ChargeNode Portal": 0.8,
85
  "Företagskonto": 0.8,
86
  "local_files": 0.6 # Lägsta prioritet för övriga filer
87
  }
88
 
89
+ # NYA: Nyckelord för att identifiera frågetyper och dynamisk prioritering
90
+ question_type_keywords = {
91
+ "navigation": [
92
+ "hur gör jag", "hur kommer jag", "var hittar jag", "var finns", "hur når jag",
93
+ "hur navigerar", "hur går jag till", "vilken meny", "vilken flik", "klicka på",
94
+ "tryck på", "välj", "gå till", "öppna", "steg", "instruktion", "navigation",
95
+ "systemträd", "meny", "knapp", "flik", "tab", "ikon", "länk", "hur använder",
96
+ "hur kommer vi", "var hittar vi", "hur får vi tillgång", "hur får vi access"
97
+ ],
98
+ "quick_answer": [
99
+ "vad kostar", "vad är", "varför", "när", "pris", "kostnad", "avgift",
100
+ "ja eller nej", "kan jag", "får jag", "är det möjligt", "vad innebär"
101
+ ]
102
+ }
103
+
104
  # Förbättrade nyckelord med organisationskontext
105
  system_keywords = {
106
  "app": ["app", "mobil", "telefon", "ladda bil", "ladda fordon", "qr-kod", "skanna", "kartvy", "favoriter", "laddningar", "nedre meny", "mitt privata"],
 
108
  "portal", "dashboard", "medlemmar", "statistik", "priser", "avtal", "felanmälan", "hjälpcenter",
109
  "samfällighet", "samfällighetsförening", "bostadsrättsförening", "förening", "organisation",
110
  "vi är", "vi har avtal", "våra uppgifter", "vårt avtal", "vår förening", "adminpanel",
111
+ "administrera", "hantera medlemmar", "logga in portal", "portal.chargenode",
112
+ "hur får vi tillgång", "hur får vi access", "våra inställningar", "vår administration"
113
  ],
114
  "företagskonto": [
115
  "företag", "företagskonto", "administratör", "fakturor", "behörighet", "användare",
116
  "org.nummer", "organisationsnummer", "företagsavtal", "mitt företag", "vårt företag"
117
  ],
118
  "betalning_privat": [
119
+ "mitt betalsätt", "min betalmetod", "mitt betalkort", "mitt kort", "min betalning", "privat betalkort",
120
  "personlig betalning", "mitt privata kort"
121
  ]
122
  }
123
 
124
+ # UTÖKADE organisationsspecifika regler
125
  organization_types = [
126
  "samfällighet", "samfällighetsförening", "bostadsrättsförening", "förening",
127
+ "organisation", "kommun", "kommunal", "offentlig", "vi är", "vi har avtal",
128
+ "våra", "vårt", "vår", "vi får", "vi vill", "vi behöver"
129
  ]
130
 
131
+ def get_question_type(query):
132
+ """Identifierar typ av fråga för dynamisk prioritering."""
133
+ query_lower = query.lower()
134
+
135
+ # Kontrollera för navigationsfrågor
136
+ if any(keyword in query_lower for keyword in question_type_keywords["navigation"]):
137
+ return "navigation"
138
+
139
+ # Kontrollera för snabba svar
140
+ if any(keyword in query_lower for keyword in question_type_keywords["quick_answer"]):
141
+ return "quick_answer"
142
+
143
+ return "general"
144
+
145
+ def get_dynamic_source_priorities(query, preferred_source):
146
+ """Justerar källprioriteringar baserat på frågetyp."""
147
+ question_type = get_question_type(query)
148
+
149
+ if question_type == "navigation":
150
+ # För navigationsfrågor: prioritera systemträd över FAQ
151
+ return {
152
+ "ChargeNode App": 1.0, # Högst för navigationsfrågor
153
+ "ChargeNode Portal": 1.0, # Högst för navigationsfrågor
154
+ "Företagskonto": 1.0, # Högst för navigationsfrågor
155
+ "FAQ": 0.7, # Lägre för navigationsfrågor
156
+ "local_files": 0.5
157
+ }
158
+ elif question_type == "quick_answer":
159
+ # För snabba svar: prioritera FAQ
160
+ return {
161
+ "FAQ": 1.0, # Högst för snabba svar
162
+ "ChargeNode App": 0.6,
163
+ "ChargeNode Portal": 0.6,
164
+ "Företagskonto": 0.6,
165
+ "local_files": 0.4
166
+ }
167
+ else:
168
+ # Allmänna frågor: använd standard
169
+ return source_priorities
170
+
171
  # --- Förbättrad loggfunktion ---
172
  def safe_append_to_log(log_entry):
173
  """Säker metod för att lägga till loggdata utan att förlora historisk information."""
 
209
  has_collective_pronouns = any(word in query_lower for word in ["vi", "våra", "vårt", "vår"])
210
  mentions_agreement = any(word in query_lower for word in ["avtal", "avtalet", "överenskommelse"])
211
 
212
+ # FÖRBÄTTRAD organisationsdetektering
213
+ organization_phrases = [
214
+ "samfällighetsförening", "bostadsrättsförening", "förening", "organisation",
215
+ "vi är", "vi har avtal", "våra uppgifter", "vårt avtal", "vår förening",
216
+ "hur får vi tillgång", "hur får vi access"
217
+ ]
218
+
219
+ has_org_phrase = any(phrase in query_lower for phrase in organization_phrases)
220
+
221
+ if is_organization or has_org_phrase or (has_collective_pronouns and mentions_agreement):
222
  print(f"🏢 Identifierad som organisationsfråga: {query}")
223
 
224
  # Om det handlar om fakturor/betalningar för företag -> Företagskonto
 
246
  return "Företagskonto"
247
 
248
  # Standard fallback - men inte FAQ för organisationer
249
+ if is_organization or has_collective_pronouns or has_org_phrase:
250
  return "ChargeNode Portal"
251
  else:
252
  return "FAQ"
 
414
  return chunks_list, sources_list, chunk_priorities_list
415
 
416
  def add_organization_faqs():
417
+ """FÖRBÄTTRADE FAQ-svar för organisationer."""
418
  global faq_dict
419
 
420
+ # Omfattande svar för samfälligheter och föreningar
421
+ samfallighet_svar = """För samfällighetsföreningar och andra organisationer med ChargeNode-avtal:
422
+
423
+ **Så får ni tillgång till er portal:**
424
 
425
+ 1. **Gå till portal.chargenode.eu**
426
+ 2. **Logga in** med de inloggningsuppgifter ni fick när avtalet tecknades
427
+ 3. Om ni saknar inloggningsuppgifter, kontakta oss på support@chargenode.eu eller 010-2051055
 
 
 
 
 
 
428
 
429
+ **I portalen kan ni:**
430
+ • **Dashboard** - Se översikt och statistik
431
+ • **Medlemmar** - Hantera medlemmar och grupper
432
+ • **Statistik** - Detaljerad förbrukningsdata
433
+ • **Priser** - Ställa in och ändra priser
434
+ • **Avtal** - Se och hantera avtalsinformation
435
+ • **Fakturering** - Fakturaöversikt och betalning
436
 
437
+ **Ändra uppgifter:**
438
+ • Klicka på 'Medlemmar' för att hantera medlemsuppgifter
439
+ • Gå till 'Avtal' för att se kontaktinformation
440
+ • Under 'Fakturering' kan ni ändra fakturauppgifter
441
+
442
+ Har ni problem med inloggning eller behöver hjälp? Kontakta vår support så hjälper vi er direkt."""
443
+
444
+ # Utökade variationer för organisationsfrågor
445
  organization_keys = [
446
  "samfällighetsförening logga in",
447
+ "förening logga in portal",
448
  "vi har avtal hur loggar vi in",
449
  "ändra våra uppgifter",
450
  "hantera vår organisation",
451
  "organisationsuppgifter",
452
  "samfällighet portal",
453
  "bostadsrättsförening portal",
454
+ "vi är en förening",
455
+ "hur får vi tillgång",
456
+ "hur får vi access",
457
+ "vi är en samfällighetsförening",
458
+ "logga in och ändra uppgifter",
459
+ "tillgång för att ändra uppgifter",
460
+ "access till våra uppgifter",
461
+ "vi vill ändra våra uppgifter",
462
+ "våra inställningar",
463
+ "vår administration"
464
  ]
465
 
466
  for key in organization_keys:
 
474
  is_organization = any(org_type in query_lower for org_type in organization_types)
475
  has_collective_pronouns = any(word in query_lower for word in ["vi", "våra", "vårt", "vår"])
476
 
477
+ # FÖRBÄTTRAD detektering av organisationsfraser
478
+ organization_phrases = [
479
+ "samfällighetsförening", "bostadsrättsförening", "förening", "organisation",
480
+ "vi är", "vi har avtal", "våra uppgifter", "vårt avtal", "vår förening",
481
+ "hur får vi tillgång", "hur får vi access"
482
+ ]
483
+ has_org_phrase = any(phrase in query_lower for phrase in organization_phrases)
484
+
485
+ # Om det är en organisationsfråga, kontrollera FAQ dictionary för organisationer
486
+ if is_organization or has_collective_pronouns or has_org_phrase:
487
+ print(f"🏢 Organisationsfråga detekterad: {query}")
488
+
489
+ # Kontrollera exakt matchning för organisationer
490
+ if query_lower in faq_dict:
491
+ print(f"✅ Hittade exakt FAQ-matchning för organisation: {query}")
492
+ return faq_dict[query_lower]
493
+
494
+ # Kontrollera partiell matchning för organisationer
495
+ for key, value in faq_dict.items():
496
+ if any(phrase in key for phrase in organization_phrases):
497
+ query_words = set(re.findall(r'\w+', query_lower))
498
+ key_words = set(re.findall(r'\w+', key))
499
+ common_words = query_words.intersection(key_words)
500
+
501
+ if len(common_words) >= 2: # Kräv minst 2 gemensamma ord
502
+ print(f"✅ Hittade partiell FAQ-matchning för organisation: {key}")
503
+ return value
504
+
505
+ # Om ingen FAQ-matchning för organisationer, låt RAG hantera det
506
+ print(f"ℹ️ Ingen direkt FAQ-matchning för organisation, låter RAG hantera")
507
+ return None
508
 
509
  # Endast för PRIVATA betalningsfrågor
510
  payment_prefixes = [
 
522
  is_general_payment = any(term in query_lower for term in general_payment_terms)
523
 
524
  if any(query_lower.startswith(prefix) for prefix in payment_prefixes) and \
525
+ (is_private_payment or (is_general_payment and not has_collective_pronouns and not has_org_phrase)):
526
 
527
  payment_answer = """Så här lägger du till/ändrar betalkort:
528
  1. Öppna ChargeNode-appen
 
557
  return None
558
 
559
  def enhanced_retrieve_context(query, k=RETRIEVAL_K):
560
+ """Förbättrad kontexthämtning med dynamisk källprioriterng."""
561
  initialize_enhanced_embeddings()
562
 
563
  # Steg 1: Kontrollera direktmatchning (högsta prioritet)
 
570
  print("Varning: Embedder eller FAISS-index inte tillgängligt")
571
  return "", []
572
 
573
+ # Steg 2: Identifiera förväntad källkontext och frågetyp
574
  preferred_source = identify_source_context(query)
575
+ question_type = get_question_type(query)
576
+ dynamic_priorities = get_dynamic_source_priorities(query, preferred_source)
577
+
578
+ print(f"📍 Förväntad källa: {preferred_source}")
579
+ print(f"🔍 Frågetyp: {question_type}")
580
+ print(f"⚖️ Dynamiska prioriteter: {dynamic_priorities}")
581
 
582
  # Steg 3: Utför embedding-sökning
583
  query_embedding = embedder.encode([query], convert_to_numpy=True)
 
590
  search_k = min(k * 3, len(chunks))
591
  D, I = index.search(query_embedding, search_k)
592
 
593
+ # Steg 4: Viktad ranking med dynamiska prioriteter
594
  candidates = []
595
  for idx, score in zip(I[0], D[0]):
596
  if 0 <= idx < len(chunks):
597
  source = chunk_sources[idx]
 
598
 
599
+ # Använd dynamiska prioriteter istället för statiska
600
+ source_priority = dynamic_priorities.get(source, 0.5)
601
+
602
+ # Extra bonuspoäng om det matchar förväntad källa
603
  source_bonus = 0.3 if source == preferred_source else 0.0
604
 
605
+ # Ytterligare bonus för navigationsfrågor från systemträd
606
+ navigation_bonus = 0.0
607
+ if question_type == "navigation" and source in ["ChargeNode App", "ChargeNode Portal", "Företagskonto"]:
608
+ navigation_bonus = 0.2
609
+
610
+ # Kombinera alla poäng
611
+ combined_score = score + source_priority + source_bonus + navigation_bonus
612
 
613
  candidates.append({
614
  'chunk': chunks[idx],
615
  'source': source,
616
  'score': combined_score,
617
  'semantic_score': score,
618
+ 'priority': source_priority,
619
+ 'navigation_bonus': navigation_bonus
620
  })
621
 
622
  # Steg 5: Sortera och välj topp k resultat
623
  candidates.sort(key=lambda x: x['score'], reverse=True)
624
  top_candidates = candidates[:k]
625
 
626
+ # Steg 6: Utökad loggning för debugging
627
  print(f"🔍 Valda källor: {[c['source'] for c in top_candidates]}")
628
+ print(f"📊 Detaljerade scores:")
629
+ for c in top_candidates[:3]: # Visa topp 3
630
+ print(f" {c['source']}: total={c['score']:.3f} (semantic={c['semantic_score']:.3f}, priority={c['priority']:.3f}, nav_bonus={c['navigation_bonus']:.3f})")
631
 
632
  # Steg 7: Bygg kontextsvar
633
  retrieved_chunks = [c['chunk'] for c in top_candidates]
 
641
  global embedder, embeddings, index, chunks, chunk_sources, chunk_priorities, faq_dict
642
 
643
  if embedder is None:
644
+ print("🚀 Initierar förbättrad RAG med dynamisk källprioriterng...")
645
 
646
  # Ladda data med källseparation
647
  print("📁 Laddar källseparerad textdata...")
 
654
  print(f"📦 {len(chunks)} chunks skapade")
655
 
656
  # NYTT: Lägg till organisationsspecifika FAQ
657
+ print("🏢 Lägger till organisationsfrågor...")
658
  add_organization_faqs()
659
 
660
  if not chunks: