Orchestration modes
Supervisor and grounded topologies, mode_config settings, NodeConfig per-node customization, and resource_id for grounded context anchoring.
Intended audience: Stakeholders, Business analysts, Solution architects, Developers, Testers
Learning outcomes by role
Stakeholders
- Contrast supervisor versus grounded modes for complexity and safety postures.
Business analysts
- Capture mode_config and node-level customization in acceptance language.
Solution architects
- Explain resource_id anchoring and topology constraints for integration designs.
Developers
- Apply mode_config and NodeConfig fields when building orchestrator configs.
Testers
- Validate mode-specific settings, grounded context injection, and per-node override behavior.
An orchestration mode defines the routing topology inside the agent graph — how intent is classified, how tools are called, how results are validated, and how the final response is assembled. The mode is set at creation alongside framework_type and both are immutable for the life of the orchestrator.
Mode overview
Section titled “Mode overview”Real implementations (full LangGraph state machine in src/cadence/engine/impl/langgraph/):
langgraph+supervisor— multi-step agent pipeline.langgraph+grounded— resource-anchored single-scope agent.
Placeholder modes registered in _BACKEND_CONFIGS but backed by create_not_implemented_orchestrator(...)
that raises UnsupportedOperationError on invoke:
langgraph:coordinator,handoff,pipeline,reflectionopenai_agents:supervisor,coordinator,handoff,pipeline,reflection,ensemble,hierarchicalgoogle_adk: none registered (all placeholder orchestrators exist but the factory skips them)
grounded is langgraph only — there is no openai_agents/grounded and no google_adk/grounded.
Create-time rules — The Pydantic schema at api/orchestrator/schemas.py accepts any of
{langgraph, openai_agents, google_adk} × {supervisor, coordinator, handoff, grounded}. The validator at
domain/orchestrator/validator.py:145-147 reads FRAMEWORK_SUPPORTED_MODES and would currently reject
openai_agents/supervisor and openai_agents/coordinator (the constant only has {supervisor, grounded} for
langgraph and empty sets elsewhere). The factory has placeholders for all those combinations, so a permissive
config that bypasses the validator will accept them. Treat the factory registry as the source of truth and assume
google_adk and most non-supervisor/non-grounded combinations are placeholders that will raise at runtime.
Details and provider constraints are on Orchestration backends.
| Mode | Framework | Status | Use case |
| ------------- | ---------------------------- | ------ | -------------------------------------------------------------------------------------- |
| supervisor | langgraph, openai_agents | Active | Open-ended assistant with multi-step tool routing, planning, and optional validation |
| grounded | langgraph | Active | Answers scoped to a specific resource anchor — resource_id required per chat request |
| coordinator | langgraph, openai_agents | Active | Multi-agent coordination topology |
| handoff | langgraph, openai_agents | Active | Sequential agent handoffs |
Architecture overview
Section titled “Architecture overview”flowchart TDIN[Incoming message] --> CL[Router node]CL -->|direct response| RE[Responder node]CL -->|clarify| CLA[Clarifier node]CL -->|plan| PL[Planner node]PL --> EX[Executor node — tool calls]EX --> |tool results| PLPL --> |max_agent_hops reached or done| SY[Synthesizer node]RE --> OUT[Response]SY --> VA{enabled_llm_validation ?}VA -->|yes| VN[Validation node]VA -->|no| OUTVN --> OUTThe supervisor topology routes incoming intent through a router node, which decides whether to answer directly,
ask for clarification, or plan a multi-step tool sequence. The planner calls tools through the executor,
iterates up to max_agent_hops times, then passes results to the synthesizer. Each logical node in the graph
can be customized individually via NodeConfig.
flowchart TDIN[Incoming message + resource_id] --> RN[Router node]RN --> PL[Planner node]PL --> EX[Executor node — tool calls scoped to resource]EX --> PLPL --> |max_agent_hops or max_tool_rounds reached| SY[Synthesizer node]SY --> VA{enabled_validator ?}VA -->|yes| VN[Validator node]VA -->|no| OUT[Response]VN --> OUTEvery grounded chat request must include a resource_id. The mode constrains tool execution and context to that
anchor object. scope_rules injects additional constraints into the agent’s system context at runtime.
Data model — mode_config
Section titled “Data model — mode_config”mode_config is nested inside the orchestrator’s config JSON object. All keys are optional; omitting a key uses the default value.
{ "default_llm_config_id": "<uuid>", "mode_config": { "max_agent_hops": 25, "max_tool_rounds": 3, "enabled_parallel_tool_calls": true, "node_execution_timeout": 60, "enabled_llm_validation": false, "enabled_clarification_intent": true }}Base settings (all modes)
Section titled “Base settings (all modes)”From BaseOrchestratorSettings (src/cadence/engine/base/settings.py:10-19) — apply to every mode:
| Field | Default | Description |
| ------------------------ | ------- | ------------------------------------------------------------------------------ |
| node_execution_timeout | 60 | Per-node execution timeout in seconds |
| max_context_window | 16000 | Maximum token context window |
| message_context_window | 10 | Number of past messages included in context (supervisor overrides this to 5) |
| enabled_auto_compact | false | Auto-compact context when it exceeds the window |
| enabled_suggestion | false | Enable follow-up suggestion generation after each response |
| conversation_tone | None | Optional tone override (from TONE_CATALOG in engine.tones) |
| autocompact | NodeConfig() | Auto-compact node config (model override, prompt, etc.) |
| suggestion_node | NodeConfig() | Suggestion node config |
| error_handler_node | NodeConfig() | Error handler node config |
Supervisor-specific settings
Section titled “Supervisor-specific settings”From BaseSupervisorSettings (src/cadence/engine/base/supervisor_settings.py:11-23):
| Field | Default | Description |
| ------------------------------ | ------- | -------------------------------------------------------------- |
| message_context_window | 5 | Overrides the base default of 10 |
| max_agent_hops | 25 | Maximum recursive tool-call cycles before forced synthesis |
| max_tool_rounds | 3 | Maximum tool call rounds per planner step |
| enabled_parallel_tool_calls | true | Allow concurrent tool calls in a single planner step |
| enabled_llm_validation | false | Run a validation LLM pass on the draft response before sending |
| enabled_clarification_intent | true | Route ambiguous queries through the clarifier before planning |
| router_node / planner_node / synthesizer_node / validation_node / clarifier_node / responder_node | NodeConfig() | Per-node LLM overrides |
Grounded-specific settings
Section titled “Grounded-specific settings”From BaseGroundedSettings (src/cadence/engine/base/grounded_settings.py:11-23):
| Field | Default | Description |
| ------------------- | ------- | ------------------------------------------------------------------------- |
| scope_rules | "" | Free-text constraints injected into the agent’s system context at runtime |
| max_agent_hops | 25 | Maximum recursive tool-call cycles before forced synthesis |
| max_tool_rounds | 3 | Maximum tool-call rounds before forced synthesis |
| enabled_parallel_tool_calls | true | Allow concurrent tool calls in a single planner step |
| enabled_validator | true | Run a validator pass to check answer quality against the resource |
| router_node / planner_node (alias agent_node) / synthesizer_node / validation_node | NodeConfig() | Per-node LLM overrides |
NodeConfig — per-node LLM customization
Section titled “NodeConfig — per-node LLM customization”Each logical node in the graph can have its own LLM config, model, and prompt override, independent of the orchestrator’s default_llm_config_id. This lets you run a cheap model for routing and a more capable one for synthesis.
class NodeConfig(BaseModel): llm_config_id: Optional[str] = None # Override org-level default for this node model_name: Optional[str] = None # Model name override (e.g. "gpt-4o-mini" for fast nodes) prompt: Optional[str] = None # System prompt override for this node temperature: Optional[float] = None max_tokens: Optional[int] = None timeout: Optional[int] = None # Per-node timeout in secondsNodes that accept NodeConfig overrides:
| Mode | Configurable nodes |
| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Supervisor | router_node, planner_node, synthesizer_node, validation_node, clarifier_node, responder_node, autocompact, suggestion_node, error_handler_node |
| Grounded | router_node, planner_node (alias: agent_node), synthesizer_node, autocompact, suggestion_node, error_handler_node |
Example: cheap model for routing, stronger model for synthesis
Section titled “Example: cheap model for routing, stronger model for synthesis”{ "default_llm_config_id": "<gpt4o-config-uuid>", "mode_config": { "router_node": { "model_name": "gpt-4o-mini" }, "synthesizer_node": { "model_name": "gpt-4o", "temperature": 0.3 } }}Grounded mode — resource_id
Section titled “Grounded mode — resource_id”Grounded mode requires a resource_id on every chat request. This is the anchor object — a document ID, URL, database record ID, or any string — that constrains what the agent is allowed to answer about.
class ChatCompletionRequest(BaseModel): message: str # Required; 1–1000 characters conversation_id: Optional[str] # Resumes an existing thread stream: bool = True resource_id: Optional[str] # Required for grounded modeThe resource_id value is written into the graph’s initial state as state.resource_id. Plugin tool nodes read it to scope their queries:
resource_id = state.get("resource_id")results = await db.query(doc_id=resource_id, question=query)Creating orchestrators in each mode
Section titled “Creating orchestrators in each mode”{ "name": "General Purpose Assistant", "framework_type": "langgraph", "mode": "supervisor", "active_plugin_ids": ["com.example.web-search"], "tier": "hot", "config": { "default_llm_config_id": "<uuid>", "mode_config": { "max_agent_hops": 10, "max_tool_rounds": 3, "enabled_parallel_tool_calls": true, "enabled_clarification_intent": true, "enabled_llm_validation": false}}}{ "name": "Document QA", "framework_type": "langgraph", "mode": "grounded", "active_plugin_ids": ["com.example.doc-retrieval"], "tier": "demand", "config": { "default_llm_config_id": "<uuid>", "mode_config": { "scope_rules": "Answer only from the provided document. Do not use general knowledge.", "max_tool_rounds": 3, "enabled_validator": true}}}Every chat request against a grounded orchestrator must include "resource_id": " <document-id>".
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Fix |
| ----------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Chat returns UnsupportedOperationError | Orchestrator mode is registered but not yet implemented (coordinator, handoff, etc.) | Use supervisor or grounded mode |
| Agent loops without completing | max_agent_hops too high or tool always returns partial results | Reduce max_agent_hops; fix tool return logic |
| Grounded mode ignores resource_id | Plugin tool doesn’t read state.resource_id | Update the plugin to scope queries using state["resource_id"] |
| Chat fails in grounded mode | resource_id missing from request | Always pass resource_id in the chat body for grounded orchestrators |
| Router sends all queries to the same path | enabled_clarification_intent catching too broadly | Set enabled_clarification_intent: false or tune the router via router_node.prompt |
| Validation node rejects valid responses | enabled_llm_validation prompt too strict | Set enabled_llm_validation: false or override validation_node.prompt |
| Need to change mode | Mode is immutable | Create a new orchestrator; update central points to reference it |