2025年11月20日

拒绝鼠标:基于Google GenAI的SolidWorks自动化交互接口设计


上个月帮朋友做一个自动化项目,需求很简单:让 SolidWorks 能听懂人话。比如直接说”在上视基准面画个100x50的矩形”,软件自己就把模型建出来。

听起来不难?我一开始也是这么想的。毕竟现在 LLM 这么火,直接把用户输入丢给模型,让它生成 C# 代码调用 SolidWorks API 不就行了?

结果我踩了整整一周的坑。先是 GPT-4 生成的代码语法错误率奇高,后是 Gemini 动不动就”发挥创意”给我加些不存在的 API。试了大概 50 多次请求后,我才意识到问题根本不在 API 对接,而在于怎么限制 LLM 的发散性。

最后方案是:用 Google GenAI (Gemini) 做语义理解,但让它输出 JSON 而不是代码。Python 负责中间转换,C# 老老实实做执行层。折腾这么久,觉得值得记录一下踩坑过程。

架构设计:分层是逼出来的

一开始我犯了个低级错误——想让 LLM 直接生成 C# 代码。想着这样最省事,省掉中间层。结果噩梦开始了:

  • Gemini 生成的代码经常引用不存在的命名空间
  • COM 对象跨线程访问直接崩溃
  • 代码逻辑对了但 SolidWorks 版本 API 不兼容

折腾了 3 个晚上,改了 20 多版 Prompt,成功率只有 30% 左右。最后我妥协了:必须加中间层。

现在的架构分成四层:

交互层:Python + 简单的 Web UI(我用 Gradio 糊了一个,丑但能用)。主要是接收用户输入,管理会话上下文。

决策层:Gemini 负责语义理解。关键是这里不生成代码,只输出 JSON。这是稳定性提升最明显的改动。

桥接层:Python 解析 JSON,校验参数合法性。比如检查尺寸是不是负数,基准面名称是否存在。这层我写了大概 400 行 Python,主要是防御性编程。

执行层:C# 封装好的 SolidWorks API。这层完全不接触 LLM,只干实事。

数据流大概是这样: 用户输入 -> Python 调用 Gemini -> Prompt 注入上下文 -> LLM 推理 -> JSON 输出 -> Python 解析校验 -> C# DLL 执行 -> SolidWorks

说实话,这个架构挺啰嗦的。但没办法,直接用 LLM 写代码太不靠谱了。

API 调度员:目前最靠谱的方案

在尝试了三种模式后,我最后只敢把”API 调度员”模式用在生产环境。其他两种?问题太多了,后面再说。

这个模式的核心思路是:LLM 不碰代码,只输出结构化数据(JSON)。把”翻译自然语言”和”执行操作”彻底分开。

我之前试过让 Gemini 直接生成 C# 代码,结果它老是擅自发挥。比如我说”画一个圆柱”,它可能给我生成包含材质设置、倒角、甚至装配操作的代码。而 JSON 模式就老实多了,因为 Schema 是固定的,它只能填空。

Prompt 我是这样写的:

**Role:** SolidWorks Automation Orchestrator.
**Objective:** Translate natural language requests into executable JSON commands based on the defined toolset.
**Constraints:**
1. Do not generate C# code. Output ONLY JSON.
2. Identify missing parameters based on engineering logic. If critical parameters are missing, request clarification.
3. Ensure topological validity (e.g., Sketch must precede Extrude).
**Tool Definitions:**
- `CreateSketch(plane, geometry_type, dimensions)`
- `ExtrudeFeature(depth, direction)`
- `Fillet(edge_selection, radius)`
**Response Schema:**
{
"operations": [
{ "function": "string", "args": { "key": "value" } }
]
}

实测下来,JSON 输出的准确率比直接生成代码高很多。我测了大概 30 组指令,JSON 的成功率在 85% 左右,而直接生成代码只有 40%。

举个例子,输入:“在上视基准面做一个100x50x10的铝板。”

输出:

{
"operations": [
{
"function": "CreateSketch",
"args": { "plane": "Top", "type": "Rectangle", "width": 100, "height": 50 }
},
{
"function": "ExtrudeFeature",
"args": { "depth": 10 }
},
{
"function": "SetMaterial",
"args": { "material": "Aluminum_6061" }
}
]
}

当然也有翻车的时候。比如用户说”做个盒子”,LLM 不知道具体尺寸,这时候 Prompt 里要求它请求澄清,但有时候它会擅自给个默认值(比如 100x100x100)。这个我还在调,目前是靠桥接层的校验来兜底。

设计助理:想法很好,但我不敢用

这个模式我花了不少心思,但现在还不敢用到正式环境。

思路是这样的:有些指令本身就很模糊,比如用户说”设计一个电机底座”。这种时候如果直接报错说”请提供尺寸”,体验很差。能不能让 LLM 扮演资深工程师,基于常识自动补全合理的默认值?

比如它自动推断:电机底座一般是铝材,厚度 10mm 比较合理,需要 4 个安装孔,孔径根据常见 M6 螺丝来定…

听起来很美好对吧?我一开始也这么觉得。但实测下来发现几个问题:

问题一:默认值不一定对。 我测了 10 个不同的模糊指令,LLM 给的默认值有 4 个是错的。比如它给”手机支架”默认厚度 5mm,但用户可能想要的是 3D 打印件,应该是 2mm。

问题二:用户不知道哪些是被推测的。 LLM 输出的 JSON 里不会标注”这个值是我猜的”,用户以为都是明确的指令。这会导致后续的修改困惑。

问题三:推理过程太长。 我要求它先输出推理过程再输出 JSON,但有时候它会搞混格式,或者推理到一半就停止输出了。

现在的 Prompt 长这样:

**Role:** Senior Mechanical Design Engineer.
**Context:** User instructions may be vague. Infer design intent and apply standard metric (mm) engineering defaults.
**Process:**
1. Analyze the request.
2. Explain the design logic and assumed dimensions.
3. Output the structured command block.
**Example Interaction:**
User: "Make a vertical cylinder."
Response:
"Analysis: Creating a cylinder on Top Plane.
Defaults: Diameter 50mm, Height 100mm (standard stock size).
[JSON_START]
{ "function": "CreateCylinder", "args": { "r": 25, "h": 100, "plane": "Top" } }
[JSON_END]"

说实话,这个模式我还在调试。可能需要用 RAG 来提供更准确的工程上下文,而不是让 LLM 瞎猜。目前只能当做实验功能,不敢给用户用。

脚本生成器:劝你别用

如果你正在看这篇文章想找最佳实践,那直接跳过这一节吧。这个模式我踩的坑最多,现在基本放弃了。

什么场景会想用代码生成?比如用户说:“给我生成 10 个孔,半径从 5mm 开始,每个增加 1mm,间隔 20mm 排成一排。“这种带循环的逻辑,JSON 确实不好描述。

我一开始想,那把逻辑控制交给 LLM 让它生成 Python 脚本呗。结果噩梦开始了。

幻觉问题太严重。 Gemini 经常给我生成根本不存在的库函数。比如它会写 sw_ops.create_hole(),但我实际提供的 API 是 sw_ops.cut_extrude()。最离谱的一次,它给我引入了一个叫 solidworks_llm_helper 的包,我上哪找这玩意儿去?

安全问题。 生成代码直接执行,万一 LLM 被诱导生成 os.system("rm -rf /") 怎么办?我后来加了沙箱,但沙箱又限制了一些正常的 SolidWorks COM 调用。

调试困难。 JSON 输出错了,我一眼能看出问题。但 Python 代码错了,可能是语法错误、逻辑错误、API 不存在、参数类型不对…排查一次要 20 分钟。

我试了大概 20 个带逻辑控制的指令,成功率不到 30%。最后我妥协了:这种需求我不支持。真要批量操作,让用户自己写脚本去。

如果你非要尝试,Prompt 可以这样写:

**Role:** Python Automation Developer for SolidWorks.
**Library Context:** Access to custom library `sw_ops`. Do NOT use raw `SldWorks` COM objects directly.
**Library API:**
- `sw_ops.sketch_circle(x, y, r)`
- `sw_ops.cut_extrude(depth)`
**Task:** Generate Python code to execute the user's logic. Wrap strictly in try-except blocks.

但听我一句劝,这玩意儿 toy project 玩玩可以,生产环境千万别用。

几点实战经验

折腾这么久,总结几条血泪教训:

一定要强制 JSON 输出。 Gemini 支持设置 response_mime_type: "application/json",这能大幅提升稳定性。我之前没开这个选项,模型有时候会给我输出 Markdown 格式的 JSON(带 ```json 标记),解析起来很痛苦。开了之后,虽然偶尔还是会有多余字符,但至少格式对了。

闭环反馈是必须的。 SolidWorks API 执行失败是常态,比如几何拓扑错误、参考面不存在之类的。这些错误一定要回传给 LLM,让它修正。我的 Prompt 会自动追加:“上一步操作失败,错误为{Error_Msg},请修正参数并重试。“实测下来,大概 60% 的错误 LLM 能自己修正,剩下的还是要人工介入。

Prompt 要持续迭代。 初期我提供了 3-5 个 Few-Shot 示例,效果还不错。但随着工具集增加,Context Window 开始吃紧。我现在在考虑用 RAG,只注入当前相关的 API 定义,而不是把所有工具都写进 Prompt。这部分还没完全实现,有经验的朋友可以交流。

别相信 LLM 说的”我理解了”。 有时候它会信誓旦旦地输出 JSON,但参数明显是错的。桥接层的校验不能省,该兜底的地方一定要兜底。

写在最后

回过头来看,这个项目最核心的收获不是技术方案,而是心态上的转变:别指望 LLM 什么都能做,把它当成一个”有点聪明但不太听话的实习生”更合适。

目前这套方案跑起来还算稳定,但离真正好用还差得远。JSON 模式虽然稳,但表达能力有限;设计助理模式想法好但不敢用;脚本生成器基本处于废弃状态。

下一步我打算试试 Gemini 的 Function Calling 功能,看能不能把工具定义和调用逻辑进一步简化。另外 RAG 的方案也在规划中,毕竟 Prompt 不能无限膨胀。

如果你也在做类似的项目,欢迎交流踩坑经验。特别是关于怎么减少 LLM 幻觉的问题,我现在还在头疼。

对了,这篇文章不是”最佳实践”,只是”我的实践”。方案肯定有很多问题,轻喷。