Dynamic Replanning After Each Action in Embabel: Architecture and Implementation
Embabel implements a plan-execute-observe feedback loop where the agent evaluates plan viability after every tool execution, triggering dynamic replanning through explicit exceptions or predicate-based decisions to adapt to failures or new information.
Dynamic replanning is a core capability of the Embabel agent framework that enables autonomous systems to recover from execution failures and adapt to changing conditions. This mechanism operates within the embabel-agent repository's execution engine, allowing agents to discard remaining actions and generate fresh strategies whenever the current plan becomes invalid. Understanding this replanning architecture is essential for building resilient agent workflows that can handle real-world unpredictability.
The Execution Loop Architecture
Embabel's agent execution follows a strict plan-execute-observe cycle implemented in the core runtime. After the initial planning phase generates a sequence of actions, the ToolLoop iterates through each execution, evaluating whether the current trajectory remains valid before proceeding to the next step.
Initial Planning and Action Execution
The PlanningAgent (or any concrete agent implementation) creates an initial plan consisting of a sequence of Tool or Skill invocations. The plan is represented as a list of Action objects that the ToolLoop dispatches sequentially. Each action executes within its own context, allowing the system to capture outputs and exceptions independently.
Post-Action Observation
After an action finishes, the EmbabelSpanEventListener records a span and adds the attribute embabel.plan.is_replanning with a value of false. The listener also captures any ReplanRequestedException thrown by the tool, creating a traceable record of the execution outcome before the replanning decision occurs.
Mechanisms for Triggering Dynamic Replanning
The system supports two primary mechanisms for initiating a replan after tool execution. Both approaches allow the agent to dynamically adapt without manual intervention.
Explicit Signals via ReplanRequestedException
Any tool can force an immediate replan by throwing ReplanRequestedException (defined in embabel-agent-api/src/main/kotlin/com/embabel/agent/core/ReplanRequestedException.kt). This exception carries a human-readable reason string (e.g., "stuck, retrying with new plan") that the engine captures in the span attribute embabel.replan.reason.
When the ToolLoop catches this exception, it logs the reason, sets the context flag for replanning, and breaks the current execution loop to generate a fresh plan.
Predicate-Based Replan Decisions
For conditional logic, the Tool utility class provides helper methods that inspect outputs without throwing exceptions:
Tool.replanWhen(predicate): Evaluates a predicate against the tool output. If the predicate returnstrue, the system creates aReplanDecisionobject.Tool.replanAndAdd(valueComputer): Computes a value to inject into the new plan while simultaneously triggering replanning.
These methods return ReplanDecision objects (defined in embabel-agent-api/src/main/java/com/embabel/agent/api/tool/ReplanDecision.java) that the loop converts into replan requests.
Observability and Metrics
Embabel exposes replanning events through both distributed tracing and Prometheus-style metrics, enabling operators to monitor agent adaptability in production.
Distributed Tracing Attributes
The EmbabelSpanEventListener (located in embabel-agent-observability/src/main/java/com/embabel/agent/observability/tracing/EmbabelSpanEventListener.java) automatically records the following span attributes:
embabel.plan.is_replanning: Boolean flag indicating whether the current iteration represents a replanning phaseembabel.replan.reason: Human-readable explanation whenReplanRequestedExceptiontriggers the replanembabel.tool_loop.replan_requested: Boolean indicating whether the tool loop resulted in a replan
These attributes are defined centrally in SpanAttributes.java (embabel-agent-observability/src/main/java/com/embabel/agent/observability/SpanAttributes.java).
Prometheus Metrics
The EmbabelMetricsEventListener (embabel-agent-observability/src/main/java/com/embabel/agent/observability/metrics/EmbabelMetricsEventListener.java) increments the counter embabel.planning.replanning.total (tagged by agent name) each time a replan occurs. This metric allows tracking replanning frequency across different agent implementations.
Implementation Examples
Below are practical implementations demonstrating how to configure tools for dynamic replanning.
Forcing Replan on Any Failure
Use Tool.replanAlways to wrap fragile operations that should trigger strategy regeneration upon any error:
Tool<String> fragileHttpCall = Tool.replanAlways(
ctx -> {
// ...perform HTTP request...
if (response.is5xx()) {
throw new RuntimeException("Server error");
}
return response.body();
}
);
Conditional Replanning
Use Tool.replanWhen to replan only when specific business logic conditions are met:
Tool<String> maybeStale = Tool.replanWhen(
ctx -> {
String data = fetchData();
return data == null || data.isEmpty(); // predicate decides to re‑plan
},
ctx -> new ReplanDecision("No data – need fresh plan")
);
Adding Context During Replan
Use Tool.replanAndAdd to compute values that should be available in the regenerated plan:
Tool<String> conditionalAdd = Tool.replanAndAdd(
ctx -> {
// compute a value that will be injected into the new plan
return computeNextStep();
},
ctx -> {
// normal execution path
return performStep();
}
);
Exception Handling in the ToolLoop
The execution loop handles replanning signals as follows:
try {
toolResult = tool.execute(context);
} catch (ReplanRequestedException e) {
observation = Observation.createNotStarted(SpanAttributes.EMBABEL_REPLAN, "replan")
.lowCardinalityKeyValue(SpanAttributes.EMBABEL_REPLAN_REASON, e.getReason())
.start();
// flag next iteration as a re‑plan
context.setReplanning(true);
// break out of the current loop – a new plan will be generated
break;
}
Summary
- Dynamic replanning in Embabel occurs after every tool execution through a plan-execute-observe feedback loop.
- Two trigger mechanisms exist: explicit
ReplanRequestedExceptionthrows and predicate-basedReplanDecisionobjects created viaTool.replanWhenorTool.replanAndAdd. - Key source files include
ReplanRequestedException.ktfor exception handling,Tool.javafor utility methods, andEmbabelSpanEventListener.javafor observability. - Observability is provided through span attributes (
embabel.plan.is_replanning,embabel.replan.reason) and the Prometheus metricembabel.planning.replanning.total. - Implementation requires wrapping tool logic with utility methods or throwing specific exceptions to signal the need for strategy regeneration.
Frequently Asked Questions
What triggers dynamic replanning after each action in Embabel?
Dynamic replanning triggers when a tool throws ReplanRequestedException or when a predicate-based utility like Tool.replanWhen evaluates to true. The ToolLoop checks for these signals immediately after each action execution, before proceeding to the next step in the plan.
How does Embabel handle the transition between execution and replanning?
When a replan is triggered, the ToolLoop breaks out of the current iteration, discards remaining actions, and sets a context flag indicating embabel.plan.is_replanning is true for the next cycle. The engine then invokes the planner again with updated context, including the reason for the replan captured in embabel.replan.reason.
Can developers customize the conditions that cause an agent to replan?
Yes. Developers can use the static utility methods in Tool.java to define custom predicates via replanWhen, or they can manually throw ReplanRequestedException with specific reason strings from within custom tool implementations. This allows fine-grained control over when the agent abandons the current plan.
How can I monitor replanning frequency in a production Embabel deployment?
Monitor the embabel.planning.replanning.total metric emitted by EmbabelMetricsEventListener, which tracks replanning events tagged by agent name. Additionally, trace spans containing embabel.tool_loop.replan_requested provide granular visibility into which specific tool executions triggered replanning behavior.
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 →