docs(21): capture phase context — AI preventivo struttura sezioni fisse, 3 tier
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
# Phase 21: Agente AI — generazione preventivo - Context
|
||||
|
||||
**Gathered:** 2026-06-20
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
L'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)
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## Implementation Decisions
|
||||
|
||||
### 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:
|
||||
1. **Situazione Attuale** — Claude descrive la situazione del cliente basandosi sul contenuto dei transcript (cosa sta vivendo, i pain emersi nelle call)
|
||||
2. **La Proposta** — Presentazione personalizzata dell'offerta: perché questa offerta è giusta per questo cliente specifico
|
||||
3. **Opzione A / Opzione B / Opzione C** — Una sotto-sezione per ogni tier, con lista dei servizi inclusi e prezzo pubblico del tier
|
||||
4. **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 `proposals` nel 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.
|
||||
|
||||
</decisions>
|
||||
|
||||
<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 schema `client_transcripts`, pattern query, struttura dati che l'AI deve leggere
|
||||
- `src/lib/lead-service.ts` — `getTranscripts(leadId)` → restituisce array con `{id, title, content, call_date}` ordinati per data DESC; il campo `content` è 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 e `public_price`; questa è la funzione da usare per costruire il contesto offerta nel prompt
|
||||
- `src/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 definire `proposals`
|
||||
- `src/db/migrations/0009_client_transcripts.sql` — ultimo esempio SQL migration a mano (struttura e convenzioni da replicare per `0010_proposals.sql`)
|
||||
|
||||
### UI & Integration Points
|
||||
- `src/components/admin/leads/LeadDetail.tsx` — struttura UI dove va aggiunto il trigger "Genera Preventivo" e la sezione bozza
|
||||
- `src/app/admin/leads/[id]/page.tsx` — page con `Promise.all` per fetch parallele (pattern da seguire, aggiungere fetch proposals)
|
||||
- `src/app/admin/leads/actions.ts` — pattern `requireAdmin` guard + `revalidatePath` (da seguire per le nuove actions proposals)
|
||||
|
||||
### Sicurezza & Architettura
|
||||
- `CLAUDE.md` → sezione Architecture Constraints: `quote_items` MAI esposti via client API; la nuova tabella `proposals` segue lo stesso principio — `content` non esposto via client token senza consenso esplicito Phase 22
|
||||
|
||||
</canonical_refs>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- `getTranscripts(leadId)` in `src/lib/lead-service.ts` — pronta, restituisce `content` integrale; concatenare i transcript in ordine cronologico per il prompt AI
|
||||
- `getOfferEditorData(macroId)` in `src/lib/offer-queries.ts` — restituisce tier A/B/C con array di servizi e `public_price` per tier; mappare questi dati nel prompt con nome servizi + prezzi
|
||||
- `nanoid` — già in uso nel progetto per PK; riusare per `proposals.id` e `proposals.slug`
|
||||
- `requireAdmin()` — 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 con `ON DELETE CASCADE` (o `SET NULL` se nullable). NON usare `drizzle-kit generate`.
|
||||
- **Prod-first migration**: la migration `0010_proposals.sql` DEVE 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.tsx` del lead usa `await Promise.all([...])` — aggiungere `getProposals(id)` nello stesso array.
|
||||
|
||||
### Integration Points
|
||||
- `src/app/admin/leads/[id]/page.tsx` — aggiungere `getProposals(id)` nel `Promise.all`, passare `proposals` a `<LeadDetail />`
|
||||
- `src/components/admin/leads/LeadDetail.tsx` — aggiungere sezione "Preventivo" con bottone trigger + form selezione offerta + textarea bozza
|
||||
- `src/db/schema.ts` — aggiungere definizione `proposals` + relazioni Drizzle
|
||||
- `src/lib/lead-service.ts` (o nuovo `src/lib/proposal-service.ts`) — `generateProposal(leadId, offerId)`, `getProposals(leadId)`, `saveProposal(id, content)`
|
||||
- `.env.local` — aggiungere `ANTHROPIC_API_KEY` (richiede configurazione in Coolify per produzione)
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## 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_price` del 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 (tipo `min-h-96`) e può editare liberamente prima di salvare.
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## Deferred Ideas
|
||||
|
||||
- **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.
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 21-Agente AI — generazione preventivo*
|
||||
*Context gathered: 2026-06-20*
|
||||
Reference in New Issue
Block a user