음식 이미지에서 재료를 추론하고 칼로리를 추산
고급
이것은자동화 워크플로우로, 16개의 노드를 포함합니다.주로 Code, Gmail, Agent, TelegramTrigger, LmChatOpenRouter 등의 노드를 사용하며. 시각 AI와 Telegram을 사용하여 음식 이미지 분석을 통해 칼로리 추정
사전 요구사항
- •Google 계정 및 Gmail API 인증 정보
- •Telegram Bot Token
카테고리
-
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"id": "aZU2RbdXlp3eXCZh",
"meta": {
"instanceId": "15d6057a37b8367f33882dd60593ee5f6cc0c59310ff1dc66b626d726083b48d",
"templateCredsSetupCompleted": true
},
"name": "Infer Ingredients from a Food Image and Estimate Calories",
"tags": [],
"nodes": [
{
"id": "agent1",
"name": "AI Agent - 재료 분석",
"type": "@n8n/n8n-nodes-langchain.agent",
"position": [
752,
400
],
"parameters": {
"text": "=あなたは料理と栄養の専門家です。提供された料理の画像を分析して、以下の情報を日本語で提供してください:\n\n1. 料理名\n2. 推定される主な食材とその分量(グラム)\n3. 各食材のカロリー\n4. 合計推定カロリー\n5. 栄養バランスの簡単な評価\n\n画像URL: {{ $json.message.photo[0].file_id }}",
"options": {
"systemMessage": "以下の指示に従って、ユーザーが入力した料理の情報を分析し、JSON形式で出力してください。\n\n1. **役割:** 料理レシピアナライザー\n2. **入力:** ユーザーからの料理名や食材に関するテキスト\n3. **処理:**\n * 料理名を特定する。\n * 食材リストを抽出し、各食材の「名前(name)」「分量(amount)」「カロリー(calories)」を特定する。分量とカロリーは一般的な値を推定してもよい。\n * 全食材のカロリーを合計し、「合計カロリー(totalCalories)」を計算する。\n * 計算結果に基づき、「栄養バランスの評価(nutritionEvaluation)」を作成する。\n4. **出力:** 以下のJSONスキーマに厳密に従ったJSONオブジェクトのみを出力する。\n * `dishName`: string\n * `ingredients`: array of objects\n * `name`: string\n * `amount`: number (単位: g)\n * `calories`: number (単位: kcal)\n * `totalCalories`: number (単位: kcal)\n * `nutritionEvaluation`: string\n5. **注意:**\n * 出力にJSON以外のテキスト(挨拶、説明など)を含めないでください。\n * JSONのキー名は指定通りにしてください。"
},
"promptType": "define",
"hasOutputParser": true
},
"typeVersion": 2
},
{
"id": "parser1",
"name": "Structured Output Parser",
"type": "@n8n/n8n-nodes-langchain.outputParserStructured",
"position": [
912,
608
],
"parameters": {
"schemaType": "manual",
"inputSchema": "{\n \"type\": \"object\",\n \"properties\": {\n \"dishName\": {\n \"type\": \"string\",\n \"description\": \"料理名\"\n },\n \"ingredients\": {\n \"type\": \"array\",\n \"description\": \"食材リスト\",\n \"items\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"食材名\"\n },\n \"amount\": {\n \"type\": \"number\",\n \"description\": \"分量(グラム)\"\n },\n \"calories\": {\n \"type\": \"number\",\n \"description\": \"カロリー(kcal)\"\n }\n }\n }\n },\n \"totalCalories\": {\n \"type\": \"number\",\n \"description\": \"合計カロリー(kcal)\"\n },\n \"nutritionEvaluation\": {\n \"type\": \"string\",\n \"description\": \"栄養バランスの評価\"\n }\n },\n \"required\": [\"dishName\", \"ingredients\", \"totalCalories\", \"nutritionEvaluation\"]\n}"
},
"typeVersion": 1.2
},
{
"id": "52bb2509-5870-4f4c-96ea-c607548b15b1",
"name": "Telegram Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"position": [
480,
400
],
"webhookId": "f6a51604-9b18-4756-a50e-1e5eb1f8da6f",
"parameters": {
"updates": [
"message"
],
"additionalFields": {
"download": true
}
},
"credentials": {
"telegramApi": {
"id": "Ikb8BOl71y5C8wqu",
"name": "Telegram account"
}
},
"typeVersion": 1.2
},
{
"id": "a8ee11fd-47bc-4524-89a5-a19380041613",
"name": "OpenRouter Chat Model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
"position": [
608,
608
],
"parameters": {
"model": "openai/gpt-5-mini",
"options": {
"temperature": 0.3
}
},
"credentials": {
"openRouterApi": {
"id": "fMR5QJezr3tD108w",
"name": "簡易デモ"
}
},
"typeVersion": 1
},
{
"id": "7397c3e2-c057-4f6b-9afa-010b0a610789",
"name": "Format for Gmail",
"type": "n8n-nodes-base.code",
"position": [
1152,
400
],
"parameters": {
"jsCode": "// AI Agentから出力されたJSON形式の料理分析結果を取得します。\nconst analysisResult = $input.first().json.output;\n\n// 結果が存在しない、または期待した形式でない場合はエラーを防ぐための処理\nif (!analysisResult || !analysisResult.dishName) {\n return [{\n json: {\n subject: \"料理分析エラー\",\n htmlBody: \"<p>AIによる分析結果を取得できませんでした。</p>\"\n }\n }];\n}\n\n// 1. メールの件名を作成\nconst subject = `【料理分析レポート】 ${analysisResult.dishName}`;\n\n// 2. HTMLメールの本文を生成\nlet htmlBody = `\n<!DOCTYPE html>\n<html lang=\"ja\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <style>\n body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; line-height: 1.6; color: #333; background-color: #f9f9f9; margin: 0; padding: 0; }\n .container { max-width: 600px; margin: 20px auto; padding: 25px; background-color: #ffffff; border: 1px solid #e0e0e0; border-radius: 12px; box-shadow: 0 4px 8px rgba(0,0,0,0.05); }\n h1 { font-size: 26px; color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 10px; margin-top: 0; }\n h2 { font-size: 20px; color: #e74c3c; margin-bottom: 15px; }\n h3 { font-size: 18px; color: #34495e; margin-top: 30px; border-bottom: 1px solid #eeeeee; padding-bottom: 8px;}\n p { margin-top: 0; color: #555; }\n table { width: 100%; border-collapse: collapse; margin-top: 15px; }\n th, td { text-align: left; padding: 12px; border-bottom: 1px solid #dddddd; }\n th { background-color: #f2f2f2; color: #333; }\n tr:last-child td { border-bottom: none; }\n .total-calories { text-align: center; background-color: #fff3cd; padding: 15px; border-radius: 8px; margin-top: 20px; }\n .evaluation { background-color: #eafaf1; padding: 15px; border-left: 5px solid #2ecc71; border-radius: 5px; margin-top: 20px;}\n .footer { text-align: center; margin-top: 25px; font-size: 12px; color: #999; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <h1>${analysisResult.dishName}</h1>\n \n <div class=\"total-calories\">\n <h2>合計推定カロリー: ${analysisResult.totalCalories} kcal</h2>\n </div>\n\n <h3>詳細な食材リスト</h3>\n <table>\n <thead>\n <tr>\n <th>食材名</th>\n <th>分量 (g)</th>\n <th>カロリー (kcal)</th>\n </tr>\n </thead>\n <tbody>\n`;\n\n// 各食材の情報をテーブルの行として追加\nif (analysisResult.ingredients && analysisResult.ingredients.length > 0) {\n analysisResult.ingredients.forEach(ingredient => {\n htmlBody += `\n <tr>\n <td>${ingredient.name}</td>\n <td>${ingredient.amount}</td>\n <td>${ingredient.calories}</td>\n </tr>\n `;\n });\n}\n\nhtmlBody += `\n </tbody>\n </table>\n\n <h3>栄養バランスの評価</h3>\n <div class=\"evaluation\">\n <p>${analysisResult.nutritionEvaluation}</p>\n </div>\n\n <div class=\"footer\">\n <p>このレポートはAIによって自動生成されました。</p>\n </div>\n </div>\n</body>\n</html>\n`;\n\n// 3. 後続のGmailノードで使えるようにデータを返す\nreturn [{\n json: {\n subject: subject,\n htmlBody: htmlBody\n }\n}];"
},
"typeVersion": 2
},
{
"id": "ae0d956b-599d-4bba-b14b-67245e3287fb",
"name": "메시지 보내기1",
"type": "n8n-nodes-base.gmail",
"position": [
1408,
400
],
"webhookId": "b0128308-fba0-4291-996d-e38ac9edaed4",
"parameters": {
"sendTo": "t.minamig20@gmail.com",
"message": "={{ $json.htmlBody }}",
"options": {
"appendAttribution": false
},
"subject": "={{ $json.subject }}"
},
"credentials": {
"gmailOAuth2": {
"id": "qEtK64Ero6pOyPe0",
"name": "Gmail account 16"
}
},
"typeVersion": 2.1
},
{
"id": "9be8d7fb-d689-41bb-b638-755bddd7a618",
"name": "Sticky 1: 개요",
"type": "n8n-nodes-base.stickyNote",
"position": [
-16,
80
],
"parameters": {
"color": 5,
"width": 520,
"height": 260,
"content": "## 🗒️ Sticky 1: Overview\n**What this template does**\n- Accepts a **food image** and runs **AI-based ingredient inference** → **calorie estimation** → **concise summary**\n- Input can be an **image URL** or **Base64 image** (via Webhook or a messaging app)\n\n**Why it’s useful**\n- Quickly generates a calorie rough estimate and a short nutrition comment for logging, sharing, or feedback loops\n\n**No hardcoded secrets**\n- All API keys must be set via **Credentials/Environment Variables** (never hardcode keys in nodes)"
},
"typeVersion": 1
},
{
"id": "0bd3d0eb-a030-4d79-a390-17e41d705f7e",
"name": "Sticky 2: 필수 조건",
"type": "n8n-nodes-base.stickyNote",
"position": [
608,
-16
],
"parameters": {
"color": 4,
"width": 520,
"height": 280,
"content": "## 🗒️ Sticky 2: Prerequisites & Credentials\n**Connections**\n- LLM (OpenAI or compatible) with **vision** capability\n- One input channel (Webhook **or** Telegram/LINE, etc.)\n\n**Environment Variables (examples)**\n- `LLM_MODEL` (e.g., `gpt-4o` or any vision-capable model)\n- `LLM_TEMPERATURE` (e.g., `0.3`)\n- `WEBHOOK_SECRET` (optional, if you validate requests)\n\n**Scopes / Permissions**\n- If image URLs are external, ensure they are publicly accessible or use signed/temporary URLs"
},
"typeVersion": 1
},
{
"id": "ff6d1495-3223-4305-a354-57a22adf71f3",
"name": "Sticky 3: 입력 형식",
"type": "n8n-nodes-base.stickyNote",
"position": [
-144,
560
],
"parameters": {
"color": 2,
"width": 520,
"height": 300,
"content": "## 🗒️ Sticky 3: Input Format (Sample)\n**Expected JSON**\n```json\n{\n \"imageUrl\": \"https://example.com/dish.jpg\"\n // or\n // \"imageBase64\": \"data:image/jpeg;base64,....\"\n}\n```\n**Notes**\n- Template assumes **one image** per request (extend with arrays for multiple images)\n- If you receive platform-specific payloads (e.g., Telegram `file_id`, LINE message objects), **normalize** them first to `imageUrl` or `imageBase64` before calling the LLM"
},
"typeVersion": 1
},
{
"id": "1bd2ee7d-a5f9-42f4-9974-4698fb05b553",
"name": "Sticky 4: 모델 및 프롬프트",
"type": "n8n-nodes-base.stickyNote",
"position": [
528,
864
],
"parameters": {
"color": 3,
"width": 520,
"height": 260,
"content": "## 🗒️ Sticky 4: Model & Prompt Policy\n**Model**\n- Use a **vision-capable** LLM (e.g., `gpt-4o`)\n- Keep `temperature` low (0.2–0.3) for stable outputs\n\n**Prompt Rules**\n- The model must return **strict JSON only**, with the fields below (no extra text)\n- Keep names concise (e.g., \"grilled chicken\", \"white rice\", \"mixed salad\")\n- Provide amounts in grams when possible (estimates are fine)"
},
"typeVersion": 1
},
{
"id": "84f0f90c-e819-4022-a76e-b63353ca9968",
"name": "Sticky 5: 출력 스키마",
"type": "n8n-nodes-base.stickyNote",
"position": [
1184,
-16
],
"parameters": {
"width": 520,
"height": 280,
"content": "## 🗒️ Sticky 5: Output Schema (Fixed)\n**JSON structure**\n```json\n{\n \"dishName\": \"string\",\n \"ingredients\": [\n { \"name\": \"string\", \"amount\": 0, \"calories\": 0 }\n ],\n \"totalCalories\": 0,\n \"nutritionEvaluation\": \"string\"\n}\n```\n**Validation**\n- If fields are empty/missing, route to an **error branch** and return a helpful message for the user"
},
"typeVersion": 1
},
{
"id": "fe6e558b-4f6a-4c0a-9b06-e3939b07be3f",
"name": "Sticky 6: 테스트 단계",
"type": "n8n-nodes-base.stickyNote",
"position": [
1200,
816
],
"parameters": {
"color": 5,
"width": 520,
"height": 220,
"content": "## 🗒️ Sticky 6: Test Steps (Under 1 Minute)\n1) Turn on the **Webhook** node and copy the **Test URL**\n2) Send a sample payload with a valid `imageUrl`\n3) Confirm the run returns the **strict JSON** structure\n4) If using Telegram/Slack replies, authenticate and send a test message to verify delivery"
},
"typeVersion": 1
},
{
"id": "24bbd216-9c8b-4a4f-b446-dd82585117fd",
"name": "Sticky 7: 오류 및 제한사항",
"type": "n8n-nodes-base.stickyNote",
"position": [
1696,
0
],
"parameters": {
"color": 4,
"width": 520,
"height": 260,
"content": "## 🗒️ Sticky 7: Error Handling & Limits\n**Retries**\n- For LLM calls, implement **exponential backoff** on 429/5xx\n- If the image fetch fails, **retry**, then go to **error branch**\n\n**Limits**\n- Watch for model **context/vision limits**; downscale/thumbnail images if needed\n- When rate-limited, use **Delay** + **Retry** to recover gracefully"
},
"typeVersion": 1
},
{
"id": "55619913-3b70-43d8-ab38-f5b8eb4a6375",
"name": "Sticky 8: 보안",
"type": "n8n-nodes-base.stickyNote",
"position": [
1776,
784
],
"parameters": {
"color": 3,
"width": 520,
"height": 220,
"content": "## 🗒️ Sticky 8: Security & PII\n- Store API keys/tokens in **Credentials/Env Vars** (never in node fields)\n- If image URLs are private, use **signed/temporary URLs**\n- **Do not log** personal data; mask/remove PII from execution logs\n- Use **HTTPS only** for all external requests"
},
"typeVersion": 1
},
{
"id": "95833663-f776-40c5-9225-30f07e580acb",
"name": "Sticky 9: 전달",
"type": "n8n-nodes-base.stickyNote",
"position": [
2272,
0
],
"parameters": {
"color": 2,
"width": 520,
"height": 220,
"content": "## 🗒️ Sticky 9: Delivery Options (No Gmail)\n- Reply via **Telegram/Slack** or return via **HTTP Response**\n- Optionally log results to **Google Sheets** or **Notion** for history/analytics\n- If email is required, use **SMTP** or a non-Gmail provider (per your stack)"
},
"typeVersion": 1
},
{
"id": "d1f31505-89ed-4483-bb52-d1f98c02db36",
"name": "Sticky 10: 확장성",
"type": "n8n-nodes-base.stickyNote",
"position": [
1840,
400
],
"parameters": {
"width": 520,
"height": 240,
"content": "## 🗒️ Sticky 10: Extensibility (Review Bonus)\n- **Multilingual output** (ja/en) switch\n- Normalize serving units (piece/slice) → **gram conversion**\n- Categorize components (main dish / side / sauce)\n- Add **evidence-based** nutrition comments (e.g., high fat/high carb) with short rationale"
},
"typeVersion": 1
}
],
"active": false,
"pinData": {},
"settings": {
"timezone": "Asia/Tokyo",
"errorWorkflow": "",
"executionOrder": "v1",
"saveManualExecutions": true,
"saveExecutionProgress": true,
"saveDataErrorExecution": "all",
"saveDataSuccessExecution": "all"
},
"versionId": "8766f935-4d36-4d19-9f54-9179e0a59262",
"connections": {
"7397c3e2-c057-4f6b-9afa-010b0a610789": {
"main": [
[
{
"node": "ae0d956b-599d-4bba-b14b-67245e3287fb",
"type": "main",
"index": 0
}
]
]
},
"52bb2509-5870-4f4c-96ea-c607548b15b1": {
"main": [
[
{
"node": "agent1",
"type": "main",
"index": 0
}
]
]
},
"a8ee11fd-47bc-4524-89a5-a19380041613": {
"ai_languageModel": [
[
{
"node": "agent1",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"agent1": {
"main": [
[
{
"node": "7397c3e2-c057-4f6b-9afa-010b0a610789",
"type": "main",
"index": 0
}
]
]
},
"parser1": {
"ai_outputParser": [
[
{
"node": "agent1",
"type": "ai_outputParser",
"index": 0
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
고급
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
고객 설문 요약 생성기
AI, Google Sheets, Slack 보고서를 사용한 고객 설문 피드백 분석
Code
Slack
Google Sheets
+
Code
Slack
Google Sheets
16 노드Toshiya Minami
AI 마케팅 에이전트: Reddit + OpenRouter + Gmail 잠재고객 생성
AI 마케팅 에이전트: Reddit, OpenRouter 및 Gmail을 통한 잠재고객 생성
If
Set
Code
+
If
Set
Code
23 노드Ari Nakos
영업
AI 연구 에이전트: Mistral 최적 OCR을 사용한 PDF 자동 분석
AI 연구 에이전트: Mistral 최적 OCR을 사용한 PDF 자동 분석
Set
Code
Gmail
+
Set
Code
Gmail
30 노드Derek Cheung
인공지능
Danelfin, TwelveData 및 Alpha Vantage 기반 AI 주식 분석
Danelfin, TwelveData 및 Alpha Vantage 통합 AI 주식 분석 시스템
Set
Code
Gmail
+
Set
Code
Gmail
74 노드Paul
암호화폐 거래
자동화 X(Twitter) 콘텐츠 엔진
AI 기반 X(Twitter) 콘텐츠 생성 및 스케줄링(LangChain 및 Blotato)
If
Code
Gmail
+
If
Code
Gmail
24 노드YUSUKE YAMAMOTO
AI, Apify 및 Telegram 통합을 사용한 도구 평가 자동화
Telegram, Apify, AI 및 Google Sheets를 사용한 웹사이트 도구 분석 자동화
If
Set
Code
+
If
Set
Code
20 노드Mirza Ajmal
AI 요약