---
name: agentbyte-plan-based-orchestration
description: Guide for creating Plan-Based multi-agent orchestration in Agentbyte (Chapter 7.5). Use this when a task should be decomposed into explicit steps, assigned to specialist agents, and retried based on structured evaluation.
license: MIT
---

Use this skill when implementing multi-agent collaboration with an explicit plan up front, step-by-step execution, evaluator-driven retries, and focused context per step.

## Inputs to confirm
- Which specialist agents should be available to the planner
- Whether review steps need explicit approval gating
- Whether the default recent `messages` history is sufficient for step execution
- How many retries and review-revision loops are acceptable

## Pattern: Basic Plan-Based Orchestration

```python
import asyncio
from agentbyte import (
    Agent,
    AgentHistoryPolicy,
    FinalResultPolicy,
    HistoryContextPolicy,
    PlanBasedOrchestrator,
    ReviewGatePolicy,
    UserMessage,
)
from agentbyte.llm import OpenAIChatCompletionClient
from agentbyte.termination import MaxMessageTermination


async def main():
    # 1. Create one chat model client for planning, evaluation, and agents
    client = OpenAIChatCompletionClient.from_api_key(
        model="gpt-4.1-mini",
        config={"temperature": 0.2, "max_tokens": 600},
    )

    # 2. Create specialists with clear descriptions
    researcher = Agent(
        name="researcher",
        description="Research specialist for concise factual context",
        instructions=(
            "Gather only the facts needed for the current step and keep them concise."
        ),
        model_client=client,
    )

    writer = Agent(
        name="writer",
        description="Writing specialist for polished user-facing prose",
        instructions=(
            "Turn available material into concise, clear writing for the user."
        ),
        model_client=client,
    )

    reviewer = Agent(
        name="reviewer",
        description="Quality reviewer for clarity, completeness, and accuracy",
        instructions=(
            "Check whether the current draft satisfies the step goal and suggest "
            "precise improvements when needed."
        ),
        model_client=client,
    )

    # 3. Initialize the plan-based orchestrator
    orchestrator = PlanBasedOrchestrator(
        agents=[researcher, writer, reviewer],
        termination=MaxMessageTermination(12),
        model_client=client,     # Required: used for planning and evaluation
        max_iterations=10,       # Safety limit for total agent turns
        max_step_retries=2,      # Retry failed steps with evaluator feedback
        history_policy=HistoryContextPolicy(
            default=AgentHistoryPolicy(
                window="recent",
                message_limit=5,
                format="messages",
            )
        ),
        review_gate_policy=ReviewGatePolicy(
            enabled=True,
            reviewer_agent_name="reviewer",
            revision_agent_name="writer",
            approval_signal="APPROVED",
            max_revision_rounds=2,
        ),
        final_result_policy=FinalResultPolicy(
            prefer_agent="writer",
            strip_signals=["APPROVED"],
            strip_mode="trailing",
        ),
    )

    # 4. Run the task
    task = UserMessage(
        content="Create a short user note explaining why solar energy is beneficial.",
        source="user",
    )
    result = await orchestrator.run(task)

    print(f"Stop reason: {result.stop_message.content}")
    print(f"Steps completed: {result.pattern_metadata.get('steps_completed')}")
    print(f"Steps failed: {result.pattern_metadata.get('steps_failed')}")
    print(f"Total retries: {result.pattern_metadata.get('total_retries')}")
    print(f"Plan: {result.pattern_metadata.get('execution_plan')}")
    print(f"Total tokens: {result.usage.total_tokens}")


asyncio.run(main())
```

## Pattern: Streaming Plan-Based Orchestration

```python
from agentbyte.messages import Message
from agentbyte.types import OrchestrationEvent, OrchestrationResponse

async for item in orchestrator.run_stream(task, verbose=True):
    if isinstance(item, Message):
        print(f"[MSG][{item.source}] {item.content}")
    elif isinstance(item, OrchestrationEvent):
        print(f"[EVENT][{item.__class__.__name__}] {item}")
    elif isinstance(item, OrchestrationResponse):
        print(f"Complete: {item.stop_message.content}")
        print(f"Steps completed: {item.pattern_metadata.get('steps_completed')}")
```

## How Plan-Based Orchestration Works

1. The orchestrator creates a structured `ExecutionPlan`.
2. Each `PlanStep` assigns one step to one named agent.
3. The assigned agent receives focused context plus the current step instruction.
4. The orchestrator evaluates the step with structured `StepProgressEvaluation`.
5. If the step fails and retries remain, the orchestrator injects targeted retry instructions.
6. When all steps are exhausted or completed, the orchestration stops cleanly.

## Structured Models

Plan-based orchestration relies on these public models:

```python
from agentbyte.orchestration.plan import (
    ExecutionPlan,
    PlanStep,
    StepProgressEvaluation,
)
```

```python
class PlanStep(BaseModel):
    task: str
    agent_name: str
    reasoning: str


class ExecutionPlan(BaseModel):
    steps: list[PlanStep]


class StepProgressEvaluation(BaseModel):
    step_completed: bool
    failure_reason: str
    confidence_score: float
    suggested_improvements: list[str]
```

## Policy Surface

```python
orchestrator = PlanBasedOrchestrator(
    agents=[agent1, agent2, agent3],
    termination=termination_condition,
    model_client=planner_client,
    max_iterations=50,
    max_step_retries=3,
    history_policy=HistoryContextPolicy(
        default=AgentHistoryPolicy(
            window="recent",
            message_limit=5,
            format="messages",
        )
    ),
    review_gate_policy=ReviewGatePolicy(enabled=False),
)
```

Use the two policies for different concerns:
- `HistoryContextPolicy`: contains a default `AgentHistoryPolicy` plus optional per-agent `AgentHistoryPolicy` overrides for step execution
- `ReviewGatePolicy`: controls explicit approval gating, revision loops, approval signals, and preferred final-result agent

`AgentHistoryPolicy` is the per-agent rule that actually defines:
- `window`
- `message_limit`
- `format`
- `include_turn_prompt`

## Pattern Metadata

After completion, `result.pattern_metadata` contains plan-aware state such as:

- `execution_plan`: Serialized plan steps and assignments
- `steps_completed`: Number of successful steps
- `steps_failed`: Number of steps exhausted after retries
- `total_retries`: Total retry attempts across all steps
- `current_step_index`: Final step index reached
- `current_step_retry_count`: Retry count for the active step
- `planner_model`: Model name used for plan/evaluation calls
- `planner_calls`: Number of planning calls made
- `evaluation_calls`: Number of step evaluations made
- `history_policy`: Serialized step-execution history policy
- `review_gate_policy`: Serialized review-gate configuration

## Usage Tracking

Final `result.usage` includes:

- all agent execution usage
- the planning model call
- step evaluation model calls

This makes plan-based orchestration more expensive than simpler patterns, but much easier to inspect and control.

## When to Use Plan-Based vs Other Patterns

| Use Plan-Based When | Use AI-Driven When | Use Round-Robin When |
|---------------------|--------------------|----------------------|
| Work has clear phases | Routing should adapt turn by turn | Agents should alternate in fixed order |
| You want explicit step decomposition | You want flexible specialist selection | You want deterministic sequencing |
| Step verification matters | Flow is exploratory | Flow is simple and cyclical |
| Retry feedback should be structured | Agent order is not known up front | Every agent should always get a turn |

## Guardrails

- **Model Client Required:** `PlanBasedOrchestrator` requires `model_client` for both plan generation and step evaluation.
- **Agent Names Must Match:** `PlanStep.agent_name` must match an actual agent name. Matching is case-insensitive, but the name still has to exist.
- **Review Gate Validation:** If `ReviewGatePolicy.enabled=True`, the configured reviewer and revision agent names must exist in the orchestrator pool.
- **Descriptions Matter:** Agent descriptions should clearly state specialization because the planner uses them to assign steps.
- **Termination Still Required:** Even though the pattern can stop when the plan completes, still provide a termination condition as a safety boundary. `MaxMessageTermination` and `TimeoutTermination` are the most useful here. `PredicateTermination` can add custom quality gates. `ConsecutiveAgentTermination` and `SourceTermination` are less relevant — plan-based orchestration controls agent selection via the plan, not an LLM selector.
- **Retries Are Bounded:** Use `max_step_retries` to keep failed steps from looping forever.
- **Focused Context:** By default plan-based orchestration sends only recent shared context plus the current step task. This is configurable via `HistoryContextPolicy`.
- **Fallback Behavior Exists:** If structured planning or evaluation fails, the orchestrator falls back to safe default behavior instead of crashing.

## Common Use Cases

1. Research -> write -> review workflows
2. Draft -> critique -> revise pipelines
3. Gather evidence -> synthesize -> present answer
4. Multi-step analyst/reporter/editor collaboration
5. Quality-sensitive tasks where each phase should be checked before moving on

## Examples

- Basic: `examples/orchestration/plan_based_basic.py`
- Streaming: `examples/orchestration/plan_based_stream.py`
- With OTel: `examples/orchestration/plan_based_with_otel.py`
