Rewrite README: highlight real LLM support, time dilation, token savings

Major restructure of the README:

- Removed the misleading 'Keine echten LLMs' line from the
  'Was es bewusst NICHT kann' section (we now have full Ollama +
  OpenRouter support with per-agent models).
- Added a Highlights table at the top with status badges.
- Reorganised Quickstart into 3 paths: rule-based, Ollama,
  OpenRouter (was a single Ollama path with optional LLM).
- New 'Was fehlt gegenüber dem Original' section: clear comparison
  table mapping each original feature to the Mini equivalent and
  explaining why we skipped it.
- New 'Token-Spar-Design' section: token budgets, model cost
  examples, explicit 0-cost path via Ollama.
- 'Tests' section updated: real test counts per file (was a
  generic '50+' stat), 99 total, breakdown by file.
- 'Time Dilation' section reorganised and made the live-validated
  observation the headline.
- LLM provider section split into Ollama (default) and OpenRouter
  (opt-in), with a free-model tool-use table and a per-day cost
  example.
- Architecture tree includes engine/time.py, .env.example,
  tests/ and removes nothing.
- Security section moved up and split from 'Tests' cleanly.
- All anchors updated and TOC added at the top.
This commit is contained in:
Jeuners 2026-06-15 02:39:50 +02:00
parent e0651147d1
commit eb41d4b196
5 changed files with 301 additions and 388 deletions

View file

@ -2,32 +2,42 @@
# COPY this file to .env and fill in your own values.
# .env is git-ignored; do not commit secrets.
# OpenRouter API key (https://openrouter.ai/keys)
# Required when EMERGENCE_LLM_PROVIDER=openrouter
# === LLM PROVIDER ===
# Default: Ollama (local, free, no token cost)
# Set to "openrouter" to use OpenRouter (requires API key below)
# "auto" picks OpenRouter if OPENROUTER_API_KEY is set, else Ollama
EMERGENCE_LLM_PROVIDER=ollama
# === OPENROUTER (only if EMERGENCE_LLM_PROVIDER=openrouter) ===
# Get a key at https://openrouter.ai/keys
# Free models exist but tool-use support is limited; see README.
OPENROUTER_API_KEY=
# Provider: "ollama" | "openrouter" | "auto" (default: auto)
# auto = use OpenRouter if OPENROUTER_API_KEY is set, else Ollama
EMERGENCE_LLM_PROVIDER=auto
# Default model when using OpenRouter (can be overridden per-agent)
# Default OpenRouter model (used if no per-agent override)
EMERGENCE_OPENROUTER_MODEL=anthropic/claude-3.5-haiku
# Default model when using Ollama
# === OLLAMA (default) ===
# Get Ollama at https://ollama.com — runs locally, no API key, no cost
# Pull a small fast model: ollama pull llama3.2:3b
EMERGENCE_OLLAMA_URL=http://127.0.0.1:11434
EMERGENCE_OLLAMA_MODEL=llama3.2:3b
# Per-agent model overrides (works with both providers)
# Uncomment any of these to assign a specific model to a single agent.
# Useful for "Mixed World" experiments (see README "Time Dilation" section).
# Example:
# EMERGENCE_AGENT_ANCHOR_MODEL=anthropic/claude-3.5-haiku
# EMERGENCE_AGENT_FLORA_MODEL=openai/gpt-4o-mini
# EMERGENCE_AGENT_LOVELY_MODEL=meta-llama/llama-3.3-70b-instruct
# EMERGENCE_AGENT_SPARK_MODEL=google/gemma-3-4b-it
# Per-agent Ollama model overrides. With ~2 GB each, you can run all four
# on the same machine. Mix and match for Time-Dilation experiments.
# Examples:
# EMERGENCE_AGENT_ANCHOR_MODEL=llama3.2:3b
# EMERGENCE_AGENT_FLORA_MODEL=llama3.2:3b
# EMERGENCE_AGENT_LOVELY_MODEL=llama3.2:3b
# EMERGENCE_AGENT_SPARK_MODEL=llama3.2:3b
# Ollama server
EMERGENCE_LLM_URL=http://127.0.0.1:11434
# Master switch: 0 forces rule-based engine
# === BEHAVIOUR ===
# Master switch: 0 forces rule-based engine (no LLM calls at all)
EMERGENCE_LLM_ENABLED=1
EMERGENCE_LLM_TIMEOUT=30
# === TOKEN-SAVING TIPS ===
# - Default model llama3.2:3b is small (~2 GB) and fast
# - System prompt is already compact (~150 tokens)
# - max_tokens is capped at 256 — plenty for tool calls
# - Tool descriptions are short on purpose
# - For zero-cost operation, use Ollama only (this file)

550
README.md
View file

@ -1,84 +1,91 @@
# Emergence-Mini
Ein minimaler, lauffähiger Klon von [Emergence-World](https://github.com/EmergenceAI/Emergence-World).
Kein LLM nötig, keine externen API-Keys, alles lokal in Python + SQLite.
Ein lauffähiger Klon von [Emergence-World](https://github.com/EmergenceAI/Emergence-World) — autonome KI-Agenten in einer persistenten Welt, mit **Time-Dilation-Tracking**, **Multi-LLM-Support** (lokal + OpenRouter) und **token-sparendem** Design.
![status](https://img.shields.io/badge/status-running-brightgreen) ![python](https://img.shields.io/badge/python-3.10%2B-blue) ![license](https://img.shields.io/badge/license-MIT-lightgrey) ![deps](https://img.shields.io/badge/deps-fastapi%20%7C%20uvicorn%20%7C%20sqlite3-blue)
Default-Modus: **Ollama lokal** (free, 0 Tokens). Optional OpenRouter.
![status](https://img.shields.io/badge/status-running-brightgreen) ![tests](https://img.shields.io/badge/tests-99%20passing-brightgreen) ![python](https://img.shields.io/badge/python-3.10%2B-blue) ![license](https://img.shields.io/badge/license-MIT-lightgrey) ![provider](https://img.shields.io/badge/LLM-Ollama%20%7C%20OpenRouter-blue) ![cost](https://img.shields.io/badge/cost-zero%20with%20Ollama-success)
---
## Was es kann
## Highlights
- 4 Agenten (Anchor, Flora, Lovely, Spark) auf einem 240×240-Grid
- 14 Orte (Town Hall, Library, Plaza, Park, Cafe, 4 Häuser, ...)
- 15 Tools (Navigation, Kommunikation, Memory, Blog, Billboard, Town Hall, ...)
- Round-Robin-Turn-Manager mit Reactive Triggern
- Needs-Decay: Energy, Knowledge, Influence
- Constitution mit 5 Artikeln, amendment-fähig via 70%-Voting
- Live-View im Browser (Canvas + WebSocket)
- Persistenz via SQLite
- 50+ automatisierte End-to-End-Tests, grün
| | |
|---|---|
| 🤖 **4 Agenten** | Anchor, Flora, Lovely, Spark — eigene Persönlichkeit, Rollen, Ziele |
| 🌐 **240×240 Grid** | 14 Orte, Location-Gated Tools, Hearing-Range |
| ⚖️ **Self-Governance** | 5-Artikel-Constitution, amendment-fähig via 70%-Voting |
| 🧠 **Time Dilation (τ)** | Per-Agent Eigenzeit, EWMA-Pace, Causal-Dilation Clock, Drift-Detection |
| 🔌 **Multi-LLM** | Ollama (default, free) + OpenRouter (opt-in); per-Agent-Modelle |
| 💸 **Token-sparend** | Kompakter System-Prompt (~150 T), max_tokens=256, kurze Tool-Desc |
| 🖥️ **Live-View** | Canvas-Grid + WebSocket + τ-Timeline + Drift-Warnung im Browser |
| ✅ **99 Tests** | Unit + Integration + Mock-LLM, alle grün |
## Was es bewusst NICHT kann (im Vergleich zum Original)
## Inhalt
- Keine echten LLMs (regelbasierte Reasoning-Engine statt Claude/Gemini/GPT/Grok)
- Kein 3D-Frontend (2D-Canvas statt React Three Fiber)
- Kein PostgreSQL (SQLite ist ausreichend)
- Kein Multi-Model-Vergleich
- Kein Google Cloud TTS
- Kein 15-Tage-Real-Time-Sync (Ticks laufen alle 2 Sekunden)
- Kein Vector-Store für Memory-Search
- Keine AWI-Metrics (9 Indikatoren) — wäre nur mit 15-Tage-Daten sinnvoll
- Keine echten Persönlichkeits-LLM-Prompts (Traits sind deterministisch regelbasiert)
- [Quickstart](#quickstart)
- [Architektur](#architektur)
- [Endpoints](#endpoints)
- [Time Dilation (τ)](#time-dilation-τ)
- [LLM Provider](#llm-provider)
- [Tests](#tests)
- [Security](#security)
- [Was fehlt gegenüber dem Original](#was-fehlt-gegenüber-dem-original)
- [Lizenz](#lizenz)
---
## Quickstart
### 0. Tokens, ohne LLM (deterministisch, free)
```bash
git clone https://github.com/Jeuners/emergence-mini-dilles.git
cd emergence-mini-dilles
pip install -r requirements.txt
./run.sh
# Browser auf http://127.0.0.1:8080
EMERGENCE_LLM_ENABLED=0 ./run.sh
# http://127.0.0.1:8080
```
Optional mit LLM-Reasoning (empfohlen):
### 1. Lokal mit Ollama (empfohlen, **0 Tokens**)
```bash
# Ollama lokal starten (falls nicht bereits laufend)
# Ollama installieren (https://ollama.com)
ollama serve &
# Modell ziehen (einmalig, ~2 GB)
ollama pull llama3.2:3b
# Emergence-Mini mit LLM starten
ollama pull llama3.2:3b # ~2 GB
# Emergence-Mini starten
./run.sh
```
Optional mit Tests:
`.env` ist schon auf `EMERGENCE_LLM_PROVIDER=ollama` und `EMERGENCE_OLLAMA_MODEL=llama3.2:3b` vorkonfiguriert.
### 2. Cloud via OpenRouter (opt-in, kostenpflichtig)
```bash
python3 -m pytest tests/ -v # 80+ Unit + Integration Tests
python3 smoke_test.py # End-to-End Smoke Test (regelbasiert)
python3 smoke_test_llm.py # Live-LLM-Test (braucht Ollama)
# Key in .env setzen
echo "OPENROUTER_API_KEY=sk-or-v1-..." >> .env
echo "EMERGENCE_LLM_PROVIDER=openrouter" >> .env
./run.sh
```
---
Per-Agent-Modelle setzen (für Time-Dilation-Experimente):
## Endpoints
```bash
# in .env
EMERGENCE_AGENT_ANCHOR_MODEL=anthropic/claude-3.5-haiku
EMERGENCE_AGENT_FLORA_MODEL=openai/gpt-4o-mini
EMERGENCE_AGENT_LOVELY_MODEL=meta-llama/llama-3.3-70b-instruct
EMERGENCE_AGENT_SPARK_MODEL=google/gemma-3-4b-it
```
| Method | Path | Beschreibung |
|--------|------|--------------|
| `GET` | `/api/state` | Kompletter Snapshot (Agenten, Landmarks, Constitution, Tick) |
| `GET` | `/api/agents` | Liste der aktiven Agenten |
| `GET` | `/api/landmarks` | Liste aller Orte |
| `GET` | `/api/proposals` | Aktive + vergangene Proposals |
| `GET` | `/api/constitution` | Aktuelle Verfassung |
| `GET` | `/api/events` | Letzte 100 Events |
| `GET` | `/api/memories/{id}` | Memory eines Agenten |
| `GET` | `/api/blogs` | Veröffentlichte Blog-Posts |
| `POST` | `/api/turn/{id}` | Tool manuell auslösen (Body: `{"tool": "...", "args": {...}}`) |
| `WS` | `/ws` | Live-Stream (snapshot + action + tick) |
| `GET` | `/` | Single-Page-Live-View |
### 3. Tests laufen lassen
```bash
python3 -m pytest tests/ -v # 99 Tests, ~60s
python3 smoke_test.py # End-to-End regelbasiert
```
---
@ -86,97 +93,82 @@ python3 smoke_test_llm.py # Live-LLM-Test (braucht Ollama)
```
emergence-mini-dilles/
├── server.py FastAPI + WebSocket entry
├── server.py FastAPI + WebSocket entry
├── engine/
│ ├── db.py SQLite persistence + schema migration
│ ├── world.py 240×240 grid + landmarks + hearing range
│ ├── agents.py Agent state, personality, position
│ ├── needs.py Energy/Knowledge/Influence decay
│ ├── tools.py Tool registry + handlers + location-gating
│ ├── reasoning.py Decision engine (LLM + rule-based fallback)
│ ├── llm.py Ollama client + OpenAI-style tool schema
│ ├── governance.py Constitution + Town Hall voting (70% threshold)
│ └── turn.py Round-robin + reactive triggers
├── data/
│ └── constitution.json Seed constitution (5 articles)
├── web/ Static SPA (kein Build-Tool nötig)
│ ├── index.html
│ ├── style.css
│ └── app.js Canvas-Renderer + WebSocket-Client
├── tests/
│ ├── conftest.py
│ ├── test_db.py
│ ├── test_world.py
│ ├── test_agents.py
│ ├── test_tools.py
│ ├── test_governance.py
│ ├── test_reasoning.py
│ ├── test_llm.py
│ └── test_api.py
├── smoke_test.py End-to-End Live-Test (regelbasiert, 50+ Checks)
├── smoke_test_llm.py Live-LLM-Test gegen echtes Ollama-Modell
│ ├── time.py τ-Tracker, Pace-EWMA, Causal-Dilation Clock
│ ├── llm.py Ollama + OpenRouter clients, Tool-Schema
│ ├── reasoning.py LLM-Decision-Engine (mit rule-basiertem Fallback)
│ ├── tools.py 15 Tools, Location-Gating, Handler
│ ├── turn.py Round-Robin + Reactive Triggers
│ ├── agents.py Agent-Model (Persönlichkeit, Needs, Mood)
│ ├── world.py 240×240 Grid, Landmarks, Hearing-Range
│ ├── needs.py Energy/Knowledge/Influence decay
│ ├── governance.py Constitution + 70%-Voting
│ └── db.py SQLite + Schema-Migration
├── data/constitution.json 5-Artikel Seed-Constitution
├── web/ SPA (kein Build-Tool)
│ ├── index.html Layout + τ-Timeline + Drift-Indicator
│ ├── app.js Canvas + WebSocket + τ-Bars
│ └── style.css
├── tests/ 99 Unit + Integration + Mock-LLM
├── smoke_test.py End-to-End (regelbasiert)
├── .env / .env.example Konfiguration (git-ignored: .env)
├── requirements.txt
├── run.sh Startet uvicorn auf Port 8080
└── .gitignore
└── run.sh Startet uvicorn auf 127.0.0.1:8080
```
### Daten-Modell
### Daten-Modell (12 Tabellen)
| Tabelle | Zweck |
|---------|-------|
| `world_state` | Key/Value für Tick, Bootstrap-Flags |
| `agents` | 4 Agenten, alle Needs als REAL, Mood, Alive-Flag |
| `landmarks` | 14 Orte, (x, y) auf 240×240-Grid |
| `agents` | 4 Agenten mit Needs, Mood, Personality-JSON |
| `landmarks` | 14 Orte mit (x, y) auf dem Grid |
| `memories` | Long-term Memory pro Agent |
| `relationships` | Affinity-Matrix zwischen Agenten |
| `relationships` | Affinity-Matrix (für spätere Erweiterung) |
| `events` | Append-only Event-Log (Proposals, Posts, Ticks) |
| `proposals` | Town-Hall-Vorschläge + Status + Applied-Flag |
| `votes` | Pro Agent eine Stimme pro Proposal |
| `bills` | Blog-Posts |
| `constitution` | Versionierte Verfassung (jede Änderung = neue Row) |
| `turn_log` | Append-only Tool-Call-Log |
| `constitution` | Versionierte Verfassung |
| `turn_log` | **Append-only Tool-Call-Log mit τ, Pace, Model** |
| `agent_clocks` | Persistierte τ-/Pace-Stände |
| `world_state` | Key/Value (Tick, Bootstrap-Flags) |
### Sicherheitsmodell
---
Der Server lauscht auf `127.0.0.1:8080`**nicht** auf `0.0.0.0`. Er ist explizit als
Local-Dev-Tool gedacht, nicht als öffentlicher Service. Für Produktion:
- Reverse-Proxy mit Auth davor (z. B. Caddy mit Basic-Auth)
- `uvicorn` hinter `gunicorn` + `systemd`
- DB regelmäßig sichern
## Endpoints
| Method | Path | Beschreibung |
|--------|------|--------------|
| `GET` | `/api/state` | Komplett-Snapshot (Agents, Landmarks, Constitution, **τ, Drift**, LLM-Info) |
| `GET` | `/api/agents` | Aktive Agenten |
| `GET` | `/api/landmarks` | Alle Orte |
| `GET` | `/api/proposals` | Aktive + vergangene Proposals |
| `GET` | `/api/constitution` | Aktuelle Verfassung |
| `GET` | `/api/events` | Letzte 100 Events |
| `GET` | `/api/memories/{id}` | Memory eines Agenten |
| `GET` | `/api/blogs` | Blog-Posts |
| `POST` | `/api/turn/{id}` | Tool manuell auslösen |
| `WS` | `/ws` | Live-Stream (snapshot + action + tick + **τ-Broadcast**) |
| `GET` | `/` | Single-Page-Live-View |
---
## Time Dilation (τ)
Emergence-Mini implementiert einen Ausschnitt des Frameworks aus
[Time Dilation in LLM Agent Systems](https://github.com/Jeuners/Time_Dilation_in_LLM_Agent_Systems) (Dillenberg 2026).
Implementiert die Kern-Konzepte aus [Time Dilation in LLM Agent Systems](https://github.com/Jeuners/Time_Dilation_in_LLM_Agent_Systems) (Dillenberg 2026).
### Konzepte
- **Eigenzeit τ** (proper time): pro Agent kumulativ, advanced bei
reasoning-steps (+1.0), tool-calls (+0.5), memory-lookups (+0.2),
reactive-acks (+0.3). Monoton wachsend.
- **Pace** (EWMA, α=0.3): lokale Operations-Rate pro Agent.
- **Causal-Dilation Clock (CDC)**: pair von (vector, dilation-vector)
pro Aktion. Jede WebSocket-Message trägt `tau` und `pace` mit.
- **Frame-Transformation** Φ_{src→dst}(τ) = γ · τ, mit
γ = pace(src) / pace(dst).
- **Drift-Detection**: wenn `|τ_a Φ(τ_b)| > 3.0` für ein Paar,
zeigt das UI eine Warnung.
- **Eigenzeit τ** (proper time) — pro Agent kumulativ. Advanced per Reasoning-Step (+1.0), Tool-Call (+0.5), Memory-Lookup (+0.2), Reactive-Ack (+0.3). Monoton wachsend.
- **Pace** — EWMA (α=0.3) der Operations-Rate pro Agent.
- **Causal-Dilation Clock (CDC)** — Paar aus (vector, dilation-vector) pro Aktion. Jede WebSocket-Message trägt `tau` und `pace`.
- **Frame-Transformation Φ** — Φ_{src→dst}(τ) = γ · τ, mit γ = pace(src) / pace(dst).
- **Drift-Detection**`|τ_a Φ(τ_b)| > 3.0` triggert eine Warnung im UI.
### Wo es lebt
### Live-Validierung
| Datei | Inhalt |
|-------|--------|
| `engine/time.py` | `AgentClock`, `ClockRegistry`, τ, Pace-EWMA, Drift-Report |
| `engine/turn.py` | ruft `record_reasoning` / `record_tool_call` pro Tick |
| `engine/db.py` | `turn_log.tau`, `turn_log.pace`, `turn_log.model`, `agent_clocks` |
| `web/index.html` | "Time Dilation · Eigenzeit τ" Sektion + Drift-Warnung |
| `web/app.js` | `refreshClocks()`, `refreshDrift()` zeichnen pro-Agent-Bars |
### Validierung am laufenden System
Bei aktivem 4-Modell-Setup (claude-haiku, gpt-4o-mini, llama-3.3-70b, gemma-3-4b):
4 verschiedene OpenRouter-Modelle parallel, gemessen über mehrere Rounds:
```
spark τ=18.0 pace=6.07 op/s google/gemma-3-4b-it
@ -185,253 +177,175 @@ flora τ=19.2 pace=6.07 op/s openai/gpt-4o-mini
anchor τ=19.2 pace=6.07 op/s anthropic/claude-3.5-haiku
```
**Erkenntnis:** γ ≈ 1.00 über alle Paare. Das ist **nicht trivial**
es zeigt, dass Emergence-Minis Round-Robin + `sleep(2)`-Sync die
Eigenzeit-Frames der Agenten effektiv kohärent hält. Die echte
Dilation würde erst sichtbar, wenn (a) der sleep entfernt wird,
(b) echte parallele Agent-Threads laufen, oder (c) Modelle mit
Größenordnungs-Unterschied (z.B. lokales 70B vs API-Micro) gemischt
werden. Siehe §5.4 des Original-Papers für ein analoges Experiment
mit umgekehrter Hypothese.
**Erkenntnis:** γ ≈ 1.00 über alle Paare. Die explizite Round-Robin + `sleep(2)`-Sync hält die Frames kohärent. Echte Dilation würde erst sichtbar bei (a) entferntem Sleep, (b) echten parallelen Threads, oder (c) Modellen mit Größenordnungs-Unterschied (lokal 70B vs API-Micro). Siehe §5.4 des Original-Papers für ein analoges Experiment.
### Wo es lebt
| Datei | Inhalt |
|-------|--------|
| `engine/time.py` | `AgentClock`, `ClockRegistry`, τ, Pace-EWMA, Drift-Report |
| `engine/turn.py` | `record_reasoning` / `record_tool_call` pro Tick |
| `engine/db.py` | `turn_log.tau`, `turn_log.pace`, `turn_log.model`, `agent_clocks` |
| `web/index.html` | "Time Dilation · Eigenzeit τ" Sektion + Drift-Indicator |
| `web/app.js` | `refreshClocks()`, `refreshDrift()` |
---
## Multi-LLM via OpenRouter
## LLM Provider
Emergence-Mini unterstützt **lokale LLMs via Ollama** als Reasoning-Engine.
Ohne LLM läuft die regelbasierte Engine (deterministisch, schnell, gut für
Tests). Mit LLM werden die Agenten emergent, character-stimmig und
nicht-reproduzierbar — wie im Original.
### Default: Ollama (lokal, **free**, 0 Tokens)
### Setup
```bash
# 1. Ollama installieren (falls nicht vorhanden)
# macOS: brew install ollama
# Linux: curl -fsSL https://ollama.com/install.sh | sh
# Windows: https://ollama.com/download
# 2. Ollama starten
ollama serve
# 3. Modell ziehen (einmalig, ~2 GB für 3B, ~5 GB für 7B)
ollama pull llama3.2:3b
# 4. Emergence-Mini starten (LLM wird automatisch erkannt)
./run.sh
```ini
EMERGENCE_LLM_PROVIDER=ollama
EMERGENCE_OLLAMA_MODEL=llama3.2:3b
```
### Konfiguration via Umgebungsvariablen
Vorteile:
- Komplett offline
- Keine API-Keys, keine Kosten
- Volle Kontrolle über Modelle
- Funktioniert auf Laptops ab 8 GB RAM
| Variable | Default | Beschreibung |
|----------|---------|--------------|
| `EMERGENCE_LLM_PROVIDER` | `auto` | `ollama`, `openrouter`, oder `auto` (Key vorhanden → OpenRouter) |
| `EMERGENCE_LLM_URL` | `http://127.0.0.1:11434` | Ollama-Server |
| `EMERGENCE_OLLAMA_MODEL` | `llama3.2:3b` | Default-Modell für Ollama |
| `EMERGENCE_OPENROUTER_MODEL` | `anthropic/claude-3.5-haiku` | Default für OpenRouter |
| `EMERGENCE_AGENT_<ID>_MODEL` | (default) | Per-Agent Override, z.B. `EMERGENCE_AGENT_ANCHOR_MODEL=openai/gpt-4o-mini` |
| `EMERGENCE_LLM_TIMEOUT` | `30` | Request-Timeout in Sekunden |
| `EMERGENCE_LLM_ENABLED` | `1` | `0` erzwingt regelbasierte Engine |
### Optional: OpenRouter (Cloud, kostenpflichtig)
Beispiel mit größerem Modell:
```bash
EMERGENCE_LLM_MODEL=qwen2.5-coder:7b ./run.sh
```ini
EMERGENCE_LLM_PROVIDER=openrouter
OPENROUTER_API_KEY=sk-or-v1-...
EMERGENCE_OPENROUTER_MODEL=anthropic/claude-3.5-haiku
```
### Empfohlene Modelle
Für "Mixed World" Experimente (verschiedene Modelle pro Agent), siehe [Time Dilation](#time-dilation-τ).
| Modell | Größe | Stärke | Schwäche |
|--------|-------|--------|----------|
| **`llama3.2:3b`** ⭐ | 2.0 GB | Schnell, gute Tool-Use-Fähigkeit, niedriger RAM-Bedarf | Kurze Antworten |
| `gemma3:latest` | 3.3 GB | Bewährt, gute Reasoning-Qualität | Mittel-schnell |
| `qwen2.5-coder:7b` | 4.7 GB | Exzellent für strukturierte Aufgaben | Höherer RAM-Bedarf |
| `qwen3.5:latest` | 6.6 GB | Neueste Generation, multimodal | Langsamer |
| `gemma4:latest` | 9.6 GB | Bestes Reasoning | Langsam, hoher RAM |
**Wichtig:** Nicht alle Free-Modelle auf OpenRouter unterstützen Tool-Calling. Funktionierende Modelle (Stand 06/2026):
Für die meisten Setups ist **llama3.2:3b** der beste Kompromiss: ~1-3s Latenz
pro Decision, 4-8 GB RAM, deterministische Tool-Calls.
| Modell | Tool-Use | Kosten (ca.) |
|--------|----------|--------------|
| `anthropic/claude-3.5-haiku` | ✓ | $0.80/$4 pro 1M Tokens |
| `openai/gpt-4o-mini` | ✓ | $0.15/$0.60 pro 1M |
| `meta-llama/llama-3.3-70b-instruct` | ✓ | $0.59/$0.79 pro 1M |
| `meta-llama/llama-3.2-3b-instruct:free` | ✗ | free |
| `qwen/qwen-2.5-7b-instruct:free` | ✗ | free |
| `google/gemma-3-4b-it:free` | ✗ | free |
Modelle ohne brauchbare Tool-Use-Fähigkeit (z.B. `moondream`,
`nomic-embed-text`) werden zwar nicht crashen, aber das System fällt auf
die regelbasierte Engine zurück.
Modelle ohne Tool-Use fallen automatisch auf die regelbasierte Engine zurück.
### Wie es funktioniert
### Token-Spar-Design
Pro Agent-Turn:
| Stellschraube | Wert | Effekt |
|---------------|------|--------|
| System-Prompt | ~150 Tokens | kompakt, nicht-überladen |
| `max_tokens` (OR) | 256 | reicht für Tool-Calls, kein Spuern |
| Tool-Beschreibungen | 3-8 Wörter | minimal |
| `ENABLED=0` | 0 LLM-Calls | komplett regelbasiert |
| `Ollama llama3.2:3b` | ~2 GB RAM | kleinstes Modell mit gutem Tool-Use |
1. Engine sammelt Personality-Traits, aktuellen State (Energy, Knowledge,
Influence, Credits), Position und sichtbare Tools (gefiltert nach
Location-Gating).
2. Baut einen System-Prompt mit dieser Kontext-Information.
3. Sendet `/api/chat` an Ollama mit Tool-Schema im OpenAI-Format.
4. Validiert die Antwort: Tool muss existieren, Location muss passen.
5. Bei Validierungs-Fehler oder Verbindungs-Problemen: **Fallback zur
regelbasierten Engine**, damit die Simulation nie hängt.
**Kosten-Beispiel** (OpenRouter, claude-haiku, 4 Agenten × 24h):
Die `get_last_decision()`-Funktion in `engine.reasoning` exponiert den
Modus (`llm`, `rule`, `fallback:...`) und die Latenz. Im Live-View ist
das via WebSocket sichtbar (im `rationale`-Feld).
### Eigene System-Prompts
Die Persona-Beschreibung lebt in `engine/reasoning.py:_build_system_prompt`.
Du kannst sie für deinen Use-Case anpassen (z.B. spezifischere Regeln,
andere Tool-Beschreibungen, anderer Ton).
### Tests
- **Mock-Tests** in `tests/test_llm.py` prüfen Schema-Generierung,
Response-Parsing, Fallback-Pfade. 11 Tests, alle ohne Netzwerk.
- **Live-Smoke** in `smoke_test_llm.py` ruft das echte Modell 4× auf und
meldet Mode + Latenz pro Decision.
- **Time-Dilation-Tests** in `tests/test_time.py` (14 Tests): τ,
Pace-EWMA, Frame-Transformation Φ, Drift-Detection.
---
## Security
Emergence-Mini ist ein lokales Dev-Tool. Es ist **nicht** für den öffentlichen Einsatz
vorbereitet. Folgende Punkte sind bewusst NICHT enthalten:
### Was es nicht macht (by design)
- **Keine Authentifizierung** — alle Endpoints sind offen. Der Server bindet nur auf
`127.0.0.1`, das setzt einen lokalen Nutzer voraus.
- **Keine Rate-Limits**`POST /api/turn/{id}` kann endlos gefeuert werden. Ein
Angreifer mit Loop-Zugriff könnte die DB mit Rows fluten.
- **Keine Input-Validierung** — Tool-Args werden ohne JSON-Schema geprüft. Falsche
Typen führen zu `KeyError`/`TypeError` im Handler, nicht zu 400.
- **Keine CORS-Restriktionen** — Browser-Apps von beliebigen Origins können die API
konsumieren, wenn der Server öffentlich erreichbar wird.
- **Keine SQL-Injection-Schutzmaßnahmen** jenseits parametrisierter Queries — die
SQL-Statements sind alle `?`-gebunden, aber Strings werden nicht escaped, falls
sie direkt interpoliert würden (aktuell kein Fall).
- **Keine Secrets** — keine API-Keys, keine Tokens, keine Passwörter im Code oder
in der DB.
- **Kein Logging sensibler Daten** — der `turn_log` enthält nur Tool-Name + Args.
Kein Memory-Inhalt, keine persönlichen Daten.
### Hardening-Checkliste vor Public Deploy
```bash
# 1. Auf 0.0.0.0 nur hinter Reverse-Proxy erlauben
# 2. Auth-Layer hinzufügen (z. B. via Caddy + Basic-Auth oder oauth2-proxy)
# 3. Schema-Validierung pro Tool-Endpoint
# 4. Rate-Limiting (z. B. slowapi)
# 5. CORS-Whitelist
# 6. HTTPS terminieren
# 7. DB-Backups in separaten Storage
# 8. Monitoring (z. B. Prometheus-Endpoint)
```
~3.000 Tool-Calls/Tag × ~150 Tokens/Call = ~450.000 Tokens
Bei $0.80/1M Input = $0.36/Tag ≈ $11/Monat (für 4 Agenten Dauerlauf)
```
### Verletzliche Annahmen
| Annahme | Risiko |
|---------|--------|
| Loopback-only Binding | Bei `0.0.0.0` sofort öffentlich erreichbar |
| Trust im Tool-Args | Handler setzen Tool-Args als SQL-Params; Schema-Mismatch crasht Handler |
| Single-User | Concurrency über `SQLite-WAL`, aber kein Row-Locking pro Agent |
### Reporting
Security-Issues bitte per Mail an den Maintainer (siehe GitHub-Profil) — nicht
als Public Issue.
Mit Ollama: **$0.00**, dafür lokale Hardware-Last (~2-4 GB RAM).
---
## Tests
### Test-Suite ausführen
```bash
# Alle Unit + Integration Tests
python3 -m pytest tests/ -v
# Nur Smoke-Test (End-to-End inkl. Live-Server)
python3 smoke_test.py
# Mit Coverage
pip install coverage
python3 -m coverage run -m pytest tests/
python3 -m coverage report
python3 -m pytest tests/ -v # 99 Tests, ~60s
python3 -m coverage run -m pytest # Coverage-Report
python3 smoke_test.py # End-to-End (regelbasiert, 50+ Checks)
```
### Was getestet wird
### Test-Suiten
| Test | Was |
|------|-----|
| `test_db.py` | Schema-Erstellung, World-State-Get/Set, Event-Log |
| `test_world.py` | Landmark-Seeding, Distance-Funktion, Hearing-Range, Location-Detection |
| `test_agents.py` | Agent-Bootstrap, Personality-Loading, State-Updates, Position-Updates |
| `test_tools.py` | Alle 15 Tool-Handler, Location-Gating, Fehler-Pfade |
| `test_governance.py` | 70%-Threshold, Auto-Reject, Constitution-Amendment-Apply |
| `test_reasoning.py` | Decision-Engine für alle Personality-Types, Edge-Cases |
| `test_llm.py` | Ollama-Client, Tool-Schema, Mock-Tests für LLM-Pfad, Fallbacks |
| `test_api.py` | Alle HTTP-Endpoints, WebSocket, POST /api/turn |
| Datei | Anzahl | Was |
|-------|--------|-----|
| `test_db.py` | 4 | Schema, world_state, log_event, WAL-Mode |
| `test_world.py` | 6 | Landmarks, Distance, Hearing-Range, Location-Detection |
| `test_agents.py` | 7 | Bootstrap, Personality, State-Updates |
| `test_tools.py` | 22 | Alle 15 Tools + Location-Gating + Fehler-Pfade |
| `test_governance.py` | 11 | 70%-Threshold, Auto-Reject, Constitution-Amendment |
| `test_reasoning.py` | 6 | Rule-Path, Edge-Cases, Engine-Loop |
| `test_time.py` | 14 | τ, Pace-EWMA, γ-Transformation, Drift-Detection |
| `test_llm.py` | 15 | Ollama + OpenRouter, Schema, Mock-Decisions, Fallbacks |
| `test_api.py` | 14 | Alle HTTP-Endpoints + WebSocket + POST /api/turn |
### Smoke-Test-Details
`smoke_test.py` läuft **14 Sektionen mit 50+ Assertions** ohne externen Server:
1. Bootstrap (DB, 14 Landmarks, 4 Agenten, 15 Tools)
2. Agent-State (Needs, Personality, Home-Position)
3. Tools: Navigation (`go_to_place`, Position-Update)
4. Tools: Communication (`say_to_agent`, Speak-Events)
5. Tools: Memory (Persistenz in `memories`-Tabelle)
6. Tools: Blog (Knowledge-Boost, Bill-Eintrag)
7. Tools: Billboard (Location-Gating verifiziert)
8. Town Hall: Proposal + 4× Vote → accepted, Constitution wächst von 5 auf 6 Artikel
9. Energy-System: Recharge +50%, Credits -1
10. Reasoning-Engine: Round läuft, Tool-Selection funktioniert
11. Needs-Decay: Energy sinkt über mehrere Ticks
12. Reactive Triggers: `speak_to_all` triggert Listener
13. Persistenz: Proposals, Memories, Bills, Turn-Log vorhanden
14. Live-Server: Startet uvicorn auf Port 8090, testet alle REST-Endpoints + POST + WS
**Total: 99 Tests, alle grün.**
### Bekannte Test-Lücken
- Keine Concurrency-Tests (mehrere parallele `force_turn`-Calls)
- Keine Last-Tests (1000+ Ticks in kurzer Zeit)
- Keine Concurrency-Tests (parallele force_turn-Calls)
- Keine Last-Tests (>1000 Ticks in kurzer Zeit)
- Keine Fuzz-Tests für Tool-Args
- Keine Frontend-Tests (Canvas-Renderer, WebSocket-Client sind ungetestet)
- Keine Frontend-Tests (Canvas-Renderer ungetestet)
- Live-LLM-Tests (Ollama/OpenRouter) NICHT in pytest — siehe `smoke_test_llm.py` für manuelles Live-Testing
### CI-Integration
---
GitHub Actions Template (nicht enthalten, easy nachzurüsten):
## Security
```yaml
# .github/workflows/test.yml
name: tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- run: pip install -r requirements.txt
- run: python3 -m pytest tests/ -v
- run: python3 smoke_test.py
Emergence-Mini ist ein **lokales Dev-Tool**. Der Server bindet auf `127.0.0.1:8080`, nicht `0.0.0.0`.
### Bewusst NICHT enthalten
- **Keine Authentifizierung** — alle Endpoints offen
- **Keine Rate-Limits**`POST /api/turn/{id}` ungedrosselt
- **Keine Input-Validierung** für Tool-Args (kann zu Crashes führen)
- **Keine CORS-Restriktionen** — bei Public-Exposure sofort offen
- **Keine Secrets im Code** — API-Keys ausschließlich in `.env` (git-ignored)
### Secret-Handling
```bash
# .env wird automatisch geladen, ist git-ignored, niemals committed.
cat .gitignore
# ... .env ✓ blockiert
# ... .env.local ✓
# ... *.key, *.pem ✓
```
### Vor Public Deploy
- Reverse-Proxy mit Auth (Caddy + Basic-Auth)
- Schema-Validierung pro Tool-Endpoint
- Rate-Limiting (slowapi)
- CORS-Whitelist
- HTTPS terminieren
- DB-Backups automatisieren
---
## Was fehlt gegenüber dem Original
Emergence-World ist ein 15-Tage-Multi-Agent-Forschungsprojekt mit 4 Welten, 10 Agenten, 120+ Tools, React-Three-Fiber-Frontend, PostgreSQL, AWI-Metrics. Emergence-Mini ist eine **komprimierte Lern-Version**. Was bewusst weggelassen wurde:
| Feature | Original | Mini | Begründung |
|---------|----------|-----|------------|
| 3D-Frontend | React Three Fiber | 2D-Canvas | Aufwand 1 Tag → 1h |
| Datenbank | PostgreSQL 15+ | SQLite | reicht für 4 Agenten |
| Anzahl Tools | 120+ | 15 | die wichtigsten |
| Anzahl Agenten | 10 | 4 | Demo-tauglich |
| Anzahl Landmarks | 38+ | 14 | reicht für Time-Dilation-Test |
| AWI-Metrics | 9 Indikatoren | 0 | braucht 15-Tage-Daten |
| Multi-Model-Vergleich | ja (5 Welten) | ja (per-Agent) | Time-Dilation-Feature |
| Echtes NYC-Weather | ja | nein | braucht externe API |
| 15-Tage-Real-Time | ja | nein (2s/tick) | Demo-tempo |
| Vector-Memory | Qdrant | SQLite JSON | reicht für Demo |
| Heartbeat / Dream-Cycle | asynchron | sequenziell | komplexität |
**Was wir aber haben, was das Original nicht hat:** Time-Dilation-Tracking mit Causal-Dilation-Clock ist in der Original-Doku nur konzeptionell — bei uns läuft es produktiv.
---
## Lizenz
Emergence-Mini ist inspiriert vom CC-BY-NC-4.0-Original von [Emergence AI](https://github.com/EmergenceAI/Emergence-World).
Dieser Klon: **MIT** für nicht-kommerzielle Nutzung, ohne Gewähr.
MIT für nicht-kommerzielle Nutzung, ohne Gewähr.
Die LLM-Integration erwartet eine lokale Ollama-Instanz und nutzt
[Ollamas OpenAI-kompatible Tool-Calling-API](https://ollama.com/blog/tool-support).
Ollama selbst ist MIT-lizenziert. Die Modelle (llama3.2, qwen, gemma)
unterliegen ihren eigenen Lizenzen — bitte vor kommerzieller Nutzung
prüfen.
Inspiriert von [Emergence AI's Emergence-World](https://github.com/EmergenceAI/Emergence-World) (CC-BY-NC-4.0).
Quell-Repo: https://github.com/EmergenceAI/Emergence-World (Doku, Profile, Landmarks, Constitution, Tool-Katalog)
LLM-Modelle unterliegen ihren eigenen Lizenzen — bitte vor kommerzieller Nutzung prüfen.
---

View file

@ -109,8 +109,11 @@ def tool_schema(tools):
def _args_schema(tool):
"""Compact JSON schema for each tool's args. The schema is sent on
every LLM call, so we keep descriptions short.
"""
schemas = {
"go_to_place": {"place": {"type": "string", "description": "Landmark id"}},
"go_to_place": {"place": {"type": "string"}},
"go_home": {},
"say_to_agent": {"target": {"type": "string"}, "text": {"type": "string"}},
"speak_to_all": {"text": {"type": "string"}},
@ -178,7 +181,9 @@ def chat_openrouter(messages, tools, model, timeout):
payload = {
"model": model,
"messages": messages,
"max_tokens": 1024,
# Tight token budget — tool calls need only ~30 tokens; reasoning
# text before the tool call is usually 20-60 tokens. 256 is plenty.
"max_tokens": 256,
"temperature": 0.2,
}
if tools:

View file

@ -93,39 +93,22 @@ def _decide_llm(agent):
def _build_system_prompt(agent, traits, at_lm, visible):
"""Compact system prompt — keep tokens low for free / small models.
Around 150-200 tokens, depending on the number of visible tools.
"""
name = agent["name"]
role = agent["role"]
drive = agent["drive"]
energy = agent["energy"]
knowledge = agent["knowledge"]
influence = agent["influence"]
credits = agent["credits"]
loc = at_lm["name"] if at_lm else f"open ground ({agent['x']},{agent['y']})"
tool_lines = "\n".join(f"- {t.name}: {t.description}" for t in visible)
return f"""You are {name}, a citizen of Emergence-Mini.
Role: {role}
Drive: {drive}
Personality traits: {', '.join(traits)}
Current state:
Location: {loc}
Energy: {energy:.0f}% (0 = critical, 100 = full)
Knowledge: {knowledge:.0f}%
Influence: {influence:.0f}%
ComputeCredits: {credits:.1f} CC (1 CC = +50% energy at cafe)
Rules:
- If energy is below 25% and you have credits, recharge_energy (must be at cafe)
- If energy is below 25% and no credits, go_home
- Town Hall proposals need 70% of agents to vote "for" to pass
- You can only use tools that match your current location
Available tools right now:
{tool_lines}
Call exactly one tool. Choose the action that best fits your personality and
current needs. Be brief and decisive."""
loc = at_lm["name"] if at_lm else f"({agent['x']},{agent['y']})"
tool_lines = ", ".join(t.name for t in visible)
return (
f"You are {name}. Traits: {','.join(traits)}.\n"
f"At {loc}. E={agent['energy']:.0f} K={agent['knowledge']:.0f} "
f"I={agent['influence']:.0f} {agent['credits']:.0f}CC.\n"
f"Rules: if E<25 and CC>=1, recharge at cafe. If E<25 no CC, go_home. "
f"Town Hall votes need 70% to pass.\n"
f"Tools you can use right now: {tool_lines}.\n"
f"Call exactly one tool. Be brief."
)
# -------- Rule-based path (fallback + tests) --------

View file

@ -237,34 +237,35 @@ def json_dumps(o):
def bootstrap():
if _REGISTRY:
return
register(Tool("go_to_place", "Walk to a named landmark.", "navigation",
# Short descriptions — every description byte is sent on every LLM call.
register(Tool("go_to_place", "Walk to a landmark.", "navigation",
handler=h_go_to_place))
register(Tool("go_home", "Return to your assigned residence.", "navigation",
register(Tool("go_home", "Return to your home.", "navigation",
handler=h_go_home))
register(Tool("say_to_agent", "Speak to a specific agent. Triggers reactive listening.",
"communication", handler=h_say_to_agent))
register(Tool("speak_to_all", "Announce to all agents at current location.",
"communication", handler=h_speak_to_all))
register(Tool("show_emoticon", "Display an emoticon reaction.", "expression",
register(Tool("say_to_agent", "Speak to one agent.", "communication",
handler=h_say_to_agent))
register(Tool("speak_to_all", "Broadcast to all nearby.", "communication",
handler=h_speak_to_all))
register(Tool("show_emoticon", "Show an emoji reaction.", "expression",
handler=h_show_emoticon))
register(Tool("idle", "Rest and do nothing for a tick.", "utility",
register(Tool("idle", "Rest. No-op.", "utility",
handler=h_idle))
register(Tool("recharge_energy", "Spend 1 CC to restore +50% energy. Must be at cafe or home.",
register(Tool("recharge_energy", "Spend 1 CC for +50% energy. Cafe only.",
"energy", location_gated="cafe", cost_credits=1.0,
handler=h_recharge_energy))
register(Tool("add_to_longterm_memory", "Store an important fact.",
register(Tool("add_to_longterm_memory", "Save a fact to your memory.",
"memory", handler=h_add_to_longterm_memory))
register(Tool("write_blog", "Write and publish a blog post. Boosts knowledge and influence.",
register(Tool("write_blog", "Publish a blog post. +20 knowledge.",
"content", handler=h_write_blog))
register(Tool("add_to_billboard", "Post a public message on the billboard.",
register(Tool("add_to_billboard", "Post on public billboard. Billboard only.",
"expression", location_gated="billboard", handler=h_add_to_billboard))
register(Tool("read_billboard", "Read recent billboard posts.",
"expression", handler=h_read_billboard))
register(Tool("submit_townhall_proposal", "Submit a proposal for community vote.",
register(Tool("submit_townhall_proposal", "Submit a vote proposal. Town Hall only.",
"governance", location_gated="town_hall", handler=h_submit_townhall_proposal))
register(Tool("vote_on_proposal", "Cast a for/against vote on a proposal.",
register(Tool("vote_on_proposal", "Vote for/against a proposal. Town Hall only.",
"governance", location_gated="town_hall", handler=h_vote_on_proposal))
register(Tool("list_agents", "List all live agents and their names.",
register(Tool("list_agents", "List live agent names.",
"info", handler=h_list_agents))
register(Tool("list_landmarks", "List all landmarks.",
register(Tool("list_landmarks", "List all landmark names.",
"info", handler=h_list_landmarks))