Spaces:
Configuration error
Configuration error
🔄 AI System Refactoring Plan
תוכנית ריפקטור מערכת ה-AI
תאריך: January 2026
סטטוס: 📋 Planning
גרסה: 1.0
📋 תוכן עניינים
- סקירת המצב הנוכחי
- הבעיות שצריך לפתור
- הארכיטקטורה החדשה
- פירוט הרכיבים
- תוכנית מימוש
- מיגרציה והגירה
- בדיקות
🔴 סקירת המצב הנוכחי
מבנה קיים
┌─────────────────────────────────────────────────────────────────────┐
│ המצב הנוכחי (בלאגן) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 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 - מצב סוכן יחיד
@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 - המנהל המרכזי
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
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 - ניהול לוגים
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
🔮 תוספות עתידיות (לא בריפקטור הנוכחי)
- סיכום צ'אט אוטומטי -
_maybe_summarize_chat() - Web Viewer real-time - WebSocket ל-AILogger
- Multi-LLM support - OpenAI, Anthropic
- Agent personalities - Different prompts per agent
- Replay system - Play back from logs
📚 קבצים קשורים
- AI_ARCHITECTURE.md - ארכיטקטורה כללית
- AI_AGENT_PRINCIPLES.md - עקרונות עיצוב
- WORK_PLAN.md - תוכנית עבודה כללית
מוכן להתחיל? 🚀