
Product Content Enrichment
- 68 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Use AI to auto-generate product descriptions, extract attributes, and tag images to enrich your catalog at scale.
About
Uses AI to generate product descriptions, extract attributes, and tag images to enrich catalogs at scale via platform tools and AI writing apps. A developer or merchandiser uses it to fill in and improve product content faster.
- AI-generated descriptions and attribute extraction
- Image tagging to enrich catalogs at scale
Product Content Enrichment by the numbers
- 68 all-time installs (skills.sh)
- Ranked #5,858 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill product-content-enrichmentAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 68 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Use AI to auto-generate product descriptions, extract attributes, and tag images to enrich your catalog at scale.
Files
Product Content Enrichment
Overview
Rich product content — compelling descriptions, complete attributes, and well-tagged images — drives both conversion and SEO. When a catalog is imported from a supplier with sparse content, enrichment is the next step. AI tools can generate descriptions, extract attributes, and suggest tags at scale. Platform-native AI features and dedicated apps handle the common cases without custom development.
When to Use This Skill
- When importing a supplier catalog that has only product names, SKUs, and sparse descriptions
- When product descriptions are inconsistent in style or missing SEO keywords
- When image metadata (alt text, tags) is missing and needs to be generated at scale
- When a catalog refresh requires rewriting hundreds of product descriptions in a new brand voice
Core Instructions
Step 1: Determine platform and choose the right tool
| Platform | Built-in AI | Recommended App/Tool |
|---|---|---|
| Shopify | Shopify Magic (AI description generation, built-in) | Jasper for Shopify, or ChatGPT for bulk generation via CSV |
| WooCommerce | None native | ChatGPT + WP All Import for bulk import; Hypotenuse AI WooCommerce plugin |
| BigCommerce | None native | Feedonomics for feed enrichment; Jasper or ChatGPT for descriptions |
| Any platform (bulk) | Claude / ChatGPT / Gemini | Generate descriptions in bulk via CSV, then import using platform tools |
---
Step 2: Platform-specific setup
---
Shopify
Option A: Shopify Magic (built-in, free)
Shopify Magic is available to all merchants on any plan.
1. Go to Admin → Products → [Product] 2. In the product description editor, click the sparkle icon (✨) at the top right 3. Enter a prompt or let Shopify Magic generate from the product title and existing details 4. Review the generated text — edit to match your brand voice 5. Click Save when satisfied
Limitations: Shopify Magic works one product at a time; not suitable for bulk enrichment.
Option B: Bulk generation with CSV + AI
For enriching hundreds of products at once:
1. Export current catalog: Admin → Products → Export (download as CSV) 2. Generate descriptions in bulk: Paste your product data into ChatGPT, Claude, or another AI tool with a prompt like:
For each of the following products, write a product description in this format:
- Opening sentence: 1 compelling benefit sentence (max 20 words)
- 2-3 sentence paragraph: features and use cases
- 4-6 bullet points: key specifications
Brand voice: [describe your brand voice]
Do not invent specifications not present in the product data.
Products:
[paste your CSV rows]3. Import back into Shopify: Use Matrixify (App Store) to import the enriched CSV with updated descriptions; map the description column to the Shopify body HTML field
Option C: Hypotenuse AI or Jasper (App Store)
These apps integrate directly with Shopify:
1. Install from the Shopify App Store 2. Connect to your product catalog 3. Select products to enrich and click generate 4. Review drafts in the app's editor before publishing 5. Publish approved descriptions directly to Shopify
---
WooCommerce
Bulk generation workflow:
1. Export products: WooCommerce → Products → Export (CSV) 2. Generate descriptions using an AI tool of your choice (ChatGPT, Claude, Jasper) 3. Import enriched data: Use WP All Import Pro to import the updated CSV back into WooCommerce, mapping the description column to the product description field
Hypotenuse AI for WooCommerce:
- Install the Hypotenuse AI plugin from WooCommerce.com
- Select multiple products from your product list
- Click Generate Content to create descriptions in bulk
- Review and approve before publishing
For attribute extraction:
- Use AI to extract structured attributes (material, dimensions, weight, care instructions) from existing descriptions
- Add extracted attributes to WooCommerce product attributes under the Attributes tab
- These become filterable facets in your navigation
---
BigCommerce
Bulk description generation:
1. Go to Products → Export and download the product catalog CSV 2. Generate enriched descriptions using an AI tool 3. Re-import using Products → Import
Feedonomics for feed enrichment:
- Install Feedonomics from the BigCommerce App Marketplace
- Feedonomics can use AI to optimize product titles and descriptions specifically for Google Shopping, Amazon, and other channels
- Particularly useful for enriching attributes required by feed destinations (GTIN, brand, MPN)
---
Custom / Headless
For headless platforms with a custom database, build an enrichment pipeline with a human review gate:
// lib/productEnrichment.ts
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic();
const DESCRIPTION_PROMPT = `You are a product copywriter. Generate a product description with:
1. A compelling opening sentence (max 20 words) highlighting the main benefit
2. A 2-3 sentence paragraph describing features
3. 4-6 key feature bullet points
Brand voice: {brandVoice}
Constraints:
- Use only the provided attributes — do not invent specifications
- Target: 80-120 words for the paragraph, plus bullets
- Include the product name and 1-2 SEO keywords naturally
- Do not use superlatives like "best" or "amazing"
Product: {name}
Category: {category}
Attributes: {attributes}`;
// Generate description for a single product
export async function generateProductDescription(product: Product, brandVoice: string): Promise<string> {
const attributeText = Object.entries(product.attributes ?? {})
.filter(([, v]) => v !== null)
.map(([k, v]) => `${k}: ${v}`)
.join('\n');
const prompt = DESCRIPTION_PROMPT
.replace('{brandVoice}', brandVoice)
.replace('{name}', product.name)
.replace('{category}', product.category)
.replace('{attributes}', attributeText || 'Not provided');
const message = await client.messages.create({
model: 'claude-opus-4-5',
max_tokens: 400,
messages: [{ role: 'user', content: prompt }],
});
return message.content[0].type === 'text' ? message.content[0].text : '';
}
// Batch enrichment with human review gate — saves drafts, never auto-publishes
export async function enrichProductsBatch(productIds: string[], brandVoice: string) {
const CONCURRENCY = 5;
const results = [];
for (let i = 0; i < productIds.length; i += CONCURRENCY) {
const chunk = productIds.slice(i, i + CONCURRENCY);
const batchResults = await Promise.all(chunk.map(async productId => {
const product = await db.products.findUnique({ where: { id: productId }, include: { attributes: true } });
try {
const description = await generateProductDescription(product, brandVoice);
// Save as draft — requires human approval before going live
await db.productEnrichmentDrafts.upsert({
where: { productId },
create: { productId, description, status: 'pending_review' },
update: { description, status: 'pending_review', updatedAt: new Date() },
});
return { productId, status: 'success' };
} catch (err) {
return { productId, status: 'error', error: err.message };
}
}));
results.push(...batchResults);
}
return results;
}
// Approve a draft and publish to the product
export async function approveDraft(productId: string, approvedBy: string) {
const draft = await db.productEnrichmentDrafts.findUnique({ where: { productId } });
if (!draft) throw new Error('Draft not found');
await db.$transaction([
db.products.update({ where: { id: productId }, data: { description: draft.description } }),
db.productEnrichmentDrafts.update({
where: { productId },
data: { status: 'approved', approvedBy, approvedAt: new Date() },
}),
]);
}---
Step 3: Review and approve AI-generated content
Never auto-publish AI-generated content without human review. AI can:
- Invent specifications not in the source data (hallucination)
- Use a tone inconsistent with your brand
- Include legally problematic claims
Review workflow: 1. Generate descriptions as drafts 2. Use a simple spreadsheet or your platform's product edit screen to review each one 3. Edit tone, accuracy, and brand voice before approving 4. Track approval rate — if you're rejecting more than 30% of drafts, refine your prompt
Prioritize which products to enrich first:
- High-traffic, low-conversion products (check Analytics)
- Products with zero or very short descriptions
- New arrivals that need SEO content to start ranking
---
Step 4: Enrich product images with alt text
Alt text serves both accessibility and image SEO.
Shopify:
- Go to Products → [Product] → Images
- Click the ... menu on any image → Edit alt text
- Enter a descriptive alt text (e.g., "Blue cotton t-shirt with round neck, men's size M")
- For bulk alt text: use Matrixify with a column for
Image Alt Text
WooCommerce:
- Upload an image and click Edit in the media library
- Fill in the Alt Text field
- For existing images: go to Media Library → [Image] → Edit
For bulk alt text generation: 1. Export a list of product images with their product titles 2. Use an AI tool to generate descriptive alt text for each image 3. Import back using your platform's bulk tools
Best Practices
- Always use human review before publishing AI descriptions — never auto-publish; track approval rate and use it to iterate on your prompts
- Store AI-generated content as drafts separate from live content — never overwrite the published description in-place until reviewed and approved
- Use lower temperature settings for product descriptions (0.3–0.5 in ChatGPT/Claude) — consistent, on-brand output is more valuable than creative variation
- Include your brand voice guidelines in every prompt — "Professional yet approachable, focus on quality, no superlatives" produces far better output than prompting without brand context
- Enrich highest-priority products first — start with your top 20% revenue products before tackling the long tail
Common Pitfalls
| Problem | Solution |
|---|---|
| AI invents specifications not in the source data | Add explicit constraints to the prompt: "Use only the provided attributes — do not invent or assume values"; verify output against the product spec |
| All AI descriptions sound identical | Add product-type-specific instructions (e.g., different prompts for footwear vs. electronics vs. apparel) |
| Descriptions miss SEO keywords | Include "naturally incorporate these SEO keywords: [list]" in the prompt; check with a keyword tool after generation |
| Bulk import overwrites good existing descriptions | Filter your import to only products with empty or very short descriptions; don't overwrite manually written descriptions |
| AI image tagging quality is poor | Use AI image analysis (GPT-4 Vision, Claude) for alt text generation rather than keyword-based tools; provide the product name as context |
Related Skills
- @catalog-import-export
- @product-data-modeling
- @product-categorization
{
"context": "Tests whether the agent implements product attribute extraction using the correct model, API settings, prompt schema, and response processing as specified in the product content enrichment skill.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Correct extraction model",
"max_score": 10,
"description": "Uses 'gpt-4o-mini' (not gpt-4o, gpt-4, gpt-3.5-turbo, or other models) for attribute extraction"
},
{
"name": "json_object response format",
"max_score": 10,
"description": "The API call for attribute extraction sets response_format to { type: 'json_object' }"
},
{
"name": "Temperature zero",
"max_score": 10,
"description": "Temperature is set to 0 for the attribute extraction API call"
},
{
"name": "Required attribute keys",
"max_score": 10,
"description": "The extraction prompt asks for all seven specific keys: material, color, dimensions, weight, care_instructions, country_of_origin, and warranty"
},
{
"name": "Null for missing values",
"max_score": 10,
"description": "The prompt explicitly instructs the model to use null (not an empty string, 'unknown', or omit the key) for any attribute not mentioned in the description"
},
{
"name": "No inference instruction",
"max_score": 10,
"description": "The prompt explicitly states NOT to guess or infer values not explicitly stated in the description"
},
{
"name": "Null value stripping",
"max_score": 10,
"description": "After parsing the JSON response, null (and undefined) values are filtered out before the attributes object is returned or saved"
},
{
"name": "JSON parse try/catch",
"max_score": 10,
"description": "JSON.parse (or equivalent) of the model's response is wrapped in a try/catch block that throws or returns a meaningful error on parse failure"
},
{
"name": "Batch continues on failure",
"max_score": 10,
"description": "When processing multiple descriptions, a failure for one product is caught per-product and the batch continues rather than throwing and stopping all processing"
},
{
"name": "Extracted results in output file",
"max_score": 10,
"description": "A results file (JSON) is written containing the extracted attributes for each processed product, with null-stripped fields"
}
]
}
Product Attribute Backfill Tool
Problem/Feature Description
HomeComforts, a home goods and furniture retailer, migrated to a new product information management (PIM) system last year. During the migration, the structured attribute fields (material composition, dimensions, care instructions, etc.) were lost for about 300 products — but those products still have rich marketing descriptions written by their copywriting team. Rather than manually filling in the attributes for hundreds of items, the data team wants to extract structured attributes from the existing descriptions automatically.
Your task is to write a Node.js script that reads the provided product descriptions and uses an AI model to extract a standard set of structured attributes from each one. The tool should handle edge cases gracefully: not all descriptions will contain every attribute, some descriptions may cause API errors, and the overall run should not fail just because a single product could not be processed. A clean JSON output file with the results should be left for the data team to review.
Output Specification
Write a Node.js script extract.js that:
1. Reads the input product descriptions from inputs/descriptions.json 2. Calls an AI API to extract structured attributes from each description 3. Writes the results to extracted_attributes.json — one object per product, with the product id and the extracted attributes (only include attributes that were actually found; omit fields that are absent) 4. Prints a completion summary to stdout (e.g., how many succeeded, how many failed)
Also write a design-notes.md explaining the prompt design choices, the model and parameter selection, and how you handled missing attributes and parsing errors.
You do not need to execute the script — just write the code so the design can be reviewed.
Input Files
The following files are provided as inputs. Extract them before beginning.
=============== FILE: inputs/descriptions.json =============== [ { "id": "hg-001", "name": "Valencia Linen Duvet Cover", "description": "Crafted from 100% stonewashed linen, the Valencia Duvet Cover brings a relaxed, lived-in texture to any bedroom. The linen is sourced from European flax farms and woven in Portugal. Machine washable at 40°C, do not tumble dry. Dimensions: 200×200cm (double). The natural flax colour develops a softer patina with each wash. No warranty provided." }, { "id": "hg-002", "name": "Oslo Solid Oak Dining Table", "description": "A minimalist dining table made from sustainably harvested solid oak with a natural oil finish. Seats 6 comfortably. Dimensions: 180cm L × 90cm W × 75cm H. Weight: 42kg. Wipe clean with a damp cloth; do not use abrasive cleaners. Manufactured in Denmark. Covered by a 5-year structural warranty." }, { "id": "hg-003", "name": "Ember Ceramic Table Lamp", "description": "A hand-thrown ceramic lamp base in a warm terracotta glaze, paired with a linen drum shade. The cord is 1.8m and fitted with an inline dimmer switch. Wipe base with a dry cloth only. Shade dimensions: 30cm diameter × 20cm height." }, { "id": "hg-004", "name": "CloudSoft Bamboo Bath Towel", "description": "Woven from 70% bamboo viscose and 30% cotton, this bath towel is exceptionally soft and quick-drying. Weight: 500gsm. Available in ivory, sage, and slate. Machine washable at 40°C; tumble dry on low. Size: 70×140cm. Made in Turkey." }, { "id": "hg-005", "name": "Meridian Wool Throw", "description": "A generously sized throw blanket in a classic herringbone weave. Made in Scotland from 100% lambswool. Hand wash cold or dry clean only. Dimensions: 130×200cm. Weight: 800g. The earthy tones — ochre, rust, and cream — are achieved using natural plant dyes." }, { "id": "hg-006", "name": "Apex Standing Desk Frame", "description": "An electric height-adjustable desk frame compatible with tops from 120–200cm wide. Steel construction with a powder-coat finish. Height range: 62–128cm. Lifting capacity: 80kg. Ships in two boxes, total weight: 28kg. Assembly required. Comes with a 3-year motor warranty." }, { "id": "hg-007", "name": "Petra Marble Cheese Board", "description": "A solid white Carrara marble serving board, ideal for charcuterie and cheese. Dimensions: 35×25cm, 2cm thick. Weight: 1.8kg. Wipe clean with a damp cloth; do not submerge in water or put in the dishwasher." }, { "id": "hg-008", "name": "Serenity Scented Soy Candle", "description": "Hand-poured in small batches using 100% soy wax and a cotton wick. Scented with a blend of cedarwood and bergamot essential oils. Burn time approximately 45 hours. Net weight: 220g. Keep away from drafts and never leave unattended while burning." } ]
{
"context": "Tests whether the agent builds an AI-powered batch description pipeline following the skill's specific model choices, prompt structure, concurrency pattern, temperature settings, and draft-saving workflow.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Prompt templates in dedicated file",
"max_score": 8,
"description": "Prompt template strings are defined in a separate module file (e.g., lib/enrichmentPrompts.js or similar), not inline inside the generation function"
},
{
"name": "Correct description model",
"max_score": 8,
"description": "Uses 'gpt-4o' (not gpt-3.5-turbo, gpt-4, gpt-4o-mini or other models) for description generation"
},
{
"name": "Temperature in range",
"max_score": 8,
"description": "Temperature parameter for the description generation call is set to a value in the range 0.3–0.5 (inclusive)"
},
{
"name": "max_tokens 400",
"max_score": 8,
"description": "max_tokens is set to 400 for the description generation API call"
},
{
"name": "Three-part prompt structure",
"max_score": 8,
"description": "The description prompt instructs the model to produce: (1) an opening sentence, (2) a feature paragraph, and (3) a bulleted list of key features"
},
{
"name": "Opening sentence word cap",
"max_score": 8,
"description": "The prompt explicitly constrains the opening sentence to a maximum of 20 words"
},
{
"name": "Paragraph word count target",
"max_score": 8,
"description": "The prompt specifies a target length of 80–120 words for the feature paragraph"
},
{
"name": "No superlatives constraint",
"max_score": 8,
"description": "The prompt explicitly instructs the model not to use superlatives such as 'best' or 'amazing'"
},
{
"name": "Attributes-only constraint",
"max_score": 8,
"description": "The prompt contains an explicit instruction not to invent or guess product specifications beyond those provided"
},
{
"name": "Concurrency limit of 5",
"max_score": 8,
"description": "Batch processing uses a chunk size or concurrency limit of 5 (not larger, not smaller) parallel API calls"
},
{
"name": "Drafts saved as pending_review",
"max_score": 10,
"description": "Generated descriptions are saved to a drafts store/table with a status value of 'pending_review', not written directly to the published product record"
},
{
"name": "Per-product error handling",
"max_score": 10,
"description": "Individual product failures are caught and logged without aborting the rest of the batch (try/catch per product inside the batch loop)"
}
]
}
Outdoor Gear Product Description Generator
Problem/Feature Description
OutdoorPeak, a mid-size outdoor retailer, has just received a supplier data export containing 12 new products — hiking boots, insulated jackets, and trekking poles — that need to be added to their website before a weekend sale. The supplier data is minimal: product names, SKUs, categories, and a few raw attributes (material, weight, dimensions), but no marketing copy. The content team has written brand voice guidelines emphasizing a confident, adventure-focused tone without overblown marketing language.
The team needs a Node.js script that reads the supplied product data and calls an AI API to generate polished product descriptions in bulk. Because the AI sometimes produces unreliable copy, the marketing team insists that all generated descriptions be held for their review before going live — they do not want anything published automatically. The script should handle failures gracefully so that a problem with one product does not stop descriptions from being generated for the rest.
Output Specification
Produce a single Node.js script file named enrich.js (or split across files in a lib/ directory) that:
1. Accepts the input product list (provided below) and generates a description for each product using an AI API 2. Prints a summary to stdout showing how many succeeded and how many failed 3. Writes a file drafts.json containing each product's generated description alongside a status field
You may use any approach to structure the code, but the output file drafts.json must exist after the script is run (you do not need to actually execute it — just write the code).
Also write a brief design-notes.md explaining the key design decisions you made for the prompt, the batch logic, and the draft-saving approach.
Input Files
The following files are provided as inputs. Extract them before beginning.
=============== FILE: inputs/products.json =============== [ { "id": "prod-001", "name": "TrailBlaze Hiking Boot", "category": "Footwear", "attributes": { "material": "Full-grain leather upper, Vibram sole", "weight": "680g per boot", "waterproofing": "Gore-Tex membrane", "ankle_support": "High-cut" }, "seoKeywords": ["waterproof hiking boots", "trail boots"] }, { "id": "prod-002", "name": "SummitShield Insulated Jacket", "category": "Outerwear", "attributes": { "material": "600-fill goose down, ripstop nylon shell", "weight": "420g", "packable": "Packs into own pocket", "temperature_rating": "-10°C" }, "seoKeywords": ["down jacket", "packable insulated jacket"] }, { "id": "prod-003", "name": "AlpinePro Trekking Poles", "category": "Equipment", "attributes": { "material": "7075 aluminium", "weight": "280g per pole", "adjustable_range": "110–135cm", "grip": "Cork grip with neoprene extension" }, "seoKeywords": ["trekking poles", "adjustable hiking poles"] }, { "id": "prod-004", "name": "RidgeLine Softshell Pants", "category": "Apparel", "attributes": { "material": "92% polyester, 8% elastane", "weight": "310g", "articulation": "Articulated knees", "pockets": "4 zip pockets" }, "seoKeywords": ["softshell hiking pants", "stretch outdoor pants"] }, { "id": "prod-005", "name": "CampLight Headlamp 350", "category": "Lighting", "attributes": { "lumens": "350 lm max", "battery": "3× AAA (included)", "weight": "89g (with batteries)", "beam_distance": "80m" }, "seoKeywords": ["headlamp", "camping headlamp"] }, { "id": "prod-006", "name": "NordicTrack Merino Base Layer", "category": "Apparel", "attributes": { "material": "100% merino wool, 200gsm", "weight": "220g", "odor_resistance": "Natural merino properties", "fit": "Slim fit" }, "seoKeywords": ["merino base layer", "wool thermal top"] }, { "id": "prod-007", "name": "PeakBag 40L Daypack", "category": "Bags & Packs", "attributes": { "material": "420D nylon", "volume": "40 litres", "weight": "950g", "back_system": "Adjustable torso length, ventilated panel" }, "seoKeywords": ["40L daypack", "hiking backpack"] }, { "id": "prod-008", "name": "GlacierGrip Waterproof Gloves", "category": "Accessories", "attributes": { "material": "Goat leather palm, fleece lining", "waterproofing": "Gore-Tex insert", "insulation": "PrimaLoft Gold 100g" }, "seoKeywords": ["waterproof gloves", "ski gloves"] }, { "id": "prod-009", "name": "HighCamp Sleeping Bag -5°C", "category": "Sleep", "attributes": { "fill": "550-fill duck down", "temperature_rating": "-5°C comfort", "weight": "1.1kg", "packed_size": "28×18cm" }, "seoKeywords": ["3-season sleeping bag", "down sleeping bag"] }, { "id": "prod-010", "name": "TerraFirm Gaiters", "category": "Footwear Accessories", "attributes": { "material": "Cordura nylon", "height": "40cm", "closure": "YKK zip + velcro", "compatibility": "Fits most hiking boots" }, "seoKeywords": ["hiking gaiters", "trail gaiters"] }, { "id": "prod-011", "name": "SolarSip Water Filter Bottle", "category": "Hydration", "attributes": { "capacity": "650ml", "filter_type": "Hollow-fibre membrane", "filtration_rating": "0.1 micron (removes bacteria & protozoa)", "weight": "148g" }, "seoKeywords": ["filtered water bottle", "hiking water filter"] }, { "id": "prod-012", "name": "StormProof Bivvy Bag", "category": "Shelter", "attributes": { "material": "Aluminium-coated polyester", "weight": "120g", "packed_size": "10×6cm", "use_case": "Emergency shelter, reflects 90% body heat" }, "seoKeywords": ["bivvy bag", "emergency shelter"] } ]
{
"context": "Tests whether the agent implements product image tagging and a human review workflow following the skill's specific model choice, API parameters, output schema, alt text constraints, color specificity, and draft/approval data model.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Correct image tagging model",
"max_score": 7,
"description": "Uses 'gpt-4o' (not gpt-4o-mini, gpt-4-vision-preview, or other models) for the image tagging API call"
},
{
"name": "detail: low for image URL",
"max_score": 7,
"description": "The image_url content part sets detail to 'low' when passing the image to the model"
},
{
"name": "json_object response format",
"max_score": 7,
"description": "The image tagging API call sets response_format to { type: 'json_object' }"
},
{
"name": "Temperature zero for tagging",
"max_score": 7,
"description": "Temperature is set to 0 for the image tagging API call"
},
{
"name": "max_tokens 200",
"max_score": 7,
"description": "max_tokens is set to 200 for the image tagging API call"
},
{
"name": "alt_text length constraint",
"max_score": 7,
"description": "The image tagging prompt explicitly constrains alt_text to a maximum of 125 characters"
},
{
"name": "Background enum constraint",
"max_score": 7,
"description": "The image tagging prompt specifies that the background field must be one of: white, lifestyle, studio, transparent"
},
{
"name": "Specific color instruction",
"max_score": 7,
"description": "The image tagging prompt instructs the model to be specific about colors (e.g., 'navy blue' not just 'blue', or equivalent specific color guidance)"
},
{
"name": "Manual alt text preserved",
"max_score": 7,
"description": "When saving image tagging results, existing manual alt text is NOT overwritten — the AI-generated alt_text is only applied if the image record has no existing alt text (logic equivalent to: altText = existingAltText || aiAltText)"
},
{
"name": "Never auto-publish",
"max_score": 7,
"description": "The code does NOT write AI-generated tags or descriptions directly to a live/published state — results are stored as drafts or in a pending state first"
},
{
"name": "Drafts in separate store",
"max_score": 7,
"description": "Generated content is saved to a dedicated drafts table or collection (e.g., productEnrichmentDrafts, enrichment_drafts) that is distinct from the published product/image record"
},
{
"name": "Atomic approval transaction",
"max_score": 7,
"description": "The approval operation updates both the product/image record AND the draft status in a single atomic transaction (e.g., db.$transaction, session.withTransaction, or equivalent)"
},
{
"name": "Minimum image dimension check",
"max_score": 8,
"description": "Before calling the image tagging API, the code checks that the image is at least 400×400 pixels and skips or flags images that are too small"
},
{
"name": "Review API endpoints",
"max_score": 8,
"description": "The implementation includes at least two HTTP endpoints (or equivalent handlers) for the review workflow: one to list pending drafts and at least one to approve or reject a draft"
}
]
}
Fashion Catalog Image Enrichment & Moderation System
Problem/Feature Description
StyleHaus, an online fashion retailer, recently completed a photography session for their new spring collection — 200 product images uploaded to their CDN. Their accessibility team has flagged that most images are missing alt text, which is a legal compliance issue. Separately, the merchandising team needs structured color and material tags on images to power their visual search and filter features ("shop by color", "filter by fabric"). Manually tagging 200 images would take days.
The tech team wants to build an automated image enrichment pipeline that uses an AI vision model to generate alt text and structured tags for each image. However, past experience with AI-generated copy has taught them a hard lesson: AI sometimes hallucinates product details. Any AI-generated content must go through a moderation queue before it is applied to live product listings. Reviewers need endpoints to browse pending items and approve or reject them. The team also wants to avoid wasting API credits on unusable low-resolution images.
Your task is to implement the image tagging service and the moderation workflow. Use the public image URLs provided below as sample inputs.
Output Specification
Write a Node.js implementation (one or more files) that includes:
1. An tagImage(imageUrl, existingAltText) function that calls an AI vision API and returns structured tagging data 2. A processImageBatch(images) function that tags a list of images and saves the results to a pending review state 3. HTTP route handlers (or a simple Express router) for:
- Listing pending image tag drafts
- Approving a draft (applying tags to the image record)
- Rejecting a draft with an optional note
4. A design-notes.md file explaining your prompt design, the model and parameter choices, how you handle the case where an image already has manually-written alt text, and your approach to the moderation workflow
You do not need to run or deploy the code — provide the implementation files for review. Use in-memory data structures (plain JS objects/Maps) instead of a real database so the code can be understood without infrastructure.
Input Files
The following files are provided as inputs. Extract them before beginning.
=============== FILE: inputs/images.json =============== [ { "id": "img-001", "productId": "prod-101", "url": "https://images.unsplash.com/photo-1542291026-7eec264c27ff?w=800", "altText": null, "width": 800, "height": 533 }, { "id": "img-002", "productId": "prod-102", "url": "https://images.unsplash.com/photo-1524592094714-0f0654e20314?w=800", "altText": "Classic silver watch on white background", "width": 800, "height": 800 }, { "id": "img-003", "productId": "prod-103", "url": "https://images.unsplash.com/photo-1594938298603-c8148c4b4e02?w=800", "altText": null, "width": 800, "height": 1067 }, { "id": "img-004", "productId": "prod-104", "url": "https://images.unsplash.com/photo-1602810316693-3667c854239a?w=300", "altText": null, "width": 300, "height": 300 }, { "id": "img-005", "productId": "prod-105", "url": "https://images.unsplash.com/photo-1434389677669-e08b4cac3105?w=800", "altText": null, "width": 800, "height": 1200 } ]
{
"name": "finsi/product-content-enrichment",
"version": "0.1.0",
"summary": "AI-assisted product descriptions, attribute extraction, and image tagging",
"skills": {
"product-content-enrichment": {
"path": "SKILL.md"
}
}
}