Definition
A visual agent system designer is a user interface that lets users define an agentic workflow as a graph of nodes, edges, parameters, tools, conditions, and state mappings.
The visual designer is not the agent runtime.
It produces an intermediate graph schema. A backend compiler turns that schema into executable orchestration on top of an engine such as LangGraph (1.2.10), XState (1.2.9), Temporal, or a custom runtime.
Examples: Flowise, LangFlow, n8n, Dify, Coze, Vellum, Wordware, Vertex AI Agent Builder, AWS Bedrock Agent Builder, Microsoft Copilot Studio.
Core Architecture
visual editor
→ canonical graph schema (JSON)
→ schema validation
→ compiler
→ execution engine (LangGraph / XState / Temporal)
→ runtime traces
→ visual replay
The product is the graph schema, not the canvas.
Visual Editor
The visual editor renders nodes and edges. Common libraries:
- React Flow — typed nodes, custom edges, React-native
- Rete.js — TypeScript-first, framework-agnostic (React, Angular, Vue)
- JointJS — diagramming and BPMN-style workflows
- LogicFlow — workflow-oriented Chinese-stack alternative
Pick by frontend stack: React Flow for React, Rete.js for Angular or framework-neutral builds.
The editor is replaceable. The graph schema is not.
Graph Schema
The canonical schema is the stable internal representation of a workflow. It must be independent of the UI library — never persist React Flow or Rete objects as the production contract.
type AgentGraph = {
id: string;
version: number;
nodes: AgentNode[];
edges: AgentEdge[];
stateSchema: StateField[];
};
type AgentNode = {
id: string;
type: 'llm' | 'tool' | 'router' | 'approval' | 'retrieval' | 'end';
label: string;
config: Record<string, unknown>;
};
type AgentEdge = {
id: string;
source: string;
target: string;
condition?: ConditionSpec;
};
A persisted instance:
{
"id": "refund-agent",
"version": 3,
"nodes": [
{ "id": "classify", "type": "llm-router",
"config": { "model": "gemini-2.5-pro", "outputSchema": "RefundRouteDecision" } },
{ "id": "approval", "type": "human-approval",
"config": { "role": "manager" } }
],
"edges": [
{ "source": "classify", "target": "approval",
"condition": { "field": "risk", "operator": "eq", "value": "high" } }
]
}
Compiler Layer
The compiler transforms the graph schema into executable runtime code. There are two common targets.
Target: LangGraph
LangGraph is graph-shaped natively, so the mapping is direct.
| Visual concept | LangGraph concept |
|---|---|
| Node | Node function |
| Edge | Edge |
| Conditional edge | Conditional edge |
| Shared memory | State |
| Start | START |
| End | END |
| Tool node | Tool node |
| Human approval | Interrupt / pause |
function compileToLangGraph(spec: AgentGraph) {
const graph = new StateGraph(State);
for (const node of spec.nodes) {
graph.addNode(node.id, nodeFactory(node));
}
for (const edge of spec.edges) {
if (edge.condition) {
graph.addConditionalEdges(edge.source, conditionFactory(edge.condition));
} else {
graph.addEdge(edge.source, edge.target);
}
}
return graph.compile();
}
Use LangGraph as the target for agent loops, tool routing, retrieval, HIL pauses, and shared state workflows.
Target: XState
XState fits when the visual workflow has explicit states, events, and transitions — approval flows, deterministic business processes, UI ↔ backend coordination.
| Visual concept | XState concept |
|---|---|
| Node | State |
| Edge | Transition |
| Event label | Event |
| Condition | Guard |
| Side effect | Action |
| Async node | Invoked actor |
| Nested group | Compound state |
| Parallel lanes | Parallel states |
function compileToXState(spec: AgentGraph) {
return createMachine({
id: spec.id,
initial: findStartState(spec),
context: buildInitialContext(spec),
states: buildStates(spec)
});
}
Graph in Canvas vs Graph in Code
Two authoring modes share the same execution engine.
graph in code: developer writes executable graph directly
graph in canvas: user edits schema; compiler emits executable graph
The canvas does not execute business logic. The canvas produces a versioned specification. The backend executes the compiled specification.
Node Registry
A node registry catalogs the allowed node types and their schemas. This is the key system boundary — users may drag any box; only registered boxes can execute.
const nodeRegistry = {
'llm-router': {
configSchema: LlmRouterConfigSchema,
compileToLangGraph: compileLlmRouter,
compileToXState: compileLlmRouterState
},
'human-approval': {
configSchema: ApprovalConfigSchema,
compileToLangGraph: compileApprovalNode,
compileToXState: compileApprovalState
}
};
Per node type, the registry must declare:
- display name
- input and output handles
- config schema (Zod or JSON Schema)
- validation rules
- runtime compiler(s)
- permission requirements
Validation Layer
Validation runs before save, publish, and execution.
Required checks:
- exactly one start node
- at least one terminal node
- no orphan nodes
- no illegal cycles
- all required configs present
- edge conditions valid against state schema
- tool permissions respected
- model output schemas match downstream node inputs
- approval required for sensitive tools
For TypeScript builders, Zod is the most direct schema implementation.
Execution Boundary
The execution boundary separates design-time configuration from runtime authority.
Design-time users may define:
- prompts
- tools
- routing conditions
- approval steps
- retrieval sources
Runtime still enforces:
- permissions
- secrets access
- rate limits
- tool allowlists
- tenant isolation
- approval requirements
- audit logging
A visual graph must never become a privilege-escalation surface. For the broader runtime concerns — durability, idempotency, tool authority, approval gates — see 1.1.1 State machines in agentic systems.
Runtime Persistence
Graph definition and graph execution instance must be persisted separately.
{ "graphId": "refund-agent", "version": 3 }
{
"runId": "run_123",
"graphId": "refund-agent",
"graphVersion": 3,
"currentNode": "approval",
"state": {},
"status": "paused"
}
Definitions are versioned and immutable once published. Runs reference a specific definition version so a redesign mid-flight does not corrupt in-flight executions.
Visual Replay
Visual replay renders runtime traces back onto the canvas.
Required trace events:
- node started
- node completed
- edge selected
- LLM output received
- tool called
- tool failed
- approval requested / granted
- run completed / failed
{
"runId": "run_123",
"event": "edge_selected",
"source": "classify",
"target": "approval",
"reason": "risk=high"
}
Without replay, visual builders are nearly impossible to debug.
Production Rule
The visual editor must not generate arbitrary executable code.
It must generate validated, declarative graph JSON. The backend compiles that JSON into a bounded runtime.
Good:
canvas → JSON spec → compiler → runtime
Bad:
canvas → user-generated JavaScript → eval
Final Pipeline
user drags boxes
→ editor updates canonical graph JSON
→ validator checks the graph
→ graph is versioned and published
→ backend compiles the graph
→ LangGraph or XState executes the graph
→ runtime persists state
→ traces stream back to canvas
→ user sees execution path on the same diagram
Core Rule
A visual agent builder is not a replacement for LangGraph or XState. It is an authoring layer above them.
The design problem is not drawing boxes. The design problem is compiling boxes into safe, typed, versioned, executable orchestration.
Cross-references
- 1.2.10 LangGraph — graph-over-state runtime; primary compiler target for agent-loop workflows.
- 1.2.9 XState — statechart runtime; primary compiler target for deterministic workflows.
- 1.1.1 State machines in agentic systems — runtime concerns shared by both targets: durability, idempotency, tool authority, approval gates.
References
- React Flow —
reactflow.dev - Rete.js —
retejs.org - LangGraph Graph API (JS) —
docs.langchain.com/oss/javascript/langgraph/graph-api - XState Actors —
stately.ai/docs/actors - XState Persistence —
stately.ai/docs/persistence