Module 22 · ai · intermediate
Solutions 22 · AI Product Description Generator
None of these have been run against live Gmail, Slack, Google Sheets or Shopify accounts yet.
Exercise 1
Replace the No Operation node with IF {{ $json.drafts.length > 0 }} → Gmail Send (or Slack):
Subject: {{ $json.drafts.length }} product descriptions to review ({{ $json.needs_review }} need attention)
{{ $json.drafts.map(d => (d.status === 'ready' ? '✓ ' : '⚠ ') + d.title + (d.issues.length ? ' (' + d.issues.join('; ') + ')' : '')).join('\n') }}
{{ $json.remaining }} more products are waiting for the next run.
Review: http://localhost:5678/webhook/pd/review
Exercise 2
- After
Queue for Review, add Split Out ondrafts→ Google Sheets Append or Update Row (match onproduct_id) with columnsproduct_id, title, status, issues, description, bullets, seo_title, meta_description, decision, note. Use{{ $json.issues.join('; ') }}and{{ $json.copy.bullets.join('\n') }}. - New workflow: Schedule every 15 min → Google Sheets Get Row(s) filtered on
decisionnot empty → HTTP Request POSThttp://localhost:5678/webhook/pd/review/decide:
Sending the sheet's description as an edit means reviewers can fix the wording right in the sheet.{ "product_id": "{{ $json.product_id }}", "decision": "{{ $json.decision.toLowerCase() }}", "note": "{{ $json.note }}", "edits": { "description": "{{ $json.description }}" } } - → Google Sheets Update Row: set
decisiontodone: {{ $json.published ? 'published' : $json.state }}so the row isn't processed twice.
Exercise 3
In decide.js, include seo_title: final.seo_title, meta_description: final.meta_description in the output. After Update Shopify Product, add two HTTP Request nodes (Shopify credential, On Error → Continue):
POST https://{{ shop }}.myshopify.com/admin/api/2024-10/products/{{ $('Apply Decision').first().json.product_id }}/metafields.json
{ "metafield": { "namespace": "global", "key": "title_tag", "type": "single_line_text_field",
"value": "{{ $('Apply Decision').first().json.seo_title }}" } }
and the same with key: "description_tag" and the meta description. In published.js, report both results.
Stretch
In select.js keep image: p.image?.src. Replace the Basic LLM Chain with OpenAI Message a Model (a vision-capable model) with the image URL as an input image and the same prompt, asking for structured JSON. In check.js, add the issue visual details: confirm against the photo whenever the source is AI and an image was used, so a person always checks them.