故事得从我给朋友做的一个环境监测平台说起。
这个平台主要服务于高校、农业养殖、畜牧业,实时监测环境的温度、湿度、CO2、PM2.5等关键环境参数。
设备每分钟上报一次数据,客户可以在云端查看。
虽然基础的单设备图表展示功能是有的,但很快就遇到了瓶颈:客户的需求越来越多,越来越细致。
他们不仅想看单个设备的参数曲线,还想对比不同设备的表现,分析特定时间段的数据趋势,甚至希望系统能“智能”地给出一些分析建议。
原有的固定图表和简单数据展示,显然已经无法满足这些灵活多变的需求,这就是我朋友面临的痛点。
这时,我们想到了Dify。对于这种后台业务逻辑相对固定(主要就是设备表和数据表),但前端展示和分析需求多样化的场景,Dify的自然语言理解和工作流编排能力,似乎提供了一个全新的解决思路。
环境监测平台设备表页面如下:
环境监测平台数据表页面如下:可以展示每台设备的温度、湿度、CO2、PM2.5等环境参数。
环境监测平台单设备图表展示:
核心思路:让机器“听懂”需求,自动绘图分析
我们的目标是搭建一个能让用户用“大白话”提问,系统就能自动从数据库捞取数据、生成图表并给出初步分析的平台。
三步走,搭建你的专属数据可视化助手
- 1. “听懂”你的需求 (用户问题理解与解析)
-
- 意图判断与需求提炼:Dify首先解析用户问题,判断是否需要图表,并提炼核心查询点。
- 关键参数提取:将“SQL查询需求”、“是否需要图表”、“图表类型”等信息结构化。
- “指挥”数据库干活 (自然语言转SQL及执行)
-
- 自然语言转SQL:Dify的核心能力之一,将用户的自然语言需求转换成SQL语句。在这一步,清晰地告诉大模型表结构和表间关系至关重要。
- 执行SQL查询:连接数据库,执行生成的SQL,获取数据。
- “呈现”数据之美 (结果汇总与图表生成)
-
- 结果汇总与分析:结合用户问题和查询结果,进行初步的数据解读和分析。引入时间插件能让分析更精准。
- 智能生成Echarts**图表:若用户需要,Dify驱动大模型将数据转换成Echarts的JSON配置格式,自动生成图表。
- 优雅回复:整合分析结果和图表,呈现给用户。同时,设计好异常处理流程,确保系统稳定性。
比如下面这个问题:卢集6和卢集7这两台设备最后1小时的CO2趋势图。
这个问题我之前做的环境监测平台还没有做两台设备的对比图,只做了单个设备的多个参数趋势图。
刚好测试一下这个流程。
1 数据准备
设备表部分数据:
数据表部分数据:
2 搭建对话流
整个对话流的流程如下:
2.1 开始节点
默认配置,接收用户问题。
2.2 判断用户问题
分析用户问题,判断用户是否需要生成图表,提取出SQL查询的需求。
输出如下:
sql_requirement: [精炼后的数据查询需求]
need_chart: [是/否]
chart_type: [推荐的Echarts图表类型或“无”]
大模型选择了DeepSeek V3。
提示词如下:
你是一名专业的数据需求提炼师。
请仔细阅读用户的自然语言问题{{#context#}},只保留与数据查询直接相关的核心需求。自动忽略与数据查询无关的内容(如生成图表、导出Excel、制作报表等)。
请判断用户是否需要用图表展示结果。如果需要,请根据问题内容推荐最合适的Echarts图表类型(如pie、bar、line等);如果不需要图表,请填写“无”。
请严格按照如下格式输出,不要有任何解释或多余内容:
sql_requirement: [精炼后的数据查询需求]
need_chart: [是/否]
chart_type: [推荐的Echarts图表类型或“无”]
2.3 参数提取
把上一个节点的三个输出参数提取出来。
2.4 自然语言转SQL(ROOKIE_TEXT2DATA)
这个节点的核心功能就是把用户的自然语言转成SQL语句了。
输入为提取后的SQL语句需求,关联参数提取节点的sql_requirement。
数据库配置: 正确填写数据库类型、IP、端口、库名、用户名、密码。
大模型:我这里就用DeepSeek V3了。
在自定义提示中需要把两张表的表结构添加进去,为了模型更好的理解和生成SQL语句。
这个例子是涉及到了多表关联查询,所以需要把两张表的表关系描述清楚。
我这个示例中两张表是通过设备ID(dev_id)关联在一起的。
表关系也没有那么难描述,基本就是这种,某张表的某个字段和另一张表的某个字段是有关联关系的。
表之间的关系:
monitor_device.dev_id 与 monitor_data.dev_id 通过设备ID关联;
一个设备可能有多条采样记录(一对多关系);
还有一个值得指出的一个配置,为【SQL返回数据量限制】,需要根据自己实际的数据量进行配置。
如果数据量太大,这里选择默认配置的话就不能返回全部数据了。
完整的自定义提示词:
表名:monitor_device
字段说明:
order_id:自增序列号,bigint
dev_id:设备id,string
proj_name:所属项目,string
dev_name:设备名称,string
dev_usr:设备用户,string
dev_pos:设备位置,string
status:设备状态(0正常 1停用),string
create_by:创建者,string
create_time:创建时间,datetime
update_by:更新者,string
update_time:更新时间,datetime
param_a_open:参数A开,int
param_a_close:参数A关,int
param_b_open:参数B开,int
param_b_close:参数B关,int
param_c_open:参数C开,int
param_c_close:参数C关,int
param_d_open:参数D开,float
param_d_close:参数D关,float
back1:备用字段1,string
back2:备用字段2,string
back3:备用字段3,string
back4:备用字段4,string
back5:备用字段5,float
back6:备用字段6,float
表名:monitor_data
字段说明:
data_id:自增序列号,bigint
dev_id:设备ID,string
samp_time:采样时间,datetime
temperature:温度,float
humidity:湿度,float
co2:CO2,float
pm1:PM1.0,float
pm2_5:PM2.5,float
pm10:PM10,float
hcho:甲醛,float
tvoc:TVOC,float
o2:O2,float
o3:O3,float
co:CO,float
h2s:硫化氢,float
so2:SO2,float
h2:H2,float
no2:NO2,float
noise:噪声,float
longitude:经度,float
latitude:纬度,float
battery:电池电量,float
illumination:光照度,float
atmos:大气压,float
back1:备用字段1,float
back2:备用字段2,float
back3:备用字段3,float
back4:备用字段4,float
back5:备用字段5,float
back6:备用字段6,float
back7:备用字段7,float
back8:备用字段8,float
back9:备用字段9,float
back10:备用字段10,float
表之间的关系:
monitor_device.dev_id 与 monitor_data.dev_id 通过设备ID关联;
一个设备可能有多条采样记录(一对多关系);
2.5 执行SQL(ROOKIE_EXCUTE_SQL)
此节点负责连接数据库,并执行上一步生成的SQL语句。
输入变量:上一节点返回的SQL语句。
数据库配置: 正确填写数据库类型、IP、端口、库名、用户名、密码。
输出变量:返回数据格式为文本。
2.6 获取当前时间
默认大模型不能获取当前时间,所以在这里增加了一个获取当前时间的插件。
注意要把时区改一下。
这个节点是方便后面大模型做分析用的,因为我的数据是环境监测设备每分钟去发送的,用户的问题也是和时间相关的。
2.7 结果汇总
根据用户的问题、SQL返回的结果做汇总和分析。
提示词如下:
请根据用户问题和查询结果,用简洁的中文自然语言回答。
并给出分析意见。
用户问题:{{#1747293861202.sql_requirement#}}
查询结果:{{#context#}}
当前时间:{{#1747300874013.text#}}
2.8 条件分支
判断是否需要图表,给到不同的分支。
2.9 生成图表
把SQL生成的结果转换成Echarts格式。
之前几篇文章用的都是现成的图表插件,这里直接用大模型生成Echarts图表能够识别的格式就行了。
提示词如下:
你是ECharts可视化专家,收到结构化数据后请严格按以下要求生成图表option:
【输入要求】
输入内容为:{{#context#}} + {{#sys.query#}}
只有当输入中包含可用于绘制图表的结构化数据时才需要响应,否则请直接返回空字符串。
【处理要求】
先判断数据类型(如数值型、分类型、时间序列等),再自动选择最合适的ECharts图表类型(如bar、line、pie等)。
option配置需完整,包含title、tooltip、legend、series等常用字段,series.type与数据类型匹配。
title.text请用简洁中文自动生成,准确反映数据内容。
响应式布局为默认设置。
series.data中必须包含所有原始数据点,不允许遗漏、采样或合并。
【输出要求】
输出内容必须是字符串,且以```echarts开头,以```结尾,中间为完整的ECharts option JSON。
只输出option配置本身,不允许有任何注释、说明、前后缀或多余内容。
若输入不满足绘图条件,直接输出空字符串。
可能有人不太明白这块,我们以饼图举个例子。
我们在回复节点直接输出,就可以得到下面这个漂亮的饼图了。
输出的格式是以echarts开头,
结尾。中间就是Echarts图表的配置项了。
```echarts
{
"title":{
"left":"center",
"text":"水果销售数据"
},
"tooltip":{
"trigger":"item"
},
"legend":{
"orient":"vertical",
"left":"left"
},
"series":[
{
"type":"pie",
"data":[
{
"value":300,
"name":"苹果"
},
{
"value":150,
"name":"香蕉"
},
{
"value":200,
"name":"橙子"
},
{
"value":100,
"name":"葡萄"
},
{
"value":50,
"name":"梨"
}
]
}
]
}
```
可以看看官方示例饼图的配置,是不是和上面的一样。
Dify看到这种包裹起来的字符串就能渲染成图表了。
之前用的现成的插件是别人写好的,如果大模型直接出这种格式的数据效果是一样的。
2.10 回复数据/图表
这个非常好理解,如果问题不用图表,直接回复分析结果。
如果需要图表,回复分析结果+图表。
2.11 异常处理
如果图表生成异常,我给了个异常处理的分支,直接回复文字的分析结果。
3 测试
1、先测试一个单表查询生图:对比不同项目下设备的总数,生成饼图
2、故意找了一台异常的测试设备,CO2的值都是0来测试。
分析结果蛮好的,给出需要检测传感器是否有故障。
3、多台设备参数对比图
4 问题汇总
在整个探索和实践过程中,也并非一帆风顺。比如遇到的下面这些问题:
大模型的时间感知:初期,我们发现大模型对“当前时间”的理解有偏差,必须引入时间插件并精确配置时区,才能保证基于时间的分析准确无误。
SQL查询的数据量平衡:返回数据量过小,分析不全面;返回数据量过大,则可能超出大模型的处理能力或API的限制,导致处理缓慢甚至报错。这需要在实际应用中根据场景特点和用户习惯不断调优。
图表生成的数据完整性与稳定性:我们观察到,在某些情况下,大模型生成的Echarts图表可能会丢失部分数据点,或者在处理复杂、大数据量的图表请求时不够稳定。这时,优化提示词、引入数据采样机制,甚至在特定场景回退到更成熟的图表插件,都是需要考虑的策略。此外,为图表生成节点设置完善的异常处理分支也必不可不可少,确保在图表生成失败时,用户至少能得到文字版的分析结果。
下面就是图表生成的时候异常报错:
这些挑战真实地反映了任何新技术或新方案在商业化落地过程中都可能遇到的“成长的烦恼”。
Dify+数据库+Echarts的组合展现了巨大的潜力,它能显著降低数据可视化的开发门槛,提升交互体验。
但从一个可行的原型到一个稳定可靠、能够大规模商业化应用的产品,中间还有很长的路要走。
这不仅仅是技术层面的调优,更涉及到对业务场景的深入理解、对用户体验的持续打磨,以及对系统健壮性和扩展性的不断优化。
商业化落地从来不是一蹴而就的,它需要耐心,需要在实践中不断发现问题、解决问题,一步一个脚印地慢慢打磨和完善。
尽管会遇到各种预料之中或之外的困难,但正是这个持续迭代、精益求精的过程,才能最终将一个好的想法锻造成一个真正有价值的产品。
DSL文件已经整理好了,放在这里了>> 👉 福利来袭
掘金大礼包:《2025最全AI大模型学习资源包》无偿分享,安全可点 👈