一 Key 配置實戰(zhàn))
1. 為什么要把裝配體零件絕對位置喂給 Cursor做非標設計的朋友大概率遇到過這個場景裝配體里幾十上百個零件你想讓 Cursor 或者某個 agent 幫你做點自動化的事比如批量導出、按位置分組、生成裝配說明結果發(fā)現(xiàn) agent 根本不知道每個零件在空間里的絕對坐標。它只能看到文件名和 BOM 數(shù)量位置信息全靠你手動截圖或者口述效率極低。SolidWorks 的裝配體本身是帶完整變換矩陣的每個零件相對于裝配體原點都有平移和旋轉。問題在于這些數(shù)據(jù)藏在 COM 接口里默認不會以文本形式暴露出來。我們要做的就是寫一段 C# 代碼遍歷裝配體組件樹把每個零件的絕對位置X/Y/Z 平移 旋轉四元數(shù)或歐拉角提取出來序列化成 JSON讓 Cursor 這類工具能直接讀。這篇聚焦三件事一是可復制的 C# 提取腳本二是用 TaoToken 統(tǒng)一 Key 管理模型調用的 config.toml 骨架三是 Cursor 側的接入配置和驗證步驟。適合有 SolidWorks 二次開發(fā)基礎、想讓 agent 讀懂裝配空間關系的工程師。整套流程我在實際項目里跑通過下面把踩過的坑和能直接用的代碼都攤開講。2. TaoToken 前置統(tǒng)一 Key 與 config.toml 骨架在寫提取腳本之前先把模型調用的通道理順。Cursor 里如果每個項目都單獨配 Key切換起來很煩而且 agent 調用模型時容易因為環(huán)境變量不一致報 401。TaoToken 的做法是給你一個統(tǒng)一 Key所有下游工具Cursor、腳本、CLI都指向同一個入口配置集中在一個 config.toml 里。先到控制臺創(chuàng)建 Key地址是 https://taotoken.net/api-keys 登錄后新建一個復制出來。這個 Key 后面會同時用在 Cursor 的模型配置和你的 C# 腳本里如果腳本需要調用模型做語義解析的話。config.toml 的骨架我建議這樣寫放在項目根目錄或者用戶目錄下都行# config.toml - TaoToken 統(tǒng)一配置骨架 [default] api_base https://taotoken.net/api api_key sk-你的統(tǒng)一Key timeout_seconds 60 [models] # 日常對話/輕量解析用這個 chat claude-sonnet-4-20250514 # 復雜裝配邏輯推理用這個 reasoning claude-opus-4-20250514 [cursor] # Cursor 讀取的模型別名映射 provider openai-compatible base_url https://taotoken.net/api model claude-sonnet-4-20250514 [solidworks] # 提取腳本輸出目錄agent 從這里讀位置數(shù)據(jù) export_dir E:\\code\\試驗\\裝配體導出 position_file assembly_positions.json這里有個細節(jié)要注意api_base用https://taotoken.net/api不要在后面加多余的斜杠否則某些 OpenAI 兼容客戶端會拼出雙斜杠導致 404。Key 不要硬編碼進 git 倉庫實際項目里用環(huán)境變量TAOTOKEN_API_KEY覆蓋config.toml 里留占位符就行。如果你打算長期用 Cursor 做編碼和 agent 任務可以看下 Coding Plan 的額度方案 https://taotoken.net/coding-plan 比按次調用劃算。模型對話的入口在 https://taotoken.net/chat 用來快速驗證 Key 是否生效很方便。3. 可復制的 C# 提取腳本遍歷裝配體拿絕對位置核心思路是遞歸遍歷AssemblyDoc的組件對每個組件調用GetTotalTransform或者逐級累乘變換矩陣得到相對于裝配體原點的絕對變換。SolidWorks API 里Component2有個GetTotalTransform(bool)方法傳 true 會返回包含裝配體原點偏移的完整變換這正是我們要的。先定義數(shù)據(jù)結構方便序列化using System; using System.Collections.Generic; using System.IO; using System.Text.Json; using SolidWorks.Interop.sldworks; using SolidWorks.Interop.swconst; namespace SwPositionExporter { public sealed class PartPosition { public string PartName { get; set; } string.Empty; public string PartPath { get; set; } string.Empty; public double[] Translation { get; set; } new double[3]; // X, Y, Z 絕對位置 public double[] RotationMatrix { get; set; } new double[9]; // 3x3 旋轉 public double[] EulerAngles { get; set; } new double[3]; // 便于人讀 public int Depth { get; set; } // 在裝配樹里的層級 } }然后是遍歷邏輯。這里的關鍵是GetTotalTransform返回的是一個 16 元素的數(shù)組前 9 個是旋轉矩陣行優(yōu)先第 10 到 12 個是平移量最后 4 個是透視相關一般忽略。public static class AssemblyPositionService { public static bool TryExportPositions( SldWorks swApp, ModelDoc2 assemblyModel, string outputJsonPath, out ListPartPosition positions, out string error) { positions new ListPartPosition(); error string.Empty; if (swApp null) { error SolidWorks 未連接; return false; } if (assemblyModel null || assemblyModel.GetType() ! (int)swDocumentTypes_e.swDocASSEMBLY) { error 當前文檔不是裝配體; return false; } try { var asmDoc (AssemblyDoc)assemblyModel; object[] components (object[])asmDoc.GetComponents(false); foreach (object obj in components) { var comp (Component2)obj; if (comp null || comp.IsSuppressed()) continue; // 關鍵true 表示包含裝配體原點偏移拿到絕對變換 double[] xform (double[])comp.GetTotalTransform(true); if (xform null || xform.Length 13) continue; var pos new PartPosition { PartName comp.Name2, PartPath comp.GetPathName(), Translation new[] { xform[9], xform[10], xform[11] }, RotationMatrix new[] { xform[0], xform[1], xform[2], xform[3], xform[4], xform[5], xform[6], xform[7], xform[8] }, Depth 0 }; pos.EulerAngles MatrixToEuler(pos.RotationMatrix); positions.Add(pos); } var options new JsonSerializerOptions { WriteIndented true }; File.WriteAllText(outputJsonPath, JsonSerializer.Serialize(positions, options)); return true; } catch (Exception ex) { error ex.Message; return false; } } // 旋轉矩陣轉歐拉角ZYX 順序方便 agent 理解朝向 private static double[] MatrixToEuler(double[] m) { double sy Math.Sqrt(m[0] * m[0] m[3] * m[3]); bool singular sy 1e-6; double x, y, z; if (!singular) { x Math.Atan2(m[7], m[8]); y Math.Atan2(-m[6], sy); z Math.Atan2(m[3], m[0]); } else { x Math.Atan2(-m[5], m[4]); y Math.Atan2(-m[6], sy); z 0; } return new[] { x * 180 / Math.PI, y * 180 / Math.PI, z * 180 / Math.PI }; } }調用的時候注意必須在 SolidWorks 主線程執(zhí)行COM 接口不是線程安全的。如果你在插件里跑直接調如果在獨立進程里跑得先ConnectToSW拿到SldWorks實例。// 調用示例 var swApp (SldWorks)Activator.CreateInstance( Type.GetTypeFromProgID(SldWorks.Application)); var model swApp.ActiveDoc; if (AssemblyPositionService.TryExportPositions( swApp, model, E:\code\試驗\裝配體導出\assembly_positions.json, out var positions, out var err)) { Console.WriteLine($導出 {positions.Count} 個零件位置); } else { Console.WriteLine($失敗: {err}); }輸出的 JSON 長這樣agent 讀起來毫無壓力[ { PartName: L1015x95鏈條護板-14齒, PartPath: E:\\cqh-圖紙\\設計模型庫\\鏈條護板\\L1015x95鏈條護板-14齒.SLDPRT, Translation: [120.5, 0.0, 340.2], RotationMatrix: [1,0,0,0,1,0,0,0,1], EulerAngles: [0,0,0], Depth: 0 } ]4. Cursor 接入配置與運行驗證拿到 JSON 之后要讓 Cursor 的 agent 能讀到。有兩種方式一是把 JSON 放進項目目錄agent 通過文件讀取工具直接讀二是通過一個輕量 HTTP 橋接agent 發(fā)請求拿數(shù)據(jù)。前者簡單后者適合數(shù)據(jù)實時變化的場景。先配 Cursor 的模型通道。打開 Cursor 設置找到 Models 部分添加一個 OpenAI 兼容的 provider{ models: [ { name: claude-sonnet-4-20250514, provider: openai, baseUrl: https://taotoken.net/api, apiKey: sk-你的統(tǒng)一Key } ] }如果你用的是 Cursor 的 config 文件方式直接寫進~/.cursor/config.json。配好后在 Cursor 里新建一個對話問它「讀取 assembly_positions.json告訴我 X 坐標最大的三個零件」如果它能正確解析并回答說明通道通了。驗證提取腳本本身我建議分三步走。第一步先在只有一個零件的簡單裝配體上跑確認GetTotalTransform(true)返回的平移量和你手動測量的位置一致。第二步加一個子裝配體檢查遞歸是否覆蓋到深層零件——注意GetComponents(false)只返回頂層要遞歸的話得對每個子裝配體再調一次。第三步對比 SolidWorks 界面里「評估 測量」的坐標誤差應該在 1e-6 以內。遞歸版本的關鍵改動private static void TraverseComponents( Component2 comp, int depth, ListPartPosition result) { if (comp null || comp.IsSuppressed()) return; double[] xform (double[])comp.GetTotalTransform(true); if (xform ! null xform.Length 13) { result.Add(new PartPosition { PartName comp.Name2, PartPath comp.GetPathName(), Translation new[] { xform[9], xform[10], xform[11] }, RotationMatrix new[] { xform[0],xform[1],xform[2], xform[3],xform[4],xform[5], xform[6],xform[7],xform[8] }, Depth depth }); } // 如果是子裝配體繼續(xù)往下鉆 var childAsm comp.GetModelDoc2() as AssemblyDoc; if (childAsm ! null) { object[] children (object[])childAsm.GetComponents(false); foreach (object c in children) TraverseComponents((Component2)c, depth 1, result); } }跑通之后你可以讓 Cursor 基于位置數(shù)據(jù)做更有意思的事比如「找出所有 Z 坐標大于 300 的護板生成一份安裝順序建議」。這時候 agent 有了空間信息回答質量完全不一樣。5. 本篇常見錯排查報錯一GetTotalTransform返回 null 或長度不足 13。最常見原因是組件被壓縮suppressed或者輕化lightweight。輕化組件需要先Resolve再取變換。加一句comp.Resolve()或者遍歷前把裝配體設為完全還原。報錯二位置全是 0。檢查你傳的參數(shù)是不是false。GetTotalTransform(false)返回的是相對于父級的變換頂層零件如果父級就是裝配體原點看起來就像 0。必須傳true才是絕對坐標。報錯三Cursor 報 401 Unauthorized。九成是 Key 沒對上。檢查 config.toml 里的api_key和 Cursor 設置里的是不是同一個以及有沒有多余空格。TaoToken 的 Key 以sk-開頭復制時別漏字符。接入文檔在 https://taotoken.net/doc 里面有各客戶端的配置示例。報錯四JSON 中文亂碼。File.WriteAllText默認用 UTF-8 無 BOM但如果你在中文 Windows 上用了Encoding.Default就會亂。顯式指定new UTF8Encoding(false)即可。報錯五agent 讀不到文件。Cursor 的工作區(qū)根目錄和你 JSON 輸出目錄不一致。要么把 JSON 放到工作區(qū)內要么在對話里給絕對路徑。我一般把export_dir設成項目子目錄省得來回切。報錯六COM 調用拋InvalidCastException。多半是SldWorks實例沒拿到或者 SolidWorks 版本和 Interop 程序集版本不匹配。確認引用的SolidWorks.Interop.sldworks.dll和你裝的 SolidWorks 主版本號一致。6. 把位置數(shù)據(jù)接進你的 agent 工作流位置 JSON 導出只是第一步真正省時間的是讓 agent 基于這些數(shù)據(jù)做決策。我現(xiàn)在的做法是裝配體一改跑一次導出腳本JSON 覆蓋更新然后在 Cursor 里直接問「對比上一版位置哪些零件移動超過 5mm」。agent 讀兩個 JSON 做 diff比人一個個量快得多。如果你想讓 agent 直接調用模型做語義分析比如「根據(jù)零件位置生成裝配工藝卡」那就需要腳本里也帶上模型調用。這時候統(tǒng)一 Key 的價值就體現(xiàn)出來了——C# 腳本、Cursor、CLI 工具全用同一個 Key不用到處配。模型對話入口 https://taotoken.net/chat 可以先手動試幾個 prompt確認效果再寫進自動化流程。長期跑編碼和 agent 任務的話Coding Plan https://taotoken.net/coding-plan 的額度更穩(wěn)不會因為單次調用超限中斷。API Key 管理在 https://taotoken.net/api-keys 接入細節(jié)看 https://taotoken.net/doc 。Claude Code 相關的配置參考 https://taotoken.net/claude-code 。最后留個實用技巧導出 JSON 的時候順手加一個exportedAt時間戳字段agent 判斷數(shù)據(jù)新舊會方便很多。還有裝配體零件名如果有中文JSON 序列化時確保JsonSerializerOptions沒開UnsafeRelaxedJsonEscaping之外的奇怪轉義否則 agent 解析中文名會出問題。