{"slug": "graph-engineering", "title": "Graph Engineering", "summary": "Graph engineering, the practice of designing the structure an AI agent runs inside, is introduced as a solution for complex multi-step tasks that overwhelm single-agent loops. The article defines key terms—harness, loop, and graph—and outlines four core patterns (sequential, branching, parallel, and conditional routing) that cover most production systems. It emphasizes that graphs prevent steps from interfering with each other, improving reliability over monolithic agent prompts.", "body_md": "You have probably built an agent. Maybe it calls a search tool, reasons over the results, formats a response, done. One loop. Works fine. You feel good about it.\n\nThen a coworker drops a harder problem on your desk. Not a single-step retrieval task. Something with moving parts, multiple specialised checks, parallel processing, a human who needs to approve before anything irreversible happens. You try to cram it all into one agent loop. The system starts behaving strangely. It skips steps. It forgets what it was doing three turns back. You add more instructions to the prompt. Things get worse.\n\nThis is the moment graph engineering becomes relevant.\n\nGraph engineering is the practice of designing the *structure* an AI agent runs inside, not the agent itself.\n\nThink of it this way. An agent is like an employee: it has skills, memory, tools, and judgment. Graph engineering is the org chart that defines who that employee talks to, when they talk to them, what information gets passed along, and under what conditions work moves forward versus gets sent back. You are not just hiring smart people. You are designing how the organisation works.\n\nThe nodes are the workers. The edges are the handoffs. The state is the memory shared between them.\n\nBefore the patterns, three terms. Get these right and the rest of the article clicks.\n\nEvery agent sits inside what practitioners call a harness. It is the wrapper around the model itself , the memory it has access to, the tools it can call, and the guardrails that prevent it from doing something catastrophic. The harness is not the agent’s intelligence. It is the agent’s operating environment.\n\nA well-built harness gives the agent exactly what it needs for its specific job and nothing extra. A poorly built harness dumps everything into the context window and hopes for the best.\n\nA loop is the iterative cycle inside a single agent. The agent reads a problem, decides whether to call a tool or produce an answer, acts, observes the result, and goes again. This is the ReAct pattern. It is powerful for focused tasks.\n\nThe key word is *focused*. One agent. One goal. Loop until done.\n\nLoops break down when the problem has distinct phases with different context requirements. Asking one agent to review code for security issues *and* performance issues *and* API design *and* test coverage in one loop is like asking one person to simultaneously be your security auditor, your performance engineer, your API designer, and your QA lead. They cannot fully inhabit all four mental models at once.\n\nA graph is a structured workflow that connects multiple nodes — each node being an agent, a function, or a processing step — with edges that define how work moves between them.\n\nThe simplest graph is one node with an edge looping back to itself. That is just a loop with extra vocabulary. The interesting graphs are the ones with branching, parallelism, synchronisation points, and conditional routing. Those are the patterns below.\n\nThese four patterns cover the overwhelming majority of what real production systems do. Everything complex you will ever build is some combination of these.\n\nThe simplest structure. Node A completes, passes its output to Node B, which completes and passes to Node C. Each step processes the output of the previous one.\n\n```\n[Node A] → [Node B] → [Node C] → Output\n```\n\nWhen do you use this? When a task genuinely benefits from decomposition into sequential steps. A code review pipeline might: first parse the diff, then check it for security issues, then format the findings into a report. Each step has a different job and benefits from receiving a focused, clean input rather than raw everything.\n\nThe critical point here: the graph is not making the steps smarter. It is preventing them from interfering with each other.\n\nA small code example shows the shape clearly:\n\n```\n# Each node does one focused job and returns a state updatedef parse_diff(state: State) -> dict:    parsed = extract_changed_files(state[\"raw_diff\"])    return {\"parsed_diff\": parsed}def check_security(state: State) -> dict:    findings = security_llm.invoke(state[\"parsed_diff\"])    return {\"security_findings\": findings}def format_report(state: State) -> dict:    report = format_llm.invoke(state[\"security_findings\"])    return {\"final_report\": report}\n```\n\nEach function receives the full state and returns only the fields it changed. The graph wires them together. No node needs to know what the others are doing.\n\nThis is where graphs justify their existence.\n\nFan-out takes one task and splits it into multiple parallel sub-tasks that run simultaneously. Join is the synchronisation node that waits for all of them to finish, then merges the results.\n\n```\n┌→ [Security Agent] ─┐              ├→ [Perf Agent]    ──┤Input → Router┤→ [API Agent]     ──┼→ [Join] → Output              ├→ [Test Agent]    ──┤              └→ [Quality Agent] ─┘\n```\n\nThe real-world example from the video is a pull request review system. You could send the same PR diff through a security reviewer, a performance reviewer, and a test coverage reviewer sequentially. Or you fan out to all three simultaneously, wait for all three to finish, and merge the findings. Total time is roughly equal to the slowest single reviewer, not the sum of all three.\n\nThe fan-out pattern in code uses the Send API to dispatch parallel branches:\n\n``` php\nfrom langgraph.constants import Senddef fan_out_to_reviewers(state: State) -> list[Send]:    # Dispatch all reviewers simultaneously    # Each runs independently, returns findings to the same state field    return [        Send(\"security_agent\", state),        Send(\"performance_agent\", state),        Send(\"api_agent\", state),        Send(\"test_agent\", state),    ]\n```\n\nThe join happens automatically when you use an accumulator on the findings field:\n\n``` python\nfrom typing import Annotatedimport operatorclass State(TypedDict):    # operator.add concatenates lists from parallel branches safely    # Without this, the last agent to finish would overwrite all the others    findings: Annotated[list, operator.add]\n```\n\nThat single operator.add annotation is doing serious work. When four agents all return findings simultaneously, LangGraph collects every write and concatenates them instead of letting the last writer win. This is not a coincidence. It is a deliberate design decision you make when you define the state schema.\n\nA router reads the current state and sends work to a different node depending on what it finds.\n\n```\n┌→ [Fixer Agent] → back to startInput → [Reviewer] ─┤                    └→ [Human Review Gate] → Approved → Done\n```\n\nFrom the video: after a code review agent evaluates a pull request, the router checks the outcome. Tests failed? Send it to a fixer agent. Tests passed? Route it to a human for final approval. The reviewer’s output determines where work goes next.\n\nThis is where the distinction between LLM routing and Python routing matters enormously. You could ask the LLM to decide where to send the work. Or you could write a Python function that reads a structured field from the state and returns a node name.\n\n```\n# The routing function is Python — not an LLM calldef route_after_review(state: State) -> str:    if state[\"test_result\"] == \"failed\":        return \"fixer_agent\"      # deterministic, testable, free    elif state[\"risk_level\"] == \"critical\":        return \"human_review\"     # also deterministic    return \"approve_and_merge\"\n```\n\n<cite index=”22–1\">The router’s output should be structured, recorded, and testable rather than buried in a free-form prompt.</cite> LLM routing is non-deterministic and expensive. Python conditional routing is testable in two lines and costs nothing. Production systems move toward Python routing as fast as they can.\n\nThis pattern runs a generator and an evaluator in a loop until the output meets a quality threshold — or until a retry counter says enough.\n\n```\n[Generator] → [Evaluator] → Good? Yes → Done                          → No, here's why → back to [Generator]\n```\n\nThe generator produces something. The evaluator grades it with structured output — not a prose critique, an actual typed judgment with a pass/fail field. If it fails, the feedback flows back into the generator’s next call. If it passes, execution moves on.\n\n```\nclass EvalResult(BaseModel):    passed: bool    feedback: str   # only matters if passed is False\nphp\ndef route_after_eval(state: State) -> str:    if state[\"eval_result\"].passed:        return \"done\"    if state[\"retry_count\"] >= 3:        return \"escalate\"   # never loop forever    return \"generator\"      # try again with feedback\n```\n\nThe retry counter is not optional. Without it, a generator that consistently fails the evaluator runs until you run out of money or patience.\n\nThis trips people up, so worth being direct.\n\n**It is not a knowledge graph.** A knowledge graph is a data structure for representing facts and their relationships — think of a database where entities are nodes and relationships are edges. Neo4j, GraphRAG, graph neural networks, these are knowledge graph tools. Graph engineering is about *execution flow*, not data storage.\n\n**It is not an agent swarm.** An agent swarm is a collection of agents that interact dynamically, often without a predefined topology. Agents negotiate, bid for tasks, and self-organise. Swarms are flexible and adaptable. They are also harder to debug and less predictable.\n\nGraph engineering sits at the other end of that spectrum. You pre-define the nodes, the edges, and the conditions under which work moves. The path is explicit. When something goes wrong, you can trace exactly which node produced the bad output and why.\n\nNeither approach is universally better. Complex open-ended research tasks might benefit from swarm flexibility. A pull request review pipeline that needs to reliably block broken code from merging benefits from graph predictability. Know which problem you have.\n\nThe graph handles the structure. Shared state handles the information.\n\nEvery node in a graph reads from a shared state object and writes updates back to it. This is not just passing variables around. It is the mechanism that lets a fan-out of five agents all contribute their findings to one coherent result, lets a router make a decision based on what three previous nodes discovered, and lets the final report node see everything that happened without any node needing to talk directly to any other.\n\nGoogle’s Agent Development Kit makes this explicit. Each node in a graph can read the shared state for context relevant to its job, write its results back, and the graph engine handles the merging, the ordering, and the persistence.\n\nThe state schema is the most important design decision in the whole system. Designed well, agents collaborate without interfering. Designed poorly — fields that overwrite instead of accumulate, missing fields that agents expect to find, no versioning strategy — and the graph produces subtly wrong outputs that are hard to trace.\n\nHere is the minimal shape every state schema needs:\n\n``` python\nfrom typing import TypedDict, Annotatedimport operatorclass GraphState(TypedDict):    # Overwrite fields: last write wins, only one valid value at a time    status: str    current_task: str    # Accumulator fields: parallel branches append safely    findings: Annotated[list, operator.add]    errors:   Annotated[list, operator.add]\n```\n\nOverwrite fields for things where only one value is valid at a time. Accumulator fields for everything that multiple agents might contribute to simultaneously. The difference between these two is the difference between parallel agents working correctly and parallel agents silently eating each other’s output.\n\nThe video uses this example. It is worth walking through completely because it uses all four patterns in one system.\n\nA developer pushes a pull request. Here is what the graph does:\n\n**Step 1 — Ingestion (Sequential).** A single node parses the diff, extracts the changed files, and detects any function signature changes that might affect external callers. This is preprocessing. No LLM involved. Pure Python.\n\n**Step 2 — Fan-Out (Parallel).** The orchestrator fans out to four or five specialist reviewer agents simultaneously: security, performance, API design, test coverage, code quality. They all read the same parsed diff from state. They all write their findings back to the accumulator field. Total time: the slowest single reviewer, not the sum.\n\n**Step 3 — Join.** Once all reviewers finish, the join node has all findings in one list. It deduplicates, ranks by severity, computes a security score.\n\n**Step 4 — Router.** The router reads the aggregated findings. Critical security issue? Route to a human approval checkpoint using interrupt(). No critical issues, all tests passing? Route to auto-approve.\n\n**Step 5 — Human Gate (Optional).** If routing sent work here, the graph pauses. A human sees the findings. They approve, reject, or ask for changes. The graph resumes exactly where it left off. The PR either merges or goes back to the developer.\n\nEvery piece of that is a pattern you have seen above. Fan-out, join, router, and the human gate is just the interrupt-based HITL from the LangGraph series. The graph is the composition of these patterns, not a new invention.\n\nFrom compiler control-flow graphs to database query plans, graph topology has always been the standard language for describing how work flows.\n\nGraph engineering did not invent nodes and edges. It borrowed them from a tradition that goes back to Dijkstra’s work on compiler optimisation, to RACI matrices in organisational design, to Petri nets in concurrent systems theory. The insight is not structural novelty. It is recognising that the problems practitioners are hitting with AI agents — coordination failures, unpredictable routing, context getting lost between steps, are the same problems that led to those tools in the first place.\n\nWhat changed is that AI agents introduced a new kind of node: one that is probabilistic, context-sensitive, and capable of producing structured outputs from natural language inputs. The old graph machinery needed new defaults. LangGraph, Google ADK, and Microsoft AutoGen are those defaults.\n\nOne speaker in the video raises an interesting question toward the end: are we just reinventing computer science? Stack engineering, hash engineering — will those be next? Maybe. But the more grounding observation is this: every generation of software complexity has produced a new organisational layer. Procedures replaced raw assembly. Objects replaced procedures. Microservices replaced monoliths. Graph engineering is the organisational layer for coordinated AI agents. That it resembles what came before is not a criticism. It is evidence that the problem is real and the solution is recognisable.\n\nIf you are building something with one agent doing one focused job in one loop, graph engineering is overhead you do not need. Keep it simple.\n\nIf your system has genuinely independent sub-tasks that could run in parallel, distinct specialists that each need their own context, conditional paths where different inputs need different handling, or irreversible actions that need a human in the loop, that is when a graph earns its place.\n\nStart with the state schema. Draw the graph on paper before writing code. Identify which nodes accumulate and which overwrite. Then build, one pattern at a time.\n\nThe structure is the system. Get that right and the agents have somewhere sensible to work.\n\n[Graph Engineering](https://pub.towardsai.net/graph-engineering-e49337901a42) was originally published in [Towards AI](https://pub.towardsai.net) on Medium, where people are continuing the conversation by highlighting and responding to this story.", "url": "https://wpnews.pro/news/graph-engineering", "canonical_source": "https://pub.towardsai.net/graph-engineering-e49337901a42?source=rss----98111c9905da---4", "published_at": "2026-09-07 19:31:01+00:00", "updated_at": "2026-09-07 20:00:29.089546+00:00", "lang": "en", "topics": ["ai-agents", "ai-infrastructure", "ai-research"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/graph-engineering", "markdown": "https://wpnews.pro/news/graph-engineering.md", "text": "https://wpnews.pro/news/graph-engineering.txt", "jsonld": "https://wpnews.pro/news/graph-engineering.jsonld"}}