Dify 流水线知识库(RAG Pipeline)深度分析

0 阅读16分钟

概述

什么是流水线知识库?

流水线知识库(RAG Pipeline Knowledge Base) 是 Dify 中一种高级的知识库模式,它将文档处理流程转化为可视化的工作流(Workflow),允许用户自定义文档的解析、清洗、切片、索引等全流程。

核心特点

  • 可视化工作流:通过拖拽方式构建文档处理流程
  • 高度可定制:每个处理环节都可自定义配置
  • 模板化管理:支持内置模板、自定义模板和导入导出
  • 异步执行:使用 Celery 任务队列处理文档
  • 状态追踪:完整记录每个节点的执行状态

适用场景

  1. 复杂文档处理需求:需要特殊的文档解析逻辑
  2. 多步骤数据清洗:需要多个清洗步骤的场景
  3. 自定义索引策略:需要特殊的向量化或索引方式
  4. 可复用处理流程:需要在多个知识库中复用同一套处理逻辑

核心概念

1. 运行模式(Runtime Mode)

Dify 中的知识库支持两种运行模式:

# api/models/dataset.py
runtime_mode = mapped_column(
    db.String(255), 
    nullable=True, 
    server_default=db.text("'general'::character varying")
)
  • general:传统知识库模式,使用固定的处理流程
  • rag_pipeline:流水线知识库模式,使用自定义工作流

2. Pipeline 实体

# api/models/dataset.py (Line 1274-1291)
class Pipeline(Base):
    """流水线实体"""
    id = mapped_column(StringUUID, server_default=db.text("uuidv7()"))
    tenant_id: Mapped[str] = mapped_column(StringUUID, nullable=False)
    name = mapped_column(db.String(255), nullable=False)
    description = mapped_column(sa.Text, nullable=False)
    workflow_id = mapped_column(StringUUID, nullable=True)  # 关联的工作流
    is_public = mapped_column(sa.Boolean, nullable=False, server_default=db.text("false"))
    is_published = mapped_column(sa.Boolean, nullable=False, server_default=db.text("false"))
    created_by = mapped_column(StringUUID, nullable=True)
    created_at = mapped_column(sa.DateTime, nullable=False)
    updated_by = mapped_column(StringUUID, nullable=True)
    updated_at = mapped_column(sa.DateTime, nullable=False)

核心字段说明

  • workflow_id:指向具体的 Workflow,定义了文档处理流程
  • is_published:标识流水线是否已发布(只有发布后才能处理文档)

3. Dataset 与 Pipeline 的关联

# api/models/dataset.py (Line 73)
pipeline_id = mapped_column(StringUUID, nullable=True)

# Dataset.is_published 属性 (Line 233-240)
@property
def is_published(self):
    if self.pipeline_id:
        pipeline = db.session.query(Pipeline).where(Pipeline.id == self.pipeline_id).first()
        if pipeline:
            return pipeline.is_published
    return False

关联关系

  • 每个 Dataset 可以关联一个 Pipeline
  • Dataset 的发布状态由其关联的 Pipeline 决定
  • 只有 runtime_mode="rag_pipeline" 的 Dataset 才会使用 Pipeline

4. 流水线模板

# api/services/rag_pipeline/pipeline_template/pipeline_template_type.py
class PipelineTemplateType(StrEnum):
    REMOTE = "remote"        # 远程模板(从市场获取)
    DATABASE = "database"    # 数据库模板
    CUSTOMIZED = "customized"  # 自定义模板
    BUILTIN = "builtin"      # 内置模板

模板类型

  • 内置模板:系统预设的通用文档处理流程
  • 自定义模板:用户自行创建并保存的模板
  • 数据库模板:存储在数据库中的模板
  • 远程模板:从插件市场获取的模板

架构设计

整体架构图

数据模型关系


工作原理

1. 创建流水线知识库

流程图

源码实现

📍 源码位置: api/services/dataset_service.py - DatasetService.create_empty_rag_pipeline_dataset

@staticmethod
def create_empty_rag_pipeline_dataset(
    tenant_id: str,
    rag_pipeline_dataset_create_entity: RagPipelineDatasetCreateEntity,
):
    # 🔑 关键点1:生成唯一的知识库名称
    if not rag_pipeline_dataset_create_entity.name:
        datasets = db.session.query(Dataset).filter_by(tenant_id=tenant_id).all()
        names = [dataset.name for dataset in datasets]
        rag_pipeline_dataset_create_entity.name = generate_incremental_name(
            names, "Untitled"
        )
    
    # 🔑 关键点2:创建 Pipeline 实体
    pipeline = Pipeline(
        tenant_id=tenant_id,
        name=rag_pipeline_dataset_create_entity.name,
        description=rag_pipeline_dataset_create_entity.description,
        created_by=current_user.id,
    )
    db.session.add(pipeline)
    db.session.flush()
    
    # 🔑 关键点3:创建 Dataset 并关联 Pipeline
    dataset = Dataset(
        tenant_id=tenant_id,
        name=rag_pipeline_dataset_create_entity.name,
        description=rag_pipeline_dataset_create_entity.description,
        permission=rag_pipeline_dataset_create_entity.permission,
        provider="vendor",
        runtime_mode="rag_pipeline",  # 设置为流水线模式
        icon_info=rag_pipeline_dataset_create_entity.icon_info.model_dump(),
        created_by=current_user.id,
        pipeline_id=pipeline.id,  # 关联 Pipeline
    )
    db.session.add(dataset)
    db.session.commit()
    return dataset

2. 配置工作流

工作流结构

流水线的核心是 Workflow,它定义了文档处理的完整流程:

# 工作流示例(YAML 格式)
version: "0.1"
kind: "rag-pipeline"
environment_variables: []
conversation_variables: []
graph:
  nodes:
    - id: "datasource"
      type: "datasource"
      data:
        title: "数据源"
        type: "datasource"
    
    - id: "document_extractor"
      type: "document-extractor"
      data:
        title: "文档解析"
        variable_selector:
          - "datasource"
          - "output"
    
    - id: "text_processing"
      type: "text-processing"
      data:
        title: "文本清洗"
        variable_selector:
          - "document_extractor"
          - "text"
    
    - id: "knowledge_retrieval"
      type: "knowledge-retrieval"
      data:
        title: "向量化索引"
        variable_selector:
          - "text_processing"
          - "output"
  
  edges:
    - source: "datasource"
      target: "document_extractor"
    - source: "document_extractor"
      target: "text_processing"
    - source: "text_processing"
      target: "knowledge_retrieval"

同步和发布流程

📍 源码位置: api/services/rag_pipeline/rag_pipeline.py - RagPipelineService.publish_workflow

def publish_workflow(
    self,
    pipeline_id: str,
):
    """
    发布工作流
    :param pipeline_id: pipeline id
    """
    # 🔑 关键点1:验证工作流存在
    pipeline = db.session.query(Pipeline).where(Pipeline.id == pipeline_id).first()
    if not pipeline:
        raise ValueError("Pipeline not found")
    
    # 🔑 关键点2:从草稿复制到正式版本
    draft_workflow = (
        db.session.query(Workflow)
        .where(
            Workflow.tenant_id == pipeline.tenant_id,
            Workflow.type == WorkflowType.RAG_PIPELINE,
            Workflow.app_id == pipeline.id,
            Workflow.version == "draft",
        )
        .first()
    )
    
    if not draft_workflow:
        raise ValueError("Draft workflow not found")
    
    # 🔑 关键点3:更新 Pipeline 状态
    pipeline.workflow_id = draft_workflow.id
    pipeline.is_published = True
    pipeline.updated_by = current_user.id
    pipeline.updated_at = datetime.now(UTC).replace(tzinfo=None)
    
    db.session.commit()

3. 文档处理执行

完整执行流程

任务队列处理

📍 源码位置: api/tasks/rag_pipeline/rag_pipeline_run_task.py - rag_pipeline_run_task

@shared_task(queue="pipeline")
def rag_pipeline_run_task(
    rag_pipeline_invoke_entities_file_id: str,
    tenant_id: str,
):
    """
    异步执行 RAG Pipeline 任务
    
    :param rag_pipeline_invoke_entities_file_id: 包含序列化实体的文件 ID
    :param tenant_id: 租户 ID
    """
    try:
        # 🔑 关键点1:从文件中读取任务实体
        rag_pipeline_invoke_entities_content = FileService(db.engine).get_file_content(
            rag_pipeline_invoke_entities_file_id
        )
        rag_pipeline_invoke_entities = json.loads(rag_pipeline_invoke_entities_content)
        
        # 🔑 关键点2:使用线程池并发执行(最大 10 个工作线程)
        with ThreadPoolExecutor(max_workers=10) as executor:
            futures = []
            for rag_pipeline_invoke_entity in rag_pipeline_invoke_entities:
                # 提交单个文档处理任务到线程池
                future = executor.submit(
                    run_single_rag_pipeline_task, 
                    rag_pipeline_invoke_entity, 
                    flask_app
                )
                futures.append(future)
            
            # 等待所有任务完成
            for future in futures:
                try:
                    future.result()
                except Exception:
                    logging.exception("Error in pipeline task")
    
    finally:
        # 🔑 关键点3:处理队列中的下一个任务
        tenant_self_pipeline_task_queue = f"tenant_self_pipeline_task_queue:{tenant_id}"
        next_file_id = redis_client.rpop(tenant_self_pipeline_task_queue)
        
        if next_file_id:
            # 继续处理下一个任务
            rag_pipeline_run_task.delay(
                rag_pipeline_invoke_entities_file_id=next_file_id,
                tenant_id=tenant_id,
            )
        else:
            # 队列为空,清除标志
            redis_client.delete(f"tenant_pipeline_task:{tenant_id}")

单个文档处理

📍 源码位置: api/tasks/rag_pipeline/rag_pipeline_run_task.py - run_single_rag_pipeline_task

def run_single_rag_pipeline_task(rag_pipeline_invoke_entity: Mapping[str, Any], flask_app):
    """在 Flask 应用上下文中运行单个 RAG Pipeline 任务"""
    with flask_app.app_context():
        # 🔑 关键点1:加载实体和用户信息
        rag_pipeline_invoke_entity_model = RagPipelineInvokeEntity.model_validate(
            rag_pipeline_invoke_entity
        )
        
        with Session(db.engine) as session:
            account = session.query(Account).where(
                Account.id == user_id
            ).first()
            pipeline = session.query(Pipeline).where(
                Pipeline.id == pipeline_id
            ).first()
            workflow = session.query(Workflow).where(
                Workflow.id == pipeline.workflow_id
            ).first()
            
            # 🔑 关键点2:创建工作流执行仓储
            workflow_execution_repository = (
                DifyCoreRepositoryFactory.create_workflow_execution_repository(
                    session_factory=session_factory,
                    user=account,
                    triggered_from=WorkflowRunTriggeredFrom.RAG_PIPELINE_RUN,
                )
            )
            
            # 🔑 关键点3:执行流水线
            from core.app.apps.pipeline.pipeline_generator import PipelineGenerator
            
            pipeline_generator = PipelineGenerator()
            pipeline_generator._generate(
                pipeline=pipeline,
                workflow_id=workflow_id,
                user=account,
                application_generate_entity=entity,
                invoke_from=InvokeFrom.PUBLISHED,
                workflow_execution_repository=workflow_execution_repository,
                streaming=streaming,
            )

4. 节点执行机制

每个工作流节点的执行遵循统一的接口:

# 节点基类接口
class Node:
    def _run(self) -> Generator[NodeRunResult, None, None]:
        """
        执行节点逻辑
        :return: 节点执行结果生成器
        """
        pass

常见节点类型

节点类型功能输入输出
datasource数据源文件/URL原始数据
document-extractor文档解析原始数据结构化文本
text-processing文本清洗文本清洗后文本
text-splitter文本切片文本文本片段列表
knowledge-indexing向量化索引文本片段索引结果

使用场景

场景 1:PDF 文档的高级处理

需求:处理包含表格和图片的复杂 PDF,需要提取表格数据并识别图片中的文字。

解决方案

graph:
  nodes:
    - id: "datasource"
      type: "datasource"
    
    - id: "pdf_extractor"
      type: "document-extractor"
      data:
        extraction_method: "ocr"  # 使用 OCR
        extract_tables: true       # 提取表格
        extract_images: true       # 提取图片
    
    - id: "table_processor"
      type: "code"
      data:
        code: |
          # 自定义表格处理逻辑
          tables = inputs['tables']
          processed_tables = []
          for table in tables:
              # 转换为 Markdown 格式
              md_table = convert_to_markdown(table)
              processed_tables.append(md_table)
          return {'output': processed_tables}
    
    - id: "text_merger"
      type: "template-transform"
      data:
        template: |
          正文内容:
          {{ text }}
          
          表格数据:
          {{ tables }}
    
    - id: "semantic_splitter"
      type: "text-splitter"
      data:
        split_method: "semantic"
        chunk_size: 1000

场景 2:多语言文档处理

需求:处理多语言文档,需要识别语言并分别处理。

解决方案

graph:
  nodes:
    - id: "language_detector"
      type: "llm"
      data:
        prompt: "检测以下文本的语言,只返回语言代码:\n{{ text }}"
        model: "gpt-4"
    
    - id: "language_router"
      type: "if-else"
      data:
        conditions:
          - variable: "{{ language_detector.output }}"
            comparison: "equals"
            value: "zh"
            target: "chinese_processor"
          - variable: "{{ language_detector.output }}"
            comparison: "equals"
            value: "en"
            target: "english_processor"
    
    - id: "chinese_processor"
      type: "text-processing"
      data:
        segmentation: "jieba"  # 中文分词
    
    - id: "english_processor"
      type: "text-processing"
      data:
        segmentation: "nltk"   # 英文分词

场景 3:增量更新知识库

需求:定期从外部 API 获取数据并更新知识库。

解决方案

graph:
  nodes:
    - id: "api_datasource"
      type: "http-request"
      data:
        url: "https://api.example.com/articles"
        method: "GET"
        headers:
          Authorization: "Bearer {{ env.API_KEY }}"
    
    - id: "json_parser"
      type: "code"
      data:
        code: |
          import json
          data = json.loads(inputs['response'])
          articles = data['articles']
          return {'articles': articles}
    
    - id: "deduplication"
      type: "code"
      data:
        code: |
          # 去重逻辑:检查文档是否已存在
          existing_docs = query_existing_documents()
          new_articles = [
              a for a in inputs['articles'] 
              if a['id'] not in existing_docs
          ]
          return {'new_articles': new_articles}
    
    - id: "batch_indexing"
      type: "iteration"
      data:
        input: "{{ deduplication.new_articles }}"
        nodes:
          - type: "knowledge-indexing"

使用方式

1. 通过控制台创建

步骤 1:创建空白流水线知识库

POST /console/api/datasets/rag/pipeline/empty-dataset
Content-Type: application/json
Authorization: Bearer <token>

{
  "name": "我的流水线知识库",
  "description": "自定义处理流程",
  "permission": "only_me"
}

响应示例

{
  "id": "dataset-uuid",
  "name": "我的流水线知识库",
  "runtime_mode": "rag_pipeline",
  "pipeline_id": "pipeline-uuid",
  "is_published": false
}

步骤 2:配置工作流

POST /console/api/datasets/{dataset_id}/rag/pipeline/workflow/sync-draft
Content-Type: application/json

{
  "graph": {
    "nodes": [...],
    "edges": [...]
  }
}

步骤 3:发布流水线

POST /console/api/datasets/{dataset_id}/rag/pipeline/workflow/publish

2. 通过 YAML 导入

YAML 模板示例

version: "0.1"
kind: "rag-pipeline"
name: "PDF 处理流水线"
description: "专门处理 PDF 文档的流水线"

environment_variables:
  - name: "OCR_API_KEY"
    value: ""
    description: "OCR 服务的 API Key"

graph:
  nodes:
    - id: "start"
      type: "datasource"
      data:
        title: "数据源"
        type: "datasource"
        desc: "上传 PDF 文件"
    
    - id: "pdf_parser"
      type: "document-extractor"
      data:
        title: "PDF 解析"
        variable_selector:
          - "start"
          - "output"
        extraction_settings:
          parser: "pdfplumber"
          extract_images: true
          ocr_enabled: true
    
    - id: "text_cleaner"
      type: "text-processing"
      data:
        title: "文本清洗"
        variable_selector:
          - "pdf_parser"
          - "text"
        rules:
          - type: "remove_extra_spaces"
          - type: "remove_urls"
    
    - id: "chunker"
      type: "text-splitter"
      data:
        title: "文本切片"
        variable_selector:
          - "text_cleaner"
          - "output"
        chunk_size: 800
        chunk_overlap: 100
    
    - id: "embedder"
      type: "knowledge-indexing"
      data:
        title: "向量化"
        variable_selector:
          - "chunker"
          - "chunks"
        embedding_model:
          provider: "openai"
          model: "text-embedding-3-small"
  
  edges:
    - source: "start"
      target: "pdf_parser"
    - source: "pdf_parser"
      target: "text_cleaner"
    - source: "text_cleaner"
      target: "chunker"
    - source: "chunker"
      target: "embedder"

导入 API

POST /console/api/datasets/rag/pipeline/dataset
Content-Type: application/json

{
  "yaml_content": "<上面的 YAML 内容>"
}

3. 使用模板创建

获取可用模板列表

GET /console/api/datasets/rag/pipeline/templates?type=built-in&language=zh-CN

响应示例

{
  "templates": [
    {
      "id": "template-1",
      "name": "通用文档处理",
      "description": "适用于大多数文档类型",
      "chunk_structure": "text",
      "icon": {...}
    },
    {
      "id": "template-2",
      "name": "高精度 PDF 处理",
      "description": "使用 OCR 和高级解析",
      "chunk_structure": "text",
      "icon": {...}
    }
  ]
}

使用模板创建

POST /console/api/datasets/rag/pipeline/dataset
Content-Type: application/json

{
  "template_id": "template-1",
  "name": "基于模板的知识库",
  "description": "使用内置模板创建"
}

源码解析

核心服务类

1. RagPipelineService

📍 源码位置: api/services/rag_pipeline/rag_pipeline.py

职责:流水线知识库的核心业务逻辑

关键方法

class RagPipelineService:
    def __init__(self, session_maker: sessionmaker | None = None):
        """初始化服务,注入仓储依赖"""
        self._node_execution_service_repo = ...
        self._workflow_run_repo = ...
    
    def get_pipeline_templates(cls, type: str = "built-in") -> dict:
        """获取流水线模板列表"""
        pass
    
    def sync_draft_workflow(self, pipeline_id: str, graph: dict):
        """同步草稿工作流"""
        pass
    
    def publish_workflow(self, pipeline_id: str):
        """发布工作流,使其可以处理文档"""
        pass
    
    def run_draft_workflow_node(self, pipeline_id: str, node_id: str, inputs: dict):
        """运行单个节点(用于调试)"""
        pass
    
    def get_rag_pipeline_paginate_workflow_runs(self, pipeline_id: str, page: int):
        """获取流水线执行历史(分页)"""
        pass

设计模式

  • 仓储模式:通过 _node_execution_service_repo_workflow_run_repo 分离数据访问逻辑
  • 工厂模式:使用 PipelineTemplateRetrievalFactory 创建不同类型的模板检索器

2. PipelineGenerator

📍 源码位置: api/core/app/apps/pipeline/pipeline_generator.py

职责:执行流水线,生成处理结果

核心流程

class PipelineGenerator:
    def generate(
        self,
        pipeline: Pipeline,
        workflow: Workflow,
        user: Union[Account, EndUser],
        args: Mapping[str, Any],
        invoke_from: InvokeFrom,
        streaming: bool = True,
    ) -> Generator:
        """
        生成流水线执行结果
        
        执行流程:
        1. 初始化工作流上下文
        2. 创建变量池
        3. 遍历工作流图的节点
        4. 执行每个节点
        5. 传递节点输出到下一个节点
        6. 生成执行事件流
        """
        # 初始化工作流入口
        workflow_entry = WorkflowEntry(
            workflow=workflow,
            user=user,
            invoke_from=invoke_from,
        )
        
        # 执行工作流
        for event in workflow_entry.run(args):
            yield event

执行策略

  • 流式执行:使用 Generator 实现流式输出
  • 事件驱动:通过事件(Event)通知节点执行状态
  • 错误处理:支持 continue-on-errorfail-branch 策略

3. RagPipelineDslService

📍 源码位置: api/services/rag_pipeline/rag_pipeline_dsl_service.py

职责:处理流水线 DSL 的导入导出

关键功能

class RagPipelineDslService:
    def import_rag_pipeline(
        self,
        tenant_id: str,
        yaml_content: str,
        mode: ImportMode,
    ) -> RagPipelineImportInfo:
        """
        导入 RAG Pipeline DSL
        
        流程:
        1. 解析 YAML 内容
        2. 验证版本兼容性
        3. 检查依赖项(模型、工具等)
        4. 创建临时数据(待用户确认)
        5. 返回导入信息
        """
        pass
    
    def export_rag_pipeline_dsl(
        self,
        dataset_id: str,
    ) -> str:
        """
        导出 RAG Pipeline DSL
        
        流程:
        1. 获取 Pipeline 和 Workflow
        2. 提取工作流图
        3. 移除敏感信息
        4. 生成 YAML 格式
        """
        pass
    
    def create_rag_pipeline_dataset(
        self,
        tenant_id: str,
        rag_pipeline_dataset_create_entity: RagPipelineDatasetCreateEntity,
    ):
        """
        从 YAML 直接创建流水线知识库
        
        流程:
        1. 导入 DSL
        2. 自动确认导入
        3. 创建 Dataset 和 Pipeline
        4. 发布工作流
        """
        pass

安全特性

  • 依赖检查:验证引用的模型、工具、插件是否可用
  • 数据加密:对敏感配置进行 AES 加密
  • 版本控制:检查 DSL 版本兼容性

执行流程关键代码

文档状态更新

# api/services/rag_pipeline/pipeline_generate_service.py
@classmethod
def update_document_status(cls, document_id: str):
    """更新文档状态为 waiting(等待处理)"""
    document = db.session.query(Document).where(Document.id == document_id).first()
    if document:
        document.indexing_status = "waiting"
        db.session.commit()

节点执行结果处理

# api/services/rag_pipeline/rag_pipeline.py
def _handle_node_run_result(
    self,
    node_id: str,
    node_run_result: NodeRunResult,
    workflow_run_id: str,
) -> dict:
    """
    处理节点执行结果
    
    :param node_id: 节点 ID
    :param node_run_result: 节点执行结果
    :param workflow_run_id: 工作流运行 ID
    :return: 处理后的结果
    """
    # 记录节点执行
    node_execution = WorkflowNodeExecution(
        id=str(uuid4()),
        workflow_run_id=workflow_run_id,
        node_id=node_id,
        node_type=node_run_result.node_type,
        title=node_run_result.title,
        inputs=node_run_result.inputs,
        outputs=node_run_result.outputs,
        status=node_run_result.status,
        error=node_run_result.error,
        elapsed_time=node_run_result.elapsed_time,
    )
    
    # 保存到数据库
    self._node_execution_service_repo.create(node_execution)
    
    return {
        "node_id": node_id,
        "status": node_run_result.status,
        "outputs": node_run_result.outputs,
    }

配置说明

环境变量

# Celery 任务队列配置
CELERY_BROKER_URL=redis://localhost:6379/1

# 流水线任务队列
CELERY_PIPELINE_QUEUE=pipeline

# 最大并发工作线程数
RAG_PIPELINE_MAX_WORKERS=10

# 任务超时时间(秒)
RAG_PIPELINE_TASK_TIMEOUT=3600

数据库配置

确保以下表存在:

-- 流水线表
CREATE TABLE pipelines (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    tenant_id UUID NOT NULL,
    name VARCHAR(255) NOT NULL,
    description TEXT,
    workflow_id UUID,
    is_public BOOLEAN DEFAULT FALSE,
    is_published BOOLEAN DEFAULT FALSE,
    created_by UUID,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_by UUID,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 文档流水线执行日志
CREATE TABLE document_pipeline_execution_logs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    pipeline_id UUID NOT NULL,
    document_id UUID NOT NULL,
    datasource_type VARCHAR(255) NOT NULL,
    datasource_info TEXT NOT NULL,
    datasource_node_id VARCHAR(255) NOT NULL,
    input_data JSONB NOT NULL,
    created_by UUID,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_doc_pipeline_exec_logs_doc_id 
ON document_pipeline_execution_logs(document_id);

Workflow 类型配置

# api/models/workflow.py
class WorkflowType(StrEnum):
    WORKFLOW = "workflow"
    CHAT = "chat"
    COMPLETION = "completion"
    RAG_PIPELINE = "rag-pipeline"  # 流水线类型

最佳实践

1. 模板化管理

建议:为不同类型的文档创建专用模板

# 创建自定义模板
rag_pipeline_service.publish_customized_pipeline_template(
    pipeline_id=pipeline_id,
    template_entity={
        "name": "法律文档处理模板",
        "description": "专门处理法律合同文档",
        "chunk_structure": "qa",  # Q&A 结构
        "icon": {"icon": "⚖️", "icon_type": "emoji"},
        "language": "zh-CN",
    }
)

优势

  • 减少重复配置
  • 统一处理标准
  • 便于团队协作

2. 错误处理策略

配置节点错误处理

nodes:
  - id: "risky_operation"
    type: "code"
    data:
      error_strategy: "continue"  # 继续执行
      on_error:
        - type: "log"
          message: "节点执行失败,已跳过"
        - type: "email"
          recipients: ["admin@example.com"]

最佳策略

  • 关键节点:使用 fail-branch,执行失败分支
  • 非关键节点:使用 continue-on-error,记录日志但继续执行
  • 数据质量检查:在切片前进行,失败则终止

3. 性能优化

3.1 批量处理

# 批量上传文档,减少任务调度开销
documents = [doc1, doc2, doc3, ...]

# 打包成一个任务
rag_pipeline_invoke_entities = [
    {
        "document_id": doc.id,
        "pipeline_id": pipeline_id,
        # ...
    }
    for doc in documents
]

# 序列化并提交
file_id = FileService.save_file(json.dumps(rag_pipeline_invoke_entities))
rag_pipeline_run_task.delay(file_id, tenant_id)

3.2 调整并发数

根据硬件资源调整线程池大小:

# api/tasks/rag_pipeline/rag_pipeline_run_task.py
# 修改 max_workers 参数
with ThreadPoolExecutor(max_workers=20) as executor:  # 默认 10
    # ...

建议配置

  • CPU 密集型任务(如文本处理):max_workers = CPU 核心数 * 2
  • IO 密集型任务(如 API 调用):max_workers = 20-50

3.3 缓存策略

nodes:
  - id: "expensive_operation"
    type: "llm"
    data:
      cache_enabled: true
      cache_ttl: 3600  # 缓存 1 小时
      cache_key_template: "{{ document.hash }}"

4. 监控与告警

4.1 执行历史查询

# 获取流水线执行历史
workflow_runs = RagPipelineService().get_rag_pipeline_paginate_workflow_runs(
    pipeline_id=pipeline_id,
    page=1,
    page_size=20,
)

for run in workflow_runs:
    print(f"执行 ID: {run.id}")
    print(f"状态: {run.status}")
    print(f"开始时间: {run.created_at}")
    print(f"耗时: {run.elapsed_time}s")

4.2 失败重试

# 重试失败的文档
RagPipelineService().retry_error_document(
    dataset_id=dataset_id,
    document_id=document_id,
)

4.3 监控指标

建议监控的关键指标:

指标说明告警阈值
任务队列长度tenant_self_pipeline_task_queue 的长度> 100
任务执行时间单个文档处理耗时> 5 分钟
失败率失败文档数 / 总文档数> 5%
并发任务数同时执行的任务数> max_workers

5. 安全建议

5.1 敏感信息处理

environment_variables:
  - name: "API_KEY"
    value: ""  # 不要在 DSL 中硬编码
    description: "从环境变量或密钥管理服务获取"

5.2 访问控制

# 设置知识库权限
dataset.permission = DatasetPermissionEnum.PARTIAL_TEAM

# 指定可访问成员
DatasetPermissionService.update_partial_member_list(
    tenant_id=tenant_id,
    dataset_id=dataset_id,
    partial_member_list=["user-1", "user-2"],
)

5.3 数据隔离

确保租户数据隔离:

# 所有查询都应包含 tenant_id
pipelines = db.session.query(Pipeline).where(
    Pipeline.tenant_id == current_tenant_id
).all()

常见问题

Q1: 流水线一直处于"未发布"状态?

原因:工作流未正确同步或发布。

解决方案

# 1. 检查工作流是否存在
draft_workflow = db.session.query(Workflow).where(
    Workflow.app_id == pipeline_id,
    Workflow.version == "draft"
).first()

if not draft_workflow:
    # 2. 同步草稿工作流
    RagPipelineService().sync_draft_workflow(
        pipeline_id=pipeline_id,
        graph={...}
    )

# 3. 发布
RagPipelineService().publish_workflow(pipeline_id=pipeline_id)

Q2: 文档处理失败,如何查看详细错误?

查看节点执行日志

node_executions = RagPipelineService().get_rag_pipeline_workflow_run_node_executions(
    workflow_run_id=workflow_run_id
)

for execution in node_executions:
    if execution.status == "failed":
        print(f"节点: {execution.title}")
        print(f"错误: {execution.error}")

Q3: 如何批量处理文档?

使用批量上传接口:

POST /console/api/datasets/{dataset_id}/documents/batch
Content-Type: multipart/form-data

files: [file1, file2, file3, ...]

系统会自动将它们打包成一个任务。

Q4: 流水线执行太慢?

优化方向

  1. 增加并发数:修改 ThreadPoolExecutor(max_workers=20)
  2. 优化节点逻辑:减少不必要的 API 调用
  3. 使用缓存:为重复操作启用缓存
  4. 拆分大文档:在切片前进行预处理

总结

流水线知识库是 Dify 提供的一种高级文档处理方式,通过可视化工作流实现了文档处理流程的完全自定义。

核心优势

  • 灵活性:可自由组合节点,适应各种复杂场景
  • 可观测性:完整的执行日志和状态追踪
  • 可复用性:通过模板实现流程复用
  • 高性能:异步任务队列 + 并发执行

适用场景

  • 需要特殊文档解析逻辑
  • 多步骤数据清洗
  • 自定义索引策略
  • 可复用处理流程

学习路径

  1. 从内置模板开始,理解基本流程
  2. 修改模板,尝试添加自定义节点
  3. 创建完全自定义的流水线
  4. 发布为模板供团队使用

最后更新: 2026-01-16
基于代码版本: Dify 主分支(截至 2026-01-16)