Why CadQuery, Not Meshes: Choosing an Output Language for AI-Generated CAD
Most AI 3D tools generate meshes. We generate executable CadQuery Python — here's why that distinction is the whole point.
Before we wrote a single line of training code, we had to answer a question that determines almost everything downstream: what should the model actually output? The obvious answer, if you've used any of the current wave of "AI to 3D" tools, is a mesh — a cloud of triangles that looks like a shape when you render it. We didn't pick that, and the reason is not aesthetic. A mesh is the wrong data structure for a part someone intends to manufacture.
What a mesh can't tell you
A triangle mesh describes a surface. It doesn't describe intent. It has no concept of a hole being a hole rather than a locally concave region, no notion of two faces being parallel because a designer meant them to be, and no tolerances. You can't ask a mesh "what's the wall thickness here" without doing a separate geometric analysis, and you can't edit it — change a dimension, and you're re-sculpting triangles, not adjusting a parameter. For renders and games this is fine. For a part destined for a CNC mill or a laser cutter, it's a dead end: manufacturing needs exact, parametric geometry, not an approximation of a surface.
CadQuery as the output language
Our pipeline generates CadQuery Python instead. CadQuery is a scripting API for OpenCascade, the same B-Rep (boundary representation) kernel underneath most professional CAD software. A CadQuery program is a sequence of operations — sketch a profile, extrude it, cut a hole, fillet an edge — executed through OpenCascade to produce real solid geometry with faces, edges, and topology, then exported to STEP. That's a fundamentally different object than a mesh: it's an exact B-Rep solid with actual features, and it tessellates into a mesh only at the very last step, for preview.
We picked CadQuery specifically over two other candidates the design considered: raw DeepCAD JSON operation sequences, and build123d (a newer, more Pythonic OpenCascade wrapper). A few properties tipped it:
- Human-readable output. A CadQuery program reads like ordinary Python —
cq.Workplane("XY").box(10, 5, 3)is legible to anyone, which matters both for debugging during development and for the automated repair loop, where a traceback needs to map back onto something a retry prompt can reason about. - A strict, well-understood parser. Because it's just Python, syntax and runtime errors come from the standard interpreter, with real line numbers and real exception types — far more useful signal than a malformed proprietary operation sequence.
- A broad library of mechanical primitives. Extrudes, revolves, fillets, chamfers, shell operations, and sheet-metal-adjacent operations are already implemented and tested inside the library, rather than something we'd have to build from scratch.
- Direct execution through OpenCascade. Running a CadQuery program produces the same class of B-Rep solid a human CAD user would produce by hand, exportable straight to STEP and DXF — the formats CNC and sheet-metal shops actually consume.
Raw DeepCAD JSON was tempting as an output format because it's what the training data is natively shaped like, but it's a closed, undocumented schema with no execution semantics of its own — every consumer would need a custom interpreter, and errors in generated JSON have no natural traceback. Emitting Python that happens to call a real geometry kernel gets us error handling, an ecosystem, and readability for free.
The trade-off we accepted
None of this is free. Generating correct, executable code is a harder learning problem than generating a plausible-looking mesh — the model has to get syntax, geometric ordering, and dimensional consistency right simultaneously, and a single malformed operation can throw an exception instead of quietly producing an ugly-but-valid shape the way a generative mesh model would. We accepted that difficulty deliberately. A model that fails loudly and specifically is one we can build a repair loop around; a model that always "succeeds" at producing an unusable mesh is one we can't. Choosing an output language that maps onto real manufacturing formats was the decision that made the rest of the architecture — the sandboxed executor, the validator, the repair loop — possible in the first place.