// lesson: shared-state

Shared State and What the Frameworks Give You

Everything your team passes around travels hand to hand: run_pipeline threads one string from agent to agent, the critic loop passes drafts and feedback, the router hands one task to one agent. That's message passing, and it has a big architectural rival: the blackboard (LangGraph calls its version graph state, CrewAI has shared memory) โ€” a shared store, often just a dict, that every agent can read and write:

board = {"task": task}
board["research"] = researcher(board["task"])
board["draft"] = writer(board["research"])
board["verdict"] = critic(board["draft"])

The trade-offs cut cleanly:

  • Message passing makes dataflow explicit โ€” you can read run_pipeline and know exactly what the writer sees, which is why its unit tests were five lines. The cost: the plumbing must anticipate what each agent needs; when stage 4 suddenly needs stage 1's output, you're re-threading signatures.
  • The blackboard makes sharing free โ€” any agent can look at anything, which fits workflows where you can't predict who needs what. The cost is the same as any global state: hidden coupling (which agents actually read board["research"]? grep and pray), ordering hazards (read a key before its writer ran and you get a KeyError at best, a silently stale value at worst), and โ€” with concurrent agents โ€” races.

A battle-tested compromise shows up in serious systems, and you already built it: keep the handoffs as messages, and keep an append-only transcript on the side. Your run_pipeline transcript is exactly that โ€” shared visibility for debugging and audit without shared mutable state for logic. If you take one design rule from this lesson: reach for messages first; adopt a blackboard when the wiring genuinely can't be predicted; and never let agents communicate through state that isn't also in the transcript, or you'll debug conversations you can't see.

So what do LangGraph and CrewAI actually sell you? Now you can read their docs as a checklist of things you've written:

  • LangGraph: your functions become nodes; run_pipeline is a chain of edges; the critic loop is a cycle with a termination condition (your max_rounds reappears as a recursion limit โ€” the same infinite-loop insurance with a different name); route is a conditional edge; the blackboard is a typed state object passed to every node. What it adds beyond the vocabulary: checkpointing (pause and resume a long workflow), streaming, human-in-the-loop interrupts, and visualization.
  • CrewAI: make_agent is an Agent (role, goal, backstory โ€” a structured system prompt); a pipeline of tasks is a Crew with Process.sequential; the router-in-charge is Process.hierarchical with a manager LLM. What it adds: a library of ready-made tool integrations, memory, and conventions so teams stop reinventing role prompts.

Use them when you need what they add โ€” durable long-running workflows, their integrations, their observability. Skip them when a hundred lines of functions you fully understand will do. Either way, you now know what's inside the box, because you built the box.

One piece remains: assembling the whole machine โ€” pipeline in, critic loop on the result, bounded, with a full transcript out โ€” as a single function. That's the final challenge.