Product Photo Alt Text Writer
Generate SEO-friendly, accessible alt text and tags for product images.Problem Statement
Who: E-commerce teams, content managers, accessibility specialistsWhy: Good alt text improves accessibility and SEO. Manual writing is time-consuming at scale.
What You’ll Build
A recipe that analyzes product images and generates descriptive alt text with relevant tags.Input/Output Contract
| Input | Type | Required | Description |
|---|---|---|---|
image_path | string | Yes | Path to product image |
brand_tone | string | No | Brand voice (default: neutral) |
locale | string | No | Target locale (default: en-US) |
| Output | Type | Description |
|---|---|---|
alt_text | string | Generated alt text |
tags | array | Relevant product tags |
ok | boolean | Success indicator |
Prerequisites
export OPENAI_API_KEY=your_key_here
pip install praisonaiagents
Step-by-Step Build
1
Create Recipe Directory
mkdir -p ~/.praisonai/templates/product-photo-alt-text-writer
cd ~/.praisonai/templates/product-photo-alt-text-writer
2
Create TEMPLATE.yaml
name: product-photo-alt-text-writer
version: "1.0.0"
description: "Generate accessible alt text for product images"
author: "PraisonAI"
license: "MIT"
tags:
- image
- accessibility
- ecommerce
- seo
requires:
env:
- OPENAI_API_KEY
packages:
- praisonaiagents
inputs:
image_path:
type: string
description: "Path to the product image"
required: true
brand_tone:
type: string
description: "Brand voice for alt text"
required: false
default: "neutral"
enum:
- neutral
- luxury
- casual
- technical
locale:
type: string
description: "Target locale for alt text"
required: false
default: "en-US"
outputs:
alt_text:
type: string
description: "Generated alt text"
tags:
type: array
description: "Relevant product tags"
ok:
type: boolean
description: "Success indicator"
cli:
command: "praison recipes run product-photo-alt-text-writer"
examples:
- 'praison recipes run product-photo-alt-text-writer --input ''{"image_path": "product.jpg"}'''
safety:
dry_run_default: false
requires_consent: false
overwrites_files: false
network_access: true
pii_handling: false
3
Create recipe.py
# recipe.py
import os
from praisonaiagents import Agent, Task, AgentTeam
def run(input_data: dict, config: dict = None) -> dict:
"""Generate alt text for product images."""
image_path = input_data.get("image_path")
brand_tone = input_data.get("brand_tone", "neutral")
locale = input_data.get("locale", "en-US")
if not image_path:
return {"ok": False, "error": {"code": "MISSING_INPUT", "message": "image_path is required"}}
if not os.path.exists(image_path):
return {"ok": False, "error": {"code": "FILE_NOT_FOUND", "message": f"Image not found: {image_path}"}}
try:
tone_guidelines = {
"neutral": "Clear, factual descriptions",
"luxury": "Elegant, sophisticated language emphasizing quality",
"casual": "Friendly, approachable descriptions",
"technical": "Precise specifications and features"
}
# Create vision agent
vision_agent = Agent(
name="Product Analyst",
role="Visual Product Expert",
goal="Analyze product images accurately",
instructions=f"""
You are a product image analyst.
- Identify the product type and category
- Note colors, materials, and features
- Describe the product's appearance
- Consider the {brand_tone} tone: {tone_guidelines[brand_tone]}
""",
)
# Create alt text writer
alt_writer = Agent(
name="Alt Text Writer",
role="Accessibility Specialist",
goal="Write effective alt text for screen readers",
instructions=f"""
You are an accessibility expert writing alt text.
- Keep alt text under 125 characters
- Be descriptive but concise
- Include key product details
- Use {brand_tone} tone
- Write for {locale} audience
- Don't start with "Image of" or "Picture of"
""",
)
# Create tag generator
tagger = Agent(
name="Product Tagger",
role="E-commerce SEO Specialist",
goal="Generate relevant product tags",
instructions="""
You are an e-commerce tagging expert.
- Generate 5-10 relevant tags
- Include category, color, material, style
- Use lowercase, hyphenated format
- Focus on searchable terms
""",
)
# Define tasks
analyze_task = Task(
name="analyze_image",
description=f"Analyze the product image at: {image_path}",
expected_output="Detailed product description",
agent=vision_agent,
)
alt_task = Task(
name="write_alt_text",
description="Write accessible alt text based on the analysis",
expected_output="Alt text under 125 characters",
agent=alt_writer,
context=[analyze_task],
)
tag_task = Task(
name="generate_tags",
description="Generate product tags for SEO",
expected_output="List of 5-10 tags",
agent=tagger,
context=[analyze_task],
)
# Execute
agents = AgentTeam(
agents=[vision_agent, alt_writer, tagger],
tasks=[analyze_task, alt_task, tag_task],
)
result = agents.start()
# Parse tags
tags_text = result.get("generate_tags", "")
tags = [t.strip().lower().replace(" ", "-") for t in tags_text.split(",") if t.strip()]
if not tags:
tags = [t.strip() for t in tags_text.split("\n") if t.strip() and not t.startswith("-")]
return {
"ok": True,
"alt_text": result.get("write_alt_text", "").strip()[:125],
"tags": tags[:10],
"artifacts": [],
"warnings": [],
}
except Exception as e:
return {"ok": False, "error": {"code": "PROCESSING_ERROR", "message": str(e)}}
4
Create test_recipe.py
# test_recipe.py
import pytest
from recipe import run
def test_missing_image_path():
result = run({})
assert result["ok"] is False
assert result["error"]["code"] == "MISSING_INPUT"
def test_file_not_found():
result = run({"image_path": "/nonexistent.jpg"})
assert result["ok"] is False
assert result["error"]["code"] == "FILE_NOT_FOUND"
def test_brand_tones():
valid_tones = ["neutral", "luxury", "casual", "technical"]
for tone in valid_tones:
assert tone in valid_tones
@pytest.mark.integration
def test_end_to_end():
import os
test_image = os.environ.get("TEST_IMAGE_PATH")
if not test_image:
pytest.skip("No test image available")
result = run({"image_path": test_image, "brand_tone": "neutral"})
assert result["ok"] is True
assert len(result["alt_text"]) <= 125
assert len(result["tags"]) > 0
Run Locally
# Basic usage
praison recipes run product-photo-alt-text-writer \
--input '{"image_path": "shoe.jpg"}'
# With brand tone
praison recipes run product-photo-alt-text-writer \
--input '{"image_path": "watch.png", "brand_tone": "luxury", "locale": "en-GB"}'
Deploy & Integrate: 6 Integration Models
- Model 1: Embedded SDK
- Model 2: CLI Invocation
- Model 3: Plugin Mode
- Model 4: Local HTTP Sidecar
- Model 5: Remote Managed Runner
- Model 6: Event-Driven
from praisonai import recipe
result = recipe.run(
"product-photo-alt-text-writer",
input={"image_path": "product.jpg", "brand_tone": "luxury"}
)
if result.ok:
print(f"Alt: {result.output['alt_text']}")
print(f"Tags: {result.output['tags']}")
# Batch process product images
for img in products/*.jpg; do
praison recipes run product-photo-alt-text-writer \
--input "{\"image_path\": \"$img\"}" --json
done
class AltTextPlugin:
def generate(self, image_path, tone="neutral"):
from praisonai import recipe
return recipe.run(
"product-photo-alt-text-writer",
input={"image_path": image_path, "brand_tone": tone}
)
const response = await fetch('http://localhost:8765/recipes/product-photo-alt-text-writer/run', {
method: 'POST',
body: JSON.stringify({ image_path: '/uploads/product.jpg' })
});
response = requests.post(
"https://api.ecommerce-tools.com/alt-text",
headers={"Authorization": f"Bearer {api_key}"},
json={"image_url": "https://cdn.example.com/product.jpg"}
)
def on_product_image_uploaded(event):
import queue as q
job_queue = q.Queue() # Replace with SQS/RabbitMQ in production
job_queue.put({
"recipe": "product-photo-alt-text-writer",
"input": {"image_path": event['image_path']},
"callback_url": f"https://api.example.com/products/{event['product_id']}/alt-text"
})
Troubleshooting
Alt text too generic
Alt text too generic
Try using a more specific brand_tone or ensure the image is clear and well-lit.
Missing product details
Missing product details
The vision model works best with clear, well-composed product photos on neutral backgrounds.
Next Steps
- Document Summarizer - Summarize product descriptions
- Customer Support Reply Drafter - Handle product inquiries

