ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Dify实战-供应商报价单格式五花八门-AI怎么知道哪列是单价

Dify实战-供应商报价单格式五花八门-AI怎么知道哪列是单价 Dify 实战:供应商报价单格式五花八门,AI 怎么知道哪列是单价?基于 Dify 1.16.x 实测(2026-08)。1. 业务场景做采购的人都懂这个画面:比价前,桌面上一排供应商发来的报价单,格式几乎没有重样的。华为的报价单是中文表头:「品名,规格,单价(元),数量,交期(天)」。烽火通信的是另一套叫法:「货物名称,规格型号,金额,数目,交付天数」。到了海外供应商,干脆全是英文:「Vendor, Item, Model, Price, Units, Delivery」。列数不一样,列名不一样,单位还可能混着来。采购想做的只有一件事:把这三份捏成一张对比表,看谁便宜、谁交期快、谁报价有猫腻。我们做了个采购比价助手,专门干这件事——上传几家报价单,自动吐出一张对比表、一份异常清单、一个「合计最低」的结论。这里藏着一个看似简单、实际很要命的问题:AI 是怎么知道「金额」这一列代表单价、「数目」代表数量、「Delivery」代表交期的?2. 场景痛点先说最直觉的思路:写规则。既然每家报价单都有列名,那就定义一个映射表——「单价」对应 unit_price、「数量」对应 quantity、「交期」对应 delivery_days,遇到就认出来。这个思路在两个地方崩掉。第一,列名组合是无限的。「单价」「报价」「价格」「金额」「Price」「Unit Price」「单价(元)」……同一语义有几十种写法,更别说还有「含税单价」「出厂价」这种变体。规则表能写 20 条,写不完 200 条,而且每来一家新供应商,就可能冒出一个没见过的叫法——规则永远在补,永远补不完。第二,规则表没有容错。认不出「金额」这一列,数据就丢了;更糟的是,如果「金额」其实是总价(数量×单价的结果),规则会把它当成单价,算出来的比价全错——错得还很隐蔽,采购拿着错误对比表去谈价,灾难。也有人想用列位置硬编码:第 3 列是单价,第 4 列是数量。这更脆——哪家报价单少一列、多一列,后面全部错位,比价结果直接报废。那怎么办?3. 方案:语义识别交给 LLM,计算规则交给 code我们把问题拆成了两层,交给两个不同的执行者:「这一列代表什么」——语义理解,交给 LLM「算出来是多少、有没有异常」——确定性计算,交给代码节点理由很简单:识别列语义的本质是语言理解。「金额」「数目」「Delivery」指向什么业务含义,人靠语感能判断,LLM 靠语言模型也能判断——这是它的强项,而且是规则写不出来的强项。而一旦语义被识别出来、数据被归一到统一结构,后面的计算(单价×数量、合计、排序、异常阈值)就必须是确定性的——这一步交给代码,一秒都不能错。这就是我们一贯的分工原则:语义理解(不确定性强)交给 LLM,计算与规则(确定性)交给 code。LLM 读到的是列名文本,不是列位置。所以列数不同没关系,列名不同也没关系——它按语义找「哪列是单价」,而不是按第几列找。4. 整体架构上传多份报价单(格式各不相同)文档提取CSV/Excel → markdown 表格合并文本加文件分隔符LLM 语义抽取列名 → 统一 JSON 字段代码计算合计/排序/异常判定LLM 结论最低价供应商输出对比表 异常 结论链路里两个关键节点:LLM 语义抽取:输入是「每份报价单的 markdown 表格文本」,输出是统一结构的 JSON——不管输入的表头长什么样,输出永远是{supplier, name, spec, unit_price, quantity, delivery_days}这个契约代码计算:拿到统一 JSON 后,单价×数量算金额、按品名算均价、判定异常(单价超同品名均价 3 倍、交期超 30 天)、生成对比表——全是确定性逻辑列名差异在 LLM 这一层就被抹平了。代码永远只面对统一结构,不需要知道「金额」还是「Price」。5. 模块设计5.1 LLM 抽取:判定标准 输出契约给 LLM 的指令里,除了输出格式,更重要的是判定标准——告诉它「单价」的定义,而不是「单价」的字面:请逐份提取每份报价单中的商品报价条目,输出 JSON 数组。 每份报价单输出一个对象: {supplier: 供应商名称, items: [{name: 品名, spec: 规格, unit: 单位, unit_price: 单价(数字,无则null), quantity: 数量(数字,无则null), delivery_days: 交期天数(数字,无则null)}]} 提取规则: 1. 供应商名称:取报价单中的公司名/供应商名;没有明确名称则用「供应商N」 2. 单价/数量/交期:从表格中识别;单价是单个商品的价格(去掉货币符号和单位); 数量是采购数量;交期是供货天数 3. 商品行就是表格数据行;表头行、说明行、合计行不提取 4. 无法识别的字段用 null,不要编造注意第 2 条:「单价是单个商品的价格,去掉货币符号和单位」——这是语义定义,不是字面匹配。有了这条,「金额」这一列到底是不是单价,LLM 会结合上下文判断:如果表格里没有单独的数量列和总价列,「金额」大概率就是单价;如果「单价」「数量」「总价」三列并存,「金额」就是总价,不能当单价。第 4 条是底线:识别不了就输出 null,禁止编造。后面代码层会对 null 字段标异常,数据缺失会被看见,而不是被悄悄算错。5.2 代码计算:规则硬承载LLM 输出的统一 JSON 进入代码节点,做四件事:计算:金额 单价 × 数量;每个供应商的合计 所有条目金额之和排序:按合计找出最低价供应商异常判定:同品名内单价超均价 3 倍 → 标异常;缺单价/缺数量 → 标异常;交期超 30 天 → 标异常生成对比表:markdown 表格,一行一个条目异常判定是确定性规则,不进 LLM——「超均价 3 倍」这个阈值必须每次都一样地执行,不能今天判 3 倍、明天判 2.5 倍。6. 运行验证我们用三份「故意刁难」的报价单做了实测——三份的列名风格完全不同:报价单表头识别结果华为品名,规格,单价(元),数量,交期(天)常规中文,单价/数量/交期全部正确烽火通信货物名称,规格型号,金额,数目,交付天数「金额」认出是单价、「数目」数量、「交付天数」交期,全部正确JuniperVendor, Item, Model, Price, Units, Delivery全英文,Price单价、Units数量、Delivery交期,全部正确识别之后,计算也全部准确:供应商品名单价数量金额华为交换机1000.0022000.00华为光模块150.00101500.00烽火通信核心交换机8500.0018500.00烽火通信光模块260.0082080.00JuniperSwitch7800.00215600.00JuniperOptics300.0061800.00合计最低:华为 3500 元——与人工计算一致。三份格式完全不同的报价单,一张表比价,结论可靠。可靠性靠三重保障兜底:语义判定标准——LLM 按「单价是单个商品的价格」的定义去认列,不是查字面null 不编造——识别不了的字段输出 null,代码层标异常,缺数据可见、不会被悄悄算错计算确定性——归一后的所有计算和规则判定在代码层,LLM 的语义错误不会扩散成算错诚实边界:如果列名完全没有语义线索(比如就叫「列1」「列2」,表里也没有能推断含义的示例值),LLM 也可能认错——这是任何语义识别方案的天花板,包括人在内(人看「列1」也不知道是啥)。但真实世界的报价单不会这样,供应商都会用正常的业务词。7. 总结识别「哪列是单价」不是一个解析问题,是一个理解问题。解析问题可以用规则,理解问题只能交给语言模型——然后用确定性代码把理解的结果锁死。这个分工可以泛化到所有「格式各异的文件 → 统一结构」的场景:简历初筛(每家简历格式不同)、发票核对(各平台票据版式不同)、工单归类(各渠道话术不同)——都是「LLM 理解语义,code 执行规则」的同构问题。讨论区:你处理过「格式各异的文件」吗?是写规则硬解析,还是用 LLM 语义识别?两者混用时的边界你怎么划?评论区聊聊。如果这篇对你有帮助,点赞 收藏 关注,后续会继续更新 Dify 应用落地中的真实实践。本文基于真实项目交付经验撰写(Dify 1.16.x 环境)。文中数据均来自我们自己的实测记录(三种不同格式报价单的识别与计算结果),不构成任何平台的官方结论。
返回列表