<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tim Alam</title>
    <description>The latest articles on DEV Community by Tim Alam (@ti_alam_36c71d4e5872).</description>
    <link>https://dev.to/ti_alam_36c71d4e5872</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3837300%2F5afd9a19-b987-40db-9ffc-4ac06c7476e1.jpeg</url>
      <title>DEV Community: Tim Alam</title>
      <link>https://dev.to/ti_alam_36c71d4e5872</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ti_alam_36c71d4e5872"/>
    <language>en</language>
    <item>
      <title>Multi-Agent Mess: Why Complex AI Workflows Belong on an Event-Driven Layer</title>
      <dc:creator>Tim Alam</dc:creator>
      <pubDate>Wed, 24 Jun 2026 07:58:09 +0000</pubDate>
      <link>https://dev.to/ti_alam_36c71d4e5872/multi-agent-mess-why-complex-ai-workflows-belong-on-an-event-driven-layer-112j</link>
      <guid>https://dev.to/ti_alam_36c71d4e5872/multi-agent-mess-why-complex-ai-workflows-belong-on-an-event-driven-layer-112j</guid>
      <description>&lt;p&gt;If you’ve spent any time building &lt;a href="https://www.dnotifier.com/" rel="noopener noreferrer"&gt;multi-agent AI systems&lt;/a&gt; lately, you’ve probably hit a wall very hard.&lt;/p&gt;

&lt;p&gt;It always starts with high hopes. You want an agent to plan, another to research, a third to write, and a fourth to critique.&lt;/p&gt;

&lt;p&gt;To keep them from running wild, you look for structure. You find frameworks like LangGraph, and they make total sense on paper. You get nodes, cycles, persistent state, memory, and human-in-the-loop interventions. You map out the perfect state machine.&lt;/p&gt;

&lt;p&gt;Then you deploy it, and the real-world performance metrics come back.&lt;/p&gt;

&lt;p&gt;The Hidden Bottleneck: State vs. Delivery&lt;br&gt;
Here is the quiet frustration nobody warns you about when building complex agentic graphs: Orchestration frameworks are great at defining logic, but they are heavy on the wire.&lt;/p&gt;

&lt;p&gt;When an agent needs to loop back to a previous state, update a shared context, or stream an execution step to a frontend UI, a traditional framework has to manage all that state transitions in memory or force you to configure external databases and complex WebSockets yourself.&lt;/p&gt;

&lt;p&gt;As soon as you add human-in-the-loop approvals or multi-agent parallel processing, your clean Python or TypeScript graph starts dragging.&lt;/p&gt;

&lt;p&gt;The system becomes tightly coupled. Your agents spend half their time waiting for the central orchestrator to process the next state transition, pass the payload, and update the stream.&lt;/p&gt;

&lt;p&gt;The Graph is Just a Series of Events&lt;br&gt;
A few months ago, we started looking at this problem from a completely different angle.&lt;/p&gt;

&lt;p&gt;We asked: What if the features that make tools like &lt;a href="https://www.dnotifier.com/" rel="noopener noreferrer"&gt;LangGraph&lt;/a&gt; great—state management, cyclical loops, memory, and branching logic—didn't live inside a rigid application framework?&lt;/p&gt;

&lt;p&gt;What if they lived natively inside a high-speed, real-time communication layer?&lt;/p&gt;

&lt;p&gt;When you break it down, every transition in an agentic graph is just an event.&lt;/p&gt;

&lt;p&gt;"Agent A finished researching" is an event.&lt;/p&gt;

&lt;p&gt;"Human rejected the draft" is an event.&lt;/p&gt;

&lt;p&gt;"Loop back to step two" is a conditional event.&lt;/p&gt;

&lt;p&gt;If your underlying architecture is a distributed pub/sub network that natively understands state, you don't need a massive framework to act as the traffic cop.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://www.dnotifier.com/" rel="noopener noreferrer"&gt;DNotifier&lt;/a&gt;: &lt;a href="https://www.dnotifier.com/" rel="noopener noreferrer"&gt;Building Stateful Graphs&lt;/a&gt; on a Live Network&lt;br&gt;
This realization is exactly why we expanded &lt;a href="https://www.dnotifier.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;DNotifier&lt;/strong&gt;&lt;/a&gt;. We didn’t just want to build a fast messaging tool; we wanted to build the native infrastructure for stateful, &lt;a href="https://www.dnotifier.com/" rel="noopener noreferrer"&gt;real-time AI workflows&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Instead of writing complex, heavy framework code to manage your agent loops, you build your nodes and edges directly on top of a distributed messaging layer.&lt;/p&gt;

&lt;p&gt;Because DNotifier natively supports persistent state, memory tracking, and streaming, you get all the heavy-duty features of an agentic graph—loops, conditional routing, human-in-the-loop pauses—without the architectural bloat.&lt;/p&gt;

&lt;p&gt;What This Changes for AI Developers&lt;br&gt;
When your agentic graph lives on an event-driven distributed architecture, the entire development experience shifts:&lt;/p&gt;

&lt;p&gt;Zero-Friction Streaming: You don’t have to write custom adapters to stream an agent's internal thought process to the user. The moment an agent publishes its state to a channel, the UI and the next agent receive it simultaneously.&lt;/p&gt;

&lt;p&gt;True Decoupling: Want to swap out your "Evaluator" agent from GPT-4o to a local Llama model? You don’t have to rewrite your graph definition. You just point the new agent to subscribe to the evaluation channel.&lt;/p&gt;

&lt;p&gt;Native Human-in-the-Loop: Pausing an execution path for human approval usually requires complex state-saving logic. With an event-driven architecture, the workflow simply pauses on a conditional subscription until a "Human_Approved" event is fired from your frontend dashboard.&lt;/p&gt;

&lt;p&gt;The Shift from Orchestration to Choreography&lt;br&gt;
Frameworks like LangGraph taught the industry a valuable lesson: complex AI needs structure, cycles, and memory. They proved that linear pipelines aren't enough for true AI intelligence.&lt;/p&gt;

&lt;p&gt;But the next step isn't making our application frameworks heavier. It's making our infrastructure smarter.&lt;/p&gt;

&lt;p&gt;By moving the graph logic out of the code and onto a distributed, real-time communication layer, you stop fighting framework limitations. You get the cyclical control, the robust state, and the deep memory your agents need—while keeping your application incredibly fast, modular, and human-scale.&lt;/p&gt;

&lt;p&gt;The Takeaway: Don't build a rigid cage for your agents to live in. Build a fast, stateful network, and let them cooperate naturally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code contrast between a framework graph and an event-driven graph&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Here is how the architecture shifts when you move from a centralized orchestration framework to a event-driven communication layer.To illustrate a classic loop (Agent $\rightarrow$ Tool $\rightarrow$ Agent), here is the side-by-side comparison of Python workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Centralized Graph Approach:&lt;/strong&gt; &lt;br&gt;
LangGraphIn LangGraph, you define a strict, centralized state schema, explicitly hardcode nodes and conditional routing logic, and compile it into a singular runtime executable.&lt;/p&gt;

&lt;p&gt;`from typing import TypedDict, List&lt;br&gt;
from langgraph.graph import StateGraph, END&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Define the rigid global state
&lt;/h1&gt;

&lt;p&gt;class WorkflowState(TypedDict):&lt;br&gt;
    messages: List[str]&lt;br&gt;
    status: str&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Define the explicit node logic
&lt;/h1&gt;

&lt;p&gt;def planner_agent(state: WorkflowState):&lt;br&gt;
    print("Planner is thinking...")&lt;br&gt;
    # Process LLM logic&lt;br&gt;
    return {&lt;br&gt;
        "messages": state["messages"] + ["Planner: Let's run a web search."], &lt;br&gt;
        "status": "call_tool"&lt;br&gt;
    }&lt;/p&gt;

&lt;p&gt;def tool_node(state: WorkflowState):&lt;br&gt;
    print("Executing tool...")&lt;br&gt;
    # Process Tool logic&lt;br&gt;
    return {&lt;br&gt;
        "messages": state["messages"] + ["Tool: Found 3 relevant articles."], &lt;br&gt;
        "status": "re_evaluate"&lt;br&gt;
    }&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Define the routing controller logic
&lt;/h1&gt;

&lt;p&gt;def router(state: WorkflowState):&lt;br&gt;
    if state["status"] == "call_tool":&lt;br&gt;
        return "tool"&lt;br&gt;
    return END&lt;/p&gt;

&lt;h1&gt;
  
  
  4. Construct and compile the centralized graph
&lt;/h1&gt;

&lt;p&gt;workflow = StateGraph(WorkflowState)&lt;br&gt;
workflow.add_node("planner", planner_agent)&lt;br&gt;
workflow.add_node("tool", tool_node)&lt;/p&gt;

&lt;p&gt;workflow.set_entry_point("planner")&lt;br&gt;
workflow.add_conditional_edges("planner", router)&lt;br&gt;
workflow.add_edge("tool", "planner") # Loop back to planner&lt;/p&gt;

&lt;p&gt;app = workflow.compile()&lt;/p&gt;

&lt;h1&gt;
  
  
  To stream this to a frontend UI, you now need to set up
&lt;/h1&gt;

&lt;h1&gt;
  
  
  custom WebSocket streaming handlers around the &lt;code&gt;app.stream()&lt;/code&gt; generator.`
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;The Event-Driven Choreography Approach: DNotifier&lt;/strong&gt;&lt;br&gt;
With DNotifier, there is no central "graph" object or traffic cop compiler. Your agents and tools are completely decoupled microservices or functions. They maintain state continuity by emitting events over a fast, real-time messaging fabric.&lt;/p&gt;

&lt;p&gt;`from dnotifier import DNotifierClient&lt;/p&gt;

&lt;h1&gt;
  
  
  Initialize the real-time communication layer
&lt;/h1&gt;

&lt;p&gt;dn = DNotifierClient(api_key="dn_live_secret_key")&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Planner Agent listens for execution requests
&lt;/h1&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/dn"&gt;@dn&lt;/a&gt;.subscribe("agent.planner.run")&lt;br&gt;
def handle_planning(event):&lt;br&gt;
    state = event.payload  # Current state travels seamlessly via the event&lt;br&gt;
    print("Planner is thinking...")&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;state["messages"].append("Planner: Let's run a web search.")

if needs_tool_validation(state):
    # Simply publish an event. Anyone listening (Tool, UI, Logs) catches it.
    dn.publish("agent.tool.execute", payload=state)
else:
    dn.publish("workflow.complete", payload=state)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h1&gt;
  
  
  2. Tool Node acts completely independently
&lt;/h1&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/dn"&gt;@dn&lt;/a&gt;.subscribe("agent.tool.execute")&lt;br&gt;
def handle_tool(event):&lt;br&gt;
    state = event.payload&lt;br&gt;
    print("Executing tool...")&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;state["messages"].append("Tool: Found 3 relevant articles.")

# Loop back naturally by publishing back to the planner channel
dn.publish("agent.planner.run", payload=state)`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Try &lt;a href="//www.dnotifier.com"&gt;DNotifier&lt;/a&gt; today and get free support from AI architects from DNotifier.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>softwareengineering</category>
      <category>webdev</category>
    </item>
    <item>
      <title>No More LangGraph — Build Your Own Agentic Graph</title>
      <dc:creator>Tim Alam</dc:creator>
      <pubDate>Sat, 21 Mar 2026 16:16:42 +0000</pubDate>
      <link>https://dev.to/ti_alam_36c71d4e5872/no-more-langgraph-build-your-own-agentic-graph-1mfa</link>
      <guid>https://dev.to/ti_alam_36c71d4e5872/no-more-langgraph-build-your-own-agentic-graph-1mfa</guid>
      <description>&lt;p&gt;There’s always a moment that comes up in almost every serious AI project.&lt;/p&gt;

&lt;p&gt;To be honest, at first everything feels simple.&lt;/p&gt;

&lt;p&gt;Like you wire up a model, add a tool or two, maybe introduce a planner.&lt;br&gt;
It works. It even feels elegant.&lt;/p&gt;

&lt;p&gt;Then the system grows!&lt;/p&gt;

&lt;p&gt;You add another agent.&lt;br&gt;
Then another.&lt;br&gt;
Then something needs memory.&lt;br&gt;
Then something needs to interrupt a workflow halfway through.&lt;/p&gt;

&lt;p&gt;And suddenly, what started as a clean “agent flow” turns into something harder to reason about.&lt;/p&gt;

&lt;p&gt;That’s usually when people reach for something like LangGraph.&lt;/p&gt;

&lt;p&gt;And to be fair — it helps.&lt;/p&gt;

&lt;p&gt;For a while, why LangGraph Feels Right in the Beginning&lt;/p&gt;

&lt;p&gt;&lt;a href="//www.dnotifier.com"&gt;LangGraph&lt;/a&gt; gives you structure.&lt;/p&gt;

&lt;p&gt;Nodes.&lt;br&gt;
Edges.&lt;br&gt;
State transitions.&lt;/p&gt;

&lt;p&gt;It turns a messy set of interactions into something you can visualize.&lt;/p&gt;

&lt;p&gt;Planner → Tool → Evaluator → Response&lt;/p&gt;

&lt;p&gt;Nice. Predictable.&lt;/p&gt;

&lt;p&gt;You feel like you’re finally in control of the system.&lt;/p&gt;

&lt;p&gt;And if your use case is contained — say, a single workflow or a well-defined loop — it works pretty well.&lt;/p&gt;

&lt;p&gt;But that’s also where the cracks start.&lt;/p&gt;

&lt;p&gt;The Problem Isn’t the Graph&lt;/p&gt;

&lt;p&gt;It took us some time to realize this.&lt;/p&gt;

&lt;p&gt;The problem isn’t the idea of a graph.&lt;/p&gt;

&lt;p&gt;The problem is assuming the graph should be predefined.&lt;/p&gt;

&lt;p&gt;Because real systems don’t behave like diagrams.&lt;/p&gt;

&lt;p&gt;They behave like environments.&lt;/p&gt;

&lt;p&gt;Things happen out of order.&lt;br&gt;
Signals come from outside the system.&lt;br&gt;
Multiple decisions happen at the same time.&lt;/p&gt;

&lt;p&gt;And suddenly your clean graph needs:&lt;/p&gt;

&lt;p&gt;interrupts&lt;br&gt;
parallel execution&lt;br&gt;
external triggers&lt;br&gt;
dynamic routing&lt;/p&gt;

&lt;p&gt;At that point, you’re not really “running a graph” anymore.&lt;/p&gt;

&lt;p&gt;You’re managing coordination.&lt;/p&gt;

&lt;p&gt;Where It Starts to Break&lt;/p&gt;

&lt;p&gt;We ran into this while building a fairly typical setup:&lt;/p&gt;

&lt;p&gt;a planning agent&lt;br&gt;
a retrieval layer&lt;br&gt;
a response generator&lt;br&gt;
some background processing&lt;/p&gt;

&lt;p&gt;Initially, everything flowed through a defined sequence.&lt;/p&gt;

&lt;p&gt;Then reality kicked in.&lt;/p&gt;

&lt;p&gt;The UI needed live updates.&lt;br&gt;
A monitoring process needed to inject signals.&lt;br&gt;
Retries depended on external conditions.&lt;br&gt;
Certain tasks needed to run in parallel.&lt;/p&gt;

&lt;p&gt;We kept trying to extend the graph.&lt;/p&gt;

&lt;p&gt;Adding conditions.&lt;br&gt;
Adding branches.&lt;br&gt;
Adding control logic.&lt;/p&gt;

&lt;p&gt;Eventually, the graph became harder to reason about than the system itself.&lt;/p&gt;

&lt;p&gt;That’s when we stepped back.&lt;/p&gt;

&lt;p&gt;A Different Way to Think About It&lt;/p&gt;

&lt;p&gt;Instead of asking:&lt;/p&gt;

&lt;p&gt;“How do we define the perfect graph?”&lt;/p&gt;

&lt;p&gt;We asked:&lt;/p&gt;

&lt;p&gt;“What if there is no central graph at all?”&lt;/p&gt;

&lt;p&gt;What if each part of the system just reacts to what’s happening?&lt;/p&gt;

&lt;p&gt;Not in a predefined order.&lt;/p&gt;

&lt;p&gt;But in real time.&lt;/p&gt;

&lt;p&gt;From Graphs to Conversations&lt;/p&gt;

&lt;p&gt;This is where the shift happens. Instead of:&lt;br&gt;
Node A → Node B → Node C&lt;/p&gt;

&lt;p&gt;You move toward:&lt;br&gt;
Something happens → multiple agents react&lt;/p&gt;

&lt;p&gt;Each component becomes independent.&lt;/p&gt;

&lt;p&gt;A planner doesn’t “call” an executor it emits an event, an executor doesn’t wait its turn. It listens for relevant signals and acts.&lt;/p&gt;

&lt;p&gt;A validator doesn’t sit at the end of a chain.&lt;/p&gt;

&lt;p&gt;It observes everything continuously.&lt;/p&gt;

&lt;p&gt;It starts to feel less like a graph…&lt;/p&gt;

&lt;p&gt;and more like a conversation.&lt;/p&gt;

&lt;p&gt;Building the Graph Without Defining It&lt;/p&gt;

&lt;p&gt;This is essentially what we ended up doing with &lt;a href="//www.dnotifier.com"&gt;DNotifier&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Not replacing graphs.&lt;/p&gt;

&lt;p&gt;Just removing the need to hardcode them.&lt;/p&gt;

&lt;p&gt;Instead of orchestrating flows, we focused on communication.&lt;/p&gt;

&lt;p&gt;Every component — whether it’s:&lt;/p&gt;

&lt;p&gt;a client&lt;br&gt;
a backend service&lt;br&gt;
an AI agent&lt;br&gt;
a background worker&lt;/p&gt;

&lt;p&gt;…talks through the same real-time messaging layer.&lt;/p&gt;

&lt;p&gt;Once that exists, something interesting happens.&lt;/p&gt;

&lt;p&gt;The “graph” still exists.&lt;/p&gt;

&lt;p&gt;But it’s no longer static.&lt;/p&gt;

&lt;p&gt;It forms dynamically based on:&lt;/p&gt;

&lt;p&gt;events&lt;br&gt;
responses&lt;br&gt;
context&lt;/p&gt;

&lt;p&gt;You don’t define the edges.&lt;/p&gt;

&lt;p&gt;You allow them to emerge.&lt;/p&gt;

&lt;p&gt;What This Looks Like in Practice&lt;/p&gt;

&lt;p&gt;Let’s take a simple example.&lt;/p&gt;

&lt;p&gt;A user asks a question.&lt;/p&gt;

&lt;p&gt;Instead of pushing it through a fixed pipeline:&lt;/p&gt;

&lt;p&gt;Chat → Retrieval → Generation → Response&lt;/p&gt;

&lt;p&gt;You get something like:&lt;/p&gt;

&lt;p&gt;the input triggers a retrieval agent&lt;br&gt;
the same input notifies a context agent&lt;br&gt;
both feed into a response generator&lt;br&gt;
a monitoring agent observes confidence levels&lt;br&gt;
if needed, another agent steps in&lt;/p&gt;

&lt;p&gt;All of this happens in parallel.&lt;/p&gt;

&lt;p&gt;No central controller.&lt;/p&gt;

&lt;p&gt;No strict ordering.&lt;/p&gt;

&lt;p&gt;Just coordination.&lt;/p&gt;

&lt;p&gt;Why This Scales Better&lt;/p&gt;

&lt;p&gt;When your system is built around interaction instead of orchestration:&lt;/p&gt;

&lt;p&gt;adding a new agent doesn’t break existing flows&lt;br&gt;
you don’t need to redesign pipelines&lt;br&gt;
components stay loosely coupled&lt;br&gt;
debugging becomes simpler (you trace events, not execution trees)&lt;/p&gt;

&lt;p&gt;Most importantly:&lt;/p&gt;

&lt;p&gt;You don’t fight your architecture as your system grows.&lt;/p&gt;

&lt;p&gt;The Subtle but Important Shift&lt;/p&gt;

&lt;p&gt;LangGraph (and similar tools) are built around control.&lt;/p&gt;

&lt;p&gt;You define what happens next.&lt;/p&gt;

&lt;p&gt;This approach is built around awareness.&lt;/p&gt;

&lt;p&gt;Each part of the system decides what to do based on what it sees.&lt;/p&gt;

&lt;p&gt;It’s a small conceptual shift.&lt;/p&gt;

&lt;p&gt;But it changes everything.&lt;/p&gt;

&lt;p&gt;When You Still Want a Graph&lt;/p&gt;

&lt;p&gt;To be clear — graphs aren’t bad.&lt;/p&gt;

&lt;p&gt;They’re useful when:&lt;/p&gt;

&lt;p&gt;workflows are predictable&lt;br&gt;
execution paths are known&lt;br&gt;
you want strict control&lt;/p&gt;

&lt;p&gt;But once your system becomes:&lt;/p&gt;

&lt;p&gt;real-time&lt;br&gt;
distributed&lt;br&gt;
multi-agent&lt;br&gt;
interactive&lt;/p&gt;

&lt;p&gt;Rigid graphs start getting in the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Thought&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At some point, every growing system hits the same realization:&lt;/p&gt;

&lt;p&gt;You can’t predict every path in advance, trying to encode all possible flows into a predefined graph becomes a losing battle.&lt;/p&gt;

&lt;p&gt;It’s easier to let the system adapt. Build a strong communication layer, let components react in real time.&lt;/p&gt;

&lt;p&gt;Let the structure emerge naturally.&lt;/p&gt;

&lt;p&gt;That’s when your “&lt;a href="//www.dnotifier.com"&gt;agentic graph&lt;/a&gt;” stops being something you draw and starts being something your system is.&lt;/p&gt;

</description>
      <category>langgraph</category>
      <category>ai</category>
      <category>websocket</category>
      <category>realtime</category>
    </item>
  </channel>
</rss>
