N
n8n Store
Workflow Market
Support Triage workflow - to publish

Support Triage workflow - to publish

by ascuncia0 views

Description

Categories

🤖 AI & Machine Learning

Nodes Used

n8n-nodes-base.setn8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.coden8n-nodes-base.webhookn8n-nodes-base.stickyNoten8n-nodes-base.stickyNoten8n-nodes-base.stickyNote
PriceKostenlos
Views0
Last Updated11/28/2025
workflow.json
{
  "id": "oSPXtQOZoHDhoaDT",
  "meta": {
    "instanceId": "566951f6832eae413e3268789d835fe5467b7767624f8c53a44727976582c562"
  },
  "name": "Support Triage workflow - to publish",
  "tags": [],
  "nodes": [
    {
      "id": "7a738865-d41d-4fef-aa5d-421f75fcc622",
      "name": "SET_SETUP",
      "type": "n8n-nodes-base.set",
      "position": [
        256,
        -240
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "c90a8acc-f2b5-486d-aec1-52e9f53573cc",
              "name": "CONF_THRESH",
              "type": "string",
              "value": "0.7"
            },
            {
              "id": "7fdb72d5-0233-492c-8058-b99ae412c121",
              "name": "JIRA_AI_LABEL",
              "type": "string",
              "value": "triaged:ai"
            },
            {
              "id": "41cec41d-a671-4e62-910f-1c1909c9ac70",
              "name": "domain",
              "type": "string",
              "value": "{   \"keywords\": {     \"boards\": [       \"create board\",       \"board not found\",       \"board update\",       \"private board\",       \"board API error\",       \"permissionLevel\",       \"organization board\"     ],     \"cards\": [       \"card not created\",       \"card update failed\",       \"attachments\",       \"custom field\",       \"labels\",       \"due date\",       \"checklist\"     ],     \"lists\": [       \"list order\",       \"archive list\",       \"list not found\",       \"create list\",       \"move list\",       \"GET lists\",       \"closed list\"     ],     \"members\": [       \"member info\",       \"username update\",       \"stale profile\",       \"API key\",       \"2FA\",       \"account details\",       \"authorization\",       \"scope ignored\",       \"security breach\"     ],     \"integrations\": [       \"webhook\",       \"Jira sync\",       \"Slack sync\",       \"delay\",       \"retry count\",       \"missing payload\",       \"API integration error\",       \"marketplace unavailable\"     ],     \"outage\": [       \"checkout down\",       \"site unavailable\",       \"cannot access\",       \"all users affected\",       \"service unavailable\",       \"api unavailable\",       \"production down\",       \"system-wide failure\",       \"security issue\",       \"critical vulnerability\"     ]   },   \"guidance_addons\": {     \"Boards\": [       \"Verify if the user’s API key has the correct scopes for board creation\",       \"Ask if the error occurs only for private vs. public boards\",       \"Check if the organization ID is passed correctly in the payload\",       \"Review Trello status page for regional board service incidents\",       \"Confirm board limit (10,000) not exceeded for the tenant\"     ],     \"Cards\": [       \"Request failing card IDs and timestamps of API calls\",       \"Check attachment file type and size against Trello’s documented limits\",       \"Verify if the custom field schema is valid and published\",       \"Review if the card update works via UI but fails via API for comparison\",       \"Ask if errors correlate with rate limit spikes or throttling events\"     ],     \"Lists\": [       \"Confirm list IDs being used are from the correct board\",       \"Check if client-side sorting logic is applied after API call\",       \"Request exact payloads sent to PUT /1/lists/{id}/closed\",       \"Verify if archived lists appear correctly when fetching with ?filter=all\",       \"Compare response between API and UI order to detect cache issues\"     ],     \"Members\": [       \"Ask for affected member IDs and when the profile update occurred\",       \"Check if 2FA was enabled before API key issuance\",       \"Request logs showing which fields remain stale after update\",       \"Verify if user is using API token vs. OAuth session\",       \"Cross-check with Trello audit logs for account security events\"     ],     \"Integrations\": [       \"Collect webhook IDs, retry counts, and response codes for failed deliveries\",       \"Ask if issue affects all webhooks or only Jira/Slack integrations\",       \"Check Trello status page for webhook delivery delays or outages\",       \"Verify if payloads include required fields (e.g., comment text, action type)\",       \"Confirm downstream systems (Jira/Slack) aren’t rejecting payloads\"     ]   },   \"components\": [     \"Boards\",     \"Cards\",     \"Lists\",     \"Members\",     \"Integrations\",     \"Other\"   ],   \"priority_policy\": [\"Highest\", \"High\", \"Medium\", \"Low\"],   \"priority_rules\": {     \"Highest\": \"Triggered if outage keywords match OR system-wide API failures (all tenants, critical path) OR confirmed security vulnerabilities (2FA, API key bypass, scope ignored).\",     \"High\": \"Triggered if core path blocked and no workaround available.\",     \"Low\": \"Explicit 'low' or 'minor', narrow scope (<10 users), workaround exists, no outage/security terms.\",     \"Medium\": \"Default; used if none of the above apply.\"   },   \"no_workaround_phrases\": [     \"no workaround\",     \"no reliable workaround\",     \"no consistent workaround\",     \"no confirmed workaround\"   ] }"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "1cc0ca0a-9423-4fea-b908-848f76041f71",
      "name": "AI Case Triage",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        720,
        -240
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-2.0-flash",
          "cachedResultName": "models/gemini-2.0-flash"
        },
        "options": {
          "systemMessage": "=You are a Technical Support Triage Engineer for Jira tickets.\n\nInput JSON:\n- ticket: {summary, description, priority, components, labels}\n- domain: {priority_policy, components, guidance_addons, keywords, no_workaround_phrases}\n- constraints: {confidence_threshold}\n\nRules:\n- Base ALL decisions only on ticket.summary + ticket.description.\n- Justifications must QUOTE exact words from the ticket.\n- Update only if confidence ≥ {{ $json.constraints.confidence_threshold }}; else set action:\"keep\".\n- priority.target MUST be one of domain.priority_policy.\n- component.target MUST be one of domain.components (use \"Other\" if unsure).\n- If evidence is missing (IDs, screenshots, config versions, logs, etc.) add nouns to \"missing_info\".\n- Always return exactly 3 items in \"guidance\":\n  - Prefer domain.guidance_addons for the chosen component.\n  - If fewer than 3 are relevant, pad with deeper or differential checks (logs, configs, comparisons).\n- Guidance must NOT duplicate missing_info requests.\n\nPriority rules:\n- Highest: explicit outage terms OR systemic API failures (all tenants) OR confirmed security vulnerabilities.\n- High: core path blocked AND no workaround.\n- Low: ALL true → explicit \"low\"/\"minor\", narrow scope (<10 users), workaround exists, no outage terms.\n- Otherwise: Medium.\n- Never keep Medium if Low criteria are fully met.\n\nConfidence:\n- Assign confidence conservatively (0.5–0.85 typical).\n- Only use >0.85 if multiple strong signals (e.g., explicit outage + \"all users\").\n\nSchema:\n{\n  \"priority\": {\"action\":\"keep|update\",\"target\":\"High\",\"justification\":\"...\", \"confidence\":0.0},\n  \"component\": {\"action\":\"keep|update\",\"target\":\"Backend\",\"justification\":\"...\", \"confidence\":0.0},\n  \"missing_info\": [\"...\"],\n  \"guidance\": [\"...\", \"...\", \"...\"]\n}\n"
        },
        "messages": {
          "values": [
            {
              "content": "={{ JSON.stringify($json, null, 2) }}"
            }
          ]
        },
        "jsonOutput": true
      },
      "credentials": {
        "googlePalmApi": {
          "id": "xxx",
          "name": "GOOGLEPALMAPI_CREDENTIAL_HERE"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d27991d1-54bf-498c-9501-e8736dff3f5e",
      "name": "Build JIRA Update",
      "type": "n8n-nodes-base.code",
      "position": [
        16,
        304
      ],
      "parameters": {
        "jsCode": "// Input: item from \"Normalize LLM Output\"\nconst { ticket, label, threshold, decision, llm_error } = $('Normalize LLM Output').first().json;\n\nconst lbl = label || 'triaged:ai';\n\n// --- Current values ---\nconst origPri  = ticket.priority || 'Unknown';\nconst origComp = (Array.isArray(ticket.components) && ticket.components.length) ? ticket.components[0] : 'None';\nconst labelExists = Array.isArray(ticket.labels) && ticket.labels.includes(lbl);\n\n// --- Proposed targets ---\nconst targetPri  = decision?.priority?.target;\nconst targetComp = decision?.component?.target;\n\n// --- Changes ---\nlet wantPri = decision.priority?.action === 'update'\n  && Number(decision.priority.confidence) >= Number(threshold)\n  && targetPri && targetPri !== origPri;\n\nlet wantComp = decision.component?.action === 'update'\n  && Number(decision.component.confidence) >= Number(threshold)\n  && targetComp && targetComp !== 'Other'\n  && targetComp !== origComp;\n\nif (llm_error) { wantPri = false; wantComp = false; }\n\nconst willAddLabel = (!labelExists) && !llm_error;\n\n// --- Build Jira Update payload ---\nconst fields = {};\nif (wantPri)  fields.priority   = { name: targetPri };\nif (wantComp) fields.components = [{ name: targetComp }];\n\nconst jiraUpdateBody = {};\nif (Object.keys(fields).length) jiraUpdateBody.fields = fields;\nif (willAddLabel) jiraUpdateBody.update = { labels: [{ add: lbl }] };\n\n// --- Build comment body ---\nconst lines = [];\n\n// ✅ 1-line summary\nlet summary = [];\nsummary.push(wantPri ? `Priority → ${targetPri}` : `Priority kept (${origPri})`);\nsummary.push(wantComp ? `Component → ${targetComp}` : `Component kept (${origComp})`);\nif (willAddLabel) summary.push(`Label added`);\nlines.push(`*Triage Summary:* ${summary.join(\" | \")}`);\nlines.push(\"\");\n\n// Suggested Next Steps + Missing Info\nif ((decision.guidance && decision.guidance.length) || (decision.missing_info && decision.missing_info.length)) {\n  lines.push(\"*Suggested Next Steps*\");\n  if (Array.isArray(decision.guidance)) decision.guidance.forEach(g => lines.push(`- ${g}`));\n  if (Array.isArray(decision.missing_info) && decision.missing_info.length) {\n    lines.push(\"\");\n    lines.push(\"*⚠️ Missing Information*\");\n    decision.missing_info.forEach(m => lines.push(`- ${m}`));\n  }\n  lines.push(\"\");\n  lines.push(\"----\");\n  lines.push(\"\");\n}\n\n// Audit\nlines.push(\"*Audit of Triage*\");\nif (llm_error) {\n  lines.push(`⚠️ LLM error (no field updates; label suppressed): ${llm_error}`);\n  lines.push(\"\");\n}\nlines.push(`*Issue:* ${ticket.key} (id: ${ticket.id})`);\nlines.push(\"\");\n\n// Priority decision\nlines.push(\"*Priority decision:*\");\nif (wantPri) {\n  lines.push(`- Action: update [${origPri} → ${targetPri}]`);\n} else {\n  lines.push(`- Action: keep [${origPri}]`);\n}\nif (decision.priority?.justification) lines.push(`- Justification: \\\"${decision.priority.justification}\\\"`);\nif (decision.priority?.confidence != null) lines.push(`- Confidence: ${Number(decision.priority.confidence).toFixed(2)}`);\nlines.push(`- Verdict: ${wantPri ? \"✅ updated\" : \"ℹ️ kept\"}`);\nlines.push(\"\");\n\n// Component decision\nlines.push(\"*Component decision:*\");\nif (wantComp) {\n  lines.push(`- Action: update [${origComp} → ${targetComp}]`);\n} else {\n  lines.push(`- Action: keep [${origComp}]`);\n}\nif (decision.component?.justification) lines.push(`- Justification: \\\"${decision.component.justification}\\\"`);\nif (decision.component?.confidence != null) lines.push(`- Confidence: ${Number(decision.component.confidence).toFixed(2)}`);\nlines.push(`- Verdict: ${wantComp ? \"✅ updated\" : \"ℹ️ kept\"}`);\nlines.push(\"\");\n\n// Label decision\nlines.push(\"*Label decision:*\");\nif (llm_error) {\n  lines.push(`- ${lbl}: [absent → not added due to LLM error]`);\n} else if (willAddLabel) {\n  lines.push(`- ${lbl}: [absent → added]`);\n} else {\n  lines.push(`- ${lbl}: [present → kept]`);\n}\n\nreturn [{\n  json: {\n    issueKey: ticket.key,\n    issueId: ticket.id,\n    jiraUpdateBody,\n    decision,\n    jsmCommentBody: { public: false, body: lines.join(\"\\n\") }\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "2969caa9-adb9-4b48-a603-c3f9c1167173",
      "name": "JIRA Update",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        240,
        304
      ],
      "parameters": {
        "url": "=https://your-domain.atlassian.net/rest/api/3/issue/{{$json.issueKey}}",
        "method": "PUT",
        "options": {
          "response": {
            "response": {
              "fullResponse": true,
              "responseFormat": "json"
            }
          }
        },
        "jsonBody": "={{ $json.jiraUpdateBody }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "id": "xxx",
          "name": "HTTPBASICAUTH_CREDENTIAL_HERE"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d2be727b-f73b-4cc2-ae60-bba98317714e",
      "name": "Build Payload for LLM",
      "type": "n8n-nodes-base.code",
      "position": [
        496,
        -240
      ],
      "parameters": {
        "jsCode": "// Support both shapes:\n// - Webhook: { body: { issue: { key, id, fields: {...} } }, ... }\n// - Direct GET: { key, id, fields: {...}, ... }\nconst srcIssue = $json.body?.issue ?? $json;\nconst fields   = srcIssue?.fields ?? {};\n\nfunction safeStr(v) { return (v ?? \"\").toString(); }\n\n// Components may be objects or strings\nconst components = Array.isArray(fields.components)\n  ? fields.components.map(c => (typeof c === 'string' ? c : c?.name)).filter(Boolean)\n  : [];\n\n// Labels from Jira fields\nconst labels = Array.isArray(fields.labels) ? fields.labels : [];\n\n// Priority can be object or string\nconst priorityName = fields.priority?.name ?? (typeof fields.priority === 'string' ? fields.priority : 'Unknown');\n\n// Parse domain (it may arrive as a JSON string)\nlet domainObj = {};\ntry {\n  domainObj = typeof $json.domain === 'string' ? JSON.parse($json.domain) : ($json.domain || {});\n} catch {\n  domainObj = {};\n}\n\n// Confidence threshold\nconst conf = Number($json.CONF_THRESH);\nconst confidence_threshold = Number.isFinite(conf) ? conf : 0.7;\n\nreturn [{\n  json: {\n    ticket: {\n      key: safeStr(srcIssue?.key),\n      id: safeStr(srcIssue?.id),\n      summary: safeStr(fields.summary),\n      description: safeStr(fields.description),\n      priority: safeStr(priorityName),\n      components,\n      labels,\n    },\n    domain: domainObj,\n    constraints: {\n      confidence_threshold,\n    },\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "acfa49ef-cb83-437c-ae4d-233f53fbc2fb",
      "name": "JIRA Add Comment",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        688,
        304
      ],
      "parameters": {
        "url": "=https://your-domain.atlassian.net/rest/servicedeskapi/request/{{$json.issueKey}}/comment",
        "method": "POST",
        "options": {},
        "jsonBody": "={{ $json.jsmCommentBody }}",
        "sendBody": true,
        "specifyBody": "json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpBasicAuth"
      },
      "credentials": {
        "httpBasicAuth": {
          "id": "xxx",
          "name": "HTTPBASICAUTH_CREDENTIAL_HERE"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "4465101d-6d93-467a-b1ad-a67a9a4414e3",
      "name": "On Update Result",
      "type": "n8n-nodes-base.code",
      "position": [
        464,
        304
      ],
      "parameters": {
        "jsCode": "// Get the original comment skeleton\nconst base = $items(\"Build JIRA Update\", 0, $itemIndex)?.[0]?.json || {};\n\n// The current item is the HTTP response (because Full Response = true)\nconst status = $json?.statusCode;\nconst text = typeof $json?.body === 'string' ? $json.body : JSON.stringify($json?.body || {});\n\nlet body = base.jsmCommentBody?.body || \"(no audit body)\";\nconst failed = typeof status === 'number' && (status < 200 || status >= 300);\n\nif (failed) {\n  body = `⚠️ Jira update failed\\nStatus: ${status}\\nBody: ${text}\\n\\n` + body;\n}\n\n// Output the final comment payload\nreturn [{\n  json: {\n    issueKey: base.issueKey,\n    jsmCommentBody: { public: false, body }\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "f960236a-9ef2-454e-9f6f-ad9a73aa0752",
      "name": "Metrics Generation",
      "type": "n8n-nodes-base.code",
      "position": [
        912,
        304
      ],
      "parameters": {
        "jsCode": "// Source A: comment/ctx\nconst ctx = $json;\n\n// Source B: Build JIRA Update (decision, confidence, targets)\nconst updateCtx = $items(\"Build JIRA Update\", 0, $itemIndex)[0]?.json || {};\n\n// Source C: JIRA Update (API response with statusCode)\nconst apiCtx = $items(\"JIRA Update\", 0, $itemIndex)[0]?.json || {};\n\nreturn [{\n  json: {\n    issueKey: ctx.issueKey || updateCtx.issueKey,\n    issueId: ctx.issueId || updateCtx.issueId,\n\n    llmError: !!(ctx.llm_error || updateCtx.llm_error),\n\n    // Changes applied (from the update payload)\n    priorityChange: !!updateCtx.jiraUpdateBody?.fields?.priority,\n    componentChange: !!updateCtx.jiraUpdateBody?.fields?.components,\n    labelAdded: !!updateCtx.jiraUpdateBody?.update?.labels,\n\n    // API statuses (prefer JIRA Update response)\n    jiraUpdateStatus: apiCtx.statusCode ?? null,\n    jiraCommentStatus: ctx.id ? 201 : null,\n\n    // Confidence values (from LLM decision)\n    priorityConfidence: updateCtx.decision?.priority?.confidence ?? null,\n    componentConfidence: updateCtx.decision?.component?.confidence ?? null,\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "4cdda784-fc9b-4716-b63e-1e0c360e8ce1",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        32,
        -240
      ],
      "webhookId": "dcc89146-5bdb-4330-9bc1-2d021db266cd",
      "parameters": {
        "path": "dcc89146-5bdb-4330-9bc1-2d021db266cd",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2.1
    },
    {
      "id": "74ae3c48-51b5-4950-8d36-3bcd97dd7737",
      "name": "Normalize LLM Output",
      "type": "n8n-nodes-base.code",
      "position": [
        1072,
        -240
      ],
      "parameters": {
        "jsCode": "// Pull the original structured payload sent to the LLM\nconst base  = $items(\"Build Payload for LLM\", 0, $itemIndex)[0]?.json || {};\nconst setup = $items(\"SET_SETUP\", 0, 0)[0]?.json || {};\n\nconst threshold = Number(base.constraints?.confidence_threshold ?? setup.CONF_THRESH ?? 0.7);\n\nfunction readRawLLMText() {\n  return (\n    $json?.content?.parts?.[0]?.text ??\n    $json?.candidates?.[0]?.content?.parts?.[0]?.text ??\n    \"\"\n  );\n}\n\nlet llm_error = null;\nlet parsed = null;\n\nconst rawText = readRawLLMText();\n\n// Try to extract a JSON object (with or without code fences)\ntry {\n  const match =\n    rawText.match(/```json\\s*([\\s\\S]*?)\\s*```/i) ||\n    rawText.match(/{[\\s\\S]*}/);\n\n  if (!match) throw new Error(\"No JSON-like content in LLM output\");\n\n  parsed = JSON.parse(match[1] || match[0]);\n} catch (e) {\n  llm_error = `LLM output was not valid JSON: ${e.message}`;\n}\n\n// Current values from the ticket\nconst currentPri  = base.ticket?.priority ?? 'Medium';\nconst currentComp = base.ticket?.components?.[0] ?? 'Other';\n\n// Normalize + coerce action when target implies a change\nfunction coerceAction(suggestedAction, target, current) {\n  const s = String(suggestedAction || 'keep').toLowerCase();\n  if (target && target !== current) return 'update';\n  return (s === 'update') ? 'update' : 'keep';\n}\n\n// Normalize (fallbacks if LLM failed)\nconst pri  = parsed?.priority  ?? {};\nconst comp = parsed?.component ?? {};\n\nconst priorityAction  = coerceAction(pri.action,  pri.target,  currentPri);\nconst componentAction = coerceAction(comp.action, comp.target, currentComp);\n\nreturn [{\n  json: {\n    ticket: base.ticket || {},\n    domain: base.domain,\n    threshold,\n    label: setup.JIRA_AI_LABEL || 'triaged:ai',\n    llm_error, // carry forward\n    decision: {\n      priority: {\n        action: priorityAction,\n        target: (pri.target ?? currentPri),\n        justification: (pri.justification ?? ''),\n        confidence: Number(pri.confidence ?? 0),\n      },\n      component: {\n        action: componentAction,\n        target: (comp.target ?? currentComp),\n        justification: (comp.justification ?? ''),\n        confidence: Number(comp.confidence ?? 0),\n      },\n      missing_info: Array.isArray(parsed?.missing_info) ? parsed.missing_info : [],\n      guidance: Array.isArray(parsed?.guidance) ? parsed.guidance : [],\n    }\n  }\n}];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "bfc707c7-8833-4741-af0c-3a900395af61",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -16,
        -576
      ],
      "parameters": {
        "color": 5,
        "width": 432,
        "height": 496,
        "content": "## **Entry & Setup** \n\n- Webhook: receives Jira tickets in real time (no polling). \n\n- Setup Parameters: confidence threshold & AI label for triage.\nDomain Knowledge (Trello): keywords, priority rules, troubleshooting guidance.\n\n\n➡️ Makes the AI act as a *Trello domain expert* instead of giving generic answers."
      },
      "typeVersion": 1
    },
    {
      "id": "b59d7de1-92c6-42c0-95b8-48db9a3c49b9",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        448,
        -576
      ],
      "parameters": {
        "color": 2,
        "width": 800,
        "height": 496,
        "content": "## **AI Analysis**\n\nPayload Builder → AI Case Triage → Normalize Output.\n\n- Prepares Jira ticket + domain context for Gemini.\n- AI suggests priority, component, missing info, and next steps.\n- Normalization ensures valid JSON + confidence thresholds.\n\n\n➡️ Guarantees the AI is *consistent, auditable, and safe to automate*."
      },
      "typeVersion": 1
    },
    {
      "id": "ae0a2993-4e40-4f39-9338-efe01a88660f",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -16,
        -48
      ],
      "parameters": {
        "width": 848,
        "height": 496,
        "content": "## **Jira Update & Audit**\n\n- Build Jira Update: creates update payload for priority/component/labels.\n- Always generates a comment with:\n  • Suggested Next Steps\n  • Missing Information\n  • Audit trail (decisions, justifications, confidence)\n\n- Jira Update + On Update Result + Add Comment:\n  • Applies field changes\n  • Posts AI-generated comment for engineers\n\n\n➡️ Provides *transparency and traceability* for every AI decision.\""
      },
      "typeVersion": 1
    },
    {
      "id": "bafeb986-f0be-4af4-9fdd-01b00f3b89e9",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        864,
        -48
      ],
      "parameters": {
        "color": 4,
        "width": 384,
        "height": 496,
        "content": "## **Metrics**\n- Captures outcome per ticket:\n  • Priority / Component / Label changes\n  • Confidence scores  \n  • API success/failure\n\n\n➡️ Enables tracking of reliability and continuous improvement."
      },
      "typeVersion": 1
    },
    {
      "id": "e0dc1407-5f13-4781-b71b-8f556da9f5c8",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -16,
        -864
      ],
      "parameters": {
        "color": 3,
        "width": 1264,
        "height": 256,
        "content": "# 🔹 AI-Driven Jira Ticket Triage\n\nAutomates Jira support tickets with:\n- LLM-powered analysis enriched by Trello domain knowledge.  \n- Automatic updates to Priority, Component, and Labels.  \n- Transparent audit comments with justifications & confidence.  \n\n\n➡️ Designed as a **scalable prototype**: clear enough to demo, simple enough to extend.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "8996ae65-f13d-4ff0-855f-fb0c698e926b",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1280,
        -864
      ],
      "parameters": {
        "color": 6,
        "width": 528,
        "height": 1312,
        "content": "# Domain Schema (Simplified Example)\n\nThis sticky defines the **domain rules** used for AI triage.  \nIt is injected via the `SET_SETUP` node as `domain`.  \n\n---\n\n## How the Schema Works\n- **components** → list of valid components for classification  \n- **priority_policy** → allowed severities  \n- **priority_rules** → rules the AI must respect  \n- **keywords** → domain-specific hints for classification  \n- **guidance_addons** → contextual guidance for engineers  \n- **no_workaround_phrases** → escalate severity if found  \n\n---\n\n## Example (Simplified)\n\n```json\n{\n  \"components\": [\"Boards\", \"Cards\", \"Other\"],\n  \"priority_policy\": [\"Highest\", \"High\", \"Medium\", \"Low\"],\n  \"priority_rules\": {\n    \"Highest\": \"Outage/security keywords detected\",\n    \"High\": \"Critical path blocked, no workaround\",\n    \"Medium\": \"Default\",\n    \"Low\": \"Minor issue or workaround exists\"\n  },\n  \"keywords\": {\n    \"boards\": [\"create board\", \"board not found\"],\n    \"cards\": [\"card update failed\"],\n    \"outage\": [\"api unavailable\"]\n  },\n  \"guidance_addons\": {\n    \"Boards\": [\n      \"Verify API key scopes for board creation\",\n      \"Check if org ID is passed correctly\"\n    ]\n  },\n  \"no_workaround_phrases\": [\"no workaround\"]\n}\n```\n\n💡 **Tip:** Start with this schema, then expand `keywords` and `guidance_addons` for your own product or ticketing system (**Zendesk**, **Freshdesk**, **ServiceNow**).\n"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "ebbeeff5-3650-4690-a779-304b4a79b6bd",
  "connections": {
    "Webhook": {
      "main": [
        [
          {
            "node": "SET_SETUP",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "SET_SETUP": {
      "main": [
        [
          {
            "node": "Build Payload for LLM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JIRA Update": {
      "main": [
        [
          {
            "node": "On Update Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Case Triage": {
      "main": [
        [
          {
            "node": "Normalize LLM Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "JIRA Add Comment": {
      "main": [
        [
          {
            "node": "Metrics Generation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "On Update Result": {
      "main": [
        [
          {
            "node": "JIRA Add Comment",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build JIRA Update": {
      "main": [
        [
          {
            "node": "JIRA Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize LLM Output": {
      "main": [
        [
          {
            "node": "Build JIRA Update",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Payload for LLM": {
      "main": [
        [
          {
            "node": "AI Case Triage",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}

相关工作流