dify案例分享-基于多模态模型的发票比对

上期文章我们提到使用dify基于多模态模型实现多种发票识别工作流的智能体。我们在实际工作中发现OCR识别的发票准确性要求比较高。基于多模态大模型OCR识别这块可能会遇到对票面信息识别不准的问题,这样会财务报销就会产生很大的问题。基于以上的问题我们来实现一个发票比对工作流。

下面我们首先介绍一下整体功能。

图片

这里面主要功能:用户上传一张发票图片,发票会经过文档提取器。文档提取器提取用户上传的发票传递给2个llm多模态模型,两个多模态模型是实现发票票面信息的提取功能。然后将提取的发票票面信息发送给第三个基于llm文本的大模型,它充当模型裁判功能。主要的功能是将2个模型输出的JSON格式的数据比对,比对的结果输出给客户。从而实现发票识别比对判断功能。

实现的效果如下:

   数据有差异的效果:

图片

 数据无差异效果:

图片

下面我们重点介绍一下这个工作流是如何实现的。

创建工作流或者chatflow

接着来到Dify中按下图顺序依次点击并点击创建(注:chatflow和工作流配置基本差不多,下面我们就以chatflow讲解)

 

图片

开始

      开始节点点开后我们需要添加一个文件上传输入参数。点击开始节点输入字段,点击右边的“+”

     

图片

我们选择单个文件,输入变量名称、支持的文件类型我们这里就选择图片。其他都可以默认,输入完成后,点击保存按钮

图片

以上步骤完成开始节点设置。

图片

文档提取器

 接下来我们在工作流画布中,选择文档提取器和开始节点连接,去掉llm和开始节点连接

 

图片

 

图片

我们在文档提取器,输入变量中选中 sys.files 变量

图片

llm(多模态发票识别)

接下来我们将文档提取器的连接线和llm大语言模型连接。然后按照以下几个步骤设置

 1.模型选择,模型我们在模型下拉列表中选择自定义OpenAI-API-compatible  Qwen/Qwen2-VL-72B-Instruct模型;模型最大标记4096

 2.上下文,这里设置开始节点file 属性值

 3.SYSTEM 提示词 我们输入如下内容

请提取这张照片的内容,其中内容格式‘发票号码’、'开票日期’、'‘出发时间’、‘始发站’、‘终点站’、‘车次’、‘票价’、‘身份证号’、‘姓名’、‘电子客票号’、‘购买方名称’、‘统一社会信用代码’字段返回信息,返回的结果信息以json格式返回

4.视觉 点击右边按钮开启多模态

5  视觉输入变量  选择节点filefiles 变量

以上完成llm模型的设置

图片

以上我们需要再设置第二个多模态发票模型来对上传的图片进行发票识别,操作和上面一样,这里就不重复讲解,区别在于我们需要选择另外一个多模态模型。这里我们选择了智普的glm-4v-plus 来实现

图片

            主要需要注意的地方是 智普模型对用户输入的提示词需要有值作为输入参数,这里我们为了让工作流运行起来,我们填写“1”

             

图片

        配置好的2个多模态模型需要和上面文档提取器连接

   

图片

基于文本发票比对模型

    接下来我们需要将2个多模态模型的输出结果和一个llm文本大语言模型进行连接。这个模型的作用主要是接收2个模型输出json值,对2个json值进行判断逐行比对判断是否一致。

 1.模型选择,模型我们在模型下拉列表中选择 deepseek-ai/DeepSeek-V2.5模型;

 2.上下文,这里可以不填写

 3.SYSTEM 提示词 我们输入如下内容

{
  "Role": "JSON 数据比对专家",

  "Profile": {
    "专长": "精确比较和分析 JSON 数据",
    "经验": "多年处理各种结构化数据的丰富经验",
    "技能": ["准确识别差异", "使用颜色高亮标注", "详细的比对报告生成"]
  },

  "Goals": [
    "逐行比较两个 JSON 数据的内容",
    "识别并标记所有存在的差异",
    "使用颜色(红色)高亮显示不同之处",
    "生成清晰、易读的比对结果报告"
  ],

  "Rules": [
    "必须逐个键值对进行比较,不遗漏任何字段",
    "只标注存在差异的部分,相同部分保持原样",
    "使用红色作为差异标注的唯一颜色",
    "对于数值型差异,需要考虑精度问题",
    "对于字符串差异,需要考虑大小写和空白字符",
    "保持 JSON 的结构完整性,不改变原有的格式和顺序"
  ],

  "Workflows": [
    "接收并解析两个待比对的 JSON 数据",
    "确保两个 JSON 数据结构一致,如果不一致,报告结构差异",
    "逐一比对每个键值对:",
    "  - 如果键不同,标记为新增或缺失",
    "  - 如果值不同,使用红色高亮标注",
    "生成详细的比对报告,包括:",
    "  - 总体差异统计",
    "  - 每个差异项的具体描述",
    "  - 高亮显示的 JSON 数据"
  ],

  "OutputFormat": {
    "type": "json",
    "structure": {
      "summary": "总体比对结果摘要",
      "differences": [
        {
          "key": "差异字段名",
          "value1": "第一个 JSON 中的值",
          "value2": "第二个 JSON 中的值",
          "highlightColor": "red"
        }
      ],
      "highlightedJSON": "包含红色高亮的完整 JSON 数据"
    }
  },

  "Examples": [
    {
      "input": {
        "json1": {
          "价税合计(小写)": "263.00",
          "收款人": "段欣冉"
        },
        "json2": {
          "价税合计(小写)": "213.00",
          "收款人": "段牛冉"
        }
      },
      "output": {
        "summary": "发现 2 处差异",
        "differences": [
          {
            "key": "价税合计(小写)",
            "value1": "263.00",
            "value2": "213.00",
            "highlightColor": "red"
          },
          {
            "key": "收款人",
            "value1": "段欣冉",
            "value2": "段牛冉",
            "highlightColor": "red"
          }
        ],
        "highlightedJSON": {
          "价税合计(小写)": "<red>263.00</red>",
          "收款人": "<red>段欣冉</red>"
        }
      }
    }
  ]
}
  1. user  提示词 我们需要输入上面2个模型的输出结果。

    完整的模型配置如下图

    图片

图片

直接回复

              这个地方设置比较简单,在回复设置一下llm text文本输出,把比对的结果输出给用户即可。

             

图片

完整的流程图如下:

图片

dsl 文件

app:
  description: ''
  icon: 🤖
  icon_background: '#FFEAD5'
  mode: advanced-chat
  name: 发票比对专家-火车票
  use_icon_as_answer_icon: false
kind: app
version: 0.1.2
workflow:
  conversation_variables: []
  environment_variables: []
  features:
    file_upload:
      allowed_file_extensions:
      - .JPG
      - .JPEG
      - .PNG
      - .GIF
      - .WEBP
      - .SVG
      allowed_file_types:
      - image
      allowed_file_upload_methods:
      - local_file
      - remote_url
      enabled: false
      image:
        enabled: false
        number_limits: 3
        transfer_methods:
        - local_file
        - remote_url
      number_limits: 3
    opening_statement: ''
    retriever_resource:
      enabled: true
    sensitive_word_avoidance:
      enabled: false
    speech_to_text:
      enabled: false
    suggested_questions: []
    suggested_questions_after_answer:
      enabled: false
    text_to_speech:
      enabled: false
      language: ''
      voice: ''
  graph:
    edges:
    - data:
        isInIteration: false
        sourceType: start
        targetType: document-extractor
      id: 1730994694827-source-1730994818842-target
      source: '1730994694827'
      sourceHandle: source
      target: '1730994818842'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: document-extractor
        targetType: llm
      id: 1730994818842-source-1730994952059-target
      source: '1730994818842'
      sourceHandle: source
      target: '1730994952059'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: llm
        targetType: answer
      id: 1730995241679-source-answer-target
      source: '1730995241679'
      sourceHandle: source
      target: answer
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: document-extractor
        targetType: llm
      id: 1730994818842-source-1730994854289-target
      source: '1730994818842'
      sourceHandle: source
      target: '1730994854289'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: llm
        targetType: llm
      id: 1730994854289-source-1730995241679-target
      source: '1730994854289'
      sourceHandle: source
      target: '1730995241679'
      targetHandle: target
      type: custom
      zIndex: 0
    - data:
        isInIteration: false
        sourceType: llm
        targetType: llm
      id: 1730994952059-source-1730995241679-target
      source: '1730994952059'
      sourceHandle: source
      target: '1730995241679'
      targetHandle: target
      type: custom
      zIndex: 0
    nodes:
    - data:
        desc: ''
        selected: false
        title: 开始
        type: start
        variables:
        - allowed_file_extensions: []
          allowed_file_types:
          - image
          allowed_file_upload_methods:
          - local_file
          - remote_url
          label: file
          max_length: 48
          options: []
          required: true
          type: file
          variable: file
      height: 90
      id: '1730994694827'
      position:
        x: -122.33815460561607
        y: 239.74853493583367
      positionAbsolute:
        x: -122.33815460561607
        y: 239.74853493583367
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        answer: '{{#1730995241679.text#}}'
        desc: ''
        selected: false
        title: 直接回复
        type: answer
        variables: []
      height: 103
      id: answer
      position:
        x: 1507.4714864776458
        y: 230.51667545618076
      positionAbsolute:
        x: 1507.4714864776458
        y: 230.51667545618076
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        desc: ''
        is_array_file: true
        selected: false
        title: 文档提取器
        type: document-extractor
        variable_selector:
        - sys
        - files
      height: 94
      id: '1730994818842'
      position:
        x: 198.13851897026086
        y: 239.74853493583367
      positionAbsolute:
        x: 198.13851897026086
        y: 239.74853493583367
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        context:
          enabled: true
          variable_selector:
          - '1730994694827'
          - file
        desc: ''
        model:
          completion_params:
            temperature: 0.1
          mode: chat
          name: Pro/Qwen/Qwen2-VL-7B-Instruct
          provider: openai_api_compatible
        prompt_template:
        - id: cb8bcd12-345d-4b95-8f48-e3360269ec60
          role: system
          text: 请提取这张照片的内容,其中内容格式‘发票号码’、'开票日期’、'‘出发时间’、‘始发站’、‘终点站’、‘车次’、‘票价’、‘身份证号’、‘姓名’、‘电子客票号’、‘购买方名称’、‘统一社会信用代码’字段返回信息,返回的结果信息以json格式返回
        selected: false
        title: 发票提取模型1
        type: llm
        variables: []
        vision:
          configs:
            detail: high
            variable_selector:
            - '1730994694827'
            - file
          enabled: true
      height: 98
      id: '1730994854289'
      position:
        x: 530.598669101873
        y: 132.04920892271053
      positionAbsolute:
        x: 530.598669101873
        y: 132.04920892271053
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        context:
          enabled: true
          variable_selector:
          - '1730994694827'
          - file
        desc: ''
        model:
          completion_params:
            temperature: 0.1
          mode: chat
          name: glm-4v-plus
          provider: zhipuai
        prompt_template:
        - id: ee1b5d7d-3303-44cf-9b5c-d29b22d3f798
          role: system
          text: 请提取这张照片的内容,其中内容格式‘发票号码’、'开票日期’、'‘出发时间’、‘始发站’、‘终点站’、‘车次’、‘票价’、‘身份证号’、‘姓名’、‘电子客票号’、‘购买方名称’、‘统一社会信用代码’字段返回信息,返回的结果信息以json格式返回
        - id: 827c3f3c-0c19-48c3-b40b-fc15bdfb0407
          role: user
          text: '1'
        selected: false
        title: 发票提取模型2
        type: llm
        variables: []
        vision:
          configs:
            detail: high
            variable_selector:
            - '1730994694827'
            - file
          enabled: true
      height: 98
      id: '1730994952059'
      position:
        x: 517.2073813384065
        y: 407.5975395538644
      positionAbsolute:
        x: 517.2073813384065
        y: 407.5975395538644
      selected: false
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    - data:
        context:
          enabled: false
          variable_selector: []
        desc: ''
        model:
          completion_params:
            temperature: 0.1
          mode: chat
          name: Qwen/Qwen2.5-72B-Instruct
          provider: siliconflow
        prompt_template:
        - id: 0a4409ea-6bb9-4c3f-9f9e-91e6eaa478aa
          role: system
          text: "{\n  \"Role\": \"JSON 数据比对专家\",\n\n  \"Profile\": {\n    \"专长\":\
            \ \"精确比较和分析 JSON 数据\",\n    \"经验\": \"多年处理各种结构化数据的丰富经验\",\n    \"技能\"\
            : [\"准确识别差异\", \"使用颜色高亮标注\", \"详细的比对报告生成\"]\n  },\n\n  \"Goals\": [\n\
            \    \"逐行比较两个 JSON 数据的内容\",\n    \"识别并标记所有存在的差异\",\n    \"使用颜色(红色)高亮显示不同之处\"\
            ,\n    \"生成清晰、易读的比对结果报告\",\n    \"准确报告完全相同的数据\"\n  ],\n\n  \"Rules\":\
            \ [\n    \"必须逐个键值对进行比较,不遗漏任何字段\",\n    \"只标注存在差异的部分,相同部分保持原样\",\n    \"\
            使用红色作为差异标注的唯一颜色\",\n    \"对于数值型差异,需要考虑精度问题\",\n    \"对于字符串差异,需要考虑大小写和空白字符\"\
            ,\n    \"保持 JSON 的结构完整性,不改变原有的格式和顺序\",\n    \"如果两个 JSON 完全相同,明确报告无差异\"\
            \n  ],\n\n  \"Workflows\": [\n    \"接收并解析两个待比对的 JSON 数据\",\n    \"确保两个\
            \ JSON 数据结构一致,如果不一致,报告结构差异\",\n    \"逐一比对每个键值对:\",\n    \"  - 如果键不同,标记为新增或缺失\"\
            ,\n    \"  - 如果值不同,使用红色高亮标注\",\n    \"  - 如果完全相同,不进行标注\",\n    \"生成详细的比对报告,包括:\"\
            ,\n    \"  - 总体差异统计(如果有)或无差异声明\",\n    \"  - 每个差异项的具体描述(如果有)\",\n    \"\
            \  - 高亮显示的 JSON 数据(如果有差异)\"\n  ],\n\n  \"OutputFormat\": {\n    \"type\"\
            : \"json\",\n    \"structure\": {\n      \"summary\": \"总体比对结果摘要\",\n\
            \      \"differences\": [\n        {\n          \"key\": \"差异字段名\",\n\
            \          \"value1\": \"第一个 JSON 中的值\",\n          \"value2\": \"第二个\
            \ JSON 中的值\",\n          \"highlightColor\": \"red\"\n        }\n    \
            \  ],\n      \"highlightedJSON\": \"包含红色高亮的完整 JSON 数据(如果有差异)\"\n    }\n\
            \  },\n\n  \"Examples\": [\n    {\n      \"input\": {\n        \"json1\"\
            : {\n          \"价税合计(小写)\": \"263.00\",\n          \"收款人\": \"段欣冉\"\n\
            \        },\n        \"json2\": {\n          \"价税合计(小写)\": \"213.00\"\
            ,\n          \"收款人\": \"段牛冉\"\n        }\n      },\n      \"output\":\
            \ {\n        \"summary\": \"发现 2 处差异\",\n        \"differences\": [\n\
            \          {\n            \"key\": \"价税合计(小写)\",\n            \"value1\"\
            : \"263.00\",\n            \"value2\": \"213.00\",\n            \"highlightColor\"\
            : \"red\"\n          },\n          {\n            \"key\": \"收款人\",\n\
            \            \"value1\": \"段欣冉\",\n            \"value2\": \"段牛冉\",\n\
            \            \"highlightColor\": \"red\"\n          }\n        ],\n  \
            \      \"highlightedJSON\": {\n          \"价税合计(小写)\": \"<red>263.00</red>\"\
            ,\n          \"收款人\": \"<red>段欣冉</red>\"\n        }\n      }\n    },\n\
            \    {\n      \"input\": {\n        \"json1\": {\n          \"发票号码\":\
            \ \"243491194230000002\",\n          \"开票日期\": \"2024-09-29\",\n     \
            \     \"购买方名称\": \"xx股份有限公司\",\n          \"统一社会信用代码\": \"913401001492097421\"\
            \n        },\n        \"json2\": {\n          \"发票号码\": \"243491194230000002\"\
            ,\n          \"开票日期\": \"2024-09-29\",\n          \"购买方名称\": \"xx股份有限公司\"\
            ,\n          \"统一社会信用代码\": \"913401001492097421\"\n        }\n      },\n\
            \      \"output\": {\n        \"summary\": \"两个 JSON 数据完全相同,没有发现任何差异。\"\
            ,\n        \"differences\": [],\n        \"highlightedJSON\": null\n \
            \     }\n    }\n  ]\n}"
        - id: 5725207a-3967-4cb0-9220-fc269ec64a58
          role: user
          text: '{{#1730994854289.text#}}

            {{#1730994952059.text#}}'
        selected: false
        title: 基于文本发票比对模型
        type: llm
        variables: []
        vision:
          enabled: false
      height: 98
      id: '1730995241679'
      position:
        x: 1078.5929830262382
        y: 262.4842834334896
      positionAbsolute:
        x: 1078.5929830262382
        y: 262.4842834334896
      selected: true
      sourcePosition: right
      targetPosition: left
      type: custom
      width: 244
    viewport:
      x: 122.11439198427718
      y: 168.52395441087617
      zoom: 0.5743491774985177

chatflow调试及发布

完成以上配置后就可以点击调试及发布了,当然如果你比较偷懒,也可以直接导入我的DSL 直接

就可以搞定了。

图片

图片

导入DSL后,是需要修改工作流中的模型就可以了。如果大家没有硅基流动的账号,可以点击https://cloud.siliconflow.cn/i/e0f6GCrN 地址来注册,目前硅基的政策是新户注册送14块钱,14块钱够玩一阵子了。

下面我们就感受一下测试效果

图片

发布

点击工作流左上角发布按钮对外提供发布

图片

我们将分享的地址发送给其他小伙伴

图片

我们点击 start chat 就可以使用了。

图片

总结

今天我们就带大家在上个文章中多个发票识别功能基础上再扩展一个新的发票比对功能的业务场景,来感受一下dify的强大。感兴趣的小伙伴可以持续关注我的文章,今天的分享就到这里,我们下个文章见。

项目体验地址

http://101.126.84.227:88/chat/4J2Nau4TuNDJb5ep

往期文章

dify案例分享-基于多模态模型的发票识别2-多种发票识别

dify案例分享-基于多模态模型的发票识别

阅读 2577

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值