PyCatan-AI / .github /instructions /AI_REFACTOR_PLAN.md
EZTIME2025
1
e36cfa2
# ๐Ÿ”„ AI System Refactoring Plan
## ืชื•ื›ื ื™ืช ืจื™ืคืงื˜ื•ืจ ืžืขืจื›ืช ื”-AI
**ืชืืจื™ืš:** January 2026
**ืกื˜ื˜ื•ืก:** ๐Ÿ“‹ Planning
**ื’ืจืกื”:** 1.0
---
## ๐Ÿ“‹ ืชื•ื›ืŸ ืขื ื™ื™ื ื™ื
1. [ืกืงื™ืจืช ื”ืžืฆื‘ ื”ื ื•ื›ื—ื™](#-ืกืงื™ืจืช-ื”ืžืฆื‘-ื”ื ื•ื›ื—ื™)
2. [ื”ื‘ืขื™ื•ืช ืฉืฆืจื™ืš ืœืคืชื•ืจ](#-ื”ื‘ืขื™ื•ืช-ืฉืฆืจื™ืš-ืœืคืชื•ืจ)
3. [ื”ืืจื›ื™ื˜ืงื˜ื•ืจื” ื”ื—ื“ืฉื”](#-ื”ืืจื›ื™ื˜ืงื˜ื•ืจื”-ื”ื—ื“ืฉื”)
4. [ืคื™ืจื•ื˜ ื”ืจื›ื™ื‘ื™ื](#-ืคื™ืจื•ื˜-ื”ืจื›ื™ื‘ื™ื)
5. [ืชื•ื›ื ื™ืช ืžื™ืžื•ืฉ](#-ืชื•ื›ื ื™ืช-ืžื™ืžื•ืฉ)
6. [ืžื™ื’ืจืฆื™ื” ื•ื”ื’ื™ืจื”](#-ืžื™ื’ืจืฆื™ื”-ื•ื”ื’ื™ืจื”)
7. [ื‘ื“ื™ืงื•ืช](#-ื‘ื“ื™ืงื•ืช)
---
## ๐Ÿ”ด ืกืงื™ืจืช ื”ืžืฆื‘ ื”ื ื•ื›ื—ื™
### ืžื‘ื ื” ืงื™ื™ื
```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ื”ืžืฆื‘ ื”ื ื•ื›ื—ื™ (ื‘ืœืื’ืŸ) โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ โ”‚
โ”‚ GameManager โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ saves state to file โ”‚
โ”‚ current_state.json โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ watches file changes โ”‚
โ”‚ play_with_prompts.py (background thread) โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ calls โ”‚
โ”‚ generate_prompts_from_state.py โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ”€โ–บ generate_what_happened_message() (guesses from state!) โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ saves โ”‚
โ”‚ prompt_N.json files โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ watches files โ”‚
โ”‚ test_ai_live.py โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ sends to โ”‚
โ”‚ LLM (Gemini) โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ ??? โ”‚
โ”‚ How do responses get back to game? โ† ๐Ÿšจ BROKEN โ”‚
โ”‚ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```
### ืงื‘ืฆื™ื ืงื™ื™ืžื™ื
| ืงื•ื‘ืฅ | ืžื™ืงื•ื | ืชืคืงื™ื“ |
|------|-------|-------|
| `prompt_manager.py` | `pycatan/ai/` | ื™ืฆื™ืจืช ืคืจื•ืžืคื˜ื™ื |
| `llm_client.py` | `pycatan/ai/` | ืชืงืฉื•ืจืช ืขื Gemini |
| `state_filter.py` | `pycatan/ai/` | ืกื™ื ื•ืŸ state ืœืฉื—ืงืŸ |
| `prompt_templates.py` | `pycatan/ai/` | ืชื‘ื ื™ื•ืช ื•-schemas |
| `config.py` | `pycatan/ai/` | ืงื•ื ืคื™ื’ื•ืจืฆื™ื” |
| `generate_prompts_from_state.py` | `examples/ai_testing/` | ื™ืฆื™ืจืช ืคืจื•ืžืคื˜ื™ื ืžืงื•ื‘ืฅ |
| `test_ai_live.py` | `examples/ai_testing/` | ืฉืœื™ื—ื” ืœ-LLM |
| `play_with_prompts.py` | `examples/ai_testing/` | ื”ืจืฆืช ืžืฉื—ืง ืขื AI |
| `web_viewer.py` | `examples/ai_testing/` | ืฆืคื™ื™ื” ื‘ืคืจื•ืžืคื˜ื™ื |
| `user.py` | `pycatan/players/` | ืžืžืฉืง User ืžื•ืคืฉื˜ |
| `human_user.py` | `pycatan/players/` | ืžื™ืžื•ืฉ ืœืฉื—ืงืŸ ืื ื•ืฉื™ |
---
## ๐Ÿšจ ื”ื‘ืขื™ื•ืช ืฉืฆืจื™ืš ืœืคืชื•ืจ
### ื‘ืขื™ื” 1: ื”ืคืจื“ืช ืื—ืจื™ื•ืช ื—ืกืจื”
```
โŒ GameManager ืœื ืฆืจื™ืš ืœื“ืขืช ืขืœ AI/ืคืจื•ืžืคื˜ื™ื
โŒ ื™ืฆื™ืจืช ืคืจื•ืžืคื˜ื™ื ืžืคื•ื–ืจืช ื‘ืžืกืคืจ ืžืงื•ืžื•ืช
โŒ ืื™ืŸ ืžืงื•ื ืžืจื›ื–ื™ ืœื ื™ื”ื•ืœ ืกื•ื›ื ื™ AI
```
### ื‘ืขื™ื” 2: "ืžื” ืงืจื”" ืžื‘ื•ืกืก ืขืœ ื ื™ื—ื•ืฉื™ื
```
โŒ generate_what_happened_message() ืžื ืกื” ืœืฉื—ื–ืจ ืžื” ืงืจื” ืžืชื•ืš state
โŒ ืœื ืžื“ื•ื™ืง - ื—ืกืจ ืžื™ื“ืข ืขืœ ืžื” ื‘ืืžืช ื”ืชืจื—ืฉ
โŒ GameManager ื™ื•ื“ืข ื‘ื“ื™ื•ืง ืžื” ืงืจื” ืื‘ืœ ื”ืžื™ื“ืข ืœื ืžื•ืขื‘ืจ
```
### ื‘ืขื™ื” 3: ืื™ืŸ ืฉืœื™ื˜ื” ืขืœ ืžืชื™ ืœืฉืœื•ื— ืคืจื•ืžืคื˜ื™ื
```
โŒ ืคืจื•ืžืคื˜ื™ื ื ืฉืœื—ื™ื ืขืœ ื›ืœ ืฉื™ื ื•ื™ ืงื•ื‘ืฅ
โŒ ืื™ืŸ ื‘ื“ื™ืงื” ืื ื›ื‘ืจ ืžืžืชื™ื ื™ื ืœืชืฉื•ื‘ื” (pending)
โŒ ืฆ'ืื˜ ืœื ืžืขื•ืจืจ ืฉืœื™ื—ืช ืคืจื•ืžืคื˜ื™ื
```
### ื‘ืขื™ื” 4: ืชืฉื•ื‘ื•ืช ืœื ื—ื•ื–ืจื•ืช ืœืžืฉื—ืง
```
โŒ ืื™ืŸ ืžืกืœื•ืœ ื‘ืจื•ืจ ืœืชืฉื•ื‘ื•ืช ืœื—ื–ื•ืจ ืœ-GameManager
โŒ ื”ื›ืœ ืขื•ื‘ื“ ื“ืจืš ืงื‘ืฆื™ื - ืœื real-time
```
### ื‘ืขื™ื” 5: ื–ื™ื›ืจื•ืŸ ืžืคื•ื–ืจ
```
โŒ note_to_self ื ืฉืžืจ ื‘ืงื‘ืฆื™ื, ืœื ื‘ืžืงื•ื ืžืจื›ื–ื™
โŒ ืื™ืจื•ืขื™ื (events) ืœื ื ืฉืžืจื™ื ืœื›ืœ ืกื•ื›ืŸ
โŒ ืกื™ื›ื•ืžื™ ืฆ'ืื˜ ืœื ืงื™ื™ืžื™ื
```
---
## ๐ŸŸข ื”ืืจื›ื™ื˜ืงื˜ื•ืจื” ื”ื—ื“ืฉื”
### ืชืจืฉื™ื ืžื‘ื ื”
```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ืืจื›ื™ื˜ืงื˜ื•ืจื” ื—ื“ืฉื” โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ GameManager โ”‚โ—„โ”€โ”€โ”€โ”€ Actions โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ AIManager โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ€ข Game Loop โ”‚ โ”‚ โ€ข Creates Prompts โ”‚ โ”‚
โ”‚ โ”‚ โ€ข Rules โ”‚โ”€โ”€โ”€โ”€ Events โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ โ€ข Manages Agents โ”‚ โ”‚
โ”‚ โ”‚ โ€ข State โ”‚ (notify_all) โ”‚ โ€ข Handles Chat โ”‚ โ”‚
โ”‚ โ”‚ โ€ข Turn Flow โ”‚ โ”‚ โ€ข Tracks Pending โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚ โ€ข Sends to LLM โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ get_input() โ”‚ โ”‚
โ”‚ โ–ผ โ–ผ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ HumanUser โ”‚ โ”‚ AIUser (Wrapper) โ”‚ โ”‚
โ”‚ โ”‚ (CLI) โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚ โ€ข Delegates to โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚ AIManager โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ AILogger โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ€ข MD logs โ”‚ โ”‚
โ”‚ โ”‚ โ€ข JSON files โ”‚ โ”‚
โ”‚ โ”‚ โ€ข Web Viewer โ”‚ โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```
### ืขืงืจื•ื ื•ืช ืžื ื—ื™ื
| ืขื™ืงืจื•ืŸ | ืคื™ืจื•ื˜ |
|--------|-------|
| **ื”ืคืจื“ืช ืื—ืจื™ื•ืช** | GameManager ืœื ื™ื•ื“ืข ืขืœ AI, AIManager ืœื ื™ื•ื“ืข ืขืœ ื—ื•ืงื™ ื”ืžืฉื—ืง |
| **ืžืงื•ื ืื—ื“ ืœืคืจื•ืžืคื˜ื™ื** | ื›ืœ ื™ืฆื™ืจืช ื”ืคืจื•ืžืคื˜ื™ื ื“ืจืš AIManager |
| **ืฉืœื™ื˜ื” ืžืจื›ื–ื™ืช** | `should_send_prompt()` ืžื—ืœื™ื˜ ืžืชื™ ืœืฉืœื•ื— |
| **"ืžื” ืงืจื”" ืžื“ื•ื™ืง** | Events ืžื’ื™ืขื™ื ื™ืฉื™ืจื•ืช ืž-GameManager |
| **ืœื•ื’ื™ื ืชื•ืืžื™ื** | AILogger ืฉื•ืžืจ ืขืœ ืื•ืชื• ืคื•ืจืžื˜ ืงื‘ืฆื™ื |
---
## ๐Ÿ“ฆ ืคื™ืจื•ื˜ ื”ืจื›ื™ื‘ื™ื
### 1. AgentState - ืžืฆื‘ ืกื•ื›ืŸ ื™ื—ื™ื“
```python
@dataclass
class AgentState:
"""
ืžืฆื‘ ืฉืœ ืกื•ื›ืŸ AI ื™ื—ื™ื“.
ื›ืœ ื”ื–ื™ื›ืจื•ืŸ ื•ื”ืžืฆื‘ ืฉืœ ืกื•ื›ืŸ ืกืคืฆื™ืคื™ ืžื ื•ื”ืœ ื›ืืŸ.
"""
# === ื–ื™ื”ื•ื™ ===
player_name: str # ืฉื ื”ืฉื—ืงืŸ ("dudu", "shon")
player_id: int # ืžืกืคืจ ืฉื—ืงืŸ (0, 1, 2...)
player_color: str # ืฆื‘ืข ("Red", "Blue")
# === ืกื˜ื˜ื•ืก ื‘ืงืฉื” ===
pending_request: bool = False # ื”ืื ืžืžืชื™ืŸ ืœืชืฉื•ื‘ื” ืž-LLM?
last_request_time: Optional[float] = None
# === ื–ื™ื›ืจื•ืŸ ืคืจื˜ื™ ===
memory: Optional[str] = None # note_to_self ืžืชืฉื•ื‘ื” ืื—ืจื•ื ื”
# === ืกื™ื›ื•ืžื™ ืฆ'ืื˜ (ืœืขืชื™ื“) ===
chat_summaries: List[str] = field(default_factory=list)
# ื“ื•ื’ืžื”: ["Turn 5: Dana agreed to trade wood for brick"]
# === ืื™ืจื•ืขื™ื ืฉืงืจื• ืžืื– ื”ืคืจื•ืžืคื˜ ื”ืื—ืจื•ืŸ ===
recent_events: List[Dict[str, Any]] = field(default_factory=list)
# ื“ื•ื’ืžื”: [{"type": "dice_roll", "message": "Rolled 6", "timestamp": 123}]
# === ืžืขืงื‘ ืฉื™ื ื•ื™ื™ื ===
last_state_hash: Optional[str] = None
last_prompt_time: Optional[float] = None
# === ืกื˜ื˜ื™ืกื˜ื™ืงื•ืช ===
total_requests: int = 0
total_tokens_used: int = 0
```
**ืžื™ืงื•ื:** `pycatan/ai/agent_state.py`
---
### 2. AIManager - ื”ืžื ื”ืœ ื”ืžืจื›ื–ื™
```python
class AIManager:
"""
ืžื ื”ืœ ืžืจื›ื–ื™ ืœื›ืœ ืกื•ื›ื ื™ ื”-AI.
ืื—ืจื™ื•ืช:
- ื ื™ื”ื•ืœ ืžืฆื‘ ื›ืœ ื”ืกื•ื›ื ื™ื
- ื™ืฆื™ืจืช ื•ืฉืœื™ื—ืช ืคืจื•ืžืคื˜ื™ื
- ืงื‘ืœืช ืชืฉื•ื‘ื•ืช ื•ื”ืžืจื” ืœืคืขื•ืœื•ืช
- ื ื™ื”ื•ืœ ืฆ'ืื˜ ื•ื”ื™ืกื˜ื•ืจื™ื”
- ื”ื—ืœื˜ื” ืžืชื™ ืœืฉืœื•ื— ืคืจื•ืžืคื˜ื™ื
"""
def __init__(
self,
game_manager: GameManager,
config: AIConfig = None,
session_dir: Path = None
):
"""
Args:
game_manager: ื”ืคื ื™ื” ืœ-GameManager ืœืงืจื™ืืช state
config: ืงื•ื ืคื™ื’ื•ืจืฆื™ื” (ื‘ืจื™ืจืช ืžื—ื“ืœ ืื None)
session_dir: ืชื™ืงื™ื™ืช session ืœืœื•ื’ื™ื
"""
self.game_manager = game_manager
self.config = config or AIConfig()
# ืจื›ื™ื‘ื™ื ืคื ื™ืžื™ื™ื
self.prompt_manager = PromptManager(self.config)
self.llm_client = self._create_llm_client()
self.logger = AILogger(session_dir)
# ืžืฆื‘
self.agents: Dict[str, AgentState] = {}
self.chat_history: List[Dict] = []
self.max_chat_history: int = 20
# === ื ื™ื”ื•ืœ ืกื•ื›ื ื™ื ===
def register_agent(self, player_name: str, player_id: int, player_color: str = ""):
"""ืจื•ืฉื ืกื•ื›ืŸ AI ื—ื“ืฉ"""
def unregister_agent(self, player_name: str):
"""ืžืกื™ืจ ืกื•ื›ืŸ (ืื ืฉื—ืงืŸ ืขื–ื‘)"""
# === ื ืงื•ื“ืช ื”ื›ื ื™ืกื” ื”ืจืืฉื™ืช ===
def process_agent_turn(self, player_name: str) -> Action:
"""
ืžืขื‘ื“ ืชื•ืจ ืฉืœ ืกื•ื›ืŸ AI.
ื–ื• ื”ืคื•ื ืงืฆื™ื” ืฉื ืงืจืืช ืž-AIUser.get_input()
Returns:
Action: ื”ืคืขื•ืœื” ืฉื”ืกื•ื›ืŸ ื‘ื—ืจ ืœื‘ืฆืข
"""
# === ืงื‘ืœืช ืื™ืจื•ืขื™ื ===
def on_game_event(self, event_type: str, message: str, affected_players: List[int] = None):
"""
ื ืงืจื ื›ืฉืžืชืจื—ืฉ ืื™ืจื•ืข ื‘ืžืฉื—ืง.
ื ืงืจื ืž-AIUser.notify_game_event() ืขื‘ื•ืจ ื›ืœ ืกื•ื›ืŸ.
"""
def on_chat_message(self, from_player: str, message: str):
"""
ื ืงืจื ื›ืฉืกื•ื›ืŸ ืฉื•ืœื— ื”ื•ื“ืขืช ืฆ'ืื˜.
ืžื•ืกื™ืฃ ืœื”ื™ืกื˜ื•ืจื™ื” ื•ืฉื•ืœื— ืคืจื•ืžืคื˜ื™ื ืœืกื•ื›ื ื™ื ืื—ืจื™ื.
"""
# === ืœื•ื’ื™ืงืช ืฉืœื™ื—ื” ===
def should_send_prompt(self, player_name: str) -> bool:
"""
ืžื—ืœื™ื˜ ืื ืœืฉืœื•ื— ืคืจื•ืžืคื˜ ืœืกื•ื›ืŸ.
ื›ืœืœื™ื:
1. ืื™ืŸ ื‘ืงืฉื” ืชืœื•ื™ื” (pending_request == False)
2. ื•ื’ื ืื—ื“ ืžืืœื”:
- ืžืฆื‘ ื”ืžืฉื—ืง ื”ืฉืชื ื” (state_hash ืฉื•ื ื”)
- ื”ื’ื™ืขื” ื”ื•ื“ืขืช ืฆ'ืื˜ ื—ื“ืฉื”
"""
# === ืคื•ื ืงืฆื™ื•ืช ืคื ื™ืžื™ื•ืช ===
def _create_prompt(self, agent: AgentState, is_active_turn: bool) -> Dict:
"""ื™ื•ืฆืจ ืคืจื•ืžืคื˜ ืžืœื ืœืกื•ื›ืŸ"""
def _build_what_happened(self, agent: AgentState) -> str:
"""ื‘ื•ื ื” ืชื™ืื•ืจ 'ืžื” ืงืจื”' ืžื”ืื™ืจื•ืขื™ื ื”ืื—ืจื•ื ื™ื"""
def _get_optimized_state(self) -> Dict:
"""ืžื—ื–ื™ืจ state ืžืžื•ื˜ื‘ ืœืฉืœื™ื—ื” ืœ-LLM"""
def _hash_state(self) -> str:
"""ื™ื•ืฆืจ hash ืฉืœ ื”-state ืœื–ื™ื”ื•ื™ ืฉื™ื ื•ื™ื™ื"""
def _convert_to_action(self, parsed_response: Dict, player_id: int) -> Action:
"""ืžืžื™ืจ ืชืฉื•ื‘ืช LLM ืœืื•ื‘ื™ื™ืงื˜ Action"""
def _broadcast_chat(self, from_player: str, message: str):
"""ืžืฉื“ืจ ื”ื•ื“ืขืช ืฆ'ืื˜ ืœื›ืœ ื”ืกื•ื›ื ื™ื"""
```
**ืžื™ืงื•ื:** `pycatan/ai/ai_manager.py`
---
### 3. AIUser - Wrapper ืœืžืžืฉืง User
```python
class AIUser(User):
"""
Wrapper ืฉืžื—ื‘ืจ ื‘ื™ืŸ GameManager ืœ-AIManager.
GameManager ืจื•ืื” ืืช ื–ื” ื›-User ืจื’ื™ืœ ื•ืžืชืงืฉืจ ืื™ืชื•
ื“ืจืš get_input() ื•-notify_game_event().
"""
def __init__(self, name: str, user_id: int, ai_manager: AIManager, color: str = ""):
"""
Args:
name: ืฉื ื”ืฉื—ืงืŸ
user_id: ืžืกืคืจ ืฉื—ืงืŸ (0-based)
ai_manager: ื”ืคื ื™ื” ืœ-AIManager
color: ืฆื‘ืข ื”ืฉื—ืงืŸ
"""
super().__init__(name, user_id)
self.ai_manager = ai_manager
self.color = color
# ืจื•ืฉื ืืช ืขืฆืžื• ื‘-AIManager
ai_manager.register_agent(name, user_id, color)
def get_input(
self,
game_state: GameState,
prompt_message: str,
allowed_actions: Optional[List[str]] = None
) -> Action:
"""
GameManager ืงื•ืจื ืœื–ื” ื›ืฉืฆืจื™ืš ืคืขื•ืœื”.
ืžืขื‘ื™ืจ ืืช ื”ื‘ืงืฉื” ืœ-AIManager ืœื˜ื™ืคื•ืœ.
"""
return self.ai_manager.process_agent_turn(self.name)
def notify_game_event(
self,
event_type: str,
message: str,
affected_players: Optional[List[int]] = None
):
"""
GameManager ืงื•ืจื ืœื–ื” ื›ืฉืžืชืจื—ืฉ ืื™ืจื•ืข.
ืžืขื‘ื™ืจ ืœ-AIManager ืœืฉืžื™ืจื”.
"""
self.ai_manager.on_game_event(event_type, message, affected_players)
def notify_action(self, action: Action, success: bool, message: str = ""):
"""
GameManager ืงื•ืจื ืœื–ื” ืื—ืจื™ ื‘ื™ืฆื•ืข ืคืขื•ืœื”.
"""
# ืืคืฉืจ ืœื”ื•ืกื™ืฃ ืœื•ื’ื™ืงื” ืื ืฆืจื™ืš
pass
```
**ืžื™ืงื•ื:** `pycatan/ai/ai_user.py`
---
### 4. AILogger - ื ื™ื”ื•ืœ ืœื•ื’ื™ื
```python
class AILogger:
"""
ืžื ื”ืœ ืœื•ื’ื™ื ืขื‘ื•ืจ ืžืขืจื›ืช ื”-AI.
ืฉื•ืžืจ ืขืœ ืชืื™ืžื•ืช ืœืคื•ืจืžื˜ ื”ืงื™ื™ื:
- player_X.md (ืœื•ื’ื™ื ืงืจื™ืื™ื)
- prompt_N.json (ืคืจื•ืžืคื˜ื™ื)
- response_N.json (ืชืฉื•ื‘ื•ืช)
ื’ื ืชื•ืžืš ื‘-Web Viewer (ืœืขืชื™ื“).
"""
def __init__(self, session_dir: Path = None):
"""
Args:
session_dir: ืชื™ืงื™ื™ืช session. ืื None, ื™ื•ืฆืจ ืื•ื˜ื•ืžื˜ื™ืช.
"""
if session_dir is None:
session_dir = self._create_session_dir()
self.session_dir = session_dir
self.session_dir.mkdir(parents=True, exist_ok=True)
self.request_counters: Dict[str, int] = {}
self.start_time = datetime.now()
# ื™ืฆื™ืจืช header ืœืœื•ื’ื™ื
self._init_session()
# === Logging ===
def log_prompt(
self,
player_name: str,
prompt: Dict,
schema: Dict,
is_active: bool = True
) -> Path:
"""
ืฉื•ืžืจ ืคืจื•ืžืคื˜ ืœืงื•ื‘ืฅ.
ื™ื•ืฆืจ:
- session/player_name/prompts/prompt_N.json
- session/player_name/prompts/prompt_N.txt
Returns:
Path: ื ืชื™ื‘ ืœืงื•ื‘ืฅ JSON
"""
def log_response(
self,
player_name: str,
response: LLMResponse,
parsed: Dict
):
"""
ืฉื•ืžืจ ืชืฉื•ื‘ื” ื•ืžืขื“ื›ืŸ MD log.
ื™ื•ืฆืจ:
- session/player_name/responses/response_N.json
- ืžืขื“ื›ืŸ session/player_name.md
"""
def log_chat(self, from_player: str, message: str):
"""ืฉื•ืžืจ ื”ื•ื“ืขืช ืฆ'ืื˜ ืœืœื•ื’"""
def log_error(self, player_name: str, error: str):
"""ืฉื•ืžืจ ืฉื’ื™ืื” ืœืœื•ื’"""
# === MD Generation ===
def _init_player_md(self, player_name: str, model: str):
"""ื™ื•ืฆืจ header ืœืงื•ื‘ืฅ MD ืฉืœ ืฉื—ืงืŸ"""
def _append_request_to_md(
self,
player_name: str,
num: int,
prompt: Dict
):
"""ืžื•ืกื™ืฃ request section ืœ-MD"""
def _append_response_to_md(
self,
player_name: str,
num: int,
response: LLMResponse,
parsed: Dict
):
"""ืžื•ืกื™ืฃ response section ืœ-MD"""
# === Utilities ===
def _create_session_dir(self) -> Path:
"""ื™ื•ืฆืจ ืชื™ืงื™ื™ืช session ืขื timestamp"""
def get_session_path(self) -> Path:
"""ืžื—ื–ื™ืจ ื ืชื™ื‘ ื”-session"""
def save_agent_memories(self, agents: Dict[str, AgentState]):
"""ืฉื•ืžืจ ืืช ื”ื–ื™ื›ืจื•ื ื•ืช ืฉืœ ื›ืœ ื”ืกื•ื›ื ื™ื ืœืงื•ื‘ืฅ"""
def save_chat_history(self, chat_history: List[Dict]):
"""ืฉื•ืžืจ ื”ื™ืกื˜ื•ืจื™ื™ืช ืฆ'ืื˜ ืœืงื•ื‘ืฅ"""
```
**ืžื™ืงื•ื:** `pycatan/ai/ai_logger.py`
---
## ๐Ÿ”„ ื–ืจื™ืžื•ืช ืขื‘ื•ื“ื”
### ื–ืจื™ืžื” 1: ืชื•ืจ ืฉืœ ืกื•ื›ืŸ AI
```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ AGENT TURN FLOW โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ โ”‚
โ”‚ 1. GameManager._process_user_action() โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ calls โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 2. AIUser.get_input(game_state, prompt_message, allowed_actions) โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ delegates to โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 3. AIManager.process_agent_turn(player_name) โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Get agent state โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Build "what_happened" from recent_events โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Create prompt via PromptManager โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ AILogger.log_prompt() โ†’ saves files โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Mark agent.pending_request = True โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ LLMClient.generate(prompt) โ”€โ”€โ”€โ”€โ”€โ”€โ–บ ๐Ÿค– Gemini โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚โ—„โ”€โ”€โ”€ Response โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Mark agent.pending_request = False โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Parse response โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ AILogger.log_response() โ†’ saves files + MD โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Update agent.memory (note_to_self) โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Clear agent.recent_events โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Handle chat_message if present โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 4. Return Action โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 5. GameManager.execute_action() โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 6. GameManager._notify_all_users() โ”€โ”€โ–บ AIUser.notify_game_event() โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ AIManager.on_game_event() โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ Save to agent.recent_eventsโ”‚
โ”‚ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```
### ื–ืจื™ืžื” 2: ื”ื•ื“ืขืช ืฆ'ืื˜
```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ CHAT MESSAGE FLOW โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ โ”‚
โ”‚ 1. Agent A response includes chat_message: "Anyone want sheep?" โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 2. AIManager._broadcast_chat("A", "Anyone want sheep?") โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Add to chat_history (shared) โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ AILogger.log_chat() โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 3. For each other agent (B, C, D): โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ Check should_send_prompt(agent) โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ”œโ”€โ–บ if pending_request: SKIP (don't queue!) โ”‚
โ”‚ โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ โ””โ”€โ–บ if not pending: Send spectator prompt โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ””โ”€โ–บ Agents see chat in their next prompt โ”‚
โ”‚ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```
### ื–ืจื™ืžื” 3: ืื™ืจื•ืข ื‘ืžืฉื—ืง (Event)
```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ GAME EVENT FLOW โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ โ”‚
โ”‚ 1. GameManager: Something happens (dice roll, build, trade...) โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 2. GameManager._notify_all_users("dice_roll", "Rolled 6 (3+3)") โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”‚ For each user: โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 3. User.notify_game_event("dice_roll", "Rolled 6 (3+3)") โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ”œโ”€โ–บ HumanUser: prints to console โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ””โ”€โ–บ AIUser: delegates to AIManager โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 4. AIManager.on_game_event("dice_roll", "Rolled 6 (3+3)") โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ–ผ โ”‚
โ”‚ 5. For each agent: โ”‚
โ”‚ โ”‚ โ”‚
โ”‚ โ””โ”€โ–บ agent.recent_events.append({ โ”‚
โ”‚ "type": "dice_roll", โ”‚
โ”‚ "message": "Rolled 6 (3+3)", โ”‚
โ”‚ "timestamp": time.time() โ”‚
โ”‚ }) โ”‚
โ”‚ โ”‚
โ”‚ 6. When agent's turn comes, recent_events โ†’ "what_happened" โ”‚
โ”‚ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```
---
## ๐Ÿ“ ืžื‘ื ื” ืงื‘ืฆื™ื ื—ื“ืฉ
```
pycatan/
โ”œโ”€โ”€ ai/
โ”‚ โ”œโ”€โ”€ __init__.py # exports
โ”‚ โ”‚
โ”‚ โ”‚ # === NEW FILES ===
โ”‚ โ”œโ”€โ”€ ai_manager.py # ๐Ÿ†• AIManager class
โ”‚ โ”œโ”€โ”€ ai_user.py # ๐Ÿ†• AIUser class (wrapper)
โ”‚ โ”œโ”€โ”€ ai_logger.py # ๐Ÿ†• AILogger class
โ”‚ โ”œโ”€โ”€ agent_state.py # ๐Ÿ†• AgentState dataclass
โ”‚ โ”‚
โ”‚ โ”‚ # === EXISTING (minimal changes) ===
โ”‚ โ”œโ”€โ”€ prompt_manager.py # โœ… Keep as-is
โ”‚ โ”œโ”€โ”€ llm_client.py # โœ… Keep as-is
โ”‚ โ”œโ”€โ”€ state_filter.py # โœ… Keep as-is
โ”‚ โ”œโ”€โ”€ prompt_templates.py # โœ… Keep as-is
โ”‚ โ”œโ”€โ”€ response_parser.py # โœ… Keep as-is
โ”‚ โ”œโ”€โ”€ schemas.py # โœ… Keep as-is
โ”‚ โ””โ”€โ”€ config.py # โœ… Keep as-is
โ”‚
โ”œโ”€โ”€ players/
โ”‚ โ”œโ”€โ”€ __init__.py
โ”‚ โ”œโ”€โ”€ user.py # โœ… Keep as-is (abstract interface)
โ”‚ โ””โ”€โ”€ human_user.py # โœ… Keep as-is
โ”‚
โ”œโ”€โ”€ management/
โ”‚ โ””โ”€โ”€ game_manager.py # โœ… Keep as-is (no changes!)
โ”‚
examples/
โ”œโ”€โ”€ ai_testing/
โ”‚ โ”‚ # === DEPRECATED (will be replaced) ===
โ”‚ โ”œโ”€โ”€ generate_prompts_from_state.py # โš ๏ธ DEPRECATED
โ”‚ โ”œโ”€โ”€ test_ai_live.py # โš ๏ธ DEPRECATED
โ”‚ โ”œโ”€โ”€ play_with_prompts.py # โš ๏ธ DEPRECATED
โ”‚ โ”‚
โ”‚ โ”‚ # === NEW ===
โ”‚ โ”œโ”€โ”€ play_with_ai.py # ๐Ÿ†• New unified entry point
โ”‚ โ”‚
โ”‚ โ”‚ # === KEEP ===
โ”‚ โ”œโ”€โ”€ web_viewer.py # โœ… Keep (works with new log format)
โ”‚ โ””โ”€โ”€ my_games/ # โœ… Keep (session storage)
```
---
## ๐Ÿ“‹ ืชื•ื›ื ื™ืช ืžื™ืžื•ืฉ
### Phase 1: ื™ืฆื™ืจืช ืจื›ื™ื‘ื™ื ื—ื“ืฉื™ื (ืœื ืฉื•ื‘ืจื™ื ืงื•ื“ ืงื™ื™ื)
#### Step 1.1: AgentState
```
๐Ÿ“ Create: pycatan/ai/agent_state.py
โฑ๏ธ Estimated: 15 min
๐Ÿ“ Contents:
- AgentState dataclass
- Helper methods
```
#### Step 1.2: AILogger
```
๐Ÿ“ Create: pycatan/ai/ai_logger.py
โฑ๏ธ Estimated: 45 min
๐Ÿ“ Contents:
- AILogger class
- log_prompt() - saves JSON + TXT
- log_response() - saves JSON + updates MD
- MD format matching existing player_X.md
```
#### Step 1.3: AIManager
```
๐Ÿ“ Create: pycatan/ai/ai_manager.py
โฑ๏ธ Estimated: 1.5 hours
๐Ÿ“ Contents:
- AIManager class
- process_agent_turn()
- on_game_event()
- on_chat_message()
- should_send_prompt()
- _broadcast_chat()
```
#### Step 1.4: AIUser
```
๐Ÿ“ Create: pycatan/ai/ai_user.py
โฑ๏ธ Estimated: 30 min
๐Ÿ“ Contents:
- AIUser class extending User
- get_input() delegation
- notify_game_event() delegation
```
#### Step 1.5: Update __init__.py
```
๐Ÿ“ Modify: pycatan/ai/__init__.py
โฑ๏ธ Estimated: 5 min
๐Ÿ“ Add exports for new classes
```
---
### Phase 2: Entry Point ื—ื“ืฉ
#### Step 2.1: Create play_with_ai.py
```
๐Ÿ“ Create: examples/ai_testing/play_with_ai.py
โฑ๏ธ Estimated: 45 min
๐Ÿ“ Contents:
- Creates GameManager with AIUsers
- Creates AIManager
- Runs game loop
```
---
### Phase 3: ื‘ื“ื™ืงื•ืช ื•ื•ื™ื“ื•ื
#### Step 3.1: Test basic flow
```
โฑ๏ธ Estimated: 30 min
๐Ÿ“ Run play_with_ai.py
- Verify prompts generated
- Verify responses logged
- Verify MD files created
- Verify actions returned to GameManager
```
#### Step 3.2: Test chat flow
```
โฑ๏ธ Estimated: 20 min
๐Ÿ“ Test chat messages
- Agent sends chat
- Other agents receive in next prompt
- Chat history saved
```
#### Step 3.3: Test Web Viewer
```
โฑ๏ธ Estimated: 15 min
๐Ÿ“ Verify web_viewer.py works with new log format
```
---
### Phase 4: Cleanup (ืื•ืคืฆื™ื•ื ืœื™)
#### Step 4.1: Mark deprecated files
```
๐Ÿ“ Add deprecation notices to:
- generate_prompts_from_state.py
- test_ai_live.py
- play_with_prompts.py
```
#### Step 4.2: Update documentation
```
๐Ÿ“ Update:
- README
- AI_ARCHITECTURE.md
```
---
## โฑ๏ธ ืกื™ื›ื•ื ื–ืžื ื™ื
| Phase | Task | Time |
|-------|------|------|
| 1.1 | AgentState | 15 min |
| 1.2 | AILogger | 45 min |
| 1.3 | AIManager | 1.5 hours |
| 1.4 | AIUser | 30 min |
| 1.5 | __init__.py | 5 min |
| 2.1 | play_with_ai.py | 45 min |
| 3.x | Testing | 1 hour |
| **Total** | | **~5 hours** |
---
## โœ… Checklist ืœืคื ื™ ื”ืชื—ืœื”
- [ ] ืžื‘ื ื” ื”ืืจื›ื™ื˜ืงื˜ื•ืจื” ื‘ืจื•ืจ
- [ ] ื”ื‘ื ืช ื–ืจื™ืžืช ื”ืขื‘ื•ื“ื”
- [ ] ื”ื‘ื ืช ืื—ืจื™ื•ืช ื›ืœ ืจื›ื™ื‘
- [ ] ืžื•ื›ืŸ ืœื”ืชื—ื™ืœ Phase 1
---
## ๐Ÿ”ฎ ืชื•ืกืคื•ืช ืขืชื™ื“ื™ื•ืช (ืœื ื‘ืจื™ืคืงื˜ื•ืจ ื”ื ื•ื›ื—ื™)
1. **ืกื™ื›ื•ื ืฆ'ืื˜ ืื•ื˜ื•ืžื˜ื™** - `_maybe_summarize_chat()`
2. **Web Viewer real-time** - WebSocket ืœ-AILogger
3. **Multi-LLM support** - OpenAI, Anthropic
4. **Agent personalities** - Different prompts per agent
5. **Replay system** - Play back from logs
---
## ๐Ÿ“š ืงื‘ืฆื™ื ืงืฉื•ืจื™ื
- [AI_ARCHITECTURE.md](.github/instructions/AI_ARCHITECTURE.md) - ืืจื›ื™ื˜ืงื˜ื•ืจื” ื›ืœืœื™ืช
- [AI_AGENT_PRINCIPLES.md](.github/instructions/AI_AGENT_PRINCIPLES.md) - ืขืงืจื•ื ื•ืช ืขื™ืฆื•ื‘
- [WORK_PLAN.md](.github/instructions/WORK_PLAN.md) - ืชื•ื›ื ื™ืช ืขื‘ื•ื“ื” ื›ืœืœื™ืช
---
**ืžื•ื›ืŸ ืœื”ืชื—ื™ืœ?** ๐Ÿš€