Generate a Graph#
One natural-language instruction goes through the LLM agent pipeline
(coordinator β per-subgraph agents β checkpoint agent β validation +
script-fix loop) and comes out as a typed, verified workflow directory β
the same artifact gap.builder authors by hand and
gap run executes.
Tip
End-to-end
gap generate "<task>" + gap run --sim works end-to-end on the
public quickstart task. The coordinator picks
perceiving-objects + grasping-with-planner + transporting-objects
from the registry without any prompt hinting; the runtime then runs the
generated workflow in sim against LLM-authored postcondition
checkpoints. gemini-3.1-flash-lite-preview on the default openrouter
provider is the fastest path; Vertex works too β see
Providers.
Note
Requirements
An LLM credential (OPENROUTER_API_KEY by default β see
Providers) plus the engine install (uv sync) and the
open-robot-skills checkout. Generation runs without the simulator β only an
LLM credential and the open-robot-skills checkout are required; running
the result in sim has the usual
quickstart requirements.
Source: examples/generate_a_graph.
CLI#
export OPENROUTER_API_KEY=... # default provider; see "Providers" below
uv run gap generate "pick up the alphabet soup can and place it in the basket" --out my_graph
The open-robot-skills checkout is auto-discovered ($GAP_SKILLS_PATH or
the checkout next to the graph-as-policy checkout); pass
--skills /path/to/open-robot-skills to override (repeatable,
precedence-ordered). Other flags:
Flag |
Default |
Meaning |
|---|---|---|
|
resolved registry set |
Skill registry root(s); repeatable |
|
|
|
|
provider default |
LLM model override |
|
|
Output directory |
|
β |
Pipeline config (llm/composition/skills knobs) |
|
off |
Debug logging |
Warning
The generated workflow lands in <out>/task_00/, not in --out itself β
point gap run at the task subdirectory (my_graph/task_00).
Python#
import gap
graph = gap.agent.generate_sync(
"pick up the alphabet soup can and place it in the basket",
out_dir="my_graph", # skills= omitted -> auto-discovered
)
print(graph) # the graph, as terminal text (below)
print(graph.path) # the written workflow folder (my_graph/task_00)
print(sorted(graph.code)) # every generated source file
print(graph) renders the workflow as box-drawing text β the same output
gap generate prints. On the checked-in
sample_generated_graph:
task_00
Pick the blue and yellow alphabet soup can and place it in the basket.
START
β
βΌ
ββ target_sg βββββββββββββββββββββββββββββββββββββββββ perceiving-objects ββ
β observe ββΆ perceive ββΆ filter_obb β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β found abort βΆ β abort
βΌ
ββ container_sg ββββββββββββββββββββββββββββββββββββββ perceiving-objects ββ
β observe ββΆ perceive ββΆ filter_obb β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β found abort βΆ β abort
βΌ
ββ grasp_sg βββββββββββββββββββββββββββββββββββββββ grasping-with-planner ββ
β open ββΆ compute_grasp ββΆ approach ββΆ observe ββΆ build_world ββΆ plan β
β ββΆ execute ββΆ close β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β grasped abort βΆ β abort
βΌ
ββ transport_sg ββββββββββββββββββββββββββββββββββββ transporting-objects ββ
β compute_drop ββΆ move_above ββΆ release β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β placed βΆ β done abort βΆ β abort
β done (success) β abort (failure, recovery: open_gripper, go_home)
gap.agent.generate is the async variant with the same signature;
gap.viz.to_text(workflow) renders any workflow dict or directory the
same way.
generate.py wraps this in a small CLI:
uv run python examples/generate_a_graph/generate.py "pick up the milk and put it in the basket"
What the output directory contains#
my_graph/task_00/ # = graph.path
βββ workflow.json # the v3 graph: top-level DAG + one entry per subgraph
βββ scripts/ # agent-authored scripts referenced by type:script nodes
β βββ perceive_dino_vlm.py
β βββ plan_grasp.py
β βββ ...
βββ checkpoints/ # LLM-authored postcondition sidecars per subgraph
β βββ grasp_sg.py # (validate=True predicates, enforced against
β βββ transport_sg.py # sim ground truth at subgraph exit)
βββ agent_traces/ # per-agent LLM transcripts for debugging
βββ multi_agent_meta.json # pipeline provenance (models, retries, timings)
grocery_fulfillment/sample_generated_graph
is a checked-in, unedited example of one (its workflow.json, scripts/,
and checkpoints/ β the agent_traces/ and multi_agent_meta.json debug
artifacts are not committed).
Validate and run the result#
uv run gap run my_graph/task_00 --validate-only # structural + skill checks
MUJOCO_GL=egl uv run gap run my_graph/task_00 \
--sim libero_object_all_variance/0 # execute on LIBERO
uv run gap viz # browse the trace
Or in Python:
gap.execute(graph.path, gap.connector.sim("libero", task=...)). See
Execution and Traces.
Providers#
Provider |
Setup |
|---|---|
|
|
|
|
Both providers default to gemini-3.1-flash-lite-preview; override per call
with --model gemini-3.1-pro-preview (or llm: {model: ...} in the
config). OpenRouter accepts Gemini slugs with or without the google/
prefix.
Pick per call with --provider/--model, or pin everything (endpoint,
temperature, max tokens, concurrency, per-agent models) in a config YAML
passed via --config:
llm:
provider: openrouter
model: my-model
endpoint: http://localhost:8000/v1
See LLM providers for the full config reference, and Generation for how the multi-agent pipeline works.
Benchmark-scale generation#
Generating one graph is the unit; the benchmark harness drives the same
pipeline over task Γ seed grids. See
grocery_fulfillment for the flagship recipe β
gap generate on grocery instructions under pose / permutation /
basket-swap variations, gated by the configβs gate_threshold β and
Benchmarking for the harness. A single
green run is not a success-rate claim: gate it with
gap benchmark <yaml> --gate.