dify工作流分享 基于dify的思维导图一键生成器 直接出图

公众号:dify实验室

基于LLMOps平台-Dify的一站式学习平台。包含不限于:Dify工作流案例、DSL文件分享、模型接入、Dify交流讨论等各类资源分享。

前言

Dify 或其底层模型默认使用 Mermaid 语法生成的思维导图,虽然快速方便,但是生成效果嘛,就像下图这样,又乱又丑。像极了买家秀与卖家秀。

图片

本文旨在分享一个基于 Dify 工作流的设计思路,利用大语言模型(LLM)的能力,结合 PlantUML 语法和外部渲染服务,实现输入主题或内容后,一键生成更美观的思维导图图片,实现下图的效果。

图片

2、工作流设计

以下是构建该 Dify 工作流的核心步骤和节点设计:

工作流概览:

输入(主题/内容)-> [LLM 处理内容/拓展主题] -> [LLM 生成 PlantUML 语法] -> [HTTP 请求 PlantUML 渲染服务] -> 输出(思维导图图片)(还 可以生成流程图,如下图所示)

图片

节点详解:

  • 1、开始节点 :

    • 配置输入变量:
      • topic

         (文本类型): 用于定义思维导图的核心主题。例如:“人工智能的应用领域”。

      • content

         (文本类型, 可选): 用户可以预先提供一些关于主题的初步内容、要点或结构。如果留空,则依赖 LLM 基于 topic 进行拓展。

        图片

    1. 2、LLM 节点 1 (内容整理与拓展):

      • 作用:

         根据输入判断是整理用户提供的内容,还是围绕主题进行智能拓展,并输出结构化的思维导图层级。

      • 输入:

         topiccontent

      • Prompt 设计 :
      • 输出变量:

        包含层级化内容的文本。

        图片

        3.LLM 节点 2 (生成 PlantUML 语法):

      • 作用:

      •  将上一步输出的结构化内容转换成 PlantUML 的思维导图语法。这里使用了deepseek v3模型。(没有免费token资源的,可以看文末福利)

      • 输入:

         上一步的结构化文本。

      • Prompt 设计 :

        # 角色:PlantUML 思维导图代码生成器
        ## 任务:
        将用户提供的、具有层级结构的文本,转换为符合 PlantUML 思维导图语法的、准确无误的代码。
        ## 输入:
        一段表示层级关系的文本。层级通常通过缩进或不同的标记符号(如 `-`, `*`, 数字等)来体现。
        ## 输出要求:
        1.  **代码封装:** 必须使用 `@startmindmap` 作为代码开头,并以 `@endmindmap` 结尾。
        2.  **层级表示(核心语法):**
            *   使用 PlantUML 的星号 (`*`) 语法来表示节点层级。
            *   根节点使用一个星号 (`*`)。
            *   每个下一级节点**必须**在前一级的基础上增加一个星号(例如,根是 `*`,其子级是 `**`,子级的子级是 `***`,以此类推)。**层级必须连续递增,不能跳级。**
        3.  **内容保真:** 必须准确地保留原始文本中的节点内容和层级结构。
        4.  **格式纯粹:** 输出结果**必须**是完整且纯粹的 PlantUML 代码块,不包含任何代码之外的解释、说明、标题、代码块标记(如 ```plantuml)或任何其他文字。
        ## 关键语法规则与常见错误(请严格遵守):
        1.  **起始/结束标签:** 绝对不能遗漏 `@startmindmap` 和 `@endmindmap`。
            *   *错误示例:* 缺少 `@endmindmap`。
        2.  **星号层级:** 层级**只由**星号数量决定,与其他缩进、符号无关。
            *   *错误示例 1 (使用其他符号):*
                ```
                * Root
                - Child 1  // 错误,应为 **
                + Child 2  // 错误,应为 **
                ```
            *   *正确示例 1:*
                ```
                * Root
                ** Child 1
                ** Child 2
                ```
            *   *错误示例 2 (跳级):*
                ```
                * Root
                *** Grandchild // 错误,缺少 ** 层级
                ```
            *   *正确示例 2 (假设有中间层):*
                ```
                * Root
                ** Child
                *** Grandchild
                ```
        3.  **星号与文本间的空格:** 在最后一个星号 (`*`) 和节点文本之间,**必须**有且仅有一个空格。
            *   *正确:* `* Node Text`
            *   *正确:* `** Child Node`
            *   *错误:* `*Node Text` (缺少空格)
            *   *错误:* `**  Child Node` (多余的空格)
        4.  **行首空格:** 行首不应有多余的空格,星号应顶格开始。
            *   *正确:* `* Node`
            *   *错误:* ` * Node` (星号前有空格)
        5.  **节点内容:** 如果原始节点内容包含 PlantUML 的特殊字符(如 `*`, `_`, `-`, `~`, `/`, `\` 等),并且需要按原样显示,理论上需要转义或使用引号,但在此基础转换任务中,优先**直接复制**原始文本内容。如果遇到渲染问题,通常是这些特殊字符导致,但生成器应首先尝试直接映射。
        ## 示例:
        **如果输入文本是:**
        项目规划
        初期调研
        市场分析 (V1.0)
        用户_访谈
        中期执行
        开发阶段 / 测试阶段
        后期总结 - Review
        **则期望的输出 PlantUML 代码是:**
        ```plantuml
        @startmindmap
        * 项目规划
        ** 初期调研
        *** 市场分析 (V1.0)
        *** 用户_访谈
        ** 中期执行
        *** 开发阶段 / 测试阶段
        ** 后期总结 - Review
        @endmindmap

      • 输出变量:

         plantuml_code (包含 PlantUML 语法的文本)。

        图片

      • 4.代码执行节点

        作用

        将plantuml代码PlantUML 代码进行特殊的压缩和编码,并生成url。

        代码如下:

        import
         zlib
        import
         base64 
        # 虽然不直接用标准base64编码,但导入以示对比
        def
         main(arg1: 
        str
        ) -> 
        dict
        :
            
        """
            接收 PlantUML 代码字符串 (arg1),将其压缩并编码为 PlantText URL。
            Args:
                arg1: 包含 PlantUML 代码的字符串。
            Returns:
                一个包含 'result' (生成的 URL) 或 'error' (错误信息) 的字典。
            """
            
        # --- PlantUML 特定的编码辅助函数 (放在 main 内部以保持自包含) ---
            PLANTUML_ALPHABET = 
        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_"
            
        def
         encode6bit(b):
                
        """复制 JS encode6bit 函数的逻辑"""
                
        if
        0
         <= b < 
        10
        : 
        return
        chr
        (
        48
         + b)
                b -= 
        10
                
        if
        0
         <= b < 
        26
        : 
        return
        chr
        (
        65
         + b)
                b -= 
        26
                
        if
        0
         <= b < 
        26
        : 
        return
        chr
        (
        97
         + b)
                b -= 
        26
                
        if
         b == 
        0
        : 
        return
        '-'
                
        if
         b == 
        1
        : 
        return
        '_'
                
        return
        '?'
        # 理论上对于 6-bit 值不会发生
            
        def
         append3bytes(b1, b2, b3):
                
        """复制 JS append3bytes 函数的逻辑"""
                c1 = b1 >> 
        2
                c2 = ((b1 & 
        0x03
        ) << 
        4
        ) | (b2 >> 
        4
        )
                c3 = ((b2 & 
        0x0F
        ) << 
        2
        ) | (b3 >> 
        6
        )
                c4 = b3 & 
        0x3F
                result = 
        ""
                result += encode6bit(c1 & 
        0x3F
        )
                result += encode6bit(c2 & 
        0x3F
        )
                result += encode6bit(c3 & 
        0x3F
        )
                result += encode6bit(c4 & 
        0x3F
        )
                
        return
         result
            
        def
         encode_plantuml(plantuml_text):
                
        """压缩并使用 PlantUML 特定的 Base64 编码"""
                
        # 1. 编码为 UTF-8 字节
                utf8_bytes = plantuml_text.encode(
        'utf-8'
        )
                
        # 2. 使用 raw deflate 压缩 (level 9, 无 zlib 头/尾)
                compressor = zlib.compressobj(level=
        9
        , method=zlib.DEFLATED, wbits=
        -15
        )
                compressed_data = compressor.compress(utf8_bytes)
                compressed_data += compressor.flush()
                
        # 3. 使用 PlantUML 特定的 Base64 进行编码
                encoded_result = []
                data_len = 
        len
        (compressed_data)
                i = 
        0
                
        while
         i < data_len:
                    b1 = compressed_data[i]
                    i += 
        1
                    b2 = compressed_data[i] 
        if
         i < data_len 
        else
        0
                    i += 
        1
                    b3 = compressed_data[i] 
        if
         i < data_len 
        else
        0
                    i += 
        1
                    encoded_result.append(append3bytes(b1, b2, b3))
                
        return
        ""
        .join(encoded_result)
            
        # --- 辅助函数结束 ---
            
        # --- 主逻辑 ---
            
        if
        not
        isinstance
        (arg1, 
        str
        ) 
        or
        not
         arg1:
                
        return
         {
        "error"
        : 
        "输入参数 arg1 必须是非空的 PlantUML 代码字符串。"
        }
            plantuml_code = arg1
            
        try
        :
                
        # 编码 PlantUML 代码
                encoded_string = encode_plantuml(plantuml_code)
                
        # 构建 URL (默认使用 png 格式)
                base_url = 
        "https://uml.planttext.com/plantuml/"
                output_format = 
        "png"
        # 你可以改为 "svg" 或 "txt"
                url = f
        "{base_url}{output_format}/{encoded_string}"
                
        # 返回包含结果 URL 的字典
                
        return
         {
                    
        "result"
        : url
                }
            
        except
         Exception 
        as
         e:
                
        # 打印错误到 Dify 的日志(如果可能)并返回错误信息
                
        print
        (f
        "生成 PlantUML URL 时出错: {e}"
        )
                
        return
         {
        "error"
        : f
        "无法生成 PlantUML URL: {e}"
        }

    • 5.HTTP 请求节点 

      作用:

      •  请求URL,获取渲染后的图片。

      • 输入:代码节点的输出
      • 输出:

         HTTP 请求的响应体。如果请求成功,响应体将是图片数据(PNG 或 SVG)。Dify 通常能识别响应头中的 Content-Type 并尝试展示图片。确保 Dify 配置能正确处理和展示图片类型的响应。

      • 输出变量:

        包含图片数据。

        图片

    • 6.结束节点 :

      • 配置:

         将http请求节点的输出的files设置为最终输出。

      • 效果:

         当工作流执行完毕时,用户将在 Dify 的结果区域直接看到生成的思维导图图片。

        图片

    3、上述流程优化余地

    尽管上述流程可以实现基本功能,但仍有进一步优化的空间:

    1. PlantUML 语法增强:

       指导 LLM 生成更复杂的 PlantUML 语法,例如添加颜色、图标、特定布局方向(左右分布等)、使用皮肤参数(skinparam)进行样式定制。这需要在 LLM 节点 2 的 Prompt 中加入更具体的指令。

    2. 错误处理:
      • 在 LLM 节点后检查输出是否符合预期格式。

      • 在 HTTP 请求节点后检查响应状态码,如果渲染失败(例如 PlantUML 语法错误或服务器问题),可以返回错误信息或默认图片。

    3. 私有化部署: 对于有隐私或性能要求的场景,可以自建 PlantUML 服务器或 Kroki 实例,并将 HTTP 请求指向内部地址。

    4、其他在线网站

    除了通过 Dify 工作流生成思维导图,市面上也有许多优秀的、用户可以直接在线使用的思维导图工具,它们通常提供更丰富的交互式编辑体验:

    • MindMeister:

       流行的在线协作思维导图工具,功能全面,界面友好。

    • XMind (Web / App):

       老牌思维导图软件,提供 Web 版本和强大的桌面/移动应用,模板丰富。

    • Coggle:

       简洁美观的在线思维导图工具,适合快速创建和分享。

    • Miro / FigJam:

       更偏向于在线白板,但其思维导图功能也非常强大,适合团队协作和头脑风暴。

    • Draw.io (diagrams.net):

       免费的在线图表绘制工具,支持包括思维导图在内的多种图表类型,功能强大但可能稍复杂。

    • ProcessOn / Boardmix:

       国内流行的在线协作绘图平台,也包含思维导图功能。

    这些工具各有侧重,用户可以根据自己的具体需求(例如,是需要 AI 辅助快速生成,还是需要手动精细绘制与协作)来选择合适的工具。

    dify实验室

    基于LLMOps平台-Dify的一站式学习平台。包含不限于:Dify工作流案例、DSL文件分享、模型接入、Dify交流讨论等各类资源分享。

    关注我可领DSL文件及token福利

    往期工作流文章

    10分钟构建基于 Dify 的智能文章仿写工作流:配置指南,效率飙升300%!

    20分钟从零到一构建Dify智能客服工作流教程(附DSL文件下载)

    使用 Dify 打造自己的免费 AI 写作神器

    Dify工作流教程|以电费单分析为例详细讲解工作流编排过程

    更多工作流案例,请到公众号主页查看

    dify相关资源 

    如果对你有帮助,欢迎点赞收藏备用。


    回复 DSL 获取公众号DSL文件资源

    回复 入群 获取二维码,我拉你入群

    回复 tk  获取免费token资源

    你又不打算赞赏,就点赞、在看吧