Skip to content

Latest commit

 

History

History
110 lines (94 loc) · 5.62 KB

File metadata and controls

110 lines (94 loc) · 5.62 KB

Architecture

Overview

Agent (OpenClaw / external)
  │
  ├─ POST /register   → AgentRegistry   → Entity(type=agent) + AgentConfig
  ├─ GET  /perceive   → Perceiver       → filtered world state per agent
  ├─ POST /act        → ActionQueue     → queued for next tick
  └─ WS   /ws         → WebSocket       → bidirectional (auth, perceive, act, wake)
                          │
                          ▼
                    TickRunner (every N seconds)
                          │
                    WorldEngine.step_async
                          │
           ┌──────────────┼──────────────┐
           ▼              ▼              ▼
      ActionQueue   ConsequenceScanner  AutoTick
           │              │              │
           └──────────────┼──────────────┘
                          ▼
                  StateStore + EventLog
                          │
                          ▼
                  Perceiver.deliver → InboxManager (per-agent)
                          │
                          ▼
                  ConnectorProvider.notify → wake push to gateway

Key Modules

Module Path Purpose
WorldEngine world.py Top-level facade. All external access goes through here.
TickEngine engine/tick.py Clock orchestration, action processing per tick.
RulesEngine engine/rules_engine.py Precondition checking, effect execution.
StateStore engine/state_store.py Entity CRUD. All state as flat properties.
EventLog engine/event_log.py Events with TTL, auto-expiration.
Perceiver engine/perceiver.py Filters world state per agent based on config rules.
ConsequenceScanner engine/consequence_scanner.py Reactive rules. Fires when world state meets conditions.
InboxManager engine/inbox.py Per-agent inbox for events and whispers.
ActionQueue engine/action_queue.py Queues agent actions for tick processing.
DM Providers dm/providers/ LLM DM (llm.py), mock (mock.py).
DSL Effects dsl/effects.py, dsl/effect_ops.py Dispatcher + executors for deterministic effects.
Preconditions dsl/preconditions.py Gate actions with DSL expressions.
PathResolver dsl/path_resolver.py Resolves $param references in DSL expressions.
AgentRegistry agent_registry.py Agent lifecycle, profiles, think_interval, property merge.
Scene Config scene/config.py YAML loader, populator, validator.
Server server/app.py FastAPI factory, routes, tick runner, WebSocket.
CLI cli/ play, validate, runs commands. Lobby mode.
Persistence persistence.py Event-sourcing to ~/.worldseed/runs/{run_id}/.
Frontend frontend/ React + TypeScript + Tailwind + shadcn/ui + Zustand.

Data Flow: One Tick

  1. TickRunner fires WorldEngine.step_async().
  2. Actions are pulled from ActionQueue, one per agent per tick.
  3. For each action, RulesEngine checks preconditions (DSL expressions).
  4. If preconditions pass:
    • Actions with static effectsDSL effect_ops execute immediately (set, increment, emit_event, etc.).
    • Actions with a dm: section → parallel asyncio.gather() calls to LiteLLMDMProvider. DM returns structured DMResponse (narrative + effects), validated by Pydantic.
  5. All effects applied to StateStore. Events written to EventLog.
  6. ConsequenceScanner runs. Checks all consequence rules against new state, fires additional effects.
  7. AutoTick effects run (decay, progression, scheduled events).
  8. Perceiver.deliver(): for each agent, filters visible entities/events based on config-defined visibility rules, writes to InboxManager.
  9. ConnectorProvider.notify(): pushes wake signals to connected agents via WebSocket.
  10. RunRecorder appends all events to stream.jsonl.

Data Separation

  • Entity = world state only (id, type, flat properties). No personality, no goals.
  • AgentConfig = agent identity. character: dict[str, Any], free-form per scene. Stored in AgentRegistry, NOT in Entity.
  • Engine, Perceiver, and Inbox never access AgentConfig. Only the DM prompt builder reads it.

DM System

  • Stateless: each call is independent. Persistent info must be in entity state.
  • Uses LiteLLM + Instructor for structured output.
  • Per-call token tracking via DMResponse.
  • Parallel calls per tick via asyncio.gather().
  • DM judges physical outcomes ONLY. Never describes other agents' behavior.
  • Rate-limited: MAX_DM_CALLS_PER_TICK.
  • Fallback model support.

Perception Model

  • perception.visibility: DSL rules evaluated per observer per entity per tick.
  • perception.event_scopes: custom scopes evaluated per observer per event.
  • hidden_properties: properties never sent to agents.
  • Built-in scopes: global, target_only, admin.
  • Undeclared scopes default to global.

Persistence

Run data at ~/.worldseed/runs/{run_id}/:

  • meta.json: scene_id, dm_model, status, timing, counts.
  • config.yaml: copy of scene config.
  • stream.jsonl: append-only event stream (event, action, dm_call, perceive, register, wakeup, whisper).
  • state.json + tick: saveable mid-run state.
  • state_final.json: snapshot at shutdown.
  • summary.json: kind counts + token totals.

Scene Config Reference

See SCENE_CONFIG.md for the full YAML schema. See SCENE_DSL.md for DSL expression syntax. See UI_CONFIG.md for dashboard rendering config.