dify案例分享-探秘:AI 怎样颠覆财报分析,打造酷炫 HTML 可视化

技术
1.前言

上市公司财报,即上市公司财务报告,是上市公司按照相关法规要求定期向公众披露的反映其财务状况、经营成果和现金流量等信息的正式记录。它是资本市场中重要的信息披露文件,为投资者、分析师及其他利益相关者提供了关键的财务数据和经营绩效信息,是评估公司财务健康状况和投资价值的重要依据。

下面是一些财务报告主要指标。

picture.image

目前各大上市公司2024年度财报和2025年第一季的财报都已经披露,那么我们可以不可以利用AI 来帮我们快速分析这些财报生成漂亮的财务报表呢?今天就带大家来实现这个工作流。

工作流效果:

picture.image

生成的报表给大家展示一下:

picture.image

picture.image

picture.image

看起来效果不错,我们接下来告诉大家3分钟生成这样的基于上市公司财务报表的可视化HTML页面。

我们首选看一下这个工作流由哪些组件构成的。

1开始。2、mineru 3 、llm大语言模型、4 参数提取器、5 代码处理生成html调用、6、直接回复。

picture.image

开始

这个开始节点主要是用户通过上传一些上市公司财务报告,所以目前它只有一个参数就是file。

关于财务文件从哪里获取,大家可以通过https://gu.qq.com/sz002594/gp/jbnb 来获取(我这里以比亚迪公司为案例)

picture.image

下载的报告是PDF格式的,下载后保存本地电脑即可。

开始节点我们设置单个文件。

picture.image

picture.image

以上我们就完成了开始节点的设置。

mineru 插件

这里我们需要用到一个叫做mineru插件。MinerU 是一款开源的高质量数据提取工具,专注于将 PDF、网页和电子书中的内容高效提取并转换为机器可读格式(如 Markdown 和 JSON)。它由上海人工智能实验室 OpenDataLab 团队开发,旨在解决复杂文档的解析问题,支持多模态内容(包括文本、图片、表格和公式)的提取。

我们需要上面这个插件工具把开始节点中上传的PDF文档信息提取出来。 有的小伙伴说dify工作流里面不是有文档提取器吗?这个不也可以提取数据吗?是的你说的没错,文档提取器是可以提取文档内容信息,因为考虑上市公司财务报表数据准确性和复杂性我们这里建议使用mineru工具来提取文档里面的数据。

mineru 插件安装

我们在插件市场查找mineru。点击安装完成插件的安装。

picture.image

picture.image

mineru 注册

在使用这个插件之前我们需要在https://mineru.net 网站上注册获取授权。这个注册需要审批我们登录https://mineru.net 网站获得授权审批通过后获取API

picture.image

mineru授权

我们获取api token

picture.image

picture.image

以上步骤我们完成授权。

picture.image

当然如果你自己有显卡资源 也可以自己部署mineru,具体步骤这里就不在这里阐述。

mineru工作流配置

我们回到dify工作流,点击添加节点-工具-mineru

picture.image

接下来设置一下mineru,输入参考就是开始节点传入的file. 解析方法: orc;开启公式识别:true 开启公式识别:true 布局检测模型:doclayout-yolo 文档语言:auto 开启ocr识别:true. 具体配置如下:

picture.image

llm大语言模型

这个llm大语言模型我们选择2025年4月29日阿里通义千问开源的Qwen3-235B-A22B模型,模型这里我们使用魔搭社区提供的免费的Qwen3-235B-A22B模型

picture.image

系统提示词:


 
 
 
 
   
# 角色:上市公司财报数据HTML页面生成专家  
## 简介:  
-作者:周辉  
-版本:3.0  
-语言:中文  
-描述:专业的财报数据分析师和HTML动态网页设计专家,擅长创建符合现代设计趋势和技术要求的财报展示页面。  
## 背景:  
你是一位资深的财务分析师和网页设计专家,专门将上市公司财报数据转化为视觉吸引力强的HTML动态网页。你熟悉各种现代web技术和设计趋势,尤其擅长BemtoGrid布局和GSAP动效。  
  
## 目标:  
生成一个完整的、可直接使用的HTML页面,用于展示上市公司财报数据,该页面应符合所有技术和设计要求。  
  
## 技术要求:  
1.使用BemtoGrid布局系统  
2.集成GSAP动效和FramerMotion  
3.基于HTML5和TailwindCSS开发  
4.响应式设计和大小字体对比应用  
## 设计规范:  
1.根据公司特性选择适当的背景颜色和主题色调  
2.应用超大字体和视觉元素突出重点,创造视觉对比  
3.中英文混排,大字体为主,英文小字点题  
4.使用简洁的矩形元素进行数据可视化  
5.高亮色透明效果用于边框,避免不同高亮色互相覆盖  
6.所有数据图表采用脚注样式,保持主题一致性  
7.避免使用emoji作为主要图标  
## 输出格式:  
请直接提供完整的HTML代码,包含所有必要的CSS和JavaScript,确保代码可以直接复制使用并正常运行。代码应包含:  
  
1.完整的HTML结构  
2.内联或外部引用的CSS(包括TailwindCSS)  
3.必要的JavaScript(包括GSAP和FramerMotion)  
4.CDN引用和其他必要的资源链接  
## 初始化:  
作为上市公司财报数据HTML页面生成专家,我已准备好为您创建一个完整的HTML页面。请提供您想要分析的上市公司及其最新财报的关键信息,我将直接为您生成可用的HTML代码。

用户提示词


 
 
 
 
   
根据{{#1747105471978.text#}}最新财报内容和补充内容以及财报数据分析内容,生成一个 HTML 动态网页

picture.image

参数提取器

接下来我们用到了一个叫做参数提取器的组件。这个组件的主要目的是把上个LLM大语言模型输出的html 提取出来。这里有小伙伴可能有疑问了,上面个流程我们不是让它直接返回html 内容吗?为什么还要用到参数提取器呢? 这是因为大模型有幻觉 每个模型对提示词理解是有偏差的,虽然我们定义让它只返回html页面,但是对于某些模型它不光返回HTML 页面还返回一堆非HTML的文本内容,这样如果不用参数提取器出来后面生成HTML 页面会报错的。

picture.image

模型这里我们选择google gemini2.5-flash模型。输入变量上个流程节点llm输出。 提取参数这里我们定义一个html参数

picture.image

指令这里我们输入


 
 
 
 
   
请提取大模输出的html部分代码,其他的不需要

完整的参数提取器相关参数设置如下:

picture.image

代码处理生成html调用

接下来这里我们使用代码处理生成html, 这块功能我们之前的文章提到过,大家可以看我之前文章。dify案例分享-deepseek赋能从 Excel 表格到统计图,一键生成代码不是梦

上面的流程中参数提取器提取的代码它需要把它转化成文件输出,这里我们利用了后端服务代码的能力。这里我们有2个地方需要讲解。第一个地方是我们编写了服务端代码,这个是为了在服务端生成HTML代码,并上传到一个第三方公网访问的地址信息(我这里用了腾讯云COS存储)。第二个地方是获取这个生成的html代码链接地址返回给dify以方便后续流程使用。

1.服务端代码

这个服务端代码主要作用就是使用fastapi提供一个http请求接口,后端通过python代码生成html并上传腾讯COS

picture.image

makehtmlapi.py


 
 
 
 
   
from fastapi import FastAPI, HTTPException,Depends, Header  
from pydantic import BaseModel  
import logging  
import time  
import uvicorn  
import configparser  
import os  
import json  
import datetime  
import random  
from qcloud\_cos import CosConfig  
from qcloud\_cos import CosS3Client  
  
app = FastAPI()  
  
# 读取配置文件中的API密钥  
config = configparser.ConfigParser()  
config.read('config.ini', encoding='utf-8')  
  
# Tencent Cloud COS configuration  
region = config.get('common', 'region')  
secret\_id = config.get('common', 'secret\_id')  
secret\_key = config.get('common', 'secret\_key')  
bucket = config.get('common', 'bucket')  
  
# 设置输出路径  
output\_path = config.get('html', 'output\_path', fallback='html\_output')  
  
# 确保输出目录存在  
os.makedirs(output\_path, exist\_ok=True)  
  
# 设置日志  
logging.basicConfig(level=logging.INFO)  
logger = logging.getLogger(\_\_name\_\_)  
  
classHTMLRequest(BaseModel):  
    html\_content: str  
    filename: str = None# 可选参数,如果不提供则自动生成  
  
defverify\_auth\_token(authorization: str = Header(None)):  
    """验证 Authorization Header 中的 Bearer Token"""  
    ifnot authorization:  
        raise HTTPException(status\_code=401, detail="Missing Authorization Header")  
      
    scheme, \_, token = authorization.partition(" ")  
    if scheme.lower() != "bearer":  
        raise HTTPException(status\_code=401, detail="Invalid Authorization Scheme")  
      
    # 从配置文件读取有效token列表  
    valid\_tokens = json.loads(config.get('auth', 'valid\_tokens'))  
    if token notin valid\_tokens:  
        raise HTTPException(status\_code=403, detail="Invalid or Expired Token")  
      
    return token  
defgenerate\_timestamp\_filename(extension='html'):  
    timestamp = datetime.datetime.now().strftime("%Y%m%d%H%M%S")  
    random\_number = random.randint(1000, 9999)  
    filename = f"{timestamp}\_{random\_number}.{extension}"  
    return filename  
  
defsave\_html\_file(html\_content, filename=None, output\_dir=None):  
    # 如果没有提供文件名,则生成一个  
    ifnot filename:  
        filename = generate\_timestamp\_filename()  
      
    # 如果没有提供输出目录,则使用默认目录  
    ifnot output\_dir:  
        output\_dir = output\_path  
      
    # 确保输出目录存在  
    os.makedirs(output\_dir, exist\_ok=True)  
      
    # 组合完整的输出路径  
    file\_path = os.path.join(output\_dir, filename)  
      
    # 写入HTML内容  
    withopen(file\_path, 'w', encoding='utf-8') as file:  
        file.write(html\_content)  
      
    # 返回文件名和输出路径  
    return filename, file\_path  
  
defupload\_cos(region, secret\_id, secret\_key, bucket, file\_name, base\_path):  
    config = CosConfig(  
        Region=region,  
        SecretId=secret\_id,  
        SecretKey=secret\_key  
    )  
    client = CosS3Client(config)  
    file\_path = os.path.join(base\_path, file\_name)  
    response = client.upload\_file(  
        Bucket=bucket,  
        LocalFilePath=file\_path,  
        Key=file\_name,  
        PartSize=10,  
        MAXThread=10,  
        EnableMD5=False  
    )  
    if response['ETag']:  
        url = f"https://{bucket}.cos.{region}.myqcloud.com/{file\_name}"  
        return url  
    else:  
        returnNone  
  
@app.post("/generate-html/")  
asyncdefgenerate\_html(request: HTMLRequest,auth\_token: str = Depends(verify\_auth\_token)):  
    try:  
        logger.info("开始处理HTML生成请求")  
        start\_time = time.time()  
          
        # 保存HTML文件  
        filename, file\_path = save\_html\_file(request.html\_content, request.filename)  
          
        # 上传到腾讯云COS  
        html\_url = upload\_cos(region, secret\_id, secret\_key, bucket, filename, output\_path)  
          
        elapsed\_time = time.time() - start\_time  
        logger.info(f"HTML生成和上传完成,耗时 {elapsed\_time:.2f} 秒,返回 URL: {html\_url}")  
          
        if html\_url:  
            return {  
                "success": True,  
                "html\_url": html\_url,  
                "filename": filename  
            }  
        else:  
            raise HTTPException(status\_code=500, detail="上传HTML文件到COS失败")  
    except Exception as e:  
        logger.error(f"处理HTML生成请求时发生错误: {str(e)}")  
        raise HTTPException(status\_code=500, detail=str(e))  
  
if \_\_name\_\_ == "\_\_main\_\_":  
    uvicorn.run(app, host="0.0.0.0", port=8088)

代码里面用到config.ini配置文件


 
 
 
 
   
[html]  
output\_path = E:\\work\\code\\2024pythontest\\makehtml\\html\_output  
  
[common]  
region = xxx         腾讯云OSS存储Region  
secret\_id = xxx      腾讯云OSS存储SecretId  
secret\_key = xxx     腾讯云OSS存储SecretKey  
bucket = xxx         腾讯云OSS存储bucket  
  
[auth]  
valid\_tokens = ["sk-zhouhui1xxx", "zhouhui112xxx"]

上面代码需要再服务器或者本地运行起来。对外提供 8088端口(端口你也可以自己改)

picture.image

上面步骤服务端就启动好了。

1.客户端代码

接下来我们在dify 代码执行中添加。

picture.image

这里我们有4个参考。分别是1.json_html 参数提取提取的html代码。2 apiurl 就是上面服务端代码的请求地址。3 apikey 服务端代码请求APIkey. 4 strtype 这里我们可以写死(上市公司)

picture.image

其中 apiurl 和apikey 我们这里用环境变量方式来实现。

picture.image

如果你本地电脑 URL 可以是 192.168.XX.XX 或者127.0.0.1 如果是服务器 可以是局域网IP 也是可以公网IP

picture.image

客户端APIKEY 和服务端APIKEY 保持一致。服务端APIKEY 就是config.ini 对应的

picture.image

我上面服务端数组定义2个值,客户端有一个值在这个数组就可以了。

客户端代码如下:


 
 
 
 
   
import json  
import re  
import time  
import requests  
  
defmain(json\_html: str, apikey: str,apiurl: str,strtype: str) -> dict:  
    try:  
        # 去除输入字符串中的 ```html 和 ``` 标记  
        html\_content = re.sub(r'^```html\s*|\s*```$', '', json\_html, flags=re.DOTALL).strip()  
          
        # 生成时间戳,确保文件名唯一  
        timestamp = int(time.time())  
        filename = f"{strtype}\_{timestamp}.html"  
          
        # API端点(假设本地运行)  
        url = f"{apiurl}"  
          
        # 请求数据  
        payload = {  
            "html\_content": html\_content,  
            "filename": filename  # 使用传入的文件名  
        }  
          
        # 设置请求头(包含认证token)  
        headers = {  
            "Authorization": f"Bearer {apikey}",  # 替换为实际的认证token  
            "Content-Type": "application/json"  
        }  
          
        try:  
            # 发送POST请求  
            response = requests.post(url, json=payload, headers=headers)  
              
            # 检查响应状态  
            if response.status\_code == 200:  
                result = response.json()  
                html\_url = result.get("html\_url", "")  
                generated\_filename = result.get("filename", "")  
                  
                # 返回结果  
                return {  
                    "html\_url": html\_url,  
                    "filename": generated\_filename,  
                    "markdown\_result":  f"[点击查看]({html\_url})"  
                }  
            else:  
                raise Exception(f"HTTP Error: {response.status\_code}, Message: {response.text}")  
          
        except requests.exceptions.RequestException as e:  
            raise Exception(f"Request failed: {str(e)}")  
      
    except Exception as e:  
        return {  
            "error": f"Error: {str(e)}"  
        }

返回3个值html_url、filename、markdown_result

picture.image

以上就完成了服务端代码生成和客户端代码调用的功能了。

直接回复

这个直接回复目前设置2个返回,一个是mineru 插件返回的文本信息,一个是最后生成的html页面链接。

picture.image

picture.image

以上我们就完成了整个工作流的制作。

3.验证及测试

我们打开工作流预览按钮。点击从本地文件上传

picture.image

选择我们之前下载的pdf文件

picture.image

picture.image

picture.image

以上我们就可以实现工作流的验证了。下面是视频演示

生成的图片最后的效果

picture.image

体验地址

体验地址https://difyhs.duckcloud.fun/chat/0yPNRuq8JCozwsTQ 备用地址(http://14.103.204.132/chat/0yPNRuq8JCozwsTQ)

4.总结

今天主要带大家了解并实现了利用 AI 快速分析上市公司财报并生成可视化 HTML 页面的工作流方案。该工作流主要由开始、mineru 插件、llm 大语言模型、参数提取器、代码处理生成 html 调用以及直接回复等组件构成。通过实际验证和测试,我们发现按照此工作流,能够在短时间内生成基于上市公司财务报表的可视化 HTML 页面,效果显著。与传统的财报分析方式相比,该方案不仅提高了分析效率,还能以更直观、美观的方式展示财报数据,为投资者、分析师及其他利益相关者提供了便利。感兴趣的小伙伴可以按照本文步骤去尝试。今天的分享就到这里结束了,我们下一篇文章见。

探秘 DeerFlow:字节跳动开源的科研创作魔法盒!

dify案例分享-开源模型加持,打字就能轻松 P 图的工作流来了

mcp-server案例分享-Excel 表格秒变可视化图表 HTML 报告,就这么简单

dify案例分享-私有化 MCP 广场搭建与网页小游戏智能体工作流实战

小白必看!启智平台轻松搞定 Qwen3 模型推理与训练

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
如何构建企业级云原生计算基础设施
云原生大数据是大数据平台新一代架构和运行形态。通过升级云原生架构,可以为大数据在弹性、多租户、敏捷开发、降本增效、安全合规、容灾和资源调度等方向上带来优势。本议题将依托字节跳动最佳实践,围绕云原生大数据解决方案进行展开。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论