Come ho creato un Agent AI per gestire i miei contenuti online

Negli ultimi anni i modelli di linguaggio (LLM) hanno rivoluzionato il modo in cui le applicazioni software prendono decisioni. Invece di dipendere da regole rigide, un agente può affidarsi a un LLM per valutare il contesto, generare una serie di azioni possibili e scegliere quella più coerente con gli obiettivi prefissati. Questa capacità di ragionamento flessibile è la base di una intelligenza artificiale più autonoma.
Tuttavia, un LLM da solo agisce come una memoria a breve termine: dimentica rapidamente le informazioni ottenute in precedenti interazioni. Senza un meccanismo di persistenza, l’agente non può ricordare cosa ha già analizzato, quali risultati ha ottenuto o quali errori ha incontrato. La mancanza di memoria porta a ripetizioni inutili e a decisioni incoerenti, penalizzando l’efficienza complessiva del sistema.
Per colmare questa lacuna, è necessario integrare tre componenti fondamentali: la logica decisionale basata su LLM, un sistema di memoria persistente (solitamente un database) e una serie di plugin o strumenti specializzati. L’interazione sinergica tra questi elementi consente di costruire un sistema AI multi‑agente in grado di operare in modo completamente autonomo, adattandosi al contesto e gestendo compiti complessi senza supervisione costante.
Architettura implementata: il caso reale del Friday Night Assistant
Il progetto Friday Night Assistant è un esempio pratico di un’architettura a tre livelli in cui ogni agente possiede responsabilità ben definite. Al vertice troviamo il MainAgent, che funge da coordinatore: analizza le metriche di Matomo (bounce rate, visite, ecc.) e identifica i contenuti che richiedono interventi. Una volta individuato un problema, il MainAgent delega il lavoro al sotto‑agente più adatto, ovvero il PostAgent per i blog post o il TutorialAgent per i tutorial.
Il PostAgent è specializzato nell’analisi dei blog post. È in grado di valutare la struttura di un articolo, verificare la presenza di codice, misurare la leggibilità e suggerire miglioramenti. Grazie alla sua memoria isolata, il PostAgent evita di ripetere operazioni già concluse e può concentrarsi su nuove ottimizzazioni. Allo stesso modo, il TutorialAgent si occupa della verifica dei prerequisiti, della coerenza step‑by‑step e della presenza di esempi pratici, garantendo che ogni tutorial sia completo e pronto per l’utilizzo.
Questa gerarchia a più livelli permette di mantenere responsabilità chiare e di scalare il sistema aggiungendo nuovi agenti senza introdurre dipendenze incrociate. Ogni agente comunica con il proprio set di plugin specifici, mantenendo il core decisionale indipendente e facilmente aggiornabile.
Perché la memoria nel database è fondamentale
Una memoria persistente è il pilastro su cui si regge l’intero ecosistema multi‑agente. Senza di essa, gli agenti tenderebbero a replicare le stesse azioni, richiedere informazioni già note e, soprattutto, perdono la continuità di ragionamento. Questo comporta un aumento dei tempi di risposta e una diminuzione della precisione delle decisioni.
Implementando la memoria in un database relazionale, ogni azione viene registrata con un chiave‑valore, l’identificatore dell’agente, il contenuto della decisione e un timestamp. Queste informazioni consentono al sistema di ricostruire lo storico delle operazioni, fornendo al LLM un contesto ricco al momento della generazione del nuovo prompt. In pratica, la memoria funge da taccuino personale per ciascun agente, garantendo isolamento e prevenendo conflitti tra le diverse tipologie di agenti.
Il vantaggio più evidente è la riduzione delle ridondanze: se il PostAgent ha già recuperato i dettagli di un post, non dovrà farlo nuovamente, ma potrà passare direttamente all’analisi della qualità. Inoltre, i risultati delle azioni precedenti, inclusi eventuali errori, vengono salvati e possono informare il LLM su come modificare la strategia, creando un ciclo di feedback continuo che migliora l’efficacia del sistema nel tempo.
Il modello AgentMemory: struttura e funzionamento
Il modello AgentMemory rappresenta un semplice key/value store progettato per memorizzare le informazioni chiave di ogni agente. Ogni record contiene un identificatore unico, la chiave descrittiva, il tipo di agente (agent_type), il valore memorizzato in formato JSON e i timestamp di creazione e aggiornamento. L’indice su agent_type e created_at rende le query estremamente rapide, anche quando il volume di dati cresce.
class AgentMemory(models.Model):
"""Simple key/value store for agent memory."""
id = models.AutoField(primary_key=True)
key = models.CharField(max_length=255, blank=True, null=True, db_index=True)
agent_type = models.CharField(
max_length=50,
default='main',
db_index=True,
help_text="Type of agent: 'main', 'post', 'tutorial'"
)
value = models.JSONField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
app_label = 'mysql_models'
indexes = [
models.Index(fields=['agent_type', 'created_at']),
]Il campo agent_type è cruciale perché separa la memoria di ogni agente, evitando sovrapposizioni e garantendo isolamento completo. Quando il PostAgent richiede le proprie memorie, esegue semplicemente un filtro su agent_type='post', ottenendo così solo le informazioni rilevanti per la sua attività. Lo stesso vale per TutorialAgent e MainAgent, che interrogano la tabella con il proprio tipo.
Esempio concreto di memoria separata per ciascun agente
Grazie al design a memoria isolata, ogni agente può recuperare e gestire autonomamente i propri dati:
# PostAgent: recupera solo le proprie memorie
post_memories = AgentMemory.objects.filter(agent_type='post').order_by('created_at')
# TutorialAgent: recupera solo le proprie memorie
tutorial_memories = AgentMemory.objects.filter(agent_type='tutorial').order_by('created_at')
# MainAgent: recupera solo le proprie memorie
main_memories = AgentMemory.objects.filter(agent_type='main').order_by('created_at')Questo approccio semplifica il debugging: se un errore è legato a un tutorial, basta interrogare le memorie del TutorialAgent per ottenere lo storico completo delle azioni eseguite. Inoltre, consente al LLM di ricevere un prompt arricchito da un contesto preciso, riducendo la probabilità di decisioni contraddittorie.
Formato dei dati in memoria: struttura di un record
Ogni entry in AgentMemory registra una singola decisione con tutti i dettagli necessari per il successivo ciclo di ragionamento. Un esempio tipico di record JSON è:
{
"id": 123,
"agent_type": "post",
"value": {
"action": "analyze_post_quality",
"args": {"slug": "python-tips"},
"reason": "Devo verificare la qualità del contenuto",
"result": {
"word_count": 150,
"has_code": false,
"readability_score": 45,
"issues": ["too_short", "no_examples"]
}
},
"created_at": "2025-12-10T14:30:00Z"
}Quando il PostAgent riprende un lavoro su un articolo modificato giorni prima, legge questo storico e decide se è necessario ripetere l’analisi o passare direttamente a un’altra fase, evitando così ridondanze inutili e migliorando la coerenza decisionale dell’intero ecosistema.
Loop decisione‑esecuzione con LLM: il ciclo completo
Il cuore del funzionamento è un loop iterativo in cui l’agente recupera la memoria, formatta il contesto, costruisce il prompt per il LLM, interpreta la risposta, esegue l’azione e aggiorna la memoria. Il ciclo si ripete finché il LLM segnala di fermarsi (stop:true). Questo meccanismo garantisce che ogni decisione sia basata su informazioni sempre aggiornate.
while True:
# 1. Recupera la memoria precedente
previous_memories = self._get_previous_memories()
memory_section = self._format_memory_section(previous_memories)
# 2. Costruisce il prompt
prompt = self._build_prompt(methods, state, memory_section, context)
# 3. Ottiene la decisione dal LLM
decision = self._get_llm_decision(prompt)
# 4. Verifica il flag di stop
if decision.get("stop"):
break
# 5. Salva la decisione (senza risultato)
memory_record = self._save_decision_to_memory(decision)
# 6. Esegue l'azione e aggiorna la memoria
state = self._execute_action(decision["action"], decision.get("args", {}), memory_record)
time.sleep(1) # pacingGrazie alla memoria persistente, il LLM non riceve più un prompt vuoto ad ogni iterazione, ma un contesto ricco di azioni già compiute e dei loro risultati. Questo riduce drasticamente il rischio di loop infiniti e consente al sistema di progredire in modo lineare verso l’obiettivo finale.
Plugin come strumenti specializzati: PostAgent e TutorialAgent
Ogni agente dispone di un set limitato di plugin che offrono funzionalità concrete senza intaccare la logica decisionale di base. I plugin sono implementati come classi con metodi statici che descrivono i metodi disponibili e le loro interfacce.
PostAgent plugin
class PostAgentPlugins:
"""Metodi specializzati per la gestione dei blog post."""
@staticmethod
def get_available_methods() -> List[Dict[str, Any]]:
return [
{"name": "get_post_details", "description": "Recupera i dettagli di un post", "parameters": {"slug": {"type": "str", "required": True}}},
{"name": "analyze_post_quality", "description": "Analizza la qualità del contenuto", "parameters": {"slug": {"type": "str", "required": True}}},
{"name": "update_post_content", "description": "Aggiorna il contenuto del post", "parameters": {"slug": {"type": "str", "required": True}, "new_content": {"type": "dict", "required": True}}},
{"name": "get_post_categories", "description": "Recupera le categorie associate", "parameters": {"slug": {"type": "str", "required": True}}}
]Questi metodi consentono al PostAgent di ottenere dettagli, analizzare la leggibilità, aggiornare il contenuto e verificare le categorie, operazioni fondamentali per ottimizzare un blog post con alto bounce rate.
TutorialAgent plugin
class TutorialAgentPlugins:
"""Metodi per la gestione dei tutorial."""
@staticmethod
def get_available_methods() -> List[Dict[str, Any]]:
return [
{"name": "get_tutorial_details", "description": "Recupera i dettagli di un tutorial", "parameters": {"slug": {"type": "str", "required": True}}},
{"name": "analyze_tutorial_structure", "description": "Analizza la struttura del tutorial", "parameters": {"slug": {"type": "str", "required": True}}},
{"name": "check_tutorial_prerequisites", "description": "Verifica la presenza di prerequisiti", "parameters": {"slug": {"type": "str", "required": True}}}
]Grazie a questi plugin, il TutorialAgent può valutare la completezza, controllare la presenza di prerequisiti e analizzare la struttura step‑by‑step, garantendo tutorial di alta qualità.
Scalabilità: aggiungere nuovi agenti in pochi minuti
L’architettura modulare consente di ampliare facilmente il sistema con nuovi agenti. Per esempio, per introdurre un VideoAgent che analizzi contenuti video, è sufficiente creare il plugin con i metodi desiderati, implementare il comando di esecuzione e aggiungere una funzione di delega. Nessuna modifica alla logica di base è necessaria, dimostrando la flessibilità della struttura.
# Plugin VideoAgent
class VideoAgentPlugins:
@staticmethod
def get_available_methods():
return [
{"name": "analyze_video_quality", "description": "Analizza la qualità del video", "parameters": {"video_id": {"type": "str", "required": True}}},
{"name": "check_video_subtitles", "description": "Verifica la presenza di sottotitoli", "parameters": {"video_id": {"type": "str", "required": True}}},
{"name": "get_video_duration", "description": "Ottiene la durata", "parameters": {"video_id": {"type": "str", "required": True}}}
]Una volta definito il plugin, basta registrare il nuovo agente nella configurazione e il sistema sarà pronto a gestire anche i video, senza alterare il funzionamento di MainAgent, PostAgent o TutorialAgent.
Vantaggi dell’architettura multi‑agente
- Separazione delle responsabilità – Ogni agente si concentra su un dominio specifico, riducendo la complessità del codice.
- Memoria isolata – Grazie a
agent_type, le memorie non si contaminano a vicenda, garantendo dati coerenti per ogni contesto. - Autonomia decisionale – LLM decide quali metodi invocare, in che ordine e quando terminare, rendendo il sistema più adattivo.
- Debugging semplificato – È possibile analizzare le azioni di un singolo agente interrogando direttamente la relativa tabella
AgentMemory. - Test indipendenti – Ogni agente può essere testato singolarmente con comandi dedicati, accelerando il ciclo di sviluppo.
- Evoluzione modulare – L’aggiunta o la modifica di un agente non influisce sulle altre componenti, favorendo la crescita continua del progetto.
Gestione degli errori e meccanismo di recovery
Il framework prevede una gestione raffinata delle eccezioni. Quando un’azione fallisce, l’errore viene catturato, salvato nella memoria e restituito al LLM, che può decidere di riprova, saltare l’azione o interrompere l’intero flusso. Ecco un esempio di implementazione:
def _execute_action(self, action, args, memory_record):
try:
state = self._execute_method(action, args)
self._update_memory_with_result(memory_record, state)
return state
except Exception as e:
error_state = {"error": str(e)}
self._update_memory_with_result(memory_record, error_state)
return error_stateIn questo modo, l’agente non si blocca in caso di fallimento, ma adatta dinamicamente la strategia, migliorando la resilienza del sistema.
Metriche e risultati reali: impatto della transizione multi‑agente
| Indicatore | Prima (singolo agente) | Dopo (multi‑agente) |
|---|---|---|
| Tempo medio di analisi per contenuto | 45 s | 30 s (‑33 %) |
| Accuratezza delle raccomandazioni | 65 % | 85 % (+20 pp) |
| Bug settimanali | 3‑4 | 1‑2 (‑50 %) |
| Tempo medio per nuova funzionalità | 2‑3 giorni | 4‑6 ore (‑75 %) |
| Percentuale di azioni duplicate | ~40 % | < 5 % |
I dati dimostrano come la memoria centralizzata, unitamente alla divisione dei compiti, abbia prodotto un notevole miglioramento nella velocità, nell’affidabilità e nella qualità delle decisioni automatizzate.
Conclusioni
Costruire un sistema AI multi‑agente autonomo richiede l’integrazione armoniosa di tre pilastri: LLM per la logica decisionale, memoria persistente per la continuità e plugin specializzati per le funzionalità operative. L’approccio illustrato con il Friday Night Assistant mostra come, strutturando l’architettura in livelli chiari, sia possibile ottenere un ecosistema altamente scalabile, flessibile e autonomo. La memoria isolata, unita al loop decisione‑esecuzione, elimina le ridondanze e consente al LLM di operare su un contesto ricco e aggiornato, migliorando significativamente le performance rispetto a soluzioni monolitiche. Questo modello rappresenta una base solida per future estensioni, quali l’integrazione di agenti per analisi video, social media o monitoraggio delle performance, aprendo la strada a sistemi AI sempre più intelligenti e indipendenti.