Agent World Simulator
May 2025Overview
Agent World Simulator is an experimental Python sandbox where autonomous LLM-powered agents can dynamically expand their own capabilities through code generation. The standout feature is the “Angel System”—an AI-powered code generator that creates new Python ability classes on-the-fly when agents request capabilities they don’t possess.
The simulation uses an Entity-Component-System (ECS) architecture combined with behavior trees and LLM-driven reasoning. Agents perceive their environment, make decisions, and can even learn by observing abilities used by other agents. All assets are procedurally generated, and the entire system runs as a single Python process with no external dependencies beyond an optional LLM API key.
Screenshots


Problem
Traditional game AI is constrained by pre-programmed behaviors. Agents can only use abilities developers explicitly coded. This project explores a radical alternative: what if agents could identify gaps in their capabilities and request entirely new abilities generated by an LLM?
This creates a testbed for emergent AI behavior research, where complex strategies can develop organically. The challenge is making LLM-generated code safe, integrating it seamlessly at runtime, and maintaining determinism for debugging and replay.
Approach
The solution combines a robust ECS game architecture with async LLM integration and hot-reloadable Python modules.
Stack
- ECS Core - Custom Entity-Component-System for flexible composition without inheritance hierarchies
- Pygame - Lightweight 2D rendering with camera controls and debug overlays
- httpx + asyncio - Non-blocking LLM API calls through OpenRouter with request queuing and caching
- Python dynamic imports - Hot-reload generated ability modules without restarting the simulation
- YAML configuration - Centralized config with dataclass validation
Challenges
- LLM latency blocking simulation - Solved with a dedicated async worker thread and a “world pause” mechanism that suspends normal systems while the Angel generates abilities
- Safe code execution - Rather than sandboxing execution, the Angel LLM performs “conceptual testing”—reasoning about the generated code’s safety before integration
- Ability discovery - Implemented a Vault system with metadata tags for semantic matching before falling back to LLM generation
- Deterministic replay - All non-deterministic events (LLM calls, ability grants) logged with tick numbers for replay debugging
Outcomes
The Angel System successfully generates working Python ability classes from natural language descriptions. Agents can request “I need to attack from a distance” and receive a generated ranged attack ability that integrates with the combat system.
Key learnings:
- ECS architecture makes runtime code injection practical—new abilities slot in without modifying existing systems
- LLM-generated code requires careful prompt engineering with templates and constraints to produce valid Python
- The world-pause pattern elegantly handles slow LLM calls without complex async game logic
- Behavior trees provide reliable fallbacks when LLM reasoning is unavailable or times out
Implementation Notes
Angel System Flow
When an agent requests an ability it doesn’t have:
# 1. Check the Vault for existing abilities
vault_match = get_vault_index().lookup(description)
if vault_match:
self._grant_to_agent(agent_id, vault_match)
return {"status": "success", "ability_class_name": vault_match}
# 2. Generate new ability via LLM
prompt = self._build_angel_code_generation_prompt(
description,
templates.get_world_constraints_for_angel(),
templates.get_code_scaffolds_for_angel(),
)
generated_code = llm.request(prompt, model=llm.angel_generation_model)
# 3. Write to disk and hot-reload
write_ability_file(generated_code)
ability_system.reload_abilities()
Ability Base Class
All abilities—built-in, vault, and generated—inherit from a simple abstract interface:
class Ability(ABC):
@property
@abstractmethod
def energy_cost(self) -> int: ...
@property
@abstractmethod
def cooldown(self) -> int: ...
@abstractmethod
def can_use(self, caster_id: int, world: Any, target_id: Optional[int] = None) -> bool: ...
@abstractmethod
def execute(self, caster_id: int, world: Any, target_id: Optional[int] = None) -> None: ...
Agent Decision Pipeline
PerceptionSystem → visible_entities, visible_ability_uses
↓
prompt_builder → LLM context with agent state, goals, and observations
↓
LLMManager.request() → queued async call to OpenRouter
↓
AIReasoningSystem → parse response into Action objects
↓
ActionQueue → validated actions for execution
↓
ActionExecutionSystem → ability checks, combat, Angel requests
Configuration
world:
size: [100, 100]
tick_rate: 10
paused_for_angel_timeout_seconds: 60
llm:
mode: offline # offline | echo | live
agent_decision_model: <model_id>
angel_generation_model: <model_id>
Related Posts
No posts yet.