Engineering / Graph workflows

DAGs, loops, and multi-agents: graph engineering vs LangGraph

What a DAG is, what graph and loop engineering are, how graph wiring enables multi-agents, how LangGraph looks in code, and how my Content Shifted canvas fits: durable automation (media first, same shape for ops), loops inside nodes today, room for a conditional agent later.

July 2026 · Architecture · Workflows · Rails · By Alfred Pararajasingam

What a DAG is

A DAG is a Directed Acyclic Graph:

  • Directed: edges have a direction (A → B means A feeds B).
  • Acyclic: following edges never brings you back to an earlier node.
[Scrape] → [Write] → [Image] → [Merge]     ✅ DAG (forward only)

[Write] → [Review] → [Write]              ❌ cycle (not a DAG)

My customer-facing canvas is a DAG: scrape, write, image, merge, and so on. That does not mean the product has no loops. It means canvas edges do not loop. Loop engineering can still live inside a node (tools + QA), or later on the canvas if I wire retry edges and a conditional agent on purpose.

What graph engineering is

Graph engineering means building workflows as nodes and edges instead of one long script. The runtime’s job is to honor the wiring: run ready nodes, pass outputs along edges, wait when inputs are incomplete, and leave a trail you can inspect.

Every graph runtime needs roughly the same pieces:

  1. Nodes: units of work (in my app: automations, files, knowledge, templates).
  2. Edges: directed links (durable TransformationAutomationChain rows).
  3. State: what the run mutates (I use files, tags, content states; LangGraph often uses a state dict).
  4. Runner: who executes the next ready node (Sidekiq jobs vs in-process walk).
  5. Barriers: fan-in waits (merge / lipsync until enough layers exist).
  6. Run identity: an execution id and history so you can replay what was planned.

Today the platform does not invent new canvas edges at runtime. Humans or presets draw them; jobs follow them when inputs are eligible.

What loop engineering is

Loop engineering is designing a step that may run more than once before it finishes: call a model, use a tool, check the result, and either stop or go around again. The control flow is a cycle (with a budget), not a single straight call.

loop (while steps < max):
  turn = llm.chat(history, tools=allowlist)
  if turn.tool_calls:
    run tools → append results → continue
  else if qa.reject?(artifact):
    append feedback → continue
  else:
    return artifact   # done

Typical pieces:

  • Allowlisted tools the model may call (image, overlay, compose, HTTP, ticket API).
  • Step / token / credit budget so the loop cannot run forever.
  • Stop conditions: model says done, QA passes, or budget exhausted.
  • Optional critique: vision or text QA that sends the model around again.

In Content Shifted, that is Ai::Agent::Runner inside agentic_transform. Graph engineering wires which nodes run in what order; loop engineering decides how one of those nodes produces its output.

How graph engineering enables multi-agents

A single agent loop is one specialist. Multi-agent means more than one role collaborating. Graph engineering is how those roles stay separate and still cooperate: each agent (or agentic node) is a node; edges define who hands what to whom.

[Researcher agent] → [Copywriter agent] → [Reviewer agent] → [Poster]
        │                    │                    │
   tools: search         tools: write         tools: critique
   knowledge retrieve    brand voice          pass/fail signal

Why the graph helps:

  • Clear roles. Researcher, writer, reviewer, and ticket-filer do not share one mega-prompt; each node has its own prompt, tools, and budget.
  • Shared artifacts, not shared brains. Agents exchange files/tags (or later a ticket id), so the next agent starts from a durable handoff, not a hidden chat transcript.
  • Parallel specialists. Fan-out edges let two agents work at once (e.g. script + B-roll), then a barrier merges results.
  • Swap one agent without rewriting all. Replace the reviewer node or its model; upstream and downstream edges stay.
  • Mix strict steps and agents. A non-agent “Create GitHub issue” node can sit after an agentic triage node. Multi-agent does not require every box to be an LLM.
# Multi-agent on the same durable graph (examples)

[Sentry webhook] → [Triage agent] → [Draft issue agent] → [Create GitHub ticket]
[Website knowledge] → [Writer agent] → [Brand reviewer agent] → [Image] → [Merge]
[Job listing] → [Fit agent] → [Cover-letter agent] → [Human approve]

Loop engineering makes each agent good at iterating. Graph engineering makes many agents a pipeline (or a DAG with joins). Without the graph, multi-agent collapses into one brittle chat; without loops, each agent is a single shot.

What LangGraph is (with pseudocode)

LangGraph is a library for building stateful graphs in code (usually Python). You write backend functions for nodes, wire edges (including conditionals and cycles), and call AI or tool APIs inside those functions. The library runs the graph.

# Pseudocode: LangGraph-style agent graph

State = { draft, status }    # writing | review | done

function write(state):
  draft = openai.chat("Write a tip card", state)
  return { draft: draft, status: "review" }

function review(state):
  ok = openai.chat("Approve this draft?", state.draft)
  if ok:
    return { status: "done" }
  else:
    return { status: "writing" }    # will loop back

graph.add_node("write", write)
graph.add_node("review", review)
graph.add_edge("write", "review")

# Conditional edge: runtime chooses next node from state
graph.add_conditional_edges("review",
  status == "done" ? END : "write")

graph.compile().invoke({ draft: null })

That is still “backend code calling AI APIs,” plus an explicit graph for control flow. Conditionals and cycles live on the same graph as the work.

What I built instead

I put the product graph in Postgres and ran it with Sidekiq so workflows are durable, multi-tenant, and restart-safe. The first product surface is media (scrape → write → image → merge → post), but the same graph engine is not limited to creatives. Any workflow that is “event or file in → steps → artifact or side effect out” can sit on the canvas:

  • Content pipelines (tip cards, ads, app-review video, blogs).
  • Ops workflows (e.g. Sentry error in → classify / summarize → open a GitHub ticket).
  • Application or intake flows (form or email in → extract → draft reply or ticket).
  • Other agentic chains where nodes call tools or external APIs and pass files/tags downstream.

What that engine always has:

  • Durable edges + jobs (survives deploys and workers).
  • Shared progress the next node can read (files, tags, content states, or similar records).
  • Visual canvas + workflow presets for authoring.
  • Credits, polymorphic node types, execution snapshots.
Canvas edges (Postgres)
  → Sidekiq job per automation
    → Transform::Execute → persist outputs / side effects
      → waiting? stop
      → else advance eligible downstream

Optional agentic node:
  Ai::Agent::Runner  (tool loop + QA inside one automation)

Examples of the same shape:
  [Website] → [Write] → [Image] → [Merge] → [Post]
  [Sentry]  → [Triage] → [Draft issue] → [Create GitHub ticket]
  [Inbox]   → [Extract] → [Draft application] → [Human review]

I borrowed LangGraph’s mental model (nodes, edges, joins, run identity), not the Python runtime. Media is how the product ships today; the graph is a general automation substrate.

Loops and conditionals: today vs possible

Today: the outer canvas is a forward DAG. Loop engineering lives inside nodes like agentic_transform: the model picks tools at runtime (text_to_image, overlay, compose) and vision QA can send it around again. That is real dynamic control flow, just not as new canvas edges.

CANVAS (fixed DAG):     … → [agentic_transform] → [merge] → …
INSIDE THE NODE:        LLM ⇄ tools ⇄ QA          ← loops here

That is still graph engineering. Nested loops make a richer node; they do not stop the outer wiring from being a graph.

Possible later: if I wire the DAG with retry paths and a conditional agent (or Router) that chooses which labeled edge to follow, the canvas becomes much more LangGraph-like:

[Write] → [Review] → [Conditional agent / Router]
                              ├─ continue → [Image] → [Merge]
                              └─ retry    → [Write]     ← canvas cycle, on purpose

So “DAG only” is a choice about where policy lives today, not a claim that loops cannot exist. Same platform, different graph shape: put if/loop inside a specialist node, or expose it on the canvas with a conditional agent and guarded retry edges.

Side by side

LangGraph Content Shifted (today)
Primary job Coded agent/state graphs Multi-tenant automation DAG across jobs (media + other workflows)
Authoring Python (or JS) code Visual canvas + presets
Outer edges Static, conditional; cycles common Static durable edges; forward DAG
Loops Often cycles on the same graph Loop engineering inside nodes (tools/QA); canvas cycles possible later with a conditional agent + retry edges
State Typed graph state Files, tags, content states
Runner In-process (+ checkpoints) Sidekiq + Postgres

LangGraph taught me how to talk about stateful graphs. Content Shifted forced durable edges, billing, and a builder customers can trust, with room to grow conditionals onto the canvas when the product needs visible policy, not only nested agent loops.

Keep reading

How one agentic node runs a tool loop, and how actions stay wrap-friendly for canvas and MCP.