Forward the Original Title: I See The Next Generation Agent Framework in Project89
To get straight to the point, @project_89 adopts an entirely new approach to designing an Agent Framework. This is a high-performance Agent Framework specifically for game development. Compared to currently used Agent Frameworks, it is more modular and offers better performance.
This article took a long time to write, trying to let everyone understand what kind of architectural upgrades this framework has made compared to the traditional Agent framework. It has been revised many times before this version, but there are still some parts in the article that are too confusing. Due to technical difficulties, I was not able to popularize it further. If you have any suggestions for improving the article, please leave your comments.
Since this is a technical blog, let’s first take a look at the founder’s technical expertise.
Before working on Project89, the founder developed this project: https://github.com/Oneirocom/Magick, which is also an AI-powered programming software. Additionally, Shaw is ranked as the fourth top developer of this project. You can also see this project in Shaw’s portfolio.
Upper left: the founder of project89; Lower right: ‘lalaune’ is Shaw of ai16z
Today we will mainly introduce the high-performance Agent Framework in project89:
https://github.com/project-89/argOS
From the perspective of application in the game field
Games currently using ECS architecture include:
Blockchain games: Mud, Dojo
Traditional games: Overwatch, Star Citizen, etc.
Moreover, mainstream game engines are evolving toward ECS architecture, such as Unity.
ECS (Entity-Component-System) is an architectural pattern commonly used in game development and simulation systems. It completely separates data and logic to efficiently manage various entities and their behaviors in large-scale scalable scenarios:
Entity
• Just an ID (number or string), containing no data or logic.
• You can mount different components to give it various properties or capabilities as needed.
Component
• Used to store specific data or state of an entity.
System
• Responsible for executing logic related to certain components.
Let’s use a specific example of Agent action to understand this system:
In ArgOS, each Agent is regarded as an Entity, which can register different components. For example, in the picture below, our Agent has the following four components:
Agent Component: mainly stores basic information such as Agent name, model name, etc.
Perception Component: Mainly used to store perceived external data
Memory Component: Mainly used to store the Memory data of Agent Entity, similar things that have been done, etc.
Action Component: Mainly stores Action data to be executed
System workflow:
Then we get an Update Agent Entity in which each Component data is updated.
4.So we can see that the System here is mainly responsible for defining which Components to execute the corresponding processing logic.
And it is obvious that in project89 it is a world filled with various types of Agents. For example, some Agents not only have the above basic abilities but also have the ability to make plans.
Then it will be as shown in the picture below:
However, unlike traditional frameworks where one system directly triggers another (e.g., the Perception System calling the Memory System), in Project89, systems do not call each other directly. Instead, each system runs independently at fixed time intervals. For example:
So far, this article has significantly simplified the architecture of ArgOS to make it easier to understand. Now, let’s take a closer look at the real ArgOS system.
In order to allow Agent to think more deeply and perform more complex tasks, ArgOS has designed many Components and many Systems.
And ArgOS divides System into “three levels” (ConsciousnessLevel):
1) CONSCIOUS system
2) Subconscious (SUBCONSCIOUS) system
3) Unconscious (UNCONSCIOUS) system
Therefore, in ArgOS, different Systems are divided according to ConsciousnessLevel to stipulate how often this System will be executed.
Why is it designed this way?
Because the relationship between various systems in ArgOS is extremely complex, as shown below:
Determine whether the stimulus changes significantly and update accordingly based on stability, processing mode (ACTIVE/REFLECTIVE/WAITING), etc.
Ultimately, “current perception” data is provided for subsequent ExperienceSystem, ThinkingSystem, etc.
2.ExperienceSystem converts the Stimuli collected by PerceptionSystem into a more abstract “experience”.
LLM or rule logic (extractExperiences) is called to identify new experiences and stored in the Memory component.
Deduplicate, filter, and verify experiences, while triggering “experience” events to other systems or external listeners through eventBus.
3.ThinkingSystem represents the agent’s internal thought process.
Extract the current status from components such as Memory and Perception, and generate “ThoughtResult” through generateThought(…) and LLM/rule logic.
Based on the thought result, it may:
• Update thoughts in Memory (thinking history).
• Trigger a new action (put in Action.pendingAction[eid]).
• Change the agent’s external Appearance (expression, posture, etc.) and generate related Stimulus to let other entities “see” the change.
4.ActionSystem executes actions if Action.pendingAction is not empty, using runtime.getActionManager().executeAction(…).
After execution, write the result back to Action.lastActionResult and notify the room or other entities.
This also produces a CognitiveStimulus (cognitive stimulation) so that subsequent systems “know” that the action has been completed, or can be incorporated into memory.
5.GoalPlanningSystem periodically evaluates the progress of goals in the Goal.current[eid] list, or checks external/own memory for significant changes (detectSignificantChanges).
When a new goal or goal adjustment is required, generate and write Goal.current[eid] via generateGoals(…).
At the same time, the goal in progress (in_progress) is updated. If the completion or failure conditions are met, the status is changed, and a completion/failure signal is sent to the corresponding Plan.
6.PlanningSystem generates or updates Plan (execution plan) for “existing goal” (Goal.current[eid]).
If it is detected that some goals do not have corresponding active plans, generate an execution roadmap containing several steps through generatePlan(…) and write it to Plan.plans[eid].
When the goal is completed or failed, the Plan status associated with it will also be updated and corresponding cognitive stimulation will be generated.
7.RoomSystem handles room-related updates:
• Obtain the list of occupants in the room (occupants) and generate “appearance” stimuli for each agent to let other entities “see” his appearance or actions.
• Create and correlate room environment Stimulus (eg appropriate “room ambience” information).
Ensure that when the Agent is in a certain spatial environment, other entities that are sensing the space can perceive changes in his appearance.
8.CleanupSystem periodically finds and removes entities marked with the Cleanup component.Used to recycle Stimulus or other objects that are no longer needed to prevent a large number of invalid entities from being left in ECS.
The following scene example shows how each System cooperates to complete a complete process in one round (or several frames).
Scene preparation: There is an Agent (EID=1) in the World, which is in the “Active” state and is in a Room (EID=100).
A new prop “MagicSword” appeared in this Room, and the corresponding Stimulus was generated.
“I saw the MagicSword, maybe pick it up and see what it can do…”The thinking result contains an Action to be executed: { tool: “pickUpItem”, parameters: { itemName: “MagicSword” } }
ThinkingSystem writes this Action to Action.pendingAction[1].
If there is a change in appearance (e.g. “face with curious expression”), the Appearance is updated and visual stimulation is generated.
4.ActionSystem sees Action.pendingAction[1] = { tool: “pickUpItem”, parameters: … }。
Execute the “pickup” action logic through runtime.getActionManager().executeAction(“pickUpItem”, 1, { itemName: “MagicSword” }, runtime).Get the result: { success: true, message: “You picked up the magic sword” }, update to Action.lastActionResult[1], and trigger the “action” event to be broadcast to the room (100).
At the same time, a cognitive stimulus (type=”action_result”) is generated, written to Memory or captured by ThinkingSystem in the next turn.
5.The GoalPlanningSystem (if the agent has goals) periodically evaluates the agent’s goals. If one of the agent’s goals at this time is “obtain a powerful weapon” and detects that the MagicSword has been obtained, the goal may be marked as completed.If /keySeatTr new changes occur (for example, “new object appears in the room” affects the goal pursued by the agent?), generate a new goal or abandon the old goal based on detectSignificantChanges.
6.PlanningSystem (if there is a related goal) checks whether a new Plan is required or an existing Plan is updated for completed or newly generated goals such as “Obtain powerful weapons”.
If completed, set the associated Plan [status] to “completed”, or generate more steps if the goal is to expand the subsequent process (“Research the Magic Sword”).
7.RoomSystem updates the list of Occupants and visible entities in the room (100) (every frame or round).
If the appearance of agent(1) changes (for example, Appearance.currentAction = “holding sword”), create a new “appearance” visual stimulus to let other Agent2 and Agent3 in the same room know that “agent1 picked up the sword”.
8.CleanupSystem removes entities or stimuli that have been marked (Cleanup). If you no longer need to keep the “MagicSword” Stimulus after picking it up, you can delete the corresponding Stimulus entity in CleanupSystem.
• Perceive changes in the environment (Perception) → Record or transform into inner experience (Experience) → Self-thinking and decision-making (Thinking) → Put it into action (Action) → Dynamically adjust goals and plans (GoalPlanning + Planning) → Synchronize the environment (Room) → Timely Recycle useless entities (Cleanup)
In ECS, each entity can have several components. According to their nature and life cycle in the system, components can be roughly divided into the following categories:
1.Core Identity Classes (Identity-Level Components)
• Agent / PlayerProfile / NPCProfile, etc.
• Used to uniquely identify entities, store core roles or unit information, and generally need to be persisted to the database.
2.Behavior & State Components
• Action, Goal, Plan, ProcessingState,etc.
• Represents what the entity is currently trying to do or what its goals are, as well as its response status to external commands and internal thinking.
• Contains pendingAction, goalsInProgress, plans, and thoughts or tasks in the queue, etc.
• Typically medium/short-term states, many dynamically changing over game rounds or business cycles.
• Whether stocking is required depends on the situation. If you want to continue running after a breakpoint, you may write to the database periodically.
3.Perception & Memory Components
• Perception, Memory, Stimulus, Experience, etc.
• Records the external information (Stimuli) perceived by the entity, and the experiences refined into it after perception (Experiences).
• Memory can often accumulate large amounts of data, such as conversation records, event history, etc.; persistence is often required.
• Perception may be real-time or temporary information, mostly valid in the short term. You can decide whether to write it to the database according to your needs (for example, only important perception events are stored).
4.Environment and space classes (Room, OccupiesRoom, Spatial, Environment, Inventory, etc.)
• Represents information such as rooms, environments, locations, object containers, etc.
• Room.id, OccupiesRoom, Environment and other fields often need to be persisted, such as room homepage description, map structure, etc.
• Changing components (such as an Entity moving between rooms) can be written event-wise or periodically.
5.Appearance and interaction classes (Appearance, UIState, Relationship, etc.)
• Record the external “visible” or “interactive” parts of the entity, such as Avatar, pose, facialExpression, social relationship network with other entities, etc.
• Some parts may be processed only in memory (real-time representation), while other parts (such as key social relationships) may be persisted.
6.Utility & Maintenance Components (Cleanup, DebugInfo, ProfilingData, etc.)
• Used to mark which entities need to be recycled (Cleanup), or record debugging information (DebugInfo) for use in monitoring and analysis.
• Typically only exists in memory and is rarely synchronized to the database unless required for logging or auditing.
Already introduced above
Besides Components and Systems, an additional resource management layer is required. This layer handles database access, state conflicts, and other essential operations.
Left side: Systems (PerceptionSystem, ExperienceSystem, ThinkingSystem, etc.):
• Each system is scheduled for execution by SimulationRuntime in the ECS loop, querying and processing the entities it cares about (through component conditions).
• When executing logic, you need to interact with Managers, for example:
Right Side: Managers (EventBus、RoomManager、StateManager、EventManager、ActionManager、PromptManager, etc):
• Provide system-level functions, which basically do not actively “drive” logic, but are called by Systems or Runtime.
• Typical examples:
• Is the “scheduler” of all Systems, starting or stopping system cycles at different levels (Conscious/Subconscious, etc.);
• Managers are also created during the construction phase and passed to each System for use.
• Note in particular that it also interacts with ComponentSync (CS), which is used to synchronously remove components or event subscriptions when recycling entities.
In conclusion:
Each System will read and write data or call services through the corresponding Manager when needed, and the Runtime will uniformly schedule the life cycle and behavior of all Systems and Managers at a higher level.
In an ECS system, Systems handle the actual logic execution, while database operations (reading/writing) are managed through a Persistence Manager (PersistenceManager / DatabaseManager) or a State Manager (StateManager). The general process is as follows:
• StateManager / PersistenceManager loads the data of core persistence components such as Agents, Rooms, Goals and so on from the database, creates corresponding entities (Entities) and initializes related component fields.
• For example, read a batch of agent records and insert them into the ECS world, and initialize Agent, Memory, Goal and other components for them.
2.ECS Runtime (Systems Update Loop)
• The system does things in each frame (or round): PerceptionSystem collects “perceptions” and writes them to the Perception component (mostly short-term out of the library).
ExperienceSystem writes the new “cognitive experience” into Memory.experiences. If it is a key experience, it may also call StateManager for immediate storage, or mark it with “needsPersistence” for subsequent batch writing.
ThinkingSystem / ActionSystem / GoalPlanningSystem etc. make decisions based on component content and update fields in ECS.
If some components (such as Goal.current) undergo major changes and require persistence (such as a new long-term goal), notify the StateManager to write this field to the database through component listening or system events.
3.Periodic or Event-Driven Persistence
• You can call interfaces such as PersistenceManager.storeComponentData(eid, “Goal”) to drop the library at certain key points in the system (such as when the target plan is updated or when an important event occurs on the Agent).
• You can also have StateManager scan components or entities with the “needsPersistence” tag in CleanupSystem or timer, and write them back to the database at once.
• In addition, log or audit data (such as action history, thought log) can also be archived and stored here.
4.Manual or Shutdown Save (Checkpointing & Persistence on Exit)
• When the server or process is to be shut down, use StateManager.saveAll() to write the unwritten data to the database uniformly to ensure that the ECS state can be restored next time it is loaded.
• For some stand-alone/offline scenarios, archives can also be triggered manually.
The following is a simple scenario to demonstrate the possible ways in which components and databases interact:
1.Startup Phase: StateManager.queryDB(“SELECT * FROM agents”) → Get a batch of agent records, create Entity (EID=x) for each record in turn, and initialize Agent, Memory, Goal and other component fields.
At the same time, load room information from the “rooms” table and create a Room entity.
2.Runtime Operations: PerceptionSystem detects the event “MagicSword appears” in a certain room and writes Perception.currentStimuli[eid].ExperienceSystem converts Stimuli into Experience and assigns it to Memory.experiences[eid].ThinkingSystem determines the next action based on Memory, Goal and other information and generates Action.pendingAction[eid].After ActionSystem executes the action, it writes the result to Memory or Action.lastActionResult. If this is a major plot event, the latest part of Memory.experiences[eid] will be marked as needsPersistence.After a while, StateManager finds that Memory.experiences[eid] has “needsPersistence” and writes it to the database (INSERT INTO memory_experiences…).
3.Stop or Checkpoint Save: Based on ECS or system scheduling, StateManager.saveAll() is called when the “server is shut down” to write the latest status of key component fields (Agent, Memory, Goal, etc.) still in memory into the database.The next time you reboot, the ECS world state can be loaded and restored from the database.
• Categorizing components not only facilitates clear management of entity data in the project, but also helps us control the data boundaries between “requires persistence” and “exists only in memory”.
• Interaction with the database is usually handled by a specialized Manager (such as StateManager), and Systems operates through it when it needs to read and write to the database, avoiding directly writing SQL or similar low-level statements in System.
• In this way, you can simultaneously enjoy the logical efficiency and flexibility of ECS, as well as the advantages of persistence, breakpoint resuming, and data statistical analysis brought by the database.
The highlights of the entire architecture are:
As shown below:
At the same time, if you want to add new functions during the development process, it will not have any impact on other systems, and you can easily load the functions you want.
From my personal point of view, this is an extremely modular framework with excellent performance. The code quality is also very high and contains good design documents. Unfortunately, Project89 has lacked visibility and promotion for this framework, which is why I spent four days writing this article to highlight its strengths. I believe great technologies deserve recognition, and tomorrow, I plan to release an English version of this article to increase awareness among gaming teams and DeFi (Decentralized Finance) developers. Hopefully, more teams will explore this framework as a potential architectural choice for their projects!
Forward the Original Title: I See The Next Generation Agent Framework in Project89
To get straight to the point, @project_89 adopts an entirely new approach to designing an Agent Framework. This is a high-performance Agent Framework specifically for game development. Compared to currently used Agent Frameworks, it is more modular and offers better performance.
This article took a long time to write, trying to let everyone understand what kind of architectural upgrades this framework has made compared to the traditional Agent framework. It has been revised many times before this version, but there are still some parts in the article that are too confusing. Due to technical difficulties, I was not able to popularize it further. If you have any suggestions for improving the article, please leave your comments.
Since this is a technical blog, let’s first take a look at the founder’s technical expertise.
Before working on Project89, the founder developed this project: https://github.com/Oneirocom/Magick, which is also an AI-powered programming software. Additionally, Shaw is ranked as the fourth top developer of this project. You can also see this project in Shaw’s portfolio.
Upper left: the founder of project89; Lower right: ‘lalaune’ is Shaw of ai16z
Today we will mainly introduce the high-performance Agent Framework in project89:
https://github.com/project-89/argOS
From the perspective of application in the game field
Games currently using ECS architecture include:
Blockchain games: Mud, Dojo
Traditional games: Overwatch, Star Citizen, etc.
Moreover, mainstream game engines are evolving toward ECS architecture, such as Unity.
ECS (Entity-Component-System) is an architectural pattern commonly used in game development and simulation systems. It completely separates data and logic to efficiently manage various entities and their behaviors in large-scale scalable scenarios:
Entity
• Just an ID (number or string), containing no data or logic.
• You can mount different components to give it various properties or capabilities as needed.
Component
• Used to store specific data or state of an entity.
System
• Responsible for executing logic related to certain components.
Let’s use a specific example of Agent action to understand this system:
In ArgOS, each Agent is regarded as an Entity, which can register different components. For example, in the picture below, our Agent has the following four components:
Agent Component: mainly stores basic information such as Agent name, model name, etc.
Perception Component: Mainly used to store perceived external data
Memory Component: Mainly used to store the Memory data of Agent Entity, similar things that have been done, etc.
Action Component: Mainly stores Action data to be executed
System workflow:
Then we get an Update Agent Entity in which each Component data is updated.
4.So we can see that the System here is mainly responsible for defining which Components to execute the corresponding processing logic.
And it is obvious that in project89 it is a world filled with various types of Agents. For example, some Agents not only have the above basic abilities but also have the ability to make plans.
Then it will be as shown in the picture below:
However, unlike traditional frameworks where one system directly triggers another (e.g., the Perception System calling the Memory System), in Project89, systems do not call each other directly. Instead, each system runs independently at fixed time intervals. For example:
So far, this article has significantly simplified the architecture of ArgOS to make it easier to understand. Now, let’s take a closer look at the real ArgOS system.
In order to allow Agent to think more deeply and perform more complex tasks, ArgOS has designed many Components and many Systems.
And ArgOS divides System into “three levels” (ConsciousnessLevel):
1) CONSCIOUS system
2) Subconscious (SUBCONSCIOUS) system
3) Unconscious (UNCONSCIOUS) system
Therefore, in ArgOS, different Systems are divided according to ConsciousnessLevel to stipulate how often this System will be executed.
Why is it designed this way?
Because the relationship between various systems in ArgOS is extremely complex, as shown below:
Determine whether the stimulus changes significantly and update accordingly based on stability, processing mode (ACTIVE/REFLECTIVE/WAITING), etc.
Ultimately, “current perception” data is provided for subsequent ExperienceSystem, ThinkingSystem, etc.
2.ExperienceSystem converts the Stimuli collected by PerceptionSystem into a more abstract “experience”.
LLM or rule logic (extractExperiences) is called to identify new experiences and stored in the Memory component.
Deduplicate, filter, and verify experiences, while triggering “experience” events to other systems or external listeners through eventBus.
3.ThinkingSystem represents the agent’s internal thought process.
Extract the current status from components such as Memory and Perception, and generate “ThoughtResult” through generateThought(…) and LLM/rule logic.
Based on the thought result, it may:
• Update thoughts in Memory (thinking history).
• Trigger a new action (put in Action.pendingAction[eid]).
• Change the agent’s external Appearance (expression, posture, etc.) and generate related Stimulus to let other entities “see” the change.
4.ActionSystem executes actions if Action.pendingAction is not empty, using runtime.getActionManager().executeAction(…).
After execution, write the result back to Action.lastActionResult and notify the room or other entities.
This also produces a CognitiveStimulus (cognitive stimulation) so that subsequent systems “know” that the action has been completed, or can be incorporated into memory.
5.GoalPlanningSystem periodically evaluates the progress of goals in the Goal.current[eid] list, or checks external/own memory for significant changes (detectSignificantChanges).
When a new goal or goal adjustment is required, generate and write Goal.current[eid] via generateGoals(…).
At the same time, the goal in progress (in_progress) is updated. If the completion or failure conditions are met, the status is changed, and a completion/failure signal is sent to the corresponding Plan.
6.PlanningSystem generates or updates Plan (execution plan) for “existing goal” (Goal.current[eid]).
If it is detected that some goals do not have corresponding active plans, generate an execution roadmap containing several steps through generatePlan(…) and write it to Plan.plans[eid].
When the goal is completed or failed, the Plan status associated with it will also be updated and corresponding cognitive stimulation will be generated.
7.RoomSystem handles room-related updates:
• Obtain the list of occupants in the room (occupants) and generate “appearance” stimuli for each agent to let other entities “see” his appearance or actions.
• Create and correlate room environment Stimulus (eg appropriate “room ambience” information).
Ensure that when the Agent is in a certain spatial environment, other entities that are sensing the space can perceive changes in his appearance.
8.CleanupSystem periodically finds and removes entities marked with the Cleanup component.Used to recycle Stimulus or other objects that are no longer needed to prevent a large number of invalid entities from being left in ECS.
The following scene example shows how each System cooperates to complete a complete process in one round (or several frames).
Scene preparation: There is an Agent (EID=1) in the World, which is in the “Active” state and is in a Room (EID=100).
A new prop “MagicSword” appeared in this Room, and the corresponding Stimulus was generated.
“I saw the MagicSword, maybe pick it up and see what it can do…”The thinking result contains an Action to be executed: { tool: “pickUpItem”, parameters: { itemName: “MagicSword” } }
ThinkingSystem writes this Action to Action.pendingAction[1].
If there is a change in appearance (e.g. “face with curious expression”), the Appearance is updated and visual stimulation is generated.
4.ActionSystem sees Action.pendingAction[1] = { tool: “pickUpItem”, parameters: … }。
Execute the “pickup” action logic through runtime.getActionManager().executeAction(“pickUpItem”, 1, { itemName: “MagicSword” }, runtime).Get the result: { success: true, message: “You picked up the magic sword” }, update to Action.lastActionResult[1], and trigger the “action” event to be broadcast to the room (100).
At the same time, a cognitive stimulus (type=”action_result”) is generated, written to Memory or captured by ThinkingSystem in the next turn.
5.The GoalPlanningSystem (if the agent has goals) periodically evaluates the agent’s goals. If one of the agent’s goals at this time is “obtain a powerful weapon” and detects that the MagicSword has been obtained, the goal may be marked as completed.If /keySeatTr new changes occur (for example, “new object appears in the room” affects the goal pursued by the agent?), generate a new goal or abandon the old goal based on detectSignificantChanges.
6.PlanningSystem (if there is a related goal) checks whether a new Plan is required or an existing Plan is updated for completed or newly generated goals such as “Obtain powerful weapons”.
If completed, set the associated Plan [status] to “completed”, or generate more steps if the goal is to expand the subsequent process (“Research the Magic Sword”).
7.RoomSystem updates the list of Occupants and visible entities in the room (100) (every frame or round).
If the appearance of agent(1) changes (for example, Appearance.currentAction = “holding sword”), create a new “appearance” visual stimulus to let other Agent2 and Agent3 in the same room know that “agent1 picked up the sword”.
8.CleanupSystem removes entities or stimuli that have been marked (Cleanup). If you no longer need to keep the “MagicSword” Stimulus after picking it up, you can delete the corresponding Stimulus entity in CleanupSystem.
• Perceive changes in the environment (Perception) → Record or transform into inner experience (Experience) → Self-thinking and decision-making (Thinking) → Put it into action (Action) → Dynamically adjust goals and plans (GoalPlanning + Planning) → Synchronize the environment (Room) → Timely Recycle useless entities (Cleanup)
In ECS, each entity can have several components. According to their nature and life cycle in the system, components can be roughly divided into the following categories:
1.Core Identity Classes (Identity-Level Components)
• Agent / PlayerProfile / NPCProfile, etc.
• Used to uniquely identify entities, store core roles or unit information, and generally need to be persisted to the database.
2.Behavior & State Components
• Action, Goal, Plan, ProcessingState,etc.
• Represents what the entity is currently trying to do or what its goals are, as well as its response status to external commands and internal thinking.
• Contains pendingAction, goalsInProgress, plans, and thoughts or tasks in the queue, etc.
• Typically medium/short-term states, many dynamically changing over game rounds or business cycles.
• Whether stocking is required depends on the situation. If you want to continue running after a breakpoint, you may write to the database periodically.
3.Perception & Memory Components
• Perception, Memory, Stimulus, Experience, etc.
• Records the external information (Stimuli) perceived by the entity, and the experiences refined into it after perception (Experiences).
• Memory can often accumulate large amounts of data, such as conversation records, event history, etc.; persistence is often required.
• Perception may be real-time or temporary information, mostly valid in the short term. You can decide whether to write it to the database according to your needs (for example, only important perception events are stored).
4.Environment and space classes (Room, OccupiesRoom, Spatial, Environment, Inventory, etc.)
• Represents information such as rooms, environments, locations, object containers, etc.
• Room.id, OccupiesRoom, Environment and other fields often need to be persisted, such as room homepage description, map structure, etc.
• Changing components (such as an Entity moving between rooms) can be written event-wise or periodically.
5.Appearance and interaction classes (Appearance, UIState, Relationship, etc.)
• Record the external “visible” or “interactive” parts of the entity, such as Avatar, pose, facialExpression, social relationship network with other entities, etc.
• Some parts may be processed only in memory (real-time representation), while other parts (such as key social relationships) may be persisted.
6.Utility & Maintenance Components (Cleanup, DebugInfo, ProfilingData, etc.)
• Used to mark which entities need to be recycled (Cleanup), or record debugging information (DebugInfo) for use in monitoring and analysis.
• Typically only exists in memory and is rarely synchronized to the database unless required for logging or auditing.
Already introduced above
Besides Components and Systems, an additional resource management layer is required. This layer handles database access, state conflicts, and other essential operations.
Left side: Systems (PerceptionSystem, ExperienceSystem, ThinkingSystem, etc.):
• Each system is scheduled for execution by SimulationRuntime in the ECS loop, querying and processing the entities it cares about (through component conditions).
• When executing logic, you need to interact with Managers, for example:
Right Side: Managers (EventBus、RoomManager、StateManager、EventManager、ActionManager、PromptManager, etc):
• Provide system-level functions, which basically do not actively “drive” logic, but are called by Systems or Runtime.
• Typical examples:
• Is the “scheduler” of all Systems, starting or stopping system cycles at different levels (Conscious/Subconscious, etc.);
• Managers are also created during the construction phase and passed to each System for use.
• Note in particular that it also interacts with ComponentSync (CS), which is used to synchronously remove components or event subscriptions when recycling entities.
In conclusion:
Each System will read and write data or call services through the corresponding Manager when needed, and the Runtime will uniformly schedule the life cycle and behavior of all Systems and Managers at a higher level.
In an ECS system, Systems handle the actual logic execution, while database operations (reading/writing) are managed through a Persistence Manager (PersistenceManager / DatabaseManager) or a State Manager (StateManager). The general process is as follows:
• StateManager / PersistenceManager loads the data of core persistence components such as Agents, Rooms, Goals and so on from the database, creates corresponding entities (Entities) and initializes related component fields.
• For example, read a batch of agent records and insert them into the ECS world, and initialize Agent, Memory, Goal and other components for them.
2.ECS Runtime (Systems Update Loop)
• The system does things in each frame (or round): PerceptionSystem collects “perceptions” and writes them to the Perception component (mostly short-term out of the library).
ExperienceSystem writes the new “cognitive experience” into Memory.experiences. If it is a key experience, it may also call StateManager for immediate storage, or mark it with “needsPersistence” for subsequent batch writing.
ThinkingSystem / ActionSystem / GoalPlanningSystem etc. make decisions based on component content and update fields in ECS.
If some components (such as Goal.current) undergo major changes and require persistence (such as a new long-term goal), notify the StateManager to write this field to the database through component listening or system events.
3.Periodic or Event-Driven Persistence
• You can call interfaces such as PersistenceManager.storeComponentData(eid, “Goal”) to drop the library at certain key points in the system (such as when the target plan is updated or when an important event occurs on the Agent).
• You can also have StateManager scan components or entities with the “needsPersistence” tag in CleanupSystem or timer, and write them back to the database at once.
• In addition, log or audit data (such as action history, thought log) can also be archived and stored here.
4.Manual or Shutdown Save (Checkpointing & Persistence on Exit)
• When the server or process is to be shut down, use StateManager.saveAll() to write the unwritten data to the database uniformly to ensure that the ECS state can be restored next time it is loaded.
• For some stand-alone/offline scenarios, archives can also be triggered manually.
The following is a simple scenario to demonstrate the possible ways in which components and databases interact:
1.Startup Phase: StateManager.queryDB(“SELECT * FROM agents”) → Get a batch of agent records, create Entity (EID=x) for each record in turn, and initialize Agent, Memory, Goal and other component fields.
At the same time, load room information from the “rooms” table and create a Room entity.
2.Runtime Operations: PerceptionSystem detects the event “MagicSword appears” in a certain room and writes Perception.currentStimuli[eid].ExperienceSystem converts Stimuli into Experience and assigns it to Memory.experiences[eid].ThinkingSystem determines the next action based on Memory, Goal and other information and generates Action.pendingAction[eid].After ActionSystem executes the action, it writes the result to Memory or Action.lastActionResult. If this is a major plot event, the latest part of Memory.experiences[eid] will be marked as needsPersistence.After a while, StateManager finds that Memory.experiences[eid] has “needsPersistence” and writes it to the database (INSERT INTO memory_experiences…).
3.Stop or Checkpoint Save: Based on ECS or system scheduling, StateManager.saveAll() is called when the “server is shut down” to write the latest status of key component fields (Agent, Memory, Goal, etc.) still in memory into the database.The next time you reboot, the ECS world state can be loaded and restored from the database.
• Categorizing components not only facilitates clear management of entity data in the project, but also helps us control the data boundaries between “requires persistence” and “exists only in memory”.
• Interaction with the database is usually handled by a specialized Manager (such as StateManager), and Systems operates through it when it needs to read and write to the database, avoiding directly writing SQL or similar low-level statements in System.
• In this way, you can simultaneously enjoy the logical efficiency and flexibility of ECS, as well as the advantages of persistence, breakpoint resuming, and data statistical analysis brought by the database.
The highlights of the entire architecture are:
As shown below:
At the same time, if you want to add new functions during the development process, it will not have any impact on other systems, and you can easily load the functions you want.
From my personal point of view, this is an extremely modular framework with excellent performance. The code quality is also very high and contains good design documents. Unfortunately, Project89 has lacked visibility and promotion for this framework, which is why I spent four days writing this article to highlight its strengths. I believe great technologies deserve recognition, and tomorrow, I plan to release an English version of this article to increase awareness among gaming teams and DeFi (Decentralized Finance) developers. Hopefully, more teams will explore this framework as a potential architectural choice for their projects!