AgentCanvas / Pages / Developer Guide / Core / Blueprint
AgentCanvas is a visual agent design platform: researchers prototype embodied AI agents by drawing node graphs that execute in real-time against simulators or real-world setups. This page covers what the platform is and offers โ€” capabilities, design principles, core components. For phasing see roadmap; closed architectural decisions in decisions/; codebase layout in codebase-map.

ยง1Problem Statement

The lede above says what AgentCanvas is; this section is the why. Skip to ยง2 Capabilities for what it does.

Embodied agents โ€” spanning VLN (Vision-and-Language Navigation), EQA (Embodied Question Answering), and VLA (Vision-Language-Action) โ€” are increasingly built by composing foundation models with perception, mapping, memory, planning, and action. Unlike end-to-end policies, whose structure is absorbed into weights, this architecture is explicit and editable. That raises the question AgentCanvas is built around โ€” can agent design be searched rather than hand-built? โ€” alongside two stacks of pain it has to clear on the way.

Agent architecture is hand-built โ€” and could be searched

Each agent fixes a choice at every join โ€” sensor abstractions, map representations, memory state, prompt structure, planner topology, model placement, action interfaces โ€” by hand, usually for a single benchmark. As foundation models and embodied tools multiply, the space grows faster than manual iteration can cover, so the natural move is to search it rather than hand-tune it.

Agent Architecture Search (AAS) already does this for text-domain agents, but embodied transfer is not free: stateful simulators, noisy multi-episode scoring, long perception/action traces, and no off-the-shelf palette of embodied primitives. AgentCanvas is our attempt to supply the missing substrate โ€” a scaffold a coding agent can read, edit, run, and verify โ€” so that searching agent design becomes possible for embodied agents too.

This is why AgentCanvas exists Externalising agent topology as a typed, inspectable graph (ยง6) turns "design a better agent" from a manual rewrite into a search over a structured space. AgentCanvas is the executor substrate for Agent Architecture Search (AAS): a coding-agent harness edits the graph through typed, API-level operations and hill-climbs on eval metrics. The AAS work itself โ€” the optimizer and its ADAS / AFlow baselines, and the cross-task results โ€” is written up separately.

Embodied-specific pain points

Common AI research pain points (amplified here)

Who it affects Researchers who want to rapidly prototype, compare, and build on embodied agent architectures without reinventing the execution stack each time.

What the platform has to demonstrate as built & shipped

ยง2Capabilities

AgentCanvas is a visual agent design platform where researchers rapidly prototype embodied AI agents by drawing node graphs on a canvas. Graphs execute in real-time against simulator environments (Habitat, MatterSim/MP3D, SIMPLER, LIBERO) or real-world setups โ€” no pipeline code required.

The key idea One JSON = one agent = one graph. Agent behavior is defined as a dataflow graph, not imperative code. The graph is the source of truth โ€” saved as a single JSON file, loaded as a complete agent, and executed directly.

Nine capabilities make this work. Each card opens its implementation detail.

ยง2.1 Graph-Expressible Agents

The canvas supports workflow DAGs, cyclic agent loops (observe โ†’ reason โ†’ act โ†’ repeat via IterIn/IterOut), and compositions of both. Researchers pick the pattern that fits their method.

How The GraphExecutor treats every graph uniformly โ€” nodes fire when inputs arrive. Cycles are handled by IterIn/IterOut gate nodes: when IterOut fires, it increments the step counter and transfers its outputs back to the paired IterIn, re-entering the loop. For pure DAGs, the same executor runs a single forward pass with no iteration. Both patterns coexist on one canvas โ€” a workflow DAG can contain an inner agent loop, or vice versa.

ยง2.2 Customizable node system

New nodes are Python classes inheriting BaseCanvasNode or BaseNodeSet. Drop the file into workspace/, the platform auto-discovers it at runtime. No framework changes needed.

How Authors subclass BaseCanvasNode and declare node_type, input_ports, output_ports, category, icon, and ui_config as class variables, then implement async forward(inputs, ctx) โ†’ dict. The WorkspaceComponentRegistry scans workspace/ subdirectories at startup (and on hot-reload via POST /api/components/reload), imports every .py file, collects concrete BaseCanvasNode subclasses, and registers them in NODE_HANDLERS. The frontend's GenericBlockRenderer renders any registered node automatically from its port and UI metadata โ€” no custom .tsx component needed. For groups of related tools, authors subclass BaseNodeSet instead โ€” a nodeset bundles multiple tools under shared initialize() / shutdown() lifecycle.

ยง2.3 Isolated runtime environments

Research tools often require conflicting Python environments (Habitat needs 3.8, SLAM needs ROS). Any BaseNodeSet can run in server mode in its own interpreter โ€” zero extra server code.

How When a nodeset is loaded with mode="server", the WorkspaceComponentRegistry launches a subprocess using the nodeset's server_python interpreter. The subprocess runs AutoServerApp, which introspects the nodeset's tools โ€” reading their input_ports and output_ports โ€” and auto-generates an HTTP server with one endpoint per tool. The framework then fetches the server's manifest, generates proxy canvas nodes from it, and registers them in NODE_HANDLERS. From the canvas perspective, proxy nodes behave identically to local nodes โ€” the researcher doesn't need to know whether a node runs in-process or in a remote subprocess.

ยง2.4 Nested graph system

Any canvas graph can be saved as a graph node and dragged onto another canvas as a reusable block. Enables hierarchical agents โ€” a high-level planner containing sub-agent graph nodes.

How A GraphDefinition can contain nodes whose .subgraph field holds another GraphDefinition. graphIn / graphOut boundary nodes define the composite's input/output interface. Before execution, flatten.py recursively expands all composites into a single flat graph โ€” prefixing inner node IDs for uniqueness and rewiring parent edges through the graphIn/graphOut boundaries. The executor only sees flat nodes. Graph nodes are stored as JSON in workspace/graph_nodes/ with snapshot semantics โ€” editing the source graph doesn't affect existing graph node instances.

ยง2.5 Dataflow execution engine

Nodes fire when inputs arrive, not in a fixed order. Supports cycles natively, enables real-time streaming, opens the door for future parallelism.

How The GraphExecutor maintains a ready queue. Entry nodes (no required inputs, no incoming edges) fire first. When a node fires, its outputs propagate along edges into downstream nodes' pending_inputs. A downstream node enters the ready queue once all its required ports have data. State is persistent across firings via _NodeStateProxy, and state containers (shared mutable state) are injected into nodes connected by state edges. A StopExecution exception from a termination node cleanly ends the loop.

ยง2.6 Real-time observability

Every step streams observations, reasoning, actions, and metrics via WebSocket. Researchers see what the agent sees, read what it thinks, and can pause/step/resume.

How Output viewer nodes (imageViewer, textScroll, actionLog, metrics, etc.) store their latest inputs. At each iteration boundary (when IterOut fires), _broadcast_step consolidates all output viewer data into a single nav_step WebSocket event โ€” including base64-encoded RGB/depth images, action names, LLM responses, state container snapshots, and metrics. The frontend renders these in real-time panels. An execution_id routes events to the correct client, enabling multiple concurrent agent runs with isolated output streams.

ยง2.7 Hook system

Shell commands fire before/after each node execution and at graph lifecycle boundaries. Hooks can validate inputs, log outputs, block nodes, or modify data โ€” all without changing the graph nodes themselves. Fail-open: hook errors never crash graph execution.

How Hooks are configured per-graph (.canvas/hooks.json) or per-node (ui_config.hooks). The executor's HookRunner matches lifecycle events (PreNodeExecute / PostNodeExecute / OnIterationStart / OnIterationEnd / OnGraphStart / OnGraphComplete) against the hook config, runs each matched hook as a subprocess with JSON-encoded event data on stdin, and accepts exit-code signals (0 pass, 1 block, 2 modify-stdout). Fail-open by design: hook errors never crash graph execution โ€” they are logged and the executor continues with the original input, preserving graph reliability across user-supplied automation.

ยง2.8 Batch evaluation & job queue

The same graph that runs on the canvas can be submitted as an eval job that scores it over hundreds of episodes. A backend-owned JobScheduler gates admission against a VRAM budget shared across all sessions; each admitted run is its own subprocess so backend restarts don't kill in-flight evals; per-episode logs and assets are self-contained on disk so a teammate can replay any single episode without re-running.

How Two stacked layers (see capability page): Layer A is JobScheduler โ€” singleton with a per-second tick() that admits queued jobs when their calibrated per-resource estimates (VRAM + RAM) fit measured free minus standing reservations (declared marginal_vram_mb as fallback) and canvas Play isn't holding the exclusive lock; spawns the admitted job as python -m app.eval_subprocess_main --run-dir โ€ฆ with PR_SET_PDEATHSIG so the kernel reaps orphans. Layer B is BatchEvalRunner in the subprocess โ€” reads spec.json/shared_urls.json, attaches to the parent backend's shared singletons via register_remote_nodeset (no duplicate VRAM), iterates the episode list through fresh LoopRunner instances, leases EnvWorkerPool.WorkerHandles when worker_count > 1, and writes self-contained episodes/ep{NNNN}/{log.jsonl, assets/, episode.json}. Shared singletons are refcounted across consuming jobs; the scheduler auto-unloads only those it loaded itself, so canvas-loaded copies stay (ADR-eval-001/002/003/004 + ADR-server-003).

ยง2.9 Visual canvas editor

The surface researchers actually touch: a React Flow editor where you drop nodes from a live backend catalog, wire typed ports, configure each node from an auto-rendered form, and watch every port update as the graph runs. The same component handles root canvas and composite drill-down โ€” no mode switching between design and observe.

How Three layers stacked over one WebSocket (see capability page): ReactFlow (@xyflow/react) owns geometry; a tab-aware Zustand store (useFlowStore) owns graph semantics + per-node live nodeOutputs; a singleton WSManager with auto-reconnect carries nav_status (run state), nav_step (consolidated per-step bundle from graphOut sinks), viewer_data (per-sink-node {node_id, step, fields}), and error_event (ErrorBus envelopes). UnifiedGraphEditor reuses one component for mode="root" (live Zustand) and mode="subgraph" (local state + explicit save back to the parent composite's NodeDef.subgraph). PropertiesPanel auto-renders config forms from each node's ui_config.config_fields โ€” adding a new node type means adding its Python configSchema, not writing a panel. Typed-wire compatibility is checked client-side in useFlowStore.onConnect before the edge enters the store, so the canvas can never reach the backend in a state only validate_graph_connectivity would catch. The same store/WS also drives the Eval, Replay, and Log-viewer pages.

ยง3Design Principles

  1. Framework has zero domain knowledge agentcanvas/backend/app/ is a pure execution engine. All domain-specific code (policies, prompts, tools, environments) lives in workspace/. The framework never imports domain code directly โ€” it discovers and bridges components at runtime.
  2. Graph, not script Agent behavior is defined as a dataflow graph, not imperative code. This makes architectures visual, composable, and inspectable. The graph is the source of truth โ€” not a visualization of code, but the code itself.
  3. Dataflow execution Nodes fire when their inputs arrive, not in a fixed order. Naturally supports cyclic agent loops (observe โ†’ think โ†’ act โ†’ repeat) and enables future parallelism and branching.
  4. Composability via nesting Composites let researchers group nodes into reusable sub-graphs at any depth. Execution always flattens to a single dataflow graph โ€” no outer/inner split at runtime. Keeps the executor simple while enabling arbitrarily deep hierarchical agent architectures.
  5. Real-time observability Every step streams observations, reasoning, actions, and metrics via WebSocket. Researchers see what the agent sees, read what it thinks, and can pause/step/resume at any point.
  6. Extend, don't fork New capabilities are added by dropping Python files into workspace/, not by modifying framework code. The component system auto-discovers new tools, skills, agents, environments, policies, and node handlers.

ยง4Core Components

Component Location Role
Canvas Editor agentcanvas/frontend/ Visual graph editor (React Flow + Zustand)
Graph Executor agentcanvas/backend/app/agent_loop/graph_executor.py Single data-driven executor โ€” fires nodes on input arrival; handles both DAG workflows and cyclic loops via IterIn/IterOut gates (no separate DAG executor)
Loop Runner agentcanvas/backend/app/agent_loop/loop_runner.py Singleton that manages pause/stop/resume lifecycle
Component Registry agentcanvas/backend/app/components/registry.py Auto-discovers workspace/ components, bridges into runtime
Wire Type System agentcanvas/backend/app/standard/wire_types.py 9 current typed edges (IMAGE, DEPTH, POSE, DISCRETE_ACTION, CONTROL, TEXT, BOOL, METRICS, ANY) + LIST[T] modifier; 3 deprecated names (ACTION, OBSERVATION, STEP_RESULT) stay registered so legacy graphs load
Node Handlers agentcanvas/backend/app/agent_loop/builtin_nodes.py + workspace/nodesets/ 14 built-in node types + nodeset-provided domain nodes, all registered in NODE_HANDLERS
Env Nodesets workspace/nodesets/env/ Gym-like env nodesets (env_habitat, env_mp3d, env_simpler, env_libero, env_hmeqa, โ€ฆ); the framework-level BaseEnvManager abstraction was removed (ADR-components-002)
Eval Harness (WIP) app/services/job_scheduler.py + agent_loop/eval_batch.py (BatchEvalRunner) + env_worker_pool.py + POST /api/eval/v2 + frontend Eval page Subprocess-per-run JobScheduler with cross-session measured resource admission (VRAM + RAM), worker-pool + batched-inference (ADR-eval-002/003/004). Replaces the retired vlnworkspace.eval Hydra CLI โ€” Eval UI is still being built (Feature TODO F5).
User Workspace workspace/ All domain components: nodesets (methods, envs, models), graphs, graph nodes

ยง5Core Features

Visual agent composition

NodeSet manager

Real-time execution & streaming

Iteration & cycles

Extensible component system

State container system (ADR-dataflow-002)

VLN-CE evaluation

ยง6Differentiation

AgentCanvas combines three structural features that are collectively absent from current agent frameworks:

Framework Externalised topology Typed ports Nodeset boundaries All three?
AgentCanvas โœ“ Graph JSON โœ“ IMAGE / DEPTH / DISCRETE_ACTION / CONTROL / POSE / TEXT / METRICS / โ€ฆ โœ“ Loadable nodesets with declared tool surface โœ“
LangGraph ~ Topology built imperatively via Python add_node / add_edge; not externalised as a single artefact โœ— Edges carry arbitrary Python state objects ~ Tool registries exist but no declared-equivalence semantics โœ—
dspy โœ— Compute graph is implicit in the optimised Module program ~ Signatures type the I/O of one module, not the inter-module wires โœ— No nodeset analogue โœ—
PocketFlow โœ“ Flow JSON exists โœ— Ports are untyped (any Python object) โœ— No nodeset analogue โœ—
Ad-hoc Python harness (typical paper repo) โœ— Topology = the source code โœ— Untyped function signatures โœ— Tools are imports โœ—

The triple matters because it makes the platform's content (graphs) structurally inspectable: graph topology is a JSON artefact, port types are declarable and checkable, and tool boundaries are explicit nodeset surfaces. This is what makes AgentCanvas a viable substrate for downstream automation โ€” for example, an automated faithfulness audit can reduce its rules to JSON-checkable structural questions only because of these surfaces. Without all three, audit cost dominates run cost.

What this is NOT claiming
  • Not that other frameworks are inferior for every use case โ€” LangGraph offers stronger imperative flexibility, dspy stronger compiler-level optimisation. AgentCanvas trades imperative flexibility for declarative auditability.
  • Not that AgentCanvas is feature-complete vs the LangChain ecosystem โ€” LangChain has hundreds of pre-built integrations; AgentCanvas has ~110 node types in v1.
  • Not that the triple is uniquely sufficient for any agent design task โ€” only necessary for tasks that benefit from structural inspectability (auditing, search, transfer benchmarking).

ยง7Open Questions

ยง8Status

Maturity shipped (main platform) ยท drafting (Eval harness UI โ€” Feature TODO F5)

Core platform stable: graph executor, IterIn/IterOut iteration, 110+ node types, server-mode nodesets (Habitat / SIMPLER / Prismatic), WebSocket streaming, NodeSet Manager, hook system. Bottleneck: Eval harness frontend (Feature TODO F5) is incomplete โ€” backend works but the Eval page in agentcanvas/frontend/src/eval/ still has gaps. Runtime graph mutation โ€” letting the AAS search loop edit graphs through typed operations rather than ad-hoc file rewrites โ€” is the headline v2 (Topology-Growing Canvas) capability, out of scope for the v1 bounded-topology paradigm. Next: finish F5 frontend.

AgentCanvas docs