8.6 KiB
Phase 21: Agente AI — generazione preventivo - Context
Gathered: 2026-06-20 Status: Ready for planning
## Phase BoundaryL'admin apre il dettaglio di un lead, seleziona un'offerta e lancia la generazione: Claude legge i transcript del lead e i dati dell'offerta (tutti e 3 i tier) e produce una bozza di preventivo strutturata in sezioni fisse. L'admin rivede il testo in una textarea e salva la bozza prima che diventi pubblicabile in Phase 22.
In scope: prompt engineering + chiamata API Claude + bozza strutturata in sezioni + form di revisione/editing + tabella proposals nel DB
Out of scope: pagina pubblica /preventivo/[slug] (Phase 22), invio email (Phase 22), accettazione/rifiuto cliente (Phase 22)
Struttura output AI
- D-01: Claude produce sezioni strutturate fisse, non testo libero. Il prompt specifica esattamente i blocchi da riempire.
- D-02: Le sezioni del preventivo generato sono, in ordine:
- Situazione Attuale — Claude descrive la situazione del cliente basandosi sul contenuto dei transcript (cosa sta vivendo, i pain emersi nelle call)
- La Proposta — Presentazione personalizzata dell'offerta: perché questa offerta è giusta per questo cliente specifico
- Opzione A / Opzione B / Opzione C — Una sotto-sezione per ogni tier, con lista dei servizi inclusi e prezzo pubblico del tier
- Prossimi Passi — Call to action finale (Claude scrive testo standard, admin può editare)
- D-03: L'admin non sceglie un tier prima della generazione. Claude genera tutti e 3 i tier in un unico documento. Il cliente leggerà le 3 opzioni e sceglierà.
Claude's Discretion
Le seguenti aree non sono state discusse — Claude ha flessibilità:
- Builder location: Entry point nel LeadDetail esistente (
src/components/admin/leads/LeadDetail.tsx) — bottone "Genera Preventivo" che apre un modal o sezione inline con selezione offerta + trigger generazione. È il posto più naturale perché i transcript del lead sono già in contesto. - UX generazione: Fire-and-wait con spinner. Nessun streaming. L'admin clicca "Genera", vede uno stato di caricamento, poi il testo appare. Più semplice e affidabile per il caso d'uso (generazione ~5-15 sec).
- Editor bozza: Textarea non-formattata. Il preventivo generato appare in una
<textarea>grande che l'admin può editare liberamente prima di salvare. Nessun rich text editor in questa fase. - SDK:
@anthropic-ai/sdk(pacchetto ufficiale Anthropic). Nessun Vercel AI SDK o LangChain. - Modello:
claude-sonnet-4-6(modello attivo del progetto). - Storage: Nuova tabella
proposalsnel DB seguendo i pattern esistenti:id(nanoid PK),lead_id(FK → leads, nullable cascade),offer_id(FK → offer_macros),content(text NOT NULL — il testo completo della bozza),slug(text unique — nanoid, per il link pubblico Phase 22),status(text:draft|published),created_at(timestamp with timezone). - Migration: SQL a mano come
0010_proposals.sql(drizzle-kit generate è rotto). Applicare a prod via SSH prima di pushare il codice. - Server action / API route: Server Action Next.js per il trigger di generazione (pattern coerente col resto del progetto). La chiamata Anthropic avviene lato server.
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Requirements & Roadmap
.planning/ROADMAP.md— Phase 21 goal, success criteria (AI-01/AI-02), dipendenze (Phase 20 + DB offerte Phase 11/12).planning/REQUIREMENTS.md— AI-01 (generazione), AI-02 (revisione bozza)
Contesto Phase 20 (transcript — input dell'AI)
.planning/phases/20-knowledge-base-cliente/20-CONTEXT.md— Decisioni schemaclient_transcripts, pattern query, struttura dati che l'AI deve leggeresrc/lib/lead-service.ts—getTranscripts(leadId)→ restituisce array con{id, title, content, call_date}ordinati per data DESC; il campocontentè testo integrale, non troncato
Dati offerta (input dell'AI)
src/lib/offer-queries.ts—getOfferEditorData(macroId)→ restituisce i dati completi dell'offerta inclusi tier A/B/C con servizi epublic_price; questa è la funzione da usare per costruire il contesto offerta nel promptsrc/app/admin/offers/actions.ts— pattern server actions per offerte (riferimento per nuovo layer proposals)
Schema & Migrations
src/db/schema.ts— pattern tabelle esistenti:leads,activities,clientTranscripts,offer_macros— usare per definireproposalssrc/db/migrations/0009_client_transcripts.sql— ultimo esempio SQL migration a mano (struttura e convenzioni da replicare per0010_proposals.sql)
UI & Integration Points
src/components/admin/leads/LeadDetail.tsx— struttura UI dove va aggiunto il trigger "Genera Preventivo" e la sezione bozzasrc/app/admin/leads/[id]/page.tsx— page conPromise.allper fetch parallele (pattern da seguire, aggiungere fetch proposals)src/app/admin/leads/actions.ts— patternrequireAdminguard +revalidatePath(da seguire per le nuove actions proposals)
Sicurezza & Architettura
CLAUDE.md→ sezione Architecture Constraints:quote_itemsMAI esposti via client API; la nuova tabellaproposalssegue lo stesso principio —contentnon esposto via client token senza consenso esplicito Phase 22
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
getTranscripts(leadId)insrc/lib/lead-service.ts— pronta, restituiscecontentintegrale; concatenare i transcript in ordine cronologico per il prompt AIgetOfferEditorData(macroId)insrc/lib/offer-queries.ts— restituisce tier A/B/C con array di servizi epublic_priceper tier; mappare questi dati nel prompt con nome servizi + prezzinanoid— già in uso nel progetto per PK; riusare perproposals.ideproposals.slugrequireAdmin()— già in ogni server action, obbligatorio anche per le nuove actions proposals
Established Patterns
- Migration a mano:
CREATE TABLE IF NOT EXISTS, tipi Postgres espliciti, FK conON DELETE CASCADE(oSET NULLse nullable). NON usaredrizzle-kit generate. - Prod-first migration: la migration
0010_proposals.sqlDEVE essere applicata a prod via SSH+docker exec (ssh -L 54321:localhost:54321 root@178.104.27.55) PRIMA di pushare il codice che la referenzia. - Server Actions con revalidatePath: ogni mutation chiama
revalidatePath()sul path del lead. - Promise.all fetch:
page.tsxdel lead usaawait Promise.all([...])— aggiungeregetProposals(id)nello stesso array.
Integration Points
src/app/admin/leads/[id]/page.tsx— aggiungeregetProposals(id)nelPromise.all, passareproposalsa<LeadDetail />src/components/admin/leads/LeadDetail.tsx— aggiungere sezione "Preventivo" con bottone trigger + form selezione offerta + textarea bozzasrc/db/schema.ts— aggiungere definizioneproposals+ relazioni Drizzlesrc/lib/lead-service.ts(o nuovosrc/lib/proposal-service.ts) —generateProposal(leadId, offerId),getProposals(leadId),saveProposal(id, content).env.local— aggiungereANTHROPIC_API_KEY(richiede configurazione in Coolify per produzione)
</code_context>
## Specific Ideas- Il preventivo è multi-tier by design: non c'è una scelta del tier nella UI di generazione. Claude scrive 3 opzioni (A/B/C) in un solo documento, il cliente legge e sceglie.
- La sezione "Opzione A/B/C" deve mostrare i nomi dei servizi inclusi in modo leggibile (non JSON grezzo), e il
public_pricedel tier in modo prominente. - La sezione "Situazione Attuale" è la parte più personalizzata — Claude deve pescare dai transcript specifici insights sul cliente, non frasi generiche. Il prompt deve guidare su questo.
- L'admin in fase di review vede l'intero testo del preventivo in una
<textarea>di altezza generosa (tipomin-h-96) e può editare liberamente prima di salvare.
- Scelta tier nel preventivo: eventualmente l'admin potrebbe scegliere un tier da presentare → Phase 22 o fase futura post v2.2.
- Streaming output AI: Claude scrive in real-time → futura ottimizzazione UX se la latenza diventa un problema.
- Versioning bozze: mantenere storico delle generazioni per un lead → futura fase.
- Agente multi-step con tool_use: architettura più sofisticata dove Claude chiama tool API invece di ricevere dati pre-imbarcati nel prompt → futura fase.
Phase: 21-Agente AI — generazione preventivo Context gathered: 2026-06-20