Generating Manufacturable assemblies with Multi-Agent CAD: A Practitioner's Look at MAC

Back to blog
Mehran Mozaffari·

What MAC actually is: Agents on a build123d Kernel

MAC is a code-first text-to-CAD pipeline built on build123d, the Python CAD library that sits on top of OpenCascade. You give it natural language, and the system writes and executes parametric Python that constructs real geometry. This is the category distinction that matters most: MAC is not a mesh generator, and it is not a sequence model predicting CAD command tokens. It's an agent loop driving a deterministic geometry kernel, and build123d is the part that makes the output trustworthy.

The central architectural bet is that frontier LLMs are already good enough at writing CAD code if you give them a closed loop to work inside. Rather than training a bespoke neural network on CAD command sequences, MAC spends inference compute at test time. A planner agent decomposes the request, a part-generation agent writes the build123d script, an executor runs it in the kernel, a visual inspector renders the intermediate result, and an independent judge agent evaluates whether the rendered geometry matches the nominal intent. When it doesn't, a repair agent takes the execution trace and the rendered evidence and produces a corrected script. The loop recurses until the judge passes or a step limit is hit.

That loop produces something fundamentally different from a single-pass generation. The output is B-Rep: exact analytical surfaces, real dimensions, a construction history you can open in SolidWorks or Fusion 360 and edit feature by feature. A triangulated mesh from a text-to-3D model has no parametric history, no true tolerances, no notion of a fillet radius being a parameter. For mechanical work that difference is the whole game — a mesh is a picture of a part, a B-Rep is the part.

What MAC adds beyond prior multi-agent CAD research — CADSmith's specifier-coder-reviewer consensus, CAD-Recode-style programmatic generation — is the assembly layer. Individual parts were already tractable; multi-part assemblies with kinematic joint definitions and URDF export for robotics simulation are the newer contribution. That's also where the hard problems live, which is where I'd spend my attention.

sequenceDiagram
    participant U as Engineer
    participant P as Planner Agent
    participant C as Coder Agent
    participant E as build123d Executor
    participant V as Visual Inspector
    participant J as Judge Agent
    participant R as Repair Agent

    U->>P: "mobile manipulator, 4-DOF arm on differential base"
    P->>P: decompose into parts and joint graph
    P->>C: part specs + mating constraints
    C->>E: build123d script
    E-->>C: execution error or B-Rep
    E->>V: tessellate and render
    V->>J: renders + measurement report
    J->>R: verdict fail - gripper overhangs base envelope
    R->>C: corrected part spec
    C->>E: revised script
    E->>J: re-rendered assembly
    J-->>U: pass - export STEP + URDF

Physical verification: Render, tolerate, and mate

The verification architecture is the most interesting engineering in MAC, and also where it breaks down. On a single part, the closed loop is genuinely strong. Code gets executed, which catches syntax and kernel-level errors immediately. The resulting B-Rep gets checked deterministically for manifoldness, self-intersection, and design-rule violations — these are algorithmic checks, not LLM judgments, and they're reliable. The geometry gets tessellated and rendered, and a vision-capable inspector looks at the image. Then an independent judge agent compares what it sees against the nominal intent from the planner. Crucially, the judge doesn't see the coder's reasoning trace, so it can't be anchored by a plausible-sounding justification for bad geometry. That independence is doing real work.

flowchart TD
    A[Text prompt] --> B[Planner agent]
    B --> C[Coder generates build123d script]
    C --> D[Executor runs script in kernel]
    D -->|exception| R[Repair agent]
    D -->|B-Rep produced| G[Deterministic geometric checks]
    G -->|non-manifold, self-intersection, DRC fail| R
    G -->|clean| V[Visual inspector renders geometry]
    V --> J[Independent judge agent vs nominal intent]
    J -->|fail| R
    R -->|corrected script| D
    J -->|pass| X[Export STEP / STL / GLB / URDF]
    J -->|step limit hit| Y[Emit best candidate + failure report]

Assemblies are a different problem class. A render tells you whether two parts look mated. It cannot tell you whether they mate correctly under tolerance. The gap is structural, not a matter of better vision models.

Take an interference fit. Two parts render as coincident when they're modeled at nominal dimensions, but a real press fit needs a designed negative clearance — a few hundredths of a millimeter that is entirely invisible at render resolution. Then add a tolerance stack-up across a four-joint chain: each joint contributes positional variance, and the aggregate can push a gripper outside its intended workspace envelope even when every individual part passed verification in isolation. The render of the assembled pose looks fine because it's evaluated at nominal, not at worst-case.

Redundant constraint definitions make it worse. If the planner defines a revolute joint and separately constrains the two parts to be coaxial, you've over-constrained the assembly. A solver will either fail or silently drop one constraint, and the visual result is identical to the correct case in both scenarios. You cannot eyeball your way to a well-posed constraint graph.

This is why I'd treat MAC's assembly output as a strong first draft rather than a finished model. For a mounting bracket, visual verification plus manifoldness checks are probably sufficient. For a kinematic chain feeding a URDF into Isaac Sim or Gazebo, the joint axes, link frames, and mate definitions need a human who understands the mechanism to review them before anything gets simulated or machined. The research is explicit that joint mating and spatial alignment can fail subtle tolerance and interference checks — I'd go further and say that's the load-bearing limitation for robotics use cases. Get a single part wrong and you catch it. Get a joint axis wrong and you find out when the arm drives itself into the base.

The economics of a 5.8M-token assembly

Here's the number that should reframe how you think about this class of system: roughly 5.8 million tokens to generate a mobile manipulator assembly. Let me make that concrete at retail frontier-API rates.

Frontier pricing has generally clustered around the low single-digit dollars per million input tokens and somewhere in the range of $10–$60 per million output tokens, depending on model tier. Agent loops are output-heavy in a way chat workloads aren't — every script the coder writes, every repair, every judge verdict, and the interleaved reasoning around each is generated text. If you call it a blended effective rate of roughly $12 per million tokens across a mixed input/output workload, 5.8M tokens lands around $70 for one assembly. Push toward a heavier output mix or a premium model tier and you're plausibly in the $150–$300 range for a single mobile manipulator.

Now price the iteration. Nothing about this loop converges on the first try, and every failed repair doesn't just cost a retry — it re-enters the full pipeline. The repair agent writes a fresh script, the executor runs it, the inspector re-renders, the judge re-evaluates. The judge is not free and it runs on every cycle, passing or failing. So cost scales super-linearly with topological complexity: more parts means more planner decomposition, more per-part loops, more cross-part mating checks, and more chances for a failure that restarts a cycle. A part that takes three repairs costs roughly four times the generation plus four judge evaluations.

The contrast with single-pass generation is stark in both directions. A direct neural CAD model does one forward pass — cheap, fast, and it will mangle anything with real topological constraints or multiple bodies. MAC buys correctness and editability with tokens, and it buys them at a price that makes speculative iteration genuinely expensive. I've found that's the discipline shift: with a single-pass model you generate ten candidates and pick one. With MAC you think before you prompt, because a bad spec doesn't cost you a retry, it costs you a full loop.

The failure mode I'd watch for is cost without convergence. A loop that hits its step limit and emits a best-effort candidate with a failure report has spent the full token budget and given you something unverified. That's the worst outcome — you've paid assembly-tier cost for draft-tier confidence. Track tokens-per-converged-assembly as your real unit of economics, not tokens-per-call. And watch the judge's verdict pattern: a loop bouncing between repair attempts with no monotonic improvement in the verdict is telling you the planner's decomposition is wrong, and no amount of repair tokens will fix a bad decomposition.

Single moderate part Complex multi-part assembly
Rough token consumption Low hundreds of thousands (fewer repair cycles, one judge pass per attempt) Millions — ~5.8M for a mobile manipulator, scaling super-linearly with part count and joint complexity
Estimated retail API cost Single-digit to low-double-digit dollars Roughly $70–$300 depending on model tier and output mix; worse if the loop fails to converge
Expected wall-clock time Seconds to a couple of minutes Minutes per assembly; each repair cycle re-enters the full pipeline
Human review required? Light — visual inspection plus manifoldness checks usually sufficient Effectively yes for anything kinematic — joint axes, link frames, and tolerance stack-ups can't be verified from a render

KCL vs build123d vs raw token generation: The correct tool for the domain

The most consequential choice in this space isn't the model, it's the code target. Three paradigms are in play: proprietary CAD DSLs like Zoo.dev's KCL, standard Python geometry libraries like build123d, and raw token generation where a network predicts CAD command sequences directly. They are not interchangeable, and picking wrong locks you in.

KCL is a custom language with a cloud execution kernel, and it's built around the assumption that the LLM should write constrained syntax verified fast by an optimized geometric engine. That's a real advantage. The grammar is tight, so there are fewer ways for a model to produce something that parses but doesn't build, and the kernel is tuned for low-latency execution rather than a local Python process spinning up OpenCascade. You get speed and you get deterministic geometry, and if your workflow is "prompt in, STEP out, at volume," that's a defensible default.

The catch is that KCL is proprietary and its kernel is remote. Your geometry construction becomes a service call to someone else's engine, in someone else's language. You can't open the kernel, you can't fork the DSL, and your pipeline's ceiling is whatever the vendor exposes. For a hobby project that's fine. For anything I intend to own and iterate on, that's a hard boundary.

MAC takes the opposite bet: sit on build123d, which is a Python wrapper over the OpenCascade kernel running locally. Every line the coder agent writes is ordinary Python you can read, step through, edit by hand, and re-run without a network round-trip. The construction history is inspectable in the sense that you own the script and can diff it. More importantly, the code target is swappable — there's nothing architecturally special about build123d versus CadQuery or OpenSCAD beyond the specific coder prompts. If a better kernel or DSL appears, the agent harness ports to it. That composability is the strategic value, and it's why I'd rather build on the Python stack even where it costs latency.

The price of that control is performance. Zoo's purpose-built engine executes faster than build123d marshalling calls through OpenCascade in a local process, and the difference compounds inside a multi-turn loop. The multi-agent architecture makes latency worse, not better: with planning, generation, render-inspection, and judging each adding a turn, minutes per assembly is the realistic floor, and that's before repairs. There's no interactive-modeling feel here.

So the rule I'd state plainly: reach for MAC when the output has to be editable CAD you can open in SolidWorks and edit by hand, and you want the whole stack local and hackable. Reach for KCL when you want low latency and low cost and you're willing to accept the closed ecosystem and the cloud kernel. It's a correctness-and-control versus latency-and-ceremony tradeoff, not a capability gap — and no amount of model improvement resolves it, because it's an architectural fork, not a quality one.

The multi-agent loop: Planner, Coder, Repairs, Inspections

I want to be precise about the division of labor here, because "multi-agent" gets used loosely and the specific split is what determines whether the system converges.

The planner does the structural work. Given a natural-language assembly spec, it decomposes the problem into named part subproblems plus the mate constraints between them — a differential base, four arm links, a gripper, and the joint graph that says which link rotates relative to which and about what axis. This is the highest-leverage agent in the system. A part-level loop can recover from a bad coder script, but a bad decomposition propagates: if the planner names a joint wrong or omits a mate constraint, no downstream repair fixes it, because the repair agents only see the subproblem they were handed. The decomposition is the spec, and everything else respects it.

The coder writes build123d for exactly one part at a time. That scoping matters. A single part is a tractable Python program, a bounded thing a model can get right, and it's the unit where the code-as-CAD bet pays off. The executor runs that script in the kernel and catches the two error classes that don't need any judgment at all: Python-level exceptions and kernel-level failures like an operation that produces invalid topology. That feedback is deterministic and immediate, and it's the cheapest repair loop in the system.

The visual inspector handles what execution can't. A script can run clean and produce the wrong shape — a bracket with the boss on the wrong face, a link that's the right length but missing a mounting feature. The inspector renders the geometry and evaluates shape intent and feature presence, which is a genuinely vision-model task because it's about appearance against intent rather than numerical truth.

The repair loop is the mechanism that stitches these together: execution errors, kernel topology complaints, and inspection feedback all become inputs that route back to the coder as a corrected part spec. The key design property is that repairs operate on one part's script, re-run through the same executor and inspector, rather than regenerating the whole assembly. That keeps the blast radius of a fix local.

The judge is the final gate, and its most important property is independence: it sees the nominal intent and the rendered evidence, not the coder's reasoning. That prevents a plausible-sounding explanation from laundering bad geometry past review. For assemblies the judge's remit widens — it's not just checking shape, it's checking kinematic structure and mate definitions, whether the joint graph the planner specified actually appears in the built parts. This is where I'd expect the most false passes, because mate correctness is exactly the thing a render can't adjudicate.

The framing that ties it together is test-time compute. There is no trained model here — no network that learned CAD. What the system does is spend inference tokens to search for geometry that satisfies the constraints, using execution and rendering as the search's reward. Every claim about MAC's quality and its cost follows from that one sentence.

Where MAC fails: A list of real failure modes

Cost is the headline failure mode but it's the least interesting. The engineering failure modes are what determine whether an assembly is actually usable, and they cluster in places that are structurally hard to verify.

The first is silent drift. The repair loop can converge to a drawing that looks plausible and quietly drops a requirement. This happens because the visual inspector judges a render against intent — it sees the object, not the engineering spec. If a part is supposed to include a specific mounting feature and the render is ambiguous at that scale, the inspector passes it, the judge passes it, and the loop terminates with a model missing a feature nobody will notice until it's machined. The failure is silent precisely because all the verification passed. A visually-fine assembly that fails a requirement check is worse than an obvious break, because there's no error to trace.

The second is entity naming drift. The planner names parts — base, link_2, gripper — and those names are the references the mate constraints and the assembly step depend on. When the coder regenerates a part in a repair cycle, or when the planner's naming shifts between iterations, references can desynchronize: a mate constraint points at a part name that no longer exists, or worse, resolves to a different part than intended. It's the classic entity-resolution bug in any multi-agent system, and here it produces subtle misalignments rather than crashes.

The third is mating precision. There is no solver in the loop for exact joint interference and no tolerance model at all. The system reasons about nominal dimensions and renders at nominal, so a real joint — one that has to drive a robot within tolerance — needs manual rework of joint axes, link frames, and clearances. For a URDF feeding a simulator, this is the difference between a model that stands and a model that walks itself into the base. The research says mating can fail subtle interference checks; I'd say that's the load-bearing limitation for robotics.

The fourth is non-determinism. Same prompt, different B-Rep — stochastic LLM inference means the coder doesn't produce identical scripts across runs, and neither do the repair decisions. That makes offline regression testing painful: you can't write a golden-file test comparing output geometry, because the output moves. You can only test invariants — does it converge, does it pass manifoldness, does the joint count match — and invariants are a much weaker test harness.

The fifth is kernel version drift. build123d sits on OCCT, and OCCT topology behavior changes across versions in non-obvious ways — fillets on unusual edge geometry being the canonical example, where an operation that succeeds on one version produces a subtly different or invalid solid on another. This is nearly impossible to catch from a screenshot, because the render looks the same. It surfaces downstream, in a STEP file a colleague opens in SolidWorks and finds broken. The mitigation is pinning both build123d and OCCT and testing on a fixed toolchain, which is unglamorous but the only defense I trust.

What binds these together is that MAC verifies the things that are cheap to verify — does it run, is it manifold, does it look right — and leaves the expensive, judgment-heavy checks as human work. That's an honest boundary, not a flaw. Treat the output as a strong first draft for anything kinematic, and the failure modes above become review checklist items rather than delivered defects.

Where it genuinely earns its money

The honest answer to "is this worth the tokens" is a category question, not a quality question. MAC earns its money when the deliverable is a parametric document that a human will finish, and it burns money whenever you ask it to be the finish line.

It's clearly worth it for common mechanical detail: mounting brackets, gear housings, simple bracket assemblies, standoffs, adapter plates. These are parts with a bounded feature set, a well-known construction idiom, and low topological branching — exactly the shape of problem the repair loop can actually converge on. A mounting bracket that would take me forty minutes in Fusion is often a two-or-three-repair MAC run, and the output is a build123d script I can edit rather than a mesh I can only look at. The same logic covers one-off STEP exports destined for a real CAD tool. If the workflow is "get me to a starting solid in twenty minutes, I'll take it from here," MAC is genuinely efficient.

It also earns its money as a neutral geometry source. Simulation teams need clean, watertight, dimensionally-real solids to mesh and load — not concept art. A mesh generator gives you unstructured triangles with no dimensions; MAC gives you a B-Rep you can hand a FEA harness. And for robotics, there's real leverage when the URDF rigging the system emits matches its joint definitions closely enough to drive a simulator — you get a first-pass kinematic model without hand-authoring link frames. The catch, as the failure modes section above lays out, is that "closely enough" is doing heavy lifting.

Where it does not earn its money is just as clear. Organic concept art: use a mesh generator, this is the wrong tool and you'll pay assembly-tier prices for a shell you don't want. Real-time front-end co-create: the minutes-per-assembly latency makes this structurally impossible, not a tuning problem. Expensive spec work: any workflow where a single failed iteration costs $10+ against a vague spec is a workflow you should not point at MAC — you'll spend the token budget exploring instead of building. And precise kinematic models with motion constraints: this is the failure mode, not a use case. MAC will hand you plausible-looking joints it cannot verify under load.

The positional claim I'd make: MAC sells you parametric documents, not finished designs. Engineers still intervene. If a team treats its output as a deliverable, they'll ship geometry that passed the loop and failed the spec. If they treat it as a draft with a script attached, the economics flip. That distinction — draft vs. deliverable — is the whole value proposition.

That's where three concrete projects get interesting. A reverse-engineering capture loop for legacy parts is a natural fit: feed a 2D drawing or a photo of a dimensional sketch through a vision model as planner input, generate the build123d script, export STEP, round to two decimals, and route to CAD review — but be warned, dimension annotation and tolerance semantics are extraordinarily hard to pull off raster input, so cost the repair loop carefully before committing. A self-hosted low-quota version of MAC swaps the heavy frontier step in planning and judging for a small 8B model via Ollama or llama.cpp, leaving only the coder on a frontier API — accept that small models drift on kernel syntax and plan for deeper repair loops; your per-token cost drops even if your per-prompt correctness does too. And a robot-kinematics syntax validator that parses MAC's URDF, runs passive forward kinematics against the B-Reps, and flags joints with no valid axis or parts that overlap at rest pose is the pragmatic answer to the mating gap — provided it runs before a human signs off, and provided you don't try to auto-fix what MAC was never designed to get right.

On the horizon: What remains unsolved and what I'd build next

The plateau is this: no agent framework in this paradigm can truly verify that a robot joint can physically be assembled and actuate under load without simulation. The judge agent compares a render to nominal intent. It has no model of interference under motion, no torque, no backlash, no manufacturing tolerance. Extend the loop for another ten thousand tokens and none of that changes, because the information isn't in the pixels — it's in the physics, and there's no physics engine in the loop.

So the interesting engineering work isn't a better judge. It's a hybrid: use the agent to generate candidate geometry, then fit explicit mating constraints into that geometry and run a deterministic constraint solve on top of it. The agent's job ends where the solver's begins.

My proposal is a joint solver agent — and I'd deliberately make it not speak natural language. Its interface is the CAD kernel and a physics engine: MuJoCo or Gazebo for kinematics, an AABB collision pass over the generated B-Reps for interference. It takes the joint definitions the planner emitted, loads the assembly, and answers concrete questions — does this revolute joint have a valid axis through both parts, do the link frames overlap at rest pose, is the declared range of motion reachable without collision, does the gripper clear the base at any configuration in its envelope. Those are deterministic checks with deterministic answers, which is exactly the property the current loop lacks. The visual inspector can't tell a coincident-mate from a press-fit; an AABB pass and a forward-kinematics sweep can tell you a gripper will intersect the base at 140 degrees, every time, reproducibly.

The right framing for a solver agent is that it produces evidence, not fixes. When it flags a joint with no acceptable axis, that's a planner-decomposition defect and belongs back at the planner — not at a repair agent trying to nudge a fillet. When it flags an interference at rest, that's a mate-constraint problem the coder can't solve without a tolerance model. The value is in surfacing the class of failure the vision loop is structurally blind to, so a human gets a specific, actionable complaint instead of a render that looks fine.

The rule of thumb I'd close with: treat MAC as a proposal engine. It generates valuable drafts — parametric, editable, dimensionally real, and worth the token spend when the spec is tight and the deliverable is a starting point. Verify everything the loop can't see, and never mistake "the judge passed" for "the assembly works." No amount of additional agent turns replaces a physics check, and writing that check doesn't mean MAC failed — it means you understood what it was for.

Resources

Updated 2026-09-15 by Mehran Mozaffari.

Related posts