ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Conductor DO_WHILE 循环任务指南:计数器循环、列表迭代与 keepLastN 迭代清理

Conductor DO_WHILE 循环任务指南:计数器循环、列表迭代与 keepLastN 迭代清理 Conductor DO_WHILE 循环任务指南计数器循环、列表迭代与 keepLastN 迭代清理【免费下载链接】conductorConductor is an event driven agentic workflow engine providing durable and highly resilient execution engine for applications and AI Agents项目地址: https://gitcode.com/GitHub_Trending/co/conductorConductor 的 DO_WHILE 任务让你在无需编写 Worker 的情况下直接在 Workflow 定义中实现先执行、后判断的循环控制流。本文基于仓库中的 DO_WHILE 任务文档与core模块源码系统讲解其任务参数、loopCondition条件表达式、items列表迭代模式、keepLastN迭代清理机制的完整配置方式并深入源码剖析每轮迭代的调度、输出绑定与失败处理逻辑读完后可直接写出可运行的循环型 Workflow 定义。DO_WHILE 任务概述在 Workflow 定义中通过如下声明使用 Do While 任务type : DO_WHILEDo While 任务DO_WHILE会顺序执行一组任务只要给定条件为 true 就持续循环。与编程语言中的do..while语句一致循环体内的任务序列先执行之后才检查条件——即使是第一次迭代也是如此。从源码结构看其实现位于 DoWhile 类它继承自WorkflowSystemTask并以Component(TASK_TYPE_DO_WHILE)注册为系统任务。当 Workflow 决策器Decider需要调度 DO_WHILE 节点时DoWhileTaskMapper 负责把WorkflowTask映射为TaskModel初始状态为IN_PROGRESS并预先解析inputParameters见 DoWhileTaskMapper.java#L63-L101。若同名的 DO_WHILE 任务已处于终态Mapper 直接返回空列表不会重复调度。任务参数Task parameters以下参数配置在 Do While 任务的顶层参数类型说明必填性loopConditionString每次迭代后求值的条件表达式是一个 JavaScript 表达式。当使用items进行列表迭代时该参数可选。计数器迭代时必填列表迭代时可选。loopOverList[Task]条件为 true 期间将被执行的 Task 配置列表。必填。itemsString求值为列表/数组的 Workflow 表达式如${workflow.input.myList}。指定后循环自动逐项迭代无需loopCondition。循环体任务可通过${do_while_ref.output.loopItem}访问当前项通过${do_while_ref.output.loopIndex}访问从零开始的索引。可选。items字段在元数据模型中定义为WorkflowTask的一个独立属性见 WorkflowTask.java#L139-L142其注释明确说明取值可以是${workflow.input.myList}这样的 Workflow 表达式或直接引用。输入参数Input parameters以下参数配置在 Do While 任务的inputParameters段中参数类型说明必填性keepLastNInteger在数据库和任务输出中保留的最近迭代数量更早的迭代会被自动移除防止数据库膨胀。未指定时保留全部迭代默认行为。适用于迭代次数很多的长时循环。最小值1。可选。源码中对keepLastN的读取方式印证了这一点——它在每轮迭代完成时从WorkflowTask的inputParameters中取出整数值见 DoWhile.java#L133-L145属于选择性开启opt-in行为只有显式设置才会触发清理。JSON 配置示例计数器迭代counter-based iteration{ name: do_while, taskReferenceName: do_while_ref, inputParameters: { keepLastN: 10 }, type: DO_WHILE, loopCondition: (function () {\n if ($.do_while_ref[iteration] 5) {\n return true;\n }\n return false;\n})();, loopOver: [ { name: first_task, taskReferenceName: first_task_ref, type: SIMPLE }, { name: second_task, taskReferenceName: second_task_ref, type: SIMPLE } ] }列表迭代list iteration{ name: do_while, taskReferenceName: do_while_ref, type: DO_WHILE, items: ${workflow.input.myList}, loopOver: [ { name: process_task, taskReferenceName: process_ref, type: SIMPLE, inputParameters: { item: ${do_while_ref.output.loopItem}, index: ${do_while_ref.output.loopIndex} } } ] }在列表迭代模式下loopItem与loopIndex由引擎在调度每一轮迭代前注入到 DO_WHILE 任务的输出中。对应源码是 injectLoopVariablesloopIndex恒为当前迭代号 - 1从 0 开始仅当索引落在列表范围内时才注入loopItem越界时记录告警日志。任务输出OutputDo While 任务返回以下参数名称类型说明iterationInteger迭代次数。任务执行中时iteration显示当前迭代号完成后显示最终迭代数。loopItemAny仅列表迭代本次迭代对应的items列表当前项使用items参数时可用。loopIndexInteger仅列表迭代当前项的从零开始索引0, 1, 2, ...使用items参数时可用。此外每一轮迭代都会生成一个以迭代号如 1、2、3为键的 map其中包含该轮所有loopOver任务的输出。这一结构由 DoWhile.java#L131 中的addOutput(String.valueOf(iteration), output)直接构造output中每个循环体任务以其去除迭代后缀的taskReferenceName为键存放其outputData。在 loopCondition 中读取状态循环条件在求值时会收到如下绑定的输入循环任务自身的输出绑定在其taskReferenceName下当前轮中每个循环体任务直接绑定在其各自的taskReferenceName下即任务的输出数据 map不再额外包一层output对象。// loop 是 DO_WHILE 的引用check 是循环体中某个任务的引用。 if ($.check[done] true || $.loop[iteration] 10) { false; } else { true; }这一绑定关系在源码中可以得到精确印证evaluateCondition 先把 DO_WHILE 任务自身的outputData放入conditionInput的referenceTaskName键下再把每个循环体任务去掉迭代后缀的引用名映射为其outputData。在列表迭代模式下还会额外注入loopIndex与loopItem供条件表达式使用。需要注意的是上述直接绑定只在求值loopCondition的瞬间生效。任务完成后各轮迭代数据仍保留在数字键如loop.output.1.check之下。执行机制ExecutionDo While 循环执行时循环内每个任务的taskReferenceName都会拼接上__i后缀其中i为从 1 开始的迭代号。这一命名规则由 TaskUtils 实现分隔符为__appendIteration负责追加、removeIterationFromTaskRefName负责还原。例如first_task在第 3 轮对应的任务引用名为first_task__3。结合源码可以进一步确认执行时序首轮调度若当前尚无循环任务被调度loopOverTasks为空引擎先检查列表迭代场景下items求值结果是否为空——空列表会直接以iteration: 0标记成功完成不执行任何循环任务见 DoWhile.java#L95-L116否则将迭代号置为 1、注入loopItem/loopIndex并调用scheduleNextIteration调度第一轮。失败处理某一轮内任一循环任务失败Do While 任务状态被置为FAILED失败原因由markTaskFailure写入见 DoWhile.java#L118-L154。由于引擎收集的是重试次数最大的任务实例任务重试后迭代号会从 1 重新开始。条件求值异常若loopCondition求值抛出异常任务被标记为FAILED_WITH_TERMINAL_ERROR并附带异常信息见 DoWhile.java#L185-L193这属于终态失败不会重试。轮次推进守卫isIterationComplete 除了要求loopOver中直接的任务都达到终态还会检查分支层级如 Switch 子分支内的任务是否全部终态并要求已终态任务的后继任务必须已被调度——这是为了防止内联同步任务先于后继任务完成时轮次被提前推进。每个循环任务的输出都作为 Do While 任务的一部分按迭代值索引存储因此loopCondition可以引用特定迭代中某任务的输出例如$.LoopTask[iteration][first_task]。迭代清理keepLastN对于迭代次数很多的 Do While 循环例如 100 次存储全部迭代数据可能导致数据库膨胀、内存耗尽和性能下降。keepLastN输入参数提供了对旧迭代的自动清理。工作原理当inputParameters中指定了keepLastN时一旦迭代数超过keepLastNConductor 会自动从数据库和任务输出两处移除旧迭代数据。以keepLastN: 5为例第 1–5 轮全部保留第 6 轮移除第 1 轮保留第 2–6 轮第 7 轮移除第 2 轮保留第 3–7 轮依此类推……源码级实现清理分为两步见 DoWhile.java#L133-L145输出清理遍历1到当前迭代号 - keepLastN逐一removeOutput对应的数字键数据库清理removeIterations 筛出属于本循环且iteration 当前迭代号 - keepLastN的全部任务通过executionDAOFacade.removeTask(taskId)逐个从持久层删除单个任务删除失败不会中断整体流程记录错误日志后继续。单元测试 DoWhileTest 验证了清理边界例如迭代 3、keepLastN5 时不删除任何键、迭代 5、keepLastN5 时同样不删除、迭代 5、keepLastN2 时删除键 1/2/3、保留 4/5等场景e2e 层另有 do_while_keep_last_n_fix.json 等工作流定义支撑集成验证。重要注意事项选择性开启只有显式设置keepLastN才会触发清理不设置则保留全部迭代默认行为。向后兼容不含keepLastN的既有 Workflow 行为不变。输出数据任务输出中只能访问最近 N 轮迭代更早的迭代被永久移除。循环条件使用keepLastN时请确保loopCondition只引用最近的迭代因为旧迭代数据已不可用。最佳实践预期 100 次迭代的循环建议将keepLastN设为合理值如 5–10在内存占用与历史迭代数据的可访问性之间取得平衡若loopCondition需要引用较早的迭代把keepLastN设得足够大。带清理的示例{ name: long_running_loop, taskReferenceName: long_running_loop_ref, inputParameters: { keepLastN: 5 }, type: DO_WHILE, loopCondition: if ($.long_running_loop_ref[iteration] 1000) { true; } else { false; }, loopOver: [ { name: process_item, taskReferenceName: process_item_ref, type: SIMPLE } ] }在这个例子中即使循环运行 1000 次任意时刻数据库与任务输出中只保留最近 5 轮迭代从而避免数据库膨胀。测试工程中还提供了一份可直接查看的演示定义 do_while_cleanup_demo.json。实战示例列表迭代简化写法当你有一组待处理项时用items参数可以获得无需手动管理计数器的简洁写法{ name: process_items, taskReferenceName: process_items_ref, type: DO_WHILE, items: ${workflow.input.itemList}, loopOver: [ { name: http, taskReferenceName: http_ref, inputParameters: { http_request: { uri: https://api.example.com/process, method: POST, body: { item: ${process_items_ref.output.loopItem}, index: ${process_items_ref.output.loopIndex} } } }, type: HTTP } ] }在这个示例中循环自动遍历workflow.input.itemList的每一项loopItem为当前项第一轮取itemList[0]loopIndex为从零开始的索引0, 1, 2, ...无需loopCondition——所有项处理完循环自动停止若输入列表为空[]Do While 任务会立即完成不执行任何循环任务。源码侧的取值逻辑见 evaluateItemsList结果若是List/Collection/数组直接使用若是单个非空值则包装为单元素列表无法求值时按空列表处理即空列表立即完成的语义来源。列表模式下是否继续的实际判定是loopIndex itemsList.size() - 1见 DoWhile.java#L540-L578。列表迭代 提前终止条件将items与loopCondition组合可以实现提前退出{ name: process_until_error, taskReferenceName: process_ref, type: DO_WHILE, items: ${workflow.input.tasks}, loopCondition: $.http_ref[response][status] success, loopOver: [ { name: http, taskReferenceName: http_ref, type: HTTP, inputParameters: { http_request: { uri: ${process_ref.output.loopItem.url}, method: GET } } } ] }该循环会在所有项处理完毕或HTTP 响应状态不是 success 时停止。对应实现中列表模式且存在loopCondition时最终判定为conditionResult hasMoreItems——条件为真且还有下一项才会继续。计数器迭代基础示例在下面的任务配置中Do While 任务综合评估两个条件迭代数与循环体任务输出{ name: Loop, taskReferenceName: LoopTask, type: DO_WHILE, inputParameters: { value: ${workflow.input.value} }, loopCondition: if ( ($.LoopTask[iteration] $.value ) || ( $.first_task[response][body] 10)) { false; } else { true; }, loopOver: [ { name: firstTask, taskReferenceName: first_task, inputParameters: { http_request: { uri: http://localhost:8082, method: POST } }, type: HTTP }, { name: secondTask, taskReferenceName: second_task, inputParameters: { http_request: { uri: http://localhost:8082, method: POST } }, type: HTTP } ], startDelay: 0, optional: false }假设执行了三轮即first_task__1、first_task__2、first_task__3与second_task__1、second_task__2、second_task__3Do While 任务将返回如下输出{ iteration: 3, 1: { first_task: { response: {}, headers: { Content-Type: application/json } }, second_task: { response: {}, headers: { Content-Type: application/json } } }, 2: { first_task: { response: {}, headers: { Content-Type: application/json } }, second_task: { response: {}, headers: { Content-Type: application/json } } }, 3: { first_task: { response: {}, headers: { Content-Type: application/json } }, second_task: { response: {}, headers: { Content-Type: application/json } } } }在循环任务中使用迭代号分页拉取有时需要在循环任务内部使用迭代值/计数器。本例调用 GitHub API 拉取全部 stargazers每轮迭代递增分页页码loopCondition中使用$.get_all_stars_loop_ref[iteration]判断是否继续循环内嵌的 HTTP 任务中使用${get_all_stars_loop_ref.output.iteration}指定 API 应返回的页码。{ name: get_all_stars, taskReferenceName: get_all_stars_loop_ref, inputParameters: { stargazers: 4000 }, type: DO_WHILE, loopCondition: if ($.get_all_stars_loop_ref[iteration] Math.ceil($.stargazers/100)) { true; } else { false; }, loopOver: [ { name: 100_stargazers, taskReferenceName: hundred_stargazers_ref, inputParameters: { counter: ${get_all_stars_loop_ref.output.iteration}, http_request: { uri: https://api.github.com/repos/ntflix/conductor/stargazers?page${get_all_stars_loop_ref.output.iteration}per_page100, method: GET, headers: { Authorization: token ${workflow.input.gh_token}, Accept: application/vnd.github.v3.starjson } } }, type: HTTP } ] }这类以输出中的iteration驱动分页的写法正是keepLastN的典型应用场景之一——拉取数千条分页数据时保留全部中间迭代会显著放大任务输出体积。Orkes Conductor 兼容性_items为兼容从 Orkes Conductor 迁移而来的工作流inputParameters中的_items参数同样被支持{ name: do_while, taskReferenceName: do_while_ref, type: DO_WHILE, inputParameters: { _items: ${workflow.input.myList} }, loopOver: [] }其行为与使用items参数完全一致对新的工作流推荐使用items参数。源码中的优先级也很明确见 isListIteration 与 evaluateItemsList优先读取顶层items字段优先级 1仅在items缺失时才回退到inputParameters._items优先级 2两种写法任一生效任务即被视为列表迭代模式。限制LimitationsDo While 任务存在以下限制分支Branching——Do While 任务内部支持 Switch、Fork/Join、Dynamic Fork 等分支任务。但由于循环任务在 Do While 任务的作用域内执行任何跨越该作用域边界的分支都不会被遵守。嵌套循环Nested loops——不支持嵌套的 Do While 任务。如需实现类似嵌套循环的功能可以在 Do While 任务内使用 Sub Workflow 任务。隔离组执行Isolation group execution——不支持隔离组执行。不过循环内任务支持 domain。e2e 测试 DoWhileWithDomainTests 即专门覆盖了循环内任务按 domain 调度的场景。条件脚本运行环境源码补充文档中将loopCondition描述为使用 Nashorn 引擎求值的 JavaScript 表达式。值得注意的是从当前仓库源码看条件求值实际委托给 ScriptEvaluator其底层引擎为GraalVM polyglot JS 引擎而非 Nashorn并带有如下运行约束单次脚本执行默认超时4 秒DEFAULT_MAX_EXECUTION_SECONDS见 ScriptEvaluator.java#L32引擎禁用了js.load/js.print/js.console脚本无法加载文件或打印相同的条件文本会命中编译缓存SOURCE_CACHE避免重复解析。因此在编写loopCondition时应保持表达式轻量——它会在每次迭代完成时执行一次。测试验证与 UI 支持DO_WHILE 行为在仓库中有分层测试可查单元测试DoWhileTest含keepLastN输出清理、空/非空items列表的调度行为、DoWhileIntegrationTest、Groovy 的 DoWhileSpec端到端测试DoWhileTests、DoWhileEdgeCasesTests、DoWhileWithDomainTests以及 test-harness 中的 DoWhileSpecUI 侧新版工作流编辑器为 DO_WHILE 类型提供了表单定义与执行面板中的逐轮迭代展示组件见ui-next/src/pages/definition/EditorPanel/TaskFormTab/forms/DoWhileTaskForm/与ui-next/src/pages/execution/RightPanel/DoWhileIteration.tsx可以按迭代展开查看每轮任务输出。小结DO_WHILE 任务覆盖了 Conductor 循环编排的两类核心需求以loopCondition驱动的计数器循环以及以items驱动的零管理列表迭代keepLastN则为高频迭代提供了输出与持久层的自动瘦身。理解先执行后判断的语义、__i任务引用命名、条件表达式中的直接绑定规则以及items与_items的优先级关系就能在 Workflow.json 中写出既正确又可控的循环型工作流。【免费下载链接】conductorConductor is an event driven agentic workflow engine providing durable and highly resilient execution engine for applications and AI Agents项目地址: https://gitcode.com/GitHub_Trending/co/conductor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表