"""Agent Session 管理 - Phase 10.3 支持会话层级管理和子会话创建。 """ import json import os import uuid from dataclasses import asdict, dataclass, field from datetime import datetime from typing import Any @dataclass class SessionContext: """会话上下文""" session_id: str parent_session_id: str | None = None root_session_id: str | None = None depth: int = 0 user_id: str | None = None created_at: str | None = None last_active: str | None = None message_count: int = 0 metadata: dict[str, Any] = field(default_factory=dict) def __post_init__(self): if self.created_at is None: self.created_at = datetime.now().isoformat() if self.last_active is None: self.last_active = self.created_at @dataclass class SessionPersistence: """会话持久化""" def __init__(self, persistence_dir: str | None = None): if persistence_dir is None: persistence_dir = os.path.join( os.path.dirname(__file__), "..", "..", "..", "data", "sessions" ) self.persistence_dir = persistence_dir def _get_session_path(self, session_id: str) -> str: return os.path.join(self.persistence_dir, f"{session_id}.json") def save(self, session: "AgentSession") -> bool: """保存会话""" try: os.makedirs(self.persistence_dir, exist_ok=True) path = self._get_session_path(session.session_id) data = { "session_id": session.session_id, "parent_session_id": session.context.parent_session_id, "root_session_id": session.context.root_session_id, "depth": session.context.depth, "user_id": session.context.user_id, "created_at": session.context.created_at, "last_active": session.context.last_active, "message_count": session.context.message_count, "metadata": session.context.metadata, "history": session._history, } with open(path, "w", encoding="utf-8") as f: json.dump(data, f, indent=2, ensure_ascii=False) return True except Exception: return False def load(self, session_id: str) -> dict[str, Any] | None: """加载会话""" try: path = self._get_session_path(session_id) if not os.path.exists(path): return None with open(path, "r", encoding="utf-8") as f: return json.load(f) except Exception: return None def delete(self, session_id: str) -> bool: """删除会话""" try: path = self._get_session_path(session_id) if os.path.exists(path): os.remove(path) return True except Exception: return False def list_sessions(self, user_id: str | None = None) -> list[dict[str, Any]]: """列出所有会话""" sessions = [] try: os.makedirs(self.persistence_dir, exist_ok=True) for filename in os.listdir(self.persistence_dir): if filename.endswith(".json"): path = os.path.join(self.persistence_dir, filename) with open(path, "r", encoding="utf-8") as f: data = json.load(f) if user_id is None or data.get("user_id") == user_id: sessions.append(data) except Exception: pass return sessions class AgentSession: """Agent 会话管理器 支持: - 会话层级(parent/root/depth) - 子会话创建 - 会话摘要 - 持久化 """ def __init__( self, session_id: str | None = None, user_id: str | None = None, parent_session_id: str | None = None, ): self.session_id = session_id or str(uuid.uuid4())[:8] self.context = SessionContext( session_id=self.session_id, user_id=user_id, parent_session_id=parent_session_id, depth=0 if parent_session_id is None else 1, ) self._history: list[dict[str, Any]] = [] self._persistence = SessionPersistence() # 如果有父会话,设置 root_session_id if parent_session_id: parent_data = self._persistence.load(parent_session_id) if parent_data: self.context.root_session_id = ( parent_data.get("root_session_id") or parent_session_id ) self.context.depth = parent_data.get("depth", 0) + 1 async def initialize(self) -> dict[str, Any]: """初始化会话""" self.context.last_active = datetime.now().isoformat() self._persistence.save(self) return { "session_id": self.session_id, "depth": self.context.depth, "parent_session_id": self.context.parent_session_id, "root_session_id": self.context.root_session_id, } async def process_message(self, message: str, response: str) -> None: """处理消息并记录到历史""" self.context.message_count += 1 self.context.last_active = datetime.now().isoformat() self._history.append( { "role": "user", "content": message, "timestamp": datetime.now().isoformat(), } ) self._history.append( { "role": "assistant", "content": response, "timestamp": datetime.now().isoformat(), } ) self._persistence.save(self) async def spawn_child_session(self, user_id: str | None = None) -> "AgentSession": """创建子会话""" child = AgentSession( user_id=user_id or self.context.user_id, parent_session_id=self.session_id, ) child.context.root_session_id = self.context.root_session_id or self.session_id await child.initialize() return child async def get_session_summary(self) -> dict[str, Any]: """获取会话摘要""" return { "session_id": self.session_id, "parent_session_id": self.context.parent_session_id, "root_session_id": self.context.root_session_id, "depth": self.context.depth, "user_id": self.context.user_id, "created_at": self.context.created_at, "last_active": self.context.last_active, "message_count": self.context.message_count, "history_length": len(self._history), } async def persist(self) -> bool: """持久化会话""" return self._persistence.save(self) def get_history(self) -> list[dict[str, Any]]: """获取会话历史""" return self._history.copy() def add_metadata(self, key: str, value: Any) -> None: """添加会话元数据""" self.context.metadata[key] = value def get_metadata(self, key: str) -> Any: """获取会话元数据""" return self.context.metadata.get(key) # 全局会话存储(内存中) _sessions: dict[str, AgentSession] = {} def get_agent_session(session_id: str) -> AgentSession | None: """获取会话""" return _sessions.get(session_id) def create_agent_session( session_id: str | None = None, user_id: str | None = None, parent_session_id: str | None = None, ) -> AgentSession: """创建新会话""" session = AgentSession( session_id=session_id, user_id=user_id, parent_session_id=parent_session_id, ) _sessions[session.session_id] = session return session