{ "id": "spintax-product-copy-pool", "name": "Generate a pool of unique product descriptions from one AI-written spintax template", "nodes": [ { "parameters": {}, "id": "pool-trigger", "name": "Start a pool run", "type": "n8n-nodes-base.manualTrigger", "typeVersion": 1, "position": [ 0, 260 ] }, { "parameters": { "assignments": { "assignments": [ { "id": "pool-brief-value", "name": "brief", "type": "string", "value": "%product% is a trail-running shoe. Write a product description of four sentences for it, made by %brand% and aimed at %audience%. Name %feature% and end with a concrete reason to buy today. Warm and specific, no superlatives, no exclamation marks.\n\nStructure it as FOUR choice points, one per sentence: wrap each sentence in its own {…} holding four complete alternatives — four different ways to say that sentence, sharing no wording. Four blocks, not one. Inside two of them nest a further {a|b}. Do not define a #def or #set you are not going to use.\n\nOutput ONE paragraph. Separate the four blocks with a single space, never a blank line: the result has to read as a paragraph, not as four one-line paragraphs.\n\nTwo things that break the output rather than weaken it. Read each variable note before you place the variable: where the note says the value carries its own article, do not add one in front. And do not let two different slots reach for the same word or its root, however far apart they sit." }, { "id": "pool-v-product", "name": "product", "type": "string", "value": "Trail Runner 3" }, { "id": "pool-v-brand", "name": "brand", "type": "string", "value": "Brightline Gear" }, { "id": "pool-v-audience", "name": "audience", "type": "string", "value": "weekend hikers" }, { "id": "pool-v-feature", "name": "feature", "type": "string", "value": "a 240 g upper that dries overnight" }, { "id": "pool-locale-field", "name": "locale", "type": "string", "value": "en" } ] } }, "id": "pool-brief", "name": "Your brief and product", "type": "n8n-nodes-base.set", "typeVersion": 3.4, "position": [ 220, 260 ] }, { "parameters": { "operation": "buildAuthoringPrompt", "brief": "={{ $json.brief }}", "locale": "={{ $('Your brief and product').first().json.locale }}", "useItemFieldNames": false, "allowedVariables": { "variable": [ { "name": "product", "case": "", "note": "the listing title exactly as it should appear — place it where no added determiner is needed" }, { "name": "brand", "case": "", "note": "the manufacturer name exactly as it should appear — place it where no added determiner is needed" }, { "name": "audience", "case": "", "note": "a noun phrase naming the buyer — place it where no added determiner is needed" }, { "name": "feature", "case": "", "note": "a complete noun phrase that already carries its own article — never put a determiner in front of it" } ] }, "channel": "landing", "variationLevel": "aggressive" }, "id": "pool-authoring", "name": "Build authoring prompt", "type": "n8n-nodes-spintax.spintax", "typeVersion": 1, "position": [ 440, 260 ] }, { "parameters": { "promptType": "define", "text": "={{ $json.userPrompt }}", "messages": { "messageValues": [ { "message": "={{ $json.systemPrompt }}" } ] } }, "id": "pool-llm", "name": "Your LLM", "type": "@n8n/n8n-nodes-langchain.chainLlm", "typeVersion": 1.5, "position": [ 660, 260 ] }, { "parameters": { "model": "gpt-5-mini", "options": {} }, "id": "pool-model", "name": "Chat model for authoring and repair", "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi", "typeVersion": 1, "position": [ 660, 480 ] }, { "parameters": { "operation": "validate", "template": "={{ $json.text ?? $json.template }}", "cleanModelOutput": true, "locale": "", "spintaxMeta": "={{ $('Build authoring prompt').item.json.spintaxMeta }}" }, "id": "pool-validate1", "name": "Validate draft", "type": "n8n-nodes-spintax.spintax", "typeVersion": 1, "position": [ 880, 260 ] }, { "parameters": { "operation": "buildRepairPrompt", "template": "={{ $json.cleanedTemplate ?? $json.template }}", "diagnostics": "={{ $json.diagnostics }}", "spintaxMeta": "={{ $('Build authoring prompt').item.json.spintaxMeta }}" }, "id": "pool-repair", "name": "Build repair prompt", "type": "n8n-nodes-spintax.spintax", "typeVersion": 1, "position": [ 1100, 620 ] }, { "parameters": { "promptType": "define", "text": "={{ $json.userPrompt }}", "messages": { "messageValues": [ { "message": "={{ $json.systemPrompt }}" } ] } }, "id": "pool-llm2", "name": "LLM (repair)", "type": "@n8n/n8n-nodes-langchain.chainLlm", "typeVersion": 1.5, "position": [ 1320, 620 ] }, { "parameters": { "operation": "validate", "template": "={{ $json.text ?? $json.template }}", "cleanModelOutput": true, "locale": "", "spintaxMeta": "={{ $('Build authoring prompt').item.json.spintaxMeta }}" }, "id": "pool-validate2", "name": "Validate (attempt 2)", "type": "n8n-nodes-spintax.spintax", "typeVersion": 1, "position": [ 1540, 620 ] }, { "parameters": {}, "id": "pool-giveup", "name": "Still invalid - stop (loop is capped)", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [ 1540, 800 ] }, { "parameters": { "operation": "renderMany", "template": "={{ $json.cleanedTemplate }}", "count": 12, "baseSeed": "pool-2026-08", "locale": "={{ $('Your brief and product').first().json.locale }}", "useIncomingItem": false, "fixedVariables": { "pair": [ { "name": "product", "value": "={{ $('Your brief and product').first().json.product }}", "neutralize": false }, { "name": "brand", "value": "={{ $('Your brief and product').first().json.brand }}", "neutralize": false }, { "name": "audience", "value": "={{ $('Your brief and product').first().json.audience }}", "neutralize": false }, { "name": "feature", "value": "={{ $('Your brief and product').first().json.feature }}", "neutralize": false } ] } }, "id": "pool-render", "name": "Render 12 variants", "type": "n8n-nodes-spintax.spintax", "typeVersion": 1, "position": [ 1760, 260 ] }, { "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "strict", "version": 2 }, "conditions": [ { "id": "pool-cardinality", "leftValue": "={{ $json.produced }}", "rightValue": "={{ $json.requested }}", "operator": { "type": "number", "operation": "equals" } } ], "combinator": "and" }, "options": {} }, "id": "pool-cardinality-gate", "name": "Twelve distinct documents?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "position": [ 1980, 260 ] }, { "parameters": {}, "id": "pool-too-thin", "name": "Too few - the template cannot fill the pool", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [ 1980, 460 ] }, { "parameters": { "operation": "lint", "lintSource": "text", "text": "={{ $json.rendered }}", "lintWindow": 6, "locale": "={{ $('Your brief and product').first().json.locale }}", "lintIgnore": "={{ [$('Your brief and product').first().json.product, $('Your brief and product').first().json.brand, $('Your brief and product').first().json.audience, $('Your brief and product').first().json.feature].join('\\n') }}" }, "id": "pool-lint", "name": "Lint each variant", "type": "n8n-nodes-spintax.spintax", "typeVersion": 1, "position": [ 2200, 260 ] }, { "parameters": {}, "id": "pool-lint-out", "name": "Defective - dropped", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [ 2200, 460 ] }, { "parameters": { "operation": "uniqueness", "textField": "rendered", "dedupJaccard": 0.6, "locale": "={{ $('Your brief and product').first().json.locale }}", "uniquenessOptions": { "footprintMax": 0.65, "minPoolForFootprint": 5, "macros": "={{ [$('Your brief and product').first().json.product, $('Your brief and product').first().json.brand, $('Your brief and product').first().json.audience, $('Your brief and product').first().json.feature].join('\\n') }}" } }, "id": "pool-uniq", "name": "Check pool uniqueness", "type": "n8n-nodes-spintax.spintax", "typeVersion": 1, "position": [ 2420, 260 ] }, { "parameters": {}, "id": "pool-uniq-out", "name": "Dropped - too similar", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [ 2420, 460 ] }, { "parameters": {}, "id": "pool-ready", "name": "Ready to publish", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [ 2640, 260 ] }, { "parameters": { "content": "## Generate a pool of unique product descriptions from one AI-written spintax template\n\n### How it works\n\n1. An LLM writes ONE spintax template from your brief; the Spintax node renders every variant from it, with no model call per document.\n2. The template is validated first, and an invalid draft gets one repair round aimed at the exact line and column.\n3. Twelve documents are rendered with a per-attempt seed, so re-rendering that template reproduces them; a full re-run writes a different one.\n4. Lint judges each document; a defective one leaves on the second output and is not shipped.\n5. Uniqueness judges the whole pool and reports a footprint before it ships.\n\n### Setup steps\n\n- [ ] Install the community node `n8n-nodes-spintax`.\n- [ ] Add OpenAI credentials to **Chat model for authoring and repair** — one node feeds both LLM steps.\n- [ ] Put your brief, product facts and locale in **Your brief and product**. Variable names and their notes live on Build authoring prompt; the model reads the note, never the value, so change the note if you change a value's shape.\n- [ ] Run it once — a valid draft renders twelve variants in one pass.\n\n### Customization\n\nFootprint Limit is 0.65 and it gates: share more of your five-word windows than that and the whole pool leaves on Dropped. Six live runs on the model this ships with scored 0.30-0.37, twelve documents every time; the template written before the brief asked for four choice points scored 0.897. Ignored Strings holds the product facts because a brand can contain an ordinary noun — \"Brightline Gear\" beside the word gear cost one document its clean verdict.", "width": 480, "height": 1000 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ -608, 64 ], "id": "a1b2c3d4-0000-4a11-9c01-5f1a2b3c4d00", "name": "Sticky Note" }, { "parameters": { "content": "## Start and set your inputs\n\nOne Set node holds the brief, the product facts and the locale; every node below reads the locale from here. The variable names and their notes live on Build authoring prompt.", "width": 416, "height": 320, "color": 7 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ -48, 96 ], "id": "a1b2c3d4-0001-4a11-9c01-5f1a2b3c4d01", "name": "Sticky Note1" }, { "parameters": { "content": "## Author the spintax template\n\nThe node builds the canonical authoring prompt from your brief and allowed variables; your model writes one template from it.", "width": 416, "height": 544, "color": 7 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 384, 96 ], "id": "a1b2c3d4-0002-4a11-9c01-5f1a2b3c4d02", "name": "Sticky Note2" }, { "parameters": { "content": "## Validate, then repair once\n\nValid means no error-level diagnostics: those drafts go straight to rendering. The rest take the repair path, where the prompt carries the exact line and column of each problem. The loop is capped at one round by wiring, so it cannot spin.", "width": 864, "height": 880, "color": 7 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 832, 96 ], "id": "a1b2c3d4-0003-4a11-9c01-5f1a2b3c4d03", "name": "Sticky Note3" }, { "parameters": { "content": "## Render, count and lint variants\n\nTwelve documents come out of one template — unless the template cannot make twelve, which the gate catches before anything is judged. Lint reads each survivor and routes anything defective off the pool.", "width": 636, "height": 512, "color": 7 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 1712, 96 ], "id": "a1b2c3d4-0005-4a11-9c01-5f1a2b3c4d05", "name": "Sticky Note5" }, { "parameters": { "content": "## Check pool uniqueness\n\nUniqueness judges the pool rather than a document. Near-duplicates leave on the second output, and a pool whose footprint exceeds the limit leaves there whole.", "width": 416, "height": 512, "color": 7 }, "type": "n8n-nodes-base.stickyNote", "typeVersion": 1, "position": [ 2364, 96 ], "id": "a1b2c3d4-0006-4a11-9c01-5f1a2b3c4d06", "name": "Sticky Note6" } ], "connections": { "Your brief and product": { "main": [ [ { "node": "Build authoring prompt", "type": "main", "index": 0 } ] ] }, "Build authoring prompt": { "main": [ [ { "node": "Your LLM", "type": "main", "index": 0 } ] ] }, "Your LLM": { "main": [ [ { "node": "Validate draft", "type": "main", "index": 0 } ] ] }, "Validate draft": { "main": [ [ { "node": "Render 12 variants", "type": "main", "index": 0 } ], [ { "node": "Build repair prompt", "type": "main", "index": 0 } ] ] }, "Build repair prompt": { "main": [ [ { "node": "LLM (repair)", "type": "main", "index": 0 } ] ] }, "LLM (repair)": { "main": [ [ { "node": "Validate (attempt 2)", "type": "main", "index": 0 } ] ] }, "Validate (attempt 2)": { "main": [ [ { "node": "Render 12 variants", "type": "main", "index": 0 } ], [ { "node": "Still invalid - stop (loop is capped)", "type": "main", "index": 0 } ] ] }, "Render 12 variants": { "main": [ [ { "node": "Twelve distinct documents?", "type": "main", "index": 0 } ] ] }, "Lint each variant": { "main": [ [ { "node": "Check pool uniqueness", "type": "main", "index": 0 } ], [ { "node": "Defective - dropped", "type": "main", "index": 0 } ] ] }, "Check pool uniqueness": { "main": [ [ { "node": "Ready to publish", "type": "main", "index": 0 } ], [ { "node": "Dropped - too similar", "type": "main", "index": 0 } ] ] }, "Start a pool run": { "main": [ [ { "node": "Your brief and product", "type": "main", "index": 0 } ] ] }, "Chat model for authoring and repair": { "ai_languageModel": [ [ { "node": "Your LLM", "type": "ai_languageModel", "index": 0 }, { "node": "LLM (repair)", "type": "ai_languageModel", "index": 0 } ] ] }, "Twelve distinct documents?": { "main": [ [ { "node": "Lint each variant", "type": "main", "index": 0 } ], [ { "node": "Too few - the template cannot fill the pool", "type": "main", "index": 0 } ] ] } }, "settings": {}, "pinData": {} }