From 975652f28df7e4a4fcd685d86e89ef20a6ec3cf2 Mon Sep 17 00:00:00 2001 From: Thomas Dannenberg Date: Wed, 22 Oct 2025 10:37:59 +0000 Subject: [PATCH] Kapitel 18/Premium Rohtext.md aktualisiert --- Kapitel 18/Premium Rohtext.md | 594 ++++++++++++++++++++++++++++++++-- 1 file changed, 576 insertions(+), 18 deletions(-) diff --git a/Kapitel 18/Premium Rohtext.md b/Kapitel 18/Premium Rohtext.md index e6a47f1..81b60a3 100644 --- a/Kapitel 18/Premium Rohtext.md +++ b/Kapitel 18/Premium Rohtext.md @@ -336,27 +336,585 @@ sodass Elyra keine weiteren Nachrichten beantwortet, bis erneut ein gültiger Tr 👉 **Screenshot geeignet:** WhatsApp-Chat mit Elyra, die freundlich auf eine Zuschauerfrage reagiert und danach automatisch inaktiv wird. -### Technische Grundlage +#### Node 1 – Webhook (Eingang: WhatsApp-Nachricht) -Die Logik besteht aus einer kleinen n8n-Kette mit klar definierten Knoten: +**Zweck:** Empfängt eingehende WhatsApp-Nachrichten, die über WAHA an diesen Workflow weitergeleitet werden. -| Node | Aufgabe | -|------|----------| -| **Webhook (Trigger)** | empfängt eingehende WhatsApp-Nachrichten über WAHA | -| **IF (Bedingung)** | prüft, ob die Nachricht „Ich brauche deine Hilfe, Elyra“ enthält | -| **KI-Node (OpenAI / Ollama)** | verarbeitet den Text und erstellt eine Antwort | -| **HTTP Request → WAHA** | sendet die Antwort an den Chat | -| **Wait / Timeout-Node** | beendet nach 10 Minuten Inaktivität | -| **IF („Danke, Elyra“) → Stop** | erkennt das manuelle Opt-Out und stoppt sofort | +**Node-Typ:** Webhook +**Name:** Elyra – Nachrichteneingang -Elyra interagiert damit ausschließlich auf Anfrage, -arbeitet ressourcenschonend und wahrt alle datenschutzrelevanten Grenzen. +**Parameter:** +- **HTTP Method:** `POST` +- **Path:** `/elyra/inbound` +- **Response Mode:** `On Received` +- **Response Code:** `200` +- **Response Data:** `No Response Body` -> [!IMPORTANT] -> Elyra darf **niemals eigenständig** Nachrichten senden oder neue Chats starten. -> Jede Kommunikation muss eindeutig vom Nutzer ausgehen. -> Nur so bleibt die Nutzung im Einklang mit den WhatsApp-AGB. +> [!NOTE] +> Die Webhook-URL wird nach dem Speichern automatisch angezeigt, z. B.: +> `https://n8n.deinedomain.tld/webhook/elyra/inbound` +> +> Diese Adresse muss im WAHA-Backend als Ziel für eingehende Nachrichten konfiguriert werden – +> üblicherweise per Weiterleitung oder Trigger-Konfiguration in deinem WhatsApp-Bot-Modul. -Mit dieser Implementierung erhält dein UCC eine intelligente, aber kontrollierte Interaktionsebene, -die sinnvoll zwischen Automatisierung und Nutzerhoheit vermittelt. +👉 **Screenshot geeignet:** Webhook-Node mit sichtbarem Pfad `/webhook/elyra/inbound` + +#### Node 2 – Function (chatId extrahieren + vorbereiten) + +**Zweck:** Extrahiert die `chatId` aus der eingehenden Nachricht und speichert sie zur späteren Wiederverwendung. +Außerdem werden alle empfangenen Daten in einheitlicher Struktur für den weiteren Verlauf vorbereitet. + +**Node-Typ:** Function +**Name:** Elyra – Nachricht vorbereiten + +```js +const body = items[0].json || {}; + +const chatId = body.chatId || 'unbekannt'; +const text = (body.text || '').trim().toLowerCase(); +const timestamp = body.timestamp || new Date().toISOString(); + +// Global für spätere Prüfung speichern +$input.item.json.chatId = chatId; +$input.item.json.text = text; +$input.item.json.timestamp = timestamp; + +return [$input.item]; +``` + +> [!TIP] +> WAHA überträgt bei eingehenden Nachrichten u. a. `chatId`, `text` und `timestamp`. +> Diese Felder werden hier bewusst standardisiert, damit nachfolgende Nodes keine Sonderfälle behandeln müssen. + +👉 **Screenshot geeignet:** Function-Node mit geöffnetem Code, markierte Zeile `const chatId = ...` + +#### Node 3 – IF (Trigger: Elyra deaktivieren?) + +**Zweck:** Erkennt, ob der Nutzer Elyra gezielt deaktivieren will – z. B. durch die Nachricht „Danke, Elyra“. + +**Node-Typ:** IF +**Name:** Elyra – Deaktivierung erkennen + +**Bedingung:** +- **Mode:** `All` +- **Type:** `String` +- **Value 1:** `{{$json["text"]}}` +- **Operation:** `Contains` +- **Value 2:** `danke, elyra` + +> [!TIP] +> Du kannst weitere Formulierungen ergänzen – z. B. `tschüss elyra`, `ich bin fertig`, etc. +> Aktuell genügt ein einzelner präziser Begriff zur Deaktivierung. + +👉 **Screenshot geeignet:** IF-Node mit Bedingung `text contains "danke, elyra"` + +**Anschlusslogik:** +- **TRUE →** weiter mit Node 4 – *Elyra – Session deaktivieren* +- **FALSE →** weiter mit Node 5 – *Elyra – Aktivierung erkennen* + +#### Node 4 – Set (Elyra-Session deaktivieren) + +**Zweck:** Setzt den Aktivierungsstatus der Elyra-Session für diesen Chat auf „inaktiv“. + +**Node-Typ:** Set +**Name:** Elyra – Session deaktivieren + +**Operation:** +- **Add Field:** `elyra_active` → `false` + +> [!NOTE] +> Die Variable `elyra_active` wird später mit der `chatId` kombiniert, +> um eine **sitzungsspezifische Statusvariable** zu erzeugen, z. B.: +> `elyra_active_491751234567@c.us` + +👉 **Screenshot geeignet:** Set-Node mit Feld `elyra_active = false` + +#### Node 5 – IF (Trigger: Elyra aktivieren?) + +**Zweck:** Erkennt, ob Elyra durch den Nutzer aktiviert werden soll – z. B. mit „Ich brauche deine Hilfe, Elyra“. + +**Node-Typ:** IF +**Name:** Elyra – Aktivierung erkennen + +**Bedingung:** +- **Mode:** `All` +- **Type:** `String` +- **Value 1:** `{{$json["text"]}}` +- **Operation:** `Contains` +- **Value 2:** `ich brauche deine hilfe, elyra` + +> [!TIP] +> Achte auf die Kleinschreibung, wenn du den Text vorher mit `.toLowerCase()` vereinheitlichst (siehe Node 2). +> Weitere Aktivierungsphrasen kannst du über zusätzliche OR-Verzweigungen ergänzen. + +👉 **Screenshot geeignet:** IF-Node mit Bedingung `text contains "ich brauche deine hilfe, elyra"` + +**Anschlusslogik:** +- **TRUE →** weiter mit Node 6 – *Sessionzähler prüfen* +- **FALSE →** weiter mit Node 9 – *Elyra – Aktivstatus prüfen* + +#### Node 6 – Function (Sessionzähler prüfen) + +**Zweck:** Zählt die aktuell aktiven Elyra-Sessions anhand der globalen Variablen +und entscheidet, ob eine weitere Session zulässig ist (Maximalgrenze: 10). + +**Node-Typ:** Function +**Name:** Elyra – Sessionzähler prüfen + +```js +// Aktuelle Liste aller aktiven Sessions abrufen +const allKeys = Object.keys($global); +const activeSessions = allKeys.filter(k => k.startsWith('elyra_active_') && $global.get(k) === true); + +// Ergebnis als Flag setzen +item.sessionCount = activeSessions.length; +item.sessionAllowed = activeSessions.length < 10; + +return [item]; +``` + +> [!NOTE] +> Jede aktive Elyra-Session wird im weiteren Verlauf als globale Variable gespeichert, z. B.: +> `elyra_active_491751234567@c.us = true` +> +> Diese Function zählt exakt, wie viele davon derzeit auf `true` stehen. + +👉 **Screenshot geeignet:** Function-Node mit markierter Zeile `item.sessionAllowed = ...` + +**Anschlusslogik:** +- **→** weiter mit Node 7 – *Aktivierung zulässig?* (IF) + +#### Node 7 – IF (Aktivierung zulässig?) + +**Zweck:** Prüft, ob die Elyra-Aktivierung für diesen Nutzer erlaubt ist (d. h. weniger als 10 aktive Sessions). + +**Node-Typ:** IF +**Name:** Elyra – Aktivierung zulässig? + +**Bedingung:** +- **Mode:** `All` +- **Type:** `Boolean` +- **Value 1:** `{{$json["sessionAllowed"]}}` +- **Operation:** `Is true` + +👉 **Screenshot geeignet:** IF-Node mit Bedingung `sessionAllowed is true` + +**Anschlusslogik:** +- **TRUE →** weiter mit Node 8 – *Elyra – Session aktivieren* +- **FALSE →** weiter mit Node 9 – *Elyra – Aktivstatus prüfen* + +#### Node 8 – Set (Elyra – Session aktivieren) + +**Zweck:** Aktiviert Elyra für diesen Nutzer, indem eine globale Sitzungsvariable gesetzt wird. + +**Node-Typ:** Set +**Name:** Elyra – Session aktivieren + +**Operation:** +- **Add Field:** + - `elyra_active` → `true` + +> [!NOTE] +> Die eigentliche Speicherung erfolgt erst im nächsten Schritt durch Kombination mit der `chatId`. +> Dieses Feld dient als Zwischenschritt für die globale Speicherung mit Namen wie: +> `elyra_active_491751234567@c.us` + +👉 **Screenshot geeignet:** Set-Node mit Feld `elyra_active = true` + +**Anschlusslogik:** +- **→** weiter mit Node 9 – *Elyra – Aktivstatus prüfen* + + +#### Node 9 – IF (Elyra ist aktiv?) + +**Zweck:** Prüft, ob für diesen Chat eine Elyra-Session aktiv ist. + +**Node-Typ:** IF +**Name:** Elyra – Aktivstatus prüfen + +**Bedingung:** +- **Mode:** `All` +- **Type:** `Expression` +- **Value 1:** + ```js + {{ + const key = 'elyra_active_' + $json["chatId"]; + return $global.get(key) === true; + }} + ``` +- **Operation:** `Is true` + +> [!TIP] +> Diese Prüfung stellt sicher, dass Elyra nur antwortet, wenn eine Sitzung explizit aktiv ist. +> Ohne vorherige Aktivierung wird der Nutzer ignoriert – oder optional weitergeleitet. + +👉 **Screenshot geeignet:** IF-Node mit geöffneter Expression und `return $global.get(...) === true` + +**Anschlusslogik:** +- **TRUE →** weiter mit Node 10 – *Thema klassifizieren* +- **FALSE →** weiter mit Node 16 – *Antwort: Elyra ist nicht aktiv* + +#### Node 10 – HTTP Request (Themenklassifizierung über Ollama) + +**Zweck:** Übergibt die empfangene WhatsApp-Nachricht an das lokale Mistral-Modell in Ollama, +um eine präzise Themenklassifizierung zu erhalten – z. B. `streamzeit`, `eventinfo`, `zeiten`, `adminanfrage`, `chatbefehl` oder `nicht zulässig`. + +**Node-Typ:** HTTP Request +**Name:** Elyra – Thema klassifizieren (Ollama) + +**Parameter:** +- **HTTP Method:** `POST` +- **URL:** `http://ollama.deinedomain.tld:11434/api/chat` +- **Headers:** + - `Content-Type`: `application/json` +- **Body Content Type:** `JSON` +- **Body Parameters:** + ```json + { + "model": "mistral", + "messages": [ + { + "role": "system", + "content": "Du bist ein Klassifizierungsagent für WhatsApp-Nachrichten. Ordne die folgende Nachricht exakt einem der folgenden Themen zu:\n\n- streamzeit\n- eventinfo\n- zeiten\n- adminanfrage\n- chatbefehl\n\nWenn keine eindeutige Zuordnung möglich ist oder der Inhalt nicht erlaubt ist, antworte exakt mit: nicht zulässig." + }, + { + "role": "user", + "content": "={{$json[\"text\"]}}" + } + ], + "stream": false + } + ``` + +> [!NOTE] +> Die Antwort muss **exakt** einem der oben genannten Schlüsselwörter entsprechen. +> Dadurch kannst du im nächsten Schritt zuverlässig prüfen, ob Elyra antworten darf – auch bei hoher Auslastung. + +👉 **Screenshot geeignet:** HTTP-Request-Node mit geöffnetem Body, markierter Promptabschnitt `"Ordne die folgende Nachricht ..."` + +**Anschlusslogik:** +- **→** weiter mit Node 11 – *Zugriffsprüfung: Thema + Sessionauslastung* + +#### Node 11 – Function (Zugriffsprüfung: Thema + Sessionauslastung) + +**Zweck:** Entscheidet, ob die Anfrage trotz möglicher Überlast bearbeitet werden darf. +Bevorzugte Themen wie `streamzeit`, `eventinfo` und `zeiten` dürfen auch bei Volllast durch – +alles andere wird bei Erreichen der Kapazitätsgrenze blockiert. + +**Node-Typ:** Function +**Name:** Elyra – Zugriff prüfen + +```js +const thema = $json["message"]?.content?.toLowerCase() || "nicht zulässig"; +const chatId = $json["chatId"]; + +// Speichern für spätere Entscheidung +item.thema = thema; +item.chatId = chatId; + +// Bevorzugte Themen +const priorisiert = ["streamzeit", "eventinfo", "zeiten"]; + +// Aktive Sessions zählen +const allKeys = Object.keys($global); +const aktive = allKeys.filter(k => k.startsWith('elyra_active_') && $global.get(k) === true); +const sessionCount = aktive.length; + +// Entscheidung +item.sessionCount = sessionCount; +item.zugriffErlaubt = sessionCount < 10 || priorisiert.includes(thema); +item.zugriffVerweigert = !item.zugriffErlaubt || thema === "nicht zulässig"; + +return [item]; +``` + +> [!TIP] +> Durch diese Logik reagiert Elyra auch bei starker Auslastung noch auf einfache Standardfragen. +> Nur komplexe oder nicht erlaubte Inhalte werden in dem Fall geblockt oder verzögert. + +👉 **Screenshot geeignet:** Function-Node mit sichtbarer Logik `zugriffErlaubt = sessionCount < 10 || …` + +**Anschlusslogik:** +- **TRUE →** weiter mit Node 12 – *Antwort generieren (Ollama)* +- **FALSE →** weiter mit Node 16 – *Antwort: Kapazitätsgrenze oder Thema abgelehnt* + +#### Node 12 – Set (Kontext für Themenverzweigung vorbereiten) + +**Zweck:** Bereitet die vom Klassifizierungsmodell gelieferte Themenkategorie und alle relevanten Kontextdaten für die nachfolgende Verzweigung vor. +Dieser Node bündelt alle Variablen, die für die spätere Entscheidung im Switch-Node benötigt werden. + +**Node-Typ:** Set +**Name:** Elyra – Kontext vorbereiten + +**Felder hinzufügen:** +- `thema` → `={{$json["message"]["content"]}}` +- `chatId` → `={{$json["chatId"]}}` +- `text` → `={{$json["text"]}}` +- `timestamp` → `={{$json["timestamp"]}}` + +> [!NOTE] +> Die Variable `thema` stammt direkt aus der Klassifizierungsantwort von Mistral (Node 10). +> Diese wird im nächsten Node verwendet, um über einen `Switch`-Node gezielt auf das Thema zu reagieren. + +👉 **Screenshot geeignet:** Set-Node mit Feldern `thema`, `chatId`, `text`, `timestamp` + +**Anschlusslogik:** +- **→** weiter mit Node 13 – *Switch: Thema auswählen* + +#### Node 13 – Switch (Thema auswählen und Pfad verzweigen) + +**Zweck:** Leitet die Verarbeitung abhängig von der klassifizierten Themenkategorie an den passenden Ablauf weiter – z. B. Datenabruf, Rechteprüfung oder feste Antwort. + +**Node-Typ:** Switch +**Name:** Elyra – Thema verzweigen + +**Bedingungstyp:** `String` +**Vergleichswert:** `={{$json["thema"]}}` + +**Cases:** +- **streamzeit** → weiter mit Node 14 – *Twitch-Kalender abrufen* +- **eventinfo** → weiter mit Node 15 – *Eventtext setzen* +- **zeiten** → weiter mit Node 16 – *Sendezeit abrufen* +- **chatbefehl** → weiter mit Node 17 – *Standardantwort setzen* +- **adminanfrage** → weiter mit Node 18 – *Adminrechte prüfen* +- **nicht zulässig** → weiter mit Node 19 – *Antwort: Thema nicht erlaubt* + +> [!TIP] +> Der Switch kann später beliebig erweitert werden, z. B. für `netdatainfo`, `feedback`, etc. +> Die Themenbezeichnungen müssen exakt zu denen aus dem Klassifizierungs-Prompt (Node 10) passen. + +👉 **Screenshot geeignet:** Switch-Node mit geöffneten Case‑Bedingungen `streamzeit`, `adminanfrage`, etc. + +**Anschlusslogik:** +- Je nach Thema → zu Node 14–19 (jeweils themenspezifisch) + +#### Node 14 – Execute Sub‑workflow (Twitch-Kalender abfragen) + +**Zweck:** Führt einen ausgelagerten Workflow aus, der den nächsten geplanten Streamtermin über die Twitch-API abruft +und die Antwort in strukturierter Form (z. B. als `streaminfo`) zurückliefert. + +**Node-Typ:** Execute Sub-workflow +**Name:** Elyra – Nächsten Stream abrufen + +**Parameter:** +- **Workflow:** `elyra_next_stream` *(wird als JSON-Download bereitgestellt)* +- **Options → Wait for Completion:** `true` +- **Options → Variables:** + - `chatId`: `={{$json["chatId"]}}` + - `text`: `={{$json["text"]}}` + - `timestamp`: `={{$json["timestamp"]}}` + +> [!NOTE] +> Dieser Sub‑Workflow wird nicht im Tutorial dokumentiert, sondern separat als JSON-Datei bereitgestellt. +> Er ruft die Twitch-API auf, extrahiert den nächsten geplanten Stream +> und speichert die Information im Feld `streaminfo` für die spätere Antwortgenerierung. + +👉 **Screenshot geeignet:** Execute Sub-workflow Node mit gewähltem Workflow `elyra_next_stream` und aktiver Option `Wait for Completion` + +**Anschlusslogik:** +- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)* + +#### Node 15 – Execute Sub‑workflow (Event-Info abrufen) + +**Zweck:** Führt einen externen Workflow aus, der die Beschreibung des nächsten geplanten Events liefert – +z. B. aus einer Kalenderquelle, Textdatei oder Datenbank. + +**Node-Typ:** Execute Sub-workflow +**Name:** Elyra – Eventinfo abrufen + +**Parameter:** +- **Workflow:** `elyra_eventinfo` *(bereitgestellt als JSON-Download)* +- **Options → Wait for Completion:** `true` +- **Options → Variables:** + - `chatId`: `={{$json["chatId"]}}` + - `text`: `={{$json["text"]}}` + - `timestamp`: `={{$json["timestamp"]}}` + +> [!NOTE] +> Auch dieser Workflow wird **nicht im Tutorial beschrieben**, sondern als JSON-Datei zum Download bereitgestellt. +> Er liefert z. B. das Feld `eventinfo` zurück, das später in den Antwort-Prompt eingebaut werden kann. + +👉 **Screenshot geeignet:** Execute Sub-workflow mit Workflow-Namen `elyra_eventinfo` und gesetzten Parametern + +**Anschlusslogik:** +- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)* + +#### Node 16 – Execute Sub‑workflow (Sendezeit abrufen) + +**Zweck:** Führt einen ausgelagerten Workflow aus, der die aktuelle oder nächste Sendezeit ermittelt – +z. B. über eine ICS-Datei, einen festgelegten Wochenplan oder eine API-basierte Quelle. + +**Node-Typ:** Execute Sub-workflow +**Name:** Elyra – Sendezeit abrufen + +**Parameter:** +- **Workflow:** `elyra_zeiten` *(bereitgestellt als JSON-Download)* +- **Options → Wait for Completion:** `true` +- **Options → Variables:** + - `chatId`: `={{$json["chatId"]}}` + - `text`: `={{$json["text"]}}` + - `timestamp`: `={{$json["timestamp"]}}` + +> [!NOTE] +> Der Workflow `elyra_zeiten` wird separat als JSON bereitgestellt +> und extrahiert z. B. aus einem Kalender die geplante Startzeit des Streams. +> Die Ausgabe erfolgt idealerweise als String im Feld `sendezeit`. + +👉 **Screenshot geeignet:** Execute Sub-workflow mit Workflow `elyra_zeiten` und gesetzten Übergabeparametern + +**Anschlusslogik:** +- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)* + +#### Node 17 – Execute Sub‑workflow (Chatbefehle aus StreamElements abrufen) + +**Zweck:** Führt einen ausgelagerten Workflow aus, der die öffentlich zugängliche StreamElements‑Commands‑Seite abruft, +alle verfügbaren Chatbefehle extrahiert und als JSON-String bereitstellt. + +**Node-Typ:** Execute Sub-workflow +**Name:** Elyra – Chatbefehle laden + +**Parameter:** +- **Workflow:** `elyra_chatcommands` *(bereitgestellt als JSON-Download)* +- **Options → Wait for Completion:** `true` +- **Options → Variables:** + - `chatId`: `={{$json["chatId"]}}` + - `text`: `={{$json["text"]}}` + +> [!NOTE] +> Dieser Sub‑Workflow ruft z. B. `https://streamelements.com/bratonien_tv/commands` auf, +> analysiert das HTML und extrahiert die Befehle samt Beschreibung als JSON‑Objekt. +> Das Ergebnis wird später an Ollama übergeben, damit Elyra kontextbezogen passende Befehle vorschlagen kann. +> +> Auch andere Anbieter wie **Nightbot**, **Moobot** oder **Botisimo** stellen vergleichbare Listen zur Verfügung – +> im Rahmen dieses Tutorials verwenden wir jedoch ausschließlich **StreamElements** als zentrale Bot-Instanz. + +👉 **Screenshot geeignet:** Execute Sub-workflow mit Workflow-Namen `elyra_chatcommands` + +**Anschlusslogik:** +- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)* + +#### Node 18 – Execute Sub‑workflow (Adminrechte prüfen) + +**Zweck:** Führt einen externen Workflow aus, der prüft, ob der aktuelle Absender (`chatId`) zu den autorisierten Admins gehört. +Nur wenn das zutrifft, wird die Anfrage weiterverarbeitet – sonst erhält der Nutzer eine entsprechende Ablehnung. + +**Node-Typ:** Execute Sub-workflow +**Name:** Elyra – Adminrechte prüfen + +**Parameter:** +- **Workflow:** `elyra_admincheck` *(bereitgestellt als JSON-Download)* +- **Options → Wait for Completion:** `true` +- **Options → Variables:** + - `chatId`: `={{$json["chatId"]}}` + - `text`: `={{$json["text"]}}` + +> [!NOTE] +> Der Sub-Workflow `elyra_admincheck` vergleicht die `chatId` mit einer internen Liste autorisierter Nutzer +> (z. B. als JSON-Datei, Umgebungsvariable oder Datenbankeintrag). +> Ist der Nutzer nicht autorisiert, wird das Feld `admin_erlaubt` auf `false` gesetzt und die Anfrage verworfen. + +👉 **Screenshot geeignet:** Execute Sub-workflow mit Workflow `elyra_admincheck` und Variable `chatId` + +**Anschlusslogik:** +- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)* + +#### Node 19 – Set (Ablehnungsprompt für nicht erlaubtes Thema) + +**Zweck:** Bereitet den Prompt für eine freundlich-bestimmte Ablehnungsantwort durch Ollama vor. +Dies erfolgt, wenn das Thema vom Klassifizierungsmodell als `nicht zulässig` eingestuft wurde. + +**Node-Typ:** Set +**Name:** Elyra – Thema abgelehnt (Prompt vorbereiten) + +**Felder hinzufügen:** +- `chatId`: `={{$json["chatId"]}}` +- `text`: `={{$json["text"]}}` +- `ablehnungskontext`: + ```text + Die folgende Anfrage konnte keinem zugelassenen Thema zugeordnet werden. + Formuliere eine kurze, aber klare Antwort, die dem Nutzer freundlich mitteilt, + dass diese Art von Anfrage aktuell nicht unterstützt wird. + Die Antwort soll sachlich bleiben, aber höflich und professionell wirken. + ``` + +👉 **Screenshot geeignet:** Set-Node mit sichtbarem Feld `ablehnungskontext` + +**Anschlusslogik:** +- **→** weiter mit Node 20 – *Antwort generieren (Ollama mit Kontext)* + +#### Node 20 – HTTP Request (Antwort generieren – Ollama) + +**Zweck:** Sendet die Nutzeranfrage zusammen mit dem relevanten Kontext (z. B. Chatbefehle, Eventinfos, Ablehnungshinweis) +an das lokale Sprachmodell **Mistral** über den Ollama-Endpunkt und erhält eine formulierte Antwort zurück. + +**Node-Typ:** HTTP Request +**Name:** Elyra – Antwort generieren (Ollama) + +**Parameter:** +- **Method:** `POST` +- **URL:** + ``` + http://ollama..tld/api/generate + ``` +- **Headers:** + - `Content-Type`: `application/json` +- **Body → JSON:** + ```json + { + "model": "mistral", + "stream": false, + "prompt": "{{$json['ablehnungskontext'] || ''}}{{$json['kontext'] || ''}}\n\nNachricht:\n{{$json['text']}}" + } + ``` + +> [!NOTE] +> Der Prompt setzt sich aus verschiedenen Quellen zusammen – je nach Pfad: +> – bei `chatbefehl`: Liste aller Chatkommandos (`kontext`) +> – bei `eventinfo` oder `sendezeit`: Zusatzinfos aus Sub-Workflows +> – bei `nicht erlaubt`: Ablehnungshinweis (`ablehnungskontext`) +> +> Der finale Prompt enthält immer den Originaltext des Nutzers. + +> [!TIP] +> Du kannst über die `prompt:`-Zusammensetzung bei Bedarf eigene Regeln einbauen, +> z. B. Priorisierungen, stilistische Hinweise oder Sprachwechsel. + +👉 **Screenshot geeignet:** HTTP-Request-Node mit Body-Feld `prompt` + +**Anschlusslogik:** +- **→** weiter mit Node 21 – *Antwort an WAHA senden* + +#### Node 21 – HTTP Request (Antwort über WAHA senden) + +**Zweck:** Sendet die von Ollama generierte oder anderweitig erzeugte Antwort als WhatsApp-Nachricht über die WAHA‑API an den Nutzer. + +**Node-Typ:** HTTP Request +**Name:** Elyra – Antwort senden (WAHA) + +**Parameter:** +- **Method:** `POST` +- **URL:** + ``` + https://waha..tld/api/sendText + ``` +- **Headers:** + - `Content-Type`: `application/json` + - *(optional)* `Authorization`: `Bearer ` +- **Body → JSON:** + ```json + { + "chatId": "={{$json[\"chatId\"]}}", + "text": "={{$json[\"antwort\"] || $json[\"text\"]}}" + } + ``` + +> [!NOTE] +> Das Feld `antwort` enthält entweder die direkte Antwort aus einem vorherigen Set‑Node +> oder die von **Ollama** generierte Antwort aus dem HTTP-Request‑Node davor. +> Falls `antwort` leer ist, wird ersatzweise `text` genutzt – z. B. für statische Fallbacks. + +👉 **Screenshot geeignet:** HTTP Request mit gesetztem Body `chatId` + `text` + +**Anschlusslogik:** +- → **Ende des Workflows**