Decomposition Project89: a modular, high -performance next -generation AI Agent framework design

Reprinted from panewslab
01/26/2025·3MOriginal text: 0xhhh
Let me talk about the conclusion first. @project_89 adopts a brand new way to design Agent Framework. This is a high-performance Agent Framework for game development. Compared with the currently used Agent Framework, it is more modular and has 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.
Developer background
This is a technical blog, so let’s first take a look at the founder’s technical strength
Founder worked on the Magick project before working on project89 , which is also a software that uses AI for programming. Shaw is also the fourth-ranked developer of this project. You can also see this project in shaw's resume.
Upper left: the founder of project89, lower right: lalaune is the shaw of ai16z
Today we will mainly introduce the high-performance Agent Framework in project89.
1. Why use ECS to design Agent Framework?
From the perspective of applications in the game field, the games currently using ECS architecture include:
- Blockchain games: Mud, Dojo
- Traditional games: Overwatch, Star Citizen, etc.
- And the current mainstream game engines are also evolving in the direction of ECS, such as Unity
What is ECS
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:
1.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.
2.Component
• Used to store specific data or state of an entity.
3.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:
-
In this game, for example, if you sense a weapon in front of you, then the execution function of the Perception System will be called to update the data in the Perception Component of the Agent Entity.
-
Then trigger the Memory System, call the Perception Component and the Memory Component at the same time, and persist the perceived data to the database through Memory.
-
Then the Action System calls the Memory Component and Action Component to obtain the surrounding environment information from the memory, and then finally executes the corresponding action.
-
Then we get an Update Agent Entity in which each Component data is updated. So we can see that the System 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:
System operation process
However, the actual system execution process is not the traditional method of calling the Memory System after the Perception System is executed. There is no calling relationship between different Systems. Each System will be executed once within a specified cycle, such as:
-
The Perception System may be executed once every 2 seconds to update the received external perceptions and update them to the Perception Component.
-
The Memory System may be executed every 1 second, extracting data from the Perception Component and loading it into the Memory Component.
-
The Plan System may be executed every 1000s. Based on the received information, it determines whether it needs to optimize and formulate a reasonable plan based on the corresponding target, and then records this part of the update into the Plan Component.
-
The Action System may also be executed every 2 seconds, so that it can react in time based on external information. At the same time, if the Plan Component is updated, it needs to update its Action Component based on this part of the data, thereby affecting the initial Action.
The article up to this point is based on my understanding of ArgOS, which greatly simplifies its architecture so that everyone can better understand it. Next, let’s look at the real ArgOS.
2. ArgOS System Architecture
In order to allow the 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):
- CONSCIOUS system
-
Contains RoomSystem, PerceptionSystem, ExperienceSystem, ThinkingSystem, ActionSystem, CleanupSystem)
-
The update frequency is usually higher (eg every 10 seconds).
-
Processing closer to the "real-time" or "conscious" level, such as environmental perception, real-time thinking, execution of actions, etc.
- Subconscious (SUBCONSCIOUS) system
-
GoalPlanningSystem, PlanningSystem
-
Update frequency is relatively low (eg every 25 seconds).
-
Handles "thinking" logic such as periodically checking/generating goals and plans.
- Unconscious (UNCONSCIOUS) system
-
Not enabled yet
-
The update frequency is slower (such as more than 50 seconds),
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:
1. PerceptionSystem is responsible for collecting "stimuli" (stimuli) from the outside world or other entities and updating them to the Perception component of the agent (Agent). Determine whether the stimulus changes significantly, and update accordingly based on stability, processing mode (ACTIVE/REFLECTIVE/WAITING), etc. Finally, "current perception" information 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) will be 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 is the agent’s own “thinking” system. Extract the current status from components such as Memory and Perception, and generate "ThoughtResult" through generateThought(...) and LLM/rule logic. According to the thinking results, it may be:
• 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 If the Action.pendingAction of an Agent is not empty, it will actually execute the action through runtime.getActionManager().executeAction(...). After execution, write the result back to Action.lastActionResult and notify the room or other entities. This will also generate CognitiveStimulus (cognitive stimulation), so that the subsequent system "knows" that the action has been completed, or can be included in the memory.
5. GoalPlanningSystem periodically evaluates the progress of the goals in the Goal.current[eid] list, or checks whether there are significant changes in external/self memory (detectSignificantChanges). When a new goal or goal adjustment is required, Goal.current[eid] is generated and written 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 the Plan (execution plan) for the "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:
• Get the list of occupants in the room (occupants), generate "appearance" stimuli for each agent, and 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 space environment, other entities that are perceiving the space can perceive changes in his appearance.
8. CleanupSystem periodically finds and removes entities marked with Cleanup components. 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.
- Example: a loop from "seeing the object" to "performing the action"
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 certain Room (EID=100). A new prop "MagicSword" appeared in this Room, and the corresponding Stimulus was generated.
-
PerceptionSystem detects the appearance of "MagicSword", generates Stimulus (type="item_appearance") for Agent(1) and adds it to Perception.currentStimuli[1]. Compared with the last Stimuli Hash, it is determined that there is a "significant change" and the ProcessingState of the agent is "reactivated" (ACTIVE mode).
-
ExperienceSystem sees that the Perception.currentStimuli of Agent(1) is not empty, so it extracts information such as "Sword appears" into one or more new Experiences (type: "observation"). Store it in Memory.experiences[1] and emit the "experience" event.
-
ThinkingSystem reads Memory, Perception and other status information, and calls generateThought: "I saw MagicSword, maybe I can 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 an appearance change (for example, "a curious expression"), the Appearance is updated and visual stimulation is generated.
-
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, cognitive stimulation (type="action_result") is generated, written to Memory or captured by ThinkingSystem in the next round.
-
GoalPlanningSystem (if the agent has goals) periodically evaluates the agent's goals. If one of the agent's goals at this time is "obtaining a powerful weapon" and detects that the MagicSword has been obtained, the goal may be marked as completed. If /keySeatTr new changes occur (such as "new objects appearing in the room" affect the goal pursued by the agent?), generate a new goal or abandon the old goal according to detectSignificantChanges.
-
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 it has been completed, set the associated Plan [status] to "completed"; or if the goal is to expand the subsequent process ("Research the Magic Sword"), generate more steps.
-
RoomSystem updates (every frame or round) the list of Occupants and visible entities in the room (100). 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”.
-
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.
Through the connection of these systems, AI Agent realizes:
• Perception of environmental changes (Perception) → Record or transform into inner experience (Experience) → Self-thinking and decision-making (Thinking) → Put into action (Action) → Dynamically adjust goals and plans (GoalPlanning + Planning) → Synchronize the environment (Room) → Timely recycling of useless entities (Cleanup)
3. Analysis of the overall architecture of ArgOS
1. Core architecture layering
2. Component classification
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:
-
Core identity classes (Identity-Level Components) • Agent / PlayerProfile / NPCProfile, etc. • Used to uniquely identify entities, store core role or unit information, and generally need to be persisted to the database.
-
Behavior & State Components • Action, Goal, Plan, ProcessingState, etc. • Represent the current things or goals that entities need to do, as well as the response status to external commands and internal thinking. • Contains pendingAction, goalsInProgress, plans, and thoughts or tasks in the queue. • 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 from a breakpoint, you may write to the database regularly.
-
Perception & Memory Components • Perception, Memory, Stimulus, Experience, etc. • Record the external information (Stimuli) perceived by the entity, and the experiences refined 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).
-
Environment and space classes (Room, OccupiesRoom, Spatial, Environment, Inventory, etc.) • Represent information such as rooms, environments, locations, item containers, etc. •
, 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.
-
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.
-
Auxiliary or operation and maintenance classes (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. • Generally exists only in memory and is rarely synchronized to the database unless logging or auditing needs.
3. System architecture
Already introduced above
4.Manager architecture
In addition to Component and System, we actually still lack a resource manager, such as how to access the database, how to deal with conflicts in status updates, etc.
Systems on the left (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:
-
Call RoomManager (RM) to query/update room information.
-
Use StateManager (SM) to get or save world/agent state, such as Memory, Goal, Plan, etc.
-
Use EventBus (EB) to broadcast or listen for events externally.
-
PromptManager (PM) is called when natural language processing or prompts are required.
---------------- Managers on the right (EventBus, RoomManager, StateManager, EventManager, ActionManager, PromptManager, etc.):
• Provides system-level functions, basically does not actively "drive" logic, but is called by Systems or Runtime.
• Typical examples:
-
ActionManager specializes in managing 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 synchronization between ECS and database or storage.
-
PromptManager provides extensions such as LLM Prompt templates and context management.
-
Intermediate SimulationRuntime (R):
• 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.
- CleanupSystem:
• Note in particular that it also interacts with ComponentSync (CS), which is used to synchronously remove 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 uniformly schedules the life cycle and behavior of all Systems and Managers at a higher level.
5. How to interact with vectors and databases
In ECS, Systems is where the logic is actually executed, and database reading and writing can be done through a "PersistenceManager/DatabaseManager" or "StateManager". The general process can be as follows:
-
When starting or loading (Initial Load) • 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.
-
ECS runtime (Systems Update Loop) • The system does something 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 need to be persisted (such as a new long-term goal), notify the StateManager to write this field to the database through component listening or system events.
-
Periodic or Event-Driven persistence (Periodic or Event-Driven) • You can call PersistenceManager.storeComponentData(eid, "Goal") at certain key points in the system (such as when the target plan is updated or when an important event occurs on the Agent). The class interface is dropped into the library. • You can also let StateManager scan components or entities containing 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.
-
Exit or breakpoint save (Manual or Shutdown Save) • 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.
- Complete sample process
The following is a simple scenario to demonstrate the possible ways in which components and databases interact:
-
At startup: StateManager.queryDB("SELECT * FROM agents") → Obtain 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.
-
Running phase: 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 period of time, StateManager finds that Memory.experiences[eid] has "needsPersistence" and writes it to the database (INSERT INTO memory_experiences...).
-
Stop or breakpoint: 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 restart, the ECS world state can be loaded and restored from the database.
• Classifying 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). Systems operate through it when they need to read and write the database, avoiding directly writing SQL or similar low-level statements in the 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.
5. Architectural innovation points
-
The highlights of the entire architecture are:
-
Each System runs independently and has no calling relationship with other Systems. Therefore, even if we want to realize the Agent's " perception of environmental changes (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) " When it comes to capabilities, each system will have many interdependencies in function, but we can still use the ECS architecture to structure the whole into independent systems. Each system can still run independently and will not interact with other systems. and coupling relationship. I think this is also the main reason why Unity has increasingly migrated to the ECS architecture in recent years.
-
And for example, I just want an Agent to have some basic capabilities. I only need to reduce the registration of some Components and the registration of the System when defining the Entity, which can be easily achieved without changing a few lines of code.
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. In addition, the performance of the current architecture is actually much better than that of the traditional object-oriented architecture. This is a recognized fact in the gaming circle, because the design of ECS is more suitable for concurrency, so when we use ECS in complex Defai scenarios, The architecture may also have more advantages, especially in scenarios where agents are expected to be used for quantitative transactions, ECS will also be useful (not just in agent game scenarios).
-
Dividing the system into conscious, subconscious and unconscious to distinguish how often different types of systems should be executed is an extremely clever design and is already a very concrete human ability.
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. However, it is a pity that the $project89 project has been lacking in publicity for this framework. , so I spent a long time (4 days) writing this article. I think good things are still worth discovering. An English version should be released tomorrow. I hope there will be more game teams or Defai teams. Open this framework to provide you with a new potential architecture choice!