agentsociety2.registry.base 源代码

"""模块注册中心(agent/env 的集中注册与惰性发现)。"""

from __future__ import annotations

from typing import Dict, List, Tuple, Type, Optional, Any
from pathlib import Path
import inspect
import os

from agentsociety2.agent.base import AgentBase
from agentsociety2.env.base import EnvBase
from agentsociety2.backend.path_security import resolve_workspace_root
from agentsociety2.logger import get_logger

logger = get_logger()


[文档] class ModuleRegistry: """agent 与环境模块的集中注册中心(单例)。 支持两类来源: - 内置模块:来自 ``agentsociety2.contrib`` 与内置 agent(例如 PersonAgent) - 自定义模块:来自 workspace 的 ``custom/`` 目录 默认启用惰性加载:只有在第一次访问 registry 内容时才触发发现与注册。 """ _instance: Optional["ModuleRegistry"] = None def __new__(cls) -> "ModuleRegistry": if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance
[文档] def __init__(self): if self._initialized: return self._initialized = True self._env_modules: Dict[str, Type[EnvBase]] = {} self._agent_modules: Dict[str, Type[AgentBase]] = {} self._workspace_path: Optional[Path] = None # Lazy loading flags self._builtin_loaded: bool = False self._custom_loaded: bool = False self._lazy_enabled: bool = True # Can be disabled to force eager loading logger.info("ModuleRegistry initialized (lazy loading enabled)")
def _ensure_builtin_loaded(self) -> None: """确保内置模块已加载(惰性加载触发点)。""" if not self._lazy_enabled: return if self._builtin_loaded: return from agentsociety2.registry.modules import discover_and_register_builtin_modules discover_and_register_builtin_modules(self) self._builtin_loaded = True def _ensure_custom_loaded(self) -> None: """确保自定义模块已加载(惰性加载触发点)。""" if not self._lazy_enabled: return if self._custom_loaded: return workspace_path = self._resolve_workspace_path() if workspace_path is None: # No workspace set, nothing to load self._custom_loaded = True return from agentsociety2.registry.modules import scan_and_register_custom_modules scan_and_register_custom_modules(workspace_path, self) self._custom_loaded = True def _ensure_loaded(self) -> None: """确保内置与自定义模块都已加载(惰性加载触发点)。""" self._ensure_builtin_loaded() self._ensure_custom_loaded() @property def env_modules(self) -> Dict[str, Type[EnvBase]]: """返回已注册环境模块映射,并在访问时触发惰性加载。""" self._ensure_loaded() return self._env_modules.copy() @property def agent_modules(self) -> Dict[str, Type[AgentBase]]: """返回已注册 agent 映射,并在访问时触发惰性加载。""" self._ensure_loaded() return self._agent_modules.copy()
[文档] def register_env_module( self, module_type: str, module_class: Type[EnvBase], is_custom: bool = False ) -> None: """注册环境模块。 :param module_type: type identifier(例如 ``simple_social_space``)。 :param module_class: 环境模块类。 :param is_custom: 是否为自定义模块。 """ if module_type in self._env_modules and not is_custom: logger.debug(f"Env module '{module_type}' already registered, skipping") return self._env_modules[module_type] = module_class logger.debug(f"Registered env module: {module_type} -> {module_class.__name__}")
[文档] def register_agent_module( self, agent_type: str, agent_class: Type[AgentBase], is_custom: bool = False ) -> None: """注册 agent。 :param agent_type: type identifier(例如 ``person_agent``)。 :param agent_class: agent 类。 :param is_custom: 是否为自定义 agent。 """ if agent_type in self._agent_modules and not is_custom: logger.debug(f"Agent '{agent_type}' already registered, skipping") return self._agent_modules[agent_type] = agent_class logger.debug(f"Registered agent: {agent_type} -> {agent_class.__name__}")
[文档] def get_env_module(self, module_type: str) -> Optional[Type[EnvBase]]: """按 type 获取环境模块类(会触发惰性加载)。 :param module_type: type identifier。 :returns: 环境模块类;未找到返回 ``None``。 """ self._ensure_loaded() return self._env_modules.get(module_type)
[文档] def get_agent_module(self, agent_type: str) -> Optional[Type[AgentBase]]: """按 type 获取 agent 类(会触发惰性加载)。 :param agent_type: type identifier。 :returns: agent 类;未找到返回 ``None``。 """ self._ensure_loaded() return self._agent_modules.get(agent_type)
[文档] def list_env_modules(self) -> List[Tuple[str, Type[EnvBase]]]: """返回已注册环境模块列表,并在访问时触发惰性加载。""" self._ensure_loaded() return list(self._env_modules.items())
[文档] def list_agent_modules(self) -> List[Tuple[str, Type[AgentBase]]]: """返回已注册 agent 列表,并在访问时触发惰性加载。""" self._ensure_loaded() return list(self._agent_modules.items())
[文档] def set_workspace(self, workspace_path: Path) -> None: """设置 workspace 路径(用于 custom 模块发现)。 :param workspace_path: workspace 目录。 """ self._workspace_path = resolve_workspace_root(str(workspace_path)) # Reset custom loaded flag so modules will be discovered on next access self._custom_loaded = False logger.debug(f"Registry workspace set to: {self._workspace_path}")
def _resolve_workspace_path(self) -> Optional[Path]: """返回用于 custom 模块发现的 workspace 路径;若无法推断则返回 ``None``。""" if self._workspace_path is not None: return self._workspace_path env_workspace = os.getenv("WORKSPACE_PATH") if env_workspace: self._workspace_path = resolve_workspace_root(env_workspace) logger.debug( f"Registry workspace inferred from WORKSPACE_PATH: {self._workspace_path}" ) return self._workspace_path cwd = Path.cwd().resolve() candidates = [cwd, *cwd.parents] for candidate in candidates: if (candidate / "custom" / "envs").exists() or ( candidate / "custom" / "agents" ).exists(): self._workspace_path = candidate logger.debug( f"Registry workspace inferred from cwd: {self._workspace_path}" ) return self._workspace_path return None
[文档] def load_builtin_modules(self) -> None: """主动加载内置模块(禁用惰性等待)。""" self._ensure_builtin_loaded()
[文档] def load_custom_modules(self) -> None: """主动加载自定义模块(禁用惰性等待)。""" self._ensure_custom_loaded()
[文档] def load_all_modules(self) -> None: """主动加载全部模块(内置 + 自定义)。""" self._ensure_loaded()
[文档] def clear_custom_modules(self) -> None: """清除 registry 中所有 custom 模块。""" to_remove = [ mt for mt, mc in self._env_modules.items() if getattr(mc, "_is_custom", False) ] for mt in to_remove: del self._env_modules[mt] to_remove = [ at for at, ac in self._agent_modules.items() if getattr(ac, "_is_custom", False) ] for at in to_remove: del self._agent_modules[at] # Reset custom loaded flag so modules will be re-discovered on next access self._custom_loaded = False logger.info(f"Cleared {len(to_remove)} custom modules")
[文档] def get_module_info(self, module_type: str, kind: str) -> Dict[str, Any]: """获取模块信息(会触发惰性加载)。 :param module_type: type identifier。 :param kind: ``env_module`` 或 ``agent``。 :returns: 模块信息字典(含参数签名、描述、是否 custom 等)。 """ self._ensure_builtin_loaded() if kind == "env_module": cls = self.get_env_module(module_type) else: cls = self.get_agent_module(module_type) if cls is None: return { "success": False, "error": f"Module '{module_type}' not found", } # Try to get AI-facing descriptions description = "" init_description = "" try: description = cls.description() except Exception: description = f"{cls.__name__}" try: init_description = cls.init_description() except Exception: init_description = "" # Get constructor signature params = {} try: sig = inspect.signature(cls.__init__) for name, param in list(sig.parameters.items())[1:]: # Skip 'self' params[name] = { "annotation": ( str(param.annotation) if param.annotation != inspect.Parameter.empty else "Any" ), "default": ( str(param.default) if param.default != inspect.Parameter.empty else None ), "kind": str(param.kind), } except Exception: logger.debug("Failed to inspect signature for tool", exc_info=True) return { "success": True, "type": module_type, "class_name": cls.__name__, "description": description, "init_description": init_description, "parameters": params, "is_custom": getattr(cls, "_is_custom", False), }
# Global registry instance _registry: Optional[ModuleRegistry] = None
[文档] def get_registry() -> ModuleRegistry: """:returns: 全局 :class:`~agentsociety2.registry.base.ModuleRegistry` 单例。""" global _registry if _registry is None: _registry = ModuleRegistry() return _registry