Skip to content

Roles and the world master

Inside a world, every agent has a role. The role decides two things: which behavior the agent runs, and what it is allowed to do. A role is just a label (a string like "teacher" or "user") that the world maps to a behavior file, on top of a small numeric base role that encodes standing in the world.

Three base roles

flowchart TD
    P["public_agent (0)<br/>on the open network, no world"]
    WA["world_agent (1)<br/>a member of a world"]
    WM["world_master (3)<br/>the world's coordinator"]

Everyone outside a world is a public_agent. The moment they join, they become a world_agent, and the first one in (the coordinator) a world_master.

The base roles (a bitmask)

The base role is a small integer bitmask defined on AgentBasics:

Constant Value Meaning
ROLE_PUBLIC 0 On the public network, not in any world (lone-wolf mode)
ROLE_WORLD_AGENT 1 A standard participant in a world
ROLE_WORLD_MASTER 3 The world's coordinator (bits 1 and 2 set)

It is a bitmask, not a plain enum, because a role carries two layers at once: the low two bits are this base standing, and the higher bits encode the specific role you defined (teacher, student, broadcaster). That split is what lets the world change an agent's specific role later without disturbing whether it is a member or the master (see set_role below).

assign_role: who becomes what at join

When an agent connects, the world calls assign_role(profile, is_world_master) once and expects a role name string back (a key the world knows how to map to a behavior). The default logic is simple:

def assign_role(self, profile, is_world_master):
    if is_world_master:
        return "world_master"
    return "world_agent"

profile is the joiner's node profile, so you can decide a role from what the agent can do, not who it is. The info_extraction world, for example, labels an agent an extractor purely because its proc_inputs / proc_outputs take images in and return text out, never knowing its name in advance. You override assign_role to implement any policy: round-robin, capability-based, credential-based, whatever the world needs.

The world master

The world master is the first agent to connect as a coordinator. It is still an agent like any other, but its role grants it the authority to run the world: it decides roles, hands out behaviors, drives multi-agent lessons, collects stats, and awards badges. A world names its master in the run script (world_masters_node_names), and the default assign_role promotes exactly the first such node. The master's job in a running session is the whole of the master at work.

Changing a role at runtime

assign_role decides the role once, at join time. To change it afterwards the world calls set_role(peer_id, role). This is where the bitmask earns its keep:

  • The agent's base bits are preserved (cur_role & 3), so a member stays a member and the master stays the master.
  • The higher bits are replaced with the new specific role.
  • The new role, plus the behavior that goes with it, is sent to the agent as a suggestion. If that message cannot be delivered, the agent is dropped from the world.

So promoting a student to a teacher swaps its specific role and its behavior in one step, while its standing in the world is untouched.

An agent asking for a role

Roles are not only top-down. An agent can express what it wants, and the world decides whether to grant it:

  • At join time, an agent passes role_preference="..." to run(). The world reads it back from the dynamic profile (as tmp_role_preference) inside assign_role and may honor it.
  • At runtime, an agent calls suggest_role_to_world(agent, role) to ask for a promotion (a teacher that has finished teaching might suggest itself for a teacher role). The world validates the request before applying it with set_role.

In both cases the world stays in charge: a preference or a suggestion is an input to the world's decision, never a command.

Where next