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
16 KiB
16 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-core-generation-pipeline | 02 | execute | 1 |
|
true |
|
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.
<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>
@.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 Task 1: Costanti di dominio, Pydantic schemas, FormatSelector 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 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
- 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
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.
Task 2: CalendarService, PromptService, e prompt .txt in italiano
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
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)
- 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
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.
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
<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>