Vache prompts. Claude codes.How it works

Building a Fashion Trend Intelligence Pipeline for $3/Month

·6 min read·by Vache Sarkissian
Updated June 3, 2026
·
Reviewed March 29, 2026
automationlocal-gpuollamafashionmarp
📚Top of Funnel

Written by Claude (Opus 4.6) Vache prompted, reviewed, and published. The data and benchmarks are real; the prose is AI-generated.

I wanted automated fashion trend research. Not a dashboard or a SaaS product — a pipeline that monitors industry sources, extracts what matters, and hands me a presentation-ready weekly report. The budget was essentially zero.

The result: 6 RSS feeds monitored daily by a local 12B model, weekly analysis by Sonnet and Opus, and a polished Marp slide deck with real article images. Total cost: ~$0.75/week.

The Architecture

RSS Feeds (6 sources) → daily 08:00 → gemma3:12b (FREE)
    └→ Signal extraction + image URLs
    └→ Accumulates in heartbeat logs all week

Sunday 09:00 → Sonnet 4.6 ($0.25) — "Research Analyst"
    └→ Aggregates 7 days, ranks trends, identifies patterns

Sunday 09:30 → Opus 4.6 ($0.50) — "Editor-in-Chief"
    └→ Produces Marp slide deck with embedded images
    └→ Auto-converts to HTML slides
    └→ Telegram notification with summary

Three models, three roles. The local model does the daily grind for free. Sonnet does analytical heavy lifting once a week. Opus polishes it into something you'd show a client.

RSS Image Extraction

The first challenge was getting real images from the articles being discussed. Fashion reporting without visuals is just... text about clothes.

RSS feeds bury images in different places depending on the publisher. I built a priority-based extractor in the Python parser:

def extract_image(item, desc_raw):
    # 1. media:thumbnail (Vogue uses this — 100% hit rate)
    thumb = item.find('media:thumbnail', media_ns)
    if thumb is not None:
        u = thumb.get('url', '')
        if u: return u
 
    # 2. media:content with medium="image"
    for mc in item.findall('media:content', media_ns):
        if mc.get('medium') == 'image':
            u = mc.get('url', '')
            if u: return u
 
    # 3. enclosure with type="image/*"
    for enc in item.findall('enclosure'):
        if enc.get('type', '').startswith('image/'):
            u = enc.get('url', '')
            if u: return u
 
    # 4. First <img> in description HTML (fallback)
    m = re.search(r'<img[^>]+src=["\']([^"\']+)["\']', desc_raw)
    if m: return m.group(1)
    return ''

The Yahoo MRSS namespace (http://search.yahoo.com/mrss/) is key — most fashion publishers use media:thumbnail or media:content for high-resolution images.

The Token Budget Problem

Here's where it got interesting. Business of Fashion image URLs look like this:

https://img.businessoffashion.com/resizer/v2/25AWNHQDBNGKBCSLZ6GM22N65A.jpg
  ?auth=8e4bef4b7630a4f67e3bcfa8262f49a0382fe4eb0dfb51c38ed7b0fa1ecb44dd
  &smart=true&width=4101&height=2307

That's ~175 characters per image URL, mostly auth tokens. With 10 items, that's 1,750 characters just in URLs. gemma3:12b runs at a 4,096 token context window on my GPU. The prompt template, instructions, and article data were already pushing 3K tokens. Adding full image URLs blew past the limit and the model hung indefinitely.

The solution: strip query params before sending to the LLM, cache full URLs separately.

# Cache full items (with auth tokens) for downstream use
echo "$limited_items" > "$CACHE_DIR/fashion-signals-latest-items"
 
# Strip query params for the LLM (saves ~1,750 tokens)
trimmed_items=$(echo "$limited_items" \
  | sed 's|\(https\?://[^|?]*\)?[^|]*|\1|g')

gemma3:12b sees clean base URLs. The weekly report collector reads the cached files and passes full URLs to Opus, which has a much larger context window.

The 403 Problem

This created a second bug I didn't catch until the first report was generated. Opus received trimmed URLs from the daily signal logs (which gemma had already processed) and used those in the slide deck. The IMAGE URL REFERENCE section with full URLs was at the bottom of the prompt — Opus ignored it.

The result: all 5 images in the report returned HTTP 403. BOF's image CDN requires the ?auth= parameter.

Relying on the LLM to cross-reference URLs is unreliable. The fix: deterministic post-processing in the heartbeat engine.

# After Opus writes output, before Marp conversion
url_map = {}
for f in glob.glob(os.path.join(cache_dir, '*-items*')):
    for line in open(f):
        parts = line.strip().split('|')
        if len(parts) >= 8 and parts[0] == 'NEW_ITEM':
            img_url = parts[7].strip()
            if img_url and '?' in img_url:
                base = img_url.split('?')[0]
                url_map[base] = img_url
 
content = open(out_file).read()
for base, full in url_map.items():
    if base in content and full not in content:
        content = content.replace(base, full)

Simple string replacement. No LLM behavior dependency. All 5 images went from 403 to 200.

Marp Slide Deck Generation

The weekly report needed to be a presentation, not a document. Marp converts Markdown to HTML slides — perfect fit since the report was already Markdown.

The Opus prompt specifies Marp frontmatter and slide structure:

marp: true
theme: default
paginate: true
style: |
  h2 {
    color: #16213e;
    border-bottom: 2px solid #e94560;
  }

Each major section becomes a slide separated by ---. The heartbeat engine detects marp: true in the output and auto-converts:

if echo "$result" | head -5 | grep -q "marp: true"; then
    marp --no-stdin "$out_path/$out_filename" \
         -o "$out_path/${task_name}-${today}.html" \
         --allow-local-files
fi

This produces both .md (viewable in Obsidian) and .html (browser slides with arrow-key navigation) in the outputs directory.

VRAM Contention: The Silent Killer

The hardest bug wasn't code — it was GPU resource management. During testing, the heartbeat timer fired a deps-monitor task (qwen3:8b, 5GB) while fashion-signals (gemma3:12b, 9.5GB) was running. Two models in 16GB VRAM: 14.5GB total. The GPU didn't crash — it just slowed to a crawl as models swapped in and out of VRAM.

Worse: killing the parent curl process doesn't cancel an in-flight Ollama generation. The runner keeps consuming 100% GPU, owned by the ollama system user, requiring sudo systemctl restart ollama to clear.

The fix was operational: stop the heartbeat timer during manual testing, and ensure the 10-minute Ollama keep-alive window prevents model overlap during normal scheduled runs.

The Output

The first weekly report covered London Fashion Week and the BAFTAs. 11 slides, 5 embedded images, 8 trends tracked. The pipeline correctly identified the trench coat as the week's dominant silhouette signal (cross-source, multi-day persistence from Burberry's LFW show) and flagged Trump's tariff whiplash as the top economic disruption.

The Telegram summary hits my phone Sunday morning:

📊 Fashion Weekly Report — Feb 17-23

TOP TRENDS:
1. Trench (Sustained) — Burberry LFW, cross-source
2. Burberry (Sustained) — Daniel Lee's dark LFW closer
3. Trump Tariffs (Spike) — 10%→15%→Supreme Court reversal
...

📎 Full slides: outputs/fashion-weekly-report-2026-02-23.html

Numbers

MetricValue
RSS sources6 (Vogue, WWD, WWD Fashion, FashionNetwork, Fashionista, BOF)
Daily extraction~10 items, 17-20 seconds (gemma3:12b)
Weekly analysis68s (Sonnet) + 84s (Opus)
Weekly cost~$0.75 (Sonnet $0.25 + Opus $0.50)
Monthly cost~$3
Output formatsMarp markdown + HTML slides + Telegram summary
Images per report5 (from article thumbnails)

What I'd Do Differently

The URL trimming + caching architecture works but adds complexity. If I were starting over, I'd either use a model with a larger context window for daily extraction (qwen3:8b at 16K would handle full URLs easily) or store extracted items in a proper SQLite database instead of pipe-delimited text files.

The three-model pipeline (local → Sonnet → Opus) might be overkill. Sonnet alone could probably produce the final report. But the local model keeps daily costs at zero, and the Opus polish is noticeably better — it writes like an editor, not an analyst.

Further Reading

About the Author

Vache Sarkissian

Building research infrastructure and products at the intersection of knowledge systems and machine learning. Creator of Linesheet Pro, vault-search, and the vachsark learning engine.

View Full Bio →
© 2026 Vache Sarkissian·Built with Claude Code
vachsark.com