首页 / AI工具 / 用 Dify 搭建企业级 AI 工作流:从需求拆解到自动化部...

用 Dify 搭建企业级 AI 工作流:从需求拆解到自动化部署全流程实战

用 Dify 搭建企业级 AI 工作流:从需求拆解到自动化部署全流程实战

引言:企业 AI 应用落地,为什么总是"卡在最后一公里"

很多团队都经历过这样的场景:技术选型时把大模型能力夸上了天,POC 阶段效果惊艳,可一旦要真正上线到生产环境,问题接踵而至——

  1. 单次对话能力 ≠ 业务流程能力:大模型擅长回答"一个问题",但真实业务往往是"多步骤、有条件分支、需要调用外部系统"的链路。比如客服场景,需要先识别意图、再检索知识库、判断是否需要人工介入,最后组织回复。
  2. 提示词工程难以维护:随着业务规则增加,Prompt 越写越长,逻辑全塞在一段文本里,无法测试、无法版本管理、无法复用。
  3. 集成成本高:把 LLM 嵌入现有系统,要处理鉴权、流式输出、超时重试、变量传递、结果格式校验……每一个都是坑。
  4. 非技术人员难以参与:业务方想调整流程,却只能提需求给开发,迭代周期长。

Dify 正是为解决这些问题而生。它把"用大模型做应用"这件事从"写一堆胶水代码"变成了"可视化编排 + 标准化 API"。本文将从部署开始,带你完整走通一条企业级 AI 工作流。

一、认识 Dify:开源 LLM 应用开发平台

Dify 是一个开源的 LLM 应用开发平台(Apache 2.0 协议),核心能力包括:

Dify 区分两种应用类型:Chatflow(对话型,支持多轮)和 Workflow(工作流型,单次输入单次输出,适合自动化任务)。本文聚焦 Workflow 类型,因为它更适合"工单处理""文档审核""数据抽取"这类一次性、可编排的自动化场景。

二、Dify 工作流核心概念

2.1 节点类型

工作流由若干节点串联而成,常用节点包括:

节点 作用
Start 入口节点,定义工作流的输入变量
LLM 调用大模型,支持系统提示词、上下文、变量插值
Knowledge Retrieval 从知识库检索相关片段,结果注入下游 LLM
Question Classifier 意图分类,把输入路由到不同分支
IF/ELSE 条件分支,根据变量值判断走向
Code 执行 Python/JS 代码,做数据转换
Template Jinja2 模板渲染,拼接文本
HTTP Request 调用外部 API,打通企业内部系统
Variable Aggregator 多分支结果汇聚
End 输出节点,定义返回给调用方的结构

2.2 变量传递

Dify 用"变量"在节点间传递数据。每个节点的输出会自动注册为变量,下游节点通过 {{node_id.output_var}} 引用。例如 LLM 节点输出文本存在 {{llm_1.text}},可在 Template 节点中拼接、在 IF/ELSE 中判断、在 HTTP Request 中作为请求体。

输入变量类型支持:String、Number、Boolean、Object、Array[String]、Array[Object] 等,类型在 Start 节点声明,保证流程类型安全。

2.3 条件分支

条件分支是工作流"智能化"的关键。两种典型用法:

二者常配合使用:先用 Classifier 做粗分类,再用 IF/ELSE 做精细路由。

三、Docker 部署 Dify:5 分钟拉起本地环境

Dify 提供完整的 Docker Compose 部署方案。

# 1. 克隆仓库
git clone https://github.com/langgenius/dify.git
cd dify/docker

# 2. 复制环境变量模板
cp .env.example .env

# 3.(可选)修改 .env,如关闭匿名遥测、调整端口
# EXPOSE_NGINX_PORT=8080
# CONSOLE_API_URL=http://localhost:8080

# 4. 启动全部服务(包括 api、worker、web、db、redis、weaviate 等)
docker compose up -d

# 5. 查看服务状态,确认均为 healthy / running
docker compose ps

启动完成后,浏览器访问 http://localhost:8080 即可进入 Dify 控制台,首次访问需注册管理员账号。

提示:生产环境建议配置独立的 PostgreSQL 与 Redis、对象存储(S3)、以及外置向量数据库(如 Weaviate / Milvus / Qdrant),并通过 .env 调整相应参数。模型供应商密钥可在控制台"设置 → 模型供应商"中统一配置。

四、创建你的第一个工作流

  1. 登录控制台,点击"创建空白应用" → 选择"工作流应用" → 命名为"工单分类"。
  2. 在画布上从左到右依次拖入节点:Start → LLM → End。
  3. Start 节点 添加输入变量 ticket_content(String / 段落输入类型)。
  4. LLM 节点 配置:
  5. 模型:选择已接入的模型(如 gpt-4o-mini
  6. 系统提示词:你是一个客服工单分类助手,请把用户问题归类为:退款、物流、售后、其他。只输出类别名称。
  7. 上下文变量:插入 {{#start.ticket_content#}}
  8. End 节点 把输出变量指向 {{#llm.text#}}
  9. 右上角点击"运行"测试,输入"我的包裹三天了还没到",应返回"物流"。
  10. 点击"发布",工作流即生成可调用的 API。

发布后,在"访问 API"页面可拿到 API Server 地址和 API Key(形如 app-xxxxxxxx)。

五、实战案例:智能客服工单分类与自动回复工作流

下面这个案例综合了知识库检索、条件分支、HTTP 调用,是一个真实可用的企业级流程。

5.1 流程设计

Start(ticket_content)
   └─> Knowledge Retrieval(检索 FAQ 知识库)
          └─> LLM(生成回复 + 置信度评估, 输出结构化 JSON)
                 └─> IF/ELSE(置信度是否 >= 0.7)
                        ├─ YES ─> End(自动回复)
                        └─ NO  ─> HTTP Request(创建人工工单) ─> End(转人工提示)

5.2 工作流 DSL 配置(JSON)

Dify 支持导出/导入 DSL,下面是该流程的精简版配置(实际导入时字段更全,这里展示核心结构,可直接作为理解与二次编辑的蓝本):

{
  "app": {
    "name": "智能客服工单处理",
    "mode": "workflow",
    "description": "自动分类工单,命中知识库则自动回复,否则转人工"
  },
  "workflow": {
    "graph": {
      "nodes": [
        {
          "id": "start",
          "type": "custom",
          "data": {
            "type": "start",
            "variables": [
              { "variable": "ticket_content", "type": "text-input", "label": "工单内容" }
            ]
          }
        },
        {
          "id": "knowledge_1",
          "type": "custom",
          "data": {
            "type": "knowledge-retrieval",
            "dataset_ids": ["<your-dataset-id>"],
            "query_variable_selector": ["start", "ticket_content"],
            "retrieval_mode": "multiple",
            "multiple_retrieval_config": {
              "top_k": 3,
              "score_threshold": 0.5,
              "reranking_enable": false
            }
          }
        },
        {
          "id": "llm_1",
          "type": "custom",
          "data": {
            "type": "llm",
            "model": { "provider": "openai", "name": "gpt-4o-mini", "mode": "chat", "completion_params": { "temperature": 0.2 } },
            "prompt_template": [
              {
                "role": "system",
                "text": "你是客服助手。根据检索到的 FAQ 回答用户问题,并严格输出 JSON。\n格式:{\"reply\": \"回复内容\", \"confidence\": 0.0~1.0}\n若 FAQ 足以回答,confidence >= 0.7;若 FAQ 不足,confidence 设为 0。\n\nFAQ:{{#knowledge_1.result#}}\n用户问题:{{#start.ticket_content#}}"
              }
            ],
            "structured_output_enabled": true,
            "structured_output": {
              "type": "object",
              "properties": {
                "reply": { "type": "string" },
                "confidence": { "type": "number" }
              },
              "required": ["reply", "confidence"]
            }
          }
        },
        {
          "id": "if_else_1",
          "type": "custom",
          "data": {
            "type": "if-else",
            "cases": [
              {
                "logical_operator": "and",
                "conditions": [
                  {
                    "variable_selector": ["llm_1", "structured_output", "confidence"],
                    "comparison_operator": ">=",
                    "value": "0.7"
                  }
                ]
              }
            ]
          }
        },
        {
          "id": "http_1",
          "type": "custom",
          "data": {
            "type": "http-request",
            "method": "post",
            "url": "https://your-internal-api.example.com/tickets",
            "headers": "{\n  \"Content-Type\": \"application/json\",\n  \"Authorization\": \"Bearer {{internal_token}}\"\n}",
            "body": "{\n  \"content\": \"{{#start.ticket_content#}}\",\n  \"reason\": \"low_confidence_auto_routed\"\n}"
          }
        },
        {
          "id": "end_auto",
          "type": "custom",
          "data": {
            "type": "end",
            "outputs": [
              { "variable": "reply", "value_selector": ["llm_1", "structured_output", "reply"] },
              { "variable": "routed_to", "value": "auto" }
            ]
          }
        },
        {
          "id": "end_manual",
          "type": "custom",
          "data": {
            "type": "end",
            "outputs": [
              { "variable": "reply", "value": "您的问题较为复杂,已转交人工客服,工单号 {{#http_1.body#}}" },
              { "variable": "routed_to", "value": "manual" }
            ]
          }
        }
      ],
      "edges": [
        { "source": "start", "target": "knowledge_1" },
        { "source": "knowledge_1", "target": "llm_1" },
        { "source": "llm_1", "target": "if_else_1" },
        { "source": "if_else_1", "sourceHandle": "true", "target": "end_auto" },
        { "source": "if_else_1", "sourceHandle": "false", "target": "http_1" },
        { "source": "http_1", "target": "end_manual" }
      ]
    }
  }
}

5.3 知识库准备

在"知识库"页面新建一个数据集,上传 FAQ 文档(支持 txt/md/pdf/docx/csv),Dify 会自动完成切片与向量化。完成后把数据集 ID 填入上面 DSL 的 dataset_ids。建议在数据集设置里开启"问答模式",让切片更贴合客服问答场景。

5.4 通过 API 调用工作流(curl)

发布后,用 curl 调用 /v1/workflows/run

curl -X POST 'http://localhost/v1/workflows/run' \
  -H 'Authorization: Bearer app-xxxxxxxxxxxxxxxx' \
  -H 'Content-Type: application/json' \
  -d '{
    "inputs": {
      "ticket_content": "我昨天买的耳机有杂音,能换吗?"
    },
    "response_mode": "blocking",
    "user": "customer-1024"
  }'

阻塞模式下返回示例:

{
  "task_id": "9d1dxxxx-xxxx",
  "workflow_run_id": "dj2kxxxx-xxxx",
  "data": {
    "id": "dj2kxxxx",
    "workflow_id": "fldxxxx",
    "status": "succeeded",
    "outputs": {
      "reply": "您好,耳机在 7 天内出现非人为质量问题可申请换新,请提供订单号...",
      "routed_to": "auto"
    },
    "elapsed_time": 3.21,
    "total_tokens": 612,
    "total_steps": 6
  }
}

5.5 Python 集成示例(requests)

requests 封装一个可复用的客户端,包含阻塞与流式两种调用方式:

import json
import requests


class DifyWorkflowClient:
    def __init__(self, base_url: str, api_key: str):
        self.base_url = base_url.rstrip("/")
        self.api_key = api_key

    def run(self, inputs: dict, user: str, response_mode: str = "blocking"):
        """阻塞模式:等待整个工作流跑完一次性返回结果。"""
        url = f"{self.base_url}/v1/workflows/run"
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
        }
        payload = {
            "inputs": inputs,
            "response_mode": response_mode,
            "user": user,
        }
        resp = requests.post(url, headers=headers, json=payload, timeout=60)
        resp.raise_for_status()
        return resp.json()

    def run_stream(self, inputs: dict, user: str):
        """流式模式:通过 SSE 逐步接收节点事件,适合长流程或前端实时展示。"""
        url = f"{self.base_url}/v1/workflows/run"
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json",
        }
        payload = {"inputs": inputs, "response_mode": "streaming", "user": user}
        with requests.post(url, headers=headers, json=payload, stream=True, timeout=60) as resp:
            for line in resp.iter_lines():
                if not line:
                    continue
                chunk = line.decode("utf-8")
                if chunk.startswith("data: "):
                    event = json.loads(chunk[6:])
                    yield event


if __name__ == "__main__":
    client = DifyWorkflowClient(
        base_url="http://localhost",
        api_key="app-xxxxxxxxxxxxxxxx",
    )

    # 阻塞调用
    result = client.run(
        inputs={"ticket_content": "我昨天买的耳机有杂音,能换吗?"},
        user="customer-1024",
    )
    print("回复内容:", result["data"]["outputs"]["reply"])
    print("路由去向:", result["data"]["outputs"]["routed_to"])
    print("耗时:", result["data"]["elapsed_time"], "秒")
    print("Token 消耗:", result["data"]["total_tokens"])

    # 流式调用示例
    for event in client.run_stream(
        inputs={"ticket_content": "订单 8899 还没发货"},
        user="customer-1025",
    ):
        # event["event"] 可能是 workflow_started / node_started / node_finished / text_chunk / workflow_finished
        if event.get("event") == "workflow_finished":
            print("\n最终输出:", event["data"]["outputs"])

5.6 用官方 Python SDK 调用

pip install dify-client
from dify_client import WorkflowClient

client = WorkflowClient("app-xxxxxxxxxxxxxxxx")
client.base_url = "http://localhost/v1"

response = client.run(
    inputs={"ticket_content": "耳机有杂音想换货"},
    user="customer-1024",
    response_mode="blocking",
)
print(response.text)

六、常见问题 FAQ

Q1:工作流支持循环和数组遍历吗? A:Workflow 不支持显式 for 循环。处理数组有两种方案:① 用 Code 节点写 Python 逻辑做批量转换;② 用"迭代(Iteration)"节点对数组逐项执行子流程,再在出口聚合结果,适合对一批文档/订单做同构处理。

Q2:知识库召回效果不好,如何优化? A:从三方面入手:① 提升文档切片质量,调整分块大小与重叠(chunk overlap),客服场景建议开启问答模式;② 在检索节点适当提高 top_k、调低 score_threshold,必要时开启 Rerank 重排序;③ 在 LLM 节点提示词里加"严格依据检索内容回答,否则说明无法回答"以降低幻觉。

Q3:API 调用偶发超时怎么办? A:长流程建议用 streaming 模式,避免网关一次性等待超时;客户端设置合理 timeout 并加指数退避重试;Dify 侧可在 .env 调整 WORKFLOW_MAX_EXECUTION_TIME 控制单次执行上限,必要时把重子流程拆成独立应用异步触发。

Q4:如何做权限控制和计费? A:Dify 按 API Key 区分应用,企业可在前面自建一层 API 网关做 Key 管理、配额限流、调用日志聚合,再按响应里的 total_tokens 字段计费。多租户建议每个业务方独立应用、独立 Key,知识库按权限隔离。

Q5:能否把工作流导出做版本管理? A:可以。每个应用支持导出 DSL(YAML 格式),纳入 Git 仓库即可版本化管理,也支持在不同环境(开发/测试/生产)间导入迁移,配合 CI/CD 可实现工作流即代码(Workflow-as-Code)的发布流水线。

总结

从"会调一次大模型"到"跑通一条企业级 AI 工作流",中间隔着需求拆解、流程编排、知识集成、API 对接、可观测运维等一堆工程问题。Dify 的价值在于把这些环节标准化:用画布表达业务逻辑、用节点封装能力、用变量贯通数据、用 API 暴露服务。

本文从 Docker 部署、核心概念、到智能客服工单分类实战,完整演示了一条"知识检索 → LLM 生成 → 条件路由 → 自动/人工分流"的工作流。把这套模式套用到文档审核、数据抽取、营销文案生成等场景,只需替换节点和提示词,即可快速复制。

AI 落地没有银弹,但好的工具能让你把精力花在业务设计而非胶水代码上——这正是 Dify 这类平台存在的意义。