Dify 实战:供应商报价单格式五花八门,AI 怎么知道哪列是单价?
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. 整体架构
链路里两个关键节点:
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. 运行验证
我们用三份「故意刁难」的报价单做了实测——三份的列名风格完全不同:
| 报价单 | 表头 | 识别结果 |
|---|---|---|
| 华为 | 品名,规格,单价(元),数量,交期(天) | 常规中文,单价/数量/交期全部正确 |
| 烽火通信 | 货物名称,规格型号,金额,数目,交付天数 | 「金额」认出是单价、「数目」=数量、「交付天数」=交期,全部正确 |
| Juniper | Vendor, Item, Model, Price, Units, Delivery | 全英文,Price=单价、Units=数量、Delivery=交期,全部正确 |
识别之后,计算也全部准确:
| 供应商 | 品名 | 单价 | 数量 | 金额 |
|---|---|---|---|---|
| 华为 | 交换机 | 1000.00 | 2 | 2000.00 |
| 华为 | 光模块 | 150.00 | 10 | 1500.00 |
| 烽火通信 | 核心交换机 | 8500.00 | 1 | 8500.00 |
| 烽火通信 | 光模块 | 260.00 | 8 | 2080.00 |
| Juniper | Switch | 7800.00 | 2 | 15600.00 |
| Juniper | Optics | 300.00 | 6 | 1800.00 |
合计最低:华为 3500 元——与人工计算一致。三份格式完全不同的报价单,一张表比价,结论可靠。
可靠性靠三重保障兜底:
语义判定标准——LLM 按「单价是单个商品的价格」的定义去认列,不是查字面
null 不编造——识别不了的字段输出 null,代码层标异常,缺数据可见、不会被悄悄算错
计算确定性——归一后的所有计算和规则判定在代码层,LLM 的语义错误不会扩散成算错
诚实边界:如果列名完全没有语义线索(比如就叫「列1」「列2」,表里也没有能推断含义的示例值),LLM 也可能认错——这是任何语义识别方案的天花板,包括人在内(人看「列1」也不知道是啥)。但真实世界的报价单不会这样,供应商都会用正常的业务词。
7. 总结
识别「哪列是单价」不是一个解析问题,是一个理解问题。 解析问题可以用规则,理解问题只能交给语言模型——然后用确定性代码把理解的结果锁死。
这个分工可以泛化到所有「格式各异的文件 → 统一结构」的场景:简历初筛(每家简历格式不同)、发票核对(各平台票据版式不同)、工单归类(各渠道话术不同)——都是「LLM 理解语义,code 执行规则」的同构问题。
本文基于真实项目交付经验撰写(Dify 1.16.x 环境)。文中数据均来自我们自己的实测记录(三种不同格式报价单的识别与计算结果),不构成任何平台的官方结论。
- Dify Agent 应用实战:Beta 版「真 Agent」的能力边界实测
- dify workflow的确定性与Hermes agent skill的"确定性"对比
- Dify 意图分类节点总翻车?从 33% 失败率到兜底不崩——可靠性与韧性的三层加固
- Dify 标注回复实战:让智能客服记住人工答案的纠错闭环
- Dify 知识库元数据过滤实战:检索噪声 75% 降到 0 的确定性闸门
- 数据库里的结构化数据,怎么建立 RAG 知识库?两条路线与选型判断
- Dify 应用上架门户:分享页每次回答都挂着内部流程节点?一个字段关掉
- RAG 知识库建库前,数据到底该怎么清洗?一条可复用的清洗管线实测
- 我们的门户机器人,为什么用 Dify 答、不把 skill 搬上云端 Hermes?
- DeepSeek 思考模式什么情况下可以关?一次空输出事故的排查实录
- Dify 定时触发(trigger-schedule)实测:工作流到点自动跑,和三个必须知道的坑
- Dify 知识库三种分段模式实测:通用、父子、Q&A 到底怎么选?
- Dify 知识库接入 Notion/网页:先搞清三件事,再谈清洗
- 知识库数据清洗后,怎么知道洗得干不干净?一套三层质量门禁实测
- Dify 实战:供应商报价单格式五花八门,AI 怎么知道哪列是单价?