Dify 平台使用层运维 Skill:DSL 创作/校验/双轨导入(Console API 优先,Playwright 兜底)、DSL 导出、app 检查、本地资源沉淀、workflow 草稿、模型 provider 配置与生产验收;不负责部署层。配套 CLI difyctl。
Scanned 9/1/2026
Install to Claude Code
npx -y skills add seed-forge/harness-ai-kit --skill infra-dify-ops --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Infra Dify Ops?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/seed-forge-infra-dify-ops)More formats (shields.io, HTML) on the badges page.
---
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)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!