Skip to main content

Orchestrations

DAG-based workflow definitions for chaining agents, tools, and knowledge lookups into repeatable pipelines.

Overview

Orchestrations let you describe a directed acyclic graph (DAG) of nodes where each node performs a discrete operation. Nodes in the same execution round run in parallel; edges with activation groups control fan-in convergence.

Use orchestrations when you know the exact steps in advance and want deterministic, auditable execution — not when you need an LLM to decide which steps to take. An agent node inside an orchestration can still use LLM reasoning internally, but the orchestration graph itself is deterministic.

See the Permissions Reference for the IAM action strings for this module.

Data Model

Orchestration

FieldTypeDescription
idstringPublic ID (orch_ prefix)
project_idstringOwning project
namestringHuman-readable name
descriptionstring | nullOptional description
nodesarrayOrdered list of node definitions
edgesarrayDirected connections between nodes
state_schemaobjectOptional JSON Schema describing the run state
input_schemaobjectOptional JSON Schema describing the run input
created_atstringISO 8601 creation timestamp
updated_atstringISO 8601 last-updated timestamp

OrchestrationRun

FieldTypeDescription
idstringPublic ID (run_ prefix)
orchestration_idstringParent orchestration
statusstringrunning | paused | completed | failed | cancelled
stateobjectCurrent mutable execution state
active_nodesarrayNode IDs awaiting input (populated when status is paused)
artifactsobjectOutputs keyed by node ID
errorobject | nullError details if failed
current_node_idstring | nullMost recently executed node ID
required_actionobject | nullPresent when status is paused (see Human Nodes)
created_atstringISO 8601 creation timestamp
updated_atstringISO 8601 last-updated timestamp

Key Concepts

Node Types

TypeDescription
agentInvokes a SOAT Agent with a prompt. Uses agentId and prompt.
toolCalls a SOAT Tool. Uses toolId and inputMapping.
transformEvaluates a JSON Logic rule against the current state. Uses expression.
knowledgeSearches a knowledge source via the Knowledge module. Uses inputMapping with query and optional memoryIds.
memory_writeWrites a Memory entry. Uses memoryId and inputMapping with content.
conditionEvaluates a JSON Logic rule and emits a string label. Downstream edges use condition: "<label>" to select the active branch.
humanPauses the run and waits for external input. The run enters paused status with requiredAction.

State and Mappings

Each node can define:

  • inputMapping — Maps state paths (JSONPath-like $.key) to node inputs before execution.
  • outputMapping — Maps node outputs back to state paths after execution.

The root state is available to every node. Transforms and conditions receive the full state object.

Parallel Execution

All nodes that become active in the same round execute concurrently via Promise.all. After all complete, their outputs and state mutations are applied sequentially to avoid races. A single node with multiple outgoing edges activates all targets in parallel.

Activation Groups (Fan-In)

Edges can carry an activation_group name and an activation_condition to control when a downstream node runs:

activation_conditionBehaviour
all (default)The target node activates only after every edge in the group comes from a completed node.
anyThe target node activates as soon as any edge in the group comes from a completed node. Activated at most once per run.

Edges without an activation_group always pass through unconditionally.

Cycle Detection

Before execution begins, the engine performs a DFS-based cycle check. If a cycle is detected the run is created, set to failed, and the error field contains code: "ORCHESTRATION_CYCLE_DETECTED".

Human Nodes

When a human node is reached, the run pauses and the GET run response includes a required_action object:

{
"required_action": {
"type": "human_input",
"node_id": "approval",
"prompt": "Please approve or reject."
}
}

Examples

Create a sequential pipeline

soat create-orchestration \
--project-id "$PROJECT_ID" \
--name "fetch-and-summarize" \
--nodes '[
{"id":"fetch","type":"tool","tool_id":"tool_abc","output_mapping":{"result":"state.raw"}},
{"id":"summarise","type":"agent","agent_id":"agt_xyz","input_mapping":{"prompt":"state.raw"},"output_mapping":{"content":"state.summary"}}
]' \
--edges '[{"from":"fetch","to":"summarise"}]'

Start a run

soat create-orchestration-run \
--orchestration-id orch_01 \
--input '{"query": "summarize Q1 revenue"}'

Parallel fan-out

Both branch_a and branch_b run concurrently after start completes:

{
"nodes": [
{ "id": "start", "type": "transform", "expression": { "var": "query" } },
{ "id": "branch_a", "type": "agent", "agent_id": "agt_a", "output_mapping": { "content": "state.a" } },
{ "id": "branch_b", "type": "agent", "agent_id": "agt_b", "output_mapping": { "content": "state.b" } }
],
"edges": [
{ "from": "start", "to": "branch_a" },
{ "from": "start", "to": "branch_b" }
]
}

Fan-in with activation_condition: all

merge runs only after both branches complete:

{
"edges": [
{ "from": "branch_a", "to": "merge", "activation_group": "join", "activation_condition": "all" },
{ "from": "branch_b", "to": "merge", "activation_group": "join", "activation_condition": "all" }
]
}

Condition-based routing

{
"nodes": [
{
"id": "check",
"type": "condition",
"expression": { "if": [{ ">": [{ "var": "score" }, 0.8] }, "high", "low"] }
},
{ "id": "high_path", "type": "agent", "agent_id": "agt_high" },
{ "id": "low_path", "type": "agent", "agent_id": "agt_low" }
],
"edges": [
{ "from": "check", "to": "high_path", "condition": "high" },
{ "from": "check", "to": "low_path", "condition": "low" }
]
}