拒绝鼠标:基于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 幻觉的问题,我现在还在头疼。
对了,这篇文章不是”最佳实践”,只是”我的实践”。方案肯定有很多问题,轻喷。