Arke
BuildWorkflows

Rhizai

Compose kladoi into multi-step workflow DAGs with scatter/gather parallelism.

What is a Rhiza?

A rhiza (plural: rhizai) is a workflow definition that chains multiple kladoi into a directed acyclic graph (DAG). You define the steps, their connections, and the handoff operations — then invoke the rhiza and it cascades through the entire pipeline automatically.

Flow Structure

A rhiza has an entry step and a flow map of named steps:

{
  "label": "Document Processing",
  "version": "1.0.0",
  "entry": "extract",
  "flow": {
    "extract": {
      "klados": { "id": "01K_EXTRACT_KLADOS" },
      "then": { "pass": "describe" }
    },
    "describe": {
      "klados": { "id": "01K_DESCRIBE_KLADOS" },
      "then": { "done": true }
    }
  }
}

Each step specifies:

  • klados — Which klados to invoke (by entity reference)
  • input — Optional static parameters to pass to the klados
  • then — What happens after the klados completes

Handoff Operations

The then field controls how data flows between steps:

Pass (1:1)

Pass all outputs directly to the next step:

{ "then": { "pass": "next_step" } }

Scatter (1:N)

Each output becomes a separate invocation of the target step:

{ "then": { "scatter": "process_item" } }

If a klados produces 500 outputs, scatter creates 500 independent invocations of process_item.

Gather (N:1)

Collect all parallel outputs from a scatter, then invoke the target step once with all results:

{ "then": { "gather": "finalize" } }

Gather uses a batch entity for coordination. Each parallel invocation completes its batch slot. When all slots are filled, the gather target fires.

Route (Conditional)

Override the target step based on output properties:

{
  "then": {
    "scatter": "process",
    "route": [
      {
        "where": { "property": "entity_class", "equals": "mention" },
        "target": "skip_to_done"
      }
    ]
  }
}

Items matching a route rule go to the override target. All others go to the default target. Route rules support:

ConditionExample
equals{ "property": "type", "equals": "image" }
and{ "and": [condition1, condition2] }
or{ "or": [condition1, condition2] }

Recurse (Loop)

Re-invoke the current step with a depth counter:

{ "then": { "recurse": "self_step", "max_depth": 5 } }

The depth increments each iteration. When max_depth is reached, the step terminates.

Done (Terminal)

End the workflow:

{ "then": { "done": true } }

Outputs are recorded as final_output relationships on the job collection.

Creating a Rhiza

POST /rhizai
Content-Type: application/json
Authorization: ApiKey <your-key>

{
  "label": "Document Processing Pipeline",
  "description": "Extract structure, generate descriptions, and index",
  "version": "1.0.0",
  "entry": "split",
  "flow": {
    "split": {
      "klados": { "id": "01K_SPLITTER" },
      "then": { "scatter": "ocr" }
    },
    "ocr": {
      "klados": { "id": "01K_OCR" },
      "then": { "gather": "structure" }
    },
    "structure": {
      "klados": { "id": "01K_STRUCTURE" },
      "then": { "pass": "describe" }
    },
    "describe": {
      "klados": { "id": "01K_DESCRIBE" },
      "then": { "done": true }
    }
  },
  "collection": "01KFNR0H0Q791Y1SMZWEQ09FGV"
}

The rhiza starts in development status. Set it to active when ready:

PUT /rhizai/{id}
Content-Type: application/json
Authorization: ApiKey <your-key>

{
  "status": "active"
}

Validation

Rhizai are validated at creation and update:

  • Entry step must exist in the flow
  • All step targets must reference existing steps
  • All paths must terminate (reach a done step)
  • No cycles allowed
  • No unreachable steps
  • All referenced kladoi must be active with verified endpoints (checked at invocation)

Invoking a Rhiza

Preview

POST /rhizai/{id}/invoke
Content-Type: application/json
Authorization: ApiKey <your-key>

{
  "target_entity": "01KFNR849AZNBWE9DYJRZR7PSA",
  "target_collection": "01KFNR0H0Q791Y1SMZWEQ09FGV",
  "confirm": false
}

Returns a preview showing all kladoi in the workflow and the permissions that will be granted to each.

Execute

POST /rhizai/{id}/invoke
Content-Type: application/json
Authorization: ApiKey <your-key>

{
  "target_entity": "01KFNR849AZNBWE9DYJRZR7PSA",
  "target_collection": "01KFNR0H0Q791Y1SMZWEQ09FGV",
  "confirm": true,
  "expires_in": 3600
}

Response (202 Accepted):

{
  "job_id": "job_01KJ...",
  "job_collection": "01KJ...",
  "status": "accepted",
  "expires_at": "2025-01-15T11:00:00Z"
}

The API:

  1. Creates a job collection for tracking
  2. Grants temporal permissions to every klados in the flow
  3. Invokes the entry klados with the target entity
  4. Returns immediately — execution cascades asynchronously

Example Workflows

Sequential Chain

Two steps in sequence:

{
  "entry": "step_one",
  "flow": {
    "step_one": {
      "klados": { "id": "01K_STAMP" },
      "then": { "pass": "step_two" }
    },
    "step_two": {
      "klados": { "id": "01K_STAMP" },
      "then": { "done": true }
    }
  }
}

The same klados can appear in multiple steps — here the stamp klados runs twice in sequence.

Scatter/Gather

Fan out, process in parallel, reconverge:

{
  "entry": "scatter",
  "flow": {
    "scatter": {
      "klados": { "id": "01K_SPLITTER" },
      "then": { "scatter": "process" }
    },
    "process": {
      "klados": { "id": "01K_PROCESSOR" },
      "then": { "gather": "finalize" }
    },
    "finalize": {
      "klados": { "id": "01K_AGGREGATOR" },
      "then": { "done": true }
    }
  }
}

The splitter produces N outputs. Each output triggers an independent invocation of the processor. When all processors complete, the aggregator runs once with all results.

Conditional Routing

Route items to different steps based on properties:

{
  "entry": "classify",
  "flow": {
    "classify": {
      "klados": { "id": "01K_CLASSIFIER" },
      "then": {
        "scatter": "process_image",
        "route": [
          {
            "where": { "property": "content_type", "equals": "text" },
            "target": "process_text"
          },
          {
            "where": { "property": "content_type", "equals": "skip" },
            "target": "done"
          }
        ]
      }
    },
    "process_image": {
      "klados": { "id": "01K_IMAGE_PROCESSOR" },
      "then": { "done": true }
    },
    "process_text": {
      "klados": { "id": "01K_TEXT_PROCESSOR" },
      "then": { "done": true }
    }
  }
}

The classifier returns outputs with a content_type property. Text items go to the text processor, "skip" items terminate immediately, and everything else (images) goes to the default image processor.

Job Collections & Observability

Every invocation creates a job collection with:

RelationshipPoints toPurpose
first_logEntry point logsWhere execution started
final_outputTerminal success logsSuccessful workflow completions
final_errorTerminal error logsAny failures in the workflow

Each klados writes a klados_log entity to the job collection containing:

  • Status (pending, running, done, error)
  • The klados ID that ran
  • Input parameters
  • Output entity IDs
  • Error details (if failed)
  • Timestamps

Logs chain together via received_from relationships, so you can trace the full execution path from any log back to the entry point.

Checking Workflow Status

Query the job collection's relationships to check completion:

GET /entities/{job_collection_id}?expand=relationships:preview

Look for final_output (success) or final_error (failure) relationships. For detailed execution traces, traverse the log chain starting from first_log.

Permission Actions

ActionDescription
rhiza:createCreate new rhizai
rhiza:viewView rhiza details
rhiza:updateUpdate rhiza configuration
rhiza:invokeInvoke rhiza workflows

On this page