Dify&Coze工作流搭建
Dify实践
主要是对于Dify 和 Coze 这种 AI 应用开发/编排平台
Dify是可以本地化部署的,这里为了简化使用官方平台
GitHub - langgenius/dify: Production-ready platform for agentic workflow development.
创建一个工作流
具体先来一个简单的示例
GitHubUsername助手
[开始] 输入 username
↓
[HTTP 请求] GET https://api.github.com/users/{{username}} (带必要 Headers)
↓
[条件分支] ├── 如果 status_code == 200 │ ↓ │ [LLM 总结] │ - 输入:API JSON │ - 输出:整理后的用户信息
├── 如果 status_code == 404 或 403 │ ↓ │ [输出] │ - “用户不存在或无法访问”
└── ELSE(其它情况) ↓ [输出] - “请求失败,状态码:xxx”
- 创建其实节点,要求传入Username参数
- 创建这个Http-fetch节点,请求api
https://api.github.com/users/{{input.username}}
demo:这里也可以
curl -s https://api.github.com/users/bx33661 \ -H "User-Agent: dify-workflow" \ -H "Accept: application/vnd.github+json" \ -H "X-GitHub-Api-Version: 2022-11-28"
- 分支条件节点,满足200的去传数据给LLM节点
不满足的返回错误,就是一个try-catch
思想
- LLM节点,这里很重要
主要就是设置
- 系统提示词
- 上下文内容
- 传入数据
这里提示词如下
下面有 GitHub 用户的原始数据:
!!!如果 API 返回里包含 "message": "Not Found",你必须原样输出“用户不存在”,不要自己编造。
请总结成中文要点:1. 用户名和昵称2. 简介3. 仓库数、粉丝数、关注数4. 创建时间(转成 YYYY-MM-DD)5. 主页链接
- 结束节点,输出结果
输出
1. 用户名:bx33661,昵称:Bpple2. 简介:未填写3. 仓库数:37,粉丝数:12,关注数:224. 创建时间:2023-07-025. 主页链接:https://github.com/bx33661
就拿这个简单的总结一下
关键就是对于数据流的接入和输出要连接好
可以在运行结果这里,查看一下这个每个节点的输入输出流
题目示例-京津冀长城杯决赛Dify工作流搭建
京津冀长城杯决赛Dify工作流搭建
题目要求如下
核心任务是:在 AI 智能体搭建平台中编辑工作流,实现 “上传指定得分 WP 附件 → 自动提取并输出该题目的格式化考点”。
格式化考点要求:需清晰呈现题目名、题目类型、考点,确保内容准确、结构清晰,题目类型从Misc、Crypto、Web、PWN、Reverse、理论、Blockchain中选择。
示例:
[{"题目名": "xtea","题目类型": "Reverse","考点":"花指令,动态调试,xtea 算法逆向","考点1": "花指令","考点2": "动态调试","考点3": "xtea 算法逆向"}]
[{"题目名": "easycms","题目类型": "Web","考点":"php-代码审计,ssrf-攻击内网应用","考点1": "php-代码审计","考点2": "ssrf-攻击内网应用"}]
最终效果
最终搭建效果如下
- 起始节点允许上传pdf文件
- 加一个文件提取器节点去处理这个pdf
- LLM节点
因为这个流是严格处理文档,所以把模型温度调到0,严格输入
System-Prompt
你将把 CTF WP 文本抽取为严格 JSON;只输出 JSON,禁止额外说明。
User-Prompt
{{#1758461559128.text#}}你是一名资深 CTF 赛事助教。现在给你一份参赛队伍的 WP(可能是 PDF、Markdown、图片 OCR 文本等)。请从中**按题目**抽取“题目名、题目类型、考点”,并以**严格的 JSON**数组输出(不要出现任何多余文本)。- 题目类型必须从此集合中二选一填写:["Misc","Crypto","Web","PWN","Reverse","理论","Blockchain"]- “考点”字段为一句简明汇总,如“花指令,动态调试,xtea 算法逆向”。- 同时把考点拆成“考点1/考点2/考点3”三个字段,按重要度排序;若不足三条,可省略末尾字段。- 若 WP 中出现多题,请逐题输出多条 JSON 对象;若无法识别题目名或类型,跳过该题。
输出格式(务必只输出 JSON):[ { "题目名": "...", "题目类型": "Misc|Crypto|Web|PWN|Reverse|理论|Blockchain", "考点": "..., ..., ...", "考点1": "...", "考点2": "...", "考点3": "..." }]
最后输出结果需要格式化,对应json-scheme
{ "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "WP考点抽取结果", "type": "object", "required": [ "items" ], "properties": { "items": { "type": "array", "items": { "type": "object", "required": [ "题目名", "题目类型", "考点" ], "additionalProperties": false, "properties": { "题目名": { "type": "string", "minLength": 1 }, "题目类型": { "type": "string", "enum": [ "Misc", "Crypto", "Web", "PWN", "Reverse", "理论", "Blockchain" ] }, "考点": { "type": "string", "minLength": 1 }, "考点1": { "type": "string" }, "考点2": { "type": "string" }, "考点3": { "type": "string" } } } } }, "additionalProperties": false}
- Code节点
对于Python代码的编写需要符合这个dify文档规范
就是输入输出变量需要在规定定义,有点像这个做IO算法题的流程,只是一个中间处理
使用Python对结果进行处理
from typing import Any, Dict, List, Optional, Unionimport json
ALLOWED = {"Misc","Crypto","Web","PWN","Reverse","理论","Blockchain"}
def _to_list(x: Union[dict, list, str, None]) -> List[Dict[str, Any]]: """把 object / list / json-string / {"items":[...]} 统统变成 list[dict]""" if x is None: return [] if isinstance(x, list): return [e for e in x if isinstance(e, dict)] if isinstance(x, dict): if "items" in x and isinstance(x["items"], list): return [e for e in x["items"] if isinstance(e, dict)] return [x] if isinstance(x, str): try: y = json.loads(x) return _to_list(y) except Exception: return [] return []
def main(arg1: Union[dict, list, str, None] = None, arg_text: Optional[str] = None) -> dict: items = _to_list(arg1) if not items and arg_text: items = _to_list(arg_text)
out: List[Dict[str, Any]] = [] for item in items: name = str(item.get("题目名","")).strip() typ = str(item.get("题目类型","")).strip() pts = str(item.get("考点","")).strip() if not name or typ not in ALLOWED or not pts: continue raw_parts = pts.replace(",", ",").split(",") parts, seen = [], set() for p in (x.strip(" ,,;;") for x in raw_parts): if p and p not in seen: seen.add(p); parts.append(p)
obj = {"题目名": name, "题目类型": typ, "考点": ", ".join(parts)} for i, p in enumerate(parts[:3], start=1): obj[f"考点{i}"] = p out.append(obj)
return {"result" :out}
- 输出节点,输出result变量
最后结果
{ "result": [ { "题目名": "SQL注入盲注", "题目类型": "Web", "考点": "布尔盲注, 字符绕过, 信息获取", "考点1": "布尔盲注", "考点2": "字符绕过", "考点3": "信息获取" } ]}
其他
模型设置
可以根据需要下载对应模型
是支持接api,直接接上就可以使用
http-request
官方文档
导出DSL
DSL(Domain Specific Language,领域专用语言)
在 Dify 里 导出 DSL 的意思是:把你在可视化画布里“拖拽出来的工作流”转成一份 流程定义文件(YAML/JSON 格式)
具体就是yaml文件,标记画布的具体内容
具体简化版如下,只展现一下具体结构
nodes: - id: start type: input variables: - name: username type: string required: true
- id: fetch_user type: http_request method: GET url: "https://api.github.com/users/{{username}}" headers: User-Agent: dify-workflow Accept: application/vnd.github+json
- id: branch type: if conditions: - case: "{{ fetch_user.status_code == 200 }}" next: summarize - case: "{{ fetch_user.status_code in [404,403] }}" next: not_found - else: error
- id: summarize type: llm prompt: | 请总结以下 JSON: {{ fetch_user.body }}
- id: not_found type: output value: "用户不存在"
- id: error type: output value: "请求失败,状态码:{{ fetch_user.status_code }}"
Debug
最关键的功能
运行测试,对于每个节点,建立后可以进行运行测试