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 returns true, the system creates a ReplanDecision object.
  • 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 phase
  • embabel.replan.reason: Human-readable explanation when ReplanRequestedException triggers the replan
  • embabel.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 ReplanRequestedException throws and predicate-based ReplanDecision objects created via Tool.replanWhen or Tool.replanAndAdd.
  • Key source files include ReplanRequestedException.kt for exception handling, Tool.java for utility methods, and EmbabelSpanEventListener.java for observability.
  • Observability is provided through span attributes (embabel.plan.is_replanning, embabel.replan.reason) and the Prometheus metric embabel.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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →