Dify

可视化 AI 应用与工作流开发平台
2 份简历参考3 道面试问答
反馈(在 GitHub 新标签页打开)

主要技术:Flask、Dify、PostgreSQL、Redis、Celery

项目目录

项目简介

Dify 是开源 AI 应用开发平台:在画布中连接知识检索、模型、条件分支和工具,把一次问答扩展为可执行的多步任务,再发布为应用或供业务系统调用的接口。它同时提供知识库管理、模型接入与运行记录,应用开发者可以在同一处配置流程并查看节点结果。

例如,用户提交材料后,先检索参考资料,再生成草稿,进入人工输入节点等待修改,最后根据所选动作继续处理。前端使用 TypeScript,后端以 Python、Flask 提供应用接口,由 Graphon 执行工作流;PostgreSQL 保存应用与运行数据,Redis、Celery 承接异步任务。

项目亮点

  • 节点之间传递明确的输入输出。 检索词、附件和上游结果通过变量选择器读取,节点将结果写回变量池;同一份资料可以交给不同模型节点处理,再由后续节点汇总,流程依赖能落到具体字段。
  • 检索策略随应用配置。 知识检索节点支持资料范围、元数据条件、候选数量和分数阈值,并可选择模型重排或关键词与向量加权,让资料筛选与后续生成分别调节。
  • 人工介入保留原运行。 人工输入节点将草稿、待填字段和动作保存为表单,暂停时序列化 RuntimeState;提交后通过 Celery 恢复变量池和未完成执行,后续节点直接读取修改值及所选动作。
  • 提交、运行和结果分别建模。 表单通过行锁控制 WAITING 到 SUBMITTED 的转换,应用接口提供阻塞结果、SSE 事件及原运行查询。表单提交、暂停恢复与执行结果分别留有状态,便于追踪人工等待和后续处理。

技术架构

web/ 负责应用配置、画布和运行展示。执行请求进入 Flask 服务后,应用运行器把工作流配置、输入和变量池交给 Graphon;知识检索、模型和工具节点按依赖读取变量、返回输出,运行记录和事件再回到应用界面或调用方。

人工输入将执行分成两次请求:先保存表单与暂停现场,用户提交后再投递恢复任务。恢复入口读取原快照并检查暂停状态,沿原运行继续执行。

代码位置 职责
web/app/components/workflow/ 工作流画布、节点配置与运行展示
api/core/app/apps/workflow/app_runner.py、api/core/workflow/workflow_entry.py 组装运行输入、变量池和工作流引擎
api/core/workflow/nodes/knowledge_retrieval/ 读取查询变量、筛选资料并输出检索片段
api/services/human_input_service.py、api/core/repositories/human_input_repository.py 人工表单校验、提交状态与恢复调度
api/tasks/app_generate/workflow_execute_task.py、api/controllers/service_api/app/workflow_events.py 恢复原运行与续接执行事件

如何写进简历

售后通 · 家电售后方案协办助手 | 电商业场景示例

项目描述:面向家电电商客服的售后方案协办系统,结合订单信息、故障描述和售后政策生成维修、退换或补料建议,支持人工修订并输出确认版方案。

技术栈:Python、Flask、Dify、PostgreSQL、Redis、Celery

主要工作:

  • 工作流依赖与变量传递:基于 Graphon 工作流引擎串联政策检索、建议生成和补料分支,以变量选择器绑定节点输入输出;将订单条件、检索依据与处置草稿存入运行变量,按依赖传递给后续节点,衔接多步骤售后协办。
  • 政策检索与证据约束:按品牌、商品和生效范围筛选政策资料,采用关键词与向量权重或模型重排选取候选;将政策片段与订单、故障条件分别交给模型,生成带依据的维修、退换或补料建议。
  • 人工审核与原运行恢复:利用人工输入节点收集客服修订与处理动作,将 RuntimeState 和变量池序列化到暂停快照;审核后通过 Celery 恢复原运行,让后续节点读取已确认字段,衔接跨请求的审核与方案生成。
  • 表单提交的并发状态控制:基于表单有效期、动作及字段校验处理审核输入,在事务内锁定表单,只允许 WAITING 转为 SUBMITTED;将提交值和所选动作回传原运行,避免同一审核表单形成多份确认结果。

文案搭档 · 品牌营销文案协作助手 | 广告营销业场景示例

项目描述:面向品牌营销团队的文案协作系统,结合商品资料、品牌规范与渠道要求生成草稿,支持人工编辑、审核和多渠道交付稿汇总。

技术栈:Python、Flask、Dify、PostgreSQL、Redis、Celery

主要工作:

  • 多渠道生成与变量依赖:基于 Graphon 将商品事实、品牌规范和活动要求组织为共享变量,再按渠道衔接模型与汇总节点;分别管理节点配置和运行变量,让各渠道复用同一组输入,并将草稿传入人工编辑步骤。
  • 品牌资料检索与事实输入约束:通过元数据条件限定商品、活动和资料范围,以混合检索及可选重排筛选产品事实与品牌规范;将事实依据和写作要求分开输入模型,结合渠道字段与长度约束组织文案,减少跨商品材料混用。
  • 流式结果聚合与暂停恢复:利用 ResponseStreamFilter 按节点输出依赖组织文案片段,人工编辑暂停时同步保存流过滤器和 RuntimeState 快照;恢复后衔接原运行的变量与分段输出,处理多渠道生成和审核跨请求的状态延续。
  • 编辑提交与确认结果一致性:校验编辑动作、必填字段与有效期,在事务中锁定待提交记录,仅允许 WAITING 状态提交;将修订内容和确认动作保存后发布回原运行,使汇总节点读取同一份编辑确认稿。

面试题与回答

3 题
  1. 如何在 Agent 系统中设计 Human-in-the-loop 人工介入能力?

    展开收起

    核心回答

    在 Dify 中,可以用人工输入节点把工作流分成“准备内容、等待人处理、根据决定继续执行”三段。节点展示上游草稿和待填字段,后端保存表单与运行现场;用户提交有效输入后,服务端记录选择和修改内容,再投递恢复任务,让后续节点读取这些结果并走对应分支。

    这里需要区分表单已提交、工作流已恢复、业务动作已完成。表单状态检查能阻止重复提交,暂停记录能限制重复恢复,但都不直接保证下游接口只执行一次。

    介入点怎样设置,输入输出是什么

    适合介入的位置包括缺少必要字段、需要审阅模型草稿,以及执行外部写入之前。以一个工作流配置示例说明:模型生成草稿后进入人工输入节点,展示草稿并允许修改,提供“确认”和“退回”两个动作;确认后将修改内容交给下一节点,退回则进入另一条处理分支。

    Dify 的人工输入回调会用变量池渲染表单内容、解析默认值,并把表单关联到节点及本次运行。尚未提交时返回 PauseRequested;恢复后读取保存的提交值,构造节点输出,并以所选动作的标识作为 selected_handle。因此后续分支消费的是明确的字段和动作,而非从一句聊天中的“同意”猜测意图。表单创建与恢复输出。

    等待期间保存什么

    当前 Python 后端通过 Flask 提供接口,由 Graphon 执行图。收到暂停事件时,WorkflowRunAgg 将运行参数、序列化的 RuntimeState、累计耗时和响应流过滤状态组织成 WorkflowResumptionContext,保存未完成节点及运行状态;暂停上下文写入配置的存储,数据库中的 WorkflowPause 记录其存储键及暂停原因。暂停现场保存、暂停记录持久化。

    这样人工处理可以与原来那次请求分开,恢复时按运行记录找回现场,无需让用户重新提交原始任务。它也不是在服务进程中一直阻塞等待一个输入框。

    提交以后怎样继续

    服务端先检查表单是否仍可提交、有无过期,再校验动作和输入字段。mark_submitted() 在事务内锁住表单行,只允许从 WAITING 变为 SUBMITTED,同时保存动作、输入、提交者及时间;并发或重复提交会收到已处理错误。提交校验与调度、表单状态原子更新。

    对于运行中的人工输入表单,服务随后按 workflow_run_id 投递 Celery 恢复任务。消费者读取暂停上下文,用 RuntimeState.from_snapshot() 重建运行现场;在带行锁的恢复事务中检查运行仍为 PAUSED、未请求停止、暂停记录匹配且尚未恢复,再标记为运行中,调用对应的工作流或对话流恢复入口。恢复任务、恢复状态转换。

    取舍与重复执行边界

    人工输入节点让等待、表单和分支成为明确流程,代价是要处理过期、恢复失败和业务结果跟踪。当前提交表单与投递恢复任务之间存在失败窗口:enqueue_resume() 捕获入队异常后只记录日志,表单可能已经提交而工作流还没继续,不能仅凭提交接口成功就显示业务办理完成。

    若接入发邮件、创建订单等写入工具,可在业务侧增加动作标识、幂等处理和回执核对,并为已提交但未继续的运行设计补偿入口。这些需要结合具体业务实现;已有的表单防重与恢复状态转换,不能代替业务动作的防重复执行。

    参考资料

  2. 工具调用如何保证幂等,避免模型重试导致重复副作用?

    展开收起

    核心回答

    以“售后通”创建维修、退换工单的场景化接入设计为例:由业务服务保存稳定的办理标识,用数据库唯一约束和办理状态处理并发重试,成功时返回原工单回执,结果未知时先查询原动作。 Dify 负责流程编排与人工审批,工单系统负责实际写入;两边需要共享业务标识,不能把工作流恢复当成工单已经幂等。

    例如客服确认一次维修申请后,模型重试、接口超时重试和工作流续办都应指向同一次办理。客服明确改成换货则是新的处置动作,在业务允许变更的状态下应形成新的处置版本。能否改处置仍需校验工单状态,幂等键不代替业务状态约束。

    哪个标识代表同一次办理

    接入服务在客服确认处置时固定租户、售后单号、处置版本和动作类型,作为业务键;同时保存规范化参数摘要,包含处理方式、目标商品等影响办理的字段。同键且参数一致才视为重试,同键却改了参数应拒绝并要求建立新处置版本。

    workflow_run_id 关联流程运行,task_id 用于停止执行,模型的工具调用 ID 关联一次调用与返回。它们都不能替代跨重试的业务键;即使同一次办理被重新受理为另一个流程运行,也应查回原来的办理记录。业务键与处置版本由可信服务维护,不能每次让模型重新生成。

    并发请求怎样只推进同一次动作

    在业务数据库中,为业务键建立唯一约束,保存参数摘要、办理状态及工单回执。不要采用“先查不存在,再发起办理”的非原子流程:两个请求可能同时通过检查。

    第一次请求通过插入或条件更新领取办理权。重复请求读取已有记录:成功则返回同一张工单;处理中则返回进度;只有确认尚未产生副作用、符合重试约定的失败才重新执行。同一数据库内的工单写入和结果登记可以放在同一个事务里提交,避免工单已创建却没有留下成功记录。

    外部工单成功,本地却超时怎么办

    调用外部工单系统前,先持久化办理记录,并向下游透传同一业务键。若下游已经创建工单,但响应丢失或本地保存回执前进程退出,应将结果标为待确认,按原业务键查询或对账,确认后补写回执。

    下游支持幂等时,按它的键范围、有效期和参数比较规则使用原键重试;不支持时,需依靠它的业务唯一号或查询能力核对。没有可核对的结果,就保留待确认状态并交由人工处理,不能换键重发。“没收到成功”与“确定没有办理”是两种状态。

    工作流恢复怎样与业务状态衔接

    Dify 的人工表单提交会锁定表单记录,恢复事务也会检查暂停状态;这些保护的是表单和流程状态。当前实现仍存在表单已提交、恢复任务尚未成功入队的窗口,见提交与恢复调度。接入层应按原运行补查、续办,并让恢复后的写工具继续使用原业务键。

    验证时重点覆盖:同键并发只得到同一张工单、同键不同参数被拒绝、下游成功后响应丢失仍能查回原工单,以及重复恢复不会新建第二张工单。流程结束和工单办理结果分别核对,以实际业务回执确认完成。

    参考资料

  3. 用 Python 调用 Dify 或 Coze 工作流时,应关注哪些工程细节?

    展开收起

    核心回答

    接入 Dify 时,由 Python 业务服务持有应用密钥,调用已发布工作流,把开始节点输入映射到 inputs,再分别处理阻塞结果或 SSE 事件。Dify 的 Flask 接口把执行请求交给应用生成服务,工作流节点由 graphon 引擎运行;这里不是把 LangGraph 当成 Dify 的执行器。

    接入层最关键的是分清两类标识:workflow_run_id 用于查询这次运行,task_id 用于请求停止任务。它们都不是业务单号或幂等键。流式接入应尽早保存这些标识,以最终状态和输出完成业务记录;断线后查询或显式续接原运行,不重复提交同一次业务。

    从请求到结果

    向 POST /v1/workflows/run 发送应用级 Bearer 凭据,请求包含 inputs、response_mode 和稳定的 user。例如工作流的开始节点定义了 target,才在 inputs 中传入这个名字;换成另一个工作流时,应重新核对输入定义。user 表示终端用户,同一用户的不同执行可以使用同一个值,它不负责请求去重。

    response_mode=blocking 等待返回结果;response_mode=streaming 返回 text/event-stream。接收 workflow_started 时保存运行标识与任务标识,节点事件可用于展示执行进度;接收 workflow_finished 后检查 data.status,成功时再按约定读取 data.outputs,失败时保存 error。不能只凭 HTTP 200 或出现过节点输出就认定整个流程成功;遇到人工输入暂停,也应保留等待状态,而不是把连接结束记为失败。

    固定版本的执行入口明确区分两种响应模式;流事件数据结构分别提供运行开始和运行完成的信息。

    断线、续接与停止

    已知 workflow_run_id 时,使用 GET /v1/workflows/run/{workflow_run_id} 查询运行状态、输出和错误。这个查询用于确认结果,不是重新执行流程;若请求在收到运行标识前就断开,不能声称已经能可靠查询原运行,需要结合平台日志或业务结果进一步核对。

    这个版本还提供 GET /v1/workflow/{workflow_run_id}/events,由客户端使用原 user 主动续接事件流。include_state_snapshot=true 可先返回已执行节点的状态摘要;运行已结束时会发送完成事件。这不等于自动重连,也不是按事件游标逐条补齐所有历史事件。 接入层仍应以运行状态和最终输出确认结果,不能通过再次调用执行接口来“恢复连接”。具体约定见事件续接入口。

    需要停止时,调用 POST /v1/workflows/tasks/{task_id}/stop,按接口要求传入 user,不能把 workflow_run_id 填进停止地址。停止接口调用任务停止服务;运行中的任务通过停止标记与引擎中止命令处理。停止请求返回成功,不应解释成已经撤销此前的业务写入,外部接口的办理结果仍需单独确认。

    接入层怎样验证

    先用一个输入输出明确的小流程确认字段映射,再覆盖成功、节点失败、人工暂停、收到运行标识后断线和停止请求等情况。若流程包含业务写入,还应模拟“下游已办理,但调用方没收到结果”后再次提交,核对是否重复办理;这部分保证依赖业务接口和接入层设计,不能由 Dify 的运行 ID 推导出来。