PTENES
Skip to content
MODULE 4.2

🏢 Org graph × work graph

Two graphs coexist in the same agent system, and confusing them is the most common cause of a mess. One stays — and the org chart. The other one passes—and that's the today’s request. This module separates the two and shows how they come together: zone defense, fan-out/fan-in, and conditional routing.

6
Topics
40
Minutes
Intermediate
Level
Practice
Type
Progress in this module
0%0 of 6
1

🏛️ Separate the org chart from the work

Two graphs coexist in the same agent system, and they are not the same graph. The org graph is the lasting structure: long-lived agents with named roles —researcher, writer, validator, publisher—each owning a domain, with accumulated memory and stable edges that change only when you redeploys the system.

Think of it as the company org chart, except each box is an agent running its own loop. The org graph answers “who exists and what each one does” — not “what are we doing now?” That second question belongs to another graph, which you’ll see in the next topic.

🆕 New here? Four words before you continue

  • Org graph: the stable structure of long-running agents—the org chart. It changes only when you redeploy the system.
  • Work graph: the graph built for a specific task. It’s created, run, and discarded.
  • Redeploy: redeploy — publish a new version of the system. This is the only point when the org graph changes shape.
  • Deterministic: same input, always the same output. The opposite of "it depends on the model's luck that day."
ORG GRAPH — the org chart researcher writer validator publisher stays — changes only on redeploy WORK GRAPH — today's request researcher validator end changes — with every request

What to look at: the dotted divider in the middle doesn’t separate two systems—it separates two perspectives of the same system. Notice that “researcher” and “validator” appear on both sides: they’re the same agents. On the left, you see all the possible relationships between them (the org chart). On the right, you see only the narrow path that today’s task actually followed—a subset assembled on the fly.

✓ It's an org graph when

  • ✓The agent has a fixed name and role; it doesn’t change from task to task
  • ✓It accumulates memory — it knows things it learned in past runs
  • ✓The edge represents "can ask for"—a long-term relationship

✗ It’s not an org graph when

  • ✗You're designing the steps of a single task — that's a work graph
  • ✗The "structure" changes every time a different request comes in
  • ✗There is no accumulated memory, only the context of that run

Key concepts

Org graph

The org chart, stable

Long life

Agent with a named role

Accumulated memory

Learns between executions

Changes on redeploy

Doesn't change at runtime

2

📋 Build today's request work graph

O work graph is ephemeral: assembled for a specific task and discarded afterward. It changes with each run—today it calls for a researcher and validator, tomorrow just the writer, then all four in a row. There’s no “the” work graph for the system; there’s a new work graph for every incoming request.

💡 The most common confusion in a messy system

Confusing the two graphs is the most common cause of a messy system. Some people recreate the entire organization with every request — instantiate a new agent, redefine roles, redesign the whole structure each time a request comes in. Others do the opposite: they lock the task into the org chart, treating every variation in a request as if it needs to become a new, permanent agent.

  • •Recreating the organization for every request is expensive and slow, and you lose the accumulated memory that made the agent good.
  • •Hardwiring the task into the org chart = the system becomes rigid, unable to handle a request that doesn’t follow the planned script.
1

Request arrives

A user or external system triggers a task. The work graph doesn’t exist yet.

2

The router chooses who participates

Look at the available org graph and select a subset of agents for this specific task.

3

A work graph runs and finishes

The task’s fine-grained path runs from start to finish, delivers the result, and is discarded.

4

Org graph remains intact

The agents that participated continue to exist, ready for the next work graph—with the memory they have accumulated.

Key concepts

Work graph

Ephemeral, one per request

Disposable

It starts, runs, disappears

Subset

Uses only part of the org graph

Common mistake

Confusing it with the org chart

3

🛡️ Apply zone defense

Zone defense is the pattern that makes an org graph worthwhile: each agent owns a domain, with persistent context. The security agent handles authentication, permissions, and audit logs. The data agent handles schemas, migrations, and query standards. The API agent handles endpoints, rate limits, and contracts. The frontend agent handles components, state, and interface standards.

Each one runs its own loop within the zone. A request from one zone to another goes through the work graph—never directly. No agent leaks into another’s context. That’s what makes a long-lived agent accumulate real value: a security agent that has already reviewed 300 changes implicitly knows that code’s threat model, without needing anything re-explained with each request.

📊 The four classic zones

  • Security — authentication, permissions, audit logs
  • Data — schema, migrations, query patterns
  • API — endpoints, rate limits, contracts
  • Frontend — components, state, interface patterns

🆕 New here? Zone defense

Zone defense: a term borrowed from sports—each player covers an area of the field, not a specific person on the opposing team. In graph engineering, each agent covers a domain of the system, not a specific task. It defends the entire zone, task after task.

✓ Zone defense works when

  • ✓The domain has a clear boundary (data isn’t API, and API isn’t frontend)
  • ✓Handoffs between zones become an explicit edge in the work graph
  • ✓Each agent accumulates real context (decision history, not just instructions)

✗ When zone defense breaks

  • ✗An agent reads or writes directly in another zone’s context (leakage)
  • ✗The zones overlap—two agents “owning” the same domain
  • ✗A zone is left ownerless and becomes a no-man’s-land

Key concepts

Zone defense

One owner per domain

Persistent context

Doesn't reset with every task

No leakage

Handoff = explicit edge

Implicit value

Accumulated experience counts

4

🔀 Create a real fan-out and fan-in

Fan-out is to open a task into N branches that run at the same time. Fan-in is to bring the N results back together into one. It’s something a loop simply doesn’t do, since a loop is sequential by nature—it processes one thing at a time, always.

The join is where the danger lies. A faulty merge silently swallows a branch, and the result looks complete even when something is missing. That’s why every join needs to check cardinality: did I receive the N I dispatched?

🆕 New here? Fan-out, fan-in, cardinality

  • Fan-out: one node dispatches work to several parallel branches at the same time.
  • Fan-in: the parallel branches converge back into a single join node.
  • Cardinality: the count—how many items went out versus how many came back. If you dispatched 5 and received 4, the cardinality doesn’t match.
source ✗ junction I received 4 of 5 fan-out into 5 branches · 1 branch fails silently · fan-in without checking cardinality doesn't notice

What to look at: four pulsing branches arrive cleanly at the join; the fifth, marked with a red X, never arrives. If the join doesn’t count how many branches it dispatched versus how many it received, it will accept “I received 4 of 5” as if it were a complete result. This is the merge that swallows a source — the result looks ready, and no one notices a piece is missing.

⚠️ Warning: join without a cardinality check

Whenever you write a fan-in, explicitly ask “how many did I expect?” before moving on. If the answer doesn’t match what arrived, stop and handle the case—don’t let the work graph continue as if it were complete. A branch that fails silently is worse than one that fails loudly.

Key concepts

Fan-out

A node becomes N branches

Fan-in

N branches become one node

Cardinality

Did I receive the N I dispatched?

Danger

A merge that silently swallows

5

🚦 Route by condition

With conditional routing, the edge chooses the destination based on the previous node's result. It's the mechanism that lets the work graph “decide” without an agent having to interpret everything again at each step. The classic example is customer support.

Low complexity goes to the canned response base (FAQ). Medium complexity goes to the AI agent. High or risky complexity escalates directly to a human. There is also a "not resolved" edge (returns to the next level) and a learning edge (returns to the classifier with feedback).

Customer support diagram: the customer sends a message, and a classification node routes it by complexity — low complexity goes to the FAQ, medium to the AI agent, and high-risk or complex cases escalate directly to a human in red. Dashed if-not-resolved arrows descend from the FAQ to the AI agent and from there to the human, all converging on a solution. A dashed green learning arrow leads from feedback back to classification.
What to look at: the legend distinguishes four types of edges — main flow (classification → FAQ/AI/human), unresolved (dashed, moves up one level), escalation (red, goes straight to a human), and learning (green, loops back to the classifier). The green learning arrow is the single that closes a cycle — it’s what makes the system improve over time instead of just working the same way forever.

🎯 Why the learning edge matters more than it seems

The first three edges (primary, if-unresolved, escalation) make the system work today. Only the green edge makes the system work better tomorrow — because it’s the only one that returns information to the node that decides. Without it, the classifier keeps making the same mistake forever; with it, every human correction becomes a signal for the next routing decision.

Key concepts

Condition-based routing

An edge chooses based on the result

Scaling

High risk goes straight to a human

If-not-resolved

Move up to the next level

Learning

The only edge that closes the cycle

6

⚙️ Let Code Decide What’s Predictable

Model routing what one if resolve is expensive, slow, and nondeterministic. The division that works: code controls predictable routing; the model handles steps that require interpretation or judgment. Every edge you can turn into a written rule is an edge that stops costing tokens and varying between runs.

✓ Leave it to the code

  • ✓"If complexity == low, route to the FAQ" — an explicit rule with no ambiguity
  • ✓"If cardinality doesn't match, stop and alert" — a mechanical check
  • ✓"If category == payment, route to the finance agent" — fixed mapping

✗ Leave it to the model

  • ✗"Does the message's tone suggest frustration?" — requires interpretation, not a rule
  • ✗"Does this answer actually answer the question?" — a quality judgment
  • ✗"What's the real intent behind this ambiguous sentence?" — interpretation

🧪 Practical example: support routing graph in JSON

Objective: write the support routing graph with explicit conditional edges, escalation and unresolved edges, a learning edge — and a join that checks cardinality. Run it in the browser console (F12 → Console) or in Node.

// 1) O grafo de roteamento: cada aresta tem uma condicao explicita
const roteador = {
  "classificacao": [
    { "para": "faq",         "quando": "complexidade == 'baixa'" },
    { "para": "agente_ia",   "quando": "complexidade == 'media'" },
    { "para": "humano",      "quando": "complexidade == 'alta' || risco == 'alto'" }
  ],
  "faq":        [ { "para": "agente_ia", "quando": "resolvido == false" } ],
  "agente_ia":  [ { "para": "humano",    "quando": "resolvido == false" } ],
  "humano":     [ { "para": "classificacao", "quando": "feedback_dado == true", "tipo": "aprendizado" } ]
};

// 2) Juncao com checagem de cardinalidade (fan-in de N tickets em lote)
function fanIn(despachados, recebidos) {
  if (recebidos.length !== despachados.length) {
    throw new Error(`cardinalidade nao bate: recebi ${recebidos.length} de ${despachados.length}`);
  }
  return recebidos;
}

// 3) Rota um ticket de exemplo
function roteia(no, ctx) {
  const arestas = roteador[no] || [];
  for (const a of arestas) {
    // avaliacao simplificada da condicao 'quando' contra o contexto
    if (eval(a.quando.replace(/complexidade/g, `'${ctx.complexidade}'`).replace(/risco/g, `'${ctx.risco}'`).replace(/resolvido/g, ctx.resolvido).replace(/feedback_dado/g, ctx.feedback_dado))) {
      return a.para;
    }
  }
  return null; // nenhuma condicao casou -- pedido some, ver "como verificar"
}

console.log(roteia("classificacao", { complexidade: "media", risco: "baixo" })); // "agente_ia"

How to verify:

  • 1. Every edge leaving a decision node (classificacao) has quando — no edge “changes” without a written condition.
  • 2. The conditions cover all the cases — test roteia("classificacao", <contexto sem match>); if it returns null, there’s an entry that doesn’t match any condition, and the request silently disappears.
  • 3. There is a path to a human from any node — follow the edges of faq e agente_ia and confirm that both reach humano when resolvido == false.

Now swap it for yours: replace the nodes with <etapas do seu suporte> is the conditions under which <campos reais do seu ticket>. Run the test from item 2 before deploying to production.

Quick check (doesn’t block anything): a fan-in received 3 responses from a fan-out that dispatched 5 branches and continued as if it were complete. What was the error?

Key concepts

Code routes

What is a rule becomes an `if`

Model judges

What requires interpretation

Full coverage

Every input has a destination

Lower cost

A written rule costs no tokens

📌 Module Summary

✓
Org graph = org chart — long-lived agents, named roles, changes only on redeploy.
✓
Work graph = today's task — ephemeral, assembled and discarded with every request.
✓
Zone defense — each agent owns a domain, with persistent context that doesn’t leak to the others.
✓
Fan-out/fan-in — open N parallel branches and join them; every join checks cardinality.
✓
Condition-based routing — the edge chooses based on the node’s result; the learning edge is the only one that closes a cycle.
✓
Code vs. model — what’s a rule becomes an `if`; what requires judgment stays with the model.

Next Module:

4.3 — Failures, harness, and observability: a merge that swallows sources, routing that doesn’t converge, and a monitor showing who ran what.