AssayAssay
02 / 08

Lesson 02 · Foundations · 12 min

Nodes and edges

A graph runtime is smaller than its reputation. Visit a node, run it, update state, choose an outgoing edge, repeat until you reach an end node or run out of steps. The version powering this lab is about a hundred and twenty lines.

while (current && steps < maxSteps) {
  const node = graph.nodes.find((n) => n.id === current);
  await runNode(node, state);

  const outgoing = graph.edges.filter((e) => e.from === node.id);
  const next = node.kind === "router"
    ? outgoing.find((e) => matches(e.when, state)) ?? outgoing.find((e) => !e.when)
    : outgoing[0];

  current = next?.to ?? null;
}

Worth knowing before adopting a framework: most of what they add is ergonomics, typing and persistence rather than a different execution model. Understanding the loop above means you can read what a framework is doing and decide whether you need it.

Four node kinds cover most workflows. An llm node calls a model. A tool node calls a function. A router node reads state and picks an edge. An end node stops. This lab adds a fifth, interrupt, covered in lesson 5.

The step limit matters as much as it does in a loop. A graph with a cycle and no ceiling has the same failure mode as an unbounded loop, and the fix is identical.

Checkpoint

Trace one run and write down the sequence of node ids it visited.

A router reads the classification and sends the message down a path suited to it. Shipping questions get an order lookup; refunds get the policy as well. The route taken is a fact you can see, not a hope.

“My order 88213 arrived with a cracked base. I'd like a refund please.”

Run the routed graph and read the trace. Every line is one node visit; the arrow on the right is the edge it chose.

Graph structure

ClassifyRouteExtract order numberLook up orderRoute againRead refunds policyReply about deliveryReply about a refundReply generallySend

Pick a graph and a message, then press Run — the nodes it visits light up.