fix: merged old code and fixed system prompt

This commit is contained in:
Ida Dittrich 2026-01-12 09:07:49 +01:00
parent b06375352e
commit d34d04a4e9
2 changed files with 175 additions and 56 deletions

View file

@ -113,27 +113,75 @@ SQL-HINWEISE:
- Verwende Tabellenaliase (a für Artikel, e für Einkaufspreis, l für Lagerplatz_Artikel, lp für Lagerplatz) für bessere Lesbarkeit
- WICHTIG: Du kannst bis zu 50 Ergebnisse pro Abfrage abrufen
ARTIKELKÜRZEL vs ARTIKELNUMMER - WICHTIG:
Es gibt zwei verschiedene Identifikatoren für Artikel:
ARTIKELKÜRZEL vs ARTIKELNUMMER vs ARTIKELBESCHRIEB/ARTIKELBEZEICHNUNG - WICHTIG:
Es gibt drei verschiedene Identifikatoren für Artikel:
1. **Artikelkürzel**: Numerisches Format (z.B. "131741", "141215")
1. **Artikelkürzel**: Numerisches Format (z.B. "131741", "141215", "167677")
- Besteht aus reinen Zahlen
- Format: Nur Ziffern, keine Buchstaben, keine Bindestriche, keine Leerzeichen
- Beispiel: "131741", "141215"
- Beispiel: "131741", "141215", "167677"
- SQL: WHERE a."Artikelkürzel" = '167677'
2. **Artikelnummer**: Alphanumerisches Format (z.B. "6AV2 181-8XP00-0AX0", "AX5206")
- Kann Buchstaben, Zahlen, Bindestriche und Leerzeichen enthalten
- Format: Alphanumerisch, kann Bindestriche und Leerzeichen enthalten
- Beispiel: "6AV2 181-8XP00-0AX0", "AX5206", "SIE.6ES7500"
- SQL: WHERE a."Artikelnummer" = '6AV2 181-8XP00-0AX0'
WICHTIG - RICHTIGE SPALTE VERWENDEN:
- Wenn der Nutzer eine rein numerische Zahl angibt (z.B. "131741", "141215") Suche in a."Artikelkürzel"
- Wenn der Nutzer eine alphanumerische Bezeichnung angibt mit Buchstaben, Bindestrichen oder Leerzeichen (z.B. "6AV2 181-8XP00-0AX0", "AX5206") Suche in a."Artikelnummer"
3. **Artikelbeschrieb/Artikelbezeichnung**: Textformat mit Wörtern (z.B. "1517H Bundle", "LED Lampe 12V")
- Enthält Wörter wie "Bundle", "Lampe", etc.
- Kann Zahlen, Buchstaben, Leerzeichen und Wörter enthalten
- Format: Text mit beschreibenden Wörtern
- Beispiel: "1517H Bundle", "LED Lampe 12V", "Kabel 5m"
- SQL: WHERE a."Artikelbeschrieb" LIKE '%1517H Bundle%' OR a."Artikelbezeichnung" LIKE '%1517H Bundle%'
KRITISCH - BREITE SUCHE BEI LAGERBESTANDSABFRAGEN
Bei Fragen nach Lagerbeständen ("wie viel haben wir auf lager", "wie viele auf lager", etc.) MUSS IMMER eine BREITE SUCHE über ALLE Identifikationsfelder durchgeführt werden:
**OBLIGATORISCH**: Verwende IMMER OR-Bedingungen mit LIKE-Patterns über mehrere Felder:
```sql
WHERE a."Artikelkürzel" LIKE '%[Suchbegriff]%'
OR a."Artikelnummer" LIKE '%[Suchbegriff]%'
OR a."Artikelbezeichnung" LIKE '%[Suchbegriff]%'
```
**WARUM**: Der Suchbegriff könnte in verschiedenen Feldern vorkommen:
- "1517H" könnte in Artikelkürzel, Artikelnummer ODER Artikelbezeichnung stehen
- "1517H Bundle" könnte in Artikelbezeichnung stehen, aber auch "1517H" allein in Artikelkürzel oder Artikelnummer
- Nutzer geben oft nur Teilinformationen an
**BEISPIEL FÜR KORREKTE ABFRAGE**:
User: "wie viel vom 1517H bundle haben wir auf lager?"
```sql
SELECT a."Artikelkürzel", a."Artikelnummer", a."Artikelbezeichnung", a."Lieferant",
e."EP_CHF", lp."Lagerplatz" as "Lagerplatzname", l."S_IST_BESTAND", l."S_SOLL_BESTAND", l."S_RESERVIERTER__BESTAND",
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"
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%'
OR a."Artikelnummer" LIKE '%1517H%'
OR a."Artikelbezeichnung" LIKE '%1517H%'
LIMIT 20
```
**WICHTIG**:
- Verwende IMMER LIKE mit Wildcards (%Suchbegriff%) für breite Suche
- Suche IMMER in allen drei Feldern (Artikelkürzel, Artikelnummer, Artikelbezeichnung) mit OR
- Bei eindeutigen numerischen Werten (z.B. "167677") kannst du auch = verwenden, aber OR mit LIKE ist sicherer
- Bei alphanumerischen Werten mit beschreibenden Wörtern (z.B. "1517H Bundle") IMMER LIKE mit OR über mehrere Felder
WICHTIG - RICHTIGE SPALTE VERWENDEN (für einfache, eindeutige Fälle):
- Wenn der Nutzer eine rein numerische Zahl angibt (z.B. "167677", "131741") Suche primär in a."Artikelkürzel", aber bei Lagerbestandsabfragen auch in anderen Feldern
- Wenn der Nutzer eine alphanumerische Bezeichnung angibt mit Buchstaben, Bindestrichen oder Leerzeichen, aber KEINE beschreibenden Wörter (z.B. "6AV2 181-8XP00-0AX0", "AX5206") Suche primär in a."Artikelnummer", aber bei Lagerbestandsabfragen auch in anderen Feldern
- Wenn der Nutzer eine Bezeichnung mit beschreibenden Wörtern angibt (z.B. "1517H Bundle", "LED Lampe", "Kabel 5m") Suche IMMER mit OR über mehrere Felder: a."Artikelbeschrieb" LIKE '%Suchbegriff%' OR a."Artikelbezeichnung" LIKE '%Suchbegriff%' OR a."Artikelnummer" LIKE '%Suchbegriff%' OR a."Artikelkürzel" LIKE '%Suchbegriff%'
Beispiele:
- "Wie viele von 141215 haben wir auf Lager?" Artikelkürzel "141215" WHERE a."Artikelkürzel" = '141215'
- "Wie viel von 6AV2 181-8XP00-0AX0 haben wir auf Lager?" Artikelnummer "6AV2 181-8XP00-0AX0" WHERE a."Artikelnummer" = '6AV2 181-8XP00-0AX0'
- "Zeig mir Informationen zu AX5206" Artikelnummer "AX5206" WHERE a."Artikelnummer" = 'AX5206'
- "Wie viele von 167677 haben wir auf Lager?" Breite Suche: WHERE a."Artikelkürzel" LIKE '%167677%' OR a."Artikelnummer" LIKE '%167677%' OR a."Artikelbezeichnung" LIKE '%167677%'
- "Wie viel vom 1517H Bundle haben wir auf Lager?" Breite Suche: WHERE a."Artikelkürzel" LIKE '%1517H%' OR a."Artikelnummer" LIKE '%1517H%' OR a."Artikelbezeichnung" LIKE '%1517H%'
- "Wie viel von 6AV2 181-8XP00-0AX0 haben wir auf Lager?" Breite Suche: WHERE a."Artikelkürzel" LIKE '%6AV2 181-8XP00-0AX0%' OR a."Artikelnummer" LIKE '%6AV2 181-8XP00-0AX0%' OR a."Artikelbezeichnung" LIKE '%6AV2 181-8XP00-0AX0%'
- "Zeig mir Informationen zu AX5206" Breite Suche: WHERE a."Artikelkürzel" LIKE '%AX5206%' OR a."Artikelnummer" LIKE '%AX5206%' OR a."Artikelbezeichnung" LIKE '%AX5206%'
Bei Fragen nach Lagerbestand: Kombiniere mit der Lagerplatz_Artikel Tabelle über JOIN und beachte die Anforderungen aus dem Abschnitt "LAGERBESTANDSABFRAGEN"
@ -385,14 +433,18 @@ WICHTIG für SQL-Abfragen:
- Bei Lagerplatzabfragen: IMMER JOIN mit Lagerplatz-Tabelle für den Namen
- 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
- Beispiel für "wie viel von 6AV2 181-8XP00-0AX0 haben wir auf lager":
* Abfrage 1: Artikel-Informationen (Artikelbezeichnung, Lieferant, etc.) aus Artikel-Tabelle
* Abfrage 2: Lagerbestände und Lagerplätze aus Lagerplatz_Artikel + Lagerplatz-Tabellen
- 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
Return ONLY valid JSON:
{{
@ -729,3 +781,55 @@ Rules:
else:
return "Chatbot Conversation"
def get_final_answer_prompt_with_results(
user_prompt: str,
context: str,
db_results_part: str,
web_results_part: str
) -> str:
"""
Get the complete prompt for generating the final answer with database and web results.
Args:
user_prompt: User's original prompt
context: Conversation context
db_results_part: Formatted database results section
web_results_part: Formatted web research results section
Returns:
Complete formatted prompt string
"""
system_prompt = get_final_answer_system_prompt()
return f"""{system_prompt}
Antworte auf die folgende Frage des Nutzers: {user_prompt}{context}
{db_results_part}{web_results_part}
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 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).
WICHTIG:
- 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"""

View file

@ -13,9 +13,10 @@ import asyncio
import re
from typing import Optional, Dict, Any, List
from modules.datamodels.datamodelChat import ChatWorkflow, UserInputRequest, WorkflowModeEnum, ChatLog
from modules.datamodels.datamodelChat import ChatWorkflow, UserInputRequest, WorkflowModeEnum, ChatLog, ChatDocument
from modules.datamodels.datamodelUam import User
from modules.datamodels.datamodelAi import AiCallRequest, AiCallOptions, OperationTypeEnum, ProcessingModeEnum
from modules.datamodels.datamodelDocref import DocumentReferenceList, DocumentItemReference
from modules.shared.timeUtils import getUtcTimestamp, parseTimestamp
from modules.services import getInterface as getServices
from modules.features.chatbot.eventManager import get_event_manager
@ -24,8 +25,10 @@ from modules.connectors.connectorPreprocessor import PreprocessorConnector
from modules.features.chatbot.chatbotConstants import (
get_initial_analysis_prompt,
generate_conversation_name,
get_final_answer_system_prompt
get_final_answer_system_prompt,
get_final_answer_prompt_with_results
)
import base64
logger = logging.getLogger(__name__)
@ -293,13 +296,12 @@ async def _emit_log_and_event(
round_number: Optional[int] = None
) -> None:
"""
Store log in database. The route's periodic chat data fetch will handle emitting it.
This avoids duplicate log emissions.
Store log in database and emit event for streaming.
Args:
interfaceDbChat: Database interface
workflowId: Workflow ID
event_manager: Event manager (unused, kept for compatibility)
event_manager: Event manager for streaming
message: Log message
log_type: Log type (info, warning, error)
status: Status string
@ -322,10 +324,37 @@ async def _emit_log_and_event(
"status": status,
"roundNumber": round_number
}
# Only store in database - route's periodic fetch will emit it
interfaceDbChat.createLog(log_data)
# Store log in database
created_log = interfaceDbChat.createLog(log_data)
# Emit event directly for streaming (using correct signature)
if created_log and event_manager:
try:
from modules.datamodels.datamodelChat import ChatLog
# Convert to dict if it's a Pydantic model
if hasattr(created_log, "model_dump"):
log_dict = created_log.model_dump()
elif hasattr(created_log, "dict"):
log_dict = created_log.dict()
else:
log_dict = log_data
await event_manager.emit_event(
context_id=workflowId,
event_type="chatdata",
data={
"type": "log",
"createdAt": log_timestamp,
"item": log_dict
},
event_category="chat",
message="New log",
step="log"
)
except Exception as emit_error:
logger.warning(f"Error emitting log event: {emit_error}")
except Exception as e:
logger.error(f"Error storing log: {e}")
logger.error(f"Error storing log: {e}", exc_info=True)
async def _check_workflow_stopped(interfaceDbChat, workflowId: str) -> bool:
@ -596,7 +625,6 @@ async def _convert_file_ids_to_document_references(
# Search database if not found in messages
if not document_id:
try:
from modules.datamodels.datamodelChat import ChatDocument
from modules.shared.databaseUtils import getRecordsetWithRBAC
documents = getRecordsetWithRBAC(
services.interfaceDbChat.db,
@ -1060,44 +1088,30 @@ 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)
# Add warning messages if needed
if not has_query_results and needsDatabaseQuery:
db_results_part = "\n\nWICHTIG: Es wurden KEINE Datenbank-Ergebnisse gefunden. Die Datenbankabfrage wurde nicht ausgeführt oder hat keine Ergebnisse zurückgegeben."
if db_results_part:
db_results_part += "\n\nWICHTIG: Es wurden KEINE Datenbank-Ergebnisse gefunden. Die Datenbankabfrage wurde nicht ausgeführt oder hat keine Ergebnisse zurückgegeben."
else:
db_results_part = "\n\nWICHTIG: Es wurden KEINE Datenbank-Ergebnisse gefunden. Die Datenbankabfrage wurde nicht ausgeführt oder hat keine Ergebnisse zurückgegeben."
if has_only_errors:
db_results_part += "\n\n⚠️⚠️⚠️ KRITISCH - ALLE QUERIES FEHLGESCHLAGEN ⚠️⚠️⚠️\n" + \
"ALLE Datenbankabfragen sind fehlgeschlagen. Es gibt KEINE gültigen Daten aus der Datenbank.\n" + \
"DU DARFST KEINE DATEN ERFINDEN! Schreibe stattdessen: 'Es wurden keine Artikel gefunden' oder 'Die Datenbankabfrage ist fehlgeschlagen'."
if db_results_part:
db_results_part += "\n\n⚠️⚠️⚠️ KRITISCH - ALLE QUERIES FEHLGESCHLAGEN ⚠️⚠️⚠️\n" + \
"ALLE Datenbankabfragen sind fehlgeschlagen. Es gibt KEINE gültigen Daten aus der Datenbank.\n" + \
"DU DARFST KEINE DATEN ERFINDEN! Schreibe stattdessen: 'Es wurden keine Artikel gefunden' oder 'Die Datenbankabfrage ist fehlgeschlagen'."
else:
db_results_part = "\n\n⚠️⚠️⚠️ KRITISCH - ALLE QUERIES FEHLGESCHLAGEN ⚠️⚠️⚠️\n" + \
"ALLE Datenbankabfragen sind fehlgeschlagen. Es gibt KEINE gültigen Daten aus der Datenbank.\n" + \
"DU DARFST KEINE DATEN ERFINDEN! Schreibe stattdessen: 'Es wurden keine Artikel gefunden' oder 'Die Datenbankabfrage ist fehlgeschlagen'."
answer_prompt = f"""{system_prompt}
Antworte auf die folgende Frage des Nutzers: {userInput.prompt}{context}
{db_results_part}{web_results_part}
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 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)."""
WICHTIG:
- 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"""
# Use the function from constants file to build the prompt
answer_prompt = get_final_answer_prompt_with_results(
userInput.prompt,
context,
db_results_part,
web_results_part
)
answerRequest = AiCallRequest(
prompt=answer_prompt,
@ -1128,6 +1142,7 @@ WICHTIG:
return
# Create assistant message with final answer
message_id = f"msg_{uuid.uuid4()}"
assistantMessageData = {
"id": message_id,
"workflowId": workflowId,