Custom Modules¶
AgentSociety 2 supports creating and registering custom agent and environment modules, allowing you to extend the platform with your own simulation components.
Overview¶
The custom module system allows you to:
Create custom agent classes with specialized behaviors
Create custom environment modules with domain-specific tools
Automatically discover and register modules through API
Test custom modules with auto-generated test scripts
Seamlessly integrate with existing AgentSociety framework
Persist requirements, design, and validation artifacts through the Progressive Disclosure workflow
Directory Structure¶
Custom modules are placed in the custom/ directory within your workspace:
workspace/
├── custom/ # User-created directory
│ ├── agents/ # Custom agent classes
│ │ └── my_agent.py
│ └── envs/ # Custom environment modules
│ └── my_env.py
└── .agentsociety/ # Auto-generated configuration
├── agent_classes/
├── env_modules/
└── custom_env_skill/
└── runs/
Create Custom Agents¶
All custom agents must inherit from AgentBase. Agents are workspace-bound stateless records: state is restored in restore() and written back in to_workspace(); see Using Agents.
from pathlib import Path
from agentsociety2.agent.base import AgentBase
from datetime import datetime
from typing import Any
class MyAgent(AgentBase):
"""My custom Agent"""
@classmethod
def mcp_description(cls) -> str:
return """MyAgent: A custom agent for specific tasks
This agent demonstrates custom behavior.
"""
async def restore(self, workspace_path: Path, service_proxy: Any) -> None:
"""恢复 workspace / 服务 / 技能,再追加自定义状态。"""
await super().restore(workspace_path, service_proxy)
self._custom_state: dict = {}
async def ask(self, message: str, readonly: bool = True, *, t=None) -> str:
"""Respond to questions from the environment"""
prompt = f"Question: {message}\nPlease answer:"
response = await self.acompletion([{"role": "user", "content": prompt}])
return response.choices[0].message.content or ""
async def step(self, tick: int, t: datetime) -> str:
"""Execute one simulation step"""
return f"Agent {self.id} executing step {tick}"
async def to_workspace(self, workspace_path: Path) -> None:
"""把动态状态写回 workspace(AGENT.json 等)。"""
self.persist_agent_json(tick=None, t=self._current_time)
Required Methods¶
Method |
Description |
|---|---|
|
Return module description (class method, recommended to override; |
|
Answer questions from the environment |
|
Execute one simulation step |
|
Write dynamic state back to the workspace (required). |
|
Restore custom state after |
Create Custom Environments¶
Custom environments must inherit from EnvBase and use the @tool decorator to register methods:
from agentsociety2.env import EnvBase, tool
from datetime import datetime
class MyEnv(EnvBase):
"""My custom environment"""
def __init__(self, config=None):
super().__init__()
# Initialize your environment state
@classmethod
def mcp_description(cls) -> str:
return """MyEnv: A custom environment
This environment provides custom tools for agents.
"""
@tool(readonly=True, kind="observe")
async def get_state(self, agent_id: int) -> dict:
"""Get current environment state (observation tool)"""
return {"agent_id": agent_id, "state": "normal"}
@tool(readonly=False)
async def do_action(self, agent_id: int, action: str) -> dict:
"""Perform an action (modification tool)"""
return {"agent_id": agent_id, "action": action, "result": "success"}
async def step(self, tick: int, t: datetime):
"""Environment step"""
self.t = t
async def to_workspace(self, workspace_path=None) -> None:
"""把动态状态写入 workspace;覆盖此方法可支持 resume,详见 :doc:`env_modules`。"""
if workspace_path is not None:
self._bind_workspace(workspace_path)
async def restore(self, workspace_path) -> bool:
"""从 workspace 恢复动态状态;resume 时调用,默认返回 False。"""
self._bind_workspace(workspace_path)
return False
reality compatibility constraints¶
The generated custom environment module must still follow the actual compatibility constraints of the current repository:
File must be located at
custom/envs/*.pyThe class definition must be located directly in this file, it cannot just be re-exported
Register key to continue using
class_nameThere is at least one legal
@toolstep()must be presentParameterless instantiation should be supported by default
cls()If the module needs observation capability, provide an
@tool(readonly=True, kind='observe')observation toolProviding an informative
mcp_description()is recommended; if not overridden, the base class default description is displayedIf the module has dynamic in-memory state and should support interrupted runs, override
to_workspaceandrestore. When--resumeis used, the module should be able to restore state fromstate/ENV_STATE.json; see “State persistence and resume” in Environment Modules.
Note
The scanner will skip files containing examples/ in the path (the example is for reference only and does not participate in registration).
@tool Decorator¶
The @tool decorator registers methods as tools accessible to agents:
Parameter |
Description |
|---|---|
|
Tool does not modify environment state |
|
Tool can modify environment state |
|
Observation tool (single agent_id parameter, readonly=True) |
|
Statistics tool (no parameters, readonly=True) |
|
Regular tool (any parameters, can be readonly=False) |
Registering Custom Modules¶
After creating custom modules, register them using the API:
Scan and Register
curl -X POST http://localhost:8001/api/v1/custom/scan \
-H "Content-Type: application/json" \
-d '{"workspace_path": "/path/to/workspace"}'
List Registered Modules
curl http://localhost:8001/api/v1/custom/list
Test Custom Modules
curl -X POST http://localhost:8001/api/v1/custom/test \
-H "Content-Type: application/json" \
-d '{"workspace_path": "/path/to/workspace"}'
Create or restore workflow run
curl -X POST http://localhost:8001/api/v1/custom/workflow/runs \
-H "Content-Type: application/json" \
-d '{"workspace_path": "/path/to/workspace", "user_request": "create a resource env"}'
Validate workflow run
curl -X POST http://localhost:8001/api/v1/custom/workflow/runs/<run_id>/validate \
-H "Content-Type: application/json" \
-d '{"module_path": "custom/envs/my_env.py", "class_name": "MyEnv"}'
API Endpoints¶
Endpoint |
Method |
Description |
|---|---|---|
|
POST |
Scan and register custom modules |
|
POST |
Test custom modules |
|
POST |
Clean custom module configuration |
|
GET |
List registered custom modules |
|
GET |
Get module status overview |
|
POST |
Create or restore a custom environment workflow run |
|
POST |
Perform end-to-end verification of scanner/tester/registry |
Examples¶
Example agents and environments can be found in the custom/ directory:
custom/agents/examples/simple_agent.py- Basic agent examplecustom/agents/examples/advanced_agent.py- Agent with memory and emotionscustom/envs/examples/simple_env.py- Counter environmentcustom/envs/examples/advanced_env.py- Resource management environment
These examples demonstrate best practices for creating custom modules.
Configuration¶
Set the WORKSPACE_PATH environment variable to point to your workspace:
export WORKSPACE_PATH=/path/to/workspace
Or add to your .env file:
WORKSPACE_PATH=/path/to/workspace
This setting tells the system where to find the custom/ directory.
Best Practices¶
Naming Conventions
Agent class names should end with
AgentEnvironment class names should end with
EnvFile names should use lowercase letters and underscores:
my_agent.py
Error Handling
Return meaningful error messages
Keep necessary logs on critical paths to facilitate review and problem location.
State Management
Use
restore()to restore state from the workspace andto_workspace()to write dynamic state back.When a custom Agent overrides
restore(), it should first callawait super().restore(...)to bind the workspace, skill runtime, and service handles.Record important state changes in replay
Keep state serializable (JSON compatible)
Tool Design
Use
kind="observe"for read-only observationsUse
kind="statistics"for aggregated dataUse
kind=Noneandreadonly=Falsefor operationsAfter generation, priority is given to locating the failure reason through
validation_report.jsonin the workflow product.