Engineering3 min read
Why agents should emit structured blocks, not HTML
Letting an agent generate report markup directly is tempting and usually a mistake. We make the case for a constrained, schema-driven block model.
Zenitya TeamZenitya
When a team first wires an agent up to produce reports, the most direct approach is to let the model generate the document itself, usually as HTML or Markdown. This is tempting, because the model is already good at writing, and a single prompt can return something that looks like a finished page. However, in our experience this approach becomes difficult to live with as soon as more than a handful of reports are generated, and the reasons are worth setting out in detail.
The problem with free-form markup
The core issue is that free-form output gives the model control over both content and presentation at the same time. This means that every report is a fresh attempt at layout, styling, and structure, and small variations accumulate into a library of pages that no longer look like they belong together. Beyond consistency, there are concrete risks when an agent emits raw markup directly:
- Safety: arbitrary HTML or inline styles from a model are difficult to trust, and sanitizing them reliably is more work than it first appears.
- Governance: when the model owns the layout, the organization cannot enforce its theme, its components, or its accessibility requirements without re-parsing whatever was produced.
- Stability: a prompt change that improves the wording can silently alter the structure, which makes the output hard to test and hard to depend on downstream.
A constrained block model
The alternative we favour is to let the agent describe the report as structured data, and to render that data ourselves. In this model the agent does not produce a page, it produces a specification, which lists the blocks the report should contain (e.g., a metric, a chart, a table, or a set of recommended actions) together with the content for each one. The renderer then turns that specification into a page using components the organization already controls, so the agent decides what the report says, however it never decides how the report looks.
{
"title": "Weekly campaign review",
"blocks": [
{ "type": "metric", "title": "Spend", "value": "$48,200", "delta": "-6%" },
{ "type": "chart", "variant": "vertical_bar", "series": "conversions_by_day" },
{ "type": "table", "title": "Top campaigns", "columns": ["Campaign", "ROAS"] },
{ "type": "actions", "title": "Next steps", "items": ["Pause two underperformers"] }
],
"expiresIn": "72h"
}This shape has a few practical advantages. The output is easy to validate, because a schema can reject anything that does not match the allowed block types before a report is ever rendered. It is also easy to test, because the same specification will always produce the same page, which means a change in the agent's wording cannot quietly break the layout. Furthermore, the presentation can evolve independently, for example when the design system changes, every existing report adopts the new styling without re-running the agent.
What the agent still owns
It is worth being clear about what this model does not take away. The agent keeps full ownership of the analysis and the narrative, it decides which metrics matter, how to summarize them, and what to recommend. What it gives up is only the freedom to invent presentation, which is arguably not a freedom worth keeping, because consistent presentation is exactly what makes a report readable across a team. The current trade-off is that the agent can only use the blocks the schema defines, however we consider that a feature, since it is what keeps generated reports predictable and on-brand.
In summary, generating markup directly gives an impressive first result and a difficult tenth one, while a constrained block model gives a slightly more deliberate first result and a far more maintainable hundredth one. When it comes to reports that a business will actually rely on, predictability tends to matter more than flexibility, and separating content from presentation is the most reliable way we have found to get both.
Further reading
- OpenAI: Introducing Structured Outputs in the API
- Anthropic: Structured outputs (Claude API docs)
- Google for Developers: Mastering controlled generation with Gemini: schema adherence
Zenitya Team writes about generated reports, structured output for agents, and the practical side of turning analysis into something a team can open.
zenitya