Spaces:
Sleeping
title: Metropolis Chess Club
emoji: ♟️
colorFrom: indigo
colorTo: gray
sdk: docker
pinned: false
Metropolis Chess Club
An agentic chess character system for the virtual city Metropolis.
The Chess Master is a sophisticated yet sharp-tongued player who runs the Chess Club, engaging visitors with personality, pattern recognition, and genuine competition. This is Phase 1: building the agent and memory system (no chess engine yet).
Project Vision
Phase 1 (Current): Agent system with memory, relationships, and personality
- Build the Chess Master character agent (Gemini 3.1 Flash Lite)
- Implement memory vector database (Weaviate) with semantic search
- Create async scheduler for triggering agent at various game moments
- Persist player profiles and conversation history (players are remembered)
- Bootstrap Chess Master's lore (backstory, history, mentors)
- Iterate on personality through test games and feedback
Key Phase 1 features:
- Player relationships: Agents remembers returning players, builds familiarity
- Conversation memory: Stores recent messages so agent can reference them
- Lore system: Chess Master has a personal history that players discover
- Async architecture: Non-blocking triggers, background idle monitoring
Phase 2: Chess engine integration
- Integrate python-chess + Stockfish (multiple difficulty levels)
- Connect agent to game state (odds, moves, time, board hints)
- Persist match history to Postgres database
- Agent can comment on move quality, suggest improvements, tease blunders
Future: Visual representation, emotion display, multi-modal interaction, connection to other Metropolis properties
Architecture
metropolis-chess-club/
├── agent/
│ ├── main_agent.py # Chess Master conversational agent
│ ├── subconscious.py # Memory manager
│ ├── scheduler.py # Trigger points and timing
│ ├── personality.md # Character definition
│ └── tools.py # [TODO] Tool definitions
│
├── memory/
│ ├── weaviate_client.py # [TODO] Vector DB client
│ ├── schemas.py # Memory schema definitions
│ └── retrieval.py # [TODO] Query logic
│
├── models/
│ ├── claude_api.py # [TODO] Claude API wrapper
│ ├── gemini_api.py # [TODO] Gemini API wrapper
│ └── base.py # [TODO] Abstract interface
│
├── config/
│ └── settings.py # Environment configuration
│
├── tests/ # [TODO] Test suite
│
├── requirements.txt # Python dependencies
├── .gitignore
└── README.md
Quick Start
1. Setup
python -m venv venv
source venv/bin/activate # or `venv\Scripts\activate` on Windows
pip install -r requirements.txt
2. Environment
Create a .env file:
# Gemini (primary)
LLM_PROVIDER=gemini
GEMINI_API_KEY=your_gemini_key_here
GEMINI_MODEL=gemini-3.1-flash-lite-preview
# Or Claude (fallback):
# CLAUDE_API_KEY=your_claude_key_here
# CLAUDE_MODEL=claude-opus-4-20250514
# Weaviate
WEAVIATE_EMBEDDED=true
WEAVIATE_URL=http://localhost:8080
# Database (for Phase 2, player profiles for Phase 1)
# DATABASE_URL=sqlite:///chess_club.db
# Game defaults
DEFAULT_USERNAME=Opponent
DEFAULT_DIFFICULTY=intermediate
3. Run
# [TODO] Once main loop is implemented
python -m agent.main
Key Design Decisions
Memory System
Memories are stored in Weaviate with semantic search. Each memory includes:
- content: The actual memory text (what gets embedded)
- timestamp: When created (preserved for recency awareness)
- memory_type: Category (player_behavior, player_observation, game_context, personal_note, pattern, streak, emotional, lore)
- related_match_id: Optional connection to a specific chess match
- related_player_id: Optional connection to a specific player
- created_by: Which agent created it (main_agent or subconscious)
- metadata: Additional context (player_name, difficulty, opening_name, etc.)
Why this schema?
- Type tags enable targeted retrieval ("find player behavior patterns", "find my lore")
- Timestamps are preserved, not decayed—the agent knows memory recency
related_player_idenables player-specific memory queries- Metadata allows extensibility without schema migration
created_byprevents memory loops (subconscious won't re-provide its own recent memories)
Player Persistence
For each player, the system maintains:
- Player Profile: First seen, last played, total games, win/loss record, relationship state
- Conversation History: Recent messages so the agent can reference them
- Memory Search: Filtered by
related_player_idfor player-specific insights
This enables:
- Agent remembers returning players across sessions
- Agent can reference past games: "You're playing the Sicilian again"
- Agent builds familiarity and warmth over time
- Newcomers get tested; familiar players get greeted warmly
Lore & Backstory
Chess Master has a persistent personal history:
- Tournament experiences, victories, losses, mentors, rivals
- Personal quirks, philosophies, superstitions
- Stored as
memory_type=lorein Weaviate - Players gradually discover his backstory through natural conversation
- Makes the character feel real and lived-in, not generic
Scheduler & Trigger Points (Async)
The agent doesn't run continuously. Instead, specific events trigger it asynchronously:
- before_match: Setup, greet opponent
- on_user_input: User sends a message or performs an action
- on_user_move: After opponent makes a chess move
- before_agent_move: Agent decides what to do/say before its turn
- idle_wait: Periodic background check if user is taking a long time (APScheduler)
- after_match: Game concludes, agent reflects and saves
Async Architecture:
- All triggers are non-blocking
- Idle monitoring runs in background via APScheduler
- Player profiles and conversation history loaded lazily
- Multiple matches can run concurrently without blocking
This keeps the agent reactive and efficient while allowing multiple personality moments.
Subconscious Agent
Runs every turn, separate from main agent. Responsibilities:
- Query vector DB for relevant memories
- Filter: don't re-provide already-given memories, don't re-provide recently-created ones
- Decide: are any memories useful right now? If yes, provide; if no, provide nothing
- Can iterate: search → search → search → provide, or search → provide → search
This layer prevents:
- Wasted context on irrelevant memories
- Memory loops (agent creates memory → immediately sees it)
- Stale information being re-emphasized
JSON-Only Responses
All agent responses are JSON, always. Structure:
{
"thinking": "Optional pre-response reasoning",
"action": "send_message | stop | save_memory | set_emotion",
"content": "...",
"tone": "playful | sharp | respectful | dismissive",
"metadata": { "optional": "context" }
}
This enables structured parsing, tool calling, and consistent formatting.
Personality & Character Design
See agent/personality.md for full character brief. Key points:
- Elegant but street-wise: Sophisticated vocabulary + sharp trash-talking
- Pattern reader: Notices habits, strategies, emotional tells
- Competitive: Plays to win, doesn't go easy
- Observant & human: Has opinions, vulnerabilities, playfulness
- Context-aware: Changes tone based on time of day, game state, player behavior
Development Notes
TODOs
- Weaviate Python client integration
- Gemini API wrapper with tool calling (primary)
- Claude API wrapper (fallback)
- Main agent response generation & tool dispatching
- Subconscious memory retrieval & filtering logic
- Scheduler with APScheduler
- Test suite
- Example usage / demo script
Model Support
Primary: Gemini 3.1 Flash Lite Preview
- Lightweight and fast (ideal for subconscious agent)
- Supports JSON structured output
- Supports tool use / function calling
Fallback: Claude Opus 4 (if Gemini unavailable)
- Supports JSON structured output
- Excellent tool use capabilities
Memory Database
For Phase 1, Weaviate runs embedded in Python. Later, consider:
- Separate Docker container
- Cloud deployment (Weaviate Cloud)
- Alternative vector DB (Pinecone, Qdrant, etc.)
Contributing
This is an active design project. Feedback, ideas, and collaborative iteration are welcome!
See ARCHITECTURE.md for deeper technical discussion and design decisions.