【Dify实战】基于Dify+数据库+Echarts搭建了一个能“听懂”人话的数据可视化助手!(含自然语言转SQL)

今天我将手把手带你利用dify的工作流编排能力和大型语言模型(LLM),搭建一个能“听懂”我们自然语言指令,自动查询数据库、判断图表类型,并最终生成酷炫Echarts图表的数据可视化助手!

忘记那些写死的SQL和固定的图表吧,是时候让数据分析变得如此智能和随心所欲了!

核心思路很简单: 用户用大白话提问 -> 工作流智能判断是否需要图表及图表类型 -> 将用户问题转化为SQL语句 -> 执行SQL查询数据库 -> 将查询结果处理成图表所需格式 -> 调用相应的Echarts图表插件生成图表 -> (如果不需要图表)LLM总结数据并回复。

听起来很酷?让我们一步步拆解这个工作流是如何搭建的。

1、 数据准备

还是用中国历史票房红榜的数据了,使用Mysql数据库。

你可以根据自己的实际业务数据进行替换。

我们选取其中的年份、电影名称、评分、导演、票房来新建数据表。

新建数据库test。

新建数据库表boxoffice结构如下:

Image

-- ----------------------------
-- Table structure for boxoffice
-- ----------------------------
DROPTABLE IF EXISTS `boxoffice`;
CREATETABLE `boxoffice`  (
  `years` varchar(64) CHARACTERSET utf8 COLLATE utf8_unicode_ci NULLDEFAULTNULL,
  `movie_name` varchar(255) CHARACTERSET utf8 COLLATE utf8_unicode_ci NULLDEFAULTNULL,
  `score` floatNULLDEFAULTNULL,
  `director` varchar(64) CHARACTERSET utf8 COLLATE utf8_unicode_ci NULLDEFAULTNULL,
  `box_office` floatNULLDEFAULTNULL
) ENGINE = MyISAM CHARACTERSET= utf8 COLLATE= utf8_unicode_ci ROW_FORMAT =Dynamic;

数据如下:

Image

2、 搭建对话流

这次用到了两个插件,需要提前安装一下。

1、插件市场搜索“db”,找到【rookie_text2data】插件进行安装。

Image

2、插件市场搜索“Echarts”,找到【ECharts图表生成】插件进行安装。

Image

新建一个对话流,这次稍微复杂一点点,整体流程如下:

Image

2.1 开始节点

这里无需特殊配置,它将作为接收用户自然语言提问的入口。

2.2 判断用户问题 (LLM节点)

这是我们工作流的第一个“智能大脑”。

它负责分析用户的原始问题,判断是否需要图表,并初步确定合适的图表类型(柱状图、折线图、饼图,或不需要图表)。

模型选择: deepseek-ai/DeepSeek-V3

Image

完整提示词如下:

你是一个智能助手,需要根据用户的问题判断是否需要用图表展示数据,并判断适合的图表类型。  
请严格按照如下JSON格式输出,不要输出多余内容:

{
  "need_chart": true/false,  // 是否需要图表
  "chart_type": "bar/line/pie/none"  // 推荐的图表类型,bar=柱状图,line=折线图,pie=饼图,none=不需要图表
}

判断标准:
- 如果用户问题中包含“趋势”、“变化”、“对比”、“分布”、“比例”、“占比”、“增长”等词,通常需要图表。
- 如果用户问题中有“每年”、“历年”、“随时间”、“随年份”等,推荐折线图(line)。
- 如果用户问题中有“对比”、“排行”、“最多”、“最少”等,推荐柱状图(bar)。
- 如果用户问题中有“占比”、“比例”、“分布”等,推荐饼图(pie)。
- 如果用户只是问具体数值、详情、描述,不需要图表,chart_type 填 none。

示例:
用户问题:“请用图表展示历年票房变化”  
输出:{"need_chart": true, "chart_type": "line"}

用户问题:“各导演的票房占比是多少?”  
输出:{"need_chart": true, "chart_type": "pie"}

用户问题:“哪吒之魔童降世的票房是多少?”  
输出:{"need_chart": false, "chart_type": "none"}

现在请判断下面这个问题:
{{#sys.query#}}

2.3 自然语言转SQL(ROOKIE_TEXT2DATA)

这个节点是实现“自然语言对话数据库”的核心。它会将用户的原始问题(sys.query)和我们预设的数据库表结构信息结合,智能地生成可执行的SQL语句。

输入变量:

  • 数据表名称: boxoffice
  • 查询语句: {{sys.query}} (引用开始节点的用户输入)

数据库配置: 正确填写数据库类型、IP、端口、库名、用户名、密码。

大模型:我这里就用DeepSeek V3了。

Image

返回数据格式为TEXT,就是自然语言转成的SQL语句。

Image

自定义提示词:

表名:boxoffice
字段说明:
- years:年份,int
- movie_name:电影名,string
- score:评分,float
- director:导演,string
- box_office:票房,int

注意事项:
- 如有分组统计,请使用SUM、AVG等聚合函数,不要直接用原始字段。
- 所有非聚合字段必须出现在GROUP BY中。
- 只输出SQL语句,不要解释。

示例查询:
1. 查询每个导演的总票房:SELECT director, SUM(box_office) FROM boxoffice GROUP BY director;
2. 查询每年票房最高的电影:SELECT years, movie_name, MAX(box_office) FROM boxoffice GROUP BY years;

之所以选择这个插件,因为支持自定义提示词,这样就可以把表结构及注意事项都写在提示词中。

2.4 执行SQL(ROOKIE_EXCUTE_SQL)

此节点负责连接数据库,并执行上一步生成的SQL语句。

输入变量:

  • 待执行的SQL语句: {{rookie_text2data.text}} (引用上一个节点的输出,即生成的SQL语句)

  • 数据库配置: 正确填写数据库类型、IP、端口、库名、用户名、密码。

  • 返回数据格式: TEXT。

Image

Image

2.5 条件分支-判断是否需要图表

这是一个逻辑判断节点,它会根据第一个LLM节点【判断用户问题】的输出,决定工作流是走向“生成图表”的分支,还是走向“无需图表,直接文字回答”的分支。

Image

2.6 处理图表数据

上一个条件分支走到需要图表,工作流会进入这个LLM节点。

它的核心任务是将SQL查询结果(目前是TEXT格式)和第一个LLM判断出的图表类型(target_chart_type),转换为后续代码节点或图表插件所需的格式化数据。

Image

完整提示词如下:

SYSTEM
你是一个数据格式化专家。你的核心任务是根据已执行的SQL查询结果和用户指定的图表类型,将数据转换为特定图表工具所需的输入格式。

## 上下文变量说明:
*`target_chart_type`: (String) 用户期望生成的图表类型,值为 "bar"、"pie" 或 "line"。此信息来源{{#1746780564950.text#}}。
*`sql_execution_result`: (JSON Array) SQL查询的执行结果,来源于{{#context#}}例如 `[{"category": "A", "value": 10}, {"category": "B", "value": 20}]`。
*`user_original_query`: (String) 用户最原始的自然语言查询,可用于辅助生成图表标题,,来源于{{#sys.query#}}。

## 图表工具输入规范:
1.**柱状图 (bar):**
    * 标题 (String)
    * 数据 (String): 数字用 ";" 分隔 (例如:"150;280;200")
    * x轴 (String): 文本用 ";" 分隔 (例如:"一月;二月;三月")

2.**饼图 (pie):**
    * 标题 (String)
    * 数据 (String): 数字用 ";" 分隔 (例如:"30;50;20")
    * 分类 (String): 文本用 ";" 分隔 (例如:"类型A;类型B;类型C")

3.**线性图表 (line):**
    * 标题 (String)
    * 数据 (String): 数字用 ";" 分隔 (例如:"10;15;13;18")
    * x轴 (String): 文本用 ";" 分隔 (例如:"周一;周二;周三;周四")

## 任务指令:

1.**解析核心数据**:
    * 将输入的 `sql_json_string_result` (它是一个字符串) **作为 JSON 进行解析**。解析后的结果可能是单个 JSON 对象(如果SQL只返回一行)或一个 JSON 对象数组(如果SQL返回多行)。我们将其称为 `core_sql_data`。
    * 例如,如果 `sql_json_string_result` 是字符串 `"[{\"colA\": \"val1\", \"colB\": 10}]"`,那么 `core_sql_data` 就是实际的数组 `[{"colA": "val1", "colB": 10}]`。
    * 如果 `sql_json_string_result` 是字符串 `"{\"colA\": \"val1\", \"colB\": 10}"`,那么 `core_sql_data` 就是实际的对象 `{"colA": "val1", "colB": 10}`。为了统一处理,如果它是单个对象,请将其视为只包含一个元素的数组。

2.**生成图表标题**: 参考 `user_original_query`,生成一个简洁明了的 `chart_tool_title`。

3.**数据提取与格式化 (基于 `core_sql_data`)**:
    * 分析 `core_sql_data`。数组中的每个对象代表一个数据点。你需要从中识别出用作标签/类别/x轴的字段(通常是文本或日期类型)和用作数值/数据的字段(通常是数字类型)。
    ***根据 `target_chart_type` 指示的类型进行处理:**
        ***若为 "bar"**:
            * 从 `core_sql_data` 中提取所有对象的数值字段值,用 ";" 连接成 `chart_tool_data_string`。
            * 提取所有对象的标签字段值,用 ";" 连接成 `chart_tool_label_string` (对应x轴)。
        ***若为 "pie"**:
            * 从 `core_sql_data` 中提取所有对象的数值字段值,用 ";" 连接成 `chart_tool_data_string`。
            * 提取所有对象的标签字段值,用 ";" 连接成 `chart_tool_label_string` (对应分类)。
        ***若为 "line"**:
            * 从 `core_sql_data` 中提取所有对象的数值字段值,用 ";" 连接成 `chart_tool_data_string`。
            * 提取所有对象的标签字段值,用 ";" 连接成 `chart_tool_label_string` (对应x轴).
    * 确保 `core_sql_data` 中至少有一个标签/类别字段和一个数值字段可供提取。如果字段不明确(例如,多个数字列),优先选择第一个文本/日期字段作为标签,第一个数字字段作为数据,或根据 `user_original_query` 中的暗示选择。

4.**构建输出**:
    * 以严格的JSON对象格式输出以下字段:
        *`chart_tool_title` (String)
        *`chart_tool_data_string` (String)
        *`chart_tool_label_string` (String)
        *`chart_type_final` (String, 其值应等于输入的 `target_chart_type`)

## 示例(假设变量已按上述说明传入):

* 若 `target_chart_type` = "bar"
* 若 `sql_json_string_result` (字符串) = `"[{\"product_name\": \"产品A\", \"total_sales\": 5500}, {\"product_name\": \"产品B\", \"total_sales\": 7200}]"`
* 若 `user_original_query` = "查询产品销售额柱状图"

期望的输出JSON:
```json
{
  "chart_tool_title": "产品销售额柱状图",
  "chart_tool_data_string": "5500;7200",
  "chart_tool_label_string": "产品A;产品B",
  "chart_type_final": "bar"
}

2.7 代码节点转换数据

功能就是提取LLM的数据,作为图表节点的输入。

Image

完整代码如下:

import json
import re # 导入正则表达式模块

defmain(llm_data_input):
    llm_data_str = llm_data_input

    # 1. 清理 Markdown 代码块标记 (保持你现有的健壮清理逻辑)
    llm_data_str = llm_data_str.strip()
    if llm_data_str.startswith("```json"):
        llm_data_str = llm_data_str[len("```json"):]
    elif llm_data_str.startswith("```"):
        llm_data_str = llm_data_str[len("```"):]
    if llm_data_str.endswith("```"):
        llm_data_str = llm_data_str[:-len("```")]
    llm_data_str = llm_data_str.strip()
    
    data = {}
    try:
        data = json.loads(llm_data_str)
    except json.JSONDecodeError as e:
        return {
            "unpacked_title": f"Error: Invalid JSON - {e}",
            "unpacked_data": "",
            "unpacked_labels": "",
            "chart_type": "error_invalid_json"
        }

    title = data.get("chart_tool_title", "无标题")
    original_data_string = data.get("chart_tool_data_string", "") # 获取原始数据字符串
    label_string = data.get("chart_tool_label_string", "")
    chart_type = data.get("chart_type_final")

    if chart_type isNone:
        chart_type = "unknown_type_from_llm"

    # 2. 清理和验证 data_string (这是关键的修改部分)
    cleaned_data_parts = []
    if original_data_string: # 只有当原始数据字符串非空时才处理
        parts = original_data_string.split(';')
        for part in parts:
            part = part.strip() # 移除每个部分前后的空格
            if part: #确保部分不是空字符串
                try:
                    # 尝试转换为 float 来验证它是否是有效数字
                    # 我们仍然以字符串形式保存,因为插件期望分号分隔的字符串
                    float(part) # 如果这里失败,会抛出 ValueError
                    cleaned_data_parts.append(part)
                except ValueError:
                    cleaned_data_parts.append("0") # 方案 b: 替换为 "0"
                    # print(f"Warning: Invalid data part '{part}' replaced with '0'.")
            else:
                # 如果部分是空字符串 (例如来自 ";;"),也替换为 "0" 或跳过
                cleaned_data_parts.append("0") # 方案 b: 替换为 "0"
                # print(f"Warning: Empty data part replaced with '0'.")
    
    unpacked_data = ";".join(cleaned_data_parts)

    return {
        "unpacked_title": title,
        "unpacked_data": unpacked_data, # 使用清理过的数据字符串
        "unpacked_labels": label_string, # 标签字符串通常不需要转为数字,所以保持原样
        "chart_type": chart_type
    }

2.8 条件分支

判断代码节点的chart_type类型,给到不同的图表插件。

Image

2.9 饼图、柱状图、折线图

这些是你预设的、能够接收格式化数据并(理想情况下)输出可渲染Echarts图表的工具。

输入变量 (以饼图为例):

  • 标题: {{转换数据节点输出.unpacked_title}}

  • 数据: {{转换数据节点输出.unpacked_data}}

  • 分类: {{转换数据节点输出.unpacked_labels}}

  • 输出: 这些插件的输出应该是Dify可以直接渲染的图表格式(例如,包含 echarts ... 的文本)。

Image

Image

Image

2.10 不需要图表LLM分析

如果最初判断用户问题不需要图表,工作流会进入这个分支。

这里我们用一个LLM节点,根据用户的问题和“执行SQL”节点返回的(TEXT格式)查询结果,用自然语言生成一段简洁的文字回答。

Image

提示词如下:

请根据用户问题和查询结果,用简洁的中文自然语言回答。
用户问题:{{#sys.query#}}
查询结果:{{#context#}}

注意查询结果中票房数据不用换算,原数据展示,单位为万元

2.11 回复节点

这是工作流的终点,负责将生成的结果(无论是图表还是文字回答)展示给用户。

你需要配置此节点,使其能够正确接收并展示来自不同分支的输出。

Image

2.12 开场白设置

在Dify应用的“提示词编排”或“开场白”设置中,你可以设计一个友好的欢迎语和一些示例问题,引导用户开始提问。

点击预览按钮,找到右下角“管理”:

Image

点击编写开场白。

Image

设置开场白和开场问题。

Image

3 、测试

测试生成3种格式的图表及无需生成图表的问题。

1、饼图测试。测试问题:各导演的票房占比是多少?

Image

可以看一下工作流具体是怎么工作的?

自然语言转SQL:

Image

转换数据:最后给图表插件传递的参数。

Image

2、折线图测试。测试问题:各导演的票房占比是多少?

Image

3、柱形图测试。测试问题:评分最高的五部电影。

Image

4、普通问题测试,无需生成图表,直接回答。

Image

通过Dify工作流的巧妙搭建,我们成功地让数据可视化助手学会了“聆听”。

自然语言转SQL,再到Echarts图表,一切都那么自然而高效。

技术的进步,是为了更好地服务于人。

从此,你可以更专注于数据背后的洞察。

把重复的任务,交给智能的助手。

那么,如何系统的去学习大模型LLM?

作为一名从业五年的资深大模型算法工程师,我经常会收到一些评论和私信,我是小白,学习大模型该从哪里入手呢?我自学没有方向怎么办?这个地方我不会啊。如果你也有类似的经历,一定要继续看下去!这些问题啊,也不是三言两语啊就能讲明白的。

所以我综合了大模型的所有知识点,给大家带来一套全网最全最细的大模型零基础教程。在做这套教程之前呢,我就曾放空大脑,以一个大模型小白的角度去重新解析它,采用基础知识和实战项目相结合的教学方式,历时3个月,终于完成了这样的课程,让你真正体会到什么是每一秒都在疯狂输出知识点。

由于篇幅有限,⚡️ 朋友们如果有需要全套 《2025全新制作的大模型全套资料》,扫码获取~
在这里插入图片描述

👉大模型学习指南+路线汇总👈

我们这套大模型资料呢,会从基础篇、进阶篇和项目实战篇等三大方面来讲解。
在这里插入图片描述
在这里插入图片描述

👉①.基础篇👈

基础篇里面包括了Python快速入门、AI开发环境搭建及提示词工程,带你学习大模型核心原理、prompt使用技巧、Transformer架构和预训练、SFT、RLHF等一些基础概念,用最易懂的方式带你入门大模型。
在这里插入图片描述

👉②.进阶篇👈

接下来是进阶篇,你将掌握RAG、Agent、Langchain、大模型微调和私有化部署,学习如何构建外挂知识库并和自己的企业相结合,学习如何使用langchain框架提高开发效率和代码质量、学习如何选择合适的基座模型并进行数据集的收集预处理以及具体的模型微调等等。
在这里插入图片描述

👉③.实战篇👈

实战篇会手把手带着大家练习企业级的落地项目(已脱敏),比如RAG医疗问答系统、Agent智能电商客服系统、数字人项目实战、教育行业智能助教等等,从而帮助大家更好的应对大模型时代的挑战。
在这里插入图片描述

👉④.福利篇👈

最后呢,会给大家一个小福利,课程视频中的所有素材,有搭建AI开发环境资料包,还有学习计划表,几十上百G素材、电子书和课件等等,只要你能想到的素材,我这里几乎都有。我已经全部上传到CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费
在这里插入图片描述
相信我,这套大模型系统教程将会是全网最齐全 最易懂的小白专用课!!

<think>好的,我需要帮助用户搭建一个基于本地知识库生成小说的聊天助手,使用Dify、DeepSeek和Ollama。首先,我要回忆一下相关的步骤和引用内容。 根据引用[3],Dify可以集成Ollama和DeepSeek来搭建本地知识库。用户之前已经部署了Ollama和DeepSeek,现在需要结合Dify。首先,用户需要安装Docker和Ollama,然后下载Dify项目。接下来,配置Dify的环境变量,特别是Ollama和DeepSeek的API密钥。之后,创建知识库,上传小说相关的数据,比如设定、角色、节等。然后,在Dify中创建应用程序,选择聊天机器人类型,并关联之前创建的知识库。测试和优化部分可能需要调整提示词,确保生成的内容符合预期。最后,部署到本地或通过接口对外提供服务。 需要确保用户已经安装了Docker和Ollama,并且下载了所需的嵌入模型,如nomic-embed-text:latest,如引用[2]提到的。同时,用户需要获取DeepSeek的API密钥,这在引用[2]和[3]中都有提到。可能还需要注意Dify的配置文件是否正确,以及如何将知识库数据导入到Dify中。另外,生成小说可能需要更复杂的提示工程,确保模型理解用户的需求,比如维持故事连贯性、角色一致性等。需要提醒用户在测试阶段多尝试不同的参数和提示词,以达到最佳效果。</think>以下是使用**Dify + DeepSeek + Ollama**搭建基于本地知识库的小说生成聊天机器人的分步教程: --- ### 一、环境准备 1. **安装Docker与Docker Compose** - 确保系统已安装Docker和Docker Compose(Windows/Mac需从官网下载Docker Desktop)[^3]。 - 验证安装: ```bash docker --version docker-compose --version ``` 2. **部署Ollama并下载模型** - 安装Ollama(支持Mac/Linux): ```bash curl -fsSL https://ollama.com/install.sh | sh ``` - 下载小说生成相关模型(如开源写作模型`writer-mega`): ```bash ollama pull writer-mega ``` 3. **获取DeepSeek API Key** - 登录DeepSeek官网申请API Key(用于增强模型推理能力)[^2]。 --- ### 二、部署Dify 1. **下载Dify项目** ```bash git clone https://github.com/langgenius/dify.git cd dify/docker ``` 2. **配置环境变量** 修改`.env`文件,添加以下配置: ```env # Ollama配置 OLLAMA_API_BASE_URL=http://host.docker.internal:11434 OLLAMA_MODEL=writer-mega # DeepSeek配置 DEEPSEEK_API_KEY=your_api_key_here ``` 3. **启动Dify服务** ```bash docker-compose up -d ``` --- ### 三、构建小说知识库 1. **准备数据集** - 上传小说相关素材(如世界观设定、角色档案、剧大纲等),支持格式:`PDF/TXT/Markdown`。 - 建议分文件存储不同主题(例如:`奇幻设定.md`, `角色对话模板.txt`)。 2. **创建知识库** - 登录Dify控制台(`http://localhost:80`),进入**知识库** → **新建知识库** → 命名(如“奇幻小说素材库”)。 - 上传文件并选择嵌入模型(推荐`nomic-embed-text:latest`)。 --- ### 四、创建聊天机器人应用 1. **新建应用程序** - 选择**对话型应用** → 输入名称(如“小说生成助手”)。 2. **配置提示词** 在**提示词编排**中添加以下内容: ```text 你是一个小说创作助手,基于用户提供的知识库生成连贯的故事节。要求: 1. 保持角色性格一致性 2. 参考知识库中的世界观设定 3. 每段输出包对话和场景描写 示例输出格式: 「场景描述」... [角色A]:“...” ``` 3. **关联知识库** - 在**数据集与模型**中绑定已创建的“奇幻小说素材库”。 - 选择混合推理模式:`Ollama(本地模型) + DeepSeek(优化逻辑连贯性)`。 --- ### 五、测试与优化 1. **在线测试** - 在Dify的**聊天调试**界面输入指令测试: ```text 生成一段骑士与龙对抗的开场场景,包紧张对话 ``` 2. **参数调整** - 调整**Temperature**(0.7-1.2增加创造性)和**Max Tokens**(建议≥2000保证段落完整)。 3. **持续迭代** - 根据输出结果补充知识库内容(如添加“战斗场景词汇表”)。 - 通过**标注反馈**功能训练模型偏好。 --- ### 六、部署使用 1. **API对接** - 在**应用设置**中获取API密钥,通过以下代码调用: ```python import requests response = requests.post( "http://localhost/v1/chat-messages", json={"query": "生成吸血鬼城堡的悬疑开头", "user_id": "123"}, headers={"Authorization": "Bearer {API_KEY}"} ) ``` 2. **本地访问** - 直接使用Dify提供的Web界面链接分享给本地用户。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值