Cycle break conditions

The starter orchestrator runs one step per request: route, branch, reply. Real agents often loop: tool, retrieve, tool again, synthesize. That is where things go wrong. A bug in the routing rules or a confused model can send the agent back to the same branch forever. Every cycle costs tokens, latency, and user patience. You need explicit budgets.

layers/orchestrator.py (extended)
python
MAX_STEPS = 4
MAX_SAME_ROUTE = 2


class OrchestratorLayer:
    def initial_state(self, message: str, thread_id: str) -> Dict[str, Any]:
        return {
            "message": message,
            "thread_id": thread_id,
            "route": None,
            "route_history": [],
            "steps": 0,
            "tool_result": None,
            "retrieved": [],
            "reply": None,
        }

    def should_stop(self, state: Dict[str, Any]) -> tuple[bool, str]:
        """Return (stop, reason). Called before every step."""
        if state["steps"] >= MAX_STEPS:
            return True, "max_steps"
        history = state["route_history"]
        if len(history) >= MAX_SAME_ROUTE:
            tail = history[-MAX_SAME_ROUTE:]
            if all(r == tail[0] for r in tail):
                return True, "repeat_route"
        return False, ""

    def advance(self, state: Dict[str, Any], route: str) -> None:
        state["steps"] += 1
        state["route_history"].append(route)

Two budgets: a hard cap on total steps, and a repeat-route guard that stops when the same branch runs back-to-back. Both are explicit, both are logged, both show up in the trace.

Ordering exercise: Order the safe orchestrator loop

Loading practice…

Quiz: Quiz

Loading practice…