How Preconditions and Postconditions Work in Embabel Actions: A Complete Guide
Embabel actions use preconditions to validate execution eligibility against the current world state and postconditions (effects) to declare state changes, enabling the embabel-agent framework to automatically generate valid action sequences.
In the embabel/embabel-agent repository, actions are first-class objects that drive automated planning through declarative constraints. Understanding how preconditions and postconditions work in Embabel actions is essential for building agents that can reason about when to execute logic and how it transforms application state.
Core Components of an Embabel Action
Each Action instance exposes four critical properties that the planning engine consumes at runtime:
- Inputs: Data expected from the blackboard, such as user prompts or tool results
- Outputs: Data written back to the blackboard after execution completes
- Preconditions: A collection of predicates that must evaluate to
trueagainst the currentWorldStatefor the planner to consider the action - Effects (Postconditions): The set of changes applied to the
WorldStateonce the action finishes
These properties are derived from state-class definitions using annotations like @Goal, @Trigger, @Precondition, and @Effect, and are accessible at runtime via action.getPreconditions() and action.getEffects().
Planning Engine Evaluation Logic
During the planning phase, the engine iterates over all actions registered with the agent. For each candidate action, it evaluates the precondition predicates against the current WorldState. Only actions whose preconditions all evaluate to true are added to the planning graph.
When the planner selects a valid path, each chosen action's effects are applied to the world state, producing the next state that subsequent actions in the sequence will evaluate against. This cycle continues until the planner reaches a goal state.
Implementing Preconditions and Effects with Annotations
Developers declare constraints directly on action classes using the @Precondition and @Effect annotations. The AgentMetadataReader class processes these annotations during agent initialization to populate the action metadata.
package com.embabel.agent.example;
import com.embabel.agent.api.annotation.Action;
import com.embabel.agent.api.annotation.Precondition;
import com.embabel.agent.api.annotation.Effect;
import com.embabel.agent.domain.io.UserInput;
@Action(name = "fetchWeather")
public class FetchWeatherAction {
@Precondition
public boolean hasLocation(UserInput input) {
return input != null && !input.getValue().isBlank();
}
@Effect
public String storeWeather(String report) {
return report;
}
public String run(UserInput location) {
// Weather API call implementation
return "Sunny at " + location.getValue();
}
}
In AgentMetadataReader.java, the framework inspects methods annotated with @Precondition to build action.getPreconditions(), and methods annotated with @Effect to populate action.getEffects(). The planner will only schedule fetchWeather when a location exists on the blackboard, and after execution, the world state will contain the weather report output.
Runtime Inspection via AgentMetadataReader
The AgentMetadataReader unrolls state classes into an Agent object containing fully populated Action instances. You can verify the extracted metadata at runtime, as demonstrated in WriteAndReviewAgentTest.java:
// From WriteAndReviewAgentTest.java
for (var action : agent.getActions()) {
System.out.println("\n=== " + action.getName() + " ===");
System.out.println(" Inputs: " + action.getInputs());
System.out.println(" Outputs: " + action.getOutputs());
System.out.println(" Preconditions: " + action.getPreconditions());
System.out.println(" Effects: " + action.getEffects());
}
This output confirms exactly what the planner evaluates when building execution graphs, showing that preconditions and postconditions are automatically derived from annotations rather than manually configured.
Summary
- Embabel actions expose
getPreconditions()andgetEffects()methods that declaratively define execution requirements and state mutations - The planning engine evaluates preconditions against the current
WorldStateand only includes actions where all predicates returntrue - Annotations (
@Precondition,@Effect) on state classes allow developers to define constraints thatAgentMetadataReaderextracts into action metadata - Effects are applied to the world state after execution, enabling subsequent actions in the plan to see updated state
- Runtime inspection through test utilities like
WriteAndReviewAgentTest.javaallows verification of the planning constraints
Frequently Asked Questions
How are preconditions evaluated during the planning phase?
The planning engine retrieves all available actions from the agent and calls the predicate methods defined in action.getPreconditions(), passing the current WorldState as context. If any precondition returns false, the action is excluded from the planning graph for that state snapshot. Only actions with all preconditions satisfied are considered valid next steps.
What is the difference between outputs and effects in Embabel actions?
Outputs represent the data values an action writes to the blackboard after execution, while effects (postconditions) describe how those outputs mutate the WorldState. In practice, effects are built from declared outputs and explicit @Effect annotations, allowing the planner to predict state changes before execution occurs.
Where does the metadata for preconditions and effects originate?
The metadata originates from Java class definitions processed by AgentMetadataReader.java. This class scans for @Action annotated classes and extracts methods marked with @Precondition and @Effect to populate the Action interface implementations used by the planner.
Can preconditions access data from the blackboard?
Yes, preconditions can access blackboard data through method parameters. As shown in the FetchWeatherAction example, precondition methods accept typed inputs like UserInput that the framework injects from the current world state, allowing predicates to validate the existence and content of blackboard entries before permitting execution.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →