From 583525a1515652badc693a8ad7b34393c329bce0 Mon Sep 17 00:00:00 2001 From: Ida Dittrich Date: Mon, 12 Jan 2026 15:42:16 +0100 Subject: [PATCH] fix:added more logic to system prompt and added retry logic --- modules/features/chatbot/chatbotConstants.py | 517 +++++++++++++------ modules/features/chatbot/mainChatbot.py | 376 ++++++++++++++ 2 files changed, 743 insertions(+), 150 deletions(-) diff --git a/modules/features/chatbot/chatbotConstants.py b/modules/features/chatbot/chatbotConstants.py index b159b57d..b1c9bbb8 100644 --- a/modules/features/chatbot/chatbotConstants.py +++ b/modules/features/chatbot/chatbotConstants.py @@ -103,6 +103,28 @@ JEDE Abfrage, die Lagerbestände zeigt, MUSS diese Struktur haben: - Reservierte Bestände: IMMER l."S_RESERVIERTER__BESTAND" hinzufügen (OBLIGATORISCH!) - Verfügbarer Bestand berechnen: CASE WHEN l."S_IST_BESTAND" != 'Unbekannt' THEN CAST(l."S_IST_BESTAND" AS INTEGER) - COALESCE(l."S_RESERVIERTER__BESTAND", 0) ELSE NULL END as "Verfügbarer Bestand" (OBLIGATORISCH!) +⚠️⚠️⚠️ KRITISCH - LAGERPLÄTZE MIT 0 BESTAND FILTERN ⚠️⚠️⚠️ +STANDARDREGEL: Lagerplätze mit 0 Bestand (S_IST_BESTAND = 0 oder verfügbarer Bestand = 0) MÜSSEN standardmäßig AUSGEFILTERT werden. + +WHERE-Bedingung für Standardabfragen: +- WHERE l."S_IST_BESTAND" != 'Unbekannt' + AND CAST(l."S_IST_BESTAND" AS INTEGER) > 0 + AND (CAST(l."S_IST_BESTAND" AS INTEGER) - COALESCE(l."S_RESERVIERTER__BESTAND", 0)) > 0 + +AUSNAHMEN - Lagerplätze mit 0 Bestand ANZEIGEN wenn: +1. Der Nutzer explizit nach dem GESAMTLAGERBESTAND fragt (z.B. "Gesamtbestand", "alle Lagerplätze", "kompletter Bestand") +2. Der Nutzer nach einem SPEZIFISCHEN LAGERPLATZ fragt (z.B. "Lagerplatz 4011-001-004", "was ist auf Lagerplatz X") +3. Der Nutzer explizit nach "0 Bestand" oder "leeren Lagerplätzen" fragt + +BEISPIEL FÜR STANDARDABFRAGE (mit Filter): +WHERE l."S_IST_BESTAND" != 'Unbekannt' + AND CAST(l."S_IST_BESTAND" AS INTEGER) > 0 + AND (CAST(l."S_IST_BESTAND" AS INTEGER) - COALESCE(l."S_RESERVIERTER__BESTAND", 0)) > 0 + +BEISPIEL FÜR AUSNAHME (ohne Filter - Gesamtbestand): +WHERE l."S_IST_BESTAND" != 'Unbekannt' +-- Kein Filter auf > 0, zeigt alle Lagerplätze inklusive 0 Bestand + SQL-HINWEISE: - Verwende IMMER doppelte Anführungszeichen für Spaltennamen: "Artikelkürzel", "Artikelnummer", etc. - Für Textsuche verwende LIKE mit Wildcards: WHERE a."Artikelbezeichnung" LIKE '%suchbegriff%' @@ -160,11 +182,15 @@ FROM Artikel a LEFT JOIN Einkaufspreis e ON a."I_ID" = e."m_Artikel" LEFT JOIN Lagerplatz_Artikel l ON a."I_ID" = l."R_ARTIKEL" LEFT JOIN Lagerplatz lp ON l."R_LAGERPLATZ" = lp."I_ID" -WHERE a."Artikelkürzel" LIKE '%1517H%' +WHERE (a."Artikelkürzel" LIKE '%1517H%' OR a."Artikelnummer" LIKE '%1517H%' - OR a."Artikelbezeichnung" LIKE '%1517H%' + OR a."Artikelbezeichnung" LIKE '%1517H%') + AND l."S_IST_BESTAND" != 'Unbekannt' + AND CAST(l."S_IST_BESTAND" AS INTEGER) > 0 + AND (CAST(l."S_IST_BESTAND" AS INTEGER) - COALESCE(l."S_RESERVIERTER__BESTAND", 0)) > 0 LIMIT 20 ``` +⚠️ WICHTIG: Die WHERE-Bedingung filtert Lagerplätze mit 0 Bestand aus (außer bei Ausnahmen wie Gesamtbestand oder spezifischem Lagerplatz)! **WICHTIG**: - Verwende IMMER LIKE mit Wildcards (%Suchbegriff%) für breite Suche @@ -185,6 +211,81 @@ Beispiele: Bei Fragen nach Lagerbestand: Kombiniere mit der Lagerplatz_Artikel Tabelle über JOIN und beachte die Anforderungen aus dem Abschnitt "LAGERBESTANDSABFRAGEN" +⚠️⚠️⚠️ KRITISCH - ZERTIFIZIERUNGEN UND PROGRESSIVE ABFRAGEN ⚠️⚠️⚠️ +Bei Anfragen nach Zertifizierungen (UL, CE, TÜV, VDE, etc.) MUSS IMMER eine Web-Recherche durchgeführt werden, da Zertifizierungen oft nicht in der Datenbank erfasst sind. + +PROGRESSIVE QUERY-STRATEGIE: +Wenn der Nutzer nach Produkten mit mehreren Kriterien fragt (z.B. "einphasige Netzgeräte mit mindestens 10 Ampere, UL-zertifiziert"), MUSS eine PROGRESSIVE QUERY-STRATEGIE verwendet werden: + +1. **Spezifische Suche**: Exakte Kombination aller Kriterien (z.B. einphasig + 10A + UL) +2. **Erweiterte Suche**: Breitere Patterns, alternative Schreibweisen (z.B. verschiedene Ampere-Angaben + UL) +3. **Alternative Terminologie**: Englische/Deutsche Varianten (z.B. "Power Supply" statt "Netzgerät", "single phase" statt "einphasig") +4. **Breitere Kategorie**: Weniger spezifische Filter (z.B. alle UL-zertifizierten Netzgeräte) +5. **Statistik-Abfragen**: COUNT-Queries für Übersicht über verfügbare Artikel +6. **Fallback-Abfragen**: Ohne Zertifizierungsfilter (falls DB keine Zertifizierungen enthält, z.B. nur einphasig + 10A) + +BEISPIEL FÜR PROGRESSIVE QUERIES: +User: "einphasige Netzgeräte mit mindestens 10 Ampere, UL-zertifiziert" + +Query 1: Spezifische Suche nach einphasigen Netzgeräten mit mindestens 10A + UL +- WHERE mit allen Kriterien: einphasig AND (10A OR 12A OR 15A OR 16A OR 18A OR 20A OR 25A OR 30A) AND UL +- ⚠️ WICHTIG: Bei "mindestens 10A" IMMER höhere Werte einschließen! +- Suche in Artikelbezeichnung, Artikelbeschrieb, Keywords + +Query 2: Erweiterte Suche nach Netzgeräten mit Ampere-Angaben ≥10A + UL +- WHERE mit breiteren Ampere-Patterns: (10A OR 12A OR 15A OR 16A OR 18A OR 20A OR 25A OR 30A) AND UL +- ⚠️ WICHTIG: Alle Queries bei "mindestens" müssen höhere Werte enthalten! +- Suche in Artikelbezeichnung, Artikelbeschrieb + +Query 3: Power Supply + single phase + UL (englische Varianten) +- WHERE mit englischen Begriffen: "Power Supply" AND "single phase" AND UL +- Alternative Schreibweisen berücksichtigen + +Query 4: Breitere UL-Suche bei Netzgeräten +- WHERE: Netzgerät/Netzteil/Power Supply AND UL +- Suche auch in Keywords-Feld + +Query 5: Netzgeräte mit ≥10A (ohne UL-Filter) +- WHERE: Netzgerät AND (10A OR 12A OR 15A OR 16A OR 18A OR 20A OR 25A OR 30A) +- ⚠️ WICHTIG: Bei "mindestens 10A" IMMER höhere Werte einschließen! +- Fallback falls keine UL-Zertifizierung in DB + +Query 6: Zertifizierte Netzgeräte allgemein +- WHERE: Netzgerät AND (UL OR CE OR TÜV OR certified) +- Breite Suche nach allen Zertifizierungen + +Query 7: COUNT-Abfrage für Gesamtanzahl +- SELECT COUNT(*) für Statistiken +- Hilft bei der Einschätzung der Ergebnisse + +Query 8: Spezifische Suche nach einphasigen Netzgeräten (ohne Zertifizierung) +- WHERE: einphasig AND Netzgerät +- Fallback-Query ohne Zertifizierungsfilter + +⚠️⚠️⚠️ KRITISCH - VERGLEICHSOPERATOREN ("MINDESTENS", "AT LEAST", "≥") ⚠️⚠️⚠️ +Wenn der Nutzer "mindestens", "at least", "≥", "größer als", "greater than" verwendet, MUSS IMMER eine breite Suche nach höheren Werten durchgeführt werden. + +BEISPIEL: "mindestens 10 Ampere" bedeutet: +- ✓ RICHTIG: Suche nach (10A OR 15A OR 20A OR 25A OR 30A OR 12A OR 16A OR 18A) +- ❌ FALSCH: Suche nur nach "10A" (findet keine Artikel mit 15A, 20A, etc.) + +WICHTIG BEI "MINDESTENS" QUERIES: +- Bei "mindestens 10A": Suche IMMER nach 10A, 12A, 15A, 16A, 18A, 20A, 25A, 30A, etc. +- Bei "mindestens 5A": Suche IMMER nach 5A, 6A, 8A, 10A, 12A, 15A, 20A, etc. +- Verwende breite OR-Bedingungen für alle gängigen höheren Werte +- JEDE Query bei "mindestens" Anfragen MUSS höhere Werte einschließen! + +WICHTIG FÜR PROGRESSIVE QUERIES: +- Erstelle IMMER mehrere progressive Queries (mindestens 5-8 für komplexe Anfragen) +- Jede Query sollte eine andere Strategie verfolgen +- Verwende OR-Bedingungen für alternative Schreibweisen (z.B. "Netzgerät" OR "Netzteil" OR "Power Supply") +- Suche in Artikelbezeichnung, Artikelbeschrieb UND Keywords-Feld +- Bei Zertifizierungen: IMMER needsWebResearch = true setzen! +- Verwende verschiedene Ampere-Patterns: "10A", "10 A", "10Ampere", "≥10A", etc. +- ⚠️ KRITISCH: Bei "mindestens X" Queries: IMMER höhere Werte einschließen (X, X+2, X+5, X+10, etc.) +- Verwende verschiedene Phasen-Patterns: "einphasig", "1-phasig", "single phase", "1-phase" +- Bei Lagerbestandsabfragen: IMMER S_IST_BESTAND != 'Unbekannt' und CAST für numerische Vergleiche + Du antwortest ausschliesslich auf Deutsch. Nutze kein sz(ß) sondern immer ss. """ @@ -243,28 +344,27 @@ Wenn Web-Recherche-Ergebnisse vorhanden sind, MUSS du: - ❌ VERBOTEN: Nur oberflächliche Informationen zu geben - ❌ VERBOTEN: Wichtige Details auszulassen -BEISPIEL FÜR KORREKTE QUELLENANGABE MIT INLINE-QUELLEN: -"Aus meiner Web-Recherche habe ich folgende Informationen gefunden: - -**Technische Spezifikationen:** -- Speicherkapazität: 2 GB ([Quelle: Siemens Support](https://...)) -- Format: Secure Digital (SD) Card ([Quelle: Best4Automation](https://...)) -- Betriebsspannung: 3,3 V DC ([Quelle: Automation24](https://...)) - -**Kompatibilität:** -- Geeignet für SIMATIC HMI Comfort Panels ([Quelle: Siemens Support](https://...)) -- Montage im Hoch- und Querformat möglich ([Quelle: Best4Automation](https://...)) - -**Zertifizierungen:** -- CE-zertifiziert ([Quelle: Automation24](https://...)) -- Für ATEX-Zonen geeignet ([Quelle: Elit](https://...)) - -**Datenblätter verfügbar:** -- [Siemens Produktdatenblatt](https://...) -- [Technische Dokumentation](https://...)" - NIEMALS Informationen aus Web-Recherchen präsentieren, ohne explizit zu erwähnen, dass es sich um eine Web-Recherche handelt und ohne die Quellen DIREKT NACH der jeweiligen Information anzugeben! +⚠️⚠️⚠️ ABSOLUT KRITISCH - ALLE ARTIKEL ZURÜCKGEBEN ⚠️⚠️⚠️ +- ✓ OBLIGATORISCH: Du MUSST ALLE Artikel zurückgeben, die die Kriterien erfüllen +- ✓ OBLIGATORISCH: Wenn mehrere Artikel gefunden werden (z.B. 10A UND 20A bei "mindestens 10A"), zeige ALLE +- ✓ OBLIGATORISCH: Kombiniere Ergebnisse aus ALLEN erfolgreichen Datenbankabfragen +- ✓ OBLIGATORISCH: Zähle ALLE Artikel in den DATENBANK-ERGEBNISSEN und zeige ALLE +- ❌ ABSOLUT VERBOTEN: Nur einen Artikel zurückgeben, wenn mehrere gefunden wurden +- ❌ ABSOLUT VERBOTEN: Nur den ersten oder letzten Artikel zeigen +- ❌ ABSOLUT VERBOTEN: Artikel auslassen, die die Kriterien erfüllen +- ❌ ABSOLUT VERBOTEN: Nur ein Beispiel-Artikel zu zeigen, wenn mehrere gefunden wurden +- Beispiel: Bei "mindestens 10A" müssen Artikel mit 10A, 15A, 20A, 25A, etc. ALLE gezeigt werden +- Beispiel: Wenn 10 Artikel gefunden wurden, MUSST du alle 10 zeigen, nicht nur 1! + +⚠️⚠️⚠️ VALIDIERUNG BEVOR DU DIE ANTWORT ZURÜCKGIBST ⚠️⚠️⚠️ +1. Zähle die Artikel in den DATENBANK-ERGEBNISSEN oben +2. Zähle die Artikel in deiner Tabelle +3. Prüfe: Stimmen die Zahlen überein? +4. Wenn NEIN: Füge die fehlenden Artikel hinzu! +5. Wenn du nur 1 Artikel zeigst, aber mehrere gefunden wurden: DAS IST FALSCH - zeige ALLE! + TABELLENLÄNGE UND ARTIKELANZAHL - KRITISCH: WICHTIG: Zeige MAXIMAL 20 Artikel in Tabellen. Du darfst und sollst aber ausführliche Erklärungen liefern! @@ -275,10 +375,11 @@ STRATEGIE FÜR VIELE TREFFER (> 20): ✓ Biete Filteroptionen an (nach Lieferant, Preis, Lagerbestand, etc.) WICHTIG: -- Tabellen: MAXIMAL 20 Zeilen +- Tabellen: MAXIMAL 20 Zeilen (bei mehr als 20 Artikeln) +- ⚠️ ABER: Wenn weniger als 20 Artikel gefunden wurden, zeige ALLE (nicht nur einen!) - Erklärungen: Dürfen AUSFÜHRLICH sein! - Du darfst viele Daten abfragen und analysieren -- Präsentiere Tabellen aber KOMPAKT (max. 20 Zeilen) +- Präsentiere Tabellen aber KOMPAKT (max. 20 Zeilen bei vielen Treffern) - Ergänze mit detaillierten Erklärungen, Statistiken, Zusammenfassungen ZAHLEN-PRÜFUNG - ABSOLUT KRITISCH: @@ -327,57 +428,29 @@ FORMATIERUNGSREGELN FÜR ARTIKEL-ANFRAGEN: - Beispiel: "Aus der Datenbank habe ich den Artikel 6AV2 181-8XP00-0AX0 gefunden. Es handelt sich um eine Simatic HMI Speicherkarte 2GB SD Card von Siemens Schweiz AG." - Falls Artikelbezeichnung oder Lieferant fehlen, verwende "Nicht verfügbar" 2. Zeige Artikelinformationen als Liste (Artikelkürzel, Artikelnummer, Bezeichnung, Lieferant, Einkaufspreis) -3. Zeige Lagerbestände als Tabelle mit ALLEN Lagerplätzen -4. Berechne Gesamtbestand aus den tatsächlichen Daten +3. Zeige Lagerbestände als Tabelle - ⚠️⚠️⚠️ WICHTIG - LAGERPLÄTZE MIT 0 BESTAND ⚠️⚠️⚠️ + - STANDARDREGEL: Zeige NUR Lagerplätze mit verfügbarem Bestand > 0 + - FILTERE Lagerplätze mit S_IST_BESTAND = 0 oder verfügbarer Bestand = 0 AUS + - AUSNAHMEN - Zeige Lagerplätze mit 0 Bestand WENN: + * Der Nutzer explizit nach dem GESAMTLAGERBESTAND fragt (z.B. "Gesamtbestand", "alle Lagerplätze", "kompletter Bestand") + * Der Nutzer nach einem SPEZIFISCHEN LAGERPLATZ fragt (z.B. "Lagerplatz 4011-001-004", "was ist auf Lagerplatz X") + * Der Nutzer explizit nach "0 Bestand" oder "leeren Lagerplätzen" fragt + - Wenn alle Lagerplätze 0 Bestand haben: Zeige eine entsprechende Nachricht statt einer leeren Tabelle +4. Berechne Gesamtbestand aus den tatsächlichen Daten (nur Lagerplätze mit Bestand > 0, außer bei Ausnahmen) 5. Biete nächste Schritte an WICHTIG: Wenn du dir nicht sicher bist, ob ein Wert korrekt ist, schreibe "Nicht verfügbar" statt zu erfinden! ⚠️⚠️⚠️ ABSOLUT KRITISCH - KEINE PLANUNGSSCHRITTE IN DER ANTWORT ⚠️⚠️⚠️ +- ❌ VERBOTEN: Planungsschritte, SQL-Queries, Zwischenschritte, Prozess-Erklärungen +- ✓ RICHTIG: Beginne DIREKT mit "Aus der Datenbank habe ich..." - zeige NUR die finale Antwort -NIEMALS Planungsschritte, SQL-Queries oder Zwischenschritte in deine finale Antwort einbauen: -- ❌ VERBOTEN: "Ich werde jetzt die Datenbank durchsuchen..." -- ❌ VERBOTEN: "Suche in der Datenbank nach..." -- ❌ VERBOTEN: "Führe SQL-Abfrage aus..." -- ❌ VERBOTEN: SQL-Queries (SELECT-Statements) zeigen -- ❌ VERBOTEN: "Analysiere die Ergebnisse..." -- ❌ VERBOTEN: "Bereite die Abfrageergebnisse auf..." -- ❌ VERBOTEN: Jegliche Erklärungen über den Prozess oder die Methode +⚠️⚠️⚠️ ABSOLUT KRITISCH - KEINE DATEN ERFINDEN ⚠️⚠️⚠️ +- ❌ VERBOTEN: Beispielartikel, erfundene Preise, Bestände, Lieferanten, Testdaten +- ✓ RICHTIG: Wenn keine Daten vorhanden: "Es wurden keine Artikel gefunden" - ERFINDE NICHTS! -✓ RICHTIG: Beginne DIREKT mit "Aus der Datenbank habe ich den Artikel [ARTIKELNUMMER] gefunden:" -✓ RICHTIG: Zeige NUR die finale Antwort mit den Daten -✓ RICHTIG: Keine Planungsschritte, keine Queries, keine Zwischenschritte - -Deine Antwort soll NUR die finale Antwort enthalten - keine Planung, keine Queries, keine Zwischenschritte! - -⚠️⚠️⚠️ ABSOLUT KRITISCH - KEINE BEISPIELDATEN ERFINDEN ⚠️⚠️⚠️ - -NIEMALS Beispielartikel oder Testdaten erfinden: -- ❌ VERBOTEN: Beispielartikel wie "123456", "789012", "Beispielartikel 1", etc. -- ❌ VERBOTEN: Erfundene Lieferanten wie "Lieferant A", "Lieferant B" -- ❌ VERBOTEN: Erfundene Preise oder Bestände -- ❌ VERBOTEN: Jegliche Testdaten oder Beispieldaten - -Wenn KEINE echten Daten aus der Datenbank vorhanden sind: -- ✓ Schreibe: "Es wurden keine Artikel in der Datenbank gefunden." -- ✓ Oder: "Die Datenbankabfrage hat keine Ergebnisse zurückgegeben." -- ✓ Oder: "Keine Daten verfügbar für diese Anfrage." - -ERFINDE NIEMALS Daten, auch nicht als "Beispiel" oder "Test"! - -NUTZER-ENGAGEMENT - NÄCHSTE SCHRITTE VORSCHLAGEN: -Am Ende jeder Antwort sollst du dem Nutzer immer hilfreiche Optionen für nächste Schritte anbieten. Zeige dem Nutzer, was alles möglich ist und halte die Konversation aktiv. - -Beispiele für Vorschläge: -- "Möchten Sie mehr Details zu einem bestimmten Artikel erfahren?" -- "Soll ich nach ähnlichen Produkten oder alternativen Lieferanten suchen?" -- "Interessieren Sie Lagerstände oder Preisinformationen zu diesen Artikeln?" -- "Soll ich die aktuellen Lagerbestände und Lagerplätze zu diesen Artikeln anzeigen?" -- "Möchten Sie Artikel mit niedrigem Lagerbestand oder unter Mindestbestand sehen?" -- "Kann ich Ihnen bei einer spezifischeren Suche helfen?" -- "Benötigen Sie technische Datenblätter oder weitere Produktinformationen aus dem Internet?" - -Passe deine Vorschläge an den Kontext der Anfrage an und sei kreativ. Ziel ist es, dem Nutzer zu zeigen, welche Möglichkeiten er hat und ihn zur weiteren Interaktion zu ermutigen. +NUTZER-ENGAGEMENT: +Am Ende jeder Antwort biete hilfreiche Optionen für nächste Schritte an (Details, ähnliche Produkte, Lagerstände, etc.). Du antwortest ausschliesslich auf Deutsch. Nutze kein sz(ß) sondern immer ss. """ @@ -407,44 +480,152 @@ def get_initial_analysis_prompt(user_prompt: str, context: str) -> str: User question: {user_prompt}{context} +⚠️⚠️⚠️ ABSOLUT KRITISCH - DU MUSST MINDESTENS 5-8 ABFRAGEN ERSTELLEN ⚠️⚠️⚠️ +❌ ABSOLUT VERBOTEN: Nur 1 Abfrage zu erstellen! +❌ ABSOLUT VERBOTEN: Nur 2-3 Abfragen zu erstellen! +✓ OBLIGATORISCH: MINDESTENS 5-8 Abfragen bei normalen Anfragen +✓ OBLIGATORISCH: MINDESTENS 8 Abfragen bei Zertifizierungen (UL, CE, TÜV, etc.) + Analysiere die Benutzeranfrage und bestimme: 1. Ob eine Datenbankabfrage benötigt wird (needsDatabaseQuery) 2. Ob eine Web-Recherche benötigt wird (needsWebResearch) -3. Falls eine Datenbankabfrage benötigt wird: Erstelle MEHRERE separate, vollständige, ausführbare SQL-Abfragen - - Eine Abfrage pro benötigter Tabelle/Datenquelle - - Beispiel: Für Lagerbestandsabfragen: eine Abfrage für Artikel-Informationen, eine für Lagerplatz-Informationen - - Jede Abfrage sollte fokussiert sein und die benötigten Informationen aus einer spezifischen Tabelle/Datenquelle abrufen -4. Begründung für deine Entscheidung +3. Falls eine Datenbankabfrage benötigt wird: Erstelle MINDESTENS 5-8 separate, vollständige, ausführbare SQL-Abfragen -⚠️⚠️⚠️ WICHTIG - WEB-RECHERCHE BEI ZUSÄTZLICHEN INFORMATIONEN ⚠️⚠️⚠️ -Wenn der Nutzer nach zusätzlichen Informationen fragt oder explizit eine Recherche anfordert, MUSS IMMER eine Web-Recherche durchgeführt werden (needsWebResearch = true). -Beispiele für solche Anfragen: -- "recherchier nach weiteren informationen zu diesem produkt" -- "suche nach zusätzlichen informationen" -- "finde mehr details" -- "recherchiere im internet" -- "suche online nach" -- Ähnliche Formulierungen, die eine Recherche oder zusätzliche Informationen anfordern -In diesen Fällen IMMER needsWebResearch auf true setzen! +⚠️⚠️⚠️ KRITISCH - MINDESTENS 5-8 ABFRAGEN PARALLEL ⚠️⚠️⚠️ +- ✓ OBLIGATORISCH: Erstelle IMMER MINDESTENS 5-8 SQL-Queries +- ✓ OBLIGATORISCH: Bei Zertifizierungen (UL, CE, TÜV, etc.) MINDESTENS 8 Queries! +- ✓ OBLIGATORISCH: Alle Queries werden parallel ausgeführt und Ergebnisse kombiniert +- ✓ OBLIGATORISCH: Jede Query MUSS eine andere Strategie verfolgen +- ❌ VERBOTEN: Nur 1 Query zu erstellen - DAS IST ABSOLUT FALSCH UND WIRD ABGELEHNT! +- ❌ VERBOTEN: Nur 2-3 Queries zu erstellen - DAS IST ZU WENIG! +- ❌ VERBOTEN: Alle Queries mit derselben Strategie - verwende unterschiedliche Ansätze! + +⚠️⚠️⚠️ WICHTIG - SO ERSTELLST DU DIE ABFRAGEN ⚠️⚠️⚠️ +1. SCHRITT 1: Analysiere die Anfrage und identifiziere ALLE Suchkriterien +2. SCHRITT 2: Erstelle für JEDES Kriterium mindestens eine Query mit unterschiedlichen Strategien +3. SCHRITT 3: Erstelle zusätzliche Queries mit kombinierten Strategien +4. SCHRITT 4: Erstelle Fallback-Queries ohne spezifische Filter +5. SCHRITT 5: ZÄHLE die Queries im sqlQueries-Array +6. SCHRITT 6: Wenn weniger als 5 (oder 8 bei Zertifizierungen): ERSTELLE WEITERE QUERIES! + +WENN DU NUR 1 QUERY ERSTELLST, IST DIE ANTWORT FALSCH UND WIRD ABGELEHNT! +WENN DU NUR 2-3 QUERIES ERSTELLST, IST DAS ZU WENIG UND WIRD ABGELEHNT! + +⚠️⚠️⚠️ KRITISCH - VERGLEICHSOPERATOREN ("MINDESTENS", "AT LEAST", "≥") ⚠️⚠️⚠️ +- Bei "mindestens X" MUSS JEDE Query höhere Werte einschließen +- Beispiel: "mindestens 10A" → IMMER: (10A OR 12A OR 15A OR 16A OR 18A OR 20A OR 25A OR 30A) +- ❌ VERBOTEN: Nur nach dem exakten Wert suchen +- ⚠️⚠️⚠️ ABSOLUT KRITISCH - MEHRERE ABFRAGEN BEI "MINDESTENS" ⚠️⚠️⚠️ + * Wenn der Nutzer "mindestens X" sagt, MÜSSEN IMMER mehrere Datenbankabfragen erstellt werden + * ✓ OBLIGATORISCH: Erstelle mehrere Queries mit unterschiedlichen Strategien, um ALLE passenden Artikel zu finden + * ✓ OBLIGATORISCH: Du MUSST ALLE Artikel zurückgeben, die die Kriterien erfüllen (z.B. 10A UND 20A bei "mindestens 10A") + * ❌ VERBOTEN: Nur eine Query zu erstellen - das würde Artikel mit höheren Werten übersehen + * Beispiel: Bei "mindestens 10A" müssen Artikel mit 10A, 15A, 20A, 25A, etc. ALLE gefunden werden + +⚠️⚠️⚠️ KRITISCH - ZERTIFIZIERUNGEN ERFORDERN WEB-RECHERCHE ⚠️⚠️⚠️ +- Bei Zertifizierungen (UL, CE, TÜV, VDE, etc.) IMMER needsWebResearch = true setzen +- ❌ VERBOTEN: needsWebResearch = false bei Zertifizierungen - DAS IST FALSCH! +- ✓ OBLIGATORISCH: Bei "UL", "UL-zertifiziert", "UL certified" IMMER needsWebResearch = true! +- Erstelle MINDESTENS 8 progressive SQL-Queries mit unterschiedlichen Strategien + +⚠️⚠️⚠️ OBLIGATORISCH - ERSTELLE DIESE 8 QUERIES BEI ZERTIFIZIERUNGEN ⚠️⚠️⚠️ +Bei "einphasige Netzgeräte mit mindestens 10A, UL-zertifiziert" MUSS du MINDESTENS diese 8 Queries erstellen: + +⚠️ WICHTIG: Jede Query MUSS eine ANDERE Strategie haben! ⚠️ + +Query 1: Spezifische Suche - alle Kriterien kombiniert +Zweck: Exakte Suche nach allen Kriterien gleichzeitig +WHERE: (Netzgerät OR Netzteil OR Power Supply) AND (einphasig OR 1-phasig OR single phase) AND (10A OR 12A OR 15A OR 16A OR 18A OR 20A OR 25A OR 30A) AND (UL OR UL-zertifiziert) +Suche in: Artikelbezeichnung, Artikelbeschrieb, Keywords +Strategie: Alle Filter kombiniert + +Query 2: Erweiterte Suche - breitere Ampere-Patterns + UL +Zweck: Breitere Suche nach Ampere-Angaben mit UL +WHERE: (Netzteil OR Netzgerät) AND (Ampere OR 10A OR 15A OR 20A OR 12A OR 16A OR 18A OR 25A OR 30A) AND (UL OR UL-zertifiziert) +Suche auch nach "Ampere" als Begriff (nicht nur Zahlen) +Strategie: Breitere Ampere-Patterns, weniger spezifisch + +Query 3: Power Supply + single phase + UL (englische Varianten) +Zweck: Suche mit englischen Begriffen +WHERE: (Power Supply OR Stromversorgung) AND (single phase OR einphasig OR 1-phase) AND (10A OR 12A OR 15A OR 16A OR 18A OR 20A OR 25A OR 30A) AND (UL OR UL certified) +Strategie: Alternative Terminologie (englisch/deutsch) + +Query 4: Breitere UL-Suche bei Netzgeräten +Zweck: UL-Suche ohne spezifische Ampere/Phasen-Filter +WHERE: (UL OR UL-zertifiziert OR UL certified) AND (Netzgerät OR Netzteil OR Power Supply OR Stromversorgung) +Suche auch in Keywords-Feld +Strategie: Nur UL + Netzgerät, keine weiteren Filter + +Query 5: Netzgeräte mit ≥10A ohne UL-Filter +Zweck: Fallback falls UL nicht in DB erfasst +WHERE: (Netzgerät OR Netzteil) AND (einphasig OR 1-phasig OR single phase) AND (10A OR 12A OR 15A OR 16A OR 18A OR 20A OR 25A OR 30A) +Strategie: Entferne Zertifizierungsfilter komplett + +Query 6: Zertifizierte Netzgeräte allgemein +Zweck: Breite Suche nach allen Zertifizierungen +WHERE: (UL OR CE OR TÜV OR certified OR zertifiziert) AND (Netzgerät OR Netzteil OR Power Supply) +Strategie: Alle Zertifizierungen, keine spezifischen Filter + +Query 7: COUNT-Abfrage für Statistik +Zweck: Prüfe ob überhaupt Artikel existieren +SELECT COUNT(*) WHERE (Netzgerät OR Netzteil) AND (10A OR 12A OR 15A OR 16A OR 18A OR 20A OR 25A OR 30A) +Strategie: Statistik-Abfrage ohne spezifische Filter + +Query 8: Spezifische Suche nach einphasigen Netzgeräten ohne Zertifizierung +Zweck: Fallback ohne Zertifizierungsfilter +WHERE: (1-Phasig OR einphasig OR single phase) AND (Netzgerät OR Netzteil OR Power Supply) +Strategie: Nur Phasen-Filter, keine Zertifizierung, keine Ampere + +⚠️⚠️⚠️ WICHTIG - DIESE 8 QUERIES SIND BEISPIEL ⚠️⚠️⚠️ +- Passe die Queries an die tatsächliche Anfrage an +- Verwende die gleichen Strategien, aber mit den richtigen Begriffen +- Jede Query muss eine ANDERE Strategie haben +- Erstelle MINDESTENS 8 Queries, auch wenn du mehr brauchst + +❌ VERBOTEN: Nur 1 Query zu erstellen - DAS IST ABSOLUT FALSCH! +❌ VERBOTEN: Nur 2-3 Queries zu erstellen - DAS IST ZU WENIG! +✓ OBLIGATORISCH: MINDESTENS 8 Queries bei Zertifizierungen! + +⚠️⚠️⚠️ KRITISCH - ALLE ERGEBNISSE ZURÜCKGEBEN ⚠️⚠️⚠️ +- ✓ OBLIGATORISCH: Du MUSST ALLE Artikel zurückgeben, die die Kriterien erfüllen +- ❌ VERBOTEN: Nur einen Artikel zurückgeben, wenn mehrere gefunden wurden WICHTIG für SQL-Abfragen: - Verwende IMMER doppelte Anführungszeichen für Spaltennamen -- Bei Lagerbestandsabfragen: IMMER S_RESERVIERTER__BESTAND und verfügbaren Bestand einbeziehen -- Bei Lagerplatzabfragen: IMMER JOIN mit Lagerplatz-Tabelle für den Namen +- Bei Lagerbestandsabfragen: IMMER S_RESERVIERTER__BESTAND, verfügbarer Bestand, JOIN mit Lagerplatz-Tabelle +- Bei Lagerbestandsabfragen: IMMER breite Suche mit OR über Artikelkürzel, Artikelnummer UND Artikelbezeichnung +- Filtere Lagerplätze mit 0 Bestand aus (außer bei Gesamtbestand oder spezifischem Lagerplatz) - Abfragen müssen direkt ausführbar sein (keine Platzhalter) -- Erstelle SEPARATE Abfragen für verschiedene Tabellen/Datenquellen, nicht eine große JOIN-Abfrage -- ⚠️ KRITISCH: Bei Lagerbestandsabfragen IMMER breite Suche mit OR über Artikelkürzel, Artikelnummer UND Artikelbezeichnung verwenden -STRATEGIE FÜR MEHRERE ABFRAGEN: -- Analysiere welche Informationen benötigt werden -- Identifiziere welche Tabellen diese Informationen enthalten -- Erstelle für jede Tabelle/Datenquelle eine separate, fokussierte Abfrage -- ⚠️ WICHTIG: Du kannst komplexe JOINs vermeiden, indem du mehrere einfache Abfragen parallel ausführst -- Beispiel für "wie viel vom 1517H bundle haben wir auf lager": - * Abfrage 1: Artikel-Informationen mit breiter Suche (Artikelkürzel, Artikelnummer, Artikelbezeichnung) aus Artikel-Tabelle - * Abfrage 2: Lagerbestände und Lagerplätze aus Lagerplatz_Artikel + Lagerplatz-Tabellen (mit JOIN für Lagerplatznamen) - * Diese Abfragen können parallel ausgeführt werden und die Ergebnisse werden dann kombiniert -- ⚠️ BEI LAGERBESTANDSABFRAGEN: Eine Abfrage mit JOINs ist OK, aber stelle sicher, dass die WHERE-Bedingung eine breite Suche über alle Identifikationsfelder enthält +⚠️⚠️⚠️ ABSOLUT KRITISCH - JSON-VALIDIERUNG - MUSS BEVOR DU DAS JSON ZURÜCKGIBST ⚠️⚠️⚠️ +DU MUSST DIESE SCHRITTE BEVOR DU DAS JSON ZURÜCKGIBST AUSFÜHREN: + +SCHRITT 1: Zähle die Anzahl der Queries im sqlQueries-Array +SCHRITT 2: Prüfe die Anzahl: + - Wenn needsDatabaseQuery = true UND weniger als 5 Queries: ERSTELLE SOFORT WEITERE QUERIES! + - Bei Zertifizierungen UND weniger als 8 Queries: ERSTELLE SOFORT WEITERE QUERIES! + - Wenn nur 1 Query im Array: DAS IST ABSOLUT FALSCH - ERSTELLE MINDESTENS 5-8 QUERIES! + - Wenn nur 2-3 Queries im Array: DAS IST ZU WENIG - ERSTELLE MINDESTENS 5-8 QUERIES! +SCHRITT 3: Prüfe, ob jede Query eine andere Strategie hat +SCHRITT 4: Wenn nicht genug Queries: ERSTELLE WEITERE QUERIES MIT ANDEREN STRATEGIEN! +SCHRITT 5: Wiederhole SCHRITT 1-4 bis du mindestens 5-8 (oder 8 bei Zertifizierungen) Queries hast +SCHRITT 6: ERST DANN gib das JSON zurück! + +⚠️⚠️⚠️ BEISPIEL FÜR KORREKTE ANZAHL VON QUERIES ⚠️⚠️⚠️ +Bei einer normalen Anfrage (z.B. "Netzgeräte mit 10A"): +- Query 1: Spezifische Suche mit allen Kriterien +- Query 2: Breitere Suche mit alternativen Begriffen +- Query 3: Suche nur nach Hauptkriterien +- Query 4: COUNT-Query für Statistik +- Query 5: Fallback-Query mit minimalen Filtern +→ MINDESTENS 5 Queries! + +Bei Zertifizierungen (z.B. "UL-zertifizierte Netzgeräte"): +- Query 1-5: Wie oben +- Query 6: UL-Suche in Keywords +- Query 7: Zertifizierte Netzgeräte allgemein +- Query 8: Fallback ohne Zertifizierungsfilter +→ MINDESTENS 8 Queries! Return ONLY valid JSON: {{ @@ -453,12 +634,22 @@ Return ONLY valid JSON: "sqlQueries": [ {{ "query": string (ready-to-execute SQL with double quotes for column names), - "purpose": string (description of what this query retrieves, e.g., "Get product information from Artikel table"), + "purpose": string (description of what this query retrieves), "table": string (primary table name, e.g., "Artikel", "Lagerplatz_Artikel") }} - ] (array of query objects, empty array if needsDatabaseQuery is false), + ] (array of query objects - MINDESTENS 5-8 bei komplexen Anfragen, MINDESTENS 8 bei Zertifizierungen!), "reasoning": string -}}""" +}} + +⚠️⚠️⚠️ FINALE PRÜFUNG BEVOR DU DAS JSON ZURÜCKGIBST ⚠️⚠️⚠️ +1. Zähle die Anzahl der Queries im sqlQueries-Array: [ANZAHL] +2. Prüfe: Ist [ANZAHL] >= 5? (Bei Zertifizierungen: >= 8?) +3. Wenn NEIN: ERSTELLE SOFORT WEITERE QUERIES! +4. Prüfe: Hat jede Query eine andere Strategie? +5. Wenn NEIN: ERSTELLE QUERIES MIT ANDEREN STRATEGIEN! +6. Wiederhole bis [ANZAHL] >= 5 (oder >= 8 bei Zertifizierungen) +7. ERST DANN gib das JSON zurück! +""" def get_query_needs_analysis_prompt( @@ -532,40 +723,47 @@ def get_empty_results_retry_instructions(empty_count: int) -> str: return f""" ⚠️⚠️⚠️ KRITISCH - LEERE ERGEBNISSE ERKANNT ⚠️⚠️⚠️ -Es wurden {empty_count} Query(s) ausgeführt, die 0 Zeilen zurückgegeben haben. Dies bedeutet, dass die bisherige Query-Strategie nicht erfolgreich war. +Es wurden {empty_count} Query(s) ausgeführt, die 0 Zeilen zurückgegeben haben. Die bisherige Query-Strategie war nicht erfolgreich. -DU MUSST JETZT EINE ALTERNATIVE QUERY-STRATEGIE VERSUCHEN! +DU MUSST JETZT MEHRERE ALTERNATIVE QUERY-STRATEGIEN VERSUCHEN! -Verfügbare Tabellen im System: -1. Artikel - Enthält alle Produktinformationen (I_ID, Artikelbezeichnung, Artikelnummer, etc.) -2. Einkaufspreis - Enthält Preisdaten (m_Artikel, EP_CHF) -3. Lagerplatz_Artikel - Enthält Lagerbestands- und Lagerplatzinformationen (R_ARTIKEL, R_LAGERPLATZ, Bestände, etc.) -4. Lagerplatz - Enthält die tatsächlichen Lagerplatznamen und -informationen (I_ID, Lagerplatz, R_LAGER, R_LAGERORT) +⚠️⚠️⚠️ OBLIGATORISCH - ERSTELLE MINDESTENS 5-8 ALTERNATIVE QUERIES ⚠️⚠️⚠️ -ALTERNATIVE STRATEGIEN ZUM AUSPROBIEREN: +Erstelle mehrere alternative SQL-Queries mit komplett anderen Strategien: -1. **Direkte Lagerplatz-Suche**: Prüfe zuerst, ob der Lagerplatzname in der Lagerplatz-Tabelle existiert: - SELECT * FROM Lagerplatz WHERE "Lagerplatz" LIKE '%[Suchbegriff]%' - -2. **Verschiedene Schreibweisen**: Versuche verschiedene Schreibweisen (Groß-/Kleinschreibung, Teilstrings): - - UPPER/LOWER Funktionen verwenden - - Verschiedene LIKE-Patterns: '%term%', 'term%', '%term' - -3. **JOIN-Strategie überprüfen**: Stelle sicher, dass R_LAGERPLATZ korrekt mit Lagerplatz.I_ID gejoint wird: - - R_LAGERPLATZ in Lagerplatz_Artikel enthält die ID (nicht den Namen!) - - Verwende: LEFT JOIN Lagerplatz lp ON l."R_LAGERPLATZ" = lp."I_ID" - -4. **Breitere Suche**: Versuche eine breitere Suche ohne exakte Filter: - - Entferne zu spezifische WHERE-Bedingungen - - Verwende OR-Bedingungen für verschiedene Suchvarianten - -5. **Andere Tabellen zuerst**: Versuche zuerst eine einfache Abfrage auf einer einzelnen Tabelle, dann JOINs: - - Starte mit Lagerplatz-Tabelle direkt - - Dann JOIN mit Lagerplatz_Artikel - - Dann JOIN mit Artikel +1. **Breitere Suche ohne Zertifizierung**: Entferne Zertifizierungsfilter komplett + - Beispiel: Suche nur nach Netzgerät + einphasig + 10A (ohne UL) + - Suche in Artikelbezeichnung, Artikelbeschrieb, Keywords -WICHTIG: Wenn alle bisherigen Queries 0 Zeilen zurückgegeben haben, MUSS eine alternative Query-Strategie versucht werden! -Erstelle eine neue Query, die eine der oben genannten Strategien verwendet. Versuche verschiedene Ansätze, bis Ergebnisse gefunden werden. +2. **Erweiterte Suche nach Netzgeräten mit Ampere-Angaben**: Breitere Ampere-Patterns + - Beispiel: (Netzteil OR Netzgerät) AND (10A OR 15A OR 20A OR Ampere) + - Suche auch nach "Ampere" als Begriff, nicht nur Zahlen + +3. **Breitere UL-Suche bei Netzgeräten**: Suche UL in allen Feldern + - Beispiel: (UL OR UL-zertifiziert) AND (Netzgerät OR Netzteil OR Power Supply) + - Suche auch in Keywords-Feld + +4. **Netzgeräte mit ≥10A ohne weitere Filter**: Minimaler Filter + - Beispiel: (Netzgerät OR Netzteil) AND (10A OR 15A OR 20A) + - Keine Filter auf einphasig oder Zertifizierung + +5. **Zertifizierte Netzgeräte allgemein**: Breite Zertifizierungs-Suche + - Beispiel: (UL OR CE OR TÜV OR certified OR zertifiziert) AND (Netzgerät OR Netzteil) + +6. **COUNT-Abfrage für Statistik**: Prüfe ob überhaupt Artikel existieren + - SELECT COUNT(*) WHERE (Netzgerät OR Netzteil) AND (10A OR 15A OR 20A) + +7. **Spezifische Suche nach einphasigen Netzgeräten**: Ohne Zertifizierung + - Beispiel: (einphasig OR 1-phasig OR single phase) AND (Netzgerät OR Netzteil) + +8. **Fallback mit minimalen Filtern**: Nur Hauptkriterien + - Beispiel: Netzgerät AND (10A OR 15A OR 20A) - keine weiteren Filter + +WICHTIG: +- Erstelle IMMER mehrere Queries (mindestens 5-8) mit unterschiedlichen Strategien +- Verwende breitere OR-Bedingungen für alternative Begriffe +- Entferne zu spezifische Filter, die möglicherweise keine Treffer finden +- Suche in Artikelbezeichnung, Artikelbeschrieb UND Keywords-Feld """ @@ -607,10 +805,18 @@ WICHTIGSTE REGELN - ABSOLUT VERBINDLICH: - Einkaufspreis: [Wert aus Datenbank oder "Nicht verfügbar"] Lagerbestände nach Lagerplätzen - [Tabelle mit ALLEN Lagerplätzen aus den Daten] + [Tabelle mit Lagerplätzen - ⚠️ WICHTIG: Nur Lagerplätze mit verfügbarem Bestand > 0 zeigen!] Lagerplatz | Ist-Bestand | Soll-Bestand | Min-Bestand | Max-Bestand | Reservierter Bestand | Verfügbarer Bestand - Gesamtbestand: [Summe aller Ist-Bestände] Stück (alle am Lagerplatz "[Lagerplatzname]") + ⚠️⚠️⚠️ KRITISCH - LAGERPLÄTZE MIT 0 BESTAND FILTERN ⚠️⚠️⚠️ + - STANDARDREGEL: Zeige NUR Lagerplätze mit verfügbarem Bestand > 0 + - FILTERE Lagerplätze mit S_IST_BESTAND = 0 oder verfügbarer Bestand = 0 AUS + - AUSNAHMEN - Zeige Lagerplätze mit 0 Bestand WENN: + * Der Nutzer explizit nach dem GESAMTLAGERBESTAND fragt + * Der Nutzer nach einem SPEZIFISCHEN LAGERPLATZ fragt + * Der Nutzer explizit nach "0 Bestand" oder "leeren Lagerplätzen" fragt + + Gesamtbestand: [Summe aller Ist-Bestände] Stück (nur Lagerplätze mit Bestand > 0, außer bei Ausnahmen) Möchten Sie: - Mehr technische Details zu diesem Artikel erfahren? @@ -618,8 +824,9 @@ WICHTIGSTE REGELN - ABSOLUT VERBINDLICH: - Informationen zu anderen Artikeln im Lager anzeigen? - Den aktuellen Preis oder Lieferzeiten prüfen? -3. STELLE SICHER, DASS ALLE LAGERPLÄTZE ANGEZEIGT WERDEN - - Wenn mehrere Lagerplätze vorhanden sind, zeige ALLE in der Tabelle +3. STELLE SICHER, DASS NUR RELEVANTE LAGERPLÄTZE ANGEZEIGT WERDEN + - Zeige NUR Lagerplätze mit verfügbarem Bestand > 0 (außer bei Ausnahmen) + - Wenn alle Lagerplätze 0 Bestand haben: Zeige entsprechende Nachricht statt leerer Tabelle - Gruppiere nicht - zeige jeden Lagerplatz als separate Zeile 4. VERWENDE NUR DIE TATSÄCHLICHEN WERTE @@ -810,26 +1017,36 @@ Antworte auf die folgende Frage des Nutzers: {user_prompt}{context} KRITISCH: Verwende NUR die oben angegebenen Daten. Erfinde KEINE Werte. Wenn Daten fehlen, schreibe "Nicht verfügbar". -WICHTIG - MEHRERE ABFRAGEN: -Die oben angegebenen DATENBANK-ERGEBNISSE können aus mehreren separaten Abfragen stammen. Jede Abfrage ist mit "=== Abfrage X ===" markiert und enthält Informationen zu einem spezifischen Aspekt (z.B. Artikel-Informationen, Lagerbestände, etc.). -- Kombiniere die Informationen aus ALLEN erfolgreichen Abfragen zu einer umfassenden Antwort -- Beispiel: Wenn Abfrage 1 Artikel-Informationen liefert und Abfrage 2 Lagerbestände liefert, kombiniere beide in deiner Antwort -- Verwende ALLE verfügbaren Informationen aus den verschiedenen Abfragen +⚠️⚠️⚠️ ABSOLUT KRITISCH - ALLE ARTIKEL ZURÜCKGEBEN ⚠️⚠️⚠️ +- ✓ OBLIGATORISCH: Du MUSST ALLE Artikel zurückgeben, die die Kriterien erfüllen +- ✓ OBLIGATORISCH: Kombiniere Ergebnisse aus ALLEN erfolgreichen Abfragen +- ✓ OBLIGATORISCH: Zähle ALLE Artikel in den DATENBANK-ERGEBNISSEN oben +- ✓ OBLIGATORISCH: Zeige ALLE gefundenen Artikel in deiner Antwort (bis zu 20 in der Tabelle) +- ❌ ABSOLUT VERBOTEN: Nur einen Artikel zurückgeben, wenn mehrere gefunden wurden +- ❌ ABSOLUT VERBOTEN: Nur den ersten Artikel zeigen +- ❌ ABSOLUT VERBOTEN: Artikel auslassen, die in den DATENBANK-ERGEBNISSEN stehen +- Beispiel: Wenn 10 Artikel in den DATENBANK-ERGEBNISSEN stehen, MUSST du alle 10 zeigen! + +⚠️⚠️⚠️ SCHRITT-FÜR-SCHRITT ANWEISUNG ⚠️⚠️⚠️ +BEVOR du deine Antwort schreibst: +1. Zähle ALLE Artikel in den DATENBANK-ERGEBNISSEN oben +2. Notiere diese Anzahl (z.B. "10 Artikel gefunden") +3. Stelle sicher, dass du ALLE diese Artikel in deiner Antwort zeigst +4. Wenn weniger als 20 Artikel: Zeige ALLE in einer Tabelle +5. Wenn mehr als 20 Artikel: Zeige die ersten 20 + Hinweis auf weitere + +⚠️⚠️⚠️ VALIDIERUNG BEVOR DU DIE ANTWORT ZURÜCKGIBST ⚠️⚠️⚠️ +- Prüfe: Zeige ich ALLE Artikel, die in den DATENBANK-ERGEBNISSEN stehen? +- Prüfe: Wenn 10 Artikel gefunden wurden, zeige ich auch 10 Artikel? +- Wenn NEIN: Füge die fehlenden Artikel hinzu! ⚠️⚠️⚠️ ABSOLUT VERBOTEN - KEINE DATEN ERFINDEN ⚠️⚠️⚠️ -Wenn KEINE Datenbank-Ergebnisse vorhanden sind, dann: -- ❌ ERFINDE KEINE Artikelnummern, Artikelbezeichnungen, Preise oder Lagerbestände! -- ❌ ERFINDE KEINE Beispielartikel! -- ✓ Schreibe stattdessen: "Es wurden keine Artikel in der Datenbank gefunden." oder "Die Datenbankabfrage ist fehlgeschlagen." - -WICHTIG: Deine Antwort soll NUR die finale Antwort enthalten - KEINE Planungsschritte, KEINE SQL-Queries, KEINE Zwischenschritte! -Beginne DIREKT mit "Aus der Datenbank habe ich..." (wenn Daten vorhanden) oder "Es wurden keine Artikel gefunden" (wenn keine Daten vorhanden). +- ❌ VERBOTEN: Artikelnummern, Preise, Bestände erfinden +- ✓ RICHTIG: Wenn keine Daten: "Es wurden keine Artikel gefunden" WICHTIG: +- Beginne DIREKT mit "Aus der Datenbank habe ich..." (keine Planungsschritte!) - Klare, strukturierte Antwort -- Zahlen mit Tausender-Trennzeichen (7'411) -- Markdown-Tabellen für Daten (max 20 Zeilen) -- Artikelnummern als Link: [ARTIKELNUMMER](/details/ARTIKELNUMMER) -- Wenn Excel-Datei erstellt wurde, bestätige dies explizit am Ende -- Wenn Excel-Verarbeitung fehlgeschlagen ist, erkläre dem Nutzer den Fehler klar""" +- Markdown-Tabellen (max 20 Zeilen) +- Artikelnummern als Link: [ARTIKELNUMMER](/details/ARTIKELNUMMER)""" diff --git a/modules/features/chatbot/mainChatbot.py b/modules/features/chatbot/mainChatbot.py index bc469a95..a00da09b 100644 --- a/modules/features/chatbot/mainChatbot.py +++ b/modules/features/chatbot/mainChatbot.py @@ -406,9 +406,19 @@ def _buildWebResearchQuery(userPrompt: str, workflowMessages: List, queryResults "weitere informationen", "further information", "additional information" ] + # Certification patterns that require web research + certification_patterns = [ + "ul", "ce", "tüv", "vde", "iec", "en", "iso", + "zertifiziert", "certified", "certification", "zertifizierung", + "geprüft", "approved", "compliance" + ] + # Check if current prompt contains search-related keywords has_search_intent = any(pattern in prompt_lower for pattern in search_patterns) + # Check if prompt contains certification-related keywords + has_certification_intent = any(pattern in prompt_lower for pattern in certification_patterns) + # Extract product information - try multiple sources article_number = None article_description = None @@ -562,6 +572,30 @@ def _buildWebResearchQuery(userPrompt: str, workflowMessages: List, queryResults if supplier: query_parts.append(supplier) + # Extract certification information from prompt if present + certification_terms = [] + if has_certification_intent: + # Extract specific certification mentions + cert_keywords = { + "ul": "UL certification", + "ce": "CE certification", + "tüv": "TÜV certification", + "vde": "VDE certification", + "iec": "IEC certification", + "iso": "ISO certification" + } + for cert_key, cert_term in cert_keywords.items(): + if cert_key in prompt_lower: + certification_terms.append(cert_term) + + # If no specific certification found but certification intent detected, add generic term + if not certification_terms: + certification_terms.append("certification") + + # Add certification terms to query if found + if certification_terms: + query_parts.extend(certification_terms) + # Add "Datenblatt" or "datasheet" if user requested it or if we have product info if "datenblatt" in prompt_lower or "datasheet" in prompt_lower or "fiche technique" in prompt_lower: query_parts.append("Datenblatt") @@ -873,6 +907,117 @@ async def _processChatbotMessage( }] reasoning = analysis.get("reasoning", "") + # Check if we need web research for certifications + user_prompt_lower = userInput.prompt.lower() + certification_keywords = ["ul", "ce", "tüv", "vde", "iec", "iso", "zertifiziert", "certified", "certification"] + has_certification = any(keyword in user_prompt_lower for keyword in certification_keywords) + if has_certification and not needsWebResearch: + logger.warning("Certification detected but needsWebResearch is false - forcing to true") + needsWebResearch = True + + # Validate query count - retry if too few queries (iterative retry up to 3 attempts) + min_queries_required = 8 if has_certification else 5 + max_retry_attempts = 3 + retry_attempt = 0 + + while needsDatabaseQuery and len(sql_queries) < min_queries_required and retry_attempt < max_retry_attempts: + retry_attempt += 1 + logger.warning(f"Only {len(sql_queries)} queries created, but {min_queries_required} required. Retry attempt {retry_attempt}/{max_retry_attempts}...") + await _emit_log_and_event( + interfaceDbChat, + workflowId, + event_manager, + f"Zu wenige Abfragen erstellt ({len(sql_queries)} statt {min_queries_required}). Versuch {retry_attempt}/{max_retry_attempts}: Erstelle alternative Strategien...", + log_type="warning" + ) + + # Build progressively stronger retry prompt + retry_context = f"{context}\n\n" + if retry_attempt == 1: + retry_context += "⚠️⚠️⚠️ KRITISCH - ZU WENIGE ABFRAGEN ERSTELLT ⚠️⚠️⚠️\n" + elif retry_attempt == 2: + retry_context += "⚠️⚠️⚠️ ABSOLUT KRITISCH - IMMER NOCH ZU WENIGE ABFRAGEN ⚠️⚠️⚠️\n" + else: + retry_context += "⚠️⚠️⚠️ LETZTER VERSUCH - DU MUSST JETZT MINDESTENS 5-8 ABFRAGEN ERSTELLEN ⚠️⚠️⚠️\n" + + retry_context += f"Du hast nur {len(sql_queries)} Abfrage(n) erstellt, aber es werden MINDESTENS {min_queries_required} benötigt!\n" + retry_context += f"Dies ist bereits Versuch {retry_attempt} von {max_retry_attempts}!\n" + retry_context += "ERSTELLE JETZT MINDESTENS 5-8 ABFRAGEN MIT VERSCHIEDENEN STRATEGIEN!\n" + retry_context += "JEDE Query muss eine ANDERE Strategie verfolgen:\n" + retry_context += "- Query 1: Spezifische Suche mit allen Kriterien\n" + retry_context += "- Query 2: Breitere Suche mit alternativen Begriffen\n" + retry_context += "- Query 3: Suche ohne Zertifizierungsfilter\n" + retry_context += "- Query 4: Suche nur nach Hauptkriterien\n" + retry_context += "- Query 5: COUNT-Query für Statistik\n" + if has_certification: + retry_context += "- Query 6: UL-Suche in Keywords\n" + retry_context += "- Query 7: Zertifizierte Netzgeräte allgemein\n" + retry_context += "- Query 8: Fallback mit minimalen Filtern\n" + retry_context += "\n⚠️ ABSOLUT VERBOTEN: Nur 1 Query zu erstellen! ⚠️\n" + retry_context += "⚠️ ABSOLUT VERBOTEN: Alle Queries mit derselben Strategie! ⚠️\n" + retry_context += "⚠️ BEVOR DU DAS JSON ZURÜCKGIBST: Zähle die Queries im sqlQueries-Array! ⚠️\n" + retry_context += "⚠️ WENN WENIGER ALS 8: ERSTELLE WEITERE QUERIES! ⚠️\n" + + retry_analysis_prompt = get_initial_analysis_prompt(userInput.prompt, retry_context) + retry_analysis_result = await method_ai.process({ + "aiPrompt": retry_analysis_prompt, + "documentList": None, + "resultType": "json", + "simpleMode": True + }) + + retry_analysis_content = None + if retry_analysis_result.success and retry_analysis_result.documents: + retry_analysis_content = retry_analysis_result.documents[0].documentData + if isinstance(retry_analysis_content, bytes): + retry_analysis_content = retry_analysis_content.decode('utf-8') + + if retry_analysis_content: + retry_analysis = _extractJsonFromResponse(retry_analysis_content) + if retry_analysis: + retry_needsDatabaseQuery = retry_analysis.get("needsDatabaseQuery", False) + retry_needsWebResearch = retry_analysis.get("needsWebResearch", False) + retry_sql_queries = retry_analysis.get("sqlQueries", []) + + logger.info(f"Retry attempt {retry_attempt}: Got {len(retry_sql_queries)} queries (required: {min_queries_required})") + + if retry_needsDatabaseQuery and len(retry_sql_queries) >= min_queries_required: + logger.info(f"Retry successful: {len(retry_sql_queries)} queries created") + needsDatabaseQuery = retry_needsDatabaseQuery + needsWebResearch = retry_needsWebResearch or needsWebResearch # Keep web research if already set + sql_queries = retry_sql_queries + reasoning = retry_analysis.get("reasoning", reasoning) + await _emit_log_and_event( + interfaceDbChat, + workflowId, + event_manager, + f"Alternative Strategie erfolgreich: {len(sql_queries)} Abfrage(n) erstellt", + log_type="info" + ) + break # Success, exit retry loop + else: + logger.warning(f"Retry attempt {retry_attempt} still insufficient: {len(retry_sql_queries)} queries (required: {min_queries_required})") + # Update sql_queries even if insufficient, so next iteration has context + if retry_needsDatabaseQuery and len(retry_sql_queries) > len(sql_queries): + sql_queries = retry_sql_queries + needsDatabaseQuery = retry_needsDatabaseQuery + needsWebResearch = retry_needsWebResearch or needsWebResearch + else: + logger.warning(f"Retry attempt {retry_attempt}: Failed to parse JSON response") + else: + logger.warning(f"Retry attempt {retry_attempt}: No content in response") + + # Final check: if still insufficient after all retries, log warning but continue + if needsDatabaseQuery and len(sql_queries) < min_queries_required: + logger.error(f"CRITICAL: After {max_retry_attempts} retry attempts, only {len(sql_queries)} queries created (required: {min_queries_required}). Continuing with insufficient queries.") + await _emit_log_and_event( + interfaceDbChat, + workflowId, + event_manager, + f"⚠️ WARNUNG: Nach {max_retry_attempts} Versuchen nur {len(sql_queries)} Abfrage(n) erstellt (benötigt: {min_queries_required}). Setze mit reduzierten Abfragen fort.", + log_type="warning" + ) + logger.info(f"Analysis: DB={needsDatabaseQuery}, Web={needsWebResearch}, SQL queries={len(sql_queries)}") # Build initial enriched web research query if needed (for logging, will be rebuilt after DB queries) @@ -964,6 +1109,208 @@ async def _processChatbotMessage( f"Warnung: {len(failed_queries)} Abfrage(n) fehlgeschlagen", log_type="warning" ) + + # Check if we got empty results and need to retry with alternative strategies + # Robust calculation: check all successful queries for empty data + total_rows = 0 + queries_with_results = 0 + queries_with_empty_results = 0 + + if successful_queries: + for query_key in successful_queries: + data_key = f"{query_key}_data" + if data_key in queryResults: + row_count = len(queryResults[data_key]) + total_rows += row_count + if row_count > 0: + queries_with_results += 1 + else: + queries_with_empty_results += 1 + else: + # Query succeeded but no data key - treat as empty + queries_with_empty_results += 1 + logger.debug(f"Query {query_key} succeeded but has no _data key") + else: + # No successful queries at all + logger.debug("No successful queries found") + + # Also check if we have any query results at all + has_any_results = total_rows > 0 + + # Debug logging + logger.info(f"Query results analysis: total_rows={total_rows}, successful_queries={len(successful_queries)}, " + f"queries_with_results={queries_with_results}, queries_with_empty_results={queries_with_empty_results}, " + f"failed_queries={len(failed_queries)}") + + # Trigger retry if: no results AND we have database queries AND we executed at least one query + # Also trigger if all successful queries returned empty results + should_retry = ( + not has_any_results and + needsDatabaseQuery and + len(sql_queries) > 0 and + (len(successful_queries) > 0 or len(failed_queries) == 0) # Either we have successful queries or no failures (queries executed but empty) + ) + + # Iterative retry loop: try up to 3 times with different strategies + max_empty_retry_attempts = 3 + empty_retry_attempt = 0 + original_sql_queries_count = len(sql_queries) + + while should_retry and empty_retry_attempt < max_empty_retry_attempts: + empty_retry_attempt += 1 + logger.info(f"No results found (attempt {empty_retry_attempt}/{max_empty_retry_attempts}), retrying with alternative query strategies...") + await _emit_log_and_event( + interfaceDbChat, + workflowId, + event_manager, + f"Keine Ergebnisse gefunden ({len(successful_queries)} erfolgreiche Abfrage(n), {total_rows} Zeilen). Versuch {empty_retry_attempt}/{max_empty_retry_attempts}: Versuche alternative Abfrage-Strategien...", + log_type="info" + ) + + # Retry analysis with empty results context - create NEW analysis with alternative strategies + from modules.features.chatbot.chatbotConstants import get_empty_results_retry_instructions + + # Build retry prompt with progressively different strategies + empty_count = len(sql_queries) + empty_results_instructions = get_empty_results_retry_instructions(empty_count) + + retry_context = f"{context}\n\n" + if empty_retry_attempt == 1: + retry_context += "⚠️⚠️⚠️ WICHTIG - ALTERNATIVE STRATEGIEN ERFORDERLICH ⚠️⚠️⚠️\n" + retry_context += "Strategie: Breitere Suche, weniger Filter\n" + elif empty_retry_attempt == 2: + retry_context += "⚠️⚠️⚠️ KRITISCH - IMMER NOCH KEINE ERGEBNISSE ⚠️⚠️⚠️\n" + retry_context += "Strategie: Entferne spezifische Filter komplett, verwende nur Hauptkriterien\n" + else: + retry_context += "⚠️⚠️⚠️ LETZTER VERSUCH - MINIMALE FILTER ⚠️⚠️⚠️\n" + retry_context += "Strategie: Nur Hauptbegriffe, keine spezifischen Filter\n" + + retry_context += f"Die bisherigen {len(sql_queries)} Abfragen haben 0 Zeilen zurückgegeben.\n" + retry_context += f"{empty_results_instructions}\n" + retry_context += f"Dies ist bereits Versuch {empty_retry_attempt} von {max_empty_retry_attempts}!\n" + retry_context += "Erstelle JETZT mehrere alternative SQL-Queries (mindestens 5-8) mit komplett anderen Strategien:\n" + + if empty_retry_attempt == 1: + retry_context += "- Breitere Suche ohne zu spezifische Filter\n" + retry_context += "- Suche ohne Zertifizierungsfilter (falls Zertifizierung nicht in DB)\n" + retry_context += "- Suche nur nach Hauptkriterien (z.B. nur Netzgerät + 10A, ohne einphasig)\n" + retry_context += "- Suche nach alternativen Begriffen (Netzteil statt Netzgerät, etc.)\n" + retry_context += "- COUNT-Queries für Statistik\n" + retry_context += "- Fallback-Queries mit minimalen Filtern\n" + elif empty_retry_attempt == 2: + retry_context += "- ENTFERNE alle Zertifizierungsfilter komplett\n" + retry_context += "- ENTFERNE Phasen-Filter (einphasig/dreiphasig)\n" + retry_context += "- Suche NUR nach: Netzgerät/Netzteil + Ampere-Angaben\n" + retry_context += "- Verwende breitere Ampere-Patterns (5A, 6A, 8A, 10A, 12A, 15A, 20A, etc.)\n" + retry_context += "- Suche auch in Keywords-Feld\n" + else: + retry_context += "- MINIMALE Filter: Nur 'Netzgerät' ODER 'Netzteil' ODER 'Power Supply'\n" + retry_context += "- KEINE spezifischen Filter auf Ampere, Phasen oder Zertifizierung\n" + retry_context += "- COUNT-Query: Wie viele Netzgeräte gibt es insgesamt?\n" + retry_context += "- Suche nach ALLEN verfügbaren Netzgeräten\n" + + retry_analysis_prompt = get_initial_analysis_prompt(userInput.prompt, retry_context) + + # AI call for retry analysis + retry_analysis_result = await method_ai.process({ + "aiPrompt": retry_analysis_prompt, + "documentList": None, + "resultType": "json", + "simpleMode": True + }) + + # Extract retry analysis + retry_analysis_content = None + if retry_analysis_result.success and retry_analysis_result.documents: + retry_analysis_content = retry_analysis_result.documents[0].documentData + if isinstance(retry_analysis_content, bytes): + retry_analysis_content = retry_analysis_content.decode('utf-8') + + if retry_analysis_content: + retry_analysis = _extractJsonFromResponse(retry_analysis_content) + if retry_analysis and retry_analysis.get("needsDatabaseQuery", False): + retry_sql_queries = retry_analysis.get("sqlQueries", []) + if retry_sql_queries: + logger.info(f"Executing {len(retry_sql_queries)} retry queries (attempt {empty_retry_attempt}) with alternative strategies...") + await _emit_log_and_event( + interfaceDbChat, + workflowId, + event_manager, + f"Führe {len(retry_sql_queries)} alternative Abfrage(n) mit anderen Strategien aus (Versuch {empty_retry_attempt})...", + log_type="info" + ) + + # Execute retry queries + try: + retry_results = await _execute_queries_parallel(retry_sql_queries) + + # Merge retry results into main results (renumber to continue sequence) + base_query_num = len(sql_queries) + for key, value in retry_results.items(): + if key.startswith("query_"): + # Extract query number from retry result + try: + query_num = int(key.split("_")[1]) + new_query_num = base_query_num + query_num + new_key = f"query_{new_query_num}" + + if not key.endswith("_data") and not key.endswith("_error"): + queryResults[new_key] = value + if f"{key}_data" in retry_results: + queryResults[f"{new_key}_data"] = retry_results[f"{key}_data"] + elif key.endswith("_error"): + queryResults[f"{new_key}_error"] = value + except (ValueError, IndexError): + # Fallback if parsing fails + new_key = f"query_{base_query_num + 1}" + if not key.endswith("_data") and not key.endswith("_error"): + queryResults[new_key] = value + + # Recalculate results after retry + retry_successful = [k for k in retry_results.keys() if k.startswith("query_") and not k.endswith("_error") and not k.endswith("_data")] + retry_rows = sum(len(retry_results.get(f"{k}_data", [])) for k in retry_successful) if retry_successful else 0 + + # Update successful_queries list to include retry results + successful_queries = [k for k in queryResults.keys() if k.startswith("query_") and not k.endswith("_error") and not k.endswith("_data")] + total_rows = sum(len(queryResults.get(f"{k}_data", [])) for k in successful_queries) + + logger.info(f"Retry attempt {empty_retry_attempt}: Found {retry_rows} rows from {len(retry_successful)} queries. Total: {total_rows} rows from {len(successful_queries)} queries") + + if retry_rows > 0: + # Success! Found results + await _emit_log_and_event( + interfaceDbChat, + workflowId, + event_manager, + f"Alternative Abfragen erfolgreich: {len(retry_successful)} Abfrage(n) mit {retry_rows} Ergebnis{'en' if retry_rows != 1 else ''} gefunden", + log_type="info" + ) + should_retry = False # Stop retry loop, we found results + break + else: + # Still no results, continue to next attempt + await _emit_log_and_event( + interfaceDbChat, + workflowId, + event_manager, + f"Versuch {empty_retry_attempt}: Immer noch keine Ergebnisse. Versuche nächste Strategie...", + log_type="warning" + ) + except Exception as retry_error: + logger.error(f"Error executing retry queries (attempt {empty_retry_attempt}): {retry_error}", exc_info=True) + # Continue to next attempt even on error + + # Check if we should continue retrying + if empty_retry_attempt >= max_empty_retry_attempts: + logger.warning(f"Reached maximum empty retry attempts ({max_empty_retry_attempts}), stopping retry loop") + await _emit_log_and_event( + interfaceDbChat, + workflowId, + event_manager, + f"⚠️ Maximale Anzahl Retry-Versuche ({max_empty_retry_attempts}) erreicht. Keine Ergebnisse gefunden.", + log_type="warning" + ) + should_retry = False except Exception as e: logger.error(f"Error executing parallel queries: {e}") queryResults["error"] = f"Error executing queries: {str(e)}" @@ -1088,6 +1435,35 @@ async def _processChatbotMessage( error_query_keys = [k for k in queryResults.keys() if k.endswith("_error")] has_only_errors = bool(error_query_keys and not successful_query_keys) + # Count total number of articles found across all queries + total_articles_found = 0 + if successful_query_keys: + for query_key in successful_query_keys: + data_key = f"{query_key}_data" + if data_key in queryResults: + article_count = len(queryResults[data_key]) + total_articles_found += article_count + logger.info(f"Query {query_key} returned {article_count} articles") + + logger.info(f"Total articles found across all queries: {total_articles_found}") + + # Add explicit article count information to prompt + if total_articles_found > 0: + article_count_info = f"\n\n⚠️⚠️⚠️ WICHTIG - ARTIKELANZAHL ⚠️⚠️⚠️\n" + article_count_info += f"In den DATENBANK-ERGEBNISSEN oben wurden INSGESAMT {total_articles_found} Artikel gefunden.\n" + article_count_info += f"DU MUSST ALLE {total_articles_found} Artikel in deiner Antwort zeigen!\n" + if total_articles_found <= 20: + article_count_info += f"Zeige ALLE {total_articles_found} Artikel in einer Tabelle.\n" + else: + article_count_info += f"Zeige die ersten 20 Artikel in einer Tabelle + Hinweis auf weitere {total_articles_found - 20} Artikel.\n" + article_count_info += f"❌ VERBOTEN: Nur einen Artikel zu zeigen, wenn {total_articles_found} gefunden wurden!\n" + article_count_info += f"✓ OBLIGATORISCH: Zeige ALLE {total_articles_found} Artikel!\n" + + if db_results_part: + db_results_part = article_count_info + db_results_part + else: + db_results_part = article_count_info + # Add warning messages if needed if not has_query_results and needsDatabaseQuery: if db_results_part: