Smart rewrite of existing seo_content using GPT-5 Mini — multi-country, any language. Includes 3 improvements: service registry injection, mandatory empathy opener, and quality gate with V3 fallback. ~13× cheaper than V1.
V2 rewrites the existing seo_content from your service JSON using gpt-5-mini-2025-08-07. No web search — with a choice of 4 article plans (A–D), 13 sections including mandatory FAQ (§12), and 3 built-in improvements for data accuracy and quality.
| Attribute | Value |
|---|---|
| Model | gpt-5-mini-2025-08-07 |
| Web searches | None |
| Input source | Existing seo_content from service JSON (up to 4,000 chars) |
| Article structure | 13 sections (Plans A/B/C/D) — includes mandatory FAQ §12 |
| Avg article length | 1,600–2,000 words |
| Avg quality score | 8/10 |
| Cost per article | ≈ €0.007–0.010 |
| Avg generation time | 45–75 seconds |
| Parallelisable | Yes — 5+ workers simultaneously |
| Cost × 1,000 articles | ≈ €7–10 |
┌─────────────────────────────────────────────────────┐ │ SERVICE DATA (name, category, notes, keywords…) │ └────────────────────────┬────────────────────────────┘ │ ┌──────────────▼──────────────────┐ │ SINGLE API CALL │ │ │ │ Rewrite Prompt (Plan A/B/C/D) │ gpt-5-mini │ + Service DATA block │ max_completion_tokens=8000 │ + SOURCE CONTENT (seo_content) │ streaming=true │ + 13-section structure + FAQ │ │ Reformulate sentences, improve │ │ structure & Billoff angle │ └──────────────┬───────────────────┘ │ REWRITTEN ARTICLE (HTML, ~1,800 words, 13 H2, 2+ tables, FAQ)
V2 assembles 4 layers of context for each article:
── SYSTEM MESSAGE ────────────────────────────────────────────── ══ VERIFIED SERVICE REGISTRY (improvement 1) ══ notice_period: 30 ← pre-populated from svc.notice_period_days early_exit_fee: null ← null → "check current terms" cooling_off_window: 14 cancellation_url: "https://..." regulator_name: "ACCC" Rule: null field → write "check current terms at {name} website" — NEVER estimate. ── USER PROMPT ───────────────────────────────────────────────── ══ EMPATHY OPENER (improvement 2) ══ Cancelling {name} in {country} should be simple — but between contract lock-ins, notice periods, and unexpected fees, many people find the process harder than it should be. This guide gives you every step, every right, and every shortcut you need... ══ SERVICE DATA ══ Name: {name} | Keyword: {main_keyword} Category: {category} | Website: {website} Cancellation methods: phone, email, account-online Country: {country} | Currency: {currency} ({symbol}) ← from locale Language: {language} | Action: {cancel_word} ← from locale ══ SOURCE CONTENT (rewrite — do NOT copy sentences verbatim) ══ {existing seo_content from service JSON, up to 4,000 chars}
After generation, a quality gate checks the output: min 1,800 words AND min 10 numeric data-points. If below either threshold and V3_FALLBACK_ENABLED = True, the article is automatically re-generated via V3 (Gemini), and the escalation reason is logged.
Heading capitalisation — enforced in system message + QUALITY CHECK self-scan: sentence case on every H1/H2/H3/H4. First word capitalised, proper nouns capitalised, everything else lowercase. Post-generation sanitiser applies as final safety net.
── DATA RETENTION RULE (improvement 4, injected before ══ OUTPUT ══) ─── ══ DATA RETENTION RULE ══ The source content contains 37 specific data points. Your rewrite MUST preserve a minimum of 14 of them verbatim. RULE: NEVER generalise specific figures. If the source says "$150 exit fee", write "$150" — NOT "a fee may apply". KEY FIGURES FROM SOURCE: $14.99 | $24.99 | 30 days | 14 days | 10% | ... ✅ Self-check: count specific numbers/dates/durations — must be ≥ 14. # min_required = max(10, floor(source_count × 0.4)) — e.g. 37 × 0.4 = 14 # Cost impact: neutral to +€0.001/article (token overhead <5%)
V2 generates articles in any language and currency — no code changes needed. Add locale fields to each service row in your JSON; the script resolves them automatically via get_locale(svc).
| Field | Example (AU) | Example (DE) | Effect in prompt |
|---|---|---|---|
country | Australia | Germany | Market name in mission statement |
language | English | German | Article language instruction (LANGUAGE: German only) |
currency | AUD | EUR | Currency label in pricing table |
currency_symbol | A$ | € | Symbol in inline price references |
cancel_word | Cancel | Kündigung | How-to-cancel heading terminology |
consumer_law | ACL/ACCC | BGB / Verbraucherschutz | Rights section (§7) legal reference |
Fallback chain: svc.country → editor config → script defaults. Any missing field is silently defaulted — you can migrate countries incrementally.
Output JSON now includes cost_usd, cost_eur and elapsed per article, plus a total cost summary at the end of the run.
| Component | Tokens (avg) | Cost (USD) | Cost (EUR) |
|---|---|---|---|
| Prompt tokens | ~1,900 | $0.00076 | €0.00070 |
| Completion tokens | ~5,700 | $0.00912 | €0.00839 |
| TOTAL per article | ~7,600 | ~$0.010 | ~€0.009 |
| × 20 articles | ~152,000 | $0.20 | €0.18 |
| × 1,000 articles | ~7.6M | $9.88 | €9.09 |
| × 50,000 articles | ~380M | $494 | €455 |
Model pricing: Input $0.40/1M tokens, Output $1.60/1M tokens (gpt-5-mini-2025-08-07).
# Run V2 batch (all 20 sample services, 5 parallel workers) python scripts/02_generate_v2.py # Output: Billoff/data/results_v2.json # Run 3-method comparison on 1 service python scripts/test_compare_3methods.py --service "Netflix"
# V2 runs with 5 concurrent workers (no rate limit concern for direct completions) from concurrent.futures import ThreadPoolExecutor MAX_WORKERS = 5 with ThreadPoolExecutor(max_workers=MAX_WORKERS) as ex: futures = {ex.submit(process_service, svc): svc for svc in services} for future in as_completed(futures): results.append(future.result())
| Metric | Result | vs V1 |
|---|---|---|
| Word count | 1,927 | −192 (−9%) |
| Tables | 4 | Same |
| H2 sections | 14 | Same |
| H3 sub-sections | 40 | +1 (better!) |
| Company fact box | ✅ | Same |
| FAQ | ✅ | Same |
| Quality score | 9/10 | −1 |
| Cost | €0.011 | 10× cheaper |
| Time | 95s | −31% faster |
| ✅ Pros | ❌ Cons |
|---|---|
| 10× cheaper than V1 | No real-time pricing data |
| Parallelisable (5+ workers) | Competitors may not have current prices |
| Consistently good structure (14 H2) | Company info may be outdated (training cutoff) |
| 9/10 quality score in tests | Less reliable for niche/local services |
| Best cost/quality ratio for scale | No verified source citations |
| Works offline (no web dependency) | May hallucinate pricing for obscure services |
A writing persona is appended to the system message for every article. Set PERSONA_ID at the top of the downloaded Python script to switch voice. Available personas: cancellation_specialist (default), consumer_rights_expert, contract_lawyer, financial_advisor. See V1 Docs for the full persona reference.
Once all 4 methods complete, the Lab runs a 2-phase parallel analysis and displays a full comparison report. The analysis is automatically saved to history alongside the article results.
| Phase | Model | Role |
|---|---|---|
| Phase 1 (parallel) | claude-haiku-4-5 × 4 | One eval per method → structured JSON (scores, E-E-A-T, improvements) |
| Phase 2 (streaming) | claude-sonnet-4-6 + Extended Thinking | Comparative synthesis → full HTML report (scorecard · E-E-A-T · winners · recommendation · improvement plan) |
Total cost per analysis: ≈€0.04–0.06. See V1 Docs for the full specification.