./run mirofish-swarm-intelligence-simulazione-agenti
MiroFish: I gave 80 chapters to a thousand agents to see how they'd finish the novel
An exploration of MiroFish, an open-source swarm intelligence engine: a thousand agents tweeting the future. How the OASIS + GraphRAG + Zep loop works, and what…
- status
- exploration
- project
- mirofish
- updated
- 2026-07-08
- author
- third-party · exploration
- tags
The novel Dream of the Red Chamber breaks off: the final chapters were lost, and for two centuries people have argued over how it should end. The flagship demo of MiroFish — an open-source project by Shanda Group (666ghj/MiroFish), not mine — takes the first 80 chapters, hundreds of thousands of words, and asks a swarm of agents to write the ending. Not by generating text the way an LLM completes a sentence: by turning the characters into users of a social network and letting them act until a story emerges. I went into the code to understand how a handful of posts and likes becomes a literary epilogue. This is the diary of that exploration — with someone else’s code in hand.
The idea: the prediction that doesn’t predict
MiroFish sells itself as a “swarm intelligence engine that predicts anything”: you give it a seed (a news item, a policy draft, a novel) and it builds you a parallel digital world where thousands of agents with personality and memory interact, while you inject variables “from above” like a god. The counterintuitive discovery, reading the code, is that there’s no forecaster. There’s no model that estimates P(ending). There’s a social simulation: the characters become accounts, they post, they follow each other, they like each other, and the “ending” is the emergent artifact of those interactions. The prediction is the story the world tells itself.
The technical core is OASIS (Open Agent Social Interaction Simulations) from the CAMEL-AI team. The versions pinned in pyproject.toml are concrete: camel-oasis==0.2.5, camel-ai==0.2.78, Flask 3 backend, Vue 3 frontend, Python 3.11–3.12. Memory and GraphRAG run on Zep Cloud (zep-cloud==3.13.0), and inference by default on Alibaba’s Qwen-plus via an OpenAI-compatible API.
The pipeline: from text to swarm in five steps
- Ontology — an LLM reads the seed text and designs the schema: which entity types (Character, Faction, Place…) and which relationship types exist in this world. No hard-coded ontology: the model draws it for each corpus.
- Graph construction — the text is broken into chunks and fed to Zep, which extracts entities and relationships into a temporal knowledge graph (edges have
valid_at/invalid_at/expired_at: facts are born and die over time). - Entity reading — nodes are filtered by the types defined in the ontology.
- Persona generation — each character-entity becomes an agent profile:
bio,persona,age,gender,mbti,profession,interested_topics. Zep is queried a second time to enrich the node before writing the character. - Simulation — OASIS builds the
agent_graphand runs.
The main loop, cleaned up but faithful, is surprisingly bare:
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() is total delegation: for each active agent, the model decides on its own what to do among CREATE_POST, LIKE_POST, REPOST, QUOTE_POST, FOLLOW, DO_NOTHING. Not everyone acts on every round: who’s active depends on a simulated clock. The default config covers 72 hours in 30-minute rounds = 144 rounds, with activity multipliers by time band (×1.5 at peak hours, ×0.5 at night, ×0.05 in the dead of night) and a per-agent probability (activity_level). The seed is an initial_post: the event that lights the fuse. On Reddit there’s also the comment; a dual-platform Twitter+Reddit parallel via subprocess is provided too.
The thorn: the loop that closes on itself
Here’s the piece that makes MiroFish more than a chatter generator. Every action an agent performs is re-translated into natural language and re-injected into the memory graph while the simulation runs. The method that does the translation is trivial and, for that reason, brilliant:
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}"
A like isn’t a counter going up: it becomes the sentence “So-and-so: liked What’s-his-name’s post «…»”. These sentences are buffered per platform (BATCH_SIZE = 5) and pushed into a graph via graph.add() from a background thread with a queue — so the simulation doesn’t block waiting on the network. The result is a closed loop: agents act → actions become facts in the graph → the graph is the memory the agents read on the next round → new actions. The world writes its own story and re-reads it in real time. The “ending” of Dream of the Red Chamber isn’t predicted: it sediments in this graph, round after round, and in the end a ReACT-style ReportAgent queries it with semantic search tools and pulls out a readable report. You can also stop and interview a single agent (INTERVIEW action, injected via IPC): your “god’s-eye point of view”.
The honest gotcha: it’s not private, and making it so hurts
My second discovery is less poetic. MiroFish, as it is, phones home: memory lives on Zep Cloud (SaaS), inference on Alibaba Bailian. For a confidential corpus — the “serious” use the project itself claims, testing policy and PR at zero risk — it’s a no. So I mapped what it would take to bring it entirely onto your own iron, and the sharp edges are instructive:
- Zep → Graphiti + FalkorDB. Zep is the SaaS built on top of Graphiti (OSS, Apache-2.0); FalkorDB is the graph with a Redis protocol. I counted 11 Zep SDK methods used across 9 files —
graph.search(),node.get_by_graph_id(),graph.add()… — to replicate behind an adapter with an identical interface, so the calling code doesn’t change. - The async/sync hell. Graphiti is entirely
async; Flask is synchronous and the memory updater already uses threads+queues. A naiveasyncio.run()per call destroys the persistent connections to FalkorDB. You need a single background event loop andrun_coroutine_threadsafe().nest_asynciojust masks the problem. - The silent killer. Graphiti extracts entities by calling an LLM that must return valid JSON. If vLLM starts without guided decoding (
--guided-decoding-backend), extraction “succeeds” without errors but the graph stays empty: zero nodes, garbage simulations, and you only notice at the end. Mandatory smoke test: after the ingest, count the nodes. - The VRAM math. Qwen2.5-72B in FP16 wants ~144 GB; an H100 has 80. It doesn’t fit. Either AWQ (~40 GB) or tensor-parallel across two cards.
How it’s going (real status)
I’ll be honest: I didn’t run the full 144 rounds. The README says so explicitly — “high consumption, try first with fewer than 40 rounds” — and without an LLM key and a Zep account the simulation doesn’t start. I read the code thoroughly, traced the loop, and designed the on-prem migration; the Graphiti+FalkorDB adapter is designed, not built. The upstream project is alive and real (it’s on Trendshift, it has a public demo); my exploration is at the level of architecture and “what would happen if”. The jump between “documented” and “run at home” stays right there, in those four sharp edges above.
What I learned
That the word “prediction” here is a Trojan horse. MiroFish doesn’t estimate a future: it cultivates one, and the quality of the result depends less on the model and more on the loop — on the fidelity with which every micro-action returns to being shared memory. The most important piece of code isn’t the LLM: it’s those five lines that turn a like into a sentence and put it back into the graph. And I learned, again, that “open-source” and “self-hosted” aren’t synonyms: a project can be entirely on GitHub and remain, in fact, a client of two clouds. The distance between the two is measured in adapters, event loops and VRAM — not in licenses.
Someone else’s project, public: github.com/666ghj/MiroFish · simulation engine: OASIS / CAMEL-AI. I just looked inside it.


