上传 STEP,拿到报价要用的数:板厚、折弯道数、孔与孔径分组、展开毛坯、切割总周长、估重。可导出 Excel / CSV,也可由 agent 直接调 JSON 接口。算不准的地方它会说算不准。
把 STEP 拖进浏览器,或整包 ZIP 一起来——自动解包、目录并进文件名、记下它来自哪个包。也可以 POST /upload。
板厚、折弯道数与明细、孔与孔径分组、展开毛坯、切割总周长、估重。非钣金件给类型判断与依据,钣金口径标「不适用」。
勾选要的列 → 导出 Excel / CSV;同一份字段走 /api/analyze 给 agent;另有展开 DXF 与工程图 PDF。
同一个圆柱面,是折弯、是孔壁、还是去毛刺圆角,取决于它怎么被做出来。折弯认的是「内外两张圆柱面半径差恰好等于料厚」这一对同轴关系——不在配对里的圆柱面,一次性排除掉孔壁与装饰圆角。
展开毛坯用体积恒等式兜底:塑性变形材料不增不减,所以毛坯净面积恒等于 V/t。这条式子没有可调参数,对不上就说明漏摊了面板或重复计入——它抓出过 7 处算法缺陷。
K 因子从几何推不出来,所以毛坯给的是区间不是一个数;翻边孔的预冲孔并列给出两条独立算法的结果,标成待裁定项。该由人定的,标出来交给人。
铣削件 / 回转体 / 铸件照样给出类型判断与依据、孔与凸台清单。钣金专用口径此时明确标注「不适用」——而不是填个 0 让人误以为「这是块没折弯的钣金」。
一包图纸拖进来自动解包,目录并进文件名(订单A/支架/左板.step → 订单A__支架__左板.step),并记下它来自哪个包、包内原路径——之后按压缩包一键筛出「这单有哪些件」。路径穿越、解压炸弹、中文名乱码都已处理。
82 个字段、11 个分组,一份定义同时供网页表格、Excel/CSV 导出与 JSON 接口。看到的列就是导出的列,不存在两边对不上。
解析结果与缩略图写到磁盘,重启即用。缓存的钥匙里带着计算代码指纹:改算法必然作废,改样式、改译名不掉——用旧版本算出来的数,比重算慢糟得多。
只依赖 OCCT(读 STEP、算几何),其余全部标准库——连 xlsx / CSV 的写出器、ZIP 解包、账号体系都是现写的。装得上、跑得动、好审计。
| 文件 | 来源压缩包 | 状态 | 形态 | 板厚 | 外形 mm | 毛坯 mm | 折弯 | 孔 | 切割总长 mm | 估重 g |
|---|---|---|---|---|---|---|---|---|---|---|
| 订单A__支架__左板.step | 订单A.zip | 自动通过 | 钣金件 | 2.0 | 578×55×38 | 611×128 | 5 | 6 | 1590.5 | 1148 |
| 订单A__支架__右板.step | 订单A.zip | 自动通过 | 钣金件 | 2.5 | 1078×81×79 | 1227×97 | 4 | 16 | 3072.3 | 1768 |
| st416-170-009.stp | — | 非钣金件 | 棱柱类 | 不适用 | 17.7×17.7×7.8 | 不适用 | 不适用 | 不适用 | 不适用 | 6.9 |
前两行来自同一个 ZIP:目录并进了文件名,来源也记着,之后按压缩包一键就能筛出「这单有哪些件」。
最后一行是块铣削件:钣金口径标「不适用」,而不是填 0——0 会被求和、被筛选,读的人会当成「一块没折弯的钣金」。
# 传一件(原始 body,文件名走查询串) curl -b jar.txt --data-binary @支架.step \ '…/upload?name=支架.step' # 取数:与网页表格、Excel 导出同一份字段定义 curl -b jar.txt '…/api/analyze?path=支架.step' # 字段字典:键名、单位、口径说明,共 82 项 curl '…/api/schema'
字段定义只有一份,同时供网页表格、Excel 导出与 JSON 接口——不会出现「界面上有、导出里没有」。
另有 MCP 服务:让 agent 直接问「这批件里哪些要人工复核」,而不是先下载再解析。
字段字典 /api/schema 自带单位与口径说明,共 82 项。
python -m server ./parts --open,跑在自己机器上,图纸一步不出内网。/api/analyze、/api/inspect、/api/schema),另有 MCP 服务,让 agent 直接问「这批件里哪些要复核」。一件冲孔板的分析要跑十几秒。我们打开 cProfile 找热点,改完,几乎没快。
2026-08-19 · 约 3 分钟这句话听着像废话。它不是。
2026-08-18 · 约 3 分钟钣金报价里最容易错、也最难自查的一个数是展开毛坯——那块料摊平之后到底多大。
报价确认、拒绝或过期只是报价到了终态,不等于这条数据已经能拿来校准。工作台新增 “待回填结果”队列与实绩覆盖率:终态报价没有任何实际结果时重新成为明确待办, 填过以后自动离队。没有仍然不是 0。
详情页原先只能填实际成本与工时,但偏差报表还需要实际成交价、废品数和结果—— 这些字段只存在于 JSON API,日常浏览器流程喂不出完整样本。现在同一张追加表单可填 五项,仍走原来的旁流、会话身份和幂等号,报价快照一个字节不动。
真实性约束下沉到账本自身:负价格、负工时、非整数废品、NaN/Infinity 和错误类型 一律拒绝,网页、JSON API 与将来的导入器都绕不过去;真实的 0 保留并在报表上显示 为 0,不再被 or "—" 吞成缺失。新增 18 条判据,关键两刀已做变异验证。
报价主线随版本化部署默认开启:生产与测试使用同一份 restart.sh,启动时显式传入 MM_FLOW=1。若线上出现问题,可在数据目录的仓外 env 写 MM_FLOW=0 后由自动部署 拉起,立即退回旧文件目录入口;已落盘台账与历史快照不删除、不迁移。
M2 把历史报价定成不可变,而回填要往上面加"后来实际花了多少"——直接改记录就破了 不可变。道理上也该分开:报价快照说的是"当时报了多少、按的什么依据",实际 结果说的是"后来真花了多少"。
所以回填走一条追加式旁流(v<N>.actuals.jsonl,哈希链),报价快照一个字节 不动,判据逐字节核过。
填错了就追加一条指回旧的,旧的留着不删——历史账本上划掉的数也是证据。被顶掉的 是整条:第一条填了成交价与工时,第二条更正时只填成交价,那条工时也跟着作废。
一条实际结果都没有 → verified=false,平均偏差给 None。对空集合取平均是 0, 而 0% 偏差读起来像"报价很准"——那是这一层能给出的最坏的一个数。
覆盖率永远印在最上面(1 / 2):只报平均值不报覆盖率,等于把"三条里有一条" 说成结论。币种不符、报价当时就没算出总价 → 那条偏差不给;报价是 0 → 给差额 不给百分比(没有分母的百分比是编出来的)。
来源(erp/manual/shopfloor/import/unknown)与可回填字段各走白名单:塞一个没人 认识的键进去,报表上永远看不到它,而填的人以为自己填了。
门禁新增 45 条(回填 30、服务 +15),24 刀变异逐一转红。五刀第一版没红:被顶掉 那条只判了同名字段(补"整条作废")、序号检查被哈希检查顶掉(补重盖章的伪造序号)、 零分母那条走的是不可比分支(补 total=0 的用例)、缺参数的 400 被 404 顶掉、 横幅标题被同页说明文字顶掉(改判加粗那一句)。按指令不跑全量:受影响 344 全绿。
goal 写死了边界:只用可解释字段,显示"为何相似"与字段差异,不做黑箱向量 结论。
数值字段就是"相差多少个百分比",分类字段一样得 1 不一样得 0,总分是参与比较那 几项的加权平均。权重明摆在 WEIGHTS 里、也印在页面上——看不见权重的 "相似度"就是黑箱。
这几个数不四舍五入:判据要验 score == Σ(match×weight) ÷ Σweight 精确成立, 舍进去一位那个恒等式就只剩"约等于",而"约等于"里能藏下一个说不清的系数。
一边没有那个字段就记进 missing,不参与加权。把"不知道"当成"很像",排在第一 条的就可能是个八竿子打不着的件。
几何上像的两条,币种/费率指纹/税口径对不上时,归一化价格一律 None 并说明 ——把数算出来印在旁边,读的人不会去看那行小字,他会看那个数。
材料默认硬过滤:钢件的价拿铝件来参考,参考的是个寂寞。
结果里带 corpus,写明这些是系统报过的价、不是成交价也不是实际成本, 带实际回填的有 0 条;本仓生产上可导入的历史报价为 0 条(M1 审计)。
applies_to_current_quote 恒为 false。历史价格不覆盖当前的材料价、工时费率或 规则结果——当前报价永远由确定性规则现算。
门禁新增 29 条(相似 19、服务 +10),17 刀变异逐一转红。两刀第一版没红:页面 判据只查"权重"两个字(每条结果的表头里也有),以及一次变异打错了函数——同一段 文本在 history_detail 里也有,replace(...,1) 取了第一处。按指令不跑全量: 受影响 365 全绿。