LinkedIn帖子生成器
高级
这是一个Content Creation、Multimodal AI领域的自动化工作流,包含 39 个节点。主要使用 Code、Merge、GoogleDrive、GoogleSheets、ScheduleTrigger 等节点。 LinkedIn内容工厂:使用GPT-5、DALL·E和Google Sheets自动生成帖子
前置要求
- •Google Drive API 凭证
- •Google Sheets API 凭证
- •OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "vnFLiG5oPRaa0GLT",
"meta": {
"instanceId": "4864679018d565a892ca43ce23dcbf870b964133cd1081846447be064da60377",
"templateCredsSetupCompleted": true
},
"name": "Linkedin 帖子生成器",
"tags": [],
"nodes": [
{
"id": "5d8195ac-0e4d-46a2-b215-e76088726704",
"name": "01_自动开始",
"type": "n8n-nodes-base.scheduleTrigger",
"position": [
-336,
96
],
"parameters": {
"rule": {
"interval": [
{
"triggerAtHour": 10
}
]
}
},
"typeVersion": 1.2
},
{
"id": "3606e466-c0e3-47e6-aac8-000cd2960fab",
"name": "02_构建简报",
"type": "n8n-nodes-base.code",
"position": [
-112,
96
],
"parameters": {
"jsCode": "return [{\n json: {\n brief: {\n audience: \"Product Managers, Senior Product Managers, founders, RMG players, startup & AI enthusiasts, Product Management enthusiasts, AI agent enthusiasts, AI product Management, AI workflow Enthusiasts, AI Productivity hackers\",\n geography: \"Global + India\",\n goals: [\"Reach/Awareness\", \"Authority building\", \"Personal branding\"],\n cta: [\"Comments\", \"Share\", \"Follow\"],\n topics: \"Anything relevant to target audience (startups, AI, fintech, gaming, RMG, etc.)\",\n angles: [\"Educational\", \"Contrarian\", \"Storytelling\", \"Framework-based\", \"Case study\"],\n tone: \"Sharp\",\n constraints: \"Avoid too much jargon, include data & sources\",\n link: null,\n avoid_duplication: false,\n trigger: \"cron\"\n }\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "d27e676c-fe72-41d0-ba06-64a31b0c2170",
"name": "03_生成想法",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
96,
96
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-5-chat-latest",
"cachedResultName": "GPT-5-CHAT-LATEST"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "You are an AI that generates LinkedIn post ideas based on a given brief."
},
{
"content": "=Audience: {{$json[\"brief\"][\"audience\"]}}\nGeography: {{$json[\"brief\"][\"geography\"]}}\nGoals: {{$json[\"brief\"][\"goals\"].join(\", \")}}\nCall-to-action: {{$json[\"brief\"][\"cta\"].join(\", \")}}\nTopics: {{$json[\"brief\"][\"topics\"]}}\nAngles: {{$json[\"brief\"][\"angles\"].join(\", \")}}\nTone: {{$json[\"brief\"][\"tone\"]}}\nConstraints: {{$json[\"brief\"][\"constraints\"]}}\n\nTask:\n- Give me 5 raw LinkedIn post ideas (short titles or one-line descriptions only).\n- Do not write the full post.\n- Make them relevant, engaging, and fit the brief.\n- Avoid repeating themes from the last 60 days.\n"
}
]
}
},
"credentials": {
"openAiApi": {
"id": "X9ubpsApQNgfKwnt",
"name": "OpenAi account 2"
}
},
"typeVersion": 1.8
},
{
"id": "2c5ad466-a960-421d-b65e-7343957d354b",
"name": "合并",
"type": "n8n-nodes-base.merge",
"position": [
-304,
512
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "e2fc1a59-8082-4b57-b907-62b6f9ef6ad4",
"name": "05_挑选想法AI",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
-144,
512
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-5",
"cachedResultName": "GPT-5"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "You are an editorial board for a LinkedIn thought-leadership account.\nYour job: pick ONE best idea from a shortlist of 3–8 ideas. \nYou must explain WHY and also RANK all candidates.\n\nConstraints:\n- Keep audience, geography, goals, tone, and constraints from the brief.\n- Prefer ideas with strong comment potential, data-backed, contrarian or framework-based angles.\n- Be very strict with clarity: avoid jargon-heavy picks.\n- Return STRICT JSON only, no extra text.\n"
},
{
"content": "=BRIEF:\n{{ JSON.stringify($item(0,\"02_BuildBrief\").$json.brief) }}\n\nCANDIDATE_IDEAS (after fuzzy-dedupe):\n{{ JSON.stringify($json.message.content.kept_fuzzy) }}\n\nTASK:\n1. Pick ONE best idea → \"chosenIdea\"\n2. Explain briefly why → \"why\"\n3. Rank ALL ideas with scores (1–10) and short notes → \"ranked\"\n\nOUTPUT SCHEMA:\n{\n \"chosenIdea\": \"<the one>\",\n \"why\": \"<reason>\",\n \"ranked\": [\n { \"idea\": \"<candidate>\", \"score\": <int>, \"note\": \"<short reason>\" }\n ]\n}\n"
}
]
}
},
"credentials": {
"openAiApi": {
"id": "X9ubpsApQNgfKwnt",
"name": "OpenAi account 2"
}
},
"typeVersion": 1.8
},
{
"id": "9aecf953-5e89-4434-b430-077ec3a598e1",
"name": "05b_合并简报和挑选",
"type": "n8n-nodes-base.merge",
"position": [
144,
512
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "6881cd94-a7ed-43a5-9824-9594a9631596",
"name": "06_生成帖子",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
496,
512
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-5",
"cachedResultName": "GPT-5"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "You are an expert LinkedIn content strategist who writes sharp, scroll-stopping posts for Product Managers, founders, and AI enthusiasts. \nYou always:\n- Write in a clear, concise, and engaging style.\n- Use strong hooks, frameworks, and storytelling.\n- Avoid jargon and filler words.\n- Support with credible data/examples when relevant.\n- End with a simple call-to-action (CTA).\n"
},
{
"content": "=Generate a LinkedIn post draft.\n\n### Inputs:\n- Chosen Idea: {{$json.chosenIdea}}\n- Why it was chosen: {{$json.why}}\n- Brief:\n - Audience: {{$json.brief.audience}}\n - Geography: {{$json.brief.geography}}\n - Goals: {{$json.brief.goals}}\n - CTA options: {{$json.brief.cta}}\n - Topics: {{$json.brief.topics}}\n - Angles: {{$json.brief.angles}}\n - Tone: {{$json.brief.tone}}\n - Constraints: {{$json.brief.constraints}}\n\n### Requirements:\n1. Start with a strong hook (1–2 lines).\n2. Expand into insight or framework (4-5 short paragraphs, max 20 lines each).\n3. Add supporting data/reference if relevant (keep sharp, not academic).\n4. End with a clear CTA (pick from the CTA options).\n5. Keep tone = {{$json.brief.tone}}.\n6. Avoid jargon, keep sentences punchy.\n\n\nReturn the output in JSON:\n{\n \"post\": \"final LinkedIn post draft here\"\n}\n"
}
]
}
},
"credentials": {
"openAiApi": {
"id": "X9ubpsApQNgfKwnt",
"name": "OpenAi account 2"
}
},
"typeVersion": 1.8
},
{
"id": "2038c6a7-a0a1-47b4-8f8c-07ecc9219f2c",
"name": "05a_解析已选想法",
"type": "n8n-nodes-base.code",
"position": [
320,
512
],
"parameters": {
"jsCode": "try {\n const raw = $json?.message?.content || \"{}\";\n const data = JSON.parse(raw); // content ke andar jo JSON string hai, use parse karte hain\n return [{\n json: {\n brief: $json.brief, // brief ko carry forward\n chosenIdea: data.chosenIdea, // TOP-LEVEL bana diya\n why: data.why,\n ranked: data.ranked\n }\n }];\n} catch (e) {\n return [{ json: { error: \"ParseError: content was not valid JSON\", detail: String(e), raw: $json?.message?.content } }];\n}\n"
},
"typeVersion": 2
},
{
"id": "f82d70ea-08fa-49db-8c88-dca6637ab547",
"name": "04_提取想法列表",
"type": "n8n-nodes-base.code",
"position": [
416,
96
],
"parameters": {
"jsCode": "// 1) Get the raw text from LLM\nconst raw = $json?.message?.content || \"\";\n\n// 2) Remove the meta lead-in and split lines\nlet lines = raw.split(\"\\n\")\n .map(l => l.trim())\n .filter(Boolean);\n\n// 3) Keep only likely idea lines (numbered items or bold lines)\n// e.g. \"1. **\\\"Why ...\\\"**\"\nconst ideaLines = lines.filter(l =>\n /^\\d+\\.\\s*/.test(l) || // numbered\n /^\\*\\*.+\\*\\*$/.test(l) // fully bold\n);\n\n// 4) Clean each line: strip numbering, bold, surrounding quotes, trailing punctuation\nfunction cleanOne(s){\n return s\n .replace(/^\\d+\\.\\s*/, \"\") // remove \"1. \"\n .replace(/^\\*\\*|\\*\\*$/g, \"\") // remove **bold**\n .replace(/^[-–•\\s]+/, \"\") // leading bullets/dashes\n .replace(/^\"+|\"+$/g, \"\") // leading/trailing quotes\n .replace(/[”“]/g, '\"') // smart quotes normalize\n .replace(/^\"|\"$/g, \"\") // quotes again (after normalize)\n .replace(/\\s{2,}/g, \" \") // collapse spaces\n .replace(/\\.*\\s*$/,\".\") // ensure single period end\n .trim();\n}\n\n// 5) Extract ideas; also catch cases where the text spans next line (ignore empty)\nlet ideas = ideaLines.map(cleanOne)\n .filter(i => i.length > 0);\n\n// 6) If we somehow got nothing (format changed), fallback: regex capture quoted chunks or numbered after the colon\nif (ideas.length === 0) {\n const numbered = [...raw.matchAll(/^\\s*\\d+\\.\\s*(.+)$/gm)].map(m => cleanOne(m[1]));\n const quoted = [...raw.matchAll(/\"([^\"]{10,})\"/g)].map(m => cleanOne(m[1]));\n ideas = (numbered.length ? numbered : quoted).filter(Boolean);\n}\n\n// 7) Deduplicate exacts and strip trailing doubled quotes if any\nideas = [...new Set(ideas.map(i => i.replace(/\"+$/,\"\").trim()))];\n\n// 8) Return as a single item\nreturn [{ json: { ideas } }];\n"
},
"typeVersion": 2
},
{
"id": "7a9e1242-f84c-4fc0-ba23-4fc3196479fe",
"name": "05_精确去重检查",
"type": "n8n-nodes-base.code",
"position": [
1264,
80
],
"parameters": {
"jsCode": "const past = items[0].json.pastIdeas.map(p => p.trim().toLowerCase());\nconst ideas = items[0].json.ideas;\n\n\nconst kept_exact = [];\nconst rejected_exact = [];\n\nfor (const idea of ideas) {\n if (past.includes(idea.trim().toLowerCase())) {\n rejected_exact.push(idea);\n } else {\n kept_exact.push(idea);\n }\n}\n\nreturn [{\n json: {\n kept_exact,\n rejected_exact,\n counts: {\n kept: kept_exact.length,\n rejected: rejected_exact.length,\n past\n }\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "8866dafc-c386-46ae-a75a-2402ecd35903",
"name": "02_读取过往想法",
"type": "n8n-nodes-base.googleSheets",
"position": [
656,
96
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1945kuHLUlrCDROQxRPb7vNZQ4mEXd67sLRNVIB9lFFA/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1945kuHLUlrCDROQxRPb7vNZQ4mEXd67sLRNVIB9lFFA",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1945kuHLUlrCDROQxRPb7vNZQ4mEXd67sLRNVIB9lFFA/edit?usp=drivesdk",
"cachedResultName": "Idea_Log"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "x2LQk3hNWfQ8t8iy",
"name": "Google Sheets account 3"
}
},
"typeVersion": 4.7
},
{
"id": "c606437f-5680-4691-adf3-1fbb17eaf9bf",
"name": "03_标准化过往想法",
"type": "n8n-nodes-base.code",
"position": [
864,
96
],
"parameters": {
"jsCode": "const pastIdeas = items.map(i => i.json.idea).filter(Boolean);\n\nreturn [{\n json: {\n pastIdeas\n }\n}];\n"
},
"typeVersion": 2
},
{
"id": "2db1b5aa-eb84-4291-a43a-60c3353affe3",
"name": "04.5_合并想法与过往想法",
"type": "n8n-nodes-base.merge",
"position": [
1088,
80
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "00f18696-842e-4261-ae0e-e24a07c9917f",
"name": "06_模糊去重",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1456,
80
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-5",
"cachedResultName": "GPT-5"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "You remove near-duplicate LinkedIn post ideas using semantic similarity. \nBe conservative: if uncertain, KEEP the idea.\nReturn STRICT JSON only, no extra text.\n"
},
{
"content": "=BRIEF (context only):\n{{ JSON.stringify($item(0,\"02_BuildBrief\").$json) }}\n\nCANDIDATE_IDEAS (after exact-dedupe):\n{{ JSON.stringify($json.kept_exact) }}\n\nPAST_IDEAS (from Idea_Log):\n{{ $json.counts.past }}\n\n\nRULES:\n- Mark an idea as duplicate only if it conveys the SAME core advice/angle as a past idea (not just same topic).\n- Prefer variety across angle (framework vs case study vs contrarian), audience (India + global), and metric focus.\n- If unsure, KEEP the idea (soft fuzzy dedupe).\n- Do NOT rewrite text; only classify.\n- Output STRICT JSON only.\n\nOUTPUT SCHEMA:\n{\n \"kept_fuzzy\": [\"<idea 1>\", \"<idea 2>\"],\n \"rejected_fuzzy\": [\n { \"idea\": \"<candidate duplicated>\", \"matched\": \"<closest past idea>\", \"reason\": \"near-duplicate (same angle/advice)\" }\n ]\n}\n"
}
]
},
"jsonOutput": true
},
"credentials": {
"openAiApi": {
"id": "X9ubpsApQNgfKwnt",
"name": "OpenAi account 2"
}
},
"typeVersion": 1.8
},
{
"id": "2cc8e18a-4f29-4456-802d-68bb1c5a829e",
"name": "10_提取发布包",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
1008,
528
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-5",
"cachedResultName": "GPT-5"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "You extract structured components from a LinkedIn draft. \nReturn STRICT JSON only. No extra text. \nPreserve facts/sources; do not invent.\n"
},
{
"content": "=INPUT DRAFT (JSON):\n{{ $json.message?.content || $json.post || \"{}\" }}\n\nBRIEF (for constraints; do not rewrite content):\n{{ JSON.stringify($item(0,\"08_ParseChosenIdea\").$json.brief) }}\n\nTASK:\n- HOOK: pick the strongest one-line opener from the draft (or rewrite lightly).\n- BODY: the cleaned post text (fix spacing only).\n- CTA: choose exactly one from {{ JSON.stringify($item(0,\"08_ParseChosenIdea\").$json.brief.cta) }} that best fits the draft ending.\n- HASHTAGS: suggest up to 8 relevant hashtags (start with #; audience/topic aligned).\n- FIRST_COMMENT: 1 line to invite discussion (optional).\n- CHAR_COUNT: integer = characters of BODY (not counting first_comment).\n\nOUTPUT (STRICT JSON):\n{\n \"hook\": \"<one line>\",\n \"body\": \"<full post text>\",\n \"cta\": \"<one of the allowed list>\",\n \"hashtags\": [\"#tag1\", \"#tag2\"],\n \"first_comment\": \"<optional, 0-140 chars>\",\n \"char_count\": 0\n}\nRULES:\n- Max 8 hashtags.\n- Keep numbers/sources exactly as in the draft.\n- No prose outside JSON.\n"
}
]
}
},
"credentials": {
"openAiApi": {
"id": "X9ubpsApQNgfKwnt",
"name": "OpenAi account 2"
}
},
"typeVersion": 1.8
},
{
"id": "fd3c747c-5164-403e-9ca3-9de40e7bfc9b",
"name": "合并1",
"type": "n8n-nodes-base.merge",
"position": [
832,
528
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "a1bff2e1-c584-4f05-983c-e93623947469",
"name": "合并2",
"type": "n8n-nodes-base.merge",
"position": [
1296,
528
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "4beb81f3-d5e0-44d9-b6b6-183314ca883c",
"name": "11_解析发布包",
"type": "n8n-nodes-base.code",
"position": [
1504,
528
],
"parameters": {
"jsCode": "try {\n // 1) Extract the JSON string from LLM output\n const raw = $json?.message?.content ?? \"{}\";\n const pack = (typeof raw === \"string\") ? JSON.parse(raw) : raw;\n\n // 2) Normalize fields\n const hook = (pack.hook || \"\").trim();\n const body = (pack.body || \"\").trim();\n const cta = (pack.cta || \"\").trim();\n const hashtags = Array.isArray(pack.hashtags) ? pack.hashtags : [];\n const first_comment = (pack.first_comment || \"\").trim();\n\n // 3) Attach brief from earlier node\n const brief = $item(0, \"08_ParseChosenIdea\")?.$json?.brief\n || $item(0, \"02_BuildBrief\")?.$json?.brief\n || {};\n\n // 4) Recompute char_count (hook + body as user sees on LinkedIn)\n const combined = (hook ? hook + \"\\n\\n\" : \"\") + body;\n const char_count = combined.length;\n\n return [{\n json: { hook, body, cta, hashtags, first_comment, char_count, brief }\n }];\n} catch (e) {\n return [{\n json: {\n error: \"ParseError_PublishPack\",\n detail: String(e),\n raw: $json?.message?.content ?? null\n }\n }];\n}\n"
},
"typeVersion": 2
},
{
"id": "c3536683-2e43-4920-a169-38ab9e338ce3",
"name": "12_具体性检查",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
-336,
896
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-5",
"cachedResultName": "GPT-5"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "You are a precision editor for LinkedIn posts. \nYour job is to add crisp specificity (examples, concrete details, brief citations) without bloating length or changing the core claim.\nFollow the brief’s tone. Preserve facts; never invent numbers.\nReturn STRICT JSON only. No extra text.\n"
},
{
"content": "=INPUT:\nHOOK:\n{{$json.hook}}\n\nBODY:\n{{$json.body}}\n\nBRIEF (context/constraints):\n{{ JSON.stringify($json.brief) }}\n\nRULES:\n- Keep the hook’s intent. You may tighten phrasing slightly.\n- Add 2–3 specific details/examples that strengthen the argument (company names, UX patterns, workflow examples, market contexts).\n- DO NOT add new numeric claims beyond what’s already in BODY. You may add source NAMES (e.g., NPCI, ACI, FIS) if they’re already referenced, but no new figures.\n- Prefer one India and one global example when relevant.\n- Keep sentences punchy; avoid jargon. Preserve bullets and spacing.\n- Net length change ≤ +50 words (target total 150–230 words including hook).\n- End with the existing CTA sentiment intact (don’t change intent).\n\nOUTPUT (STRICT JSON):\n{\n \"hook\": \"<tightened or original hook>\",\n \"body\": \"<revised body with added specificity>\",\n \"notes\": [\n \"What was clarified or made specific in 1–2 bullets\"\n ]\n}\n"
}
]
}
},
"credentials": {
"openAiApi": {
"id": "X9ubpsApQNgfKwnt",
"name": "OpenAi account 2"
}
},
"typeVersion": 1.8
},
{
"id": "b51b8336-95f0-498c-9997-73fd6c8f2d0a",
"name": "合并 3",
"type": "n8n-nodes-base.merge",
"position": [
-16,
896
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "391ac3da-c92c-4a5c-b2da-944af179c253",
"name": "13_声音一致性",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
336,
896
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-5",
"cachedResultName": "GPT-5"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "You are a voice refinement assistant.\nYour job is to take a draft post (hook, body, notes, brief) and adjust the language so it matches the user’s preferred voice:\n- Tone: Sharp, crisp, authoritative\n- Avoid heavy jargon; keep sentences short and punchy\n- Use simple, data-backed phrasing\n- Ensure scannability (short paragraphs, clean bullets if needed)\n\nDo NOT drop or alter the \"brief\" field.\nDo NOT remove citations, numbers, or data.\nAlways return the same JSON schema with {hook, body, notes, brief}.\n"
},
{
"content": "=INPUT_HOOK:\n{{ $json.hook }}\n\nINPUT_BODY:\n{{ $json.body }}\n\nNOTES_FOR_CONTEXT (optional):\n{{ JSON.stringify($json.notes || []) }}\n\nBRIEF (do not modify; include unchanged in output):\n{{ JSON.stringify($json.brief) }}\n\nTASK:\n- Adjust HOOK and BODY to match the voice above.\n- Keep all citations/numbers intact.\n- Improve rhythm and scannability (line breaks, bullets).\n- Do not add new claims or change meaning.\n- Output JSON only:\n{\n \"hook\": \"<refined one-liner>\",\n \"body\": \"<refined post body>\",\n \"notes\": {{ JSON.stringify($json.notes || []) }},\n \"brief\": {{ JSON.stringify($json.brief) }}\n}\n"
}
]
}
},
"credentials": {
"openAiApi": {
"id": "X9ubpsApQNgfKwnt",
"name": "OpenAi account 2"
}
},
"typeVersion": 1.8
},
{
"id": "ddb1f7e4-861f-4849-b228-50b695449e62",
"name": "12a_解析具体性JSON",
"type": "n8n-nodes-base.code",
"position": [
160,
896
],
"parameters": {
"jsCode": "try {\n const raw = $json?.message?.content ?? $json;\n const data = typeof raw === 'string' ? JSON.parse(raw) : (raw || {});\n return [{\n json: {\n hook: data.hook || \"\",\n body: data.body || \"\",\n notes: Array.isArray(data.notes) ? data.notes : [],\n brief: $json.brief || $item(0,\"11_ParsePublishPack\")?.$json?.brief || {}\n }\n }];\n} catch (e) {\n return [{ json: { error: \"ParseError_Specificity\", detail: String(e), raw: $json?.message?.content || null } }];\n}\n"
},
"typeVersion": 2
},
{
"id": "ef02a5d6-5e50-4f22-aef9-41785fe92290",
"name": "13a_解析声音JSON",
"type": "n8n-nodes-base.code",
"position": [
656,
896
],
"parameters": {
"jsCode": "try {\n const raw = $json?.message?.content ?? $json;\n const data = typeof raw === 'string' ? JSON.parse(raw) : (raw || {});\n return [{\n json: {\n hook: data.hook || \"\",\n body: data.body || \"\",\n notes: Array.isArray(data.notes) ? data.notes : [],\n brief: data.brief || {}\n }\n }];\n} catch (e) {\n return [{\n json: { error: \"ParseError_VoiceConformity\", detail: String(e), raw: $json?.message?.content || null }\n }];\n}\n"
},
"typeVersion": 2
},
{
"id": "b13a9c53-e561-4b10-8062-58a4c65260ee",
"name": "14_构建CTA和话题标签_LLM",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
880,
896
],
"parameters": {
"modelId": {
"__rl": true,
"mode": "list",
"value": "gpt-4o-mini",
"cachedResultName": "GPT-4O-MINI"
},
"options": {},
"messages": {
"values": [
{
"role": "system",
"content": "You build CTA + hashtags + an optional first comment for a LinkedIn post.\nBe deterministic and rule-following.\n\nRules:\n- Choose exactly ONE CTA from the allowed list provided in \"brief.cta\".\n- Generate up to 8 hashtags. Short, relevant, no spaces, no emojis. Use PascalCase or established tags (e.g., #ProductManagement, #UPI). No duplicates.\n- First comment: 1 line, ≤ 140 characters, invites discussion (optional but preferred).\n- Compute char_count for the final visible text = length of:\n <HOOK>[two newlines]<BODY>\n- Preserve facts and numbers from the body; do not add new numeric claims.\n- Return STRICT JSON only. No prose.\n- Do NOT rewrite hook or body; they will be merged downstream.\n"
},
{
"content": "=INPUT:\n{\n \"hook\": \"{{$json.hook}}\",\n \"body\": \"{{$json.body}}\",\n \"brief\": {{ JSON.stringify($json.brief) }}\n}\n\nTASK:\n1) Pick \"cta\" from brief.cta exactly (e.g., \"Comments\", \"Share\", or \"Follow\").\n2) Create \"hashtags\": max 8, relevant to the post and audience, PascalCase when possible, no spaces, no emojis, no duplicates.\n3) Write a concise \"first_comment\" (≤140 chars) that invites discussion; avoid repeating the hook verbatim.\n4) Compute \"char_count\" = character length of:\n HOOK + \"\\n\\n\" + BODY\n (count every character; do not include first_comment in this count).\n5) Output JSON ONLY in this schema:\n\n{\n \"cta\": \"Comments\",\n \"hashtags\": [\"#AI\", \"#ProductManagement\"],\n \"first_comment\": \"Your one-line discussion prompt here...\",\n \"char_count\": 1234\n}\n"
}
]
}
},
"credentials": {
"openAiApi": {
"id": "X9ubpsApQNgfKwnt",
"name": "OpenAi account 2"
}
},
"typeVersion": 1.8
},
{
"id": "afb9ea3a-1c87-4e58-be14-ae140c074748",
"name": "合并4",
"type": "n8n-nodes-base.merge",
"position": [
1264,
912
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "cfc7e722-2b69-44ae-a42e-6fc99fd304fc",
"name": "14b_塑造合并包",
"type": "n8n-nodes-base.code",
"position": [
1504,
912
],
"parameters": {
"jsCode": "try {\n // 1) Bring forward core fields (from Input A of Merge)\n const hook = ($json.hook || \"\").trim();\n const body = ($json.body || \"\").trim();\n const notes = Array.isArray($json.notes) ? $json.notes : [];\n const brief = $json.brief || {};\n\n // 2) Parse LLM pack (from Input B of Merge)\n let llmPack = {};\n if ($json?.message?.content) {\n llmPack = JSON.parse($json.message.content);\n } else {\n // if your LLM ever returns plain JSON at top-level\n llmPack = $json;\n }\n\n // 3) Normalize CTA against allowed list from brief\n const allowedCTAs = Array.isArray(brief.cta) && brief.cta.length ? brief.cta : [\"Comments\",\"Share\",\"Follow\"];\n let cta = (llmPack.cta || \"\").trim();\n if (!allowedCTAs.includes(cta)) cta = allowedCTAs[0];\n\n // 4) Normalize hashtags: #prefix, dedupe, cap 8\n let hashtags = Array.isArray(llmPack.hashtags) ? llmPack.hashtags : [];\n hashtags = [...new Set(hashtags.map(h => \"#\" + h.replace(/^#/, \"\").replace(/\\s+/g, \"\")))].slice(0, 8);\n\n // 5) First comment\n const first_comment = (llmPack.first_comment || \"\").trim();\n\n // 6) Deterministic visible char count (hook + body only)\n const char_count = ((hook ? hook + \"\\n\\n\" : \"\") + body).length;\n\n return [{\n json: {\n hook,\n body,\n notes,\n cta,\n hashtags,\n first_comment,\n char_count,\n brief\n }\n }];\n} catch (e) {\n return [{\n json: { error: \"ShapeError_14b\", detail: String(e), raw: $json?.message?.content || null }\n }];\n}\n"
},
"typeVersion": 2
},
{
"id": "62b0dd6b-e736-456e-8cbb-cd27f721cbbe",
"name": "15_互动健康度",
"type": "n8n-nodes-base.code",
"position": [
-320,
1248
],
"parameters": {
"jsCode": "try {\n const hook = ($json.hook || \"\").trim();\n let body = ($json.body || \"\").trim();\n const notes = Array.isArray($json.notes) ? $json.notes : [];\n const brief = $json.brief || {};\n let cta = ($json.cta || \"Comments\").trim();\n let hashtags = Array.isArray($json.hashtags) ? $json.hashtags.slice(0, 32) : [];\n let first_comment = ($json.first_comment || \"\").trim();\n\n // -------------------------\n // 1) Normalize spacing & punctuation\n // -------------------------\n // Collapse 3+ newlines to 2\n body = body.replace(/\\r/g, \"\")\n .replace(/\\n{3,}/g, \"\\n\\n\")\n .replace(/[ \\t]+\\n/g, \"\\n\");\n\n // Fix spaced hyphenated words: \"AI- native\" -> \"AI-native\"\n // Avoid bullet lines that begin with \"- \"\n body = body.split(\"\\n\").map(line => {\n if (/^\\s*-\\s/.test(line)) return line; // keep bullets as-is for now\n return line.replace(/\\b([A-Za-z0-9]+)\\s*-\\s*([A-Za-z0-9]+)\\b/g, \"$1-$2\");\n }).join(\"\\n\");\n\n // -------------------------\n // 2) Bullet hygiene & scannability\n // -------------------------\n // Ensure \"- \" (hyphen + space) bullets\n body = body.replace(/^\\s*-\\s*/gm, \"- \");\n // Add a blank line before and after bullet blocks\n body = body.replace(/([^\\n])\\n(- .+(?:\\n- .+)+)/g, \"$1\\n\\n$2\")\n .replace(/((?:- .+(?:\\n|$))+)(\\S)/g, \"$1\\n$2\");\n\n // -------------------------\n // 3) Ensure a question if CTA = Comments\n // -------------------------\n const endsWithQuestion = /\\?\\s*$/.test(body.trim());\n if (cta === \"Comments\" && !endsWithQuestion) {\n // If last 2 lines already contain a question, skip\n const tail = body.split(\"\\n\").slice(-3).join(\" \");\n if (!/\\?/.test(tail)) {\n body = body.trim() + \"\\n\\nWhat do you think?\";\n }\n }\n\n // -------------------------\n // 4) Hashtag hygiene (dedupe, cap 8, casing)\n // -------------------------\n // Preferred casing map for common tags\n const preferred = {\n \"ai\": \"AI\",\n \"upi\": \"UPI\",\n \"productmanagement\": \"ProductManagement\",\n \"productstrategy\": \"ProductStrategy\",\n \"a iagents\": \"AIAgents\", // guard against odd spacing\n \"aiagents\": \"AIAgents\",\n \"indiatch\": \"IndiaTech\",\n \"indiatech\": \"IndiaTech\",\n \"distribution\": \"Distribution\",\n \"growth\": \"Growth\",\n \"fintech\": \"Fintech\",\n \"digitalpayments\": \"DigitalPayments\",\n \"developerexperience\": \"DeveloperExperience\",\n \"platformstrategy\": \"PlatformStrategy\",\n \"startups\": \"Startups\",\n \"innovation\": \"Innovation\",\n \"workflows\": \"Workflows\",\n };\n\n const toPascal = (s) => s\n .split(/[^A-Za-z0-9]+/).filter(Boolean)\n .map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())\n .join(\"\");\n\n hashtags = hashtags\n .map(h => String(h).trim())\n .filter(Boolean)\n .map(h => h.replace(/^#/, \"\")) // strip leading #\n .map(h => h.replace(/\\s+/g, \"\")) // no spaces\n .map(h => preferred[h.toLowerCase()] || toPascal(h)) // apply preferred casing or PascalCase\n .map(h => \"#\" + h)\n .filter((h, i, arr) => arr.indexOf(h) === i) // dedupe\n .slice(0, 8); // cap 8\n\n // -------------------------\n // 5) First comment fallback (optional)\n // -------------------------\n if (!first_comment) {\n first_comment = (cta === \"Comments\")\n ? \"What’s one rail that would make your AI unavoidable this quarter?\"\n : \"Follow for more product + AI breakdowns.\";\n }\n\n // -------------------------\n // 6) Recompute char_count (visible: hook + body)\n // -------------------------\n const char_count = ((hook ? hook + \"\\n\\n\" : \"\") + body).length;\n\n return [{\n json: {\n hook,\n body,\n notes,\n cta,\n hashtags,\n first_comment,\n char_count,\n brief\n }\n }];\n} catch (e) {\n return [{\n json: {\n error: \"EngagementHygieneError\",\n detail: String(e),\n raw: $json\n }\n }];\n}\n"
},
"typeVersion": 2
},
{
"id": "53d3ea8a-56d2-469c-9410-8d858f2b0e8e",
"name": "16_格式合规性",
"type": "n8n-nodes-base.code",
"position": [
-96,
1248
],
"parameters": {
"jsCode": "// Cleans duplicates of the hook from body, enforces LinkedIn limits, and returns a compliant pack.\n\nfunction smartTrim(text, limit) {\n if (text.length <= limit) return text;\n const cut = text.slice(0, limit);\n // Try to end at a sentence or line boundary\n const lastStop = Math.max(\n cut.lastIndexOf(\". \"),\n cut.lastIndexOf(\"! \"),\n cut.lastIndexOf(\"? \"),\n cut.lastIndexOf(\"\\n\")\n );\n if (lastStop > 40) return cut.slice(0, lastStop + 1).trim(); // keep a sensible boundary\n return cut.trimEnd() + \"…\";\n}\n\nreturn items.map(item => {\n const data = item.json || {};\n const MAX_CHARS = 3000; // LinkedIn hard limit\n const hook = (data.hook || \"\").trim();\n let body = (data.body || \"\").replace(/\\r/g, \"\");\n let hashtags = Array.isArray(data.hashtags) ? data.hashtags : [];\n const cta = (data.cta || \"\").trim();\n const first_comment = (data.first_comment || \"\").trim();\n const brief = data.brief || {};\n const notes = Array.isArray(data.notes) ? data.notes : [];\n\n // 1) Remove duplicate hook lines from the start of body\n const hookNorm = hook.trim();\n let lines = body.split(\"\\n\");\n while (lines.length && lines[0].trim() === hookNorm) lines.shift(); // remove repeated hook(s)\n while (lines.length && lines[0].trim() === \"\") lines.shift(); // remove leading blank lines\n body = lines.join(\"\\n\").trim();\n\n // 2) Build visible text = hook + blank line + body\n // (always show hook at top once)\n const visiblePrefix = hook ? hook + \"\\n\\n\" : \"\";\n // enforce max by trimming ONLY the body portion\n const allowedBodyLimit = Math.max(0, MAX_CHARS - visiblePrefix.length);\n const bodyTrimmed = smartTrim(body, allowedBodyLimit);\n\n const visible = visiblePrefix + bodyTrimmed;\n\n // 3) Hashtag cap (10), normalize # and dedupe\n hashtags = [...new Set(\n hashtags\n .map(h => \"#\" + String(h || \"\").replace(/^#/, \"\").replace(/\\s+/g, \"\"))\n )].slice(0, 10);\n\n // 4) Final char count of visible text\n const char_count = visible.length;\n\n return {\n json: {\n hook, // unchanged\n body: bodyTrimmed, // cleaned & possibly trimmed\n notes,\n cta,\n hashtags,\n first_comment,\n char_count,\n brief\n }\n };\n});\n"
},
"typeVersion": 2
},
{
"id": "401c8de0-761f-49d0-b3a0-0004f54f5651",
"name": "生成图像",
"type": "@n8n/n8n-nodes-langchain.openAi",
"position": [
128,
1248
],
"parameters": {
"model": "gpt-image-1",
"prompt": "={\n \"prompt\": \"Create a LinkedIn-style social graphic.\\n\\nUse this headline EXACTLY: \\\"{{$json.hook}}\\\".\\n\\nFrom the BODY below, infer 2–3 core concepts (nouns/themes) and represent them with simple, universal flat icons/illustrations (no brand logos). Prefer tech/product metaphors (e.g., phone, graph, gears, database, API plug, search, document, bolt, shield) ONLY if they match the body themes.\\n\\nBODY:\\n{{$json.body}}\\n\\nLayout:\\n- Large bold headline at top-left.\\n- Under headline, one short tagline derived from the body (≤120 chars). Do NOT paste full paragraphs.\\n- Beside/below the text, place the 2–3 icons you inferred. Arrange them in a clear left→right story flow.\\n\\nStyle:\\n- Clean, modern, minimalist, LinkedIn-friendly.\\n- Background: dark (#0B0F15) with subtle gradient; typography in white/light grey.\\n- Accent palette (use sparingly): blue #4A90E2, green #7ED321.\\n- Mix text + visuals (NOT plain text poster).\\n\\nFooter:\\n- Add a small, subtle footer strip with hashtags: {{ Array.isArray($json.hashtags) ? $json.hashtags.slice(0,3).join(' ') : '' }}\\n\\nConstraints:\\n- No faces/people. No brand marks or watermarks. High contrast, legible at mobile feed size.\",\n \"size\": \"1024x1024\",\n \"n\": 1\n}\n",
"options": {},
"resource": "image"
},
"credentials": {
"openAiApi": {
"id": "X9ubpsApQNgfKwnt",
"name": "OpenAi account 2"
}
},
"typeVersion": 1.8
},
{
"id": "2f764832-1f2d-494c-ae7e-016f3da41618",
"name": "便签",
"type": "n8n-nodes-base.stickyNote",
"position": [
-624,
16
],
"parameters": {
"color": 3,
"width": 2544,
"height": 288,
"content": "## 想法生成及与已发布帖子的去重"
},
"typeVersion": 1
},
{
"id": "e10e205e-e5fc-4c1f-9df3-8a8605f977af",
"name": "便签1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-624,
400
],
"parameters": {
"color": 4,
"width": 2544,
"height": 288,
"content": "## 基于想法的帖子生成"
},
"typeVersion": 1
},
{
"id": "a664f3a1-65ed-412c-a94e-ba9556f6395c",
"name": "便签 2",
"type": "n8n-nodes-base.stickyNote",
"position": [
-624,
816
],
"parameters": {
"color": 6,
"width": 2544,
"height": 288,
"content": "## 想法润色、添加CTA和话题标签"
},
"typeVersion": 1
},
{
"id": "3cc14f12-fd29-46b2-878a-e653b7616510",
"name": "便签 3",
"type": "n8n-nodes-base.stickyNote",
"position": [
-624,
1168
],
"parameters": {
"width": 2528,
"height": 288,
"content": "## 图片生成、最终帖子准备就绪并更新 Google 表格和 Google Drive"
},
"typeVersion": 1
},
{
"id": "12dc9885-c076-4cb3-b178-b718ef116840",
"name": "合并5",
"type": "n8n-nodes-base.merge",
"position": [
320,
1264
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "d4420fe7-8b84-456f-bc3c-7da1b0f6974e",
"name": "18_发布打包器",
"type": "n8n-nodes-base.code",
"position": [
480,
1264
],
"parameters": {
"jsCode": "// Expecting merged item with text JSON + binary.image\nconst j = $json || {};\nconst b = $binary?.image || $binary?.data || null;\n\nif (!j.hook && !j.body) {\n throw new Error('No post fields found. Ensure Merge node combines 16_FormatCompliance (JSON) with 17_GenerateImage (binary).');\n}\nif (!b) {\n throw new Error('No image binary found. In Image node set Binary Property = \"image\" (or \"data\") and wire it into the Merge.');\n}\n\n// Normalize\nconst hashtags = Array.isArray(j.hashtags) ? j.hashtags : (j.hashtags ? String(j.hashtags).split(/\\s+/) : []);\nconst finalPost = `\n${j.hook || ''}\n\n${j.body || ''}\n\n${hashtags.length ? hashtags.join(' ') : ''}${j.cta ? `\\n\\n${j.cta}` : ''}\n`.trim();\n\nreturn [{\n json: {\n ...j,\n final_post: finalPost,\n image_provider: 'image_gen_node',\n image_filename: b.fileName || 'post.png',\n image_mime: b.mimeType || 'image/png',\n image_size: b.fileSize || '',\n image_binary_id: b.id || ''\n },\n binary: { image: b }\n}];\n"
},
"typeVersion": 2
},
{
"id": "6635de34-cf6c-4bd2-ac54-3aa932f510f1",
"name": "上传文件",
"type": "n8n-nodes-base.googleDrive",
"position": [
672,
1264
],
"parameters": {
"name": "={{ $now.toFormat(\"yyyyLLdd_HHmmss\") + \"_\" + $json[\"hook\"].replace(/[^a-zA-Z0-9]/g,\"_\").slice(0,30) + \".png\" }}",
"driveId": {
"__rl": true,
"mode": "list",
"value": "My Drive",
"cachedResultUrl": "https://drive.google.com/drive/my-drive",
"cachedResultName": "My Drive"
},
"options": {},
"folderId": {
"__rl": true,
"mode": "list",
"value": "1M8-qn3kL5P0Fb7MZllIL3KL2jVxROFkm",
"cachedResultUrl": "https://drive.google.com/drive/folders/1M8-qn3kL5P0Fb7MZllIL3KL2jVxROFkm",
"cachedResultName": "Linkedin Post Generator"
},
"inputDataFieldName": "=image"
},
"credentials": {
"googleDriveOAuth2Api": {
"id": "4wtxqZRczqngQF8G",
"name": "Google Drive account"
}
},
"typeVersion": 3
},
{
"id": "c3afa08f-4402-4455-a99d-a8447a7483ce",
"name": "18b_构建表格行",
"type": "n8n-nodes-base.code",
"position": [
1072,
1264
],
"parameters": {
"jsCode": "// 18b_BuildSheetRow\n\nconst rows = [];\n\nfor (const item of items) {\n const x = item.json;\n\n // === IDEA (dedupe safety) ===\n const pickSentence = (txt) => {\n if (!txt) return '';\n const s = txt.split(/[.?!]/)[0];\n return s.length > 100 ? s.slice(0, 100) : s;\n };\n\n const ideaTextRaw =\n x.locked_idea ||\n x.chosenIdea ||\n x.idea ||\n pickSentence(x.hook) ||\n pickSentence(x.body) ||\n pickSentence(x.final_post);\n\n const idea = (ideaTextRaw || '').trim();\n const ideaSlug = idea.toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n\n // === Hash for exact dedupe ===\n const crypto = require('crypto');\n const ideaHash = crypto.createHash('md5').update(idea).digest('hex');\n\n // === Timestamps ===\n const tsISO = new Date().toISOString();\n const tsIST = new Date().toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' });\n\n // === Sheet Row Object ===\n rows.push({\n timestamp_iso: tsISO,\n timestamp_ist: tsIST,\n\n post_id: x.id || '',\n\n idea,\n idea_slug: ideaSlug,\n idea_hash: ideaHash,\n\n hook: x.hook || '',\n body: x.body || '',\n cta: x.cta || '',\n hashtags: Array.isArray(x.hashtags) ? x.hashtags.join(' ') : (x.hashtags || ''),\n first_comment: x.first_comment || '',\n final_post: x.final_post || '',\n\n notes: Array.isArray(x.notes) ? x.notes.join(' | ') : (x.notes || ''),\n\n image_url: x.webViewLink || x.webContentLink || '',\n image_filename: x.image_filename || x.name || '',\n image_mime: x.image_mime || x.mimeType || '',\n image_size: x.image_size || x.fileSize || '',\n\n // Extra debug/meta\n image_binary_id: x.image_binary_id || '',\n provider: x.image_provider || '',\n });\n}\n\nreturn rows.map(r => ({ json: r }));\n"
},
"typeVersion": 2
},
{
"id": "855dfa96-f231-4f3c-8272-c1ce195eab59",
"name": "合并6",
"type": "n8n-nodes-base.merge",
"position": [
864,
1264
],
"parameters": {
"mode": "combine",
"options": {},
"combineBy": "combineByPosition"
},
"typeVersion": 3.2
},
{
"id": "a34b9b18-bbff-4ace-a89f-c7a057f7c3a6",
"name": "19_表格追加",
"type": "n8n-nodes-base.googleSheets",
"position": [
1280,
1264
],
"parameters": {
"columns": {
"value": {},
"schema": [
{
"id": "timestamp_ist",
"type": "string",
"display": true,
"required": false,
"displayName": "timestamp_ist",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "post_id",
"type": "string",
"display": true,
"required": false,
"displayName": "post_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "hook",
"type": "string",
"display": true,
"required": false,
"displayName": "hook",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "body",
"type": "string",
"display": true,
"required": false,
"displayName": "body",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "cta",
"type": "string",
"display": true,
"required": false,
"displayName": "cta",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "hashtags",
"type": "string",
"display": true,
"required": false,
"displayName": "hashtags",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "first_comment",
"type": "string",
"display": true,
"required": false,
"displayName": "first_comment",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "image_url",
"type": "string",
"display": true,
"required": false,
"displayName": "image_url",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "image_filename",
"type": "string",
"display": true,
"required": false,
"displayName": "image_filename",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "notes",
"type": "string",
"display": true,
"required": false,
"displayName": "notes",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "timestamp_iso",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "timestamp_iso",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "idea",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "idea",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "idea_slug",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "idea_slug",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "idea_hash",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "idea_hash",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "final_post",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "final_post",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "image_mime",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "image_mime",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "image_size",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "image_size",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "image_binary_id",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "image_binary_id",
"defaultMatch": false,
"canBeUsedToMatch": true
},
{
"id": "provider",
"type": "string",
"display": true,
"removed": false,
"required": false,
"displayName": "provider",
"defaultMatch": false,
"canBeUsedToMatch": true
}
],
"mappingMode": "autoMapInputData",
"matchingColumns": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
},
"options": {},
"operation": "appendOrUpdate",
"sheetName": {
"__rl": true,
"mode": "list",
"value": "gid=0",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1945kuHLUlrCDROQxRPb7vNZQ4mEXd67sLRNVIB9lFFA/edit#gid=0",
"cachedResultName": "Sheet1"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "1945kuHLUlrCDROQxRPb7vNZQ4mEXd67sLRNVIB9lFFA",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/1945kuHLUlrCDROQxRPb7vNZQ4mEXd67sLRNVIB9lFFA/edit?usp=drivesdk",
"cachedResultName": "Idea_Log"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "x2LQk3hNWfQ8t8iy",
"name": "Google Sheets account 3"
}
},
"typeVersion": 4.7
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "23358699-7458-4693-940e-06895ad60da1",
"connections": {
"Merge": {
"main": [
[
{
"node": "05_PickIdeaAI",
"type": "main",
"index": 0
}
]
]
},
"Merge1": {
"main": [
[
{
"node": "10_ExtractPublishPack",
"type": "main",
"index": 0
}
]
]
},
"Merge2": {
"main": [
[
{
"node": "11_ParsePublishPack",
"type": "main",
"index": 0
}
]
]
},
"Merge3": {
"main": [
[
{
"node": "12a_ParseSpecificityJSON",
"type": "main",
"index": 0
}
]
]
},
"Merge4": {
"main": [
[
{
"node": "14b_ShapeMergedPack",
"type": "main",
"index": 0
}
]
]
},
"Merge5": {
"main": [
[
{
"node": "18_PublishPackager",
"type": "main",
"index": 0
}
]
]
},
"Merge6": {
"main": [
[
{
"node": "18b_BuildSheetRow",
"type": "main",
"index": 0
}
]
]
},
"Upload file": {
"main": [
[
{
"node": "Merge6",
"type": "main",
"index": 0
}
]
]
},
"01_AutoStart": {
"main": [
[
{
"node": "02_BuildBrief",
"type": "main",
"index": 0
}
]
]
},
"02_BuildBrief": {
"main": [
[
{
"node": "03_GenerateIdea",
"type": "main",
"index": 0
},
{
"node": "Merge",
"type": "main",
"index": 1
},
{
"node": "05b_MergeBriefAndPick",
"type": "main",
"index": 1
},
{
"node": "Merge1",
"type": "main",
"index": 1
},
{
"node": "Merge2",
"type": "main",
"index": 1
},
{
"node": "Merge3",
"type": "main",
"index": 1
}
]
]
},
"05_PickIdeaAI": {
"main": [
[
{
"node": "05b_MergeBriefAndPick",
"type": "main",
"index": 0
}
]
]
},
"03_GenerateIdea": {
"main": [
[
{
"node": "04_ExtractIdeasList",
"type": "main",
"index": 0
}
]
]
},
"06_GeneratePost": {
"main": [
[
{
"node": "Merge1",
"type": "main",
"index": 0
}
]
]
},
"02_ReadPastIdeas": {
"main": [
[
{
"node": "03_NormalizePastIdeas",
"type": "main",
"index": 0
}
]
]
},
"18b_BuildSheetRow": {
"main": [
[
{
"node": "19_Sheets Append",
"type": "main",
"index": 0
}
]
]
},
"Generate an image": {
"main": [
[
{
"node": "Merge5",
"type": "main",
"index": 0
}
]
]
},
"12_SpecificityPass": {
"main": [
[
{
"node": "Merge3",
"type": "main",
"index": 0
}
]
]
},
"13_VoiceConformity": {
"main": [
[
{
"node": "13a_ParseVoiceJSON",
"type": "main",
"index": 0
}
]
]
},
"13a_ParseVoiceJSON": {
"main": [
[
{
"node": "14_BuildCTAHashtags_LLM",
"type": "main",
"index": 0
},
{
"node": "Merge4",
"type": "main",
"index": 1
}
]
]
},
"18_PublishPackager": {
"main": [
[
{
"node": "Upload file",
"type": "main",
"index": 0
},
{
"node": "Merge6",
"type": "main",
"index": 1
}
]
]
},
"04_ExtractIdeasList": {
"main": [
[
{
"node": "02_ReadPastIdeas",
"type": "main",
"index": 0
},
{
"node": "04.5_MergeIdeasPastIdeas",
"type": "main",
"index": 1
}
]
]
},
"05_ExactDedupeCheck": {
"main": [
[
{
"node": "06_FuzzyDeduplication",
"type": "main",
"index": 0
}
]
]
},
"05a_ParsePickedIdea": {
"main": [
[
{
"node": "06_GeneratePost",
"type": "main",
"index": 0
}
]
]
},
"11_ParsePublishPack": {
"main": [
[
{
"node": "12_SpecificityPass",
"type": "main",
"index": 0
}
]
]
},
"14b_ShapeMergedPack": {
"main": [
[
{
"node": "15_EngagementHygiene",
"type": "main",
"index": 0
}
]
]
},
"16_FormatCompliance": {
"main": [
[
{
"node": "Generate an image",
"type": "main",
"index": 0
},
{
"node": "Merge5",
"type": "main",
"index": 1
}
]
]
},
"15_EngagementHygiene": {
"main": [
[
{
"node": "16_FormatCompliance",
"type": "main",
"index": 0
}
]
]
},
"03_NormalizePastIdeas": {
"main": [
[
{
"node": "04.5_MergeIdeasPastIdeas",
"type": "main",
"index": 0
}
]
]
},
"05b_MergeBriefAndPick": {
"main": [
[
{
"node": "05a_ParsePickedIdea",
"type": "main",
"index": 0
}
]
]
},
"06_FuzzyDeduplication": {
"main": [
[
{
"node": "Merge",
"type": "main",
"index": 0
}
]
]
},
"10_ExtractPublishPack": {
"main": [
[
{
"node": "Merge2",
"type": "main",
"index": 0
}
]
]
},
"14_BuildCTAHashtags_LLM": {
"main": [
[
{
"node": "Merge4",
"type": "main",
"index": 0
}
]
]
},
"04.5_MergeIdeasPastIdeas": {
"main": [
[
{
"node": "05_ExactDedupeCheck",
"type": "main",
"index": 0
}
]
]
},
"12a_ParseSpecificityJSON": {
"main": [
[
{
"node": "13_VoiceConformity",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
这是一个高级难度的工作流,适用于Content Creation、Multimodal AI等场景。适合高级用户,包含 16+ 个节点的复杂工作流
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
AI驱动视频创作与上传至Instagram、TikTok和YouTube
从云端硬盘进行AI驱动视频创作并上传至Instagram、TikTok和YouTube
If
Set
Code
+14
53 节点DevCode Journey
Content Creation
批量SEO内容生成与带AI图片的Webflow草稿创建(模板)
使用GPT、Gemini图片和Webflow草稿创建进行批量SEO内容生成
If
Set
Code
+18
54 节点Dahiana
Content Creation
使用OpenAI、ElevenLabs和Hedra创建病毒式婴儿播客YouTube短视频
使用OpenAI、ElevenLabs和Hedra创建病毒式婴儿播客YouTube短视频
Code
Wait
Merge
+12
38 节点Electrabot
Content Creation
使用AI、Hedra和ElevenLabs生成病毒式婴儿名人播客
使用AI、Hedra和ElevenLabs生成病毒式婴儿名人播客
Code
Wait
Merge
+11
35 节点LeeWei
Content Creation
带有 Chatgpt Agent Prompt 检查的信用卡
AI驱动信用卡推荐系统,集成OpenAI GPT、Telegram和Google Sheets
If
Set
Code
+7
31 节点Nishant
AI Chatbot
使用Dumpling AI从YouTube视频自动生成平台特定帖子
使用GPT-4o和Dumpling AI从YouTube视频自动生成Instagram、Facebook和LinkedIn帖子
Set
Code
Merge
+6
20 节点Yang
Content Creation