> AI 帮你填了一张表单——你怎么知道哪些字段可以直接确认、哪些需要仔细核对?EAMX 的 DynamicForm 用 A/B/C 三组分类 + 绿/黄/红置信度着色 + 来源标签,让用户在 3 秒内做出判断。本文详解 prepare_equipment_form 的完整表单构造逻辑。
传统 AI 表单有两个极端:
EAMX 的解法:给每字段一个置信度 + 一个来源标签,让用户按颜色有选择地核对。
``<br>铭牌照片 / 合同 PDF<br> │<br> Stage 1: nameplate-extractor → RawField[] (含 confidence)<br> Stage 2: field-classifier → ClassifiedFields<br> │<br> ┌─────┼─────┐<br> │ │ │<br> A类 B类 C类<br> │ │ │<br> ▼ ▼ ▼<br>DynamicForm UI Card<br>``
| 分组 | 来源 | 置信度 | 前端颜色 | 用户操作 |<br>|---|---|---|---|---|<br>| A 类 | 铭牌/合同 OCR 提取 | 0.85-0.95 | 🟢 绿色 | 闭眼过 |<br>| B 类 | 历史数据推断 + 系统默认 | 0.55-0.80 | 🟡 黄色 | 看一眼 |<br>| C 类 | 未映射的技术参数 | 铭牌置信度 | 📋 灰色 | 存在 JSONB |
```typescript<br>// equipment.agent.ts — prepare_equipment_form<br>const conf = (val: unknown, high = 0.92, low = 0.0) =><br> val != null && val !== "" ? high : low;
const classAFields = [<br> { key: "name", label: "设备名称", confidence: conf(p.name, 0.95),<br> source: p.name ? "铭牌" : "未识别", required: true },<br> { key: "model", label: "型号/规格", confidence: conf(p.model, 0.92),<br> source: p.model ? "铭牌" : "未识别", required: false },<br> { key: "serialNumber", label: "出厂序列号", confidence: conf(p.serialNumber, 0.92),<br> source: p.serialNumber ? "铭牌" : "未识别", required: false },<br> { key: "ratedPower", label: "额定功率", confidence: conf(p.ratedPower, 0.88),<br> source: p.ratedPower ? "铭牌" : "未识别" },<br> { key: "weight", label: "重量", confidence: conf(p.weight, 0.85),<br> source: p.weight ? "铭牌/手册" : "未识别" },<br> { key: "purchaseValue",label: "原值(元)", confidence: conf(p.purchaseValue, 0.88),<br> source: p.purchaseValue ? "合同/发票" : "未识别" },<br> // ... 共 14 个 A 类字段<br>];<br>```
置信度赋值规则:有值 → 给一个基于"该字段在铭牌上通常有多清晰"的经验置信度。无值 → 0,前端渲染为红色"待填写"。
B 类字段需要"台账管理"但不直接从铭牌/合同中提取——分类、部门、位置等。EAMX 从同型号历史设备推断:
``typescript<br>// 查询同型号已有设备(最近 5 条)<br>const similar = await db<br> .select({ category, department, location, grade })<br> .from(equipmentsTable)<br> .where(and(eq(tenantId), ilike(model, %${modelVal}%`)))<br> .orderBy(desc(createdAt)).limit(5);
// 取出现最频繁的值<br>bSuggestion = {<br> category: freq(similar.map(s => s.category)),<br> department: freq(similar.map(s => s.department)),<br> location: freq(similar.map(s => s.location)),<br> grade: freq(similar.map(s => s.equipmentGrade)),<br>};<br>```
在表单上呈现为黄色 + 来源标签:
``typescript<br>{<br> key: "category", label: "设备分类",<br> value: bSuggestion.category,<br> confidence: 0.72,<br> source: "AI建议 · 同租户历史设备",<br> warning: 基于同类型设备历史归类建议:${bSuggestion.category},<br>},<br>{<br> key: "department", label: "使用部门",<br> value: bSuggestion.department,<br> confidence: 0.65,<br> source: "AI建议 · 同租户历史设备",<br>},<br>``
B 类字段的黄色着色和 warning 提示确保用户不会盲目接受 AI 的推断。
铭牌上大量不属于台账列的参数(额定电压、工作压力、防护等级等),全部进入 technicalSpecsJson:
```typescript<br>// field-classifier.ts — 未命中 FIELD_MAP 的字段<br>if (!mappedKey) {<br> if (!(field.label in technicalSpecs))<br> technicalSpecs[field.label] = val;<br>}
// flattenToFormParams — 序列化为 JSON 字符串<br>params.technicalSpecsJson = JSON.stringify(technicalSpecs);<br>```
前端以可编辑技术参数区域展示,每条带 [铭牌] 来源标签。
``typescript<br>return {<br> type: "UI_CARD",<br> cardType: "dynamic_form",<br> formTitle: "设备台账确认",<br> taskId: "create_equipment", // 前端提交时回传<br> description: "请核对AI提取的设备信息...",<br> fieldGroups: [<br> { id: "classA", title: "A类 — 基本信息(从文档提取)",<br> description: "AI直接从铭牌/合同/发票中识别的字段,绿色表示高置信度" },<br> { id: "classB", title: "B类 — 管理属性(AI建议,请核对)",<br> description: "台账管理所需但文档未记录的字段" },<br> ],<br> fields: [...classAFields, ...classBFields],<br> technicalParams, // C 类<br>};<br>``
前端按 fieldGroups 分组渲染,根据 confidence 自动着色,根据 source 显示标签,根据 required 标记必填。
| 文件 | 职责 |<br>|---|---|<br>| agents/equipment.agent.ts L226-438 | prepare_equipment_form —— A/B/C 三组构造 |<br>| agents/field-classifier.ts | Stage 2 纯代码分类、flattenToFormParams |<br>| agents/nameplate-extractor.ts | Stage 1 视觉提取(提供 A 类字段的原始 confidence) |
下一篇:插件化架构设计——PluginRegistry 74 行核心代码如何支撑 11 个独立模块。
作者:白杨,十余年设备资产管理从业经验,EAMX 产品负责人。