n8n exportieren

Experte

Dies ist ein Personal Productivity, Multimodal AI-Bereich Automatisierungsworkflow mit 17 Nodes. Hauptsächlich werden If, Set, Code, Switch, Webhook und andere Nodes verwendet. Aufzeichnung von Lebensmittelkalorien aus Bildern in Google Sheets mit LINE und OpenAI Vision

Voraussetzungen
  • HTTP Webhook-Endpunkt (wird von n8n automatisch generiert)
  • Möglicherweise sind Ziel-API-Anmeldedaten erforderlich
  • Google Sheets API-Anmeldedaten
  • OpenAI API Key
Workflow-Vorschau
Visualisierung der Node-Verbindungen, mit Zoom und Pan
Workflow exportieren
Kopieren Sie die folgende JSON-Konfiguration und importieren Sie sie in n8n
{
  "id": "HzZvyjb6riqHb2FR",
  "meta": {
    "instanceId": "9101df148a9ab5ba09dd343a0c584806dbd6c3b74e74be8e97699184269b6877"
  },
  "name": "export n8n",
  "tags": [],
  "nodes": [
    {
      "id": "74a3253b-68d1-4745-85a1-70f5fa3260a9",
      "name": "Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        -592,
        48
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "80b1a421-efcd-487e-8c18-925496da9b0c",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.events[0].message.type }}",
                    "rightValue": "text"
                  }
                ]
              }
            },
            {
              "conditions": {
                "options": {
                  "version": 2,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "c124e4cd-78b0-4ce1-bf72-26806f7211e8",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.body.events[0].message.type }}",
                    "rightValue": "image"
                  }
                ]
              }
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra"
        }
      },
      "typeVersion": 3.2
    },
    {
      "id": "adf17e8b-4e45-4ad4-90e9-208b5bf6207f",
      "name": "KI-Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        160,
        -160
      ],
      "parameters": {
        "text": "={{ $json.text }}",
        "options": {
          "systemMessage": "=You are an AI assistant. Your job is to respond to user messages."
        },
        "promptType": "define"
      },
      "typeVersion": 2.1
    },
    {
      "id": "ef15eb25-64d9-4c30-becc-7f338d46ce55",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        32,
        48
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4.1-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "mO23EZGRXh9cFi9G",
          "name": "OpenAi account 2"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "17e136f4-34f7-42c8-9793-19d13610aa49",
      "name": "Bild analysieren",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "position": [
        -96,
        272
      ],
      "parameters": {
        "text": "=Estimate calories only if the item in this image is a food item, and output the calories in the following simple JSON format:\n\n[{\n\"dishName\": \"result\",\n\"calories\": \"result\",\n}]\n\nDo not output anything but this JSON only if it is food.",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "chatgpt-4o-latest",
          "cachedResultName": "CHATGPT-4O-LATEST"
        },
        "options": {},
        "resource": "image",
        "inputType": "base64",
        "operation": "analyze"
      },
      "credentials": {
        "openAiApi": {
          "id": "mO23EZGRXh9cFi9G",
          "name": "OpenAi account 2"
        }
      },
      "typeVersion": 1.8
    },
    {
      "id": "0359b260-8639-475b-8d47-cc76e004f027",
      "name": "Felder bearbeiten1",
      "type": "n8n-nodes-base.set",
      "position": [
        576,
        272
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "6e9a99a3-62b1-4121-b7d7-148eaebbe9ba",
              "name": "output",
              "type": "string",
              "value": "={{ $json.message }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0535be0a-4aac-48a8-8909-41abca4944a0",
      "name": "Einfacher Speicher",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        176,
        48
      ],
      "parameters": {
        "sessionKey": "={{ $('LINE webhook').item.json.body.events[0].source.userId }}",
        "sessionIdType": "customKey"
      },
      "typeVersion": 1.3
    },
    {
      "id": "2851ca68-76c5-4b97-910d-2d4fa729754f",
      "name": "Code",
      "type": "n8n-nodes-base.code",
      "position": [
        352,
        608
      ],
      "parameters": {
        "jsCode": "const inputData = $input.all();\nconst results = [];\n\nfor (let i = 0; i < inputData.length; i++) {\n  try {\n\n    let aiResponse = inputData[i].json.output || inputData[i].json.text || inputData[i].json.content || '';\n    \n    if (!aiResponse) {\n      console.log(`アイテム ${i}: 空の応答をスキップ`);\n      continue;\n    }\n    \n    // 文字列の前後の空白を削除\n    aiResponse = aiResponse.trim();\n    \n    // JSONの前後にある不要なテキストを削除\n    // ```json や ``` で囲まれている場合の処理\n    if (aiResponse.includes('```json')) {\n      const jsonStart = aiResponse.indexOf('```json') + 7;\n      const jsonEnd = aiResponse.indexOf('```', jsonStart);\n      if (jsonEnd !== -1) {\n        aiResponse = aiResponse.substring(jsonStart, jsonEnd).trim();\n      }\n    } else if (aiResponse.includes('```')) {\n      const jsonStart = aiResponse.indexOf('```') + 3;\n      const jsonEnd = aiResponse.indexOf('```', jsonStart);\n      if (jsonEnd !== -1) {\n        aiResponse = aiResponse.substring(jsonStart, jsonEnd).trim();\n      }\n    }\n    \n    // JSONの開始と終了を見つける\n    const jsonStartIndex = aiResponse.indexOf('[');\n    const jsonEndIndex = aiResponse.lastIndexOf(']');\n    \n    if (jsonStartIndex !== -1 && jsonEndIndex !== -1 && jsonEndIndex > jsonStartIndex) {\n      const jsonString = aiResponse.substring(jsonStartIndex, jsonEndIndex + 1);\n      \n      try {\n        // JSONをパース\n        const parsedData = JSON.parse(jsonString);\n        \n        // 配列でない場合は配列に変換\n        const dataArray = Array.isArray(parsedData) ? parsedData : [parsedData];\n        \n        // 各アイテムを処理\n        dataArray.forEach((item, index) => {\n          // 必要なフィールドが存在することを確認\n          if (item && typeof item === 'object') {\n            const processedItem = {\n              dishName: item.dishName || item.dish_name || item.name || 'Unknown',\n              calories: item.calories || item.calorie || item.cal || 0,\n              // 元のデータも保持(デバッグ用)\n              originalIndex: i,\n              itemIndex: index,\n              timestamp: new Date().toISOString()\n            };\n            \n            // カロリーが数値でない場合は数値に変換を試行\n            if (typeof processedItem.calories === 'string') {\n              const numericCalories = parseInt(processedItem.calories.replace(/[^\\d]/g, ''));\n              processedItem.calories = isNaN(numericCalories) ? 0 : numericCalories;\n            }\n            \n            results.push({ json: processedItem });\n          }\n        });\n        \n      } catch (parseError) {\n        console.log(`アイテム ${i}: JSON解析エラー - ${parseError.message}`);\n        console.log(`問題のあるJSON文字列: ${jsonString}`);\n        \n        // JSONが無効な場合でも、手動でデータを抽出を試行\n        const manualExtract = extractDataManually(aiResponse);\n        if (manualExtract) {\n          results.push({ json: manualExtract });\n        }\n      }\n    } else {\n      console.log(`アイテム ${i}: 有効なJSON構造が見つかりません`);\n      \n      // 手動でデータを抽出を試行\n      const manualExtract = extractDataManually(aiResponse);\n      if (manualExtract) {\n        results.push({ json: manualExtract });\n      }\n    }\n    \n  } catch (error) {\n    console.log(`アイテム ${i}: 処理エラー - ${error.message}`);\n    \n    // エラーが発生した場合でもデフォルト値で処理を継続\n    results.push({\n      json: {\n        dishName: 'Error',\n        calories: 0,\n        error: error.message,\n        originalIndex: i,\n        timestamp: new Date().toISOString()\n      }\n    });\n  }\n}\n\n// 手動でデータを抽出する関数\nfunction extractDataManually(text) {\n  try {\n    const dishMatch = text.match(/\"dishName\"\\s*:\\s*\"([^\"]+)\"/i) || \n                     text.match(/\"dish_name\"\\s*:\\s*\"([^\"]+)\"/i) ||\n                     text.match(/\"name\"\\s*:\\s*\"([^\"]+)\"/i);\n    \n    const caloriesMatch = text.match(/\"calories\"\\s*:\\s*\"?(\\d+)\"?/i) ||\n                         text.match(/\"calorie\"\\s*:\\s*\"?(\\d+)\"?/i) ||\n                         text.match(/\"cal\"\\s*:\\s*\"?(\\d+)\"?/i);\n    \n    if (dishMatch || caloriesMatch) {\n      return {\n        dishName: dishMatch ? dishMatch[1] : 'Unknown',\n        calories: caloriesMatch ? parseInt(caloriesMatch[1]) : 0,\n        extractedManually: true,\n        timestamp: new Date().toISOString()\n      };\n    }\n  } catch (error) {\n    console.log('手動抽出エラー:', error.message);\n  }\n  return null;\n}\n\n// 結果が空の場合のデフォルト処理\nif (results.length === 0) {\n  console.log('処理可能なデータが見つかりませんでした');\n  results.push({\n    json: {\n      dishName: 'No Data',\n      calories: 0,\n      error: 'No valid data found',\n      timestamp: new Date().toISOString()\n    }\n  });\n}\n\nconsole.log(`処理完了: ${results.length} 件のアイテムを変換しました`);\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "bff30c01-da43-463d-940e-b396d8237c61",
      "name": "Zeile in Tabelle anhängen",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        608,
        608
      ],
      "parameters": {
        "columns": {
          "value": {
            "cal": "={{ $json.calories }}",
            "date": "={{ $now.format('yyyy-MM-dd') }}",
            "menu": "={{ $json.dishName }}"
          },
          "schema": [
            {
              "id": "date",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "date",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "menu",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "menu",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "cal",
              "type": "string",
              "display": true,
              "required": false,
              "displayName": "cal",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {},
        "operation": "append",
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": 1178520345,
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1upzLlUvmXYgALLtUR6WEHUtxh064mDNrBVM-b3Of_vg/edit#gid=1178520345",
          "cachedResultName": "11_test"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "1upzLlUvmXYgALLtUR6WEHUtxh064mDNrBVM-b3Of_vg",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1upzLlUvmXYgALLtUR6WEHUtxh064mDNrBVM-b3Of_vg/edit?usp=drivesdk",
          "cachedResultName": "kote2 n8n sheet"
        }
      },
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "XH1Umk2FssMX8K1X",
          "name": "Google Sheets account"
        }
      },
      "typeVersion": 4.6
    },
    {
      "id": "1bf7ef0f-1e8d-4e61-b3bd-aaaefcf15366",
      "name": "Code1",
      "type": "n8n-nodes-base.code",
      "position": [
        352,
        272
      ],
      "parameters": {
        "jsCode": "// n8n Code node - AIエージェントからのJSON文字列をLINEメッセージに変換\nconst inputData = $input.all();\nconst results = [];\n\nfor (let i = 0; i < inputData.length; i++) {\n  try {\n    // AIエージェントからの出力を取得\n    let aiResponse = inputData[i].json.output || inputData[i].json.text || inputData[i].json.content || '';\n    \n    // 空の場合はスキップ\n    if (!aiResponse) {\n      console.log(`アイテム ${i}: 空の応答をスキップ`);\n      continue;\n    }\n    \n    // 文字列の前後の空白を削除\n    aiResponse = aiResponse.trim();\n    \n    // JSONデータを抽出する\n    const menuItems = extractMenuData(aiResponse);\n    \n    if (menuItems && menuItems.length > 0) {\n      // LINEメッセージを生成\n      const lineMessage = generateLineMessage(menuItems);\n      \n      results.push({\n        json: {\n          message: lineMessage,\n          menuCount: menuItems.length,\n          totalCalories: menuItems.reduce((sum, item) => sum + item.calories, 0),\n          timestamp: new Date().toISOString(),\n          originalIndex: i\n        }\n      });\n      \n      console.log(`アイテム ${i}: ${menuItems.length}個のメニューを処理しました`);\n    } else {\n      // 食品が検出されなかった場合\n      results.push({\n        json: {\n          message: \"画像から食品を検出できませんでした。\",\n          menuCount: 0,\n          totalCalories: 0,\n          timestamp: new Date().toISOString(),\n          originalIndex: i\n        }\n      });\n      \n      console.log(`アイテム ${i}: 食品が検出されませんでした`);\n    }\n    \n  } catch (error) {\n    console.log(`アイテム ${i}: 処理エラー - ${error.message}`);\n    \n    // エラーが発生した場合のデフォルトメッセージ\n    results.push({\n      json: {\n        message: \"画像の解析でエラーが発生しました。もう一度お試しください。\",\n        menuCount: 0,\n        totalCalories: 0,\n        error: error.message,\n        timestamp: new Date().toISOString(),\n        originalIndex: i\n      }\n    });\n  }\n}\n\n// JSONデータを抽出する関数\nfunction extractMenuData(text) {\n  const menuItems = [];\n  \n  try {\n    // JSONの前後にある不要なテキストを削除\n    let cleanText = text;\n    \n    // ```json や ``` で囲まれている場合の処理\n    if (cleanText.includes('```json')) {\n      const jsonStart = cleanText.indexOf('```json') + 7;\n      const jsonEnd = cleanText.indexOf('```', jsonStart);\n      if (jsonEnd !== -1) {\n        cleanText = cleanText.substring(jsonStart, jsonEnd).trim();\n      }\n    } else if (cleanText.includes('```')) {\n      const jsonStart = cleanText.indexOf('```') + 3;\n      const jsonEnd = cleanText.indexOf('```', jsonStart);\n      if (jsonEnd !== -1) {\n        cleanText = cleanText.substring(jsonStart, jsonEnd).trim();\n      }\n    }\n    \n    // JSONの開始と終了を見つける\n    const jsonStartIndex = cleanText.indexOf('[');\n    const jsonEndIndex = cleanText.lastIndexOf(']');\n    \n    if (jsonStartIndex !== -1 && jsonEndIndex !== -1 && jsonEndIndex > jsonStartIndex) {\n      const jsonString = cleanText.substring(jsonStartIndex, jsonEndIndex + 1);\n      \n      try {\n        // JSONをパース\n        const parsedData = JSON.parse(jsonString);\n        \n        // 配列でない場合は配列に変換\n        const dataArray = Array.isArray(parsedData) ? parsedData : [parsedData];\n        \n        // 各アイテムを処理\n        dataArray.forEach((item) => {\n          if (item && typeof item === 'object') {\n            const dishName = item.dishName || item.dish_name || item.name || 'Unknown';\n            let calories = item.calories || item.calorie || item.cal || 0;\n            \n            // カロリーが文字列の場合は数値に変換\n            if (typeof calories === 'string') {\n              const numericCalories = parseInt(calories.replace(/[^\\d]/g, ''));\n              calories = isNaN(numericCalories) ? 0 : numericCalories;\n            }\n            \n            // 有効なデータのみ追加\n            if (dishName !== 'Unknown' && calories > 0) {\n              menuItems.push({\n                dishName: dishName,\n                calories: calories\n              });\n            }\n          }\n        });\n        \n      } catch (parseError) {\n        console.log('JSON解析エラー:', parseError.message);\n        // 手動でデータを抽出を試行\n        const manualExtract = extractDataManually(text);\n        if (manualExtract) {\n          menuItems.push(manualExtract);\n        }\n      }\n    } else {\n      // 手動でデータを抽出を試行\n      const manualExtract = extractDataManually(text);\n      if (manualExtract) {\n        menuItems.push(manualExtract);\n      }\n    }\n    \n  } catch (error) {\n    console.log('データ抽出エラー:', error.message);\n  }\n  \n  return menuItems;\n}\n\n// 手動でデータを抽出する関数\nfunction extractDataManually(text) {\n  try {\n    const dishMatch = text.match(/\"dishName\"\\s*:\\s*\"([^\"]+)\"/i) || \n                     text.match(/\"dish_name\"\\s*:\\s*\"([^\"]+)\"/i) ||\n                     text.match(/\"name\"\\s*:\\s*\"([^\"]+)\"/i);\n    \n    const caloriesMatch = text.match(/\"calories\"\\s*:\\s*\"?(\\d+)\"?/i) ||\n                         text.match(/\"calorie\"\\s*:\\s*\"?(\\d+)\"?/i) ||\n                         text.match(/\"cal\"\\s*:\\s*\"?(\\d+)\"?/i);\n    \n    if (dishMatch && caloriesMatch) {\n      const calories = parseInt(caloriesMatch[1]);\n      if (!isNaN(calories) && calories > 0) {\n        return {\n          dishName: dishMatch[1],\n          calories: calories\n        };\n      }\n    }\n  } catch (error) {\n    console.log('手動抽出エラー:', error.message);\n  }\n  return null;\n}\n\n// LINEメッセージを生成する関数\nfunction generateLineMessage(menuItems) {\n  if (!menuItems || menuItems.length === 0) {\n    return \"画像から食品を検出できませんでした。\";\n  }\n  \n  let message = \"カロリー計算:\\n\";\n  \n  // 各メニューアイテムを追加\n  menuItems.forEach((item) => {\n    message += `・${item.dishName}(${item.calories}kcal)\\n`;\n  });\n  \n  message += \"シートに記入しました\";\n  \n  return message;\n}\n\n// 結果が空の場合のデフォルト処理\nif (results.length === 0) {\n  console.log('処理可能なデータが見つかりませんでした');\n  results.push({\n    json: {\n      message: \"画像の処理でエラーが発生しました。もう一度お試しください。\",\n      menuCount: 0,\n      totalCalories: 0,\n      timestamp: new Date().toISOString()\n    }\n  });\n}\n\nconsole.log(`LINE メッセージ生成完了: ${results.length} 件のメッセージを生成しました`);\nreturn results;"
      },
      "typeVersion": 2
    },
    {
      "id": "56bfb625-6b6b-480d-a81d-55ad8a629c57",
      "name": "LINE webhook",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -1040,
        144
      ],
      "webhookId": "0980b700-284e-4e52-a33e-71e087eea37f",
      "parameters": {
        "path": "0980b700-284e-4e52-a33e-71e087eea37f",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 2
    },
    {
      "id": "9e59d336-a880-42aa-b69c-66b445208d1c",
      "name": "Benutzerverifizierung",
      "type": "n8n-nodes-base.if",
      "position": [
        -800,
        144
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "28c9bc55-ef1b-4fee-8c22-a48b88a82c34",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $json.body.events[0].source.userId }}",
              "rightValue": "{your id}"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "11ddcfb0-a2e8-4002-ba64-bff77a4bf066",
      "name": "Bilder herunterladen",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -368,
        272
      ],
      "parameters": {
        "url": "=https://api-data.line.me/v2/bot/message/{{ $json.body.events[0].message.id }}/content",
        "options": {},
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer {channel access toaken}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b40c936c-5273-493a-822f-36b44de906b3",
      "name": "Nur Nachricht",
      "type": "n8n-nodes-base.set",
      "position": [
        -128,
        -160
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "8504cdbe-9904-4e83-aee9-362f02a25014",
              "name": "text",
              "type": "string",
              "value": "={{ $json.body.events[0].message.text }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "599b110f-7771-4497-a41f-b231c3ebc3b8",
      "name": "LINE senden",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1184,
        128
      ],
      "parameters": {
        "url": "https://api.line.me/v2/bot/message/reply",
        "method": "POST",
        "options": {},
        "jsonBody": "={\n  \"replyToken\": \"{{ $('LINE webhook').item.json.body.events[0].replyToken }}\",\n  \"messages\": [\n    {\n    \"type\": \"text\",\n    \"text\":{{ JSON.stringify($json.output) }}\n    }\n  ]\n} ",
        "sendBody": true,
        "sendHeaders": true,
        "specifyBody": "json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer +pLP8YfaojvZ8MzTsryZHngxknOchb4z7RDnDKzh+n2QLxlZKm36+S2yc0WRNrNztHO98iNh1wa+4Vlo73PuRwIrK7q15L493jCEEbRiyv4OKzmHI5hX9PCssHyD9LRZlTdlDfF6M3Vg9Vs1zfAkRgdB04t89/1O/w1cDnyilFU="
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "978b63a0-fab0-445f-8f1b-0ab3b92f22b1",
      "name": "Haftnotiz",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1504,
        -64
      ],
      "parameters": {
        "width": 368,
        "height": 560,
        "content": "## LINE Food Image Calorie Logger to Google Sheets\n\nThis workflow allows a LINE user to send either text or an image of food to a connected LINE bot.\n\n- **Text message** → The AI agent responds directly via LINE.  \n- **Image message** → The workflow downloads it from LINE’s API, analyzes it using OpenAI’s Vision model, estimates calories **only if the image contains food**, and formats the result into JSON.  \n- Detected dishes and calories are appended to a Google Sheet, and a confirmation message is sent back to the user via LINE.\n\n### Key Features\n- Integrates **LINE Messaging API** webhook with n8n\n- Uses **OpenAI Vision** to detect food and estimate calories\n- Automatically logs results into **Google Sheets**\n- Sends **real-time feedback** to the LINE user\n\n### How to Use\n1. Set up a LINE Messaging API channel and get your **channel access token**.\n2. Add your **OpenAI API credentials** in n8n.\n3. Replace placeholders for `{channel access token}`, `{your id}`, and **Google Sheet IDs** with your own.\n4. Activate the workflow and send a food image or text message to your LINE bot.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "78855553-f504-4ee0-913e-1b1c933f0cae",
      "name": "Haftnotiz1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        192
      ],
      "parameters": {
        "color": 4,
        "width": 704,
        "height": 272,
        "content": "## message to LINE"
      },
      "typeVersion": 1
    },
    {
      "id": "e2180426-a161-4483-a630-63eb54cf0d29",
      "name": "Haftnotiz2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        512
      ],
      "parameters": {
        "color": 3,
        "width": 704,
        "height": 272,
        "content": "## write to Google Sheets"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "a7eb22fe-627c-4d1a-aaee-a05a9fb4eb81",
  "connections": {
    "2851ca68-76c5-4b97-910d-2d4fa729754f": {
      "main": [
        [
          {
            "node": "bff30c01-da43-463d-940e-b396d8237c61",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1bf7ef0f-1e8d-4e61-b3bd-aaaefcf15366": {
      "main": [
        [
          {
            "node": "0359b260-8639-475b-8d47-cc76e004f027",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "74a3253b-68d1-4745-85a1-70f5fa3260a9": {
      "main": [
        [
          {
            "node": "b40c936c-5273-493a-822f-36b44de906b3",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "11ddcfb0-a2e8-4002-ba64-bff77a4bf066",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "adf17e8b-4e45-4ad4-90e9-208b5bf6207f": {
      "main": [
        [
          {
            "node": "599b110f-7771-4497-a41f-b231c3ebc3b8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "0359b260-8639-475b-8d47-cc76e004f027": {
      "main": [
        [
          {
            "node": "599b110f-7771-4497-a41f-b231c3ebc3b8",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "56bfb625-6b6b-480d-a81d-55ad8a629c57": {
      "main": [
        [
          {
            "node": "9e59d336-a880-42aa-b69c-66b445208d1c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "b40c936c-5273-493a-822f-36b44de906b3": {
      "main": [
        [
          {
            "node": "adf17e8b-4e45-4ad4-90e9-208b5bf6207f",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "17e136f4-34f7-42c8-9793-19d13610aa49": {
      "main": [
        [
          {
            "node": "2851ca68-76c5-4b97-910d-2d4fa729754f",
            "type": "main",
            "index": 0
          },
          {
            "node": "1bf7ef0f-1e8d-4e61-b3bd-aaaefcf15366",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "0535be0a-4aac-48a8-8909-41abca4944a0": {
      "ai_memory": [
        [
          {
            "node": "adf17e8b-4e45-4ad4-90e9-208b5bf6207f",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "11ddcfb0-a2e8-4002-ba64-bff77a4bf066": {
      "main": [
        [
          {
            "node": "17e136f4-34f7-42c8-9793-19d13610aa49",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ef15eb25-64d9-4c30-becc-7f338d46ce55": {
      "ai_languageModel": [
        [
          {
            "node": "adf17e8b-4e45-4ad4-90e9-208b5bf6207f",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "9e59d336-a880-42aa-b69c-66b445208d1c": {
      "main": [
        [
          {
            "node": "74a3253b-68d1-4745-85a1-70f5fa3260a9",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Häufig gestellte Fragen

Wie verwende ich diesen Workflow?

Kopieren Sie den obigen JSON-Code, erstellen Sie einen neuen Workflow in Ihrer n8n-Instanz und wählen Sie "Aus JSON importieren". Fügen Sie die Konfiguration ein und passen Sie die Anmeldedaten nach Bedarf an.

Für welche Szenarien ist dieser Workflow geeignet?

Experte - Persönliche Produktivität, Multimodales KI

Ist es kostenpflichtig?

Dieser Workflow ist völlig kostenlos. Beachten Sie jedoch, dass Drittanbieterdienste (wie OpenAI API), die im Workflow verwendet werden, möglicherweise kostenpflichtig sind.

Workflow-Informationen
Schwierigkeitsgrad
Experte
Anzahl der Nodes17
Kategorie2
Node-Typen12
Schwierigkeitsbeschreibung

Für fortgeschrittene Benutzer, komplexe Workflows mit 16+ Nodes

Autor
kote2

kote2

@kote2

I share practical examples and ideas for AI automation using tools like n8n, explained in a way that’s easy for beginners to understand. While Dify is currently more well-known in Japan, n8n complements it and is expected to gain even more attention in the future. This channel aims to be a practical guide you can rely on when that wave of popularity arrives. That said, I’m still learning too—so let’s learn and grow together!

Externe Links
Auf n8n.io ansehen

Diesen Workflow teilen

Kategorien

Kategorien: 34