Ask an LLM to produce a paragraph and it's forgiving - readers fill the gaps. Ask it to produce structured output that a downstream system must consume - XML, JSON, a config, a query - and the rules change completely. One missing attribute, one wrong enum, one malformed tag, and the whole thing is silently useless. Worse: it looks right.
I ran into exactly this building an AI feature that turns plain-English requests into a strict domain XML format consumed by a legacy engine. The first instinct - the one everyone has - was to make the prompt better. Add more examples. Add more rules. It helped, until it didn't. The model would nail nine requests and quietly mangle the tenth, and there was no way to know which was which without opening every output.
A bigger prompt raises your average. It does nothing for your worst case - and in production, your worst case is the only thing that matters.
Stop calling the model. Start orchestrating it.
The shift that fixed it was conceptual: stop treating the LLM as a function you call once and trust, and start treating generation as a graph of small, checkable steps. The model is one node in that graph - not the graph itself.
Here's the shape of the pipeline I landed on:
# the generation graph, conceptually intent # parse the user's request → generate # LLM emits a structured JSON draft → render # deterministically turn JSON into target XML → validate # check XML against the schema (XSD) → repair # if invalid: feed errors back, regenerate ↺ # loop validate→repair, bounded retries → approve # human reviews before anything persists → persist
Generate JSON first, not the final format
The model never writes the final XML directly. It produces an intermediate JSON object, and a plain, deterministic renderer turns that JSON into XML. This one move removes a whole class of failures: the LLM doesn't have to get namespaces, escaping, and tag ordering right - code does that, the same way every time. The model only has to get the meaning right. Narrow the surface you trust the model with, and you narrow where it can fail.
The schema is your ground truth
Every rendered output is validated against the real schema before it goes anywhere. This is the part teams skip, and it's the most important: the validator, not the model, decides what's correct. The schema is a contract that doesn't drift, doesn't hallucinate, and doesn't have a good day or a bad day. If it passes validation, it's structurally sound by definition.
The repair loop is where "agentic" earns its name
When validation fails, the errors aren't thrown away - they're fed back into a repair step that regenerates with the specific complaints in hand: "element X is missing a required attribute, enum Y is not permitted here." The model is far better at fixing a concrete, named error than at avoiding an unknown one. The loop is bounded - a few attempts, then it gives up gracefully and flags for a human rather than spinning forever or shipping garbage.
This is the difference between a single prompt and an agent: the system observes the result of its own action and acts again. Not because it's autonomous - because it has a feedback signal worth acting on.
Humans approve. Always.
Even a structurally valid rule isn't auto-published. It enters an approval queue carrying its provenance - which model, what prompt, what confidence. The AI proposes; a person commits. That single guardrail is what makes the feature safe to put near a production system, and it's non-negotiable for anything with real consequences.
What I'd tell you to steal
- Put a deterministic layer between the model and the output format. Let the LLM own meaning; let code own structure.
- Validate against a real schema, every time. Your schema is the test suite for generative output.
- Feed errors back as a repair loop, bounded. Self-healing beats a perfect first try.
- Keep a human in the loop for anything irreversible. Provenance + approval, not blind autonomy.
- Treat the model as a component, not the system. The reliability lives in the orchestration around it.
Agentic AI gets sold as autonomy. In practice, the systems that actually survive contact with production are the opposite: tightly orchestrated, heavily validated, and humble about what the model is allowed to decide. The intelligence is in the loop - not the prompt.
Building something agentic?
I design AI systems and distributed architecture that hold up in production. Always happy to compare notes.
Get in touch →