设备铭牌是一台设备的身份凭证。品牌、型号、序列号、额定功率、制造日期等信息刻在铭牌上,并且只在铭牌上才是权威来源。 设备台账中这些字段正确与否,取决于录入时的取值是否来自铭牌本身,而非来自经手人的记忆或事后回忆。
传统工作流的做法是人工比对:设备管理员在设备现场读取铭牌,随后在系统中逐字段录入。一台设备耗时 3–10 分钟。 这段时间中风险最高的是序列号一类的高密度字符串——它不具备语义,无法凭常识校验,出错之后录入人员自身也难以发现。 台账中大量「型号相近、序列号错一位」的记录由此产生,并且在后续的维修、备件与折旧环节才暴露出来。
目标可以一句话定义:使用者在设备现场拍摄铭牌照片,系统自动提取结构化字段,返回一张已预填的设备台账表单,使用者只需核对确认。
把这一能力理解为在表单上加一个拍照按钮,会低估其中的工程约束。约束来自图像本身:铭牌的拍摄条件不受控制, 画面中可能出现反光、遮挡、倾斜、局部磨损与中英文混排;识别结果要写入的是有固定字段定义与权限规则的设备台账, 写错一个序列号的代价高于不写。以下各节说明这两端之间如何衔接。
对 AI 管线而言,铭牌建台账这一场景提出三项要求,并且三项要求之间存在张力:提高忠实性通常要牺牲速度,提高速度容易放宽对结果的约束。
| 要求 | 含义与判定方式 |
|---|---|
| 忠实性 | 看到什么提取什么,不推断、不编造、不遗漏。判定方式是字段取值能否在图像上逐字定位 |
| 性能 | 设备建档属高频操作,端到端延迟须控制在 3–8 秒以内 |
| 可靠降级 | 图片质量不足或画面中不含铭牌时,不展示空表单误导使用者 |
忠实性的难点在于视觉模型的默认行为是理解画面内容,其产物形态是一句结论,例如「这是一台螺杆式空压机」。 结论无法逐字段对应到图像上的字符,也就无法核对。管线因此把「理解」这一步完全排除在提取阶段之外, 要求模型只做字符级转录。
性能要求并非体验层面的偏好,而是这条管线能否被真正使用的前提。设备建档通常发生在设备旁边或收货现场, 操作者手中只有移动设备。延迟超出 3–8 秒这一区间后,操作者会退回纸质记录与事后补录的旧习惯,管线的价值随之消失。
可靠降级对应的是另一种损失:一张半空的表单比一张空表单更危险。使用者会默认系统给出的字段已经过校验,从而跳过核对; 未被提取的字段则被当作该设备本就没有此项。降级路径要做的,是在识别结果不足以支撑一张表单时明确说明原因,并给出下一步建议。
管线分为两个阶段,末端接一次动态表单构造。两个阶段之间的边界,是整条管线最重要的设计决定。
Stage 1 只负责「读」,完全不理解台账的字段结构;Stage 2 只负责「映射」,完全不依赖模型;Stage 3 把映射结果组装为前端可渲染的表单结构。 三个环节的输入与产出如下表。
| 环节 | 实现方式 | 输入与产出 |
|---|---|---|
| Stage 1 视觉提取 | Qwen-VL 视觉模型 | 输入铭牌图片(base64);产出铭牌原文键值对 RawField[],每项含 label、value、confidence、source 四个属性 |
| Stage 2 字段映射 | 静态查表,纯代码 | 输入 RawField[];产出台账标准字段、技术参数与来源映射 |
| Stage 3 表单构造 | 动作契约 prepare_equipment_form | 输入映射结果;产出 DynamicForm UI Card,含分组、置信度与来源标签 |
「提取—映射分离」带来三项可验证的好处:
识别精度与台账字段的正确性属于两类问题。把两者压在同一个模型调用里,会同时失去两者的可优化性与可核对性; 管线在这两件事之间划出一条明确的边界:模型只负责「看见什么」,代码负责「写成什么」。
依据:nameplate-extractor.ts、field-classifier.ts、equipment.agent.ts 的实现代码。
视觉提取选用 Qwen/Qwen3-VL-32B-Instruct,通过 OpenAI 兼容接口调用。选择依据有三项:
enable_thinking: false,关闭后首 token 延迟大幅降低。这一项在性能一节中还有进一步说明。
提取函数接收图片的 base64 编码、MIME 类型与来源名称,返回铭牌原文键值对数组。调用参数中有三处为固定值:
temperature: 0 保证同一图片的输出稳定,max_tokens: 1024 限制输出长度,
detail: "high" 要求按高精度解析图像细节。
PROMPT 即工业铭牌识别提示词,全文如下:
该提示词经过多次迭代,其中四项判断直接决定管线行为。
其一,锁定转录而非理解。「看到什么写什么」是整条管线的根基。视觉模型天然倾向于对画面内容作理解与推理, 会输出「这是一台空压机」之类的结论;Stage 1 需要的只是原始键值对。提示词第一句就锁定了方向,把理解排除在输出之外。
其二,模糊字符要求诚实标注。提示词要求对模糊字符给出 0.4–0.6 的置信度,而非一律给高分。 这一约定直接影响 Stage 3 的着色:置信度落在该区间的字段在前端标记为黄色,提示操作者逐字核对。
其三,输出格式锁定为 JSON。Qwen-VL 具有输出思维链的倾向,不加约束时会先输出大段推理再给出结果。 提示词末句要求「只输出 JSON,不要任何解释文字」,并与输出长度上限配合,避免把响应时间消耗在解释上。
其四,参数提取宁多勿少。工业铭牌的参数数量常在二十项以上。模型在做结构化提取时倾向于丢弃「看起来不重要」的参数,
例如防护等级 IP54、绝缘等级 F。设备台账的 technical_specs JSONB 列专门用于存放这类参数,
因此提示词明确要求每一个数值参数都要提取。
不同版本的 Qwen-VL 对纯 JSON 输出的遵从程度并不一致:有的会在 JSON 前后附加说明文字,有的会用 Markdown 代码块包裹。
因此 Stage 1 不依赖 response_format 一类接口参数保证格式,而由 extractJsonObject() 做容错解析。
解析思路是取第一个左花括号与最后一个右花括号之间的内容交给 JSON.parse。
无论模型在 JSON 前后附加了什么文字,中间的对象都能被取出;解析失败时返回空值,不抛出异常,由调用方按无结果处理。
依据:nameplate-extractor.ts 的调用参数与提示词全文;llm-json-utils.ts 的容错解析实现。
Stage 1 的产出是铭牌原文键值对,例如 { label: "额定功率", value: "37 kW", confidence: 0.88 }。
Stage 2 的任务是把它们分为两类:A 类为台账中有对应列的字段,C 类为台账中没有独立列的字段。这一阶段不调用任何模型。
FIELD_MAP 是约 60 条字符串映射,覆盖工业铭牌上的常见标注,归入 16 个标准字段。以下是其中一部分:
该表的设计原则有两条:维护成本低——新增设备类型只需追加别名条目,提示词与前端代码都不需要改动; 中英混合覆盖——同一字段同时收录中文铭牌、英文铭牌与中英混排铭牌的写法。
| 标准字段 | 典型别名 |
|---|---|
name 设备名称 | 设备名称、产品名称、品名。铭牌顶部最大字体的名称若无键名,由提示词规则填入该字段 |
model 型号 / 规格 | 型号、规格、Model、Model No.、Spec、Type |
serialNumber 出厂序列号 | 出厂编号、序列号、S/N、SN、Serial No.、Product No.。别名最多,也是最容易漏识的一项 |
ratedPower 额定功率 | 额定功率、功率、Power、Rated Power。取值含单位 |
manufacturer 制造商 | 制造商、生产厂家、Manufacturer、Mfr。可与合同来源交叉校验 |
铭牌字符在拍摄与识别过程中可能出现轻微偏差,因此匹配分两级进行:先去标点后做精确匹配,再退回子串匹配。
归一化处理的字符集包含中英文冒号、句读、顿号、各类括号、连字符与空白,比较前统一转为大写。 经过归一化,「额定功率:」与「额定功率」被视为同一字符串。子串匹配用于处理识别误差或铭牌版式造成的轻微偏差, 例如标注中夹带附加说明文字的情形。两级匹配都不命中时,字段落入 C 类,而不是被丢弃。
分类函数遍历键值对,对每一项先查静态表,再退回模糊匹配;命中标准字段的进入 A 类,未命中的进入技术参数集合。
分类逻辑中值得单独说明的是多源冲突处理:同一字段若由多个来源提供取值,管线保留置信度更高的那一项。 铭牌上的「制造商」置信度为 0.95 时,会优先于合同上的同名字段(置信度 0.78),并且表单中该字段的来源标签会随之显示为铭牌。
整个 Stage 2 为纯内存计算,实测耗时低于 0.5 毫秒,无任何网络 IO。这一阶段的稳定性不依赖外部服务, 也不随识别模型的更换而变化。
依据:field-classifier.ts 中 FIELD_MAP 与 classifyFields 的实现;耗时为本机实测口径。
分类结果交给动作契约中的 prepare_equipment_form,构造一张按来源分为三类的动态表单。
三类字段的差别在于取值来源与可信程度,前端据此采用不同的着色。
A 类字段由铭牌、合同、发票等文档直接提取,置信度直接沿用 Stage 1 的输出,不做事后调整。
置信度不低于 90% 的字段在前端渲染为绿色,操作者可以快速通过;必填字段与低置信字段渲染为红色,需要优先检查。
台账需要的管理属性(资产分类、使用部门、安装位置、设备等级)通常不出现在铭牌上。管线以型号为键, 查询同一租户下的历史设备,取出现频率最高的取值作为建议值。
B 类字段的来源标签显示为「AI 建议 · 同租户历史设备」,置信度通常落在 0.65–0.72 区间,前端渲染为黄色。 这类取值来自统计而非文档,操作者需要逐项确认后才写入台账。
铭牌上所有不属于台账独立列的参数,全部进入 technicalSpecsJson,以 JSONB 形式存储。典型内容如下:
前端以可编辑的技术参数区域展示这部分内容,每条带「铭牌」来源标签。这样处理有两点好处: 台账的列结构保持稳定,不为个别铭牌上的参数增设列;铭牌参数不因「台账没有对应列」而被丢弃。
Stage 3 的返回值是一张 UI_CARD,包含分组定义、字段数组与技术参数三部分。
前端收到 UI_CARD 后按 fieldGroups 分组渲染,每个字段依据 confidence 自动着色(绿 / 黄 / 红), 并附来源标签(「铭牌」「合同」「AI 建议」)。着色规则的作用是让操作者在数秒内判断哪些字段可以直接通过、哪些需要逐字核对。
| 类别 | 取值来源 | 典型置信度 | 前端表现 |
|---|---|---|---|
| A 类 | 铭牌、合同、发票等文档直接提取 | 由模型输出,示例值为 0.92–0.95 | 不低于 90% 渲染为绿色,可快速通过;必填与低置信字段渲染为红色 |
| B 类 | 同租户同型号历史设备的统计取值 | 通常 0.65–0.72 | 渲染为黄色,来源标注为「AI 建议 · 同租户历史设备」,需逐项确认 |
| C 类 | 铭牌上的技术参数,写入 technicalSpecsJson | 随提取结果,不单独设定 | 以可编辑的技术参数区域展示,每条带「铭牌」来源标签 |
识别结果的可信度如何呈现、哪些字段需要人工确认,均可由演示环境核对工具清单与动作定义,无需申请。 查看核验方式 →
铭牌识别并非一个独立的页面功能,而是 AI 助手内部的一条专用管线。是否走这条管线,由意图判断决定: 命中建档关键词时走两阶段快速管线,未命中时交由主模型自行判断。
当使用者上传图片,且消息中包含「建档 / 新建设备 / 录入设备」等关键词时,协调器直接走两阶段快速管线, 绕过模型的工具调用环节。
该路径的判定条件是一条关键词表与图片存在性的合取。命中后依次执行视觉提取、字段分类、有效性检查与表单构造四步, 其中只有第一步调用模型,后三步均在服务端完成,不产生额外的模型往返。
当使用者上传图片但未使用建档关键词(例如仅上传图片,或图片附带「帮我看看这个」这类含糊描述)时, 管线不走快速通道,而是先由视觉模型描述图片内容,再把描述并入文本交给主模型,由其自行选择动作。
两条路径的分工如下表。这一划分的目的是把高频且意图明确的场景从模型推理中剥离出来: 识别结果不因模型当次的判断而变化,延迟也不受提示词与工具清单长度的影响。
| 路径 | 触发条件 | 执行方式 |
|---|---|---|
| 两阶段快速管线 | 图片存在,且消息命中建档关键词表 | 视觉提取 → 字段分类 → 有效性检查 → 表单构造,绕过工具调用环节 |
| 模型判断路径 | 图片存在,但消息未命中关键词表 | 视觉模型描述图片内容,描述并入文本后由主模型自行选择动作,可能落到报修、入库等其他动作 |
若图片质量不足(模糊、反光、遮挡),或画面中不含铭牌,识别的返回结果可能为空或接近为空。判定条件是: 提取结果为空,或标准字段为零且技术参数少于两项。
命中降级条件时不展示表单,改由模型说明原因并给出建议。典型输出包括「图片中未识别到设备铭牌」 「铭牌模糊,建议重拍」「这是入库单,应使用入库功能」三类。 这一处理是对使用者信任的保护:宁可明确说明未识别出结果并给出下一步建议,也不展示一张半空且不可靠的表单。
依据:coordinator.agent.ts 中两条路径的分支条件、有效性判定与降级实现。
这一节的数字来自开发过程中的一次性能故障。它同时说明一件事:调用参数的传递链路与模型本身的配置同样重要, 配置正确但未送达服务端,效果等同于未配置。
初始实现使用 Vercel AI SDK 的 generateText() 调用视觉模型,并在调用选项中设置禁用思维链:
该参数在经 SDK 转发到 Qwen3-VL 的运行平台(SiliconFlow)时未被正确传递。Qwen3-VL 因此以默认的全思维链模式运行—— 先生成数万 token 的推理内容,再输出最终结果。单次调用延迟由此达到 130 秒以上。 这一延迟在开发环境中一度被误读为模型本身的速度上限。
修复的做法是放弃 generateText(),改回 OpenAI 原生客户端:
关键改动有四项:
openai.chat.completions.create,避开参数转发链路中的丢失点。extra_body 传递,直达模型服务端。temperature: 0 保证同一图片的输出稳定,便于核对与回归比对。max_tokens: 1024 限制输出长度。铭牌字段通常在 500 token 以内,该上限足以容纳完整结果,同时防止异常输出拖长响应。修复后实测延迟降至 3–8 秒,满足设备建档场景对响应时间的要求。
| 对照项 | 修复前 | 修复后 |
|---|---|---|
| 调用方式 | Vercel AI SDK 的 generateText() | OpenAI 原生客户端 chat.completions.create |
| 思维链参数的传递 | 经 providerOptions 转发,未送达服务端 | 经 extra_body 直达服务端,生效 |
| 模型运行模式 | 全思维链,先生成数万 token 推理内容 | 禁用思维链,直接输出结果 |
| 单次调用延迟 | 130 秒以上 | 3–8 秒 |
| 输出的确定性 | 受思维链内容影响 | temperature 设为 0,同一图片输出稳定 |
禁用思维链的参数集中在 FAST_OPTS 一处定义,由客户端统一附加到每次调用,避免在各调用点重复书写、漏写或写法不一:
该选项对 Qwen3 系列模型通过 extra_body 生效;对非思考型模型(如 GPT、豆包)该参数会被忽略,无副作用。
因此同一个出口可以服务全部模型,不需要在调用处逐个判断模型类型。该开关默认开启,可通过环境变量
AI_DISABLE_THINKING=false 关闭。
130 秒与 3–8 秒为同一管线的实测值,差异来自识别路径的调用方式重构,而非更换模型。 工具清单与动作定义可由演示环境核对,无需申请。 查看核验方式 →
提取函数的第三个参数是来源名称,这一设计预留了多文档提取的能力。同一台设备的信息可能分散在铭牌、采购合同与发票三份材料中, 三者共用同一个提取函数与同一套分类逻辑:
三份材料的提取结果合并后交给同一个分类函数。分类函数按置信度取舍,铭牌字段(置信度 0.95)自动优先于合同字段(置信度 0.78); 前端表单的来源标签显示每个字段实际来自哪一份文档,便于在核对时回到原始材料。
| 来源 | 调用方式 | 说明 |
|---|---|---|
| 铭牌 | extractFromNameplate(base64, "image/jpeg", "铭牌") | 现场拍摄的铭牌照片,管线的默认来源 |
| 合同 | extractFromNameplate(contractBase64, "image/png", "合同") | 采购合同 PDF 先转为页面截图,随后按图片处理 |
| 发票 | extractFromNameplate(invoiceBase64, "image/jpeg", "发票") | 与铭牌共用同一提取函数与分类逻辑 |
口径:多源合并的取舍规则为取置信度更高的取值;来源标签随字段一并返回前端,不单独存储。
上述各节的实现细节可以收敛为八项设计决策:
| 设计决策 | 作用 |
|---|---|
| 两阶段分离(提取 / 映射) | 提取提示词不涉业务逻辑;映射为纯代码,无附加延迟 |
| Qwen3-VL 32B 视觉模型 | 中文 OCR 准确率较高,对工业铭牌的中英混排适应良好 |
| 静态 FIELD_MAP(约 60 条) | 新增字段只需追加入口,提示词无需改动 |
| OpenAI 原生客户端 + enable_thinking: false | 单次调用延迟由 130 秒降至 3–8 秒 |
| JSON 容错解析(extractJsonObject) | 不依赖模型输出格式的一致性 |
| DynamicForm 三类分组 + 置信度着色 | 操作者在数秒内判断哪些字段可直接通过 |
| 有效性检查 + 模型降级 | 图片无效时不展示半空表单 |
| 多源支持(铭牌 / 合同 / 发票) | 同一设备可从多份文档提取,自动取置信度更高的取值 |
该管线已在 EAMX 中稳定运行,核心文件与职责如下表。
| 文件 | 职责 |
|---|---|
artifacts/api-server/src/agents/nameplate-extractor.ts | Stage 1 视觉提取(171 行) |
artifacts/api-server/src/agents/field-classifier.ts | Stage 2 字段分类(232 行) |
artifacts/api-server/src/agents/equipment.agent.ts | Stage 3 动态表单构造(prepare_equipment_form 约 230 行) |
artifacts/api-server/src/agents/coordinator.agent.ts | 两阶段管道集成与降级 |
artifacts/api-server/src/lib/llm-json-utils.ts | JSON 容错解析 |
lib/integrations-openai-ai-server/src/client.ts | OpenAI 客户端与 FAST_OPTS |
识别管线的产出是一张预填表单,写入台账的动作由使用者确认后触发。卡片字段定义与其后的折旧、维修、处置各阶段的衔接, 见 资产购置之后,一次把账建对 →
本文各项数字均带测试口径。就这条管线而言,以下三项不在承诺范围内。
| 事项 | 说明 |
|---|---|
| 识别准确率不承诺 100% | 置信度着色与人工核对环节的存在即为此。绿色字段表示模型的置信度较高,不表示该字段已由系统校验 |
| 不承诺完全无人值守 | B 类推断字段与 C 类技术参数均需操作者确认;管线产出的是预填表单,写入台账由使用者确认后触发 |
| 图片无效时不做猜测 | 铭牌模糊、反光或画面中不含铭牌时,管线说明原因并给出建议,不生成半空表单 |
口径:延迟数字为开发环境实测记录;识别准确率未在本文中列示。
130 秒到 3–8 秒的延迟优化、约 60 条字段映射、三类字段的置信度区间、六个核心文件与职责—— 上述内容均可由演示环境核对工具清单与动作定义,无需申请。