Back to skills
SKILL.md
Infra Dify Ops
ASecurityDify 平台使用层运维 Skill:DSL 创作/校验/双轨导入(Console API 优先,Playwright 兜底)、DSL 导出、app 检查、本地资源沉淀、workflow 草稿、模型 provider 配置与生产验收;不负责部署层。配套 CLI difyctl。
- 21 stars
- 0 votes
- 0 copies
- 2 views
- Added September 1, 2026
Works with
Security analysis
100/100Pro scans all 10 files and shows the line behind each finding
npx -y skills add seed-forge/harness-ai-kit --skill infra-dify-ops --agent claude-codeAre you the author of Infra Dify Ops?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/seed-forge-infra-dify-ops)---
name: infra-dify-ops
description: Dify 平台使用层运维 Skill:DSL 创作/校验/双轨导入(Console API 优先,Playwright 兜底)、DSL 导出、app 检查、本地资源沉淀、workflow 草稿、模型 provider 配置与生产验收;不负责部署层。配套 CLI difyctl。
---
# infra-dify-ops
> 配套 CLI `difyctl`(`difyresctl` 旧包与 shim 已下线)。
## 用途
用于在 组织内部集群 场景中做 Dify 平台的使用层资源运维与 workflow DSL 创作,而不是处理部署层。它作为 AI 平台基础设施治理的一部分纳入 `infra-*`,但不接管 Dify Compose、数据库或容器生命周期。
这个 Skill 主要覆盖:
- Dify app / workflow / chatflow / agent 等资源的本地归档
- **DSL 创作**:从业务需求生成 import-ready DSL(`workflow-create` + spec v2)、本地校验(`dsl validate`)
- **双轨 DSL 导入**:优先 Console API(`dsl import --via auto`),5xx/网络错误自动降级 Playwright;不再默认依赖 RPA
- **DSL 导出**:经 Console API 拉取现网 app 的 DSL(`dsl export`)
- 导出的 DSL 文件校验、摘要、快照与 current 版本维护
- 基于 app API key 的使用层检查
- 模型 provider 配置(`openai_api_compatible` 加模型,联动 newapictl 消费)
- Dify 1.13.x workflow DSL / graph 兼容性排障与生产验收
- Studio 导入后 graph 损坏、未发布 app、输出结构漂移等使用层救援
- 资源清单 `resources.yml` 的登记与批量筛选计划
- 把后续新 workflow 及时沉淀到统一资源目录
## DSL 创作与导入(0.2.0 新增)
### 三步创作流程
1. `difyctl workflow-create intake ... --spec-out ./specs/x.yml`:从业务需求生成结构化 spec(默认 v2)
2. `difyctl workflow-create scaffold --spec ./specs/x.yml --output ./drafts/x.dify.yml`:产出 **import-ready DSL**(含 `app`/`kind: app`/`version: "0.6.0"`/`workflow.features`/完整 graph node wrapper + position + edges)
3. `difyctl dsl validate ./drafts/x.dify.yml`:本地校验(version/mode、图端点可达性、环检测、edge 引用、LLM 必填字段、变量引用语法、if-else handle)
### 双轨导入决策
- `difyctl dsl import --dsl ./drafts/x.dify.yml --via auto`(推荐):
- Console API 成功 → 返回 `app_id` + `app_url`
- Console API 返回 5xx 或网络错误 → 自动降级 Playwright 浏览器导入
- Console API 返回 4xx(配置/鉴权错误)→ 直接报错,不降级
- `--via api`:只走 Console API,失败即报错(CI / 无 GUI 环境)
- `--via browser`:只走 Playwright(现网 API 不可用时的兜底)
导入前默认自动跑一次 `dsl validate`;用 `--skip-validate` 跳过。
详细 DSL 结构、节点 schema、布局与踩坑见:
- `references/REFERENCE-DSL-AUTHORING.md`
- `references/REFERENCE-DSL-GOTCHAS.md`
## 输入
- 当前资源目标,例如 `app`、`workflow`、`chatflow`
- 本地 DSL 导出文件路径
- Dify app base URL 与 app API key(如需远端检查)
- 资源 ID、标题、标签、app_id、app_name
- 当前项目的资源工作区目录
## 输出
- 规范化的资源目录
- `resource.json` 元数据
- `dsl/current.*` 与 `dsl/snapshots/*` 快照
- 远端 app 信息或参数检查结果
- 对后续沉淀方式的明确建议
## 工作流
1. 先判断这是“远端 app 检查”还是“本地资源沉淀”。
2. 若需要远端检查:
- 优先使用 `difyctl app info`
- 必要时再用 `difyctl app parameters`
- 如需补充环境描述,可再用 `difyctl app meta`
- 若任务涉及命名、描述、发布对象或 Studio 卡片展示,先确认 live app 事实,再讨论本地 DSL 或 registry
3. 若需要沉淀资源:
- 先用 `difyctl resource init <resource_id> --mode ...`
- 再把导出的 DSL 用 `difyctl resource capture ...` 归档
- 默认维护 `dsl/current.*` 与时间戳快照
4. 若要维护项目级资源清单:
- 先用 `difyctl registry init`
- 再用 `difyctl registry upsert ...`
- 批量筛选时用 `difyctl batch plan ...`
5. 若需要从零生成一个业务需求对应的 workflow:
- 先用 `difyctl workflow-create intake ...` 或 `draft --from-demand ...`
- 再用 `difyctl workflow-create validate-spec ...`
- 然后用 `difyctl workflow-create scaffold ...` 产出 DSL 草稿
6. 若需要把本地资源同步回 Dify Studio:
- 先用 `difyctl studio create-plan ...`
- 或对已有资源用 `difyctl studio export-plan ...`
- 或用 `difyctl studio duplicate-plan ...`
7. 若准备切到真实浏览器自动化执行:
- 先用 `difyctl studio browser-doctor`
- 再用 `difyctl studio import-dsl-run --dsl ...`
- 当前阶段要预期登录页和按钮选择器仍需现场微调
8. 若只想快速理解一个 DSL:
- 先用 `difyctl dsl summarize <path>`
9. 每次新增或明显改造 workflow 后,都要判断是否应该:
- 继续补当前资源目录
- 新增一个独立资源目录
- 回填到项目级知识卡片或 SOP
10. 若导入后的 workflow 在 Dify 1.13.x 中无法运行或 Studio 无法正常打开:
- 先把问题分层为 `graph schema`、`publish state`、`runtime invoke` 三类
- 优先用黑盒 app API 调用和只读数据库检查定位,不要一上来就改库
- 确认是旧 DSL 兼容性问题后,再决定是“最小字段补丁”还是“整图原生重建”
11. 若需要做生产救援:
- 先确认 draft graph、published workflow、apps.workflow_id、api_tokens 四个对象是否齐全
- 再确认 `start`、`llm`、`end` 三类节点是否符合当前 Dify 原生 schema
- 最后用真实样例做健康路径与异常路径双验证,结果通过后再宣布可用
12. 若任务同时涉及 Git 台账、本地 DSL、Studio 页面和 live app:
- 先把四层对象分开看,不要假设它们天然一致
- 优先核对 live app,再核对 `resources.yml` / `resource.json` / `dsl/current.*`
- 若只是 Studio 资产漂移,先修正 Studio,再把本地台账回写到一致状态
13. 若需要查询/新增 Dify 模型提供商下的模型(模型台账):
- 先 `difyctl provider login --username-env ... --password-env ... --headed` 取 Console 会话,用返回的 `full_cookie_header`(含 `csrf_token`,纯 `session_cookie` 会 401)作为 `--console-key`
- 用 `difyctl provider models --provider <full-path>` 查某提供商已配置模型(台账视图),随时看清现状
- 用 `difyctl provider add-model --provider <full-path> --model <name> --endpoint-url <base>/v1 --api-key <key>` 给 openai_api_compatible 类可自定义模型的提供商加单个模型;先 `--dry-run` 预览 payload(无需 Console 会话)
- newapi 消费 key 无法经 Admin API 读取(服务端全程脱敏、忽略客户端传入 key),必须从 newapi Web UI 复制或由用户提供;获取后 `newapi/v1/chat/completions` 冒烟测试确认模型可用再注册
14. 若需要改某 workflow 的 LLM 节点模型:
- `GET /console/api/apps` 定位 app_id → `GET .../workflows/draft` 取 graph
- 改 `data.type=="llm"` 节点的 `model.provider` 与 `model.name` → `POST .../workflows/draft` 保存 → `POST .../workflows/publish` 发布
- 用 `POST .../workflows/draft/run`(SSE 流)跑一次,确认 `node_finished`/`workflow_finished` 均 `status=succeeded` 且 `error=None`
## 一致性校验与命名漂移
### 四层事实源
处理 Dify 使用层问题时,至少区分以下四层:
- live app:`/v1/info`、`/v1/parameters`、`/v1/meta` 实际返回的对象
- Studio asset:`/apps` 卡片、应用标题、描述、发布态和可见名称
- local registry:`resources.yml`、`resource.json` 中登记的 `resource_id`、`title`、`app_id`、`app_name`
- local DSL:`dsl/current.*`、`dsl/snapshots/*`、`drafts/*` 中沉淀的 graph 与元数据
不要因为其中一层已经更新,就推断其他三层也已经同步。
### 推荐校验顺序
1. 先确认当前问题属于 live app、Studio asset、local registry、local DSL 中的哪一层,或是否多层叠加。
2. 只要任务涉及 app 名称、描述、默认 workflow 绑定或“页面显示不对”,优先执行 `difyctl app info`。
3. 若 组织内部集群 本地网络、代理或浏览器自动化环境不稳定,优先以内网可达机器上的 live app 检查结果为准。
4. 确认 live app 事实后,再决定是:
- 更新本地 registry / DSL
- 更新 Studio app 信息
- 同时做两边对齐
5. 完成修改后,至少做一次双验证:
- live app 验证
- 本地资源验证
### 命名治理规则
> **命名格式的权威真源在 `fleet-platform/resources/dify/README.md`「资源命名规范」章节**(resource_id 正则 `^[a-z][a-z0-9]*(-[a-z0-9]+)*$`、文件布局、扩展名、ledger v2 schema)。本节只讲**漂移治理**,不重复定义命名格式;`difyctl` 亦在代码/`--help` 内置同一正则并硬校验非法 id。
- `resource_id` 是稳定标识,优先保持稳定,不要因为展示名称变化就频繁改动。
- `title` 和 `app_name` 可以随业务语义演进,但改名后要主动检查 Studio 与 live app 是否同步。
- `drafts/*` 与 `dsl/snapshots/*` 允许保留历史名称,但不能拿历史快照代表当前现网事实。
- 浏览器自动化如果按名称查找 app,默认要警惕 registry 名称与现网名称已经漂移。
### 处理顺序建议
遇到“代码里已经叫新名字,但 Dify 页面还是旧名字”这类问题时,默认按下面顺序处理:
1. 用 app API 先确认现网 `name` / `description` / `mode`
2. 再核对 `resources.yml`、`resource.json`、`dsl/current.*`
3. 若只是 Studio / live app 漂移,先修正 Studio
4. 再把本地 registry 和 README 等元数据回写到最终一致状态
5. 最后保留一次可追溯验证结果,避免下次会话重新猜
## Dify 1.13.x 修复策略
### 故障分层
- `graph schema`:Studio 打不开、运行前即报 `validation errors`,通常是旧 DSL 与当前 `start` / `llm` / `end` 节点 schema 不兼容。
- `publish state`:app 存在但不可调用,常见特征是 `apps.workflow_id` 为空、未指向 published workflow,或只有 draft 没有 published 版本。
- `runtime invoke`:API 可进业务层但 blocking 运行异常、超时、输出漂移,通常需要看 workflow runs 和 API logs。
### 推荐排查顺序
1. 先做黑盒调用,确认错误是在鉴权、参数校验、graph 校验还是节点执行阶段。
2. 再检查 app 发布态是否完整:
- `apps.workflow_id` 应指向 published workflow,而不是 draft
- 同一个 app 通常至少有一条 `draft` workflow 和一条带时间戳版本号的 published workflow
- 若需要使用 service API,还要确认存在 `api_tokens.type = app`
3. 再检查 graph 结构是否是当前 Dify 原生格式:
- `start.variables` 应是 `variable` / `label` / `type: text-input` 这类 `VariableEntity`
- `llm` 节点至少要有 `model`、`context`、`vision`
- `end.outputs` 应是 `variable` + `value_selector` + `value_type`
4. 只有在 Studio 已不可用、且黑盒与只读检查都证明是元数据损坏时,才进入 break-glass 数据层修复。
### Break-Glass 原则
- 这类修复属于使用层资源救援,不是常规路径,必须有明确授权。
- 优先做“可回滚的最小改动”,例如先补齐缺失字段,再验证是否足够。
- 若旧 graph 与当前 Dify 版本差异过大,不要持续缝补,直接重建成当前原生 graph。
- 不在总结里扩散完整 API token、数据库密码等凭证;验证时只汇报是否成功与关键输出形状。
### 生产可用判定
- `published workflow` 已存在,且 `apps.workflow_id` 已切到该 published 版本。
- app API 黑盒调用成功,不再停留在 schema 校验或参数缺失报错。
- 至少完成两类验证:
- 健康样例:应返回稳定的 `title`、`markdown_body`、`plain_text_body`
- 异常样例:正文必须贴合输入事实,不能复用历史样例或编造新数字
- 最新 `workflow_runs` 记录应能落库并收敛到 `succeeded`,必要时辅以 API logs 核对。
## Dify 1.13.x Graph 约定
### 优先选择
- 对监控日报、通知封装、结构化摘要这类场景,优先用 `start -> 单个 llm(structured_output) -> end`。
- 不要默认串多个自由文本 LLM 节点再靠 end 兜底拆字段,这样更容易产生幻觉和字段漂移。
### 原生字段约定
- `start.variables` 使用当前 `VariableEntity` 结构,例如 `variable`、`label`、`type: text-input`。
- `llm` 节点在 Dify 1.13.x 下应显式带上:
- `model`
- `context`
- `vision`
- `retry_config`(`difyctl` scaffold 默认注入 3 次重试)
- `error_strategy`(`difyctl` scaffold 默认注入 `default_value` fallback)
- 需要结构化结果时加 `structured_output_enabled` 与 `structured_output.schema`
- 新建 workflow 的默认 LLM 模型使用 `langgenius/deepseek/deepseek` / `deepseek-v4-flash`,`mode: chat`,`temperature: 0.1`。
- `end.outputs` 直接映射到上游 `structured_output` 字段,例如:
- `["llm_node", "structured_output", "title"]`
- `["llm_node", "structured_output", "markdown_body"]`
- `["llm_node", "structured_output", "plain_text_body"]`
### 提示词约束
- 明确写出“只能依据 snapshot_json 中已有事实,不允许猜测、补造、外推”。
- 明确要求空区块输出“未观测到异常”或“无数据”。
- 对正文结构写死章节名,而不是依赖模型自行发挥。
- 若需要多语言,只让 `language` 影响表述语言,不影响事实本身。
### LLM 节点容错默认行为(0.4.0 新增)
`difyctl workflow-create scaffold` 默认为每个 LLM 节点注入两层容错:
1. **`retry_config`**:`{retry_enabled: true, max_retries: 3, retry_interval: 1000}`
- 触发条件:模型返回 429 / 5xx / 超时
- 与 newapi 侧的 proxy 重试 + 渠道 fallback 互补:newapi 处理上游不可用,Dify 侧处理模型响应本身的异常
- 传 `retry_config=None` 可禁用(仅限调试)
2. **`error_strategy`**:`{type: default_value, default_value: ""}`
- 触发条件:重试全部耗尽后
- 效果:输出空值让下游节点继续执行,而非整个 workflow 崩溃
- 对于 `structured_output` 节点,`default_value` 应是一个含全部必填字段的空对象,例如 `{"title":"","markdown_body":"","plain_text_body":""}`
- 传 `error_strategy=None` 可禁用(不推荐用于生产)
`difyctl dsl validate` 会对缺失这两项的 LLM 节点发出 warning,提示手动补齐。
手写或从 Dify 导出的 DSL 如果缺少 `retry_config` / `error_strategy`,建议用 `difyctl dsl lint` 检查后再导入。
## App 服务密钥 / 调用 / 运维(0.7.0 新增)
创建工作流后「取 key → 调用」现已在 CLI 内闭环,覆盖各类应用:
```bash
# 1) 取 app 的 service API key(Console API):完整 token 仅打印一次 + 存 config.yaml assets.difyctl.app_keys
difyctl app keys create --app-id <app_id>
difyctl app keys list --app-id <app_id> # 只显示掩码前缀
difyctl app keys delete --app-id <app_id> --key-id <kid>
# 2) 调用应用(mode 自动从 ledger/--mode 解析,覆盖全类型)
difyctl app run --app-id <app_id> --inputs '{"text":"hi"}' # workflow -> /v1/workflows/run (blocking)
difyctl app run --app-id <app_id> --query "你好" # chat/advanced-chat/chatflow -> /v1/chat-messages (blocking)
difyctl app run --app-id <app_id> --mode agent-chat --query "你好" # agent-chat -> /v1/chat-messages (streaming,Dify 拒绝 blocking,自动改流式聚合)
difyctl app run --app-id <app_id> --mode completion --query "写首诗" # completion -> /v1/completion-messages
# 3) 导入即可用(opt-in 闭环)
difyctl dsl import --dsl draft.dify.yml --via api --resource-id my-wf --create-key --smoke --smoke-inputs '{"text":"hi"}'
# 导入默认带重名去重守卫:现网已存在同名 app 时 block(列出 existing app_id);确需重复用 --allow-duplicate。
# 排查现网重复:difyctl registry audit 的 duplicate_names 段列出同名 app 分组。
# 3b) 渐进式:原地更新已有 app(不产生副本)
difyctl dsl export --app-id <app_id> --output cur.dify.yml # 拉当前线上 DSL
difyctl dsl diff --app-id <app_id> --dsl new.dify.yml # 推送前预览差异
difyctl dsl import --dsl new.dify.yml --via api --app-id <app_id> # 原地更新(同 app_id,无副本)
difyctl dsl import --dsl new.dify.yml --via api --update-if-exists # 或按同名自动定位单个 app 更新
difyctl app publish --app-id <app_id> # workflow/advanced-chat 更新后需 publish 才能 /v1 run
difyctl dsl lint --dsl new.dify.yml # 推送前静态检查:硬编码密钥/悬空变量/空模型
difyctl dsl retarget --dsl new.dify.yml --provider <p> --model <m> # 批量改绑模型
difyctl dsl apply --dir ./dify-apps --dry-run # 声明式:目录 DSL 幂等同步(新建/原地更新/歧义跳过)
difyctl dsl apply --dir ./dify-apps --publish # 执行同步并发布
# 3c) 长期治理:台账回填 + 重复清理
difyctl registry sync --dry-run # 预览把未登记的线上 app 回填进台账(分页全量)
difyctl registry sync # 执行回填
difyctl registry prune-duplicates # dry-run:列出同名冗余 app 的删除计划(保留最新)
difyctl registry prune-duplicates --apply # 执行删除(破坏性,保留 --keep newest/oldest 之一)
# 3d) 知识库(RAG 数据集)+ 全量备份
difyctl dataset list # 列出所有知识库(分页全量)
difyctl dataset list --name 营销 # 按名称子串过滤
difyctl dataset documents --dataset-id <id> # 某知识库的文档列表
difyctl dataset create --name "KB" --indexing-technique high_quality # 建库(high_quality 需 embedding 模型)
difyctl dataset add-doc --dataset-id <id> --name doc --text "..." # 加文本文档(/v1 + 自动取 dataset key)
difyctl dataset add-doc --dataset-id <id> --name doc --file ./a.md # 或从文件读入
difyctl dataset delete --dataset-id <id> --yes # 删库(破坏性,需 --yes)
difyctl app annotations --app-id <id> # 列出 app 标注(问答缓存)
difyctl dsl export --all --output-dir ./dify-backup # 全量备份每个 app 的 DSL(灾备)
# 输出约定:stdout 恒为纯 JSON(可直接管道消费);人类可读的错误/告警走 stderr。全局 --quiet 静默 stderr、--verbose 追踪、-q 简写。
# 4) 运维车队
difyctl app rename --app-id <app_id> --name "New Name" # Console PUT + 同步 ledger
difyctl app delete --app-id <app_id> # Console 删除 + ledger status→deprecated
difyctl registry audit # live apps vs ledger:僵尸/未登记/漂移
# 5) /v1 运行时全覆盖(0.8.0,均用 app-* service token)
difyctl app run --app-id <id> --mode agent-chat --query "你好" --stream # 强制流式聚合
difyctl app run-status --app-id <id> --run-id <workflow_run_id> # 运行明细/状态
difyctl app stop --app-id <id> --task-id <task_id> --user <u> # 中止流式任务
difyctl app upload --app-id <id> --file ./a.png --user <u> # 文件/图片输入
difyctl app logs --app-id <id> --limit 20 # 运行日志/审计
difyctl app conversations --app-id <id> --user <u> # 会话列表
difyctl app messages --app-id <id> --conversation-id <cid> --user <u> # 会话消息
difyctl app conversation-rename --app-id <id> --conversation-id <cid> --name "X" --user <u>
difyctl app conversation-delete --app-id <id> --conversation-id <cid> --user <u>
difyctl app feedback --app-id <id> --message-id <mid> --rating like --user <u> # 点赞/踩
difyctl app suggested --app-id <id> --message-id <mid> --user <u> # 追问建议
difyctl app audio-to-text --app-id <id> --file ./a.wav --user <u> # 语音转文字
difyctl app text-to-audio --app-id <id> --text "你好" --output ./o.mp3 --user <u> # 文字转语音
```
**安全红线**:完整 `app-*` token 只存本地 `~/.harness-ai-kit/config.yaml`(app_keys 映射,禁入 Git);`app keys create` 完整 token 仅一次性打印,其余输出与 ledger 一律掩码(prefix+`****`)。key 解析优先级:`--app-key` > config.yaml app_keys[app_id] > app_api_key。
## 配置上下文
本技能依赖以下配置,AI 在运行时按如下优先级解析:
1. 用户对话中明确提供的值(最高优先级)
2. `~/.harness-ai-kit/config.yaml` 中 `assets.difyctl`(配套 CLI 配置段)或 `global` 段
3. `config.defaults.yaml` 中的默认值
如用户未提供且无默认值的 required 字段,**必须主动询问用户**。
禁止从 AGENTS.md 或脚本中读取硬编码配置值。
## 推荐输出格式
执行完毕后按以下结构输出:
**状态**:✅ / ⚠️ / ❌
| 资源ID | 类型(app/workflow/chatflow) | 动作(init/capture/reconcile/validate) | DSL 快照 / live 校验结果 |
|--------|----------------------------|--------------------------------------|------------------------|
(漂移排查场景补充四层事实源对照:live app / Studio asset / local registry / local DSL 的一致性结论;生产救援场景注明 published workflow、apps.workflow_id、健康/异常双验证是否通过。)
## 约束
- 这个 Skill 不负责 Docker、Compose、Nginx、容器启停与 Dify 升级。
- 优先把 Dify 资源沉淀为项目内可跟踪文件,而不是只留在 Dify UI 中。
- 公开稳定的使用层入口先以 app API key 与导出 DSL 为主,不默认依赖不稳定的控制台私有接口。
- 数据库直修只作为 break-glass 救援手段,不作为常规导入、发布或编辑路径。
- 当前已对齐中文 Dify Studio 登录页与 `/apps` -> `创建空白应用` / `导入 DSL 文件` / `更多 -> 导出 DSL / 复制 / 编辑信息` 流程。
- 新资源命名应稳定、可检索、可长期复用,不要使用一次性临时名称。
- 不要把本地 `drafts/*`、历史 snapshot 名称或 `.env` 中的展示名,直接当成现网 app 事实;涉及展示层问题时必须再做 live app 校验。
## 命令约定
初始化本地配置:
```bash
difyctl config init --base-url https://dify.example.com --workspace-dir ./fleet-platform/resources/dify
```
推荐最小环境变量:
```bash
DIFY_BASE_URL=https://dify.example.com
DIFY_STUDIO_USERNAME=you@example.com
DIFY_STUDIO_PASSWORD=your-password
```
远端检查解析顺序:
1. 显式命令参数 `--base-url` / `--app-api-key`
2. `difyctl` 已保存配置
3. 环境变量 `DIFY_BASE_URL`
4. `workspace_dir` 附近 `.env` 中的 `DIFY_BASE_URL`、`DIFY_API_KEY` 或 `DIFY_APP_API_KEY`
若 `DIFY_BASE_URL` 以 `/v1` 结尾,CLI 会自动收口到站点根地址。
检查 readiness:
```bash
difyctl doctor
```
远端 app 检查:
```bash
difyctl app info
difyctl app parameters
difyctl app meta
```
命名或描述漂移校验:
```bash
difyctl app info
difyctl reconcile diff-only sales-intake
difyctl reconcile show sales-intake
difyctl registry show sales-intake
difyctl resource show sales-intake
```
本地资源沉淀:
```bash
difyctl resource init my-workflow --mode workflow --title "My Workflow" --dsl ./exports/my-workflow.yml
difyctl resource capture my-workflow --dsl ./exports/my-workflow-v2.yml --label exported-2026-05-11
difyctl resource list
difyctl resource show my-workflow
```
DSL 快速摘要:
```bash
difyctl dsl summarize ./exports/my-workflow.yml
```
资源清单与批量计划:
```bash
difyctl registry init
difyctl registry upsert sales-intake --mode workflow --title "Sales Intake" --tag sales --dsl-path resources/sales-intake/dsl/current.yml
difyctl batch plan --mode workflow --tag sales
```
从零生成 workflow 草稿:
```bash
difyctl workflow-create intake --name "Sales Intake" --mode workflow --goal "Triage inbound leads" --input lead_text --output final_reply --step "Classify intent" --step "Generate reply" --spec-out ./specs/sales-intake.yml
difyctl workflow-create validate-spec --spec ./specs/sales-intake.yml
difyctl workflow-create scaffold --spec ./specs/sales-intake.yml --output ./drafts/sales-intake.dify.yml
```
Studio 操作计划:
```bash
difyctl studio create-plan --name "Sales Intake" --mode workflow --dsl ./drafts/sales-intake.dify.yml
difyctl studio export-plan sales-intake
difyctl studio duplicate-plan sales-intake
```
浏览器自动化入口:
```bash
difyctl studio browser-doctor
difyctl studio import-dsl-run --dsl ./drafts/sales-intake.dify.yml --headed
```
生产验收建议:
```bash
curl -X POST "$DIFY_BASE_URL/v1/workflows/run" \
-H "Authorization: Bearer $DIFY_APP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"inputs": {
"snapshot_json": "{\"summary\":{\"overall_status\":\"healthy\"},\"hosts\":[],\"containers\":[],\"entrypoints\":[],\"alerts\":[],\"links\":[]}",
"period_label": "2026-05-12",
"audience": "ops-oncall",
"language": "zh-CN"
},
"response_mode": "blocking",
"user": "smoke-test"
}'
```
## 示例
示例 1:刚从 Dify 导出一个 workflow DSL,想立即沉淀
1. `difyctl resource init sales-intake --mode workflow --title "Sales Intake" --dsl ./exports/sales-intake.yml`
2. 确认 `resource.json`、`dsl/current.yml` 与首个 snapshot 已生成
3. 后续版本继续用 `capture` 累积快照
示例 2:想先看当前 app 的运行参数
1. 配置 `base_url` 与 `app_api_key`
2. 运行 `difyctl app parameters`
3. 把结果作为后续资源整理和比对依据
示例 3:从业务需求直接起一个 workflow 草稿
1. 用 `workflow-create intake` 生成结构化 spec
2. 用 `validate-spec` 校验 spec
3. 用 `scaffold` 生成 starter DSL
4. 再进入 `resource init / capture` 与后续 Studio 导入流程
示例 4:对已登记资源生成 Dify Studio 操作计划
1. 先确认该资源已存在于 `resources.yml`
2. 运行 `difyctl studio export-plan sales-intake`
3. 按计划把导出的 DSL 回填到本地资源目录
示例 5:导入后的 workflow 在 Dify 1.13.x 中无法运行
1. 先用 app API 做黑盒调用,记录它卡在鉴权、参数校验、graph 校验还是节点执行
2. 对照一个正常 app,检查 draft / published workflow、`apps.workflow_id` 和 app token 是否齐全
3. 若报 `VariableEntity`、`EndNodeData` 或 `LLMNodeData` 校验错误,优先按当前原生 schema 重建 graph
4. 修复后至少跑一条健康样例和一条异常样例,再确认 `workflow_runs` 已 `succeeded`
示例 6:本地 registry 已改名,但 Studio 页面仍显示旧名称
1. 先运行 `difyctl app info`,确认 live app 当前 `name` 和 `description`
2. 再核对 `resources.yml`、`resource.json` 和 `dsl/current.*` 是否已经切到新语义
3. 若确认只是 Studio asset 漂移,再执行 `studio edit-info-run`
4. 修改完成后重新做一次 live app 校验,不要只看 Studio 页面标题
示例 7:新开会话时想先快速确认是否存在漂移
1. 先运行 `difyctl reconcile diff-only sales-intake`
2. 若只有 live app 网络错误,优先切到内网可达环境复核
3. 若存在 `observations`,再运行 `difyctl reconcile show sales-intake` 看完整上下文
## 给 LLM 应用加"联网搜索/工具"能力(Dify 1.x 插件版)
**关键事实**:Dify 的 **LLM 节点本身没有内置"联网搜索"开关**。联网搜索通过**工具(1.x 里是插件)**提供,两种接法:
- **Agent 应用 / Agent 节点**:把搜索工具挂到 Agent,由模型自主决定何时搜(依赖模型 function-calling 能力,不够确定)。
- **Chatflow/Workflow 的 Tool 节点**(推荐,确定性):显式放一个搜索 Tool 节点在 LLM 节点前,把检索结果通过 `context` 拼进 LLM 上下文。不依赖模型 tool-calling,最稳。
**可用 web-search 工具(本实例,`GET /console/api/workspaces/current/tools/builtin` 探测)**:
| 工具 | provider(1.x 全名)| tool_name | 授权/密钥 |
|------|--------------------|-----------|-----------|
| Wikipedia | `langgenius/wikipedia/wikipedia` | `wikipedia_search`(query+language 必填)| 已授权·免密钥·**稳定** |
| DuckDuckGo | `langgenius/duckduckgo/duckduckgo` | `ddgo_search`(query 必填,max_results 必填)| 已授权·免密钥·**免费端点常被限流(202 Ratelimit)** |
| Tavily | `langgenius/tavily/tavily` | tavily search | 已授权·**需 key**·生产推荐 |
| Google/Bing/SearchApi/Brave/Jina | 对应插件 | — | **需各自 API key** |
**工具节点 DSL 关键 schema(1.x,workflow.graph.nodes[].data)**:
```yaml
type: tool
provider_id: langgenius/wikipedia/wikipedia # 与 provider_name 一致,用插件全名
provider_name: langgenius/wikipedia/wikipedia
provider_type: builtin
tool_name: wikipedia_search
tool_configurations: {} # provider 级配置(proxy 等)
tool_parameters:
query: {type: mixed, value: "{{#<start_node_id>.query#}}"} # 变量引用
language: {type: constant, value: zh} # 常量
retry_config: {max_retries: 2, retry_enabled: true, retry_interval: 1000}
```
LLM 节点消费工具输出:`context.enabled: true` + `context.variable_selector: [<tool_node_id>, "text"]`,prompt 里引用 `{{#<tool_node_id>.text#}}`。节点外壳统一 `type: custom`,真实类型在 `data.type`。
**CLI 一键运维闭环(已真机验证 GREEN)**:
```bash
# 1) 作者一个 start -> tool(wikipedia_search) -> llm(ollama) -> end 的 workflow DSL(见上 schema)
# 2) 导入(带重名去重) + 建 key
difyctl dsl import --dsl websearch.dify.yml --via api --resource-id web-search-pilot-wiki --create-key
# 3) 发布(workflow 必须 publish 才能 /v1 run)
difyctl app publish --app-id <app_id>
# 4) 试点跑通:联网检索 -> LLM 汇总
difyctl app run --app-id <app_id> --mode workflow --inputs @q.json # q.json: {"query":"..."}
# 排错:run_status=failed 时看 error(如 DuckDuckGo 202 Ratelimit 即免费端点限流,非配置错误)
```
**踩坑与选型**:DuckDuckGo 免费端点在服务器 IP 上常返回 `202 Ratelimit`(节点结构正确、确实发起了真实搜索,但被限流);生产联网搜索优先 **Tavily / SearchApi(配 key)** 或给 DDG 配 `proxy_server`。零成本试点/演示用 **Wikipedia** 最稳(`wikipedia_search`,免密钥、无限流)。
### Tool 节点参数放置(关键坑,已真机踩过 3 次)
- **运行时输入**(`form: llm`,如 `query`)→ 放 `tool_parameters`,**必须包裹** `{type: mixed|variable|constant, value: ...}`。
- **静态配置项**(`form: form`,如 `max_results`/`search_depth`/`include_answer`)→ 放 `tool_configurations`,**用原始值、不要包裹**(写成 `{max_results: 5, include_answer: "advanced"}`;写成 `{type:constant,value:...}` 会报 `value {...} not in options`)。
- **`select` 型但默认值是布尔**的配置项(Tavily 的 `include_answer`/`include_raw_content`)**必须显式赋一个合法字符串选项**(如 `include_answer: "advanced"`、`include_raw_content: "false"`),否则默认 `False` 触发 `value False not in options`。
### 本实例 web-search 现状(探测结论,随时可复查)
- **Tavily 已授权且已配 key**(`tavily_api_key: <set>`),5 个工具 `tavily_search/tavily_extract/tavily_crawl/tavily_map/tavily_research` → **search+read 开箱即用**,已真机 GREEN(`tavily_search`→LLM 返回带来源 URL 的实时答案)。生产联网搜索首选它。
- **SearXNG 插件未安装**(无 provider):自托管要先装 marketplace 插件 + 部署 SearXNG 容器 + 配 base_url + 上游引擎国际出口代理。
- **Firecrawl 插件在册但未配**(`base_url`/`api_key` 空):需云端 key 或自托管 `base_url`;旧 app 里 `provider_id: firecrawl` 短名是遗留写法,新建用插件全名 `langgenius/firecrawl/firecrawl`。
### 自托管 SearXNG + Firecrawl 接入(已真机 GREEN)
**CLI/API 授权 tool provider**(difyctl 暂无此命令,走 Console API):
```
POST /console/api/workspaces/current/tool-provider/builtin/<provider>/add
body: {"credentials": {...}, "type": "api-key", "name": "self-hosted"}
```
- **坑**:该接口常返回 400 `Cannot release a lock that's no longer owned`,但**凭据其实已保存**——以 `GET tools/builtin` 的 `is_team_authorization=True` 为准,**别盲目重试**(会建重复凭据)。
**SearXNG**(`langgenius/searxng/searxng`,cred 字段 `searxng_base_url`,免密钥):
- 搜索工具 `searxng_search`:`query`(llm) + `search_type`(form,**必填**,options: general/images/videos/news/map/music/it/science/files/social_media) + `time_range`(form,可选)。
- **关键坑**:SearXNG 节点结果只在 **`json`**(array[object],含 title/url/content)里,**`text` 为空**!prompt 必须引用 `{{#节点.json#}}`(非 `text`),context 关掉或指 json。真机 GREEN:MCP 查询→9501 tokens、带来源 URL。
- 直连自检:`GET http://<searxng>/search?q=...&format=json` 返回 `results[]` + `unresponsive_engines`(本实例 google cse+duckduckgo 正常、brave 挂)。
**Firecrawl**(`langgenius/firecrawl/firecrawl`,creds `base_url`+`firecrawl_api_key`,自托管 key 任意非空):
- `scrape`:`url`(llm) + `formats`/`onlyMainContent`/`timeout`(form)。输出走 `text`(与 SearXNG 不同)。真机 GREEN:抓 runoob 页→摘要。
### 联网搜索 workflow spec 模板库(一键脚手架)
技能自带 3 个可直接脚手架的 spec(`references/specs/`),`difyctl workflow-create scaffold --spec <spec> --output x.dify.yml` 一键生成 DSL:
| spec | 拓扑 | 特点 | 适用 |
|------|------|------|------|
| `web-search-searxng.spec.yml` | `start→tool(SearXNG)→llm→end` | 自托管、快、无需 sandbox(json 含 content 摘要)| 多数联网问答 |
| `web-search-tavily.spec.yml` | `start→tool(Tavily)→llm→end` | 托管兜底、结果已 LLM 清洗、带来源 | 快速接入/无自托管时 |
| `deep-research-websearch.spec.yml` | `start→tool(SearXNG)→template(取URL)→tool(Firecrawl)→llm→end` | 两段式、读全文、最扎实(50-73s)| 深研/需全文落地引用 |
均真机验证 scaffold→import→run 全绿。导入+运行通用流程:
```bash
difyctl workflow-create scaffold --spec <skill>/references/specs/<spec>.spec.yml --output x.dify.yml
difyctl dsl import --dsl x.dify.yml --via api --resource-id <rid> --create-key
difyctl app publish --app-id <id>
difyctl app run --app-id <id> --mode workflow --inputs '{"query":"..."}'
```
注意各自的输出变量与参数坑(SearXNG 在 `json` 非 `text`;Tavily `include_answer` 等 select-with-bool-default 必须显式赋合法字符串;工具节点静态配置进 `tool_configurations` 原始值、运行时输入进 `tool_parameters` 包裹)。
### 两段式深研(SearXNG搜索→取URL→Firecrawl读→LLM)—— 已真机 GREEN
- **一键脚手架(推荐)**:技能自带 spec `references/specs/deep-research-websearch.spec.yml`,直接生成两段式 DSL:
```bash
difyctl workflow-create scaffold --spec <skill>/references/specs/deep-research-websearch.spec.yml --output dr.dify.yml
difyctl dsl validate dr.dify.yml
difyctl dsl import --dsl dr.dify.yml --via api --resource-id deep-research-websearch --create-key
difyctl app publish --app-id <id>
difyctl app run --app-id <id> --mode workflow --inputs '{"query":"..."}'
```
`workflow-create scaffold`(difyctl 0.13.0+)支持 `tool` step 与跨节点引用重映射:spec 里用稳定 step-id(`start`/`searxng`/`pick_url`/…)互引 `{{#id.field#}}` 与 `value_selector`,scaffold 自动改写为生成的 node-id。
- 拓扑:`start → searxng_search → 取首条url(template-transform) → firecrawl scrape → llm → end`。取 URL 也可用 **Code 节点**(`def main(arg1)->{"url":...}`)。
- **依赖 dify-sandbox**:Code 与 Template-Transform 节点都在 `dify-sandbox` 里执行;曾遇 `-500 fork/exec /usr/local/bin/python3`(sandbox 缺 python3,拖垮所有含 Code/Template 的 workflow),修复 sandbox 后恢复。
- **超时**:全链 50-65s;difyctl 默认超时已调到 **120s**(0.12.2+),或 `app run --timeout` 单次覆盖。真机 GREEN:56-73s、带来源 URL。
- 规避:单段 `SearXNG→LLM`(json 已含 content 摘要)无需 sandbox、更快,适合多数问答;两段式用于需要读全文的深研。
## 参考文档
- [REFERENCE-DSL-AUTHORING.md](references/REFERENCE-DSL-AUTHORING.md)
- [REFERENCE-DSL-GOTCHAS.md](references/REFERENCE-DSL-GOTCHAS.md)
Files in this skill
- CHANGELOG.md
- SKILL.md
- USAGE.md
- config.defaults.yaml
- references/REFERENCE-DSL-AUTHORING.md
- references/REFERENCE-DSL-GOTCHAS.md
- references/specs/deep-research-websearch.spec.yml
- references/specs/web-search-searxng.spec.yml
- references/specs/web-search-tavily.spec.yml
- skill.json
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.