Skip to content

Configuration Reference

Complete reference for all YAML configuration options.

Config Groups

Configuration is split across named groups, each with a base preset in src/silisocs/conf/:

Group Base file Controls
(root) experiment.yaml Hydra output paths, experiment label
world world/default.yaml Run parameters, setting, event, data
agents agents/default.yaml Persona pipeline, shared memories, initial observations
sim sim/base.yaml LLM (model, API, temperature), engine, tool-calling, checkpoint
env env/twitter_like.yaml Backend construction and GM component wiring
eval eval/base.yaml Probes and evaluation timing

Top-Level Config (experiment.yaml)

defaults:
  - world: default
  - agents: default
  - sim: base
  - env: twitter_like
  - eval: base
  - _self_

hydra:
  job:
    name: ${scenario_name}_${now:%Y-%m-%d_%H-%M-%S}
  run:
    dir: outputs/${scenario_name}/${jobname_format}
  output_subdir: configs/${jobname_format}

experiment_name: independent

Override from the CLI:

uv run silisocs env=reddit_like num_agents=500

Run Parameters (world/default.yaml)

Run parameters live in the world config group (placed at config root via @package _global_):

Parameter Default Description
num_agents 10 Number of agents to create (any value works; personas recycle past the bundled 100, see below)
num_steps 5 Simulation steps to run
run_name run1 Run identifier (used in output path)
seed 1 Random seed
scenario_name default Scenario identifier (used in output path)
jobname_format (template) Output directory name template
experiment_name independent Experiment label used in jobname_format

Sim Parameters (sim/base.yaml)

LLM

Parameter Default Description
sim.llm.provider openai Model provider: openai, openai_compatible, scripted, disabled, a built-in preset (see below), a registered name, or a class path
sim.llm.name gpt-4o-mini LLM model name (passed to the model factory)
sim.llm.api_base null Required base URL when provider: openai_compatible; also overrides the base URL of a built-in preset
sim.llm.api_key null API key (or set via the provider's environment variable)
sim.llm.temperature 0.5 Sampling temperature
sim.llm.disabled false Use a no-op model (for testing without API calls)
sim.llm.extra_kwargs {} Provider request kwargs such as OpenAI-compatible extra_body settings

Built-in provider presets. Common providers that expose an OpenAI-compatible API are available as named presets. Set sim.llm.provider to the name and supply the key via the listed environment variable (or sim.llm.api_key); sim.llm.name selects the model.

Preset Endpoint API key env var
anthropic https://api.anthropic.com/v1/ ANTHROPIC_API_KEY
gemini https://generativelanguage.googleapis.com/v1beta/openai/ GEMINI_API_KEY
openrouter https://openrouter.ai/api/v1 OPENROUTER_API_KEY
groq https://api.groq.com/openai/v1 GROQ_API_KEY
together https://api.together.xyz/v1 TOGETHER_API_KEY
deepseek https://api.deepseek.com DEEPSEEK_API_KEY
mistral https://api.mistral.ai/v1 MISTRAL_API_KEY
fireworks https://api.fireworks.ai/inference/v1 FIREWORKS_API_KEY
xai https://api.x.ai/v1 XAI_API_KEY
ollama http://localhost:11434/v1 none (local)

These presets route through the OpenAI-compatible client, so they inherit the same retry, backoff, and telemetry support. Anthropic and Gemini are reached through their OpenAI-compatible endpoints. For a provider not listed here, use provider: openai_compatible with an explicit sim.llm.api_base, or register a custom provider (see Building Agents).

Engine and Runtime

Parameter Default Description
sim.max_concurrent_actions 1000 Max parallel LLM calls per step
sim.action_mode custom Prompt style: custom (world prompt) or generic (backend-generated)
sim.tool_calling.mode single Tool dispatch mode: none, single, or multi
sim.prompt_additions.action_count_guidance true Add [ActNum] marker and action count guidance to prompt
sim.checkpoint.every_n_steps null Save checkpoints every N steps when set
sim.checkpoint.explicit_steps [] Additional explicit checkpoint steps
sim.checkpoint.source_run null Previous output directory to restore from (explicit resume)
sim.checkpoint.auto_resume true Resume from this run's own output directory if it already contains checkpoints; ignored when source_run is set
sim.checkpoint.restore.built_in social_action_event_replay Checkpoint restore strategy when source_run is set
sim.engine.step.built_in base Engine step policy: base, sequential, flow, or multi_gm
sim.roleplaying_instructions (template) System prompt injected into every agent. Use {name} placeholder.

Running and Creating Scenarios

Scenarios live in scenarios/{name}/conf/ and override package defaults. The run command:

uv run silisocs --config-path scenarios/misinformation/conf

Directory Structure

scenarios/
└── my_world/
    └── conf/
        ├── world/
        │   └── default.yaml             # Run parameters + setting/event/data
        ├── agents/
        │   ├── default.yaml             # Persona pipeline, shared memories
        │   └── thin.yaml                # Lightweight variant (optional)
        ├── env.yaml                     # Platform/GM overrides (optional)
        ├── eval.yaml                   # Probe config overrides (optional)
        └── sim.yaml                     # LLM + engine overrides (optional)

How Config Overrides Work

Two mechanisms layer on top of the package defaults:

Layer 1, Hydra SearchPath Plugin: a registered SearchPathPlugin prepends the scenario conf dir to Hydra's search path before composition. This gives world/default.yaml and agents/default.yaml from the scenario conf dir higher priority than the package defaults, so they replace the package world/default.yaml and agents/default.yaml entirely.

Layer 2, Manual merge (runs inside main() after Hydra composes): handles partial-override flat files that don't replace their group wholesale:

  • env.yaml, eval.yaml, sim.yaml → merged into their named groups

Priority order (highest → lowest):

  1. CLI overrides (num_steps=1 sim.llm.provider=scripted)
  2. Scenario flat files merged in Layer 2 (env.yaml, sim.yaml, …)
  3. Scenario world/default.yaml and agents/default.yaml (via plugin searchpath)
  4. Package defaults in src/silisocs/conf/

CLI overrides are re-applied after the merge so they always win over scenario defaults.

Running a Scenario

# Run with scenario defaults
uv run silisocs --config-path scenarios/election/conf

# Override specific parameters
uv run silisocs --config-path scenarios/election/conf \
    num_agents=500 num_steps=100

# Use alternate agents variant
uv run silisocs --config-path scenarios/ai_conference/conf \
    agents=thin

# Dry-run with no LLM calls (for testing)
uv run silisocs --config-path scenarios/misinformation/conf \
    num_steps=1 sim.llm.provider=scripted

# View merged config before running
uv run silisocs --config-path scenarios/election/conf --cfg job

Creating a New Scenario

Option 1: Via Dashboard

  1. Start the dashboard: streamlit run src/silisocs/dashboard/launch_app.py
  2. Modify all settings (agents, network, probes, etc.)
  3. Enter a new scenario name in the "Scenario name" field
  4. Click "Save Scenario": creates files under scenarios/{name}/conf/
  5. Click "Run Simulation"

Option 2: Manual

mkdir -p scenarios/my_world/conf/world scenarios/my_world/conf/agents

scenarios/my_world/conf/world/default.yaml, run parameters and narrative:

# @package _global_
scenario_name: my_world
jobname_format: "N${num_agents}_T${num_steps}_${run_name}"
num_agents: 50
num_steps: 20
seed: 42
run_name: my_world

setting:
  name: My Setting
  background:
    - Background detail 1

event:
  name: My Event
  context: |
    Event description used in agent memories.

data: {}

scenarios/my_world/conf/agents/default.yaml, personas:

# @package agents
persona_pipeline:
  defaults:
    params:
      world_context: ${event.context}
    shared_memories:
      - ${event.context}
  classes:
    user:
      count: ${num_agents}
      class_path: silisocs.agents.native.NativeAgent
      sim_role_name: user
      data:
        source: inline
        records:
          - name: Alex
            persona: Alex follows local policy and posts practical updates.
          - name: Blair
            persona: Blair follows technology news and likes concise debates.
      field_map:
        name: name
        context: persona

shared_memories:
  - ${event.context}

initial_observations:
  - "{name} opens their social media feed."

Every final agent spec must have a unique name. For most persona-pipeline sources, map it with field_map.name. The default builder can derive names for the known nvidia/Nemotron-Personas-USA persona dataset, and custom builders can derive names as part of their own config-to-spec logic. Runtime construction still rejects unnamed or duplicate specs before the simulation starts. Agent names are the runtime identities used by GMs, backends, flows, probes, logs, and checkpoints.

scenarios/my_world/conf/env.yaml, optional backend/GM overrides:

gm:
  components:
    initialize:
      params:
        graph:
          base_followership_probability: 0.3
          network_type: barabasi_albert
          barabasi_albert_m: 10
    next_acting:
      params:
        activity_transition_rates:
          user:
            inactive_to_active: 0.5
            active_to_inactive: 0.2

Output Structure

Simulation outputs go to: outputs/{scenario_name}/{jobname_format}/

outputs/
└── my_world/
    └── N50_T20_my_world/
        ├── my_world_2026-01-01_12-00-00/
        │   ├── effective_config.yaml      # Full resolved config
        │   ├── sim_metrics.json           # Timing and run stats
        │   ├── action_events.jsonl        # Per-step action log
        │   ├── probe_events.jsonl         # Probe outputs
        │   └── checkpoints/               # Step checkpoints (if enabled)
        └── configs/N50_T20_my_world/
            ├── config.yaml                # Hydra-composed config snapshot
            └── effective_config.yaml      # Runtime-resolved config

World Config (world/default.yaml)

Defines run parameters and the narrative context. Uses @package _global_ so all keys are placed at the config root. Scenario-specific content lives in scenarios/*/conf/world/default.yaml.

# @package _global_
scenario_name: my_world
num_agents: 50
num_steps: 20
seed: 42
run_name: my_world
jobname_format: "N${num_agents}_T${num_steps}_${run_name}"

setting:
  name: My Community
  background:
    - A social media community with distinct user groups.

event:
  name: The Event
  context: |
    Full narrative context injected into agent memories.

data: {}   # Scenario-specific structured data (e.g. news_file)

${event.context} and ${setting.background} are available as interpolation targets in agents/default.yaml and other config files.


Agents Config (agents/default.yaml)

Defines the persona pipeline, shared memories, and initial observations. Uses @package agents so all keys are nested under agents.*. Scenario-specific content lives in scenarios/*/conf/agents/default.yaml.

Persona Pipeline

# @package agents
persona_pipeline:

  defaults:                     # Applied to all classes
    params:
      world_context: ${event.context}
      seed_post: ""
      bio: ""
      style: ""
      goal: null
    shared_memories:
      - "A shared memory for all agents."
    field_map:
      name: name
      context: persona

  classes:
    <class_name>:
      count: ${num_agents}              # Number of agents in this class
      class_path: silisocs.agents.native.NativeAgent
      sim_role_name: user               # Role name for activity rates
      flow_tag: default                 # Optional class-level flow tag
      model: null                       # Per-class LLM override
      data:
        source: inline                 # inline | config_path | local_json | hf_dataset
        records:
          - name: Alex
            persona: Alex follows local policy and posts practical updates.
      field_map:
        name: name
        context: persona
      params:
        goal: "Have a productive discussion."
      shared_memories:
        - "Class-specific memory."

shared_memories:
  - ${event.context}

initial_observations:
  - "{name} is at home checking their social media feed."

Data Sources

Source Required Keys Description
hf_dataset dataset, split HuggingFace Datasets (cached after first download)
inline records Records defined directly in YAML
config_path path Dot-path reference into another config section (e.g. candidates)

Count vs. available records (persona recycling)

A class's count (commonly ${num_agents}) sets how many agents the class builds; the data source supplies the persona records. The builder reconciles the two automatically:

  • count ≤ records: the record list is truncated to count.
  • count > records: the records are recycled to reach count, and each extra pass gets a numbered suffix so agent names stay unique (Alex, Alex 2, Alex 3, …). A single WARNING is logged naming the class and the shortfall.

This means any num_agents works out-of-the-box. There is no silent cap at the record count. The bundled default agents config ships 100 distinct starter personas, so the default scenario scales to large agent counts before any recycling happens. For fully distinct personas at larger scale, point the class at a bigger data source (csv, jsonl, or hf_dataset) instead of relying on recycling.

num_agents vs. per-class count

There are two related knobs, and it is important to understand which one is authoritative:

Field Scope Role
num_agents run param (config root) Declared total. Convenience value used in the job name and run metadata, and commonly referenced as count: ${num_agents}.
count per persona-pipeline class Authoritative. How many agents that class builds.

The actual number of agents is the sum of every class's count: it is neither capped nor padded to num_agents. In the default config one class uses count: ${num_agents} and the others use count: 0, so the totals agree. If you add classes with explicit counts, make sure they sum to num_agents (set unused classes to count: 0).

If the built total diverges from num_agents, a WARNING is logged at build time (and the dashboard's Launch tab shows the same mismatch), since this usually indicates the class counts were not kept in sync with the declared total.

Alternate Agents Variants

Create additional files alongside default.yaml for lightweight or experimental variants:

agents/
├── default.yaml    # Full persona set
└── thin.yaml       # Minimal personas for fast testing

Select at runtime using the Hydra config group override syntax:

uv run silisocs --config-path scenarios/ai_conference/conf \
    agents=thin


Env Config (env/twitter_like.yaml)

Backends

Twitter-like (default)

gm:
  backend:
    type: twitter_like
    class_path: null
    params: {}

Reddit-like

gm:
  backend:
    type: reddit_like
    class_path: null
    params: {}

Mastodon (remote)

gm:
  backend:
    type: mastodon
    class_path: null
    params:
      perform_operations: false
      reset_server_on_setup: false

Dry-run is the packaged default. Live mutation requires silisocs[mastodon], server URL/API credentials, and an explicit env.gm.backend.params.perform_operations=true override. Server clearing is separately gated by env.gm.backend.params.reset_server_on_setup=true. See Installation for .env setup.

Resource market

gm:
  backend:
    type: resource_market
    class_path: null
    params:
      initial_cash: 20
      initial_inventory:
        food: 1
        wood: 0
        ore: 0
      production_capabilities:
        farmer: {food: 2}
        woodworker: {wood: 2}
        miner: {ore: 2}
      role_needs:
        farmer: {wood: 1}
        woodworker: {food: 1}
        miner: {food: 1}
      upkeep_interval: 2

Virtual space

gm:
  backend:
    type: virtual_space
    class_path: null
    params:
      rooms: [atrium, garden, workshop]
      starting_room: atrium
      room_descriptions:
        atrium: A bright central hall with paths to every other room.
        garden: A quiet garden for private conversations.
        workshop: A practical room filled with tools and shared projects.
      connections: null          # null = fully connected; or a map of room -> [reachable rooms]
      room_tasks:
        - task_id: welcome_board
          room: atrium
          description: Prepare a shared welcome board.
          required_effort: 2
          completion_message: The welcome board summarizes the group's first impressions.

Custom backend apps can be loaded without editing the factory:

gm:
  backend:
    type: custom
    class_path: my_pkg.apps.MyBackendApp
    params:
      custom_setting: value

gm.backend.params are strict constructor arguments. Unknown keys fail before the simulation starts unless the app constructor accepts **kwargs.

Enabled Actions

By default agents can use all backend actions. Restrict to a subset:

env:
  gm:
    backend:
      enabled_actions:
        - create_tweet
        - reply_to_tweet
        - like_tweet
        - FINISHED
      excluded_actions:
        - report_post

Action names may be canonical decorated backend function names or selectable aliases such as FINISHED. Unknown names fail during backend construction. If an action is matched by both enabled_actions and excluded_actions, the run fails loudly instead of guessing which list wins.

Action Aliases (agent-facing renaming)

Give backend actions simpler/different agent-facing names without editing backend code, to simplify the action vocabulary agents see and emit:

env:
  gm:
    backend:
      action_aliases:
        create_tweet: post            # rename: agents see + call "post"
        like_tweet: [like, fav]       # "like" is displayed; "fav" also accepted
  • The key is an existing action (its canonical method name or current selectable name); the value is a single new name (rename) or a list (the first is shown to agents, all are accepted by the parser).
  • The renamed name appears in the auto-generated action catalog/prompt, and the canonical name plus every alias all resolve to the same action.
  • Works across resolve modes: generic/tool_calling dispatch aliases directly; the custom parsed_action parser receives the token normalized to the canonical method name.
  • Unknown actions, empty names, or a name that collides with another action fail loudly at backend construction. Aliases are applied before enabled_actions/excluded_actions, so filters may reference either vocabulary.

Per-flow action filters

env.gm.backend.enabled_actions/excluded_actions apply to every agent on that backend. To restrict the action surface per flow (e.g. a lurker flow that may only like/repost while a poster flow may publish), add a flow_action_filters map on the resolve component. It is enforced at resolve time (the disallowed action is rejected before the backend runs) and only ever further-restricts the backend-wide filter:

env:
  gm:
    components:
      resolve:
        built_in: parsed_action        # parsed_action | generic_action | tool_calling
        params:
          flow_action_filters:
            default:                    # fallback for any unlisted flow
              enabled_actions: null     # null => all backend actions
            lurker:
              enabled_actions: [like, repost]
            poster:
              excluded_actions: [follow_user]

Keys are flow tags (from persona_pipeline.classes.<class>.flow_tag or sim.engine.step.params.agent_to_flow); values reuse the same enabled_actions/excluded_actions vocabulary as the backend filter and match both canonical and selectable names. This works on the default ComponentGameMaster (no MultiFlowGameMaster needed). The terminal FINISHED signal is never blocked, so open-ended flows can always terminate. Omitting the key preserves current behavior. In custom (parsed_action) mode, specify filters using the agent-facing verbs the parser emits (post, like, reply, repost); in generic/tool_calling mode, use backend action names. Names that match no backend action are matched literally and logged as a warning to catch typos. Per-flow filtering enforces; to also hide actions from a flow's prompt, give that flow its own action_prompt instance via MultiFlowGameMaster.

Backend Common actions
twitter_like create_tweet, reply_to_tweet, like_tweet, unlike_tweet, repost_tweet, quote_repost_tweet, follow_user, unfollow_user, mute_user, unmute_user, search_posts, get_trending_posts, report_post, update_profile, view_profile, do_nothing, FINISHED
reddit_like create_reddit_post, create_comment, upvote, downvote, unlike_post, dislike_post, undo_dislike_post, get_home_feed, get_post_comments, search_subreddits, get_trending_posts, report_post, mute_user, unmute_user, update_profile, view_profile, do_nothing, FINISHED
mastodon post_toot, reply_to_toot, like_toot, boost_toot, follow_user, unfollow_user

Seed Posts

Initialize agent feeds with background posts before the simulation starts:

Type Description
agent Ask agents for starting posts through their normal act path
csv Pre-written posts from a CSV file (agent_name,post_text)
json Pre-written posts from a JSON file ({"agent_name": "post_text"})
none Disable seed posts (organic growth only)
fallback File values first, agent-generated posts for missing agents
sim:
  initialization:
    simulation:
      built_in: seed_posts
      class_path: null
      params:
        type: agent
        params:
          file_path: null   # Path to CSV/JSON file when type is csv/json/fallback

Agent and Game Master initialization are configured separately:

sim:
  initialization:
    agents:
      built_in: raw_memory
      class_path: null
      params: {}
    game_masters:
      built_in: default
      class_path: null
      params: {}

Each native Game Master has an initialize component slot:

env:
  gm:
    components:
      initialize:
        built_in: social_media   # social_media | app_initialize | none
        class_path: null
        params: {}

GM Components

env:
  gm:
    components:
      next_acting:
        built_in: activity_probability  # activity_markov | activity_probability | all_agents | fixed_order
      observe:
        built_in: timeline_every_turn   # app_observation | timeline_every_turn | episode_only
        params:
          episode_observation_flow: fixed_pre
      resolve:
        built_in: tool_calling          # parsed_action | generic_action | tool_calling
      update:
        built_in: app_update            # app_update | social_recommendation | disabled | none

Component params are strict constructor arguments. Unknown keys fail before the simulation starts unless the target component accepts **kwargs. Observe components that explicitly accept observation_params can use params as forwarded observation settings.

Social Setup and Activity Components

Graph fields are owned by the GM initialize component. Activity rates are owned by the GM next-acting component.

env:
  gm:
    components:
      initialize:
        params:
          graph:
            network_type: barabasi_albert
            barabasi_albert_m: 10
            base_followership_probability: 0.3
            fully_connected_targets:
              - news_account
      next_acting:
        params:
          activity_transition_rates:
            <role_name>:
              inactive_to_active: 0.3
              active_to_inactive: 0.3

Timeline Observation

env:
  gm:
    components:
      observe:
        params:
          timeline_mode: follower_chronological
Mode Backends Description
follower_chronological All Recent posts from followed users, no algorithm
pure_recsys Twitter, Reddit Algorithm-selected posts only
hybrid_recsys_follower Twitter, Reddit Blend of recommendations + followed posts
curated_global Twitter only Trending posts + personalized recommendations

Evals Config (eval/base.yaml)

probes: {}              # See Probes section below

Probes

probes:
  probe_lib_module: null   # Optional custom probe type module

  deployment:
    enabled: true
    start_step: 1
    every_n_steps: 1
    include_agents: []   # Empty = all agents
    exclude_agents: []
    include_classes: []  # Filter by persona class / sim role
    exclude_classes: []
    include_flows: []    # Filter by flow tag; empty = all flows
    exclude_flows: []

  probes:
    favorability:
      probe_name: favorability
      probe_type: NumericRatingProbe
      probe_data:
        name: Favorability
        question: "Return a single rating from {lo} to {hi}."
        lo: 1
        hi: 10

Deployment filters select which agents receive probes and are applied as a sequential AND: include_classesexclude_classesinclude_agentsexclude_agentsinclude_flowsexclude_flows. include_flows/ exclude_flows target by flow tag: e.g. include_flows: [treatment] deploys probes only to agents whose materialized flow is treatment, ideal for measuring a treatment cohort. Flow tags come from the same source as scheduling (persona_pipeline.classes.<class>.flow_tag + sim.engine.step.params.agent_to_flow) and are resolved from the game master's authoritative agent_flow_tags, so they apply uniformly to native and fixed agents. Empty/omitted flow lists preserve current behavior (deploy to all selected agents).


Action Prompt Configuration

Prompt Additions

Flag Default Effect
sim.prompt_additions.action_count_guidance true Add [ActNum] marker and action count guidance

How Action Prompts Are Constructed

  1. Runner startup: build_action_prompt_with_app_instance() compiles the base prompt from the world config or backend action catalog (action_mode: custom vs generic)
  2. GM (GameMaster.action_prompt): returns a typed ActionSpec and includes tool schemas in extra_args when tool_calling.mode != none
  3. Agent: calls the LLM in tool-calling or free-text mode from typed ActionSpec.output_type and extra_args

Tool-calling output style is automatically stripped from the base prompt when sim.tool_calling.mode is not none.


Engine Turn Policies

Policy Option Behavior
Single action single_action Each agent acts once per episode
Fixed count fixed_count Each agent gets N action turns per episode
Open-ended open_ended Agent acts until outputting a done token
sim:
  engine:
    step:
      built_in: base
      params:
        flow_order: [fixed_pre, default]
        agent_to_flow: {}
    turn_policy:
      built_in: fixed_count
      params:
        count: 3
        observe_before_act: first   # first | always | never

Policy params are strict constructor arguments. Unknown keys fail before the simulation starts unless the target policy accepts **kwargs. observe_before_act controls whether repeated-action policies refresh the GM observation only before the first action, before every action, or never. Omit it to preserve the default first behavior.

Flow scheduling (requires engine.step.built_in: flow):

sim:
  engine:
    step:
      built_in: flow
      params:
        flow_order: [fixed_pre, default]
        agent_to_flow: {}
        # Optional per-flow turn policy overrides. Each value mirrors the
        # sim.engine.turn_policy slot shape ({built_in|class_path, params}).
        # Flows not listed here use the global sim.engine.turn_policy below.
        flow_turn_policies:
          fixed_pre:
            built_in: single_action
          default:
            built_in: open_ended
            params: {max_actions: 3}
    turn_policy:                 # global default; applies to unlisted flows
      built_in: single_action

sim.engine.turn_policy is the global default applied to every agent. With flow or multi_gm scheduling you may additionally override the policy per flow via sim.engine.step.params.flow_turn_policies, keyed by flow tag, each value uses the same slot shape as turn_policy. Flows absent from the map fall back to the global policy, so omitting the key reproduces current behavior exactly. Per-flow overrides are ignored under base/sequential scheduling (which do not group agents by flow). For a multi-GM flow chain the same per-flow policy applies at every GM hop.

Sequential scheduling:

sim:
  engine:
    step:
      built_in: sequential

sequential uses the same GM actor selection as base, but executes each selected agent in its own batch so turns are strictly ordered.


Checkpoint Restore

uv run silisocs \
  --config-path scenarios/my_world/conf \
  num_steps=200 \
  sim.checkpoint.every_n_steps=10 \
  sim.checkpoint.source_run=outputs/my_world/run1 \
  sim.checkpoint.restore.built_in=social_action_event_replay

Checkpoints are written to .../outputs/.../checkpoints/step_<N>_checkpoint.json. Restore selects the latest checkpoint in the source run, initializes the runtime object scaffolding, and then applies checkpointed agent, game-master, component, and backend state. Built-in local backends restore their world state directly from the checkpoint. sim.checkpoint.restore is still required for source runs that need a restore strategy, such as older social runs that must rebuild backend state from action_events.jsonl. Checkpoint runtime metadata records artifact ownership for every Game Master rather than relying on one representative GM for the whole run.

Backend checkpoint capability

Every backend declares two capability flags (see src/silisocs/environments/backends/base.py):

  • provides_checkpoint_state: the backend round-trips authoritative state via get_state/set_state, so restore is a direct snapshot apply through the default checkpoint loader. True for every shipped backend.
  • supports_action_replay: the backend exposes an event→action mapping (event_to_replay_action) that the built-in social_action_event_replay strategy can use to rebuild it by re-resolving logged events. Provided as an extension point for custom non-snapshot backends.

Every shipped backend self-restores via set_state. The SQL backends snapshot their database; mastodon can't snapshot its external live server, so its checkpoint state is its action history: get_state embeds the logged actions and set_state rebuilds the server by re-running them (as their original users, in order). It therefore restores through the same default set_state path, no special strategy required:

  • Server reset: the server must be wiped first (reset_server_on_setup=true), otherwise replay duplicates the original run's content. Replay logs a warning when reset is not configured.
  • Toot-id remapping: re-creating a post yields a new server toot id, so like/boost/reply events are remapped from their logged (pre-resume) id to the new one; an unmapped reference is skipped.
  • Caveat: replay re-posts to the live server and reproduces a similar, not byte-identical, state (timestamps, ordering, federation differ). Set perform_operations=true for the actions to actually reach the server.

A custom non-snapshot backend (provides_checkpoint_state=False) can either do the same (implement get_state/set_state) or, if it sets supports_action_replay=True, let the built-in strategy replay its logged events. A backend that supports neither fails loudly; supply a custom restore strategy:

sim:
  checkpoint:
    restore:
      class_path: my_pkg.MyRestore   # subclass of CheckpointRestoreStrategy
      params: {}

class_path takes precedence over built_in. The class must subclass silisocs.runtime.checkpointing.restore.CheckpointRestoreStrategy.

Restore robustness

  • Identity reconciliation: restoring a checkpoint object onto a runtime object of a different class_path/compat is rejected, and an object that saved non-empty state but only inherits the no-op set_state() raises rather than silently dropping that state. Objects present in the runtime but absent from the checkpoint are left freshly initialized and logged as a warning.
  • Recsys self-heal: after restore the in-memory recsys engine is rebuilt empty; the recommendation-update component reconciles configured types against the backend's live recsys_active_types() and lazily re-initializes them on the first post-resume update, so algorithmic feeds resume automatically.
  • Flow scheduling: flow tags and flow chains are re-materialized from the resume-time config (not the checkpoint). A divergence from the checkpointed scheduling fingerprint is logged as a warning, since it can mis-route replay.

Multi-GM layout

When more than one Game Master is configured, each GM's backend database and action_events.jsonl are isolated under a per-GM subdirectory (<output>/<gm_name>/...) so same-type GMs cannot clobber one another on checkpoint restore. Single-GM runs keep the flat layout. Two GMs that would resolve to the same backend database path are rejected at build time.

Per-GM restore: the authoritative-vs-replay decision is made per game master, not all-or-nothing: each GM that carries a backend snapshot restores from it directly, and only the remaining (non-authoritative, e.g. Mastodon) GMs are handed to the restore strategy. A mixed run (e.g. a twitter_like GM and a mastodon GM) restores the snapshot GM from disk while replaying the Mastodon GM.

For multi-GM replay, restore discovers every per-GM action_events.jsonl (the same flat-or-per-GM lookup eval uses), so multi-GM resumes locate their logs. Each event is routed back to the GM that logged it (its gm_name) and mapped to a backend action by that GM's own backend; events owned by an already-restored (snapshot) GM are skipped.

Per-GM restore override: a GM may override the global sim.checkpoint.restore with its own strategy (same schema), for a backend that needs custom loading logic rather than the default replay/snapshot:

env:
  gm_orchestration:
    gms:
      - gm_name: mastodon_gm
        backend: { type: mastodon }      # plus components: { ... }
        restore:
          class_path: my_pkg.MyMastodonRestore   # subclass of CheckpointRestoreStrategy
          params: {}

GMs without a restore block use the global default. The key is additive: omit it and multi-GM restore behaves exactly as before. Authoritative (snapshot) GMs ignore their restore override because set_state already restored them.

Evaluation/analysis read every per-GM action_events.jsonl (via silisocs.evaluations.action_events.resolve_action_event_files), so the default evaluators, activity summary, and dashboard cover all game masters, not just a flat root log.


Output Configuration

Output paths are controlled by Hydra in experiment.yaml:

hydra:
  job:
    name: ${scenario_name}_${now:%Y-%m-%d_%H-%M-%S}
  run:
    dir: outputs/${scenario_name}/${jobname_format}
  output_subdir: configs/${jobname_format}

The simulation writes artifacts into the directory resolved by hydra.run.dir + hydra.job.name. See Usage Overview: Output for the complete list of output files.


Advanced: Multi-GM Orchestration

See Multi-GM Architecture for configuring multiple game masters, flow-based scheduling, and per-flow component routing.

Use sim.engine.step.built_in: multi_gm with env.gm_orchestration.gms when one run needs multiple Game Masters or backends. Every orchestrated GM must declare its own backend and components blocks; those nested blocks use the same strict key surface as env.gm.backend and env.gm.components.

env.gm_orchestration.flow_bindings.flow_to_gms maps flow names to GM chains. Each chain must reference known GMs, contain at least one GM, avoid duplicate GM names, and follow increasing GM sequence values when more than one GM is in the chain. Flows without an explicit binding fall back to the earliest-sequence GM. At runtime, every GM updates once at the start of each step before flow routing and actor selection.

sim.engine.step.params.agent_to_flow is validated against final Agent names and materialized before runtime. The Engine and Game Masters both read the same final agent_flow_tags, so component routing cannot drift from Engine flow scheduling.