"Build me an e-commerce platform with real-time inventory and payment processing."
That sentence becomes a 15-20 node interactive architecture diagram on a React Flow canvas in about four seconds. This post is for the engineers who want to know exactly how that pipeline works. No hand-waving. No "AI magic." The actual stages, the actual problems, and the code-level decisions that make it work.
A prompt-to-diagram pipeline
is a system that converts a natural-language description of software into a schema-validated graph of nodes and edges, positions that graph automatically, and renders it as an editable diagram.
If you want to try it first: codelit.io. Then come back and see what's happening under the hood.
What Are the Five Stages of a Prompt-to-Diagram Pipeline?
Codelit runs every prompt through five stages in a fixed order: intent parsing, pattern selection, structured generation, layout computation, and canvas rendering. Each stage hands a stricter data structure to the next one, and each stage has its own failure modes.
| Stage | What goes in | What comes out | Where the difficulty sits |
|---|---|---|---|
| 1. Intent parsing | Raw prompt text | Component list plus modifiers | The prompt is vague and the defaults have to be defensible |
| 2. Pattern selection | Component list | A topology (microservices, monolith, event-driven) | Catching cues like "keep it simple" that change the whole shape |
| 3. Structured generation | Components plus pattern | Node and edge JSON | Making an LLM emit valid, schema-conformant output every time |
| 4. Layout computation | Node and edge JSON | Coordinates for every node | Looking like an architecture diagram, not a plate of spaghetti |
| 5. Canvas rendering | Positioned graph | Interactive React Flow canvas | Keeping every node and edge type distinct and still editable |
Let me walk through them.
Stage 1: How Does Codelit Know What an E-Commerce Platform Needs?
Codelit's intent parser maps a domain phrase like "e-commerce platform" onto a known component set — product catalog, shopping cart, checkout, payment processing, order management, user auth — and then applies modifiers found in the rest of the prompt.
"Build me an e-commerce platform" is vague. Does it include a mobile app? Admin dashboard? Analytics? Recommendation engine?
I don't try to read minds. The parser extracts explicit requirements and infers reasonable defaults. Modifiers change the output from there. "With real-time inventory" adds a WebSocket layer and an inventory service with event-driven updates. "And payment processing" adds a payment gateway (Stripe-like) with webhook handling.
I built this as a two-pass system:
Pass 1: Keyword extraction and domain mapping. Fast, deterministic, zero AI cost. Handles the 80% case.
Pass 2: Send ambiguous elements to the LLM for classification. Only fires when Pass 1 can't resolve the intent confidently. This keeps the common cases fast and cheap.
Stage 2: Why Does the Diagram Include Services Nobody Asked For?
Because the selected pattern requires them. When Codelit picks microservices for a multi-domain e-commerce app, it also inserts an API gateway, a message broker, and load balancers. Nobody typed those into the prompt, but the architecture doesn't work without them.
Once I know the components, I need to decide how they connect. This is where architectural knowledge gets encoded. I maintain a library of patterns — monolith, microservices, event-driven, CQRS, serverless, hybrid — and each pattern defines a topology: how services communicate, where data flows, what infrastructure sits between them.
E-commerce with multiple domains (catalog, cart, payment, inventory) naturally fits microservices with an API gateway. But if the user says "keep it simple" or "this is for a hackathon," the system selects a modular monolith instead. That single phrase changes every downstream stage.
Stage 3: How Do You Get Schema-Valid JSON Out of an LLM? (Where I Lost Sleep)
You stop asking nicely and start validating. Codelit sends the target schema with the prompt, uses native structured-output modes where a provider offers them, parses the response with Zod, and feeds validation errors back to the model for up to three retries.
This is where most of the engineering effort lives. The LLM needs to produce a JSON object defining every node (type, label, properties, tier) and every edge (source, target, label, type). Getting LLMs to produce consistent, valid, schema-conformant JSON is genuinely hard. I tried three approaches before finding one that works.
| Approach | How well it worked | Where it broke | Verdict |
|---|---|---|---|
| Free-form prose, parsed afterwards | Brittle from day one | The model used different names for the same service across paragraphs, and the parser couldn't handle the variation | Scrapped after two weeks |
| JSON schema described in the prompt | ~80% of responses | The other 20%: commentary outside the JSON block, incorrect nesting, hallucinated fields | Not good enough |
| Schema enforcement with a Zod validation loop | Over 99% | Only on prompts that contradict themselves | What I ship today |
The prompt includes the target schema. I use structured output modes where available (OpenAI's response_format, Anthropic's tool use). When the response comes back, I validate it with Zod:
If validation fails, I send the Zod error back to the LLM with the original prompt and ask it to fix the output. That loop runs up to three times. The remaining failures are prompts that are genuinely contradictory ("build me a serverless monolith," and sure, I can try).
Here's What the Output Actually Looks Like
Stage 3 returns a flat JSON object with a nodes array and an edges array. Every node carries a type and a tier. Every edge carries a source, a target, and a type. For the e-commerce prompt, simplified, it looks like this:
That typed graph drives everything downstream: layout, rendering, styling, interactivity. It's also the graph chaos mode walks when it propagates a cascading failure, which only works because every edge already knows whether it carries a synchronous call or an event.
Stage 4: How Does Dagre Decide Where Each Node Goes?
Codelit lays the graph out with dagre, a JavaScript implementation of the Sugiyama algorithm for layered graphs. Nodes are grouped by tier and assigned to horizontal layers, then ordered within each layer to minimise edge crossings.
On top of dagre, I apply architectural conventions as post-processing heuristics:
- Databases always go at the bottom
- Load balancers center above the services they front
- Message queues sit between producers and consumers
- External services go on the right edge
This is the same problem I hit converting Mermaid syntax into an interactive canvas: the source tells you what connects to what and never where anything sits. Whatever invents the geometry has to be deterministic — same input, same output. If you regenerate the same architecture and the boxes land somewhere else, you stop believing the diagram means anything.
Stage 5: Rendering on the React Flow Canvas
React Flow renders the positioned graph, and the node and edge types from Stage 3 map straight onto visual treatments. The schema value decides the shape, the stroke and the animation, so nothing on the canvas is decorative.
| Element | Schema type | How it renders on the canvas |
|---|---|---|
| Node | database | Cylinder |
| Node | service | Rounded rectangle |
| Node | queue | Distinctive queue icon |
| Node | external | Cloud boundary |
| Edge | sync | Solid arrow |
| Edge | async | Dashed line |
| Edge | data-flow | Thicker line |
| Edge | event | Animated dashes |
Every element is interactive: click, drag, right-click to modify. The custom node components, the animated edges and the layout wiring are a subject of their own, and I pulled them apart in building the interactive architecture canvas with React Flow. The diagram is a starting point, not a finished product.
The Iteration Problem That Nearly Broke Everything
When a user follows up with "add caching," Codelit modifies the existing diagram instead of generating a new one. The model receives a compressed summary of the current canvas plus the modification request, and returns a diff: nodes to add, edges to add, properties to change.
The naive approach is to generate a completely new diagram. That throws away every manual adjustment — repositioned nodes, renamed services, added annotations — and users notice immediately. Correct behaviour is narrow: add a Redis node, connect it to the relevant services, leave everything else untouched.
Context management is critical here. A 30+ node architecture with full properties eats a massive chunk of the context window. I aggressively summarise: the LLM doesn't need every property, just the structural skeleton. Detailed properties stay client-side and get merged back after the diff.
Conversation history handles reference resolution across turns. "Make that database a cluster," which database? The one from the last turn. "Add a cache in front of it," in front of what? The service we just discussed. That context flows into every prompt.
Why Route Across 11 Providers Instead of Picking the Best One?
Because one provider is one point of failure, and because each model has different structured-generation habits. Codelit routes across 11 providers with automatic fallback, so a slow or unavailable model reroutes without the user seeing anything except a diagram.
I maintain provider-specific prompt variants. One model tends to over-generate and adds components nobody asked for. Another under-specifies edges, creating nodes but forgetting to connect them. The prompt variants compensate for those tendencies. The routing itself, and why a single provider is a liability, is a longer story I told in how Codelit's multi-provider fallback works.
The Zod validation loop normalises everything. Whichever model generated the output, it passes the same validation, and everything downstream just receives valid data. Provider differences don't leak past Stage 3.
What I'd Do Differently
Two things. I'd write the JSON schema and the Zod validation on day one, and I'd build the diff-based follow-up system before the first release. Both were retrofits, and both cost more to add late than they would have cost to start with.
I wasted months wrestling with inconsistent LLM output before realising that enforcing a structured contract is an engineering problem, not an AI problem. Retrofitting statefulness onto a pipeline that assumed every generation was independent was the same lesson in a different costume.
The pipeline keeps evolving. I'm working on multi-step generation for complex architectures (data layer first, then application, then edge) instead of generating everything in one shot. Early results show better separation of concerns.
The same structured-output backbone drives two more canvases now: product boards and agent workflows. You describe an agent job once, and Codelit designs the production version, model routing and approval gates included. If you want the rest of that story, how Codelit got built starts with a Lucidchart rage-quit.
Try it yourself at codelit.io and check out the full feature set. The gap between natural language and structured diagrams is absolutely bridgeable. You just need a pipeline that's engineered, not vibed.
Questions people actually ask
- How does AI turn a text prompt into an architecture diagram?
- In five stages. An intent parser maps the prompt onto a known set of components, a pattern selector picks a topology such as microservices or a modular monolith, a language model generates node and edge JSON against a fixed schema, a layout engine assigns coordinates to every node, and a canvas library renders the result as an editable diagram. Each stage hands a stricter data structure to the next.
- How do you stop an LLM from returning invalid JSON?
- You validate instead of prompting harder. Codelit sends the target schema with the request, uses a provider's native structured output mode where one exists, then parses the response with Zod. If validation fails, the validation error goes back to the model alongside the original prompt and it tries again, up to three times. Prompt-only structured output worked about 80 percent of the time. Schema enforcement plus the retry loop pushed that over 99 percent.
- How long does it take to generate an architecture diagram from a prompt?
- About four seconds, producing an interactive diagram of roughly 15 to 20 nodes. Most of that time is the language model generating structured JSON. The first parsing pass is keyword based and costs nothing, and layout is a deterministic computation over the returned graph rather than another model call. A failed validation adds a round trip, because the error is sent back to the model for correction.
- Can you edit an AI generated architecture diagram without regenerating it?
- Yes, and that matters more than it sounds. Regenerating from scratch throws away repositioned nodes, renamed services and annotations the user added by hand. Codelit sends a compressed summary of the current canvas along with the follow up request, and the model returns a diff rather than a new diagram: nodes to add, edges to add, properties to change. Detailed properties stay client side and get merged back in.
- Why route diagram generation across multiple AI providers?
- A single provider is a single point of failure, and models differ in how they handle structured generation. One over generates and adds components nobody asked for. Another creates nodes but forgets to connect them. Codelit keeps provider specific prompt variants to compensate, routes across 11 providers, and falls back automatically when one is down or slow. Schema validation normalises whatever comes back, so provider differences never reach the renderer.
- What layout algorithm positions the nodes in a generated diagram?
- Codelit uses dagre, a JavaScript implementation of the Sugiyama method for layered graph drawing. Nodes are grouped by tier into horizontal layers and ordered within each layer to minimise edge crossings. Architectural conventions are then applied as post processing: databases sink to the bottom, load balancers centre above the services they front, queues sit between producers and consumers, and external services move to the right edge. The layout is deterministic, so the same input always produces the same positions.