PythonTypeScript工作流与业务工具

项目目录

项目简介

Dify 是搭建和发布 AI 应用的平台,可在可视化流程中组合模型、知识检索、条件分支和工具,支持人工输入、API 调用以及运行记录查询。

如何写进简历

售后通 · 家电电商售后协办平台 | 场景化示例

项目描述:面向家电电商客服,结合订单、故障描述与售后凭证检索适用政策,生成维修、退换或补料建议。客服修改确认后继续办理,并跟踪工单回写结果。

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

主要工作:

  • 人工审批与断点续办:基于 Dify 运行快照保存变量与未完成节点,将客服修订持久化为人工输入,通过 Celery 恢复原运行,支持售后审核跨请求暂停、修改与继续执行。
  • 工单幂等与回执一致性:以售后单号、处置版本和动作类型构造稳定业务键,通过唯一约束和办理状态拦截并发重试;保存工单回执,超时结果未知时先查询原动作,避免重复创建维修或退换单。
  • 流式续接与任务状态管理:以 workflow_run_id 关联事件和运行结果,保留 task_id 用于停止请求;断线后续接原运行或查询最终状态,区分等待审核、执行失败与流程完成,避免通过重跑流程恢复进度。

面试题与回答

  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 推导出来。