AI时代 你不是一个旁观者。文末可领DSL文件及token福利。
前言
Dify 或其底层模型默认使用 Mermaid 语法生成的思维导图,虽然快速方便,但是生成效果嘛,就像下图这样,又乱又丑。像极了买家秀与卖家秀。
本文旨在分享一个基于 Dify 工作流的设计思路,利用大语言模型(LLM)的能力,结合 PlantUML 语法和外部渲染服务,实现输入主题或内容后,一键生成更美观的思维导图图片,实现下图的效果。
2、工作流设计
以下是构建该 Dify 工作流的核心步骤和节点设计:
工作流概览:
输入(主题/内容)-> [LLM 处理内容/拓展主题] -> [LLM 生成 PlantUML 语法] -> [HTTP 请求 PlantUML 渲染服务] -> 输出(思维导图图片)(还 可以生成流程图,如下图所示)
节点详解:
- 1、开始节点 (Start Node):
- 配置输入变量:
topic
(文本类型): 用于定义思维导图的核心主题。例如:“人工智能的应用领域”。
content
(文本类型, 可选): 用户可以预先提供一些关于主题的初步内容、要点或结构。如果留空,则依赖 LLM 基于
topic
进行拓展。
- 2、LLM 节点 1 (内容整理与拓展):
- 作用:
根据输入判断是整理用户提供的内容,还是围绕主题进行智能拓展,并输出结构化的思维导图层级。
-
输入:
topic
,
content
- Prompt 设计 (示例):
你是一个思维导图助手。请分析以下输入:
主题:{{topic}}
用户提供的内容:{{content}}
任务:
1. 如果 '用户提供的内容' 不为空且足够详细,请将其整理成一个清晰的、层级分明的思维导图结构。
2. 如果 '用户提供的内容' 为空或过于简单,请围绕 '主题' 进行深入思考和拓展,生成一个包含主要分支和子节点的、结构化的思维导图内容。
3. 输出结果必须是清晰的层级列表格式(例如使用缩进或符号表示层级),不要包含任何解释性文字,只输出结构本身。
示例输出格式:
- 主题
- 分支1
- 子节点1.1
- 子节点1.2
- 分支2
- 子节点2.1
- 输出变量:
包含层级化内容的文本。
-
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、上述流程优化余地
尽管上述流程可以实现基本功能,但仍有进一步优化的空间:
- PlantUML 语法增强:
指导 LLM 生成更复杂的 PlantUML 语法,例如添加颜色、图标、特定布局方向(左右分布等)、使用皮肤参数(skinparam)进行样式定制。这需要在 LLM 节点 2 的 Prompt 中加入更具体的指令。 2. 错误处理:
- 在 LLM 节点后检查输出是否符合预期格式。
- 在 HTTP 请求节点后检查响应状态码,如果渲染失败(例如 PlantUML 语法错误或服务器问题),可以返回错误信息或默认图片。
- 私有化部署:
对于有隐私或性能要求的场景,可以自建 PlantUML 服务器或 Kroki 实例,并将 HTTP 请求指向内部地址。
4、其他在线网站
除了通过 Dify 工作流生成思维导图,市面上也有许多优秀的、用户可以直接在线使用的思维导图工具,它们通常提供更丰富的交互式编辑体验:
- MindMeister:
流行的在线协作思维导图工具,功能全面,界面友好。
- XMind (Web / App):
老牌思维导图软件,提供 Web 版本和强大的桌面/移动应用,模板丰富。
- Coggle:
简洁美观的在线思维导图工具,适合快速创建和分享。
- Miro / FigJam:
更偏向于在线白板,但其思维导图功能也非常强大,适合团队协作和头脑风暴。
- Draw.io (diagrams.net):
免费的在线图表绘制工具,支持包括思维导图在内的多种图表类型,功能强大但可能稍复杂。
- ProcessOn / Boardmix:
国内流行的在线协作绘图平台,也包含思维导图功能。
这些工具各有侧重,用户可以根据自己的具体需求(例如,是需要 AI 辅助快速生成,还是需要手动精细绘制与协作)来选择合适的工具。
关注我可领DSL文件及token福利
往期工作流文章
10分钟构建基于 Dify 的智能文章仿写工作流:配置指南,效率飙升300%!
20分钟从零到一构建Dify智能客服工作流教程(附DSL文件下载)
更多工作流案例,请到公众号主页查看
dify相关资源
如果对你有帮助,欢迎点赞收藏 备用。
回复 DSL 获取公众号DSL文件资源
回复 入群 获取二维码,我拉你入群
回复 tk 获取免费token资源
你又不打算赞赏,就点赞、在看吧。