fix:added more logic to system prompt and added retry logic

This commit is contained in:
Ida Dittrich 2026-01-12 15:42:16 +01:00
parent 9042b8daf8
commit 583525a151
2 changed files with 743 additions and 150 deletions

View file

@ -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)"""

View file

@ -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: