- Add optional chaining (?.) for safe navigation of response properties - Add fallback values if response fields are missing - Extract vertrauen field directly in Parse node for easier reference - Update Check Confidence node to reference $json.vertrauen instead of nested path - Handles cases where LLM response format is unexpected
282 lines
8.5 KiB
JSON
282 lines
8.5 KiB
JSON
{
|
|
"name": "Workflow A - Mail Processing (HTTP)",
|
|
"description": "Fetch unprocessed conversations from Freescout, analyze with AI, save suggestions",
|
|
"nodes": [
|
|
{
|
|
"id": "uuid-trigger-1",
|
|
"name": "Trigger",
|
|
"type": "n8n-nodes-base.cron",
|
|
"typeVersion": 1,
|
|
"position": [250, 200],
|
|
"parameters": {
|
|
"cronExpression": "*/5 * * * *"
|
|
}
|
|
},
|
|
{
|
|
"id": "uuid-get-conversations",
|
|
"name": "Get Unprocessed Conversations",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4,
|
|
"position": [450, 200],
|
|
"parameters": {
|
|
"url": "http://host.docker.internal:4000/query/freescout",
|
|
"method": "POST",
|
|
"headers": {
|
|
"Content-Type": "application/json"
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "{\"query\":\"SELECT c.id, c.number, c.subject, c.customer_email, c.status, GROUP_CONCAT(t.body SEPARATOR ',') as threads_text FROM conversations c LEFT JOIN threads t ON c.id = t.conversation_id LEFT JOIN conversation_custom_field ccf ON c.id = ccf.conversation_id AND ccf.custom_field_id = 8 WHERE c.status = 1 AND ccf.id IS NULL GROUP BY c.id LIMIT 20\"}"
|
|
}
|
|
},
|
|
{
|
|
"id": "uuid-split-results",
|
|
"name": "Split Results",
|
|
"type": "n8n-nodes-base.splitInBatches",
|
|
"typeVersion": 3,
|
|
"position": [650, 200],
|
|
"parameters": {
|
|
"batchSize": 1,
|
|
"options": {
|
|
"basePath": "data"
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"id": "uuid-extract-data",
|
|
"name": "Extract Conversation Data",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 3,
|
|
"position": [850, 200],
|
|
"parameters": {
|
|
"options": {},
|
|
"assignments": {
|
|
"assignments": [
|
|
{
|
|
"name": "ticket_id",
|
|
"value": "={{ $json.id }}",
|
|
"type": "number"
|
|
},
|
|
{
|
|
"name": "ticket_number",
|
|
"value": "={{ $json.number }}",
|
|
"type": "number"
|
|
},
|
|
{
|
|
"name": "subject",
|
|
"value": "={{ $json.subject }}",
|
|
"type": "string"
|
|
},
|
|
{
|
|
"name": "problem_text",
|
|
"value": "={{ ($json.threads_text || 'No description provided').substring(0, 2000) }}",
|
|
"type": "string"
|
|
},
|
|
{
|
|
"name": "customer_email",
|
|
"value": "={{ $json.customer_email }}",
|
|
"type": "string"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"id": "uuid-llm-analyze",
|
|
"name": "LiteLLM AI Analysis",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4,
|
|
"position": [850, 200],
|
|
"parameters": {
|
|
"url": "http://llm.eks-ai.apps.asgard.eks-lnx.fft-it.de/v1/chat/completions",
|
|
"method": "POST",
|
|
"headers": {
|
|
"Content-Type": "application/json"
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "{\"model\":\"gpt-oss_120b_128k-gpu\",\"messages\":[{\"role\":\"system\",\"content\":\"Du bist ein IT-Support-Assistent. Analysiere das folgende IT-Support-Ticket und gib eine strukturierte JSON-Antwort mit folgenden Feldern: kategorie (z.B. Hardware, Software, Netzwerk, Zugriff), lösung_typ (BARAMUNDI_JOB, AUTOMATISCHE_ANTWORT, oder ESKALATION), vertrauen (Dezimal zwischen 0.0 und 1.0 - wie sicher bist du bei dieser Lösung), baramundi_job (Name des Jobs falls BARAMUNDI_JOB), antwort_text (Die Antwort an den Nutzer), begründung (Kurze Erklärung deiner Analyse)\"},{\"role\":\"user\",\"content\":\"Ticket-Nummer: {{$json.ticket_number}}\\nBetreff: {{$json.subject}}\\nProblembeschreibung:\\n{{$json.problem_text}}\\n\\nBitte antworte NUR mit gültiger JSON in dieser Struktur: {\\\"kategorie\\\": \\\"...\\\", \\\"lösung_typ\\\": \\\"...\\\", \\\"vertrauen\\\": 0.75, \\\"baramundi_job\\\": \\\"...\\\", \\\"antwort_text\\\": \\\"...\\\", \\\"begründung\\\": \\\"...\\\"}\"}],\"temperature\":0.7,\"max_tokens\":1000}"
|
|
}
|
|
},
|
|
{
|
|
"id": "uuid-parse-response",
|
|
"name": "Parse AI Response",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 3,
|
|
"position": [1050, 200],
|
|
"parameters": {
|
|
"options": {},
|
|
"assignments": {
|
|
"assignments": [
|
|
{
|
|
"name": "response_text",
|
|
"value": "={{ $json.choices?.[0]?.message?.content || '{}' }}",
|
|
"type": "string"
|
|
},
|
|
{
|
|
"name": "ai_response",
|
|
"value": "={{ typeof $json.response_text === 'string' ? JSON.parse($json.response_text) : $json.response_text }}",
|
|
"type": "object"
|
|
},
|
|
{
|
|
"name": "vertrauen",
|
|
"value": "={{ $json.ai_response?.vertrauen || 0.5 }}",
|
|
"type": "number"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"id": "uuid-check-confidence",
|
|
"name": "Check Confidence >= 0.6",
|
|
"type": "n8n-nodes-base.if",
|
|
"typeVersion": 2,
|
|
"position": [1250, 200],
|
|
"parameters": {
|
|
"conditions": {
|
|
"options": {
|
|
"caseSensitive": true,
|
|
"extractValue": false
|
|
},
|
|
"combinator": "and",
|
|
"conditions": [
|
|
{
|
|
"id": "condition_1",
|
|
"leftValue": "={{ $json.vertrauen }}",
|
|
"rightValue": 0.6,
|
|
"operator": {
|
|
"name": "filter.operator.gte",
|
|
"value": ">="
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
},
|
|
{
|
|
"id": "uuid-save-to-db",
|
|
"name": "Save Suggestion to Freescout DB",
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4,
|
|
"position": [1450, 100],
|
|
"parameters": {
|
|
"url": "http://host.docker.internal:4000/query/freescout",
|
|
"method": "POST",
|
|
"headers": {
|
|
"Content-Type": "application/json"
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "{\"query\":\"INSERT INTO conversation_custom_field (conversation_id, custom_field_id, value) VALUES ({{$json.ticket_id}}, 6, '{{$json.ai_response | json.stringify}}') ON DUPLICATE KEY UPDATE value = VALUES(value); INSERT INTO conversation_custom_field (conversation_id, custom_field_id, value) VALUES ({{$json.ticket_id}}, 7, 'PENDING') ON DUPLICATE KEY UPDATE value = VALUES(value); INSERT INTO conversation_custom_field (conversation_id, custom_field_id, value) VALUES ({{$json.ticket_id}}, 8, '1') ON DUPLICATE KEY UPDATE value = VALUES(value);\"}"
|
|
}
|
|
},
|
|
{
|
|
"id": "uuid-no-action",
|
|
"name": "Skip - Low Confidence",
|
|
"type": "n8n-nodes-base.set",
|
|
"typeVersion": 3,
|
|
"position": [1450, 350],
|
|
"parameters": {
|
|
"options": {},
|
|
"assignments": {
|
|
"assignments": [
|
|
{
|
|
"name": "skipped",
|
|
"value": true,
|
|
"type": "boolean"
|
|
},
|
|
{
|
|
"name": "reason",
|
|
"value": "Confidence {{$json.vertrauen}} < 0.6",
|
|
"type": "string"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
],
|
|
"connections": {
|
|
"Trigger": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Get Unprocessed Conversations",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Get Unprocessed Conversations": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Split Results",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Split Results": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Extract Conversation Data",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Extract Conversation Data": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "LiteLLM AI Analysis",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"LiteLLM AI Analysis": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Parse AI Response",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Parse AI Response": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Check Confidence >= 0.6",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Check Confidence >= 0.6": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Save Suggestion to Freescout DB",
|
|
"index": 0
|
|
}
|
|
],
|
|
[
|
|
{
|
|
"node": "Skip - Low Confidence",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"active": false,
|
|
"settings": {
|
|
"errorHandler": "continueOnError"
|
|
}
|
|
}
|