text-to-cad-harness

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

⚙ Text-to-CAD Harness

⚙ 文本转CAD工具

Skill by ara.so — Daily 2026 Skills collection.
An open source harness that lets AI coding agents (Claude Code, Codex, Cursor, etc.) generate, export, and preview 3D CAD models from natural language descriptions. Models are written in Python using build123d on top of OpenCascade (OCP), exported to STEP/STL/DXF/GLB/URDF, and inspected in a local React/Vite CAD Explorer viewer.

ara.so开发的技能 — 2026每日技能合集。
这是一款开源工具,允许AI编码Agent(Claude Code、Codex、Cursor等)根据自然语言描述生成、导出并预览3D CAD模型。模型基于OpenCascade(OCP),使用Python结合build123d编写,可导出为STEP/STL/DXF/GLB/URDF格式,并能在本地React/Vite CAD Explorer查看器中查看。

How It Works

工作原理

User prompt → Agent edits models/*.py → Python skill regenerates artifacts → Viewer previews geometry
  • models/
    — Source-controlled Python CAD files (build123d scripts)
  • skills/cad/
    — Bundled CAD skill (STEP, STL, DXF, GLB, snapshots,
    @cad[...]
    references)
  • skills/urdf/
    — Bundled URDF skill (robot links, joints, validation)
  • viewer/
    — Local React/Vite CAD Explorer (no backend required)

用户提示 → Agent编辑models/*.py文件 → Python技能重新生成工件 → 查看器预览几何模型
  • models/
    — 受版本控制的Python CAD文件(build123d脚本)
  • skills/cad/
    — 集成的CAD技能(支持STEP、STL、DXF、GLB、快照、
    @cad[...]
    引用)
  • skills/urdf/
    — 集成的URDF技能(机器人连杆、关节、验证)
  • viewer/
    — 本地React/Vite CAD Explorer查看器(无需后端)

Installation

安装步骤

1. Clone the repo

1. 克隆仓库

bash
git clone https://github.com/earthtojake/text-to-cad.git
cd text-to-cad
bash
git clone https://github.com/earthtojake/text-to-cad.git
cd text-to-cad

2. Set up Python CAD environment

2. 设置Python CAD环境

bash
python3.11 -m venv .venv
./.venv/bin/python -m pip install --upgrade pip
./.venv/bin/pip install -r requirements-cad.txt
Requires Python 3.11+. The
requirements-cad.txt
pins build123d, OCP, and all geometry dependencies.
bash
python3.11 -m venv .venv
./.venv/bin/python -m pip install --upgrade pip
./.venv/bin/pip install -r requirements-cad.txt
需要Python 3.11及以上版本。
requirements-cad.txt
中固定了build123d、OCP及所有几何相关依赖的版本。

3. Install viewer dependencies

3. 安装查看器依赖

bash
cd viewer
npm install
bash
cd viewer
npm install

4. Start the CAD Explorer

4. 启动CAD Explorer查看器

bash
npm run dev
Open http://localhost:4178 to browse generated models.

bash
npm run dev
打开http://localhost:4178浏览生成的模型。

Project Structure

项目结构

text-to-cad/
├── models/                  # Your CAD source files live here
│   └── my_part/
│       ├── part.py          # build123d Python source
│       ├── part.step        # Generated STEP export
│       ├── part.stl         # Generated STL export
│       └── part.glb         # Generated GLB for viewer
├── skills/
│   ├── cad/
│   │   ├── SKILL.md         # CAD skill documentation
│   │   └── ...
│   └── urdf/
│       ├── SKILL.md         # URDF skill documentation
│       └── ...
├── viewer/                  # React/Vite local viewer
│   ├── package.json
│   └── src/
├── requirements-cad.txt     # Python dependencies
└── assets/

text-to-cad/
├── models/                  # CAD源文件存放目录
│   └── my_part/
│       ├── part.py          # build123d Python源文件
│       ├── part.step        # 生成的STEP导出文件
│       ├── part.stl         # 生成的STL导出文件
│       └── part.glb         # 供查看器使用的GLB格式文件
├── skills/
│   ├── cad/
│   │   ├── SKILL.md         # CAD技能文档
│   │   └── ...
│   └── urdf/
│       ├── SKILL.md         # URDF技能文档
│       └── ...
├── viewer/                  # React/Vite本地查看器
│   ├── package.json
│   └── src/
├── requirements-cad.txt     # Python依赖文件
└── assets/

Writing CAD Models (build123d)

编写CAD模型(build123d)

All models live under
models/
as Python scripts using build123d.
所有模型以Python脚本形式存放在
models/
目录下,使用build123d编写。

Basic Part Example

基础零件示例

python
undefined
python
undefined

models/bracket/bracket.py

models/bracket/bracket.py

from build123d import *
with BuildPart() as bracket: # Base plate with BuildSketch(Plane.XY): Rectangle(80, 60) extrude(amount=5)
# Vertical wall
with BuildSketch(Plane.XZ.offset(30)):
    Rectangle(80, 40)
extrude(amount=5)

# Fillets on all edges
fillet(bracket.edges(), radius=2)

# Mounting holes
with BuildSketch(bracket.faces().filter_by(Axis.Z).sort_by(Axis.Z)[-1]):
    with Locations((-25, -15), (25, -15), (-25, 15), (25, 15)):
        Circle(3.5)
extrude(amount=-5, mode=Mode.SUBTRACT)
from build123d import *
with BuildPart() as bracket: # 底座 with BuildSketch(Plane.XY): Rectangle(80, 60) extrude(amount=5)
# 垂直壁
with BuildSketch(Plane.XZ.offset(30)):
    Rectangle(80, 40)
extrude(amount=5)

# 所有边倒圆角
fillet(bracket.edges(), radius=2)

# 安装孔
with BuildSketch(bracket.faces().filter_by(Axis.Z).sort_by(Axis.Z)[-1]):
    with Locations((-25, -15), (25, -15), (-25, 15), (25, 15)):
        Circle(3.5)
extrude(amount=-5, mode=Mode.SUBTRACT)

Export

导出

export_step(bracket.part, "models/bracket/bracket.step") export_stl(bracket.part, "models/bracket/bracket.stl")
undefined
export_step(bracket.part, "models/bracket/bracket.step") export_stl(bracket.part, "models/bracket/bracket.stl")
undefined

Running a Model to Generate Artifacts

运行模型生成工件

bash
./.venv/bin/python models/bracket/bracket.py
bash
./.venv/bin/python models/bracket/bracket.py

Parametric Part with Variables

带变量的参数化零件

python
undefined
python
undefined

models/hex_spacer/hex_spacer.py

models/hex_spacer/hex_spacer.py

from build123d import *
from build123d import *

Parameters — agent edits these values

参数 — Agent可编辑这些值

OUTER_DIAMETER = 12.0 # mm, across-flats HEIGHT = 10.0 # mm HOLE_DIAMETER = 5.0 # mm (M5 clearance) WALL_THICKNESS = 2.0 # mm
with BuildPart() as spacer: with BuildSketch(Plane.XY): RegularPolygon(radius=OUTER_DIAMETER / 2, side_count=6) extrude(amount=HEIGHT)
with BuildSketch(Plane.XY):
    Circle(HOLE_DIAMETER / 2)
extrude(amount=HEIGHT, mode=Mode.SUBTRACT)

fillet(spacer.edges().filter_by(Axis.Z), radius=0.5)
export_step(spacer.part, "models/hex_spacer/hex_spacer.step") export_stl(spacer.part, "models/hex_spacer/hex_spacer.stl")
undefined
OUTER_DIAMETER = 12.0 # 毫米,对边距 HEIGHT = 10.0 # 毫米 HOLE_DIAMETER = 5.0 # 毫米(M5间隙孔) WALL_THICKNESS = 2.0 # 毫米
with BuildPart() as spacer: with BuildSketch(Plane.XY): RegularPolygon(radius=OUTER_DIAMETER / 2, side_count=6) extrude(amount=HEIGHT)
with BuildSketch(Plane.XY):
    Circle(HOLE_DIAMETER / 2)
extrude(amount=HEIGHT, mode=Mode.SUBTRACT)

fillet(spacer.edges().filter_by(Axis.Z), radius=0.5)
export_step(spacer.part, "models/hex_spacer/hex_spacer.step") export_stl(spacer.part, "models/hex_spacer/hex_spacer.stl")
undefined

Assembly Example

装配体示例

python
undefined
python
undefined

models/assembly/assembly.py

models/assembly/assembly.py

from build123d import *
from build123d import *

Base

底座

with BuildPart() as base: with BuildSketch(Plane.XY): Rectangle(100, 80) extrude(amount=10)
with BuildPart() as base: with BuildSketch(Plane.XY): Rectangle(100, 80) extrude(amount=10)

Post

立柱

with BuildPart() as post: with BuildSketch(Plane.XY): Circle(8) extrude(amount=50)
with BuildPart() as post: with BuildSketch(Plane.XY): Circle(8) extrude(amount=50)

Combine into assembly

组合成装配体

assembly = Compound( children=[ base.part, post.part.move(Location((0, 0, 10))), ] )
export_step(assembly, "models/assembly/assembly.step") export_stl(assembly, "models/assembly/assembly.stl")

---
assembly = Compound( children=[ base.part, post.part.move(Location((0, 0, 10))), ] )
export_step(assembly, "models/assembly/assembly.step") export_stl(assembly, "models/assembly/assembly.stl")

---

Exporting Formats

导出格式

From within any
models/*.py
script, use build123d export functions:
python
from build123d import *
在任意
models/*.py
脚本中,可使用build123d的导出函数:
python
from build123d import *

STEP — full geometry, use for CAD interchange

STEP — 完整几何模型,用于CAD数据交换

export_step(part, "models/my_part/my_part.step")
export_step(part, "models/my_part/my_part.step")

STL — mesh for 3D printing / simulation

STL — 网格模型,用于3D打印/仿真

export_stl(part, "models/my_part/my_part.stl")
export_stl(part, "models/my_part/my_part.stl")

DXF — 2D drawing / laser cutting

DXF — 2D图纸/激光切割

section = part.section(Plane.XY) export_dxf(section, "models/my_part/my_part.dxf")
section = part.section(Plane.XY) export_dxf(section, "models/my_part/my_part.dxf")

GLB — viewer-compatible 3D web format

GLB — 兼容查看器的Web 3D格式

export_gltf(part, "models/my_part/my_part.glb")

---
export_gltf(part, "models/my_part/my_part.glb")

---

URDF Robot Descriptions

URDF机器人描述

The bundled URDF skill generates robot description files. See
skills/urdf/SKILL.md
for full docs.
集成的URDF技能可生成机器人描述文件。完整文档请查看
skills/urdf/SKILL.md

URDF Example Structure

URDF示例结构

models/my_robot/
├── robot.py          # build123d geometry for each link
├── robot.urdf        # Generated URDF XML
└── meshes/
    ├── base.stl
    ├── arm.stl
    └── gripper.stl
models/my_robot/
├── robot.py          # 每个连杆的build123d几何模型
├── robot.urdf        # 生成的URDF XML文件
└── meshes/
    ├── base.stl
    ├── arm.stl
    └── gripper.stl

Minimal URDF Output Pattern

最简URDF输出示例

xml
<!-- models/my_robot/robot.urdf (generated) -->
<?xml version="1.0"?>
<robot name="my_robot">
  <link name="base_link">
    <visual>
      <geometry>
        <mesh filename="meshes/base.stl"/>
      </geometry>
    </visual>
  </link>
  <link name="arm_link">
    <visual>
      <geometry>
        <mesh filename="meshes/arm.stl"/>
      </geometry>
    </visual>
  </link>
  <joint name="base_to_arm" type="revolute">
    <parent link="base_link"/>
    <child link="arm_link"/>
    <origin xyz="0 0 0.1" rpy="0 0 0"/>
    <axis xyz="0 0 1"/>
    <limit lower="-1.57" upper="1.57" effort="10" velocity="1"/>
  </joint>
</robot>

xml
<!-- models/my_robot/robot.urdf (generated) -->
<?xml version="1.0"?>
<robot name="my_robot">
  <link name="base_link">
    <visual>
      <geometry>
        <mesh filename="meshes/base.stl"/>
      </geometry>
    </visual>
  </link>
  <link name="arm_link">
    <visual>
      <geometry>
        <mesh filename="meshes/arm.stl"/>
      </geometry>
    </visual>
  </link>
  <joint name="base_to_arm" type="revolute">
    <parent link="base_link"/>
    <child link="arm_link"/>
    <origin xyz="0 0 0.1" rpy="0 0 0"/>
    <axis xyz="0 0 1"/>
    <limit lower="-1.57" upper="1.57" effort="10" velocity="1"/>
  </joint>
</robot>

CAD Explorer Viewer

CAD Explorer查看器

The local viewer reads exported files from
models/
and renders them in browser using WebAssembly (WASM).
本地查看器读取
models/
目录下的导出文件,通过WebAssembly(WASM)在浏览器中渲染模型。

Viewer Commands

查看器命令

bash
cd viewer
bash
cd viewer

Start dev server

启动开发服务器

npm run dev # → http://localhost:4178
npm run dev # → http://localhost:4178

Build for static hosting

构建用于静态托管的版本

npm run build
npm run build

Preview production build

预览生产构建版本

npm run preview
undefined
npm run preview
undefined

Viewer Features

查看器功能

  • Browse all models in
    models/
    directory
  • Inspect STEP/GLB geometry in 3D
  • Copy
    @cad[...]
    geometry references for agent follow-up edits
  • Quick snapshot renders for iteration review

  • 浏览
    models/
    目录下的所有模型
  • 以3D形式查看STEP/GLB几何模型
  • 复制
    @cad[...]
    几何引用,供Agent后续编辑使用
  • 快速生成快照渲染图,用于迭代评审

@cad[...]
Geometry References

@cad[...]
几何引用

After generating a model, the viewer provides stable
@cad[...]
handles. Paste these into your agent prompt to give it geometry-aware context for precise edits.
undefined
生成模型后,查看器会提供稳定的
@cad[...]
句柄。将其粘贴到Agent提示中,可为Agent提供几何感知上下文,实现精准编辑。
undefined

Example agent follow-up using a reference

使用引用进行Agent后续编辑的示例

@cad[models/bracket/bracket.step#face:top] — add a countersunk hole at center

---
@cad[models/bracket/bracket.step#face:top] — 在中心添加一个沉头孔

---

Agent Workflow (Step-by-Step)

Agent工作流程(分步说明)

Typical session with Claude Code or Codex

使用Claude Code或Codex的典型会话

1. User: "Create a parametric L-bracket with 4 mounting holes, 5mm thick"

2. Agent: creates models/l_bracket/l_bracket.py using build123d

3. Agent runs: ./.venv/bin/python models/l_bracket/l_bracket.py
   → generates l_bracket.step, l_bracket.stl, l_bracket.glb

4. User: opens http://localhost:4178, inspects the model

5. User copies @cad[...] reference from viewer

6. User: "Make the wall taller — @cad[models/l_bracket/l_bracket.step#face:wall]"

7. Agent edits WALL_HEIGHT parameter in l_bracket.py, reruns script

8. User commits models/l_bracket/ (source + artifacts together)

1. 用户:“创建一个带4个安装孔、厚度5mm的参数化L型支架”

2. Agent:使用build123d创建models/l_bracket/l_bracket.py文件

3. Agent运行命令:./.venv/bin/python models/l_bracket/l_bracket.py
   → 生成l_bracket.step、l_bracket.stl、l_bracket.glb文件

4. 用户:打开http://localhost:4178,查看模型

5. 用户从查看器复制@cad[...]引用

6. 用户:“把壁加高 — @cad[models/l_bracket/l_bracket.step#face:wall]”

7. Agent编辑l_bracket.py中的WALL_HEIGHT参数,重新运行脚本

8. 用户提交models/l_bracket/目录(包含源文件和工件)

Common Patterns

常用模式

Pattern: Slot / Cutout

模式:槽口/切口

python
with BuildPart() as panel:
    with BuildSketch(Plane.XY):
        Rectangle(100, 60)
    extrude(amount=3)

    # Horizontal slot
    with BuildSketch(Plane.XY):
        SlottedHole(length=30, radius=3, rotation=0, align=Align.CENTER)
    extrude(amount=-3, mode=Mode.SUBTRACT)
python
with BuildPart() as panel:
    with BuildSketch(Plane.XY):
        Rectangle(100, 60)
    extrude(amount=3)

    # 水平槽口
    with BuildSketch(Plane.XY):
        SlottedHole(length=30, radius=3, rotation=0, align=Align.CENTER)
    extrude(amount=-3, mode=Mode.SUBTRACT)

Pattern: Mirrored Geometry

模式:镜像几何

python
with BuildPart() as symmetric_part:
    with BuildSketch(Plane.XY):
        Rectangle(40, 20)
    extrude(amount=10)
    mirror(about=Plane.YZ)
python
with BuildPart() as symmetric_part:
    with BuildSketch(Plane.XY):
        Rectangle(40, 20)
    extrude(amount=10)
    mirror(about=Plane.YZ)

Pattern: Shelling a Solid

模式:实体抽壳

python
with BuildPart() as box:
    Box(60, 40, 30)
    # Remove top face to shell into an open container
    shell(box.faces().sort_by(Axis.Z)[-1:], thickness=-2)

python
with BuildPart() as box:
    Box(60, 40, 30)
    # 移除顶面,将实体抽壳为开放式容器
    shell(box.faces().sort_by(Axis.Z)[-1:], thickness=-2)

Troubleshooting

故障排除

ProblemFix
ModuleNotFoundError: build123d
Run with
./.venv/bin/python
, not system
python
Viewer shows no modelsCheck that
.glb
files exist in
models/
subdirectories
npm run dev
port conflict
Change port in
viewer/vite.config.*
STEP export fails silentlyEnsure the part solid is valid — check for
part.is_valid
before export
Python 3.12+ OCP errorsPin to Python 3.11 as required by
requirements-cad.txt
Fillet fails on sharp geometryReduce fillet radius or apply after all cuts
问题解决方法
ModuleNotFoundError: build123d
使用
./.venv/bin/python
运行,而非系统默认的
python
查看器未显示任何模型检查
models/
子目录中是否存在
.glb
文件
npm run dev
端口冲突
修改
viewer/vite.config.*
中的端口
STEP导出无提示失败确保零件实体有效 — 导出前检查
part.is_valid
Python 3.12+版本出现OCP错误按照
requirements-cad.txt
要求固定使用Python 3.11
圆角操作在尖锐几何上失败减小圆角半径,或在所有切割操作完成后再应用圆角

Validate a Part Before Export

导出前验证零件有效性

python
from build123d import *

with BuildPart() as my_part:
    Box(50, 50, 20)
python
from build123d import *

with BuildPart() as my_part:
    Box(50, 50, 20)

Check validity

检查有效性

assert my_part.part.is_valid, "Part geometry is invalid — check for bad operations"
export_step(my_part.part, "models/my_part/my_part.step") print(f"Exported: volume={my_part.part.volume:.2f} mm³")

---
assert my_part.part.is_valid, "零件几何无效 — 检查操作是否有误"
export_step(my_part.part, "models/my_part/my_part.step") print(f"已导出:体积={my_part.part.volume:.2f} mm³")

---

Skills Reference

技能参考

SkillDocsStandalone Repo
CAD (STEP/STL/DXF/GLB)
skills/cad/README.md
earthtojake/cad-skill
URDF (robots)
skills/urdf/README.md
earthtojake/urdf-skill

技能文档独立仓库
CAD(STEP/STL/DXF/GLB)
skills/cad/README.md
earthtojake/cad-skill
URDF(机器人)
skills/urdf/README.md
earthtojake/urdf-skill

Key Dependencies

核心依赖

PackagePurpose
build123d
Pythonic 3D CAD modelling API
OCP
/ OpenCascade
Geometry kernel (STEP, Boolean ops)
cadquery
Underlying geometry utilities
React 18
+
Vite 7
Viewer frontend
WASM
In-browser geometry rendering
用途
build123d
类Python风格的3D CAD建模API
OCP
/ OpenCascade
几何内核(支持STEP、布尔运算)
cadquery
底层几何工具库
React 18
+
Vite 7
查看器前端框架
WASM
浏览器内几何渲染