Tavily + Perplexity Sonar · strategia costi, guardrail e uso iper-personalizzato

Vincolo: niente abilitato in prod finché cap + monitoraggio non sono in piedi (lo sono, vedi §4). I prezzi sono verificati dal web (giugno 2026), non dal training.

0. TL;DR

  • Costo unitario reale: Tavily $0 su Free (o $0.016/ricerca PAYG) · Perplexity ~$0.0055/check (il request-fee low pesa il 90%).
  • Costo a 500 clienti: Tavily ~$0/mese (condiviso, sublineare nei combo) · Perplexity ~$8/mese atteso, ≤$50–165/mese anche al cap rigido. < 0,4% del ricavo/cliente in ogni scenario.
  • Il rischio non è il margine, è l'abuso non limitato. I guardrail tagliano la coda di rischio + danno osservabilità.
  • Spedito (PR1): cap mensile Perplexity (Pro 30 / Business 100, hard), gating su route + hook publish, pin model=sonar+context=low, costo reale loggato, ledger external_api_usage + guard Sentry, budget-guard Tavily, grana-città (Tavily iperlocale).
  • Compliance: Perplexity → sub-processor secondary (preavviso 30gg, efficace 2026-07-10) · Tavily → non sub-processor (fonte dati, zero PII). Ratifica audit 08/06.
  • Founder checklist: §8.
  • Stato 2026-06-06 sera · TUTTO CHIUSO: #277 (guardrail + città + compliance) MERGED + deployato + verificato live (probe /api/reach/visibility-check dal workspace founder → 404 no_published_site = oltre il check-key → key Perplexity attiva + tier-gate + cap enforcement deployati, zero costo perché 404 prima della call Sonar). Key Tavily + Perplexity inserite + redeploy (founder). #282 (flywheel competitor→Content) MERGED + deployato (CI verde dopo lo sblocco billing). Grana-città live. Billing GitHub Actions risolto (budget Actions era già $40 · le fail erano run pre-fix con budget $0 · ri-lanciata la CI → verde). Primo uso reale: Sonar al primo sito pubblicato · Tavily al cron del lunedì (riga external_api_usage provider=tavily).
  • Dogfooding 2026-06-06 sera · PRIMO CHECK PERPLEXITY REALE eseguito (workspace Multiverbe · sito-test minimale creato→archiviato per sbloccare la route): 200 in 6,6s, costo reale $0,00572 (costIsReal:true, dal billing del provider, = stima $0,0055), 9 competitor citati, flywheel alimentato. Confronto con la competitive-analysis BOS: ✅ bexio citato (conferma il #1 documentato) · 🔴 GAP: KLARA (klara.ch) è la PRIMA raccomandazione dell'AI per il positioning Multiverbe (svizzero all-in-one PMI) ma manca dal BOS (+ Giobby) → la feature ha trovato un competitor di testa mancante. Task task_c2b7d487 per aggiungerlo. Pipeline verificata end-to-end dal vivo.

1. Assunzioni esplicite

Parametro Valore Fonte
Tavily search_depth advanced = 2 crediti/ricerca codice web-trends.ts + docs
Tavily frequenza 1 ricerca per combo (settore×zona[×città]), cron settimanale (Lun 05:00) ×4,33/mese content-market-context.ts
Tavily Free 1.000 crediti/mese, no carta docs.tavily.com
Tavily PAYG $0.008/credito docs.tavily.com
Perplexity modello sonar, search_context_size: low (pin) perplexity-sonar.ts
Perplexity token ~100 in + ~400 out (system IT + risposta breve) stima da payload max_tokens 600
Perplexity prezzi token $1/$1 per 1M (in/out) · request-fee $5/1k a low docs.perplexity.ai
Perplexity trigger 1 al publish (raro) + check manuali on-demand · solo Pro/Business publish.ts + visibility-check.ts
Cambio $1 ≈ CHF 0,90 corrente
Eleggibili Perplexity ~60% dei clienti (Starter escluso) pricing tier

2. Prezzi verificati dal web (giugno 2026)

Costo reale per chiamata:

Calcolo Totale
Tavily advanced 2 cr × $0.008 $0.016 ($0 su Free)
Perplexity sonar low 100/1M·$1 + 400/1M·$1 + $0.005 fee = $0.0001 + $0.0004 + $0.005 ≈ $0.0055 ≈ CHF 0,005

Il codice precedente stimava sonar = $0.003 (–45%, non contava il request-fee). Corretto + ora il costo reale è letto da usage.cost.total_cost quando il provider lo riporta.

3. Modello costo a 5 / 50 / 500 clienti

Tavily — costo CONDIVISO (non per-cliente), sublineare nei combo

Clienti Combo stimati¹ Crediti/mese Costo TOTALE/mese
5 ~4 ~35 $0 (Free)
50 ~20 ~175 $0 (Free)
500 ~70 ~610 $0 (Free)
500 patologico (196 combo) 196 ~1.700 ~$5,6 totali

¹ I combo sono limitati dalle coppie (settore × zona × città) REALI dei clienti, non dal numero di clienti: molti ristoranti a Lugano = 1 combo. Tetto $ vero = il piano Tavily: tenuto Free (no carta) = tetto rigido $0; esauriti i crediti l'adapter fail-open sul baseline.

⚠️ La grana-città (§5) alza i combo da ~per-zona a ~per-città: a 500 clienti potrebbe sfiorare/superare i 1.000 crediti free (~$2–3/mese PAYG). Resta trascurabile; il budget-guard + Free lo coprono.

Perplexity — per-cliente (Pro/Business), atteso vs cap

Clienti Eleggibili Atteso $/mese² CHF/cliente atteso Worst-case al cap
5 3 $0,08 ~CHF 0,02 $0,50
50 30 $0,83 ~CHF 0,025 ~$5
500 300 $8,25 ~CHF 0,025 ~$50 (Pro) / ~$165 (tutti Business)

² ~5 check/mese per cliente eleggibile (1 publish + 4 manuali).

Impatto a margine

Tier Prezzo/mese Perplexity atteso Perplexity al cap % ricavo (cap)
Pro CHF 149 ~CHF 0,02 CHF 0,15 (30 check) 0,1%
Business CHF 249 ~CHF 0,05 CHF 0,50 (100 check) 0,2%

Tavily + Perplexity combinate: < 0,2% del ricavo/cliente a regime, < 0,4% ai cap rigidi. Il moat (zero-commissioni, hosting CH) non è toccato.

Conclusione strategica: il rischio non è l'economia unitaria — è (a) abuso non limitato di /visibility-check, (b) hook publish senza cap, (c) flip a sonar-pro/high (×3–10), (d) bug che esplode i combo Tavily, (e) zero osservabilità. I guardrail (§4) chiudono tutti e cinque.

4. Guardrail spediti (PR1) — defense-in-depth

# Guardrail Dove
1 Cap mensile ai_visibility_checks (Starter 0 / Pro 30 / Business 100, hard_cap) migration 2026-06-06_external_search_apis_guardrails.sql (applicata prod)
2 Enforcement server-side (402 al superamento), enforced-always ma fail-open api/_lib/tier-cap-check.ts
3 Cap pre-Sonar sulla route manuale + sull'hook di publish visibility-check.ts · publish.ts
4 Pin model='sonar' + search_context_size='low' esplicito perplexity-sonar.ts
5 Costo reale loggato (usage.cost.total_cost, fallback calcolato) perplexity-sonar.ts · engine.ts
6 Ledger unico external_api_usage (Perplexity per check, Tavily per combo) api/_lib/external-api-usage.ts
7 Guard spesa mensile per provider → Sentry + email founder api/cron/tier-caps-alerts.ts
8 Tavily budget-guard: combo cap/run (400) + Free = tetto $0 content-market-context.ts

Soglie anomalia (env-tunabili): Perplexity $25/mese · Tavily $8/mese (≈ valore dei 1.000 crediti free → superarlo = "Free esaurito"). Default alti vs atteso → scattano solo su vera anomalia.

Tier-gating Perplexity confermato: route visibility-check.ts 403 per Starter/tier sconosguto (least-privilege); hook publish già gated Pro/Business. + cap ora su entrambi.

5. 🧠 Uso iper-intelligente — architettura "Ambient + Sharp"

Layer 1 · Ambient (condiviso, ~gratis, cron): trend settore×zona via Tavily→Vertex EU. Già live. Ogni tenant riceve il "rumore di fondo" tempestivo.

Layer 2 · Sharp (per-cliente, ~$0 incrementale perché riusa cron o dati già catturati):

  • (A) Grana-città — SPEDITO. La chiave del cron passa da (settore, zona) a (settore, zona, città) quando la città è nota (da sites.brand_dna.locality). Un palestra di Lugano e una di Bellinzona non condividono più i trend. Query iperlocale "a Lugano" invece di "in Ticino", localizzata per lingua zona (de→"in", fr→"à"). Fail-soft: città assente → grana-zona (comportamento storico). Costo: bounded dalle città reali dei clienti, dentro il Free.
  • (B) Flywheel competitor → Content — DA FARE (dopo Perplexity live). Perplexity ci dice chi cita l'AI per "miglior {settore} a {città}" = i veri concorrenti locali (ai_visibility_results.cited_domain). Oggi quel dato muore in un cruscotto Reach. Rimandarlo nel Content planet come angoli di differenziazione: "Le AI consigliano [3 concorrenti] a {città}. Ecco 3 contenuti dove sono deboli." Zero call extra. Richiede che Perplexity abbia già girato e catturato dati (per questo è post-attivazione).
  • (C) Strategia query — DA FARE. Ruotare i 3 intent (discovery→category→branded "{nome azienda}", il gap più temuto da una PMI) sui check schedulati → quadro 360° allo stesso costo/check. (D) Locale Perplexity dalla zona (cliente di Zurigo interroga in DE, non IT) → citazioni più pertinenti.

Sequenza onesta: A + guardrail ora (Tavily non dipende da Perplexity). B/C/D dopo l'attivazione di Perplexity (il flywheel non ha carburante finché Perplexity non gira).

6. Classificazione sub-processor (allineata all'audit 08/06)

  • Perplexity → sub-processor secondary (già deciso founder/codice). Aggiunto a subprocessors.ts (v2026-06-09) + SUBPROCESSOR_CHANGELOG. Preavviso 30gg revFADP art. 9: annuncio 2026-06-09, efficace 2026-07-10. Query non-PII ma check per-cliente + risultati per-workspace → disclosure conservativa. ⚠️ announcedOn = data reale di deploy. DPA URL omessa finché non verificata dal web.
  • Tavily → NON sub-processor (fonte dati). Riceve solo settore+zona+stagione, zero PII/dato cliente, indice pubblico condiviso. Non tratta dati personali per conto del titolare → fuori da art. 9/28. Disclosure come "fonte dati" in sub-processors.md. Ratifica audit 08/06.

Conseguenza chiave: l'esposizione di Perplexity a clienti esterni parte solo dopo il preavviso (~2026-07-10). Il workspace interno del founder (nessun titolare terzo da notificare) può dogfoodare subito. Tavily non ha questo vincolo (non è sub-processor) → live appena PR1 in master + key inserita + Tavily su Free.

7. Cosa è stato spedito / deferito

PR1 (cost-safety + città) — code repo, CI-verde, NON auto-merge:

  • migration applicata prod (additiva, RLS deny-by-default sul ledger; advisor: solo INFO rls_enabled_no_policy, stessa postura voluta di idempotency_keys).
  • 9 file codice + 3 test (40 test verdi: cost-math, aggregatore spesa, grana-città, compliance changelog).

PR2 (compliance)subprocessors.ts + questo doc + sub-processors.md. Ratifica date all'audit 08/06.

Deferito (post-attivazione Perplexity): flywheel competitor→Content (B), intent rotation (C), locale-by-zone (D). Regen supabase.ts types: trivial follow-up (nessun gate di drift, tabella non referenziata dal client tipato).

8. 🔴 DA FARE — founder

✅ RISOLTO #0 — Billing GitHub Actions (2026-06-06 sera): il budget Actions era già a $40 (Settings → Billing → Budgets and alerts · le fail erano run pre-fix con budget $0/minuti inclusi esauriti). Ri-lanciata la CI di #282 → verde → mergiato. GitHub a posto, CI ripartita repo-wide (sblocca anche le PR growth/sales delle altre sessioni). Era il collo v88.

  1. Tavily (#277 già in master → può andare live):
    • Tenere l'account Tavily su Free (NESSUNA carta) = tetto rigido $0.
    • Inserire TAVILY_API_KEY (da app.tavily.com) su Vercel multiverbe-app → Production + Preview, SensitiveRedeploy. (SEARCH_PROVIDER=tavily già presente.)
    • Verifica: lunedì prossimo il cron scrive righe tavily in external_api_usage.
  2. Perplexity (gate compliance):
    • Attivare billing pay-as-you-go su console.perplexity.ai.
    • Inserire PERPLEXITY_API_KEY su Vercel multiverbe-app (Prod+Preview, Sensitive) → Redeploy.
    • Esterni solo dal 2026-07-10 (preavviso 30gg). Interno/founder: dogfooding subito OK.
    • Verificare URL DPA Perplexity dal web → aggiungere dpaUrl in subprocessors.ts.
  3. Compliance: ratificare le classificazioni (Perplexity sub-processor / Tavily fonte dati) al subprocessor-completeness-audit di lun 08/06; confermare announcedOn = data reale di deploy (e effectiveFrom = +31gg). Aggiornare iubenda 92115618 + DPA cliente template.
  4. Review PR1 + PR2 (non auto-merge). Post-merge: 1 check manuale di visibilità sul workspace founder → confermare riga ledger + costo reale.
  5. (Opz.) Tarare le soglie Sentry via env EXTERNAL_SPEND_ALERT_PERPLEXITY_USD / _TAVILY_USD.

Ref: subprocessors.ts · sub-processors.md · tier-cap-check.ts · external-api-usage.ts · migration 2026-06-06_external_search_apis_guardrails.sql · skill multiverbe-ch-compliance.