Entities shouldn’t magically know things. Every piece of knowledge should have a traceable origin—who learned what, from whom, when, with what confidence.Key principle: entity.knowledge_state ⊆ {e.information for e in entity.exposure_events}An entity cannot know something without a recorded exposure event explaining how they learned it.
@Validator.register("biological_constraints", "ERROR")def validate_biological_constraints(entity: Entity, context: dict): age = entity.entity_metadata.get("age", 0) action = context.get("action", "") violations = [] # Age-based constraint checks if age > 100 and "physical_labor" in action: violations.append(f"age {age} incompatible with physical labor") if (age < 18 or age > 50) and "childbirth" in action: violations.append(f"age {age} outside plausible childbirth range (18-50)") if age < 5 and any(a in action for a in ["negotiate", "strategic_planning", "combat"]): violations.append(f"age {age} incompatible with {action}") if age > 80 and any(a in action for a in ["sprint", "heavy_lifting", "combat"]): violations.append(f"age {age} incompatible with {action}") if violations: return {"valid": False, "message": "; ".join(violations)} return {"valid": True, "message": "Biological constraints satisfied"}
Information propagation respects relationship topology.Entities can only share knowledge if they have a relationship path. Knowledge doesn’t teleport across disconnected subgraphs.
Note: Specific numerical values (O2 rates, water capacity, radiation levels) in simulation output are LLM-generated narrative, not computed by the engine. The engine enforces structural constraints (information conservation, energy budgets, behavioral inertia), not physics calculations.
An LLM-based Knowledge Extraction Agent that understands semantic meaning.From workflows/knowledge_extraction.py:1-22:
"""LLM-based knowledge extraction from dialog turns.Replaces naive capitalization-based extraction with an intelligent agentthat understands semantic meaning and extracts only valuable knowledge items.The agent is passed:1. The dialog turns to analyze2. Causal graph context (what knowledge already exists)3. Entity metadata (who is speaking, who is listening)It returns structured KnowledgeItem objects with:- Semantic content (complete thoughts, not single words)- Speaker/listener attribution- Category (fact, decision, opinion, plan, revelation, question, agreement)- Confidence and causal relevance scores"""
class KnowledgeItem(BaseModel): content: str # Complete semantic unit (not a single word!) speaker: str # Entity who communicated this listeners: List[str] # Entities who received it category: str # fact, decision, opinion, plan, revelation, question, agreement confidence: float # 0.0-1.0, extraction confidence context: str | None # Why this matters in the scene source_turn_index: int | None # Which turn (0-indexed) causal_relevance: float # 0.0-1.0, importance for causal chain
| Category | Description | Example |
|----------|-------------|---------||
| fact | Verifiable information | “The meeting is at 3pm” |
| decision | Choice communicated | “We decided to pivot to B2B” |
| opinion | Subjective view | “I think the design needs work” |
| plan | Intended future action | “We’ll launch in March” |
| revelation | New information changing understanding | “The competitor already filed the patent” |
| question | Only if reveals information itself | “Did you know about the acquisition?” |
| agreement | Consensus reached | “We all agree on the pricing” |
The agent receives causal context from existing exposure events to:
Avoid redundant extraction - Don’t store facts already in the system
Recognize novel information - New facts worth storing
Understand relationships - How new knowledge connects to existing
def build_causal_context(entities, store): """Build context from existing knowledge for the extraction agent.""" for entity in entities: # Get recent exposure events exposures = store.get_exposure_events(entity.entity_id, limit=10) # Include static knowledge from metadata static = entity.entity_metadata.get("knowledge_state", []) # Format as context for LLM ...
class KnowledgeExtractionResponse(BaseModel): items: list[ExtractedKnowledge] = Field(default_factory=list) reasoning: str | None = Field( default="", description="Brief reasoning about what was extracted and why" ) skipped_content: Any | None = Field( default=None, description="Content that was intentionally not extracted" )