AI Engineering & LLMs (50 Qs)•Part 3: Agentic Workflows, Function Calling & MCP
Multi-Agent Orchestration & State Machines: LangGraph vs. CrewAI & Cyclical Execution
1. Directed Acyclic Graphs (DAGs) vs. Cyclical State Machines
Early agent frameworks (LangChain chains, AutoGPT) ran simple linear DAGs. Real-world enterprise workflows are inherently cyclical:
- Write code → Run test → Test fails → Loop back to fix code → Re-test → Pass → Deploy.
less
┌──────────────────────────────────────────────┐
│ LangGraph Cyclical State Machine │
└──────────────────────┬───────────────────────┘
│
▼
[ Architect Node ]
│
▼
[ Developer Node ] ◄────────┐
│ │
▼ │ (Iterative fix)
[ Code Tester Node ] ────────┘
│
Did all unit tests pass?
│
┌──────────────┴──────────────┐
▼ ▼
YES: [ Deployer ] NO: Max Retries?
2. Framework Comparison: LangGraph vs. CrewAI
| Dimension | LangGraph | CrewAI |
|---|---|---|
| Core Abstraction | Stateful Graph (Nodes = functions, Edges = conditional routing) | Role-playing Agents with assigned Tasks and Crews |
| Control Granularity | Low-level, deterministic, complete state control | High-level, persona-driven, autonomous delegation |
| State Persistence | First-class checkpointers (PostgreSQL, Redis, SQLite) | In-memory task context |
| Human-in-the-Loop | Native interrupt and resume tokens at any graph node | Custom hook callbacks |
| Best Used In | Production enterprise systems with strict governance | Rapid prototyping, exploratory content generation |
3. Production Cyclical Graph Pattern in LangGraph
python
from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
class AgentState(TypedDict):
code: str
test_output: str
attempts: int
def developer_node(state: AgentState) -> AgentState:
# LLM writes or fixes code based on test_output
return {"code": "def solve(): return 42", "attempts": state["attempts"] + 1}
def tester_node(state: AgentState) -> AgentState:
# Run test suite in sandbox
passed = state["attempts"] >= 2 # Simulating test pass on attempt 2
return {"test_output": "PASSED" if passed else "AssertionError: expected 42"}
def should_continue(state: AgentState) -> str:
if state["test_output"] == "PASSED":
return END
if state["attempts"] >= 3:
return END # Give up
return "developer"
workflow = StateGraph(AgentState)
workflow.add_node("developer", developer_node)
workflow.add_node("tester", tester_node)
workflow.set_entry_point("developer")
workflow.add_edge("developer", "tester")
workflow.add_conditional_edges("tester", should_continue)
app = workflow.compile()