./run mirofish-swarm-intelligence-simulazione-agenti
MiroFish: ho dato 80 capitoli a mille agenti per vedere come finivano il romanzo
Esplorazione di MiroFish, motore open-source di swarm intelligence: mille agenti che twittano il futuro. Come funziona il loop OASIS + GraphRAG + Zep, e cosa serve per farlo girare in casa.
- status
- draft
- project
- mirofish
- updated
- 2026-07-07
- author
- terzi · esplorazione
- tags
Il romanzo Il sogno della camera rossa si interrompe: gli ultimi capitoli sono andati perduti, e per due secoli si è discusso come dovesse finire. La demo di punta di MiroFish — un progetto open-source di Shanda Group (666ghj/MiroFish), non mio — prende i primi 80 capitoli, centinaia di migliaia di parole, e chiede a uno sciame di agenti di scrivere il finale. Non generando testo alla maniera di un LLM che completa una frase: trasformando i personaggi in utenti di un social network e lasciandoli agire finché una storia emerge. Sono andato nel codice per capire come una manciata di post e like diventi un epilogo letterario. Questo è il diario di quell’esplorazione — con il codice altrui alla mano.
L’idea: la previsione che non predice
MiroFish si vende come “motore di swarm intelligence che predice qualsiasi cosa”: gli dai un seme (una notizia, una bozza di policy, un romanzo) e ti costruisce un mondo digitale parallelo dove migliaia di agenti con personalità e memoria interagiscono, mentre tu inietti variabili “dall’alto” come un dio. La scoperta controintuitiva, leggendo il codice, è che non c’è nessun forecaster. Non c’è un modello che stima P(finale). C’è una simulazione sociale: i personaggi diventano account, postano, si seguono, si mettono like, e il “finale” è l’artefatto emergente di quelle interazioni. La previsione è la storia che il mondo si racconta da solo.
Il cuore tecnico è OASIS (Open Agent Social Interaction Simulations) del team CAMEL-AI. Le versioni pinnate nel pyproject.toml sono concrete: camel-oasis==0.2.5, camel-ai==0.2.78, backend Flask 3, frontend Vue 3, Python 3.11–3.12. La memoria e il GraphRAG girano su Zep Cloud (zep-cloud==3.13.0), e l’inferenza di default su Qwen-plus di Alibaba via API OpenAI-compatibile.
La pipeline: da testo a sciame in cinque passi
- Ontologia — un LLM legge il testo-seme e progetta lo schema: quali tipi di entità (Personaggio, Fazione, Luogo…) e quali tipi di relazione esistono in questo mondo. Niente ontologia hard-coded: la disegna il modello per ogni corpus.
- Costruzione del grafo — il testo viene spezzato in chunk e dato in pasto a Zep, che estrae entità e relazioni in un knowledge graph temporale (gli edge hanno
valid_at/invalid_at/expired_at: i fatti nascono e muoiono nel tempo). - Lettura entità — si filtrano i nodi per i tipi definiti nell’ontologia.
- Generazione delle persona — ogni entità-personaggio diventa un profilo agente:
bio,persona,age,gender,mbti,profession,interested_topics. Zep viene interrogato una seconda volta per arricchire il nodo prima di scrivere il carattere. - Simulazione — OASIS costruisce l’
agent_graphe gira.
Il loop principale, ripulito ma fedele, è sorprendentemente scarno:
self.env = oasis.make(
agent_graph=self.agent_graph,
platform=oasis.DefaultPlatformType.TWITTER,
database_path=db_path,
semaphore=30, # max richieste LLM concorrenti, per non affogare l'API
)
await self.env.reset()
for round_num in range(total_rounds):
active_agents = self._get_active_agents_for_round(env, simulated_hour, round_num)
actions = {agent: LLMAction() for _, agent in active_agents}
await self.env.step(actions)
LLMAction() è la delega totale: per ogni agente attivo, il modello decide da solo cosa fare tra CREATE_POST, LIKE_POST, REPOST, QUOTE_POST, FOLLOW, DO_NOTHING. Non tutti agiscono ad ogni round: chi è attivo dipende da un orologio simulato. La config di default copre 72 ore in round da 30 minuti = 144 round, con moltiplicatori di attività per fascia oraria (×1.5 nelle ore di punta, ×0.3 di notte) e una probabilità per-agente (activity_level). Il seme è un initial_post: l’evento che accende la miccia. Su Reddit c’è anche il commento; è previsto pure il parallelo dual-platform Twitter+Reddit via subprocess.
La spina: il loop che chiude su sé stesso
Qui sta il pezzo che rende MiroFish più di un generatore di chiacchiere. Ogni azione che un agente compie viene ritradotta in linguaggio naturale e reiniettata nel grafo di memoria mentre la simulazione gira. Il metodo che fa la traduzione è banale e per questo geniale:
def to_episode_text(self) -> str:
describe_func = action_descriptions.get(self.action_type, self._describe_generic)
description = describe_func()
# "nome_agente: descrizione dell'azione", senza prefissi da simulazione
return f"{self.agent_name}: {description}"
Un like non è un contatore che sale: diventa la frase “Tizio: ha messo like al post di Caio «…»”. Queste frasi vengono bufferizzate per piattaforma (BATCH_SIZE = 5) e spinte in un grafo via graph.add() da un thread di background con una coda — così la simulazione non si blocca aspettando la rete. Il risultato è un anello chiuso: gli agenti agiscono → le azioni diventano fatti nel grafo → il grafo è la memoria che gli agenti leggono al round dopo → nuove azioni. Il mondo scrive la propria storia e la rilegge in tempo reale. Il “finale” del Sogno della camera rossa non è predetto: sedimenta in questo grafo, round dopo round, e alla fine un ReportAgent in stile ReACT lo interroga con degli strumenti di ricerca semantica e ne cava un report leggibile. Puoi anche fermarti e intervistare un singolo agente (azione INTERVIEW, iniettata via IPC): il tuo “punto di vista da dio”.
Il gotcha onesto: non è privato, e farlo diventare tale fa male
La mia seconda scoperta è meno poetica. MiroFish, così com’è, telefona a casa: la memoria vive su Zep Cloud (SaaS), l’inferenza su Alibaba Bailian. Per un corpus riservato — l’uso “serio” che il progetto stesso rivendica, testare policy e PR a rischio zero — è un no. Quindi ho mappato cosa servirebbe a portarlo interamente sul proprio ferro, e i bordi taglienti sono istruttivi:
- Zep → Graphiti + FalkorDB. Zep è il SaaS costruito sopra Graphiti (OSS, Apache-2.0); FalkorDB è il grafo con protocollo Redis. Ho contato 13 metodi SDK Zep usati in 9 file —
graph.search(),node.get_by_graph_id(),graph.add()… — da replicare dietro un adapter con interfaccia identica, così il codice chiamante non cambia. - L’inferno async/sync. Graphiti è interamente
async; Flask è sincrono e l’updater della memoria usa già thread+code. Un ingenuoasyncio.run()per chiamata distrugge le connessioni persistenti a FalkorDB. Serve un unico event-loop di sfondo erun_coroutine_threadsafe().nest_asynciomaschera il problema e basta. - Il killer silenzioso. Graphiti estrae entità chiamando un LLM che deve restituire JSON valido. Se vLLM parte senza guided decoding (
--guided-decoding-backend), l’estrazione “riesce” senza errori ma il grafo resta vuoto: zero nodi, simulazioni spazzatura, e te ne accorgi solo alla fine. Smoke test obbligatorio: dopo l’ingest, conta i nodi. - La matematica della VRAM. Qwen2.5-72B in FP16 vuole ~144 GB; una H100 ne ha 80. Non ci sta. O AWQ (~40 GB) o tensor-parallel su due schede.
Come va (stato reale)
Sono onesto: non ho fatto girare i 144 round completi. Il README lo dice esplicitamente — “alto consumo, prova prima con meno di 40 round” — e senza una chiave LLM e un account Zep la simulazione non parte. Ho letto il codice a fondo, tracciato il loop, e progettato la migrazione on-prem; l’adapter Graphiti+FalkorDB è disegnato, non costruito. Il progetto a monte è vivo e reale (è su Trendshift, ha una demo pubblica); la mia esplorazione è a livello di architettura e di “cosa succederebbe se”. Il salto tra “documentato” e “girato in casa” resta tutto lì, in quei quattro bordi taglienti sopra.
Cosa ho imparato
Che la parola “previsione” qui è un cavallo di Troia. MiroFish non stima un futuro: ne coltiva uno, e la qualità del risultato dipende meno dal modello e più dal loop — dalla fedeltà con cui ogni azione micro torna a essere memoria condivisa. Il pezzo di codice più importante non è l’LLM: sono quelle cinque righe che trasformano un like in una frase e la rimettono nel grafo. E ho imparato, di nuovo, che “open-source” e “self-hosted” non sono sinonimi: un progetto può essere tutto su GitHub e restare, di fatto, un client di due cloud. La distanza tra i due si misura in adapter, event-loop e VRAM — non in licenze.
Progetto altrui, pubblico: github.com/666ghj/MiroFish · motore di simulazione: OASIS / CAMEL-AI. Io ci ho solo guardato dentro.