rb — projects
rafabenedetti.com
case study · 2026
Projects — Case Study

Operator-Steered Data Visualization Agent

Raw data into polished, interactive visualizations — under a human operator's direction.

Type
Operator-in-the-loop agent
Schema
TypeScript, strict mode
Charts
Observable Plot · D3
Year
2026
01 Overview

An agent that turns raw data into polished, interactive web visualizations under the direction of a human operator. A single orchestrator coordinates a set of deterministic tools — profiler, designer, code generator, renderer, and critic — rather than a loose swarm of autonomous sub-agents.

The payoff of that choice is restraint: behavior stays inspectable and the failure surface stays small. The system is legible end to end, so when something goes wrong you can see exactly where.

02 The two representations

Two stable intermediate representations form the spine — one for what the data is, one for what the chart should be. Both are defined in a TypeScript schema, typechecked under strict mode.

DatasetProfile
The input side. Per-field inferred type, cardinality, null rate, domain, sample values, and detected semantics — plus dataset-level facts: row count, grain / primary key, and candidate temporal and geographic dimensions.
VizSpec
The design side, grammar-agnostic. Goal, audience, chart family, marks, per-channel encodings with their scales and transforms, interactions, and annotations.

An interaction taxonomy spans three capability bands, and a scope field — single versus linked — routes the work to the right generation tier without redesign. New data sources become thin adapters into the profile; new output formats become packagers out of the spec.

new source → adapternew format → packager→ the reasoning in the middle never moves
03 Generation & critique

Charts are generated on Observable Plot, with a continuous escape hatch down to raw D3 for anything the grammar can’t express. (Vega-Lite sits in reserve as an optional tier for coordinated multi-view dashboards.) Generated code is then exercised through two distinct failure channels and a closing loop.

01
jsdom smoke pass
A fast structural check that catches breakage cheaply — treated as approximate only, since jsdom returns zero for real SVG text metrics.
02
Headless render
The faithful path: renders the chart in Chromium via Playwright, where the geometry is real.
03
Vision critique
A vision model grades the screenshot against an explicit rubric — misleading or truncated axes, clipped legends, overlapping labels, weak color — defects invisible at the code level that one-shot generation never catches.
04
Root-cause regeneration
Severity-ranked findings collapse to root causes that drive a targeted regeneration — not a blind retry — capped at ~3 attempts so the loop converges instead of thrashing.

Generator and critic share the same rubric, so the system is graded against the very standard it is trying to meet.

04 The operator in the loop

Throughout, the human operator stays at the center, and the architecture is built to surface exactly the points where their judgment matters. Their goals, functional requirements, and — above all — their visual taste are treated as first-class inputs.

Because intent lives in an explicit, human-readable spec rather than buried in generated code, the operator intervenes at the level of design, not syntax — accepting, rejecting, or redirecting at any stage. The goal is convergence on what the operator finds correct and compelling, not a fixed house style.

author / edit the VizSpecdefine the rubricaccept · reject · redirect