docs(01): create phase plan
Phase 01: Core Generation Pipeline - 4 plan(s) in 3 wave(s) - Wave 1: 01-01 (infra) + 01-02 (core services) parallel - Wave 2: 01-03 (LLM pipeline + API routers) - Wave 3: 01-04 (Web UI) with human-verify checkpoint - Ready for execution
This commit is contained in:
311
.planning/phases/01-core-generation-pipeline/01-02-PLAN.md
Normal file
311
.planning/phases/01-core-generation-pipeline/01-02-PLAN.md
Normal file
@@ -0,0 +1,311 @@
|
||||
---
|
||||
phase: 01-core-generation-pipeline
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- backend/services/__init__.py
|
||||
- backend/services/calendar_service.py
|
||||
- backend/services/format_selector.py
|
||||
- backend/services/prompt_service.py
|
||||
- backend/schemas/__init__.py
|
||||
- backend/schemas/calendar.py
|
||||
- backend/schemas/generate.py
|
||||
- backend/data/format_mapping.json
|
||||
- backend/data/prompts/system_prompt.txt
|
||||
- backend/data/prompts/pas_valore.txt
|
||||
- backend/data/prompts/listicle_valore.txt
|
||||
- backend/data/prompts/bab_storytelling.txt
|
||||
- backend/data/prompts/aida_promozione.txt
|
||||
- backend/data/prompts/dato_news.txt
|
||||
- backend/constants.py
|
||||
autonomous: true
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "CalendarService genera esattamente 13 slot con distribuzione PN corretta (4 valore, 2 storytelling, 2 news, 3 riprova, 1 coinvolgimento, 1 promo)"
|
||||
- "Ogni slot ha livello Schwartz assegnato con distribuzione corretta (L5+L4=6, L3=4, L2=2, L1=1)"
|
||||
- "FormatSelector mappa ogni combinazione tipo_contenuto x livello_schwartz a un formato narrativo"
|
||||
- "PromptService carica, lista e compila prompt .txt con sostituzione variabili"
|
||||
- "CANVA_FIELDS e' definito come costante locked e contiene tutti i nomi colonna CSV"
|
||||
- "Almeno 5 prompt base esistono come file .txt scritti IN italiano"
|
||||
artifacts:
|
||||
- path: "backend/services/calendar_service.py"
|
||||
provides: "CalendarService con generate_calendar() che produce 13 slot PN + Schwartz + nicchie + date"
|
||||
contains: "class CalendarService"
|
||||
- path: "backend/services/format_selector.py"
|
||||
provides: "FormatSelector con select_format(tipo, livello) -> formato narrativo"
|
||||
contains: "class FormatSelector"
|
||||
- path: "backend/services/prompt_service.py"
|
||||
provides: "PromptService con load, list, compile con variabili"
|
||||
contains: "class PromptService"
|
||||
- path: "backend/constants.py"
|
||||
provides: "CANVA_FIELDS, PERSUASION_DISTRIBUTION, SCHWARTZ_DISTRIBUTION costanti locked"
|
||||
contains: "CANVA_FIELDS"
|
||||
- path: "backend/schemas/calendar.py"
|
||||
provides: "CalendarSlot, CalendarRequest, CalendarResponse Pydantic models"
|
||||
contains: "class CalendarSlot"
|
||||
- path: "backend/schemas/generate.py"
|
||||
provides: "SlideContent, GeneratedPost Pydantic models per output LLM e CSV"
|
||||
contains: "class GeneratedPost"
|
||||
- path: "backend/data/format_mapping.json"
|
||||
provides: "Tabella mapping tipo_contenuto x livello_schwartz -> formato narrativo"
|
||||
- path: "backend/data/prompts/system_prompt.txt"
|
||||
provides: "System prompt scritto IN italiano per generazione caroselli"
|
||||
key_links:
|
||||
- from: "backend/services/calendar_service.py"
|
||||
to: "backend/constants.py"
|
||||
via: "Importa PERSUASION_DISTRIBUTION e SCHWARTZ_DISTRIBUTION"
|
||||
pattern: "from.*constants.*import"
|
||||
- from: "backend/services/format_selector.py"
|
||||
to: "backend/data/format_mapping.json"
|
||||
via: "Carica mapping da file JSON"
|
||||
pattern: "format_mapping"
|
||||
- from: "backend/services/prompt_service.py"
|
||||
to: "backend/config.py"
|
||||
via: "Usa PROMPTS_PATH per localizzare file .txt"
|
||||
pattern: "PROMPTS_PATH"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Creare i servizi core del dominio: CalendarService (distribuzione 13 post PN + Schwartz + nicchie + date), FormatSelector (mapping tipo x livello -> formato), PromptService (carica/compila prompt .txt), e definire le costanti fondamentali (CANVA_FIELDS, distribuzioni).
|
||||
|
||||
Purpose: Costruire la logica di dominio pura (zero dipendenza LLM) che orchestra il calendario editoriale e prepara i prompt per la generazione. Queste sono le fondamenta su cui LLMService e CSVBuilder si appoggeranno.
|
||||
|
||||
Output: Servizi Python testabili indipendentemente, 5 prompt .txt in italiano, schema Pydantic per slot calendario e post generato, costante CANVA_FIELDS locked.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@C:\Users\miche\.claude/get-shit-done/workflows/execute-plan.md
|
||||
@C:\Users\miche\.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/research/ARCHITECTURE.md
|
||||
@.planning/research/PITFALLS.md
|
||||
@.planning/phases/01-core-generation-pipeline/01-CONTEXT.md
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Costanti di dominio, Pydantic schemas, FormatSelector</name>
|
||||
<files>
|
||||
backend/constants.py
|
||||
backend/schemas/__init__.py
|
||||
backend/schemas/calendar.py
|
||||
backend/schemas/generate.py
|
||||
backend/services/__init__.py
|
||||
backend/services/format_selector.py
|
||||
backend/data/format_mapping.json
|
||||
</files>
|
||||
<action>
|
||||
1. Creare backend/constants.py con le costanti LOCKED del progetto:
|
||||
|
||||
PERSUASION_DISTRIBUTION — distribuzione per ciclo di 13 post:
|
||||
- "valore": 4
|
||||
- "storytelling": 2
|
||||
- "news": 2
|
||||
- "riprova_sociale": 3
|
||||
- "coinvolgimento": 1
|
||||
- "promozione": 1
|
||||
|
||||
SCHWARTZ_DISTRIBUTION — distribuzione livelli per 13 post:
|
||||
- L5: 3 post (storytelling + news)
|
||||
- L4: 3 post (valore + storytelling)
|
||||
- L3: 4 post (valore + riprova)
|
||||
- L2: 2 post (riprova + coinvolgimento)
|
||||
- L1: 1 post (promozione)
|
||||
Nota: L5+L4=6, L3=4, L2=2, L1=1 come da requirement CAL-02.
|
||||
|
||||
CANVA_FIELDS — lista ORDINATA dei nomi colonna CSV per Canva Bulk Create:
|
||||
Metadati: campagna, fase_campagna, tipo_contenuto, formato_narrativo, funzione, livello_schwartz, target_nicchia, data_pub_suggerita
|
||||
Slide (8 slide x 3 campi = 24):
|
||||
cover_title, cover_subtitle, cover_image_keyword
|
||||
s2_headline, s2_body, s2_image_keyword
|
||||
s3_headline, s3_body, s3_image_keyword
|
||||
s4_headline, s4_body, s4_image_keyword
|
||||
s5_headline, s5_body, s5_image_keyword
|
||||
s6_headline, s6_body, s6_image_keyword
|
||||
s7_headline, s7_body, s7_image_keyword
|
||||
cta_text, cta_subtext, cta_image_keyword
|
||||
Extra: caption_instagram
|
||||
Totale: 8 metadati + 24 slide + 1 caption = 33 colonne
|
||||
|
||||
FORMATI_NARRATIVI — lista dei 7 formati: PAS, AIDA, BAB, Listicle, Storytelling, Dato_Implicazione, Obiezione_Risposta
|
||||
|
||||
FUNZIONI_CONTENUTO — le 4 funzioni: Intrattenere, Educare, Persuadere, Convertire
|
||||
|
||||
FASI_CAMPAGNA — le 4 fasi: Attira, Cattura, Coinvolgi, Converti
|
||||
|
||||
NICCHIE_DEFAULT — lista default: ["generico", "dentisti", "avvocati", "ecommerce", "local_business", "agenzie"]
|
||||
|
||||
POST_PER_CICLO = 13
|
||||
|
||||
Nota: usare _image_keyword (non _image_url) per le colonne immagine — l'URL verra' solo con Unsplash in Phase 4. Per ora il CSV contiene keyword testuali.
|
||||
|
||||
2. Creare backend/schemas/__init__.py (vuoto).
|
||||
|
||||
3. Creare backend/schemas/calendar.py con Pydantic models:
|
||||
- CalendarSlot: indice (int), tipo_contenuto (str), livello_schwartz (str), formato_narrativo (str), funzione (str), fase_campagna (str), target_nicchia (str), data_pub_suggerita (str, formato YYYY-MM-DD), topic (Optional[str], default None — verra' generato dall'LLM o overridden dall'utente)
|
||||
- CalendarRequest: obiettivo_campagna (str), settimane (int, default 2), nicchie (Optional[list[str]]), frequenza_post (int, default 3 — post a settimana), data_inizio (Optional[str])
|
||||
- CalendarResponse: campagna (str), slots (list[CalendarSlot]), totale_post (int)
|
||||
|
||||
4. Creare backend/schemas/generate.py con Pydantic models:
|
||||
- SlideContent: headline (str), body (str), image_keyword (str)
|
||||
- GeneratedPost: cover_title (str), cover_subtitle (str), cover_image_keyword (str), slides (list[SlideContent] — 6 slide centrali s2-s7), cta_text (str), cta_subtext (str), cta_image_keyword (str), caption_instagram (str)
|
||||
- GenerateRequest: slot (CalendarSlot), obiettivo_campagna (str), brand_name (Optional[str]), tono (Optional[str])
|
||||
- PostResult: slot_index (int), status (Literal["success", "failed", "pending"]), post (Optional[GeneratedPost]), error (Optional[str])
|
||||
- GenerateResponse: campagna (str), results (list[PostResult]), total (int), success_count (int), failed_count (int)
|
||||
|
||||
5. Creare backend/data/format_mapping.json:
|
||||
Matrice tipo_contenuto x livello_schwartz -> formato_narrativo.
|
||||
Struttura: { "valore": { "L5": "Listicle", "L4": "PAS", "L3": "PAS", "L2": "Obiezione_Risposta", "L1": "AIDA" }, ... }
|
||||
Coprire tutte le 6 combinazioni tipo x 5 livelli con i 7 formati disponibili, scegliendo il formato piu' efficace per ogni combinazione.
|
||||
|
||||
6. Creare backend/services/__init__.py (vuoto).
|
||||
|
||||
7. Creare backend/services/format_selector.py:
|
||||
- class FormatSelector con __init__ che carica format_mapping.json
|
||||
- Metodo select_format(tipo_contenuto: str, livello_schwartz: str) -> str che ritorna il formato narrativo
|
||||
- Fallback a "PAS" se combinazione non trovata
|
||||
- Metodo get_mapping() -> dict per esporre la tabella completa
|
||||
</action>
|
||||
<verify>
|
||||
- backend/constants.py: CANVA_FIELDS ha esattamente 33 elementi, PERSUASION_DISTRIBUTION somma a 13, SCHWARTZ_DISTRIBUTION somma a 13
|
||||
- backend/schemas/calendar.py: CalendarSlot importabile, CalendarRequest ha campo obiettivo_campagna
|
||||
- backend/schemas/generate.py: GeneratedPost ha slides come list[SlideContent], PostResult ha campo status
|
||||
- backend/data/format_mapping.json: contiene tutte le 6 chiavi tipo_contenuto, ciascuna con 5 livelli
|
||||
- backend/services/format_selector.py: FormatSelector ha metodo select_format
|
||||
</verify>
|
||||
<done>
|
||||
Costanti di dominio locked (CANVA_FIELDS, PERSUASION_DISTRIBUTION, SCHWARTZ_DISTRIBUTION). Pydantic schemas per calendario e generazione definiti. FormatSelector carica mapping da JSON e mappa tipo x livello -> formato. Tutto il dominio ha tipi espliciti, nessuna stringa magica.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: CalendarService, PromptService, e prompt .txt in italiano</name>
|
||||
<files>
|
||||
backend/services/calendar_service.py
|
||||
backend/services/prompt_service.py
|
||||
backend/data/prompts/system_prompt.txt
|
||||
backend/data/prompts/pas_valore.txt
|
||||
backend/data/prompts/listicle_valore.txt
|
||||
backend/data/prompts/bab_storytelling.txt
|
||||
backend/data/prompts/aida_promozione.txt
|
||||
backend/data/prompts/dato_news.txt
|
||||
</files>
|
||||
<action>
|
||||
1. Creare backend/services/calendar_service.py:
|
||||
- class CalendarService
|
||||
- Metodo generate_calendar(request: CalendarRequest) -> CalendarResponse:
|
||||
a. Genera 13 slot con distribuzione PERSUASION_DISTRIBUTION (import da constants)
|
||||
b. Assegna livelli Schwartz secondo la logica naturale:
|
||||
- valore: L4 (2), L3 (2) — educare chi e' consapevole del problema/soluzioni
|
||||
- storytelling: L5 (2) — attirare inconsapevoli con storie
|
||||
- news: L5 (1), L4 (1) — intrattenere/educare
|
||||
- riprova_sociale: L3 (2), L2 (1) — persuadere chi conosce soluzioni/prodotto
|
||||
- coinvolgimento: L2 (1) — interagire con chi conosce il prodotto
|
||||
- promozione: L1 (1) — convertire chi e' pronto
|
||||
Verifica che totali siano: L5=3, L4=3, L3=4, L2=2, L1=1
|
||||
c. Assegna funzioni contenuto: Intrattenere (storytelling, news, coinvolgimento), Educare (valore), Persuadere (riprova_sociale), Convertire (promozione)
|
||||
d. Distribuisce nelle 4 fasi campagna: Attira (L5), Cattura (L4+L3), Coinvolgi (L3+L2), Converti (L1+L2)
|
||||
e. Ordina gli slot nella sequenza campagna (Attira -> Cattura -> Coinvolgi -> Converti)
|
||||
f. Calcola date_pub_suggerita dalla data_inizio con la frequenza specificata (default: 3 post/settimana, lun-mer-ven)
|
||||
g. Assegna nicchie con rotazione: 50% generico, 50% verticali in rotazione dalla lista nicchie
|
||||
- Usa FormatSelector internamente per assegnare formato_narrativo a ogni slot
|
||||
- Metodo statico _distribute_niches(slots, nicchie) per la logica rotazione
|
||||
|
||||
2. Creare backend/services/prompt_service.py:
|
||||
- class PromptService(__init__ riceve prompts_dir: Path)
|
||||
- list_prompts() -> list[str]: elenca tutti i .txt nella directory
|
||||
- load_prompt(name: str) -> str: carica contenuto file .txt
|
||||
- compile_prompt(name: str, variables: dict[str, str]) -> str: carica e sostituisce {{variabile}} con valori dal dict
|
||||
- Usa doppia graffa {{variabile}} come delimitatore (Pitfall 7)
|
||||
- Solleva ValueError se una variabile nel template non ha corrispondenza nel dict
|
||||
- save_prompt(name: str, content: str) -> None: salva contenuto (per Phase 2 editor)
|
||||
- get_required_variables(name: str) -> list[str]: parse il template e ritorna lista variabili {{...}} trovate
|
||||
|
||||
3. Creare i 5 prompt base + system prompt, TUTTI scritti IN italiano (Pitfall 8):
|
||||
|
||||
backend/data/prompts/system_prompt.txt:
|
||||
- Ruolo: esperto di content marketing B2B per PMI italiane
|
||||
- Tono: diretto, provocatorio ma costruttivo, usa il "tu"
|
||||
- Target: imprenditori e manager italiani
|
||||
- Regole: "cosa fare" mai "come farlo", benefici concreti, evitare jargon
|
||||
- Lingua: italiano naturale, NON tradotto dall'inglese
|
||||
- Output: JSON strutturato con i campi specificati nello schema
|
||||
|
||||
backend/data/prompts/pas_valore.txt (formato PAS per post valore):
|
||||
- Sezioni: SYSTEM (ref system_prompt), USER, OUTPUT_SCHEMA
|
||||
- Variabili: {{obiettivo_campagna}}, {{target_nicchia}}, {{livello_schwartz}}, {{topic}}, {{brand_name}}
|
||||
- Istruzioni PAS: Problema -> Agitazione -> Soluzione
|
||||
- Output schema JSON esplicito con campi GeneratedPost
|
||||
|
||||
backend/data/prompts/listicle_valore.txt (formato Listicle per post valore):
|
||||
- Variabili: stesse di PAS
|
||||
- Istruzioni Listicle: lista numerata di consigli pratici
|
||||
- Enfasi su valore educativo
|
||||
|
||||
backend/data/prompts/bab_storytelling.txt (formato BAB per storytelling):
|
||||
- Variabili: stesse + enfasi emotiva
|
||||
- Istruzioni BAB: Before -> After -> Bridge
|
||||
- Tono narrativo, storia di trasformazione
|
||||
|
||||
backend/data/prompts/aida_promozione.txt (formato AIDA per promo):
|
||||
- Variabili: stesse + {{call_to_action}}
|
||||
- Istruzioni AIDA: Attenzione -> Interesse -> Desiderio -> Azione
|
||||
- Focus su conversione
|
||||
|
||||
backend/data/prompts/dato_news.txt (formato Dato+Implicazione per news):
|
||||
- Variabili: stesse
|
||||
- Istruzioni: dato/statistica -> cosa significa -> come agire
|
||||
- Focus su urgenza informata
|
||||
|
||||
TUTTI i prompt devono:
|
||||
- Essere scritti interamente in italiano (istruzioni E esempi)
|
||||
- Usare {{variabile}} per tutti i parametri dinamici
|
||||
- Specificare l'output JSON schema esplicito con i campi di GeneratedPost
|
||||
- Includere istruzioni sul tono (tu, diretto, concreto)
|
||||
- Specificare il numero di slide e la struttura (cover, 6 slide centrali, CTA)
|
||||
</action>
|
||||
<verify>
|
||||
- CalendarService.generate_calendar() con CalendarRequest(obiettivo_campagna="test", settimane=2) produce CalendarResponse con esattamente 13 slot
|
||||
- Distribuzione PN: contare tipi -> 4 valore, 2 storytelling, 2 news, 3 riprova, 1 coinvolgimento, 1 promo
|
||||
- Distribuzione Schwartz: contare livelli -> L5=3, L4=3, L3=4, L2=2, L1=1
|
||||
- PromptService.list_prompts() ritorna almeno 6 file (system + 5 base)
|
||||
- PromptService.compile_prompt("pas_valore", {"obiettivo_campagna": "test", ...}) sostituisce tutte le variabili senza errori
|
||||
- Tutti i prompt .txt contengono SOLO testo italiano, nessuna istruzione in inglese
|
||||
- Nessun prompt contiene numeri hardcoded per slide count — usano {{num_slides}} o la struttura e' definita nell'output schema
|
||||
</verify>
|
||||
<done>
|
||||
CalendarService genera 13 slot con distribuzione PN e Schwartz corretta, assegna fasi campagna, calcola date, ruota nicchie. PromptService carica e compila prompt con variabili {{...}}. 5 prompt base + system prompt scritti IN italiano, con output JSON schema esplicito. Nessun valore hardcoded nei template.
|
||||
</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
1. CANVA_FIELDS ha 33 elementi e corrisponde allo schema in PROJECT.md (+ caption_instagram)
|
||||
2. CalendarService produce 13 slot con distribuzione verificabile contando tipi e livelli
|
||||
3. Ogni prompt .txt usa solo {{variabile}} e non contiene numeri/strutture hardcoded
|
||||
4. FormatSelector mappa tutte le 30 combinazioni (6 tipi x 5 livelli)
|
||||
5. Pydantic schemas validano correttamente un esempio di GeneratedPost
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- CalendarService genera calendario con distribuzione PN e Schwartz corretta
|
||||
- FormatSelector mappa ogni combinazione tipo x livello a un formato
|
||||
- PromptService carica, lista, compila prompt con sostituzione variabili
|
||||
- 5 prompt base + system prompt esistono come file .txt in italiano
|
||||
- CANVA_FIELDS locked come costante con tutti i nomi colonna
|
||||
- Pydantic schemas per calendario, generazione, e risultati definiti
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/01-core-generation-pipeline/01-02-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user