8
n8n 中文网amn8n.com

导出 n8n

高级

这是一个Personal Productivity, Multimodal AI领域的自动化工作流,包含 17 个节点。主要使用 If, Set, Code, Switch, Webhook 等节点。 使用LINE和OpenAI Vision从图片记录食物卡路里到Google Sheets

前置要求
  • HTTP Webhook 端点(n8n 会自动生成)
  • 可能需要目标 API 的认证凭证
  • Google Sheets API 凭证
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "id": "HzZvyjb6riqHb2FR",
  "meta": {
    "instanceId": "9101df148a9ab5ba09dd343a0c584806dbd6c3b74e74be8e97699184269b6877"
  },
  "name": "导出 n8n",
  "tags": [],
  "nodes": [
    {
      "id": "74a3253b-68d1-4745-85a1-70f5fa3260a9",
      "name": "切换",
      "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": "AI 代理",
      "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": "分析图片",
      "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": "编辑字段1",
      "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": "简单记忆",
      "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": "代码",
      "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": "在表格中添加行",
      "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": "代码1",
      "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": "用户验证",
      "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": "图片下载",
      "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": "仅消息",
      "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",
      "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": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1504,
        -64
      ],
      "parameters": {
        "width": 368,
        "height": 560,
        "content": "## LINE 食物图片卡路里记录器到 Google Sheets"
      },
      "typeVersion": 1
    },
    {
      "id": "78855553-f504-4ee0-913e-1b1c933f0cae",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        192
      ],
      "parameters": {
        "color": 4,
        "width": 704,
        "height": 272,
        "content": "## 发送消息到 LINE"
      },
      "typeVersion": 1
    },
    {
      "id": "e2180426-a161-4483-a630-63eb54cf0d29",
      "name": "便签 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        512
      ],
      "parameters": {
        "color": 3,
        "width": 704,
        "height": 272,
        "content": "## 写入 Google Sheets"
      },
      "typeVersion": 1
    }
  ],
  "active": false,
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "a7eb22fe-627c-4d1a-aaee-a05a9fb4eb81",
  "connections": {
    "Code": {
      "main": [
        [
          {
            "node": "Append row in sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code1": {
      "main": [
        [
          {
            "node": "Edit Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [
          {
            "node": "only message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "images download",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "send LINE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields1": {
      "main": [
        [
          {
            "node": "send LINE",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LINE webhook": {
      "main": [
        [
          {
            "node": "user verification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "only message": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze image": {
      "main": [
        [
          {
            "node": "Code",
            "type": "main",
            "index": 0
          },
          {
            "node": "Code1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "images download": {
      "main": [
        [
          {
            "node": "Analyze image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "user verification": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

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

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

高级 - 个人效率, 多模态 AI

需要付费吗?

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

工作流信息
难度等级
高级
节点数量17
分类2
节点类型12
难度说明

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

作者
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!

外部链接
在 n8n.io 查看

分享此工作流