DEV Community

Cover image for Workflow Series (09): Framework Comparison — Prompt-based, LangGraph, Temporal, or n8n?
WonderLab
WonderLab

Posted on

Workflow Series (09): Framework Comparison — Prompt-based, LangGraph, Temporal, or n8n?

Four Approaches, Fundamentally Different

Choosing a workflow framework means matching execution model, engineering cost, and team capability. There's no universally better option.

Approach         Workflow definition     State persistence    Execution engine
──────────────────────────────────────────────────────────────────────────────
Prompt-based     Markdown + YAML         Hand-written JSON    LLM (A-layer)
LangGraph        Python code (graph)     Built-in State       Python code (deterministic)
Temporal         Python/TypeScript       Built-in (database)  Code (deterministic)
n8n              Visual canvas + JSON    Built-in             Code (deterministic)
Enter fullscreen mode Exit fullscreen mode

The first three support semantic branching (confidence >= 0.95). n8n is limited to boolean expressions. LangGraph and Temporal use deterministic code as the execution engine; Prompt-based uses the LLM itself.


Prompt-based

Workflow definition: Markdown + YAML files

# workflow.md
## Phase 3: Root Cause Analysis
Execute subagent: rnd-automotive-issue-analyzer
Context: {{ phases.phase2.log_dir }}

Routing:
- confidence >= 0.95 → Phase 4
- 0.6 <= confidence < 0.95 → Gate A
- confidence < 0.6 and retries < 3 → retry Phase 3
- confidence < 0.6 and retries >= 3 → human escalation
Enter fullscreen mode Exit fullscreen mode

Strengths:

  • Non-engineers can read and modify the workflow definition
  • Changing Markdown is faster than changing code — good for frequent iteration
  • LLM executes routing logic; not every edge case requires hardcoding
  • Low startup cost; right for POC and rapid validation

Weaknesses:

  • LLM-executed routing has non-determinism: same input may route differently across runs
  • No language-level type system or testing tool support
  • Observability requires manual Langfuse integration — not out of the box
  • Maintenance difficulty grows as the number of Markdown files increases

Best for:

  • Workflows that change frequently, where editing Markdown beats editing code
  • Teams without dedicated engineers, or where non-technical people maintain workflow logic
  • Rapid POC validation — working MVP in 3 days

LangGraph

Workflow definition: Python code (graph structure)

from langgraph.graph import StateGraph, END
from typing import TypedDict

class WorkflowState(TypedDict):
    jira_key: str
    bug_info: dict        # Phase 1 output
    analysis: dict        # Phase 3 output
    fix_result: dict      # Phase 4 output
    analyze_retries: int

def analyze_node(state: WorkflowState) -> dict:
    result = call_skill("rnd-automotive-issue-analyzer", state["bug_info"])
    return {
        "analysis": result,
        "analyze_retries": state["analyze_retries"] + 1
    }

def route_after_analyze(state: WorkflowState) -> str:
    confidence = state["analysis"]["confidence"]
    retries = state["analyze_retries"]

    if confidence >= 0.95: return "fix_and_verify"
    if confidence >= 0.6:  return "gate_A"
    if retries < 3:        return "analyze"       # retry
    return "human_escalation"

graph = StateGraph(WorkflowState)
graph.add_node("analyze", analyze_node)
graph.add_conditional_edges("analyze", route_after_analyze, {
    "fix_and_verify": "fix_and_verify",
    "analyze": "analyze",
    "gate_A": "gate_A",
    "human_escalation": "human_escalation",
})
Enter fullscreen mode Exit fullscreen mode

LangGraph concepts mapped to Prompt-based equivalents:

LangGraph concept           Prompt-based equivalent
────────────────────────────────────────────────────────
WorkflowState (TypedDict)   workflow_state.json structure
Node function               Each Phase's execution logic
conditional_edges           Routing conditions in workflow.md
checkpointer                workflow_state.json itself
interrupt (human-in-loop)   Approval gate
Enter fullscreen mode Exit fullscreen mode

Understanding this mapping makes conversion systematic — not a rewrite from scratch.

Strengths:

  • Routing logic is pure Python; deterministic and unit-testable
  • TypedDict State Schema provides type checking — missing fields caught at development time
  • Built-in LangSmith integration; Trace is out-of-the-box
  • Supports nested subgraphs for complex state machines

Weaknesses:

  • Workflow definition is code; non-engineers can't read or modify it
  • Changes go through code review — iteration is slower than Markdown
  • Learning curve: requires understanding graph structure and the StateGraph API

Best for:

  • Complex workflow logic requiring precise state machine control
  • Teams comfortable with Python, with relatively stable (not daily-changing) workflows
  • Code-level type checking and test coverage requirements

Temporal

Workflow definition: Python or TypeScript code

from temporalio import workflow, activity
from datetime import timedelta

@activity.defn
async def analyze_bug(bug_info: dict) -> dict:
    return await call_skill("rnd-automotive-issue-analyzer", bug_info)

@workflow.defn
class BugFixWorkflow:
    @workflow.run
    async def run(self, jira_key: str) -> dict:
        bug_info = await workflow.execute_activity(
            fetch_jira_ticket,
            jira_key,
            start_to_close_timeout=timedelta(minutes=5)
        )
        analysis = await workflow.execute_activity(
            analyze_bug,
            bug_info,
            start_to_close_timeout=timedelta(minutes=30),
            retry_policy=RetryPolicy(maximum_attempts=3)
        )
        # ...
Enter fullscreen mode Exit fullscreen mode

Strengths:

  • True Durable Execution at the code layer — crash recovery guaranteed without hand-written state files
  • Native support for long-running workflows (days, weeks), suited for SLA-governed enterprise processes
  • Built-in visualization UI and complete workflow history
  • Strict separation of Activities and Workflows

Weaknesses:

  • Complex deployment: requires a running Temporal Server
  • High learning curve: Temporal's programming model differs from standard async code in non-obvious ways
  • Temporal's Activity timeout and retry model doesn't cleanly match LLM call behavior

Best for:

  • Workflows running longer than an hour, involving human approval with extended wait times
  • Enterprise-grade processes requiring strong consistency guarantees
  • Teams with dedicated backend engineers maintaining infrastructure

n8n

Workflow definition: Visual canvas + JSON

[HTTP Request] → [AI Agent] → [Condition] → [Jira Comment]
                              ↓
                           [Send Email]
Enter fullscreen mode Exit fullscreen mode

Strengths:

  • Visual; non-technical users can build and understand workflows
  • Large library of built-in integration nodes (Jira, GitHub, Slack, databases)
  • Self-hosted; data stays on-premises

Weaknesses:

  • Branching is limited to boolean expressions; semantic routing requires an external LLM node
  • Complex state management (retry counters, candidate result aggregation) is clumsy in the visual interface
  • Version control: JSON file diffs are unreadable; merge conflicts are painful
  • AI Agent nodes have limited capability — can't implement Orchestrator-Subagents patterns

Best for:

  • Primarily API integration, with AI as one step among many
  • Workflows that need to be visible to non-technical stakeholders
  • Simple tasks with no complex state management requirements

Decision Tree

Workflow changes frequently; non-engineers need to maintain it?
  → Prompt-based (Markdown)

Complex logic; code-level tests; team knows Python?
  → LangGraph

Runtime > 1 hour; enterprise SLA; dedicated backend team?
  → Temporal

Primarily API integration; AI is one node; visual presentation needed?
  → n8n
Enter fullscreen mode Exit fullscreen mode

Hybrid use: These approaches aren't mutually exclusive. A real system might use n8n for triggers and notification integrations (listen for new Jira tickets → send Feishu notification), while LangGraph or Prompt-based handles the core AI Agent workflow logic.


Migrating from Prompt-based to LangGraph

The mapping is systematic — the work is translating implicit LLM routing into explicit Python, not redesigning the workflow.

Step 1: Convert workflow.md's context structure to WorkflowState

class WfBugE2EState(TypedDict):
    jira_key: str
    bug_info: dict | None
    log_dir: str | None
    analysis: dict | None
    fix_results: list[dict]        # Phase 4 parallel candidate results
    selected_fix: dict | None      # Phase 4 fan-in result
    commit_result: dict | None
    analyze_retries: int
Enter fullscreen mode Exit fullscreen mode

Step 2: Convert each Phase's template to a Node function

def phase3_analyze_node(state: WfBugE2EState) -> dict:
    result = spawn_subagent(
        template="templates/analyze.md",
        context={"bug_info": state["bug_info"], "log_dir": state["log_dir"]}
    )
    return {
        "analysis": result,
        "analyze_retries": state["analyze_retries"] + 1,
    }
Enter fullscreen mode Exit fullscreen mode

Step 3: Convert workflow.md's routing conditions to conditional_edges

def route_after_analyze(state: WfBugE2EState) -> str:
    conf = state["analysis"]["confidence"]
    retries = state["analyze_retries"]
    if conf >= 0.95: return "phase_4_fix"
    if conf >= 0.6:  return "gate_A"
    if retries < 3:  return "phase_3_analyze"
    return "human_escalation"
Enter fullscreen mode Exit fullscreen mode

The core design (Phase structure, Context passing, approval gates) is identical in both. What changes is moving routing logic from "LLM interprets the condition" to "Python evaluates the condition."


Summary

  1. No universally best framework — only the best fit: Prompt-based for fast iteration and non-technical maintenance; LangGraph for complex state machines with code-level tests; Temporal for long-running enterprise processes; n8n for API integration and visual presentation
  2. LangGraph and Prompt-based are convertible: WorkflowState = workflow_state.json, Node = Phase/Step, conditional_edges = routing conditions — one-to-one mapping, systematic migration
  3. Framework choice doesn't change the core engineering: Context passing modes, approval gates, idempotency, version binding — these principles apply regardless of which framework you use

Check out PrimeSkills — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.

Find more useful knowledge and interesting products on my Homepage

Top comments (0)