[模板] - 仪表板聊天

高级

这是一个自动化工作流,包含 30 个节点。主要使用 N8n、Set、Code、Merge、Webhook 等节点。 AI模型使用仪表板:追踪LLM工作流的令牌指标和成本

前置要求
  • HTTP Webhook 端点(n8n 会自动生成)
  • OpenAI API Key

分类

未分类
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "Yu2P4qvlbmpPT3mg",
  "meta": {
    "instanceId": "b35269c8495db354f1459fb10ec8f343e34cd6c71fb24a004bb668ccda72b4e6",
    "templateCredsSetupCompleted": true
  },
  "name": "[模板] - 仪表板聊天",
  "tags": [],
  "nodes": [
    {
      "id": "85c930e9-cb8a-47ad-8082-5ae97c945b4b",
      "name": "当收到聊天消息时",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "position": [
        -320,
        1264
      ],
      "webhookId": "09b95814-ef46-410f-adfb-7ef2683d499d",
      "parameters": {
        "public": true,
        "options": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "cbd8fb15-5cf0-4993-a5f5-c732496c3efe",
      "name": "AI 代理",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -96,
        1264
      ],
      "parameters": {
        "text": "=Réponds à ce message : \n{{ $json.chatInput }}\n",
        "options": {},
        "promptType": "define"
      },
      "typeVersion": 2.2
    },
    {
      "id": "e0decd9d-dc72-4283-815b-08f79b1b33b9",
      "name": "简单记忆",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        -48,
        1456
      ],
      "parameters": {},
      "typeVersion": 1.3
    },
    {
      "id": "522553ba-e4eb-42d9-ae80-d6de88577792",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        1648
      ],
      "parameters": {
        "color": 3,
        "width": 1968,
        "height": 464,
        "content": "## 🧠 迷你工作流 — 令牌追踪"
      },
      "typeVersion": 1
    },
    {
      "id": "0f270c23-7f82-49b4-8bbb-4362d12fcd60",
      "name": "OpenAI 聊天模型",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -224,
        1456
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "Eyt0iPqZCLeZxqlC",
          "name": "OpenAi account"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "d36b0ff3-8367-4790-ae92-141df8205be1",
      "name": "获取执行ID",
      "type": "n8n-nodes-base.set",
      "position": [
        272,
        1440
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "3677fb1a-e0b3-4468-bd10-6250ee768329",
              "name": "id",
              "type": "string",
              "value": "={{$execution.id}}"
            },
            {
              "id": "44775e78-8260-4316-a358-39b8b941313e",
              "name": "model",
              "type": "string",
              "value": "={{$('OpenAI Chat Model').params.model}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "6f390583-909a-4ecd-822e-12feb703ff30",
      "name": "模型/令牌信息",
      "type": "n8n-nodes-base.set",
      "position": [
        576,
        1872
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "701dd054-2196-489d-8c9c-05d802ddf0d9",
              "name": "model_name",
              "type": "string",
              "value": "={{ $('OpenAI Chat Model').params.model.value }}"
            },
            {
              "id": "4ec60898-644f-46c4-9b14-a306c64c2da9",
              "name": "completionTokens",
              "type": "number",
              "value": "={{ $json.data.resultData.runData['OpenAI Chat Model'][0].data.ai_languageModel[0][0].json.tokenUsage.completionTokens }}"
            },
            {
              "id": "10c28ae4-4618-48c9-9787-63acd3fe66b3",
              "name": "promptTokens",
              "type": "number",
              "value": "={{ $json.data.resultData.runData['OpenAI Chat Model'][0].data.ai_languageModel[0][0].json.tokenUsage.promptTokens }}"
            },
            {
              "id": "41d5534d-dc94-4751-a233-446b039c3a43",
              "name": "totalTokens",
              "type": "number",
              "value": "={{ $json.data.resultData.runData['OpenAI Chat Model'][0].data.ai_languageModel[0][0].json.tokenUsage.totalTokens }}"
            },
            {
              "id": "ec0edb07-dabe-44fd-93df-a901838472c8",
              "name": "executionId",
              "type": "string",
              "value": "={{ $json.id }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a47a978e-c1aa-4f98-89b4-e25294ff9d63",
      "name": "插入行2",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        496,
        1440
      ],
      "parameters": {
        "columns": {
          "value": {
            "action": "={{ $('When chat message received').item.json.action }}",
            "output": "={{ $('AI Agent').item.json.output }}",
            "chatInput": "={{ $('When chat message received').item.json.chatInput }}",
            "sessionId": "={{ $('When chat message received').item.json.sessionId }}",
            "executionId": "={{ $json.id }}"
          },
          "schema": [
            {
              "id": "sessionId",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "sessionId",
              "defaultMatch": false
            },
            {
              "id": "action",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "action",
              "defaultMatch": false
            },
            {
              "id": "output",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "output",
              "defaultMatch": false
            },
            {
              "id": "chatInput",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "chatInput",
              "defaultMatch": false
            },
            {
              "id": "completionTokens",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "completionTokens",
              "defaultMatch": false
            },
            {
              "id": "promptTokens",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "promptTokens",
              "defaultMatch": false
            },
            {
              "id": "totalTokens",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "totalTokens",
              "defaultMatch": false
            },
            {
              "id": "globalCost",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "globalCost",
              "defaultMatch": false
            },
            {
              "id": "modelName",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "modelName",
              "defaultMatch": false
            },
            {
              "id": "executionId",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "executionId",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "GyHAqQLTtmZbynYI",
          "cachedResultUrl": "/projects/E58XbkoO8SgET2Sl/datatables/GyHAqQLTtmZbynYI",
          "cachedResultName": "Template - data"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7b0b70b9-5b36-4e36-b811-da21af4dbaae",
      "name": "获取执行记录",
      "type": "n8n-nodes-base.n8n",
      "position": [
        352,
        1872
      ],
      "parameters": {
        "options": {
          "activeWorkflows": true
        },
        "resource": "execution",
        "operation": "get",
        "executionId": "={{ $json.executionId }}",
        "requestOptions": {}
      },
      "credentials": {
        "n8nApi": {
          "id": "sqvLt8TzAknSm5NM",
          "name": "n8n account 2"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "cdd04485-cdbc-4015-85a1-fdd5c359f4f4",
      "name": "由 Github 模型提供支持",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        -320,
        1872
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 30
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6552176e-15d3-4886-b8b8-f463d0c49e08",
      "name": "获取行数据",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -96,
        1872
      ],
      "parameters": {
        "filters": {
          "conditions": [
            {
              "keyName": "modelName",
              "condition": "isEmpty"
            }
          ]
        },
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "GyHAqQLTtmZbynYI",
          "cachedResultUrl": "/projects/E58XbkoO8SgET2Sl/datatables/GyHAqQLTtmZbynYI",
          "cachedResultName": "Template - data"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ca373635-6cc4-44e1-b4f5-d44ed3d487b1",
      "name": "更新行数据",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        1376,
        1856
      ],
      "parameters": {
        "columns": {
          "value": {
            "modelName": "={{ $json.model_name }}",
            "globalCost": "={{ $json.globalCost }}",
            "totalTokens": "={{ $json.promptTokens }}",
            "promptTokens": "={{ $json.promptTokens }}",
            "completionTokens": "={{ $json.completionTokens }}"
          },
          "schema": [
            {
              "id": "sessionId",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "sessionId",
              "defaultMatch": false
            },
            {
              "id": "action",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "action",
              "defaultMatch": false
            },
            {
              "id": "output",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "output",
              "defaultMatch": false
            },
            {
              "id": "chatInput",
              "type": "string",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "chatInput",
              "defaultMatch": false
            },
            {
              "id": "completionTokens",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "completionTokens",
              "defaultMatch": false
            },
            {
              "id": "promptTokens",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "promptTokens",
              "defaultMatch": false
            },
            {
              "id": "totalTokens",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "totalTokens",
              "defaultMatch": false
            },
            {
              "id": "globalCost",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "globalCost",
              "defaultMatch": false
            },
            {
              "id": "modelName",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "modelName",
              "defaultMatch": false
            },
            {
              "id": "executionId",
              "type": "number",
              "display": true,
              "removed": true,
              "readOnly": false,
              "required": false,
              "displayName": "executionId",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "filters": {
          "conditions": [
            {
              "keyName": "executionId",
              "keyValue": "={{ $json.executionId }}"
            }
          ]
        },
        "operation": "update",
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "GyHAqQLTtmZbynYI",
          "cachedResultUrl": "/projects/E58XbkoO8SgET2Sl/datatables/GyHAqQLTtmZbynYI",
          "cachedResultName": "Template - data"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "63788429-3fd4-423d-8b29-6b8d4101720e",
      "name": "编辑字段",
      "type": "n8n-nodes-base.set",
      "position": [
        -128,
        992
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "95f26c22-8da0-4d86-91f5-ee633cc72e98",
              "name": "today",
              "type": "string",
              "value": "={{ $today }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "a3372fa9-18a2-4152-891d-2e40faf0b31f",
      "name": "插入行1",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        -304,
        2256
      ],
      "parameters": {
        "columns": {
          "value": {
            "name": "={{ $json.name }}",
            "promptTokensPrice": "={{ $json.promptTokensPrice }}",
            "completionTokensPrice": "={{ $json.completionTokensPrice }}"
          },
          "schema": [
            {
              "id": "name",
              "type": "string",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "name",
              "defaultMatch": false
            },
            {
              "id": "promptTokensPrice",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "promptTokensPrice",
              "defaultMatch": false
            },
            {
              "id": "completionTokensPrice",
              "type": "number",
              "display": true,
              "removed": false,
              "readOnly": false,
              "required": false,
              "displayName": "completionTokensPrice",
              "defaultMatch": false
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "5tsC5vulvGwYGS2g",
          "cachedResultUrl": "/projects/E58XbkoO8SgET2Sl/datatables/5tsC5vulvGwYGS2g",
          "cachedResultName": "Model - Price"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "a61ae8a0-85a6-4e1f-b3ec-8f4248839816",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        2144
      ],
      "parameters": {
        "color": 5,
        "width": 544,
        "height": 272,
        "content": "## 📊 n8n的LLM定价表"
      },
      "typeVersion": 1
    },
    {
      "id": "c89ef6c5-8b35-45d3-8e21-b54125323bee",
      "name": "获取行数据1",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        80,
        992
      ],
      "parameters": {
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "GyHAqQLTtmZbynYI",
          "cachedResultUrl": "/projects/E58XbkoO8SgET2Sl/datatables/GyHAqQLTtmZbynYI",
          "cachedResultName": "Template - data"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "9f4069e9-1d31-455c-9d4c-782ff0857ba0",
      "name": "编辑字段1",
      "type": "n8n-nodes-base.set",
      "position": [
        -80,
        2256
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "f52f1004-3bed-448b-9655-b6c61d238f57",
              "name": "",
              "type": "string",
              "value": ""
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "c3c21917-4e60-4545-b524-2f5bb28789bf",
      "name": "遍历项目",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        128,
        1872
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "451c7483-b793-47d9-869b-695bea8771ab",
      "name": "无操作,什么都不做",
      "type": "n8n-nodes-base.noOp",
      "position": [
        352,
        1680
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "e9c3fa27-22b9-42f5-bf51-e51f78abbe94",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        912
      ],
      "parameters": {
        "color": 6,
        "width": 1152,
        "height": 256,
        "content": "## 生成仪表板"
      },
      "typeVersion": 1
    },
    {
      "id": "a92b9155-33e0-4d53-8e48-7d6ccd48a433",
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -320,
        992
      ],
      "webhookId": "176f23d4-71b3-41e0-9364-43bea6be01d3",
      "parameters": {
        "path": "176f23d4-71b3-41e0-9364-43bea6be01d3",
        "options": {},
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "3ce8907e-c978-4769-942a-806cd7182351",
      "name": "获取行数据3",
      "type": "n8n-nodes-base.dataTable",
      "position": [
        784,
        1744
      ],
      "parameters": {
        "operation": "get",
        "returnAll": true,
        "dataTableId": {
          "__rl": true,
          "mode": "list",
          "value": "5tsC5vulvGwYGS2g",
          "cachedResultUrl": "/projects/E58XbkoO8SgET2Sl/datatables/5tsC5vulvGwYGS2g",
          "cachedResultName": "Model - Price"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0d493cb2-d367-4eb7-968e-1b05e8e0dee5",
      "name": "合并1",
      "type": "n8n-nodes-base.merge",
      "position": [
        944,
        1856
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "advanced": true,
        "mergeByFields": {
          "values": [
            {
              "field1": "name",
              "field2": "model_name"
            }
          ]
        }
      },
      "typeVersion": 3.2
    },
    {
      "id": "4f7b3e8e-8b1b-4ccc-af0f-2371ca846d95",
      "name": "JavaScript代码1",
      "type": "n8n-nodes-base.code",
      "position": [
        1152,
        1856
      ],
      "parameters": {
        "jsCode": "// Parcours tous les items reçus\nreturn items.map(item => {\n  // Récupération des valeurs depuis l'item\n  const completionTokens = item.json.completionTokens || 0;\n  const promptTokens = item.json.promptTokens || 0;\n  const completionTokensPrice = item.json.completionTokensPrice || 0;\n  const promptTokensPrice = item.json.promptTokensPrice || 0;\n\n  // Calcul du coût global\n  const globalCost = (completionTokens * completionTokensPrice) + (promptTokens * promptTokensPrice);\n\n  // Retourner l'item avec globalCost ajouté\n  return {\n    json: {\n      ...item.json,\n      globalCost\n    }\n  };\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "a5c21eed-3dcf-4a38-a341-d8bc42aaaf22",
      "name": "便签3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -416,
        1200
      ],
      "parameters": {
        "color": 4,
        "width": 1104,
        "height": 416,
        "content": "## 聊天示例:"
      },
      "typeVersion": 1
    },
    {
      "id": "07c09e80-88a2-443b-bd8a-59db83d8c0a1",
      "name": "无操作,不执行任何操作1",
      "type": "n8n-nodes-base.noOp",
      "position": [
        272,
        1264
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "10c02d77-5179-44a5-a219-213921b41377",
      "name": "JavaScript代码",
      "type": "n8n-nodes-base.code",
      "position": [
        288,
        992
      ],
      "parameters": {
        "jsCode": "// === Calcul des KPI pour les conversations ===\nlet totalConversations = 0;\nlet totalCompletionTokens = 0;\nlet totalPromptTokens = 0;\nlet totalTokens = 0;\nlet totalCost = 0;\nlet sessions = new Set();\nlet modelUsage = {};\nlet conversationsParJour = [];\n\nfor (const item of items) {\n  const data = item.json;\n\n  totalConversations += 1;\n  totalCompletionTokens += data.completionTokens || 0;\n  totalPromptTokens += data.promptTokens || 0;\n  totalTokens += data.totalTokens || 0;\n\n  // Calcul du coût global si absent\n  let globalCost = data.globalCost;\n  if (globalCost === null || globalCost === undefined) {\n    const completionTokensPrice = data.completionTokensPrice || 0;\n    const promptTokensPrice = data.promptTokensPrice || 0;\n    globalCost = (data.completionTokens || 0) * completionTokensPrice + (data.promptTokens || 0) * promptTokensPrice;\n  }\n  totalCost += globalCost;\n\n  // Comptage des sessions uniques\n  if (data.sessionId) sessions.add(data.sessionId);\n\n  // Comptage messages par modèle\n  const model = data.modelName || \"unknown\";\n  if (!modelUsage[model]) modelUsage[model] = 0;\n  modelUsage[model] += 1;\n\n  // Tableau journalier\n  if (data.createdAt) {\n    const day = data.createdAt.split(\"T\")[0];\n    let dayEntry = conversationsParJour.find(d => d.date === day);\n    if (!dayEntry) {\n      dayEntry = { date: day, count: 0, totalCost: 0, promptTokens: 0, completionTokens: 0 };\n      conversationsParJour.push(dayEntry);\n    }\n    dayEntry.count += 1;\n    dayEntry.totalCost += globalCost;\n    dayEntry.promptTokens += data.promptTokens || 0;\n    dayEntry.completionTokens += data.completionTokens || 0;\n  }\n}\n\n// Tri par date\nconversationsParJour = conversationsParJour.sort((a, b) => a.date.localeCompare(b.date));\n\n// Moyennes\nconst avgTokensPerConversation = totalConversations > 0 ? (totalTokens / totalConversations).toFixed(2) : 0;\nconst avgCostPerConversation = totalConversations > 0 ? (totalCost / totalConversations).toFixed(6) : 0;\n\n// === Génération HTML ===\nconst html = `\n<!DOCTYPE html>\n<html lang=\"fr\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>Dashboard Conversations</title>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js\"></script>\n<link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css\">\n<style>\n:root {\n  --radius: 0.65rem;\n  --background: #ffffff;\n  --foreground: #000000;\n  --card: #f8f9fa;\n  --card-foreground: #000000;\n  --primary: #3b82f6;\n  --chart-conversations: #3b82f6;\n  --chart-tokens-prompt: #3b82f6;\n  --chart-tokens-completion: #10b981;\n  --destructive: #ef4444;\n  --muted: #9ca3af;\n  --border: #e5e7eb;\n}\nbody.dark {\n  --background: #0b142c;\n  --foreground: #f1f5f9;\n  --card: #1e293b;\n  --card-foreground: #f1f5f9;\n  --primary: #60a5fa;\n  --chart-conversations: #60a5fa;\n  --chart-tokens-prompt: #60a5fa;\n  --chart-tokens-completion: #34d399;\n  --destructive: #f87171;\n  --muted: #64748b;\n  --border: #334155;\n}\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n\n    body {\n      background: var(--background);\n      color: var(--foreground);\n      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n      padding: 20px;\n      transition: background-color 0.3s ease, color 0.3s ease;\n    }\n\n    .container {\n      max-width: 1400px;\n      margin: 0 auto;\n    }\n\n    .header {\n      display: flex;\n      justify-content: space-between;\n      align-items: center;\n      margin-bottom: 40px;\n    }\n\n    .header h1 {\n      font-size: 2.5rem;\n      font-weight: 700;\n    }\n\n    .section-title-header {\n      font-size: 2.5rem;\n      font-weight: 600;\n      margin-top: 40px;\n      margin-bottom: 20px;\n      display: flex;\n      align-items: center;\n      gap: 12px;\n    }\n\n    .section-title-header i {\n      color: var(--chart-topup);\n      font-size: 2.5rem;\n    }\n\n    .section-title {\n      font-size: 1.5rem;\n      font-weight: 600;\n      margin-top: 40px;\n      margin-bottom: 20px;\n      display: flex;\n      align-items: center;\n      gap: 12px;\n    }\n\n    .section-title i {\n      color: var(--primary);\n      font-size: 1.75rem;\n    }\n\n    .kpi-grid {\n      display: grid;\n      grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));\n      gap: 1rem;\n      margin-bottom: 30px;\n    }\n\n    .kpi-card {\n      background: var(--card);\n      border-radius: 0.625rem;\n      padding: 20px;\n      border: 1px solid var(--border);\n      transition: background-color 0.3s ease, border-color 0.3s ease;\n      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n    }\n\n    .kpi-value {\n      font-size: 2rem;\n      font-weight: 700;\n      margin-bottom: 8px;\n      color: var(--card-foreground);\n      transition: color 0.3s ease;\n    }\n\n    .kpi-positive {\n      color: var(--chart-investment);\n    }\n\n    .kpi-negative {\n      color: var(--destructive);\n    }\n\n    .kpi-label {\n      font-size: 0.875rem;\n      color: var(--muted-foreground);\n      transition: color 0.3s ease;\n    }\n\n    .charts-grid {\n      display: grid;\n      grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));\n      gap: 20px;\n      margin-bottom: 30px;\n    }\n\n    .chart-container {\n      background: var(--card);\n      border-radius: 0.625rem;\n      padding: 20px;\n      border: 1px solid var(--border);\n      transition: background-color 0.3s ease, border-color 0.3s ease;\n      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n      position: relative;\n      height: 400px;\n    }\n\n    .chart-wrapper {\n      position: relative;\n      height: 100%;\n    }\n\n    .theme-toggle {\n      display: flex;\n      align-items: center;\n      gap: 10px;\n      cursor: pointer;\n      user-select: none;\n    }\n\n    .toggle-switch {\n      width: 48px;\n      height: 24px;\n      background: var(--border);\n      border-radius: 12px;\n      position: relative;\n      cursor: pointer;\n      transition: background-color 0.3s ease;\n    }\n\n    .toggle-switch::after {\n      content: '';\n      position: absolute;\n      width: 20px;\n      height: 20px;\n      background: var(--card);\n      border-radius: 50%;\n      top: 2px;\n      left: 2px;\n      transition: transform 0.3s ease;\n      box-shadow: 0 1px 3px rgba(0,0,0,0.3);\n    }\n\n    body.dark .toggle-switch::after {\n      transform: translateX(24px);\n    }\n\n    @media (max-width: 768px) {\n      .kpi-grid {\n        grid-template-columns: 1fr;\n      }\n      .charts-grid {\n        grid-template-columns: 1fr;\n      }\n      .header {\n        flex-direction: column;\n        gap: 20px;\n      }\n    }\n\n</style>\n</head>\n<body class=\"dark\">\n<div class=\"container\">\n  <div class=\"header\">\n    <h1><i class=\"fas fa-comments\"></i> Dashboard Conversations</h1>\n    <div class=\"theme-toggle\" onclick=\"toggleTheme()\">\n      <span>🌙</span>\n      <div class=\"toggle-switch\"></div>\n      <span>☀️</span>\n    </div>\n  </div>\n\n  <div class=\"section-title\"><i class=\"fas fa-wallet\"></i> Statistiques Clés</div>\n  <div class=\"kpi-grid\">\n    <div class=\"kpi-card\">\n      <div class=\"kpi-value kpi-positive\">${totalConversations.toLocaleString('fr-FR')}</div>\n      <div class=\"kpi-label\">Total Messages</div>\n    </div>\n    <div class=\"kpi-card\">\n      <div class=\"kpi-value kpi-positive\">${sessions.size.toLocaleString('fr-FR')}</div>\n      <div class=\"kpi-label\">Sessions uniques</div>\n    </div>\n    <div class=\"kpi-card\">\n      <div class=\"kpi-value kpi-positive\">${totalTokens.toLocaleString('fr-FR')}</div>\n      <div class=\"kpi-label\">Total Tokens</div>\n    </div>\n    <div class=\"kpi-card\">\n      <div class=\"kpi-value kpi-positive\">${avgTokensPerConversation}</div>\n      <div class=\"kpi-label\">Tokens moyens par message</div>\n    </div>\n    <div class=\"kpi-card\">\n      <div class=\"kpi-value kpi-negative\">${totalCost.toFixed(6)} €</div>\n      <div class=\"kpi-label\">Coût total</div>\n    </div>\n    <div class=\"kpi-card\">\n      <div class=\"kpi-value kpi-negative\">${avgCostPerConversation} €</div>\n      <div class=\"kpi-label\">Coût moyen par message</div>\n    </div>\n  </div>\n\n  <div class=\"section-title\"><i class=\"fas fa-chart-bar\"></i> Historique Journalier</div>\n  <div class=\"charts-grid\">\n    <div class=\"chart-container\"><div class=\"chart-wrapper\"><canvas id=\"conversationsChart\"></canvas></div></div>\n    <div class=\"chart-container\"><div class=\"chart-wrapper\"><canvas id=\"tokensChart\"></canvas></div></div>\n  </div>\n</div>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js\"></script>\n<script>\nconst conversationsParJour = ${JSON.stringify(conversationsParJour)};\n\nlet charts = {};\n\nfunction getCSSVariable(varName){return getComputedStyle(document.documentElement).getPropertyValue(varName).trim();}\nfunction getThemeColors(){const isDark=document.body.classList.contains('dark'); return {textPrimary:isDark?'#f1f5f9':'#000', textSecondary:isDark?'#94a3b8':'#6b7280', gridColor:isDark?'rgba(148,163,184,0.1)':'rgba(0,0,0,0.1)', borderColor:isDark?'#475569':'#d1d5db', tooltip:isDark?'rgba(15,23,42,0.9)':'rgba(0,0,0,0.9)', chartColor:getCSSVariable('--chart-conversations'), chartPrompt:getCSSVariable('--chart-tokens-prompt'), chartCompletion:getCSSVariable('--chart-tokens-completion')}};\n\nfunction createChartOptions(label){\n  const colors=getThemeColors();\n  return {responsive:true, maintainAspectRatio:false, plugins:{title:{display:true,text:label,color:colors.textPrimary,font:{size:16,weight:'bold'},padding:20},legend:{display:true,position:'top'},tooltip:{backgroundColor:colors.tooltip,titleColor:'#ffffff',bodyColor:'#ffffff',borderColor:colors.borderColor,borderWidth:1,padding:12,displayColors:true}}, scales:{x:{grid:{display:true,color:colors.gridColor,drawBorder:false},ticks:{color:colors.textSecondary,font:{size:11}}},y:{grid:{display:true,color:colors.gridColor,drawBorder:false},ticks:{color:colors.textSecondary,font:{size:11}},beginAtZero:true}}};\n}\n\nfunction initCharts(){\n  const colors=getThemeColors();\n  const ctx=document.getElementById('conversationsChart').getContext('2d');\n  charts.conversations=new Chart(ctx,{type:'bar',data:{labels:conversationsParJour.map(d=>new Date(d.date).toLocaleDateString('fr-FR',{day:'2-digit',month:'2-digit'})),datasets:[{label:'Nombre de messages',data:conversationsParJour.map(d=>d.count),backgroundColor:colors.chartColor,borderColor:colors.chartColor,borderWidth:0,borderRadius:6}]},options:createChartOptions('Messages journaliers')});\n\n  // Graphique tokens empilé\n  const ctxTokens=document.getElementById('tokensChart').getContext('2d');\n  charts.tokens=new Chart(ctxTokens,{type:'bar',data:{labels:conversationsParJour.map(d=>new Date(d.date).toLocaleDateString('fr-FR',{day:'2-digit',month:'2-digit'})),datasets:[{label:'Prompt Tokens',data:conversationsParJour.map(d=>d.promptTokens),backgroundColor:colors.chartPrompt},{label:'Completion Tokens',data:conversationsParJour.map(d=>d.completionTokens),backgroundColor:colors.chartCompletion}]},options:{...createChartOptions('Tokens utilisés par jour'),scales:{x:{stacked:true,grid:{display:true,color:colors.gridColor,drawBorder:false},ticks:{color:colors.textSecondary,font:{size:11}}},y:{stacked:true,grid:{display:true,color:colors.gridColor,drawBorder:false},ticks:{color:colors.textSecondary,font:{size:11}},beginAtZero:true}}}});\n}\n\nfunction updateChartsTheme(){\n  const colors=getThemeColors();\n  if(charts.conversations){\n    charts.conversations.data.datasets[0].backgroundColor=colors.chartColor;\n    charts.conversations.data.datasets[0].borderColor=colors.chartColor;\n    charts.conversations.options.plugins.title.color=colors.textPrimary;\n    charts.conversations.options.scales.x.grid.color=colors.gridColor;\n    charts.conversations.options.scales.x.ticks.color=colors.textSecondary;\n    charts.conversations.options.scales.y.grid.color=colors.gridColor;\n    charts.conversations.options.scales.y.ticks.color=colors.textSecondary;\n    charts.conversations.options.plugins.tooltip.backgroundColor=colors.tooltip;\n    charts.conversations.update();\n  }\n  if(charts.tokens){\n    charts.tokens.data.datasets[0].backgroundColor=colors.chartPrompt;\n    charts.tokens.data.datasets[1].backgroundColor=colors.chartCompletion;\n    charts.tokens.options.plugins.title.color=colors.textPrimary;\n    charts.tokens.options.scales.x.grid.color=colors.gridColor;\n    charts.tokens.options.scales.x.ticks.color=colors.textSecondary;\n    charts.tokens.options.scales.y.grid.color=colors.gridColor;\n    charts.tokens.options.scales.y.ticks.color=colors.textSecondary;\n    charts.tokens.options.plugins.tooltip.backgroundColor=colors.tooltip;\n    charts.tokens.update();\n  }\n}\n\nfunction toggleTheme(){document.body.classList.toggle('dark'); updateChartsTheme();}\ndocument.addEventListener('DOMContentLoaded',initCharts);\n</script>\n</body>\n</html>\n`;\n\n// Retourner le HTML en binaire pour la node n8n\nreturn [{ binary: { data: Buffer.from(html, 'utf8') } }];\n"
      },
      "typeVersion": 2
    },
    {
      "id": "63aae3c1-6479-477f-a26b-85f3d589a6bc",
      "name": "响应Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        496,
        992
      ],
      "parameters": {
        "options": {},
        "respondWith": "binary"
      },
      "typeVersion": 1.4
    },
    {
      "id": "b2f02fbb-2604-4f60-9449-6a8fa76ee670",
      "name": "便签4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        912
      ],
      "parameters": {
        "width": 496,
        "height": 352,
        "content": "## 🤖 n8n AI工作流仪表板"
      },
      "typeVersion": 1
    },
    {
      "id": "19d31dbb-e89e-40c6-853d-f898ebbe1122",
      "name": "便签5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        1296
      ],
      "parameters": {
        "width": 496,
        "height": 464,
        "content": "## ⚙️ 设置与运行"
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "pinData": {
    "Edit Fields1": [
      {
        "json": {
          "name": "claude-4.5-sonnet",
          "promptTokensPrice": 0.000003,
          "completionTokensPrice": 0.000015
        }
      },
      {
        "json": {
          "name": "claude-4.5-sonnet-extended-context",
          "promptTokensPrice": 0.000006,
          "completionTokensPrice": 0.0000225
        }
      },
      {
        "json": {
          "name": "gpt-5",
          "promptTokensPrice": 0.00000125,
          "completionTokensPrice": 0.00001
        }
      },
      {
        "json": {
          "name": "gpt-5-mini",
          "promptTokensPrice": 2.5e-7,
          "completionTokensPrice": 0.000002
        }
      },
      {
        "json": {
          "name": "gpt-5-nano",
          "promptTokensPrice": 5e-8,
          "completionTokensPrice": 4e-7
        }
      },
      {
        "json": {
          "name": "Gemini-2.5-Pro",
          "promptTokensPrice": 0.00000125,
          "completionTokensPrice": 0.00000125
        }
      },
      {
        "json": {
          "name": "Gemini-1.5-Flash",
          "promptTokensPrice": 7.5e-7,
          "completionTokensPrice": 0.000003
        }
      },
      {
        "json": {
          "name": "gpt-4o",
          "promptTokensPrice": 0.0000025,
          "completionTokensPrice": 0.00001
        }
      },
      {
        "json": {
          "name": "gpt-4o-mini",
          "promptTokensPrice": 1.5e-7,
          "completionTokensPrice": 6e-7
        }
      },
      {
        "json": {
          "name": "gpt-4.1-mini",
          "promptTokensPrice": 4e-7,
          "completionTokensPrice": 0.0000016
        }
      },
      {
        "json": {
          "name": "gpt-4.1-nano",
          "promptTokensPrice": 1e-7,
          "completionTokensPrice": 4e-7
        }
      },
      {
        "json": {
          "name": "o3-mini",
          "promptTokensPrice": 0.0000011,
          "completionTokensPrice": 0.0000044
        }
      },
      {
        "json": {
          "name": "Gemini-2.0-Flash-Lite",
          "promptTokensPrice": 7.5e-8,
          "completionTokensPrice": 3e-7
        }
      },
      {
        "json": {
          "name": "DeepSeek-V3",
          "promptTokensPrice": 2.7e-7,
          "completionTokensPrice": 0.0000011
        }
      },
      {
        "json": {
          "name": "Claude-3.7-Sonnet",
          "promptTokensPrice": 0.000003,
          "completionTokensPrice": 0.000015
        }
      },
      {
        "json": {
          "name": "gpt-oss-20b",
          "promptTokensPrice": 3e-8,
          "completionTokensPrice": 1.4e-7
        }
      }
    ]
  },
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "d1513dc9-185a-4183-bdcd-531b250483c3",
  "connections": {
    "Merge1": {
      "main": [
        [
          {
            "node": "Code in JavaScript1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "No Operation, do nothing1",
            "type": "main",
            "index": 0
          },
          {
            "node": "Get Excution ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s)": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Get row(s)1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s)1": {
      "main": [
        [
          {
            "node": "Code in JavaScript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s)3": {
      "main": [
        [
          {
            "node": "Merge1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Insert row1": {
      "main": [
        [
          {
            "node": "Edit Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Update row(s)": {
      "main": [
        [
          {
            "node": "Loop Over Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Excution ID": {
      "main": [
        [
          {
            "node": "Insert row2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Over Items": {
      "main": [
        [
          {
            "node": "No Operation, do nothing",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Get an execution",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get an execution": {
      "main": [
        [
          {
            "node": "Model/Token Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Model/Token Info": {
      "main": [
        [
          {
            "node": "Get row(s)3",
            "type": "main",
            "index": 0
          },
          {
            "node": "Merge1",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get row(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code in JavaScript1": {
      "main": [
        [
          {
            "node": "Update row(s)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When chat message received": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

这是一个高级难度的通用自动化工作流。适合高级用户,包含 16+ 个节点的复杂工作流

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
高级
节点数量30
分类-
节点类型15
难度说明

适合高级用户,包含 16+ 个节点的复杂工作流

作者

Growth engineer focused on AI, data automation, and custom integrations. Use to turn complex ideas into simple, and scalable workflows with solid infrastructure

外部链接
在 n8n.io 上查看 →

分享此工作流