Da qui parte una serie di post in cui racconterò come sto costruendo Character Engine, un progetto a cui lavoro da un po’ di tempo.
L’idea di partenza
Character Engine è un sistema per creare e far vivere personaggi virtuali su Instagram: profili gestiti da un’intelligenza artificiale, ma con una personalità propria, una storia, dei gusti, un modo di scrivere riconoscibile — e soprattutto un aspetto coerente nel tempo, non un’immagine diversa e scollegata a ogni post.
Il primo personaggio che ho costruito si chiama Sofia. Ha una sua “bible” — un insieme di regole e caratteristiche che ne definiscono voce, valori, ricordi, senso dell’umorismo — e una libreria di scene, luoghi, oggetti e momenti della sua giornata da cui il sistema pesca per raccontare cosa sta facendo. L’obiettivo è che ogni post sembri scritto da lei, non generato da un algoritmo che pesca frasi a caso.
Perché lo sto facendo
Mi ha sempre incuriosito l’idea di un personaggio che esiste solo digitalmente ma che si comporta, nel modo in cui comunica online, come una persona vera: con continuità, con dei temi che le stanno a cuore, con un aspetto che rimane riconoscibile foto dopo foto invece di cambiare completamente ogni volta.
La parte tecnica — automatizzare la scrittura dei contenuti, la generazione delle immagini, la pubblicazione — è nata come conseguenza naturale di questa idea più che come punto di partenza. Volevo che Sofia potesse “vivere” senza che io dovessi occuparmi ogni giorno di scrivere il post, scegliere la foto, caricarla a mano. Un personaggio che si racconta da solo, giorno dopo giorno, restando sempre coerente con chi è.
Portare dentro Sofia
Inizialmente la mia idea era creare un progetto semplice, gestito con n8n e un paio di chiamate ad openai e ad uno dei generatori di immagini ai. Poi nel giro di qualche giorno è evoluto. Non più solo Sofia, ma un motore di personaggi generabili.
Con lo scheletro pronto, ho scritto due comandi Artisan per migrare tutto quello che esisteva già nel vecchio prototipo: bible, profilo visivo, foto di riferimento, e un’intera libreria di elementi di scena (location, oggetti, mood, luce, camera, attività — quasi cento elementi in totale). Tutto idempotente, rilanciabile in sicurezza, perché sapevo che ci sarei tornata sopra più volte prima di avere lo schema giusto.
Una cosa collaterale simpatica: dentro le cartelle del vecchio prototipo esistevano già abbozzi di altri personaggi mai portati a termine — Fernando, Marta, Minnie, Pippo, Sara, Sushi (un pesce rosso, non si può dire che non sia fantasiosa). Materiale per un capitolo futuro, quando deciderò se e come dar loro vita.
Il cuore del problema: perché i post si ripetevano
Qui inizia la parte più interessante, ed è anche la ragione per cui mi sono messa a scrivere questa serie: quando ho iniziato a guardare da vicino la logica di generazione del vecchio sistema, ho trovato un bug che spiegava un problema che avevo notato da tempo, cioè che dopo circa cinque giorni Sofia pubblicava contenuti quasi identici a quelli di qualche giorno prima.
La causa, una volta trovata, era quasi comica: il vecchio codice aveva due selettori di argomento. Uno, SceneSelector, semplicissimo — un array fisso di otto argomenti pescati a caso, senza nessuna memoria di cosa fosse già uscito. L’altro, EventSelector, molto più sofisticato, con pesi e giorni di raffreddamento (cooldown) per evitare ripetizioni — ma che non veniva mai richiamato realmente dal codice che generava i post. Era lì, scritto, funzionante, e completamente inutilizzato. Per aggravare le cose, la funzione che avrebbe dovuto registrare nella cronologia ogni evento generato non veniva mai chiamata da nessuna parte: quindi anche il sistema “buono”, se acceso, avrebbe comunque lavorato su una memoria sempre vuota.
Ho risolto ricostruendo tutto da capo dentro Laravel: una tabella di eventi di vita con peso e giorni di cooldown, e una tabella di cronologia che stavolta viene davvero scritta a ogni generazione, così il selettore può guardare indietro ed escludere sia l’ultimo evento usato sia quelli ancora “in raffreddamento”. Semplice da dire, ma ha richiesto di seguire con calma tutta la catena di chiamate del vecchio sistema per capire dove si rompeva davvero — non bastava intuirlo, andava verificato riga per riga.
E l’immagine? Sempre la stessa
Il secondo problema, altrettanto fastidioso: l’immagine generata sembrava sempre la stessa persona, nella stessa posa, con la stessa espressione. Anche qui la causa era quasi buffa: il vecchio sistema aveva una mappa di espressioni (thinking → una foto, writing → un’altra, smile → un’altra ancora) che però non corrispondeva mai davvero al vocabolario usato altrove nel codice per descrivere le attività — un tipico caso di due parti del sistema scritte in momenti diversi che avevano smesso di parlarsi. Il risultato era che il controllo falliva sempre, e il sistema ripiegava sempre sulla stessa unica foto di default.
La soluzione, questa volta, è stata più semplice: invece di provare a far combaciare etichette che non combaciavano, ho fatto ruotare casualmente tra le foto di riferimento reali disponibili. Meno elegante sulla carta, molto più efficace nella pratica.
Il salto di qualità: cambiare modello di immagine
Risolti i due bug “strutturali”, è arrivato il problema più ostinato: anche quando tutto il resto funzionava, l’immagine generata spesso non aveva niente a che vedere con la scena descritta nel post. Sempre la scrivania, sempre il laptop, sempre la stessa maglia pesante — anche quando il testo parlava di una passeggiata al parco.
Ho passato diverso tempo a pensare che fosse un problema di prompt scritto male, e in parte lo era. Ma la scoperta più importante è arrivata leggendo davvero la documentazione ufficiale del modello di immagine che stavo usando (Flux Kontext, via fal.ai): quel modello, per come è pensato, tende a preservare fortemente la struttura della foto di riferimento — utile per piccole modifiche, pessimo per cambiare completamente scena partendo da un’unica foto sempre uguale (scrivania, maglione, capelli raccolti). E soprattutto: non supporta affatto un parametro di prompt negativo, anche se il mio codice lo calcolava e lo inviava lo stesso, convinto che servisse a qualcosa.
Cambiare modello (Ideogram Character, pensato apposta per mantenere un personaggio coerente attraverso scene diverse) e riscrivere il prompt — passando da un formato lungo e a blocchi a un testo breve, discorsivo, quasi naturale — ha fatto la differenza che cercavo da settimane. Il test finale: stessa scena di prova (parco, passeggiata, bottiglia d’acqua e telefono in mano), e finalmente un’immagine che corrispondeva davvero — location corretta, attività corretta, persino l’abbigliamento finalmente coerente con la stagione invece del solito maglione pesante.
L’ultimo tassello: pubblicare da sola
Con generazione di testo e immagine finalmente affidabili, mancava l’ultimo pezzo per chiudere il cerchio: far sì che Sofia pubblichi davvero, senza che io debba prendere il post e caricarlo a mano su Instagram.
Il primo post pubblicato
Risolto anche questo, il momento che aspettavo: un post generato interamente da Laravel — testo, immagine, e stavolta anche la pubblicazione — è finito davvero su un account Instagram di test, senza che io toccassi nulla a mano oltre a lanciare un comando. Che emozione!
Non è ancora pronto per i clienti reali: Meta richiede una revisione formale dell’app (l’App Review) prima di poter pubblicare per conto di account che non siano i miei di test, e quella richiede tempo — documentazione, un video del flusso completo, e va avviata per tempo, non all’ultimo momento. Ma la parte tecnica, quella che contava davvero dimostrare a me stessa che funzionasse, oggi funziona.
Dove sono adesso
Riassumendo lo stato delle cose a oggi: schema dati e pannello admin pronti, dati di Sofia migrati, generazione di testo e immagine finalmente affidabili e senza le ripetizioni e i bug visivi del vecchio sistema, e pubblicazione automatica su Instagram testata con un post reale. Restano da fare: la logica dei crediti (oggi è solo un segnaposto nel codice), il rinnovo automatico del token Instagram prima che scada ogni 60 giorni, la revisione Meta per poter servire clienti veri, e qualche rifinitura — tra cui insegnare al sistema a tener conto di stagione e ora del giorno quando scrive e genera immagini, cosa su cui sto ancora lavorando.
La domanda che mi porto dietro
Mentre lavoravo a tutto questo, mi sono fatta una domanda che non riesco a togliermi dalla testa: il mercato è già pieno di generatori di avatar AI, in foto e ormai anche in video, usati per promuovere prodotti e servizi di ogni tipo. Quanto tempo passerà prima che chi guarda se ne accorga, si stufi, e l’intera categoria perda credibilità agli occhi di chi la osserva?
Per capirlo mi sono chiesta cosa dia davvero, oggi, il successo a un influencer vero — perché viene seguito, perché in qualche modo viene persino idolatrato o imitato. E la risposta a cui sono arrivata è: la storia che c’è dietro. Un influencer ha una vita, un passato, un mondo che si costruisce nel tempo, post dopo post, anno dopo anno. Non è uno spot pubblicitario — quella forma l’abbiamo già vista appassire, sostituita da qualcosa di più continuo e più vicino nell’apparenza a una persona vera.
Ed è esattamente qui che un progetto come questo rischia di più: un avatar AI senza una vera storia dietro è solo l’ennesimo spot travestito da persona. Per funzionare davvero deve avere quello che ha un influencer vero — continuità, coerenza, un mondo che si ricorda di se stesso. È anche per questo che ho passato così tanto tempo su un bug di “memoria” invece che sulla qualità di una singola immagine: la storia che non si ripete è più importante della singola foto perfetta.
C’è poi una domanda etica che immagino in molti si faranno leggendo questo — e me la faccio anche io: cosa c’è di giusto nel costruire un personaggio che non esiste e farlo vivere come se esistesse? Non ho una risposta definitiva, ma mi faccio volentieri una controdomanda altrettanto scomoda: siamo davvero sicuri che il mondo raccontato da un influencer in carne e ossa sia sempre più reale di quello raccontato da un’intelligenza artificiale? Quanto di ciò che vediamo online, vero o generato che sia, è già costruito, filtrato, recitato per noi? E se lo è in entrambi i casi, forse la domanda giusta non è “è reale?”, ma “cosa sta dando a chi lo segue?”.
Perché nulla vieta che un personaggio come questo diventi un modello positivo — imperfetto, pasticcione anche, ma capace di restituire qualcosa di buono a chi lo segue, invece di vendere solo un’immagine irraggiungibile.
Ma soprattutto la domanda più hot…. Chissà cosa farà domani Sofia?
Cosa vorrei che diventasse
Oggi Character Engine esiste per Sofia. Ma l’ambizione è più grande: vorrei che diventasse uno strumento che chiunque possa usare per costruire il proprio personaggio virtuale — con la propria personalità, il proprio stile visivo, i propri temi — senza dover sapere nulla di codice o di intelligenza artificiale. Un sistema dove basta definire chi è il personaggio, e lui si occupa di scrivere, generare le immagini e pubblicare, restando fedele a se stesso nel tempo. Una specie di versione “Sims” dei social, insomma.
L’intelligenza artificiale resta comunque una faccenda contraddittoria: da un lato semplifica, dall’altro il timore diffuso è che tolga — lavoro, autenticità, significato. Quello che vorrei che questo progetto desse alle persone, invece, è tempo. E più possibilità di fare business. Non voglio costruire qualcosa che tolga: vorrei costruire qualcosa che restituisca lo spazio che oggi si consuma a fare a mano cose che potrebbero essere automatizzate senza perdere l’anima del racconto.
È un progetto ancora in corso, e nei prossimi post racconterò i passi avanti fatti e le difficoltà che sto incontrando lungo la strada — perché costruire qualcosa che si comporti in modo coerente e credibile, invece di essere semplicemente “un altro generatore di contenuti AI”, si sta rivelando più interessante e complicato di quanto pensassi all’inizio.
Non so ancora dove questo mi porterà, la cosa bella è che giorno dopo giorno e notte dopo notte la mia mente evolve partorendo mille scenari possibili. Speriamo non si areni in qualche isola remota di fantasia senza farne più ritorno 🙂. L’unica cosa sicura è che al momento mi sta portando esperienza, che è il mio obiettivo principale.
Nella prossima puntata racconterò come va la fase di test più esteso — generare più post di fila e verificare che davvero non si ripetano nel tempo — e i primi passi sulla logica dei crediti.