零基础入门Dify!万字详解工作流(workflow)实现机制,小白也能变大神!

一、工作流系统概述

1.1 核心概念

Dify 工作流系统是一个基于图(Graph)的执行引擎,允许用户通过可视化界面设计和执行复杂的 AI 工作流。工作流由多种类型的节点(Node)组成,这些节点通过边(Edge)连接,形成有向图结构。

1.2 系统架构

图片

工作流系统主要由以下几个部分组成:

  • 图引擎:负责解析工作流配置,构建执行图,并控制节点的执行顺序

  • 节点实现:各种类型节点的具体实现,如 LLM、知识检索、条件分支等

  • 变量管理:管理工作流执行过程中的变量传递和存储

  • 执行记录:记录工作流和节点的执行状态、输入输出和性能指标

二、数据模型设计

2.1 工作流数据模型

图片

Dify 使用多个模型来表示工作流及其执行状态:

  • WorkflowModel:工作流的基本信息,包括 ID、名称、描述、配置等

  • WorkflowRunModel:工作流的执行记录,包括执行状态、开始时间、结束时间等

  • WorkflowNodeExecutionModel:节点的执行记录,包括节点类型、输入、输出、状态等

  • ConversationVariable:存储会话变量,包括名称、值类型、值等

  • WorkflowDraftVariable:存储草稿工作流中的变量,包括会话变量、系统变量和节点变量

2.2 工作流节点类型

Dify 工作流支持多种类型的节点,每种节点有不同的功能和配置:

图片

Dify 支持多种类型的节点,包括:

  • START:工作流的起始节点

  • END:工作流的结束节点

  • LLM:大语言模型节点,用于生成文本

  • KNOWLEDGE_RETRIEVAL:知识检索节点,用于从知识库中检索信息

  • IF_ELSE:条件分支节点,根据条件选择执行路径

  • CODE:代码执行节点,执行自定义代码

  • HTTP_REQUEST:HTTP 请求节点,与外部 API 交互

  • TOOL:工具节点,调用预定义的工具

  • AGENT:代理节点,执行复杂的任务

 这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

三、工作流执行机制

3.1 工作流执行流程

图片

  1. 初始化工作流运行记录

  2. 解析工作流配置,构建执行图

  3. 从起始节点开始执行

  4. 根据图的边定义,确定下一个要执行的节点

  5. 执行节点,记录执行结果

  6. 重复步骤 4-5,直到达到结束节点或出现错误

  7. 完成工作流执行,更新运行记录

3.2 图引擎执行机制

图片

图引擎是工作流执行的核心,负责:

  • 解析节点和边配置

  • 构建边映射和反向边映射

  • 识别根节点和叶子节点

  • 检查节点连接性和循环

  • 管理并行执行

  • 控制执行流程

四、变量管理机制

4.1 变量池设计

Dify 工作流使用变量池(VariablePool)管理工作流执行过程中的变量。变量池包含以下几类变量:

  1. 系统变量:以 sys. 为前缀,如 sys.query(用户输入)、sys.files(用户上传文件)

  2. 环境变量:工作流级别的配置变量

  3. 会话变量:在会话中持久化的变量

  4. 节点变量:各节点的输入输出变量

图片

4.2 变量传递机制

节点之间通过变量池传递数据。每个节点执行时:

  1. 节点执行后,将输出添加到变量池中

  2. 下一个节点从变量池中获取所需的输入变量

  3. 支持通过选择器和模板字符串引用变量

  4. 支持文件类型变量的传递

变量的引用使用 {{#node_id.variable_name#}} 的模板语法。

五、节点实现机制

5.1 基础节点结构

所有节点都继承自 BaseNode 抽象类,实现自己的 _run 方法:

图片

所有节点都继承自 BaseNode 类,实现以下方法:

  • _run:节点的具体执行逻辑

  • _get_inputs:获取节点的输入变量

  • _get_outputs:处理节点的输出变量

以下是 BaseNode 类的核心实现:

class BaseNode(Generic[GenericNodeData]):
    _node_data_cls: type[GenericNodeData]
    _node_type: NodeType

    def __init__(
        self,
        id: str,
        config: Mapping[str, Any],
        graph_init_params: "GraphInitParams",
        graph: "Graph",
        graph_runtime_state: "GraphRuntimeState",
        previous_node_id: Optional[str] = None,
        thread_pool_id: Optional[str] = None,
    ) -> None:
        self.id = id
        self.tenant_id = graph_init_params.tenant_id
        self.app_id = graph_init_params.app_id
        self.workflow_type = graph_init_params.workflow_type
        self.workflow_id = graph_init_params.workflow_id
        self.graph_config = graph_init_params.graph_config
        self.user_id = graph_init_params.user_id
        self.user_from = graph_init_params.user_from
        self.invoke_from = graph_init_params.invoke_from
        self.workflow_call_depth = graph_init_params.call_depth
        self.graph = graph
        self.graph_runtime_state = graph_runtime_state
        self.previous_node_id = previous_node_id
        self.thread_pool_id = thread_pool_id

        node_id = config.get("id")
        ifnot node_id:
            raise ValueError("Node ID is required.")

        self.node_id = node_id

        node_data = self._node_data_cls.model_validate(config.get("data", {}))
        self.node_data = node_data

    @abstractmethod
    def _run(self) -> NodeRunResult | Generator[Union[NodeEvent, "InNodeEvent"], None, None]:
        """
        Run node
        :return:
        """
        raise NotImplementedError

    def run(self) -> Generator[Union[NodeEvent, "InNodeEvent"], None, None]:
        try:
            result = self._run()
        except Exception as e:
            logger.exception(f"Node {self.node_id} failed to run")
            result = NodeRunResult(
                status=WorkflowNodeExecutionStatus.FAILED,
                error=str(e),
                error_type="WorkflowNodeError",
            )

        if isinstance(result, NodeRunResult):
            yield RunCompletedEvent(run_result=result)
        else:
            yieldfrom result

BaseNode 类是所有节点的基类,它定义了节点的基本属性和方法:

  1. 初始化方法:接收节点 ID、配置、图引擎参数等,初始化节点的基本属性

  2. 抽象方法 _run:子类必须实现的方法,包含节点的具体执行逻辑

  3. run 方法:调用 _run 方法并处理异常,将结果包装为事件返回

5.2 节点类型实现

5.2.1 StartNode 实现

StartNode 是工作流的起始节点,负责将用户输入和系统变量作为节点的输出:

class StartNode(BaseNode[StartNodeData]):
    _node_data_cls = StartNodeData
    _node_type = NodeType.START

    def _run(self) -> NodeRunResult:
        node_inputs = dict(self.graph_runtime_state.variable_pool.user_inputs)
        system_inputs = self.graph_runtime_state.variable_pool.system_variables

        # TODO: System variables should be directly accessible, no need for special handling
        # Set system variables as node outputs.
        for var in system_inputs:
            node_inputs[SYSTEM_VARIABLE_NODE_ID + "." + var] = system_inputs[var]

        return NodeRunResult(status=WorkflowNodeExecutionStatus.SUCCEEDED, inputs=node_inputs, outputs=node_inputs)

StartNode 的实现非常简单,它主要完成以下工作:

  1. 从变量池中获取用户输入和系统变量

  2. 将系统变量添加到节点输入中,以 SYSTEM_VARIABLE_NODE_ID.var 的形式作为键

  3. 返回包含这些输入和输出的 NodeRunResult,状态为成功

5.2.2 IfElseNode 实现

IfElseNode 是条件分支节点,根据条件选择执行路径:

class IfElseNode(BaseNode[IfElseNodeData]):
    _node_data_cls = IfElseNodeData
    _node_type = NodeType.IF_ELSE

    def _run(self) -> NodeRunResult:
        """
        Run node
        :return:
        """
        node_inputs: dict[str, list] = {"conditions": []}

        process_data: dict[str, list] = {"condition_results": []}

        input_conditions = []
        final_result = False
        selected_case_id = None
        condition_processor = ConditionProcessor()
        try:
            # Check if the new cases structure is used
            if self.node_data.cases:
                for case in self.node_data.cases:
                    input_conditions, group_result, final_result = condition_processor.process_conditions(
                        variable_pool=self.graph_runtime_state.variable_pool,
                        conditions=case.conditions,
                        operator=case.logical_operator,
                    )

                    process_data["condition_results"].append(
                        {
                            "group": case.model_dump(),
                            "results": group_result,
                            "final_result": final_result,
                        }
                    )

                    # Break if a case passes (logical short-circuit)
                    if final_result:
                        selected_case_id = case.case_id  # Capture the ID of the passing case
                        break

            else:
                # Fallback to old structure if cases are not defined
                input_conditions, group_result, final_result = _should_not_use_old_function(
                    condition_processor=condition_processor,
                    variable_pool=self.graph_runtime_state.variable_pool,
                    conditions=self.node_data.conditions or [],
                    operator=self.node_data.logical_operator or"and",
                )

                selected_case_id = "true"if final_result else"false"

                process_data["condition_results"].append(
                    {"group": "default", "results": group_result, "final_result": final_result}
                )

            node_inputs["conditions"] = input_conditions

        except Exception as e:
            return NodeRunResult(
                status=WorkflowNodeExecutionStatus.FAILED, inputs=node_inputs, process_data=process_data, error=str(e)
            )

        outputs = {"result": final_result, "selected_case_id": selected_case_id}

        data = NodeRunResult(
            status=WorkflowNodeExecutionStatus.SUCCEEDED,
            inputs=node_inputs,
            process_data=process_data,
            edge_source_handle=selected_case_id or"false",  # Use case ID or 'default'
            outputs=outputs,
        )

        return data

IfElseNode 的实现主要完成以下工作:

  1. 使用 ConditionProcessor 处理条件逻辑

  2. 遍历 cases 结构中的条件组,并根据结果确定 selected_case_id

  3. 如果使用旧的结构,则调用 _should_not_use_old_function 进行兼容处理

  4. 返回包含条件结果的 NodeRunResult,并设置 edge_source_handle 以指示下一个要执行的节点

5.2.3 LLM 节点实现

LLM 节点是工作流中最核心的节点之一,它负责调用大语言模型生成文本。LLM 节点的执行流程:

图片

以下是 LLMNode 类的部分实现:

class LLMNode(BaseNode[LLMNodeData]):
    _node_data_cls = LLMNodeData
    _node_type = NodeType.LLM

    # Instance attributes specific to LLMNode.
    # Output variable for file
    _file_outputs: list["File"]
    _llm_file_saver: LLMFileSaver

    def __init__(
        self,
        id: str,
        config: Mapping[str, Any],
        graph_init_params: "GraphInitParams",
        graph: "Graph",
        graph_runtime_state: "GraphRuntimeState",
        previous_node_id: Optional[str] = None,
        thread_pool_id: Optional[str] = None,
        *,
        llm_file_saver: LLMFileSaver | None = None,
    ) -> None:
        super().__init__(
            id=id,
            config=config,
            graph_init_params=graph_init_params,
            graph=graph,
            graph_runtime_state=graph_runtime_state,
            previous_node_id=previous_node_id,
            thread_pool_id=thread_pool_id,
        )
        # LLM file outputs, used for MultiModal outputs.
        self._file_outputs: list[File] = []

        if llm_file_saver isNone:
            llm_file_saver = FileSaverImpl(
                user_id=graph_init_params.user_id,
                tenant_id=graph_init_params.tenant_id,
            )
        self._llm_file_saver = llm_file_saver

    def _run(self) -> Generator[NodeEvent | InNodeEvent, None, None]:
        def process_structured_output(text: str) -> Optional[dict[str, Any]]:
            """Process structured output if enabled"""
            ifnot self.node_data.structured_output_enabled ornot self.node_data.structured_output:
                returnNone
            return self._parse_structured_output(text)

        node_inputs: Optional[dict[str, Any]] = None
        process_data = None
        result_text = ""
        usage = LLMUsage.empty_usage()
        finish_reason = None

        try:
            # init messages template
            self.node_data.prompt_template = self._transform_chat_messages(self.node_data.prompt_template)

            # fetch variables and fetch values from variable pool
            inputs = self._fetch_inputs(node_data=self.node_data)

            # fetch jinja2 inputs
            jinja_inputs = self._fetch_jinja_inputs(node_data=self.node_data)

            # merge inputs
            inputs.update(jinja_inputs)

            node_inputs = {}

            # fetch files
            files = (
                self._fetch_files(selector=self.node_data.vision.configs.variable_selector)
                if self.node_data.vision.enabled
                else []
            )

            if files:
                node_inputs["#files#"] = [file.to_dict() for file in files]

            # fetch context value
            generator = self._fetch_context(node_data=self.node_data)
            context = None
            for event in generator:
                if isinstance(event, RunRetrieverResourceEvent):
                    context = event.context
                    yield event
            if context:
                node_inputs["#context#"] = context

            # fetch model config
            model_instance, model_config = self._fetch_model_config(self.node_data.model)
            
            # ... 更多代码 ...

LLMNode 是一个典型的节点实现,负责调用大语言模型:

  1. 初始化节点参数和模型配置

  2. 处理输入变量和文件

  3. 构建提示消息

  4. 调用 LLM 模型

  5. 处理模型返回的结果

  6. 生成节点执行结果

5.2.4 ToolNode 实现

ToolNode 是工具节点,负责调用预定义的工具:

class ToolNode(BaseNode[ToolNodeData]):
    """
    Tool Node
    """

    _node_data_cls = ToolNodeData
    _node_type = NodeType.TOOL

    def _run(self) -> Generator:
        """
        Run the tool node
        """

        node_data = cast(ToolNodeData, self.node_data)

        # fetch tool icon
        tool_info = {
            "provider_type": node_data.provider_type.value,
            "provider_id": node_data.provider_id,
            "plugin_unique_identifier": node_data.plugin_unique_identifier,
        }

        # get tool runtime
        try:
            from core.tools.tool_manager import ToolManager

            tool_runtime = ToolManager.get_workflow_tool_runtime(
                self.tenant_id, self.app_id, self.node_id, self.node_data, self.invoke_from
            )
        except ToolNodeError as e:
            yield RunCompletedEvent(
                run_result=NodeRunResult(
                    status=WorkflowNodeExecutionStatus.FAILED,
                    inputs={},
                    metadata={WorkflowNodeExecutionMetadataKey.TOOL_INFO: tool_info},
                    error=f"Failed to get tool runtime: {str(e)}",
                    error_type=type(e).__name__,
                )
            )
            return

        # get parameters
        tool_parameters = tool_runtime.get_merged_runtime_parameters() or []
        parameters = self._generate_parameters(
            tool_parameters=tool_parameters,
            variable_pool=self.graph_runtime_state.variable_pool,
            node_data=self.node_data,
        )
        
        # get conversation id
        conversation_id = self.graph_runtime_state.variable_pool.get_system_variable("conversation_id")

        # invoke tool
        try:
            from core.tools.entities.tool_entities import ToolInvokeMessage
            from core.tools.tool_engine import ToolEngine

            # invoke tool
            tool_invoke_message = ToolInvokeMessage(
                conversation_id=conversation_id,
                tool_parameters=parameters,
            )

            # invoke tool
            tool_response = ToolEngine.generic_invoke(
                tenant_id=self.tenant_id,
                app_id=self.app_id,
                tool_runtime=tool_runtime,
                tool_invoke_message=tool_invoke_message,
                user_id=self.user_id,
                invoke_from=self.invoke_from,
            )

            # ... 处理工具响应 ...
        except Exception as e:
            # ... 处理异常 ...

ToolNode 的实现主要完成以下工作:

  1. 获取工具信息和工具运行时

  2. 生成工具参数

  3. 获取会话 ID

  4. 通过 ToolEngine.generic_invoke 调用工具

  5. 处理工具返回的结果

  6. 生成节点执行结果

5.2.5 KnowledgeRetrievalNode 实现

KnowledgeRetrievalNode 是知识检索节点,负责从知识库中检索相关信息:

class KnowledgeRetrievalNode(LLMNode):
    """
    Knowledge Retrieval Node
    """

    _node_data_cls = KnowledgeRetrievalNodeData
    _node_type = NodeType.KNOWLEDGE_RETRIEVAL

    def _run(self) -> Generator[NodeEvent | InNodeEvent, None, None]:
        """
        Run node
        """
        node_data = cast(KnowledgeRetrievalNodeData, self.node_data)

        # get query variable
        query_variable = node_data.query_variable
        ifnot query_variable:
            yield RunCompletedEvent(
                run_result=NodeRunResult(
                    status=WorkflowNodeExecutionStatus.FAILED,
                    inputs={},
                    error="Query variable is not set",
                )
            )
            return

        # get query from variable pool
        query = self.graph_runtime_state.variable_pool.get_variable(query_variable)
        ifnot query:
            yield RunCompletedEvent(
                run_result=NodeRunResult(
                    status=WorkflowNodeExecutionStatus.FAILED,
                    inputs={},
                    error=f"Query variable {query_variable} is empty",
                )
            )
            return

        # check rate limit
        ifnot self._check_rate_limit():
            yield RunCompletedEvent(
                run_result=NodeRunResult(
                    status=WorkflowNodeExecutionStatus.FAILED,
                    inputs={},
                    error="Rate limit exceeded",
                )
            )
            return

        # get retrieval model config
        retrieval_model_config = {
            "search_method": node_data.search_method,
            "reranking_enable": node_data.reranking_enable,
            "reranking_model": node_data.reranking_model,
            "top_k": node_data.top_k,
            "score_threshold": node_data.score_threshold,
        }

        # ... 执行知识检索逻辑 ...

KnowledgeRetrievalNode 的实现主要完成以下工作:

  1. 从变量池中提取查询变量

  2. 检查查询是否为空

  3. 进行速率限制检查

  4. 定义检索模型配置

  5. 执行知识检索

  6. 处理检索结果

  7. 生成节点执行结果

5.2.6 CodeNode 实现

CodeNode 是代码执行节点,负责执行用户定义的代码:

class CodeNode(BaseNode[CodeNodeData]):
    """
    Code Node
    """

    _node_data_cls = CodeNodeData
    _node_type = NodeType.CODE

    def _run(self) -> NodeRunResult:
        """
        Run node
        """
        node_data = cast(CodeNodeData, self.node_data)

        # get code language and content
        code_language = node_data.code_language
        code_content = node_data.code_content

        # get input variables
        input_variables = {}
        for input_variable in node_data.input_variables:
            variable_name = input_variable.variable_name
            variable_value = self.graph_runtime_state.variable_pool.get_variable(input_variable.variable_selector)
            input_variables[variable_name] = variable_value

        # execute code
        try:
            from core.workflow.nodes.code.code_executor import CodeExecutor

            result = CodeExecutor.execute_workflow_code_template(
                code_language=code_language,
                code_content=code_content,
                input_variables=input_variables,
            )

            # check output variables
            outputs = {}
            for output_variable in node_data.output_variables:
                variable_name = output_variable.variable_name
                if variable_name notin result:
                    return NodeRunResult(
                        status=WorkflowNodeExecutionStatus.FAILED,
                        inputs=input_variables,
                        error=f"Output variable {variable_name} not found in code execution result",
                    )

                variable_value = result[variable_name]
                variable_type = output_variable.variable_type

                # check variable type
                if variable_type == "string":
                    ifnot self._check_string(variable_value, output_variable.max_length):
                        return NodeRunResult(
                            status=WorkflowNodeExecutionStatus.FAILED,
                            inputs=input_variables,
                            error=f"Output variable {variable_name} is not a valid string or exceeds max length",
                        )
                elif variable_type == "number":
                    ifnot self._check_number(variable_value):
                        return NodeRunResult(
                            status=WorkflowNodeExecutionStatus.FAILED,
                            inputs=input_variables,
                            error=f"Output variable {variable_name} is not a valid number",
                        )
                # ... 其他类型检查 ...

                outputs[variable_name] = variable_value

            return NodeRunResult(
                status=WorkflowNodeExecutionStatus.SUCCEEDED,
                inputs=input_variables,
                outputs=outputs,
            )

        except (CodeExecutionError, CodeNodeError) as e:
            return NodeRunResult(
                status=WorkflowNodeExecutionStatus.FAILED,
                inputs=input_variables,
                error=str(e),
            )

CodeNode 的实现主要完成以下工作:

  1. 获取代码语言和代码内容

  2. 从变量池中获取输入变量

  3. 通过 CodeExecutor.execute_workflow_code_template 执行代码

  4. 检查输出变量的类型和长度

  5. 处理执行结果和潜在的异常

  6. 生成节点执行结果

5.2.7 AgentNode 实现

AgentNode 是代理节点,负责调用 AI 代理执行复杂任务:

class AgentNode(ToolNode):
    """
    Agent Node
    """

    _node_data_cls = AgentNodeData
    _node_type = NodeType.AGENT

    def _run(self) -> Generator:
        """
        Run the agent node
        """
        node_data = cast(AgentNodeData, self.node_data)

        # get agent strategy
        try:
            from core.agent.strategy.strategy_factory import StrategyFactory

            strategy = StrategyFactory.create_strategy(
                tenant_id=self.tenant_id,
                app_id=self.app_id,
                strategy_mode=node_data.strategy_mode,
                strategy_config=node_data.strategy_config,
                user_id=self.user_id,
                invoke_from=self.invoke_from,
            )
        except Exception as e:
            yield RunCompletedEvent(
                run_result=NodeRunResult(
                    status=WorkflowNodeExecutionStatus.FAILED,
                    inputs={},
                    error=f"Failed to create agent strategy: {str(e)}",
                )
            )
            return

        # generate agent parameters
        agent_parameters = self._generate_parameters(
            tool_parameters=node_data.parameters,
            variable_pool=self.graph_runtime_state.variable_pool,
            node_data=node_data,
        )

        # get conversation id
        conversation_id = self.graph_runtime_state.variable_pool.get_system_variable("conversation_id")

        # invoke agent
        try:
            agent_response = strategy.invoke(
                conversation_id=conversation_id,
                inputs=agent_parameters,
                files=[],
            )

            # ... 处理代理响应 ...
        except Exception as e:
            # ... 处理异常 ...

AgentNode 的实现主要完成以下工作:

  1. 获取代理策略

  2. 生成代理参数

  3. 获取会话 ID

  4. 通过 strategy.invoke 调用代理

  5. 处理代理返回的结果

  6. 生成节点执行结果

5.2.8 HttpRequestNode 实现

HttpRequestNode 是 HTTP 请求节点,负责发送 HTTP 请求并处理响应:

class HttpRequestNode(BaseNode[HttpRequestNodeData]):
    """
    Http Request Node
    """

    _node_data_cls = HttpRequestNodeData
    _node_type = NodeType.HTTP_REQUEST

    def _run(self) -> NodeRunResult:
        """
        Run node
        """
        node_data = cast(HttpRequestNodeData, self.node_data)

        # get default config
        default_config = {
            "method": node_data.method,
            "url": node_data.url,
            "headers": node_data.headers,
            "params": node_data.params,
            "body": node_data.body,
            "timeout": node_data.timeout,
            "retry_count": node_data.retry_count,
            "retry_interval": node_data.retry_interval,
        }

        # init executor
        executor = HttpRequestExecutor(
            tenant_id=self.tenant_id,
            app_id=self.app_id,
            user_id=self.user_id,
            variable_pool=self.graph_runtime_state.variable_pool,
            default_config=default_config,
        )

        # execute http request
        try:
            response = executor.execute()

            # extract files
            files = []
            ifresponse.files:
                for file inresponse.files:
                    files.append(file.to_dict())

            # success
            ifresponse.success:
                return NodeRunResult(
                    status=WorkflowNodeExecutionStatus.SUCCEEDED,
                    inputs=response.request_info,
                    outputs={
                        "status_code": response.status_code,
                        "response_body": response.response_body,
                        "response_headers": response.response_headers,
                        "files": files,
                    },
                )
            # failed
            else:
                return NodeRunResult(
                    status=WorkflowNodeExecutionStatus.FAILED,
                    inputs=response.request_info,
                    error=response.error,
                )
        except Exception as e:
            return NodeRunResult(
                status=WorkflowNodeExecutionStatus.FAILED,
                inputs={},
                error=str(e),
            )

HttpRequestNode 的实现主要完成以下工作:

  1. 获取默认配置

  2. 初始化 HttpRequestExecutor

  3. 执行 HTTP 请求

  4. 处理响应(包括成功和失败情况)

  5. 提取文件

  6. 生成节点执行结果

六、工作流数据流动

6.1 工作流创建和发布

图片

  1. 用户在界面上设计工作流,定义节点和连接

  2. 系统将设计转换为工作流配置

  3. 创建工作流模型和草稿变量

  4. 发布工作流,使其可被调用

6.2 工作流调试和执行

图片

  1. 用户触发工作流执行

  2. 系统创建工作流运行记录

  3. 图引擎解析工作流配置,构建执行图

  4. 按照图的定义执行节点

  5. 记录每个节点的执行状态和结果

  6. 完成工作流执行,更新运行记录

 这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

七、图引擎机制

图引擎是工作流执行的核心,负责解析工作流图结构并执行节点。

7.1 图引擎实现

以下是 GraphEngine 类的部分实现:

class GraphEngine:
    """
    Graph Engine
    """

    def __init__(
        self,
        tenant_id: str,
        app_id: str,
        workflow_type: WorkflowType,
        workflow_id: str,
        user_id: str,
        invoke_from: InvokeFrom,
    ) -> None:
        """
        Initialize graph engine
        """
        self.tenant_id = tenant_id
        self.app_id = app_id
        self.workflow_type = workflow_type
        self.workflow_id = workflow_id
        self.user_id = user_id
        self.invoke_from = invoke_from

    def execute(
        self,
        workflow_run_id: str,
        workflow_config: dict[str, Any],
        user_inputs: dict[str, Any],
        system_variables: dict[str, Any],
        environment_variables: dict[str, Any],
        session_variables: dict[str, Any],
        *,
        event_handler: Optional[Callable[[WorkflowEvent], None]] = None,
    ) -> Generator[WorkflowEvent, None, None]:
        """
        Execute workflow
        """
        # Create graph init params
        graph_init_params = GraphInitParams(
            tenant_id=self.tenant_id,
            app_id=self.app_id,
            workflow_id=self.workflow_id,
            workflow_run_id=workflow_run_id,
            user_id=self.user_id,
            invoke_from=self.invoke_from,
        )

        # Create variable pool
        variable_pool = VariablePool(
            user_inputs=user_inputs,
            system_variables=system_variables,
            environment_variables=environment_variables,
            session_variables=session_variables,
        )

        # Create graph runtime state
        graph_runtime_state = GraphRuntimeState(
            variable_pool=variable_pool,
        )

        # Create graph
        graph = Graph(
            workflow_config=workflow_config,
            graph_init_params=graph_init_params,
            graph_runtime_state=graph_runtime_state,
        )

        # Create thread pool
        thread_pool = GraphEngineThreadPool(max_workers=10)

        # Execute graph
        try:
            # Yield workflow started event
            yield WorkflowStartedEvent(
                workflow_run_id=workflow_run_id,
            )

            # Execute graph
            for event in graph.execute(thread_pool=thread_pool):
                # Handle event
                if event_handler:
                    event_handler(event)

                # Yield event
                yield event

                # Check if workflow is completed
                if isinstance(event, WorkflowCompletedEvent):
                    break

        except Exception as e:
            # Yield workflow failed event
            yield WorkflowFailedEvent(
                workflow_run_id=workflow_run_id,
                error=str(e),
            )

        finally:
            # Shutdown thread pool
            thread_pool.shutdown(wait=True)

GraphEngineThreadPool 是一个继承自 ThreadPoolExecutor 的线程池,用于管理工作流的并行执行:

class GraphEngineThreadPool(ThreadPoolExecutor):
    """
    Graph Engine Thread Pool
    """

    def __init__(self, max_workers: Optional[int] = None) -> None:
        """
        Initialize graph engine thread pool
        """
        super().__init__(max_workers=max_workers)
        self._futures: dict[str, Future] = {}

    def submit_task(
        self, thread_pool_id: str, fn: Callable, *args: Any, **kwargs: Any
    ) -> Future:
        """
        Submit task to thread pool
        """
        future = self.submit(fn, *args, **kwargs)
        self._futures[thread_pool_id] = future
        future.add_done_callback(lambda _: self._futures.pop(thread_pool_id, None))
        returnfuture

    def is_full(self) -> bool:
        """
        Check if thread pool is full
        """
        return len(self._futures) >= self._max_workers

    def get_future(self, thread_pool_id: str) -> Optional[Future]:
        """
        Get future by thread pool id
        """
        return self._futures.get(thread_pool_id)

7.2 图结构解析

图引擎首先解析工作流的图结构,包括:

  1. 解析节点:解析工作流中的所有节点,包括节点类型、配置等

  2. 解析边:解析节点之间的连接关系,包括源节点、目标节点、源端口、目标端口等

  3. 构建节点映射:构建节点ID到节点对象的映射

  4. 构建边映射:构建边ID到边对象的映射

7.3 图结构实现

以下是 Graph 类的部分实现,它负责解析工作流配置并执行节点:

class Graph:
    """
    Graph
    """

    def __init__(
        self,
        workflow_config: dict[str, Any],
        graph_init_params: GraphInitParams,
        graph_runtime_state: GraphRuntimeState,
    ) -> None:
        """
        Initialize graph
        """
        self.workflow_config = workflow_config
        self.graph_init_params = graph_init_params
        self.graph_runtime_state = graph_runtime_state

        # Parse workflow config
        self.nodes = self._parse_nodes(workflow_config.get("nodes", {}))
        self.edges = self._parse_edges(workflow_config.get("edges", {}))

        # Build node and edge mappings
        self.node_mapping = self._build_node_mapping(self.nodes)
        self.edge_mapping = self._build_edge_mapping(self.edges)

        # Build source and target node mappings
        self.source_node_mapping = self._build_source_node_mapping(self.edges)
        self.target_node_mapping = self._build_target_node_mapping(self.edges)

    def execute(self, thread_pool: GraphEngineThreadPool) -> Generator[WorkflowEvent, None, None]:
        """
        Execute graph
        """
        # Find start node
        start_node = self._find_start_node()
        ifnot start_node:
            yield WorkflowFailedEvent(
                workflow_run_id=self.graph_init_params.workflow_run_id,
                error="Start node not found",
            )
            return

        # Execute start node
        for event in self._execute_node(start_node, thread_pool=thread_pool):
            yield event

        # Yield workflow completed event
        yield WorkflowCompletedEvent(
            workflow_run_id=self.graph_init_params.workflow_run_id,
        )

    def _execute_node(
        self, node: BaseNode, thread_pool: GraphEngineThreadPool, previous_node_id: Optional[str] = None
    ) -> Generator[WorkflowEvent, None, None]:
        """
        Execute node
        """
        # Yield node started event
        yield NodeStartedEvent(
            workflow_run_id=self.graph_init_params.workflow_run_id,
            node_id=node.node_id,
            node_type=node.node_type,
        )

        # Run node
        try:
            for event in node.run():
                # Handle node event
                if isinstance(event, RunCompletedEvent):
                    # Get node run result
                    node_run_result = event.run_result

                    # Update variable pool
                    if node_run_result.outputs:
                        for variable_name, variable_value in node_run_result.outputs.items():
                            self.graph_runtime_state.variable_pool.add(
                                node_id=node.node_id,
                                variable_name=variable_name,
                                variable_value=variable_value,
                            )

                    # Yield node completed event
                    yield NodeCompletedEvent(
                        workflow_run_id=self.graph_init_params.workflow_run_id,
                        node_id=node.node_id,
                        node_type=node.node_type,
                        status=node_run_result.status,
                        inputs=node_run_result.inputs,
                        outputs=node_run_result.outputs,
                        process_data=node_run_result.process_data,
                        error=node_run_result.error,
                    )

                    # Find next nodes
                    next_nodes = self._find_next_nodes(
                        node_id=node.node_id,
                        edge_source_handle=node_run_result.edge_source_handle,
                    )

                    # Execute next nodes
                    for next_node in next_nodes:
                        # Check if thread pool is full
                        if thread_pool.is_full():
                            # Execute next node in current thread
                            for event in self._execute_node(
                                node=next_node,
                                thread_pool=thread_pool,
                                previous_node_id=node.node_id,
                            ):
                                yield event
                        else:
                            # Execute next node in new thread
                            thread_pool_id = str(uuid.uuid4())
                            thread_pool.submit_task(
                                thread_pool_id=thread_pool_id,
                                fn=self._execute_node_in_thread,
                                node=next_node,
                                thread_pool=thread_pool,
                                previous_node_id=node.node_id,
                                thread_pool_id=thread_pool_id,
                            )
                else:
                    # Yield other events
                    yield event

        except Exception as e:
            # Yield node failed event
            yield NodeFailedEvent(
                workflow_run_id=self.graph_init_params.workflow_run_id,
                node_id=node.node_id,
                node_type=node.node_type,
                error=str(e),
            )

7.4 节点执行

图引擎根据图结构执行节点:

  1. 确定起始节点:通常是START节点

  2. 执行节点:调用节点的run方法

  3. 处理节点结果:根据节点执行结果确定下一个要执行的节点

  4. 处理并行执行:如果有多个分支,可以并行执行

7.4.1 节点执行的主要流程

节点执行的主要流程如下:

  1. 发出节点开始事件:触发 NodeRunStartedEvent,通知系统节点开始执行

  2. 调用节点的 run 方法:执行节点的具体逻辑

  3. 处理节点事件

    • 处理 RunCompletedEvent:获取节点执行结果

    • 处理 RunStreamChunkEvent:处理流式输出

    • 处理 RunRetrieverResourceEvent:处理检索资源

  4. 处理重试逻辑:如果节点执行失败且配置了重试,则进行重试

  5. 更新变量池:将节点输出变量添加到变量池中

  6. 发出节点完成事件:根据执行结果触发相应事件

    • 成功:触发 NodeRunSucceededEvent

    • 失败:触发 NodeRunFailedEvent

    • 异常但继续:触发 NodeRunExceptionEvent

  7. 查找下一个要执行的节点:根据边映射和条件确定下一个节点

  8. 执行下一个节点:可能是串行执行或并行执行

图片

7.4.2 查找下一个节点的机制

在工作流执行过程中,确定下一个要执行的节点是关键步骤。GraphEngine 类的 _run 方法实现了这一机制:

  1. 获取边映射:通过 self.graph.edge_mapping.get(next_node_id) 获取当前节点的所有出边

  2. 单边处理:如果只有一条出边,直接获取目标节点ID

    if len(edge_mappings) == 1:
        edge = edge_mappings[0]
        # 检查是否有运行条件
        if edge.run_condition:
            result = ConditionManager.get_condition_handler(...).check(...)
            if not result:
                break  # 条件不满足,停止执行
        next_node_id = edge.target_node_id
    
  3. 多边处理:如果有多条出边,需要根据条件或并行策略确定下一个节点

    • 条件分支:如果边有运行条件,根据条件结果确定要执行的分支
      if any(edge.run_condition for edge in edge_mappings):
          # 按条件分组
          condition_edge_mappings: dict[str, list[GraphEdge]] = {}
          # 检查每个条件组
          for _, sub_edge_mappings in condition_edge_mappings.items():
              # 检查条件是否满足
              result = ConditionManager.get_condition_handler(...).check(...)
              if result:
                  # 条件满足,确定下一个节点
                  if len(sub_edge_mappings) == 1:
                      final_node_id = edge.target_node_id
                  else:
                      # 并行执行多个分支
                      parallel_generator = self._run_parallel_branches(...)
      
    • 并行分支:如果没有条件或条件满足,可能需要并行执行多个分支
      else:
          parallel_generator = self._run_parallel_branches(
              edge_mappings=edge_mappings,
              in_parallel_id=in_parallel_id,
              parallel_start_node_id=parallel_start_node_id,
              handle_exceptions=handle_exceptions,
          )
      
  4. 并行分支执行:通过 _run_parallel_branches 方法处理并行分支

    • 创建线程池和队列管理并行执行

    • 为每个分支创建一个线程执行

    • 收集并处理所有分支的执行结果

  5. 检查节点是否在当前并行分支内:确保节点执行不会跨越并行分支边界

    if in_parallel_id and self.graph.node_parallel_mapping.get(next_node_id, "") != in_parallel_id:
        break
    

通过这种机制,工作流系统能够灵活地处理各种复杂的执行路径,包括条件分支和并行执行,确保工作流按照设计的逻辑正确执行。

八、图引擎与节点执行的通信机制

图引擎与节点执行之间的通信是通过事件驱动机制实现的,这种机制使得工作流执行过程中的各个组件能够松耦合地交互,提高了系统的可扩展性和可维护性。

8.1 事件驱动架构

工作流系统采用事件驱动架构,通过定义和传递各种事件来实现图引擎与节点之间的通信。这种架构具有以下特点:

  1. 松耦合:图引擎和节点之间通过事件进行通信,而不是直接调用,降低了组件间的依赖

  2. 可扩展:新的节点类型和事件类型可以轻松添加到系统中,而不需要修改现有代码

  3. 异步处理:事件可以异步处理,提高系统的响应性和吞吐量

  4. 状态追踪:通过事件可以追踪工作流的执行状态和历史

图片

8.2 核心事件类型

工作流系统定义了多种事件类型,用于表示工作流执行过程中的不同状态和操作:

8.2.1 图级事件
  • GraphRunStartedEvent:工作流开始执行

  • GraphRunSucceededEvent:工作流成功完成

  • GraphRunFailedEvent:工作流执行失败

  • GraphRunPartialSucceededEvent:工作流部分成功(有些节点失败但不影响整体结果)

8.2.2 节点级事件
  • NodeRunStartedEvent:节点开始执行

  • NodeRunSucceededEvent:节点执行成功

  • NodeRunFailedEvent:节点执行失败

  • NodeRunExceptionEvent:节点执行异常但继续执行

  • NodeRunRetryEvent:节点重试执行

  • NodeRunStreamChunkEvent:节点产生流式输出

  • NodeRunRetrieverResourceEvent:节点检索资源

8.2.3 并行分支事件
  • ParallelBranchRunStartedEvent:并行分支开始执行

  • ParallelBranchRunSucceededEvent:并行分支执行成功

  • ParallelBranchRunFailedEvent:并行分支执行失败

8.2.4 迭代和循环事件
  • IterationRunStartedEvent:迭代开始

  • IterationRunNextEvent:迭代下一步

  • IterationRunSucceededEvent:迭代成功完成

  • IterationRunFailedEvent:迭代失败

  • LoopRunStartedEvent:循环开始

  • LoopRunNextEvent:循环下一步

  • LoopRunSucceededEvent:循环成功完成

  • LoopRunFailedEvent:循环失败

8.3 事件传递流程

事件在工作流系统中的传递流程如下:

  1. 事件生成:图引擎或节点执行器生成事件

    yield NodeRunStartedEvent(
        id=node_instance.id,
        node_id=node_instance.node_id,
        node_type=node_instance.node_type,
        node_data=node_instance.node_data,
        route_node_state=route_node_state,
        predecessor_node_id=node_instance.previous_node_id,
        # 其他参数...
    )
    
  2. 事件传递:通过 Python 生成器(Generator)机制传递事件

    def run(self) -> Generator[GraphEngineEvent, None, None]:
        # ...
        generator = graph_engine.run()
        for event in generator:
            # 处理事件
            yield event
    
  3. 事件处理:工作流入口点(WorkflowEntry)接收事件并分发给回调处理器

    for event in generator:
        if callbacks:
            for callback in callbacks:
                callback.on_event(event=event)
        yield event
    
  4. 回调处理:回调处理器根据事件类型执行相应的操作

    def on_event(self, event: GraphEngineEvent) -> None:
        if isinstance(event, NodeRunStartedEvent):
            self.on_workflow_node_execute_started(event=event)
        elif isinstance(event, NodeRunSucceededEvent):
            self.on_workflow_node_execute_succeeded(event=event)
        # 处理其他事件类型...
    

8.4 事件处理回调

工作流系统定义了回调接口,允许外部系统注册回调函数来处理工作流事件:

class WorkflowCallback(ABC):
    @abstractmethod
    def on_event(self, event: GraphEngineEvent) -> None:
        """处理工作流事件"""
        raise NotImplementedError

系统提供了多种内置回调实现,如:

  1. WorkflowLoggingCallback:记录工作流执行日志

  2. WorkflowAppRunnerCallback:处理应用级别的工作流事件

8.5 事件与状态管理

事件不仅用于通信,还用于管理工作流的状态:

  1. 节点状态追踪:通过事件记录节点的执行状态和结果

    # 节点开始执行
    yield NodeRunStartedEvent(...)
    
    # 节点执行成功
    yield NodeRunSucceededEvent(...)
    
  2. 变量传递:事件携带节点的输入和输出变量

    # 节点执行成功事件包含输出变量
    yield NodeRunSucceededEvent(
        # ...
        outputs=run_result.outputs,
        # ...
    )
    
  3. 错误处理:事件携带错误信息,用于错误处理和重试

    # 节点执行失败事件包含错误信息
    yield NodeRunFailedEvent(
        error=route_node_state.failed_reason or "Unknown error.",
        # ...
    )
    

8.6 事件转换与应用集成

工作流应用运行器(WorkflowAppRunner)将工作流事件转换为应用级别的队列事件,实现与应用系统的集成:

def _handle_event(self, workflow_entry: WorkflowEntry, event: GraphEngineEvent) -> None:
    if isinstance(event, NodeRunSucceededEvent):
        self._publish_event(
            QueueNodeSucceededEvent(
                node_execution_id=event.id,
                node_id=event.node_id,
                node_type=event.node_type,
                node_data=event.node_data,
                inputs=inputs,
                process_data=process_data,
                outputs=outputs,
                execution_metadata=execution_metadata,
                # 其他参数...
            )
        )
    # 处理其他事件类型...

这种转换机制使得工作流系统能够与外部应用系统无缝集成,同时保持内部实现的独立性。

8.7 事件通信示例

以下是一个完整的事件通信流程示例,展示了从节点执行到事件处理的整个过程:

8.7.1 节点执行与事件生成

当图引擎执行一个节点时,会生成一系列事件:

# 1. 节点开始执行事件
yield NodeRunStartedEvent(
    id=node_instance.id,
    node_id=node_instance.node_id,
    node_type=node_instance.node_type,
    node_data=node_instance.node_data,
    route_node_state=route_node_state,
    # 其他参数...
)

# 2. 执行节点的run方法
generator = node_instance.run()
for item in generator:
    # 传递节点产生的事件
    yield item

# 3. 节点执行成功事件
yield NodeRunSucceededEvent(
    id=node_instance.id,
    node_id=node_instance.node_id,
    node_type=node_instance.node_type,
    node_data=node_instance.node_data,
    route_node_state=route_node_state,
    # 其他参数...
)
8.7.2 事件传递与处理

事件通过工作流入口点传递给回调处理器:

# WorkflowEntry.run方法
def run(self, *, callbacks: Sequence[WorkflowCallback]) -> Generator[GraphEngineEvent, None, None]:
    generator = graph_engine.run()
    for event in generator:
        # 分发事件给回调处理器
        for callback in callbacks:
            callback.on_event(event=event)
        # 继续传递事件
        yield event
8.7.3 回调处理器处理事件

回调处理器根据事件类型执行相应的操作:

# WorkflowLoggingCallback.on_event方法
def on_event(self, event: GraphEngineEvent) -> None:
    if isinstance(event, NodeRunStartedEvent):
        self.print_text("\n[NodeRunStartedEvent]", color="yellow")
        self.print_text(f"Node ID: {event.node_id}", color="yellow")
        self.print_text(f"Node Title: {event.node_data.title}", color="yellow")
        self.print_text(f"Type: {event.node_type.value}", color="yellow")
    elif isinstance(event, NodeRunSucceededEvent):
        self.print_text("\n[NodeRunSucceededEvent]", color="green")
        # 打印节点执行结果
        if event.route_node_state.node_run_result:
            node_run_result = event.route_node_state.node_run_result
            self.print_text(f"Outputs: {jsonable_encoder(node_run_result.outputs)}", color="green")
    # 处理其他事件类型...
8.7.4 应用运行器处理事件

应用运行器将工作流事件转换为应用级别的队列事件:

# WorkflowAppRunner._handle_event方法
def _handle_event(self, workflow_entry: WorkflowEntry, event: GraphEngineEvent) -> None:
    if isinstance(event, GraphRunStartedEvent):
        self._publish_event(QueueWorkflowStartedEvent(...))
    elif isinstance(event, NodeRunSucceededEvent):
        self._publish_event(QueueNodeSucceededEvent(...))
    elif isinstance(event, NodeRunFailedEvent):
        self._publish_event(QueueNodeFailedEvent(...))
    # 处理其他事件类型...

8.8 事件通信的优势

图引擎与节点执行之间基于事件的通信机制具有以下优势:

  1. 解耦组件:图引擎和节点执行器通过事件进行通信,而不是直接调用,降低了组件间的耦合度

  2. 简化调试:事件包含完整的上下文信息,便于调试和问题排查

  3. 支持异步执行:事件可以异步处理,支持并行执行和分布式部署

  4. 可扩展性:新的节点类型和事件类型可以轻松添加到系统中,而不需要修改现有代码

  5. 状态追踪:通过事件可以完整记录工作流的执行状态和历史,便于监控和审计

  6. 错误处理:事件携带错误信息,支持灵活的错误处理策略和重试机制

九、错误处理机制

工作流系统提供了完善的错误处理机制,包括错误策略、重试机制和异常处理,确保工作流在面对各种异常情况时能够灵活应对。

9.1 错误处理策略

工作流系统提供了两种主要的错误处理策略:

  1. FAIL_BRANCH:当节点执行失败时,沿着失败分支继续执行

    • 将错误信息和类型添加到变量池

    • 设置 edge_source_handle 为 FAILED,使工作流可以沿着专门处理失败情况的分支继续执行

    • 适用于需要针对失败情况执行特定逻辑的场景

  2. DEFAULT_VALUE:当节点执行失败时,使用预定义的默认值继续执行

    • 将错误信息和类型添加到变量池

    • 使用节点配置中预定义的默认值作为节点输出

    • 适用于即使失败也需要提供某种结果的场景

图片

9.2 节点重试机制

对于某些类型的节点,系统支持在执行失败时进行重试:

  1. 重试配置

    • max_retries:最大重试次数

    • retry_interval_seconds:重试间隔时间(秒)

  2. 重试流程

    • 节点执行失败后,检查是否配置了重试

    • 如果当前重试次数小于最大重试次数,触发 NodeRunRetryEvent 事件

    • 等待指定的重试间隔时间

    • 重新执行节点

  3. 重试事件

    • 系统触发 NodeRunRetryEvent 事件,包含重试索引、开始时间等信息

    • 事件可用于监控和记录重试情况

if node_instance.should_retry and retries < max_retries:
    retries += 1
    route_node_state.node_run_result = run_result
    yield NodeRunRetryEvent(
        id=str(uuid.uuid4()),
        node_id=node_instance.node_id,
        node_type=node_instance.node_type,
        node_data=node_instance.node_data,
        route_node_state=route_node_state,
        error=run_result.error or "Unknown error",
        retry_index=retries,
        start_at=retry_start_at,
    )
    time.sleep(retry_interval)

8.3 可继续执行和可重试的节点类型

系统定义了特定类型的节点,它们在错误处理方面有特殊行为:

  1. 可继续执行的节点类型CONTINUE_ON_ERROR_NODE_TYPE):

    • 即使执行失败,工作流也可以继续执行

    • 例如:HTTP请求节点、LLM节点等

    • 这些节点可以配置错误策略(FAIL_BRANCH或DEFAULT_VALUE)

  2. 可重试的节点类型RETRY_ON_ERROR_NODE_TYPE):

    • 执行失败时可以自动重试

    • 例如:HTTP请求节点、数据库操作节点等

    • 这些节点可以配置最大重试次数和重试间隔

通过这些机制,工作流系统能够灵活处理各种错误情况,提高工作流的健壮性和可靠性。

9. 变量管理机制

变量管理是工作流执行的重要组成部分,负责管理工作流中的变量。

8.1 变量池

变量池是工作流中所有变量的集合,包括:

  1. 用户输入变量:用户提供的输入

  2. 系统变量:系统提供的变量,如时间戳、会话ID等

  3. 环境变量:环境相关的变量

  4. 会话变量:会话相关的变量

  5. 节点输出变量:节点执行后的输出变量

以下是 VariablePool 类的部分实现:

class VariablePool:
    """
    Variable Pool
    """

    def __init__(
        self,
        user_inputs: dict[str, Any],
        system_variables: dict[str, Any],
        environment_variables: dict[str, Any],
        session_variables: dict[str, Any],
    ) -> None:
        """
        Initialize variable pool
        """
        self.user_inputs = user_inputs
        self.system_variables = system_variables
        self.environment_variables = environment_variables
        self.session_variables = session_variables

        # Initialize variable dictionary
        self.variable_dictionary: dict[str, Any] = {}

    def add(self, node_id: str, variable_name: str, variable_value: Any) -> None:
        """
        Add variable to variable pool
        """
        # Check if variable value is File
        if isinstance(variable_value, File):
            # Convert File to dict
            variable_value = variable_value.to_dict()

        # Add variable to variable dictionary
        self.variable_dictionary[f"{node_id}.{variable_name}"] = variable_value

    def get_variable(self, variable_selector: str) -> Any:
        """
        Get variable from variable pool
        """
        # Check if variable selector is empty
        ifnot variable_selector:
            returnNone

        # Check if variable selector is system variable
        if variable_selector.startswith(SYSTEM_VARIABLE_NODE_ID):
            # Get system variable
            variable_name = variable_selector.split(".", 1)[1]
            return self.get_system_variable(variable_name)

        # Check if variable selector is user input
        if variable_selector.startswith(USER_INPUT_NODE_ID):
            # Get user input
            variable_name = variable_selector.split(".", 1)[1]
            return self.get_user_input(variable_name)

        # Check if variable selector is environment variable
        if variable_selector.startswith(ENVIRONMENT_VARIABLE_NODE_ID):
            # Get environment variable
            variable_name = variable_selector.split(".", 1)[1]
            return self.get_environment_variable(variable_name)

        # Check if variable selector is session variable
        if variable_selector.startswith(SESSION_VARIABLE_NODE_ID):
            # Get session variable
            variable_name = variable_selector.split(".", 1)[1]
            return self.get_session_variable(variable_name)

        # Get variable from variable dictionary
        return self.variable_dictionary.get(variable_selector)

8.2 变量传递

变量在节点之间的传递遵循以下规则:

  1. 变量选择器:通过变量选择器指定要使用的变量

  2. 变量作用域:变量的作用域为整个工作流

  3. 变量覆盖:后执行的节点可以覆盖先执行的节点的变量

变量选择器的格式为 node_id.variable_name,例如:

  • system.conversation_id:系统变量中的会话ID

  • user_input.query:用户输入中的查询

  • node_1.result:节点1的输出变量 result

9. 并行执行机制

工作流支持并行执行多个分支,通过 GraphEngineThreadPool 实现:

图片

Dify 工作流支持并行执行多个分支:

  1. 通过 GraphParallel 模型定义并行分支

  2. 使用 parallel_mapping 和 node_parallel_mapping 管理并行关系

  3. 支持条件分支,根据条件选择执行路径

  4. 限制并行层级,避免过度复杂的执行图

十、总结

Dify 工作流系统是一个功能强大的可视化 AI 工作流引擎,通过图结构组织节点执行,使用变量池管理数据流动,支持多种节点类型、错误处理和并行执行。系统的核心组件包括:

  1. 工作流服务:管理工作流的生命周期

  2. 工作流入口:工作流执行的入口点

  3. 图引擎:负责节点的调度和执行

  4. 变量池:管理工作流中的变量

  5. 节点实现:各类节点的具体实现

通过这些组件的协同工作,Dify 工作流系统能够支持从简单到复杂的 AI 应用场景,为用户提供灵活且强大的工作流设计和执行能力。

  这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

一、大模型风口已至:月薪30K+的AI岗正在批量诞生

2025年大模型应用呈现爆发式增长,根据工信部最新数据:

国内大模型相关岗位缺口达47万

初级工程师平均薪资28K

70%企业存在"能用模型不会调优"的痛点

真实案例:某二本机械专业学员,通过4个月系统学习,成功拿到某AI医疗公司大模型优化岗offer,薪资直接翻3倍!

二、如何学习大模型 AI ?


🔥AI取代的不是人类,而是不会用AI的人!麦肯锡最新报告显示:掌握AI工具的从业者生产效率提升47%,薪资溢价达34%!🚀

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

1️⃣ 提示词工程:把ChatGPT从玩具变成生产工具
2️⃣ RAG系统:让大模型精准输出行业知识
3️⃣ 智能体开发:用AutoGPT打造24小时数字员工

📦熬了三个大夜整理的《AI进化工具包》送你:
✔️ 大厂内部LLM落地手册(含58个真实案例)
✔️ 提示词设计模板库(覆盖12大应用场景)
✔️ 私藏学习路径图(0基础到项目实战仅需90天)

 

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

*   大模型 AI 能干什么?
*   大模型是怎样获得「智能」的?
*   用好 AI 的核心心法
*   大模型应用业务架构
*   大模型应用技术架构
*   代码示例:向 GPT-3.5 灌入新知识
*   提示工程的意义和核心思想
*   Prompt 典型构成
*   指令调优方法论
*   思维链和思维树
*   Prompt 攻击和防范
*   …

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

*   为什么要做 RAG
*   搭建一个简单的 ChatPDF
*   检索的基础概念
*   什么是向量表示(Embeddings)
*   向量数据库与向量检索
*   基于向量检索的 RAG
*   搭建 RAG 系统的扩展知识
*   混合检索与 RAG-Fusion 简介
*   向量模型本地部署
*   …

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

*   为什么要做 RAG
*   什么是模型
*   什么是模型训练
*   求解器 & 损失函数简介
*   小实验2:手写一个简单的神经网络并训练它
*   什么是训练/预训练/微调/轻量化微调
*   Transformer结构简介
*   轻量化微调
*   实验数据集的构建
*   …

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

*   硬件选型
*   带你了解全球大模型
*   使用国产大模型服务
*   搭建 OpenAI 代理
*   热身:基于阿里云 PAI 部署 Stable Diffusion
*   在本地计算机运行大模型
*   大模型的私有化部署
*   基于 vLLM 部署大模型
*   案例:如何优雅地在阿里云私有部署开源大模型
*   部署一套开源 LLM 项目
*   内容安全
*   互联网信息服务算法备案
*   …

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

<think>好的,用户现在想了解Dify节点的相关信息或配置,特别是节点的配置和使用方法。让我先回顾一下之前的对话历史和提供的参考资料。用户之前询问了如何下载Dify源码,现在转向节点的具体配置和使用。 首先,用户提供的引用中提到了零基础AI开发系列教程中的Dify节点详细介绍,分为上、中、下三部分,这可能涉及节点的类型、功能以及具体操作步骤。另外,引用[2]提到了HTTP请求节点的认证方式,引用[3]则讨论了变量聚合节点的作用。这些信息都是关键点,需要整合到回答中。 用户的具体问题是如何配置和使用Dify节点,因此需要分步骤解释,包括节点类型、配置步骤、常见节点的操作示例,以及注意事项。可能需要结合之前的回答中提到的源码结构和相关模块,如蓝图控制器,来增强回答的专业性。 需要注意的是,用户可能对Dify工作流设计不太熟悉,因此需要从基础开始解释,例如节点的概念、如何添加到工作流、配置参数等。同时,结合引用中的HTTP节点和变量聚合节点作为例子,能够具体说明配置过程,帮助用户理解。 另外,用户之前的问题涉及源码下载和依赖项,因此在回答节点配置时,可能需要提到环境准备,比如确保Docker和相关服务已正确安装,这部分在之前的回答中已经提到过,可以引用之前的建议作为参考。 还需要考虑用户可能的后续问题,比如遇到配置错误如何处理,或如何调试节点,所以应在注意事项中涵盖这些方面,或者生成相关问题引导用户进一步探索。 最后,确保回答结构清晰,分点说明,并正确引用提供的资料,比如引用[1]的教程系列,引用[2]的HTTP节点认证,引用[3]的变量聚合节点功能。同时,使用中文,符合格式要求,行内公式用$...$,独立公式用$$...$$,但可能这里公式较少,重点在配置步骤和代码示例。</think>### Dify 节点配置与使用详解 Dify 的节点是工作流设计的核心组件,用于实现数据处理、逻辑控制和结果聚合等功能。以下是节点配置与使用的关键要点及操作示例: --- #### 一、节点基础配置 1. **节点类型与功能** - **输入节点**:接收外部数据(如用户输入、API请求) - **处理节点**:执行逻辑操作(如LLM调用、条件分支、变量聚合[^3]) - **输出节点**:返回最终结果(如API响应、文件生成) 2. **通用配置步骤** ```bash # 示例:通过Dify界面配置节点的基本流程 1. 创建工作流 -> 拖拽节点至画布 -> 连接节点 2. 双击节点 -> 填写参数 -> 保存配置 ``` - **必填字段**:节点名称、输入/输出变量映射 - **动态参数**:支持通过 `{{variable}}` 语法引用上下文变量 --- #### 二、常见节点操作示例 1. **HTTP请求节点** *(适用于调用外部API)* - **认证配置**: 使用Bearer Token认证时,需在请求头添加: ```http Authorization: Bearer {{your_api_key}}[^2] ``` - **参数映射**: 将上游节点的输出变量(如 `{{query_result}}`)映射到请求体参数 2. **变量聚合节点** *(整合多分支结果)* ```yaml # 配置示例:聚合分类结果 branches: - path: A output_vars: {category: "科技"} - path: B output_vars: {category: "生活"} aggregated_var: unified_category[^3] ``` - **作用**:将不同分支的 `category` 变量统一映射为 `unified_category` 3. **LLM调用节点** *(核心AI处理单元)* - **模型选择**:支持本地部署模型(如DeepSeek)或云端API - **提示词模板**: 使用动态变量增强灵活性: ```python "请根据{{user_input}}生成摘要,限制{{max_length}}字" ``` --- #### 三、高级配置技巧 1. **条件分支控制** ```python # 通过表达式实现动态路由 if {{input.score}} > 0.7: route_to = "positive_feedback" else: route_to = "default_process" ``` 2. **错误处理机制** - **重试策略**:配置HTTP节点的超时时间与重试次数 - **异常捕获**:添加备用处理节点应对API调用失败 3. **性能优化** - **并行执行**:对无依赖关系的节点启用并行处理 - **缓存设置**:为LLM节点配置结果缓存(需启动Redis服务[^1]) --- #### 四、注意事项 1. **环境依赖** 确保已部署 PostgreSQL(数据库)、Redis(缓存)、Weaviate(向量搜索)等基础设施[^1] 2. **调试工具** 使用工作流的 **"测试运行"** 功能实时观察变量传递状态 3. **版本兼容** 升级Dify版本时需检查节点配置变更(参考《Dify升级指南》[^1])
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值