Hierarchical Composition

This is hypergraph's core insight: real AI workflows naturally nest DAGs inside cycles and cycles inside DAGs. Hierarchical composition makes this explicit and clean.

The Pattern

1. Build a graph for one task
2. Use it as a node in a larger graph
3. The larger graph can be used as a node in an even larger graph
4. Repeat at any depth
# A graph...
rag = Graph([embed, retrieve, generate], name="rag")

# ...becomes a node
workflow = Graph([
    validate,
    rag.as_node(),  # Graph as a single node
    format_output,
])

Why This Matters

You don't build one graph and stop. You build graphs, compose them, and reuse them in many contexts:

Context
The Same Graph Used As...

Inference

Direct execution for user queries

Evaluation

A node inside a test harness

Optimization

A component in a prompt tuning loop

Batch processing

Mapped over a dataset

Build once. Use everywhere.


Example 1: DAG Inside a Cycle (Multi-Turn RAG)

A multi-turn conversation is cyclic — the user can keep asking follow-up questions. But retrieval within each turn is a DAG.

The structure:

The RAG pipeline runs to completion on each turn. The outer loop decides whether to continue.


Example 2: Cycle Inside a DAG (Evaluation Harness)

Now flip it: your cyclic conversation graph becomes a node inside an evaluation DAG.

The structure:

Same graph, different context. In inference, conversation handles live users. In evaluation, it's a component being tested.


Example 3: Prompt Optimization (Multiple Nesting Levels)

Context engineering and prompt optimization involve nested loops:

Three levels of nesting:


Think Singular, Scale with Map

Another dimension of hierarchy: write logic for one item, scale to many.

Why this works:

  • No batch loops in your code

  • Each function is testable with a single input

  • The framework handles fan-out, parallelism, and caching

This combines with hierarchical composition:


The .as_node() API

Convert any graph to a node:

Key properties:

  • The nested graph runs to completion before the outer graph continues

  • Inputs and outputs are determined by the nested graph's InputSpec

  • Type annotations flow through for strict_types validation


When to Use Hierarchical Composition

Use Case
Pattern

Reusable components

Build once, .as_node() everywhere

Testing complex flows

Test the inner graph independently

Evaluation harnesses

Wrap production graph in test DAG

Multi-agent systems

Each agent is a graph, orchestrator composes them

Prompt optimization

Nested loops for run → evaluate → improve

Batch processing

.as_node().map_over(...) for fan-out

What's Next?

Last updated