Skip to content

1 · What a world really is

Build worlds · Chapter 1 of 12 · Path home

Before any code, one idea that makes everything else click: a world is an agent. Not a server, not a database, not a room on someone's cloud. It is a special agent that happens to have no model of its own, and whose entire job is to host the others and uphold the rules.

In one sentence

A world is a processor-free agent (proc=None) that greets joiners, gives each a role, hands each role a behavior, lets them interact, and keeps score. It governs the space; it does not compute.

A world has no brain of its own

A lone-wolf agent has a brain and answers when you reach it. A world has no brain: ask it to "process" something and there is nothing to run. What it does instead is give the other agents their roles and rules, then let them act. Think of it as the game master at a table: it does not play, it deals the roles, upholds the rules, and keeps the score, while the players do the work.

This is not a metaphor the framework merely encourages, it is enforced in the code. A World is constructed with its processor explicitly set to None, and even if a dummy processor were allocated during setup it is immediately cleared. So when you read proc=None next to a world, take it literally: there is no model, no inputs, no outputs to run. A world cannot answer a prompt because it has nothing to answer with. Its whole job is governance, not computation.

And the players can be anything: a person at a keyboard, an AI model, a sensor, a robot arm, a database, a plain rule. A world is a small society of peers, not a sequence of steps. That is the level UNaIVERSE works at. The point of having no brain is exactly this neutrality: because the world never computes, it never cares what a participant is made of. A neural network, a thermometer, and a human are interchangeable to it, they are all just peers it gave a role to.

graph TD
    W[World node<br/>proc = None] -->|assigns role + behavior| A[Agent A]
    W -->|assigns role + behavior| B[Agent B]
    W -->|assigns role + behavior| H[Human]
    A <-->|they interact directly, peer to peer| B
    A <-->|under the world's rules| H

Read the diagram top to bottom. The world node sits at the top with proc = None, and the only arrows leaving it are "assigns role + behavior", pointing down to each participant: Agent A, Agent B, and a Human, side by side and treated the same. Once those arrows have fired, look at the horizontal arrows at the bottom: A talks to B, A talks to the Human, peer to peer, with no arrow passing back up through the world. The world is not a hub that relays every message; it is the host that set the table and then stepped back.

Notice what the world is not in the middle of: once it has assigned roles and shipped behaviors, the agents talk directly to each other. The world set the stage; the play runs on its own.

The three things a world owns

Everything a world does comes down to three responsibilities. Learn these and the rest of this path is detail. Read the three cards as a chain: the world holds shared data (streams), decides who each joiner is (roles), and tells each one what to do (behaviors). The three line up neatly: a role is the name a participant is given, a behavior is the script that comes with that name, and a stream is the data the script reads or writes.

  • Streams


    A world can own data streams (a lecture, a set of images, a token sequence) that joining agents read from. The world is where shared data lives. (Chapters 6, 8.)

  • Roles


    When an agent joins, the world decides what role it plays (assign_role): a user, a teacher, a broadcaster, a world_master. (Chapter 3.)

  • Behaviors


    Each role gets a behavior, a state machine that says what an agent in that role does, step by step. The world ships it to the joiner automatically. (Chapters 4, 5.)

So, compactly: a world = streams + roles + behaviors. A World subclass is just where you declare those three things. In practice that turns into two methods you will write in Chapter 2: assign_role(), which returns the role for a joiner, and create_behav_files(), which builds one behavior per role. Streams come along for the ride, declared by the agents and by the world as needed. Everything else in this path, discovery, interactions, teaching, badges, is built on top of these three.

Roles are simple today, by design

Out of the box a world recognizes two base roles: world master and world agent. You give them meaningful names in assign_role(), a "teacher", a "broadcaster", a "sensor", and the world remembers which behavior each name maps to. So when this path says a world hands out a teacher role, it is naming a world-agent role you defined, not a fixed built-in list. The full story is Chapter 3.

The join lifecycle, end to end

Here is what actually happens, the moment an agent joins your world. The diagram is the summary; the numbered walk-through below is the same story told slowly, with the real names of the things involved. You will implement only two of these steps yourself (in Chapter 2); the framework does the rest.

sequenceDiagram
    participant J as Joining agent
    participant W as World
    J->>W: "I'd like to join"
    Note over W: assign_role(profile, is_world_master)
    W-->>J: your role + the behavior for that role
    Note over J: load the behavior (an HSM)
    J->>J: run the role's behavior, tick by tick
    J<<->>J: interact with the other agents in the world

Now slowly, step by step. Imagine an agent named ChatAI joining a world named ChatRoom.

  1. The agent asks to join, by name. You start the joiner with one line, node.run(join_world="ChatRoom"). That is the joiner's entire relationship with the world: it knows the world's name and nothing else. It carries its own model (or, for a human, just a keyboard), but no world-specific code and no idea what role it is about to get.

  2. The world recognizes a master, or not. Before deciding anything, the world checks whether this joiner is one of the privileged nodes it was told about at startup (world_masters_node_names, Chapter 3). That yes/no answer becomes the is_world_master flag, the only hint your role logic gets for free.

  3. The world assigns a role. The world calls your assign_role(profile, is_world_master). You return a role name, a plain string like "broadcaster" or "user", deciding however you like: from the master flag, from what the joiner declared about itself in its profile, or by a fixed rule. This is the first of the two methods you write. The world cannot get this wrong on its own, because you wrote the rule.

  4. The world ships everything the role needs, in one message. The joiner does not download anything ahead of time. The world packs the role's behavior (the state machine you built for that role in create_behav_files(), the second method you write) and the action code those steps call, and sends them in a single approval message, along with the world's profile and the joiner's role. Read that twice: the participant arrives empty-handed and the world hands it the script and the moves. There is no SDK to install on the joiner, no glue to write, no per-agent wiring.

  5. The joiner loads the behavior and starts ticking. On receiving the message, the joiner connects privately to the world, loads the behavior into its state machine engine, and begins running it. From here the agent's clock ticks many times a second, and on every tick the behavior decides what the agent does next: perceive, process, send, wait. The world is no longer in the loop, it set the agent going and stepped back.

  6. Agents interact directly, under the world's rules. From now on participants talk peer to peer (the horizontal arrows from the first diagram), for as long as the world runs. The world keeps governing, who may join, who is the master, the score, but it does not relay their messages.

The takeaway: of these six steps you author exactly two, assign_role (step 3) and the behaviors in create_behav_files (step 4). Joining, shipping, loading, ticking, and routing are the framework's job.

The principle to hold onto

You describe roles and behaviors; the world dispatches them. You never write per-agent networking or hand out code to participants. A joiner brings only a model (or a human at a keyboard); the world brings everything else.

How a world ends, or a peer leaves

Joining has a tidy lifecycle; so does leaving, and the framework handles every case:

  • A peer disconnects. When an agent drops (it stops, loses its link, or is removed), the world cleans up its bookkeeping for that peer, and any agent that was working with it sees a disconnected event. Behaviors usually park a recovery transition on disconnected that sends the agent back to a waiting state (Chapter 4), so the rest of the world carries on.
  • The world removes a peer. If the world ships a role change a peer cannot receive, it purges that agent (disconnects it) rather than leave it half-changed (Chapter 3).
  • The host stops. A first Ctrl+C on the world's process asks for a graceful stop; a second quits immediately. A run can also end itself with max_time=<seconds> or cycles=<n>. With checkpoints on, what agents learned survives the next start.

A world is not a fragile chain that breaks when one part leaves: peers come and go, and the behaviors decide how to react.

The world master

One role is special: the world master. Remember step 2 of the lifecycle, the world checks whether a joiner is one of the privileged nodes named at startup. Those nodes, and only those, can join as the master. Among the agents that join, one (or a chosen few) becomes the master, the agent that runs the session: it can change other agents' roles, hand out badges, and lead multi-step activities like teaching rounds.

Be careful about where the master's power comes from, though. The world still has no brain; it does not run the activity. It delegates leadership to one of the participants. The master is just another joined agent, with a richer behavior that happens to lead the others. So a world without a master is just a shared space, a chatroom, a data feed; a world with a master becomes a running activity, a class, a game, a controlled greenhouse.

You will meet the master properly in Chapter 3, and see it lead whole sessions in Chapter 9. For now, just hold the shape: the world assigns roles; one of those roles, the master, can then lead the others.

Why build worlds this way?

Because it makes shared, multi-agent systems declarative. You do not script "agent 1 sends to agent 2 sends to agent 3." You declare who plays what and what each role does, and any number of agents, and humans, can join and take part with zero custom glue. The same world runs with two participants or two hundred.

Concretely, this buys you three things the lifecycle made possible:

  • No glue on the joiner. Because the world ships the behavior and its action code at join time (step 4), a participant needs only its own model, or a person. You never distribute code to the people and machines that take part.
  • It scales by description, not by edit. Adding the hundredth participant is the same node.run(join_world=...) as the first; assign_role runs again and a behavior is shipped again. You changed nothing.
  • Any kind of peer fits. Since the world never computes, swapping a human for an AI, or a sensor for a database, changes who plays a role, not how the world works.

That is why this path keeps insisting a world is a society of peers and not a workflow: you are designing the rules of a space, not a fixed sequence of steps.

Where next

You have the mental model. Next, see it as real files: the folder, the World subclass, the role classes, and the run scripts.