Project89 adopts a brand new approach to design a high-performance Agent Framework for game development.
Author: 0xhhh
To start with the conclusion, @project_89 employs a novel method to design the Agent Framework, which is a high-performance Agent Framework specifically for game development. Compared to the currently used Agent Frameworks, it is more modular and offers better performance.
This article took a long time to write, aiming to make everyone understand the architectural upgrades this framework has made compared to traditional Agent frameworks. It has gone through many revisions to reach this version, but there are still some overly technical parts that I couldn't simplify well. If you have any suggestions for improving the article, feel free to leave your comments.
Developer Background
This is a technical blog, so let's first take a look at the technical strength of the founder:
Before working on project89, the founder developed this project: https://github.com/Oneirocom/Magick, which is also a software that utilizes AI for programming. Shaw is the fourth developer ranked in this project, and you can also see this project in Shaw's resume.
Top left: Founder of project89, bottom right: Lalaune, who is Shaw from ai16z
Today, we will mainly introduce the high-performance Agent Framework within project89:
https://github.com/project-89/argOS
1. Why Use ECS to Design the Agent Framework
From the perspective of game applications, current games using the ECS architecture include: Blockchain games: Mud, Dojo; Traditional games: Overwatch, Star Citizen, etc. Moreover, mainstream game engines are also evolving towards ECS, such as Unity.
What is ECS?
ECS (Entity-Component-System) is a commonly used architectural pattern in game development and simulation systems. It completely separates data from 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. • Can mount different components as needed to give it various attributes or abilities.
Component • Used to store specific data or state of the entity.
System • Responsible for executing logic related to certain components.
To understand this system with a specific example of an Agent's actions: In ArgOS, each Agent is viewed as an Entity, which can register different components. For instance, in the image below, this Agent has the following four components: Agent Component: primarily stores basic information such as Agent name, model name, etc. Perception Component: mainly used to store perceived external data. Memory Component: primarily used to store the Memory data of the Agent Entity, such as past actions, etc. Action Component: mainly stores the Action data to be executed.
Workflow of the System:
In this game, if it perceives a weapon in front of it, it will call the execution function of the Perception System to update the data in the Perception Component of this Agent Entity;
Then it triggers the Memory System, simultaneously calling the Perception Component and Memory Component, to persist the perceived data into the database through Memory;
Next, the Action System calls the Memory Component and Action Component, retrieves information about the surrounding environment from memory, and ultimately executes the corresponding action;
Thus, we obtain an Update Agent Entity where each Component's data has been updated, showing that the System is mainly responsible for defining which Components to execute corresponding processing logic on.
It is also evident that in project89, it is a world filled with various types of Agents. For example, some Agents not only possess the basic abilities mentioned above but also have planning capabilities. This is illustrated in the image below:
Execution Process of the System
However, the actual execution process of the system is not as we imagine, where the Perception System calls the Memory System after execution. There is no calling relationship between different Systems; each System executes once within a specified period, for example:
The Perception System may execute every 2 seconds to update the received external perceptions and update them in the Perception Component.
The Memory System may execute every 1 second to extract data from the Perception Component and load it into the Memory Component.
The Plan System may execute every 1000 seconds to determine whether it needs to optimize and formulate a reasonable plan based on the received information and corresponding goals, then record this update in the Plan Component.
The Action System may also execute every 2 seconds, allowing timely responses to external information. If the Plan Component has updates, it needs to update its Action Component based on this data, thereby affecting the initially made Action.
Up to this point, the article has greatly simplified the architecture of ArgOS based on my understanding to help everyone better comprehend it. Next, let's look at the real ArgOS.
2. ArgOS System Architecture
In ArgOS, to enable Agents to engage in deeper thinking and execute more complex tasks, many Components and Systems have been designed.
Moreover, ArgOS categorizes Systems into "three levels" (Consciousness Level):
1) Conscious Systems
Includes RoomSystem, PerceptionSystem, ExperienceSystem, ThinkingSystem, ActionSystem, CleanupSystem.
The update frequency is usually high (e.g., every 10 seconds).
Handles processing that is closer to "real-time" or "conscious" levels, such as environmental perception, real-time thinking, executing actions, etc.
2) Subconscious Systems
GoalPlanningSystem, PlanningSystem.
The update frequency is relatively low (e.g., every 25 seconds).
Handles the logic of "thinking," such as periodic checks/generation of goals and plans.
3) Unconscious Systems
Currently not activated.
The update frequency is slower (e.g., over 50 seconds).
Thus, in ArgOS, different Systems are categorized based on Consciousness Level to determine how often each System executes.
Why design it this way? Because the relationships between various systems in ArgOS are extremely complex, as shown in the diagram below:
The PerceptionSystem is responsible for collecting "stimuli" from the external environment or other entities and updating them in the Agent's Perception component. It assesses whether the stimuli have significantly changed and makes corresponding updates based on stability, processing modes (ACTIVE/REFLECTIVE/WAITING), etc. Ultimately, it provides information about the "current perception" for subsequent systems like ExperienceSystem and ThinkingSystem.
The ExperienceSystem converts the stimuli collected by the PerceptionSystem into more abstract "experiences." It may call LLM or rule logic (extractExperiences) to identify new experiences and store them in the Memory component. It deduplicates, filters, and verifies experiences while triggering "experience" events to other systems or external listeners through the eventBus.
The ThinkingSystem is the Agent's own "thinking" system. It extracts the current state from components like Memory and Perception, generating "thought results" (ThoughtResult) through generateThought(…) with LLM or rule logic. Based on the thought results, it may: • Update the thoughts in Memory (thinking history). • Trigger new Actions (placed in Action.pendingAction[eid]). • Change the Agent's external Appearance (expressions, postures, etc.) and generate related Stimuli to let other entities "see" the changes.
The ActionSystem executes actions if an Agent's Action.pendingAction is not empty, using runtime.getActionManager().executeAction(…) to perform the action. After execution, it writes the result back to Action.lastActionResult and notifies the room or other entities. This also generates CognitiveStimuli to inform subsequent systems that the action has been completed or can be incorporated into memory.
The GoalPlanningSystem periodically evaluates the progress of goals in Goal.current[eid] or checks for significant changes in external/internal memory (detectSignificantChanges). When new goals are needed or adjustments are required, it generates and writes new goals into Goal.current[eid] through generateGoals(…). It also updates goals that are in progress (in_progress); if they meet completion or failure conditions, it changes their status and sends completion/failure signals to the corresponding Plan.
The PlanningSystem generates or updates Plans (execution plans) for "existing goals" (Goal.current[eid]). If it detects that certain goals do not have corresponding active plans, it generates an execution roadmap containing several steps through generatePlan(…) and writes it into Plan.plans[eid]. It also updates the associated Plan status when goals are completed or failed, generating corresponding cognitive stimuli.
The RoomSystem handles updates related to the room: • It retrieves the list of occupants in the room (occupants) and generates "appearance" stimuli for each agent, allowing other entities to "see" their appearance or actions. • It creates room environment Stimuli (such as relevant "room atmosphere" information) and associates them. It ensures that when an Agent is in a certain spatial environment, other entities perceiving that space can notice changes in its appearance.
The CleanupSystem regularly searches for and removes entities marked with the Cleanup component. It is used to recycle unnecessary Stimuli or other objects, preventing a large number of invalid entities from lingering in the ECS.
- Example: A cycle from "seeing an object" to "executing an action"
The following scenario example demonstrates how each System collaborates in a turn (or several frames) to complete a full process sequentially.
Scene Preparation: There is an Agent (EID=1) in the World, currently in "Active" status, and located in a Room (EID=100). A new item "MagicSword" has appeared in this Room, generating the corresponding Stimulus.
The PerceptionSystem detects the appearance of the "MagicSword," generating Stimulus (type="item_appearance") for Agent(1) and adding it to Perception.currentStimuli[1]. By comparing the last Stimuli Hash, it determines that "there is a significant change," and "reactivates" the Agent's ProcessingState (ACTIVE mode).
The ExperienceSystem sees that Agent(1)'s Perception.currentStimuli is not empty, so it extracts information like "Sword appears" into one or more new Experiences (type: "observation"). These are stored in Memory.experiences[1] and an "experience" event is emitted.
The ThinkingSystem reads the state information from Memory, Perception, etc., and calls generateThought: "I saw the MagicSword; maybe I can pick it up to see what it can do…" This thought result includes an Action to be executed: { tool: "pickUpItem", parameters: { itemName: "MagicSword" } }. The ThinkingSystem writes this Action into Action.pendingAction[1]. If there is an appearance change (e.g., "curious expression"), it updates the Appearance and generates visual stimuli.
The ActionSystem sees Action.pendingAction[1] = { tool: "pickUpItem", parameters: … }. It executes the "pick up" action logic through runtime.getActionManager().executeAction("pickUpItem", 1, { itemName: "MagicSword" }, runtime). The result is: { success: true, message: "You picked up the Magic Sword" }, which is updated to Action.lastActionResult[1] and triggers an "action" event broadcast to the room (100). It also generates cognitive stimuli (type="action_result") to be written into Memory or captured by the ThinkingSystem in the next turn.
The GoalPlanningSystem (if the agent has goals) periodically evaluates the agent's goals. If one of the agent's goals is "obtain a powerful weapon" and it detects that the MagicSword has been acquired, it may mark that goal as completed. If new changes are detected (e.g., "new objects appearing in the room" affecting the agent's pursued goals?), it generates new goals or abandons old ones based on detectSignificantChanges.
The PlanningSystem (if there are related goals) checks whether new Plans are needed or existing Plans should be updated for goals like "obtain a powerful weapon" that have been completed or just generated. If completed, it sets the associated Plan [status] to "completed"; or if the goal needs to expand into subsequent processes ("study the Magic Sword"), it generates more steps.
The RoomSystem (every frame or turn) updates the list of Occupants in the room (100) and visible entities. If the agent (1) changes appearance (e.g., Appearance.currentAction = "holding sword"), it creates new "appearance" visual stimuli to inform other agents in the same room (Agent2, Agent3) that "agent1 picked up the sword."
The CleanupSystem removes marked (Cleanup) entities or stimuli. If the "MagicSword" Stimulus is no longer needed after being picked up, it can be deleted from the corresponding Stimulus entity in the CleanupSystem.
Through the connection of these systems, the AI Agent achieves: • Perceiving environmental changes (Perception) → Recording or transforming into internal experiences (Experience) → Self-reflecting and decision-making (Thinking) → Taking action (Action) → Dynamically adjusting goals and plans (GoalPlanning + Planning) → Synchronizing the environment (Room) → Timely recycling of useless entities (Cleanup).
3. Overall Architecture Analysis of ArgOS
1. Core Architecture Layers
2. Component Classification
In ECS, each entity (Entity) can have several components (Component). Based on their nature and lifecycle in the system, components can be roughly classified into the following categories:
- Core Identity Components (Identity-Level Components)
- Agent / PlayerProfile / NPCProfile, etc.
- Used to uniquely identify entities and store core character or unit information, generally requiring persistence to a database.
- Behavior and State Components (Behavior & State Components)
- Action, Goal, Plan, ProcessingState, etc.
- Represent the current actions or goals of the entity, as well as the response state to external commands and internal thoughts.
- Include pendingAction, goalsInProgress, plans, and queued thoughts or tasks, etc.
- Typically represent medium/short-term states, many of which dynamically change with game turns or business cycles.
- Whether to persist to the database depends on the situation. If a breakpoint resume is desired, it may be periodically written to the database.
- Perception and Memory Components (Perception & Memory Components)
- Perception, Memory, Stimulus, Experience, etc.
- Record the external information (Stimuli) perceived by the entity, as well as the experiences (Experiences) distilled from those perceptions.
- Memory often accumulates a large amount of data, such as conversation records, event histories, etc.; it often requires persistence.
- Perception may consist of real-time or temporary information, mostly valid in the short term, and whether to write to the database can be determined based on needs (e.g., only storing significant perception events).
- Environment and Spatial Components (Room, OccupiesRoom, Spatial, Environment, Inventory, etc.)
- Represent information about rooms, environments, locations, item containers, etc.
- Fields like Room.id, OccupiesRoom, Environment, etc., often need to be persisted, such as room homepage descriptions, map structures, etc.
- Continuously changing components (e.g., entities moving between different rooms) can be written in an event-driven or periodic manner.
- Appearance and Interaction Components (Appearance, UIState, Relationship, etc.)
- Record the entity's "visible" or "interactive" parts, such as Avatar, pose, facialExpression, and social relationship networks with other entities.
- Some can be processed only in memory (real-time performance), while others (e.g., key social relationships) may need to be persisted.
- Auxiliary or Operational Components (Cleanup, DebugInfo, ProfilingData, etc.)
- Used to mark which entities need to be recycled (Cleanup) or to record debugging information (DebugInfo) for monitoring and analysis purposes.
- Generally exist only in memory and are rarely synchronized to the database unless logging or auditing is required.
3. System Architecture
As introduced above.
4. Manager Architecture
In addition to Components and Systems, we actually lack a resource manager, such as how to access the database and how to handle conflicts when state updates occur, etc.
On the 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).
- During execution logic, it needs to interact with Managers, for example:
- Calling RoomManager (RM) to query/update room information.
- Using StateManager (SM) to get or save world/agent states, such as Memory, Goal, Plan, etc.
- Utilizing EventBus (EB) to broadcast or listen for events.
- Calling PromptManager (PM) when natural language processing or prompts are needed.
On the right side, Managers (EventBus, RoomManager, StateManager, EventManager, ActionManager, PromptManager, etc.):
Provide system-level functions, generally do not actively "drive" logic but are called by Systems or Runtime.
Typical examples:
ActionManager specifically manages the registration and execution of actions.
EventManager / EventBus is used for event publishing and subscription mechanisms.
RoomManager manages rooms, layouts, and occupants.
StateManager is responsible for synchronizing ECS with the database or storage.
PromptManager provides LLM prompt templates, context management, and other extensions.
The central SimulationRuntime (R):
- Acts as the "scheduler" for all Systems, starting or stopping system loops at different levels (Conscious/Subconscious, etc.);
- Also creates Managers during the construction phase and passes them to each System for use.
CleanupSystem:
It is particularly noted that it also interacts with ComponentSync (CS) to synchronize the removal of components or event subscriptions when recycling entities.
Conclusion: Each System will read and write data or call services through the corresponding Manager when needed, while Runtime unifies the scheduling of all System and Manager lifecycles and behaviors at a higher level.
5. How to Interact with Vectors and the Database
In ECS, Systems are where the actual logic is executed, and database read/write can be accomplished through a "Persistence Manager (PersistenceManager / DatabaseManager)" or "State Manager (StateManager)." The general process can be outlined as follows:
- At Startup or Load (Initial Load)
- StateManager / PersistenceManager loads data for core persistent components like Agents, Rooms, Goals, etc., from the database, creating corresponding entities (Entities) and initializing related component fields.
- For example, reading a batch of agent records to insert into the ECS world, initializing their Agent, Memory, Goal, etc., components.
- During ECS Runtime (Systems Update Loop)
- Systems perform actions in each frame (or turn): PerceptionSystem collects "perceptions" and writes them to the Perception component (mostly short-term, not persisted). ExperienceSystem writes new "cognitive experiences" into Memory.experiences; if they are key experiences, it may simultaneously call StateManager for immediate storage or mark them with "needsPersistence" for later batch writing. ThinkingSystem / ActionSystem / GoalPlanningSystem, etc., make decisions based on component content and update fields in ECS. If certain components (like Goal.current) undergo significant changes that need persistence (e.g., new long-term goals), they notify StateManager to write that field to the database through component listeners or system events.
- Periodic or Event-Driven Persistence
- At certain key points in the system (e.g., when goal plans are updated or significant events occur for an Agent), interfaces like PersistenceManager.storeComponentData(eid, "Goal") can be called for persistence.
- It can also be done in CleanupSystem or timers, allowing StateManager to scan components or entities marked with "needsPersistence" and write them back to the database in one go.
- Additionally, logs or audit data (such as action history, thought logs) can also be archived here.
- Exit or Manual Save (Manual or Shutdown Save)
When the server or process is about to shut down, StateManager.saveAll() is called to write all unwritten data to the database to ensure that the ECS state can be restored on the next load.
For some single-player/offline scenarios, manual archiving can also be triggered.
Complete Example Process
The following provides a simple scenario demonstrating a possible way for components and the database to interact:
At startup: StateManager.queryDB("SELECT * FROM agents") → retrieves a batch of agent records, creating an Entity (EID=x) for each record in turn, and initializing its Agent, Memory, Goal, and other component fields. At the same time, it loads room information from the "rooms" table to create Room entities.
During the runtime phase: PerceptionSystem detects the event "MagicSword appears" in a certain room and writes it to Perception.currentStimuli[eid]. ExperienceSystem converts Stimuli into Experience and assigns it to Memory.experiences[eid]. ThinkingSystem decides the next action to take based on information from Memory, Goal, etc., generating Action.pendingAction[eid]. After ActionSystem executes the action, it writes the result to Memory or Action.lastActionResult. If this is a significant plot event, it will mark the latest part of Memory.experiences[eid] as needsPersistence. After a period, StateManager finds that Memory.experiences[eid] has the "needsPersistence" flag, and then writes it to the database (INSERT INTO memory_experiences …).
Stopping or breakpoint: Based on ECS or system scheduling, when "server shutdown" is called, StateManager.saveAll() is invoked to write the latest state of key component fields (Agent, Memory, Goal, etc.) still in memory into the database. The next time it restarts, it can load and restore the ECS world state from the database.
- Classifying components not only facilitates clear management of entity data in the project but also helps us control the boundaries between "needs persistence" and "exists only in memory" data.
- Interaction with the database is usually handled by a dedicated Manager (such as StateManager), and Systems perform operations through it when they need to read or write to the database, avoiding direct SQL or similar low-level statements in the System.
- This allows us to enjoy both the logical efficiency and flexibility of ECS, as well as the advantages of persistence, breakpoint resume, and data statistical analysis provided by the database.
### 5. Architectural Innovations
The highlights of the entire architecture are:
Each System operates independently and does not have a calling relationship with other Systems. Therefore, even if we want to implement the Agent's ability to "perceive environmental changes (Perception) → record or transform into internal experiences (Experience) → self-think and make decisions (Thinking) → take action (Action) → dynamically adjust goals and plans (GoalPlanning + Planning) → synchronize environment (Room) → timely recycle useless entities (Cleanup)," there will be many functional dependencies among the Systems. However, we can still structure the overall architecture into various unrelated Systems through the ECS architecture, allowing each System to operate independently without coupling with other Systems. I believe this is also the main reason why Unity has been increasingly migrating towards the ECS architecture in recent years.
Moreover, for example, if I only want an Agent to have some basic capabilities, I can easily achieve this by simply reducing the registration of some Components and Systems when defining the Entity, without needing to change a few lines of code.
As shown in the figure below:
At the same time, if you want to add new features during the development process, it will not affect other Systems, and you can easily load the desired functionality. Additionally, the performance of the current architecture is actually much stronger than that of traditional object-oriented architectures, which is a well-recognized fact in the gaming community. This is because the design of ECS is more suitable for concurrency, so when we are in complex DeFi scenarios, adopting the ECS architecture may also have advantages, especially in scenarios where Agents are used for quantitative trading; ECS will also have its place (not just in Agent gaming scenarios).
Dividing Systems into conscious, subconscious, and unconscious to distinguish different types of Systems based on how often they should execute is an extremely clever design, already resembling a very concrete human capability.
From my personal perspective, this is an extremely modular framework with excellent performance, high code quality, and well-designed documentation. However, unfortunately, the $project89 project has always lacked promotion for this framework, which is why I spent a long time (4 days) writing this article. I believe that good things are worth discovering, and I plan to publish an English version tomorrow, hoping that more game teams or DeFi teams will come across this framework, providing everyone with a new potential architectural choice!
免责声明:本文章仅代表作者个人观点,不代表本平台的立场和观点。本文章仅供信息分享,不构成对任何人的任何投资建议。用户与作者之间的任何争议,与本平台无关。如网页中刊载的文章或图片涉及侵权,请提供相关的权利证明和身份证明发送邮件到support@aicoin.com,本平台相关工作人员将会进行核查。