From 4c20cd4ac61045f7f954cdb47643888e3b94f384 Mon Sep 17 00:00:00 2001 From: Charlie Crane Date: Fri, 13 Dec 2024 15:28:01 +0000 Subject: [PATCH] #2869 - initial creation of how to section for the new extendable agents. --- CHANGELOG.md | 7 ++ .../how_to_guides/extensible_agents.rst | 73 +++++++++++++++++++ src/primaite/game/agent/agent_log.py | 1 + .../agent/scripted_agents/random_agent.py | 15 +--- 4 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 docs/source/how_to_guides/extensible_agents.rst diff --git a/CHANGELOG.md b/CHANGELOG.md index e9147947..3aec3ba1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.0.0] = TBC + +### Added + +### Changed +- Agents now follow a common configuration format, simplifying the configuration of agents and their extensibilty. + ## [3.3.0] - 2024-09-04 ### Added diff --git a/docs/source/how_to_guides/extensible_agents.rst b/docs/source/how_to_guides/extensible_agents.rst new file mode 100644 index 00000000..718ea09a --- /dev/null +++ b/docs/source/how_to_guides/extensible_agents.rst @@ -0,0 +1,73 @@ +.. only:: comment + + © Crown-owned copyright 2024, Defence Science and Technology Laboratory UK + +.. _about: + +Extensible Agents +***************** + +Agents defined within PrimAITE have been updated to allow for easier creation of new bespoke agents. + + +Developing Agents for PrimAITE +============================== + +Agents within PrimAITE, follow the shown inheritance structure, and + +# TODO: Turn this into an inheritance diagram + +AbstractAgent + | + | - AbstractScriptedAgent + | | + | | - AbstractTAPAgent + | | | + | | | - DataManipulationAgent + | | + | | + | | - RandomAgent + | | + | | - PeriodicAgent + | | + | | - RandomAgent + | + | + | - ProxyAgent + | + | - ControlledAgent + + +#. **ConfigSchema**: + + Configurable items within a new agent within PrimAITE should contain a ``ConfigSchema`` which holds all configurable variables of the agent. This should not include parameters related to its *state*. + + + .. code-block:: python + + class ExampleAgent(AbstractAgent, identifier = "example_agent"): + """An example agent for demonstration purposes.""" + + config: "ExampleAgent.ConfigSchema" + """Agent configuration""" + num_executions: int + """Number of action executions by agent""" + + class ConfigSchema(AbstractAgent.ConfigSchema): + """ExampleAgent configuration schema""" + + agent_name: str + """Name of agent""" + action_interval: int + """Number of steps between agent actions""" + +#. **identifier**: + + All agent classes should have a unique ``identifier`` attribute, for when they are added to the base ``AbstractAgent`` registry. PrimAITE notation is for these to be written in snake_case + +Changes to YAML file +==================== + +Agent configurations specified within YAML files used for earlier versions of PrimAITE will need updating to be compatible with PrimAITE v4.0.0+. + +# TODO: Show changes to YAML config needed here diff --git a/src/primaite/game/agent/agent_log.py b/src/primaite/game/agent/agent_log.py index 7f7b6ffd..6eaf9e73 100644 --- a/src/primaite/game/agent/agent_log.py +++ b/src/primaite/game/agent/agent_log.py @@ -30,6 +30,7 @@ class AgentLog(BaseModel): agent_name: str = "unnamed_agent" current_episode: int = 1 current_timestep: int = 0 + logger: logging def __init__(self, agent_name: Optional[str]): """ diff --git a/src/primaite/game/agent/scripted_agents/random_agent.py b/src/primaite/game/agent/scripted_agents/random_agent.py index 8b0a3591..0e9d2763 100644 --- a/src/primaite/game/agent/scripted_agents/random_agent.py +++ b/src/primaite/game/agent/scripted_agents/random_agent.py @@ -53,16 +53,6 @@ class PeriodicAgent(AbstractScriptedAgent, identifier="Periodic_Agent"): next_execution_timestep: int = 0 """Timestep of the next action execution by the agent.""" - @property - def num_executions(self) -> int: - """Convenience method for accessing num_executions from config.""" - return self.config.num_executions - - @property - def next_execution_timestep(self) -> int: - """Convenience method for accessing next_execution_timestep from config.""" - return self.config.next_execution_timestep - def _set_next_execution_timestep(self, timestep: int, variance: int) -> None: """Set the next execution timestep with a configured random variance. @@ -78,7 +68,8 @@ class PeriodicAgent(AbstractScriptedAgent, identifier="Periodic_Agent"): """Do nothing, unless the current timestep is the next execution timestep, in which case do the action.""" if timestep == self.next_execution_timestep and self.num_executions < self.config.max_executions: self.num_executions += 1 - self._set_next_execution_timestep(timestep + self.config.frequency, self.config.variance) - return "NODE_APPLICATION_EXECUTE", {"node_id": 0, "application_id": 0} + self._set_next_execution_timestep(timestep + self.frequency, self.variance) + self.target_node = self.action_manager.node_names[0] + return "node_application_execute", {"node_name": self.target_node, "application_name": 0} return "DONOTHING", {}