Back to blog

Technical brief

Let the Agent Write the SQL. Then Validate It.

VuonApril 20264 min read

Seven or eight years ago, the semantic layer felt like the right answer to a hard problem. Looker formalized it. Cube made it open source. The idea was elegant: define your datasets, your relationships, your metrics, in one place, and then run every query through an engine that understood those definitions. You got a guarantee. The query the engine produced was semantically correct by construction, because the engine itself enforced the rules.

That model worked well for point-and-click analytics. It was never designed for agents.

When AI started showing up in data tooling, there were two obvious ways to adapt. The first was to hand the agent the semantic layer files directly, as context, and let it generate SQL from there. Simple to implement, but it quietly breaks the guarantee that made semantic layers valuable in the first place. The agent is only weakly referencing those files. It can drift. It will drift. Agents are correctness-seeking in a broad sense, but they'll escape-hatch your LookML the moment a more natural query path presents itself.

The second approach, which Looker, Cube, and some of the major cloud platforms have explored, is smarter but limiting. Instead of generating SQL directly, you get the agent to generate the rigid JSON payload that feeds the semantic engine, the same config a user would produce by toggling filters and selecting metrics in the UI. This preserves correctness. But it trades away the thing that makes agents actually useful: the ability to write expressive, creative queries that the point-and-click UI never could have produced.

OLD WAYintentvia LLMOutputvalidated SQLJSON intentfilters | metrics | dimensionsSemantic layerdatasets | joins | metrics | dimsSemantic engineconstrain intent → compile plan → SQLdeterministic

Both approaches are working around a mismatch. The semantic layer was built to constrain a deterministic query generator. Agents aren't deterministic query generators.

At Vuon, we decided to flip the architecture. Let the agent do what it's genuinely good at: writing SQL. Don't constrain it upfront. Then, after the query is generated, run a strict validation pass using a semantic validator we built specifically for this purpose.

The validator parses the AST of the generated query and evaluates it against the semantic layer, checking whether the query actually conforms to the rules: the joins, the metric definitions, the dimensional relationships. This is a procedural, deterministic step. It either passes or it doesn't. If it fails, the agent gets specific feedback and tries again.

This matters because it separates two concerns that the pre-AI architecture had fused together. The semantic layer no longer has to constrain the generation path. It just has to describe what's true about your data. The agent gets full latitude to write expressive SQL. And the validator provides the hard correctness check that the previous architecture built into the generator itself.

AI NATIVE WAYintentvia LLMOutputvalidated SQLSQL draftmodel writes SQLSemantic validation (AST)joins | metrics | relationshipspass / fail + specific feedbackfail → retrypass

Building the validator wasn't simple. We had to construct our own engine that could ingest semantic context, parse SQL syntax trees, and run validations that span both. But the result is an architecture that actually fits how agents work, rather than forcing agents into a shape built for something else.

The broader lesson here isn't really about semantic layers. It's about the pattern of taking pre-AI infrastructure and retrofitting it for agents. Usually, that produces something that's worse in some dimension, either too constrained, or too loose. The better question is: what shape does this component need to be in a world where the generator is a language model? In our case, the answer was: separate generation from validation, and make validation a first-class, deterministic step. The agent writes the query. We check the work.