专家工作流与图调度器

core/src/workflow 里的一条固定八阶段长任务流水线:每个阶段交给一次性子会话,执行阶段由依赖图调度器按并发上限推进、可让 planner 扩图,终审可以重开节点,状态写成可恢复的文件快照。它在当前版本只有 CLI 与 TUI 的 /expert 一个入口,与动态工作流是两条独立的线。

作者 David更新于 30 篇(共 47 篇)

apps/zcode-cli/packages/core/src/workflow 里有一个名叫 Expert Workflow 的内置工作流,定义里的描述只有一句(apps/zcode-cli/packages/core/src/workflow/definition.ts:36):

Durable long-task workflow for large NL->Code work. Child agent sessions perform each phase.

也就是把一个大的“自然语言到代码”任务拆成八个固定阶段,每个阶段交给一次性的子会话去跑;其中执行阶段不是一个会话,而是一张依赖图,由 WorkflowGraphScheduler 按依赖与并发上限推进;每走一步都把快照写盘,进程死了可以接着跑。它与第 31 到 33 篇的动态工作流名字相近,但代码、存储与入口都不相通,最后一节专门对照。

位置职责
core/src/workflow/definition.ts内置定义与默认策略
core/src/workflow/expert/ExpertWorkflowRuntime:阶段循环、阶段执行、终审循环、图种子、失败分类、重试
core/src/workflow/scheduler.tsscheduler/WorkflowGraphScheduler:依赖图调度、planner 扩图、集合状态
core/src/workflow/lifecycle.ts快照的纯函数变换:恢复修复、取消、重开、播种、提示词更新
apps/zcode-cli/packages/contracts/src/workflow/index.ts定义、快照、图、事件与存储端口的 zod schema
apps/zcode-cli/packages/adapters/src/workflow/index.ts文件存储实现
apps/zcode-cli/packages/bootstrap/src/app/workflow-facade.tsworkflow-methods.ts装配:子会话 runner、定义解析、对外方法

它接进产品了吗

接进了,但只有一条路。在 bootstrap、cli、services、ui 里搜 ExpertWorkflowRuntimeWorkflowGraphSchedulercreateExpertWorkflowDefinition,调用方只有下面这条链:

  1. bootstrapcreateApp 构造 createWorkflowFacadeapps/zcode-cli/packages/bootstrap/src/app/create-app.ts:826),把它的方法摊进 App 对象(create-app.ts:1122)。门面在这里 new ExpertWorkflowRuntime,并按定义缓存实例(apps/zcode-cli/packages/bootstrap/src/app/workflow-facade.ts:160)。
  2. cli 命令中心/expert 被解析成已知命令(apps/zcode-cli/packages/cli/src/command-center/slash-commands.ts:36),分派到 handleExpertCommandapps/zcode-cli/packages/cli/src/command-center/create.ts:217)。TUI 的提交处理与无头模式都经过这个命令中心(apps/zcode-cli/packages/cli/src/tui-prompt-handler.ts:238apps/zcode-cli/packages/cli/src/prompt-command.ts:498),无头模式还把 /expert 明确留在命令中心,不交给普通 prompt 路径(prompt-command.ts:453)。
  3. 帮助文案/expert [status|resume|stop|<task>] 出现在共享的命令帮助表与 zcode --help 里(packages/shared/src/zcode-slash-command-help.ts:58apps/zcode-cli/packages/i18n/src/locales/zh-CN.ts:59)。

没有接上的部分:

  • 桌面端与 Web:App 输入框展示的内置命令只有 goalcompactinit 与 App 专属的 planapps/zcode-cli/packages/bootstrap/src/slash-command-surface.ts:3),v4 命令表里也没有专家工作流的命令(packages/shared/src/zcode-protocol-v4/command.ts:43),桌面端没有入口。
  • 门面上的其余方法runExpertWorkflowBackgroundretryWorkflowlistExpertWorkflowsreadExpertWorkflowEvents,以及不带 Expert 前缀的 runWorkflowresumeWorkflow 等同族方法(apps/zcode-cli/packages/bootstrap/src/app/workflow-methods.ts:5),在门面之外都没有调用方。App 选项里的 onWorkflowEvent 也没有任何宿主传入(apps/zcode-cli/packages/bootstrap/src/app/types.ts:181)。
  • 自定义定义:门面支持从 workflows/definitions/<id>.json 读别的定义(workflow-facade.ts:148),但 /expert 从不传 definitionIdworkflowKindapps/zcode-cli/packages/cli/src/command-center/handlers/expert.ts:90),能跑到的只有内置定义。
  • 后台任务体系:run 不登记进运行时任务表,也不产生 <task-notification>(对照后台任务与通知)。连 startBackground 也只是 void continueRun(...) 后立即返回(apps/zcode-cli/packages/core/src/workflow/expert/runtime.ts:66)。
  • 测试:开源仓库里没有针对它的测试,全仓只有 4 个测试文件(在 packages/ui/testpackages/services/test 下),都与此无关。

怎么用

输入行为出处
/expert显示当前工作目录下最近更新的那次 runexpert.ts:108apps/zcode-cli/packages/core/src/workflow/expert/runtime-context.ts:295
/expert <task>切到 yolo,新建 run 并在前台一直跑到完成或暂停expert.ts:86
/expert status [runId]显示状态expert.ts:22
/expert resume [runId]切到 yolo,从第一个未完成的阶段接着跑expert.ts:58
/expert stop [runId]cancel 同义)取消expert.ts:123

几个容易踩的地方:

  • 任务文本如果以 statusresumestopcancel 开头,会被当成子命令,后面的文字变成 runId(expert.ts:117)。
  • “切到 yolo”切的是主会话本身:TUI 先调一次 setMode("yolo"),门面再改一次运行时配置(workflow-facade.ts:232),跑完不会切回去。帮助文案也写明了是 yolo 模式(zcode-slash-command-help.ts:55)。
  • 命令是前台的:处理函数一直等到 continueRun 返回(workflow-methods.ts:247runtime.ts:41),期间各阶段子会话的事件经 onEvent 转给 TUI 显示(workflow-facade.ts:111)。
  • 中途中止这条命令会触发中止信号,run 被记为 cancelledapps/zcode-cli/packages/core/src/workflow/expert/run-loop.ts:84);cancelled 是终态,之后 resume 只会显示状态(apps/zcode-cli/packages/core/src/workflow/expert/ids.ts:27runtime.ts:101)。能续跑的是进程被杀留下的 running 快照,以及因错误停下的 paused run。

run 的全部状态写在 <cliStorageRoot>/workflows 下,默认即 ~/.zcode/cli/workflowsworkflow-facade.ts:75apps/zcode-cli/packages/adapters/src/workflow/index.ts:35),runId 形如 wf_expert_<uuid>runtime-context.ts:40):

路径内容写法
index.json所有 run 的列表项每写一次快照就整体重写(adapters/src/workflow/index.ts:128
runs/<runId>/run.json完整快照每次状态变化整体覆盖(adapters/src/workflow/index.ts:121
runs/<runId>/events.jsonl工作流事件追加(adapters/src/workflow/index.ts:45
runs/<runId>/graph.jsonl图记录:meta、node、edge、collection、op追加;存储端口没有读取它的方法(apps/zcode-cli/packages/contracts/src/workflow/index.ts:792),只写不读
runs/<runId>/artifacts/report.md阶段、节点、planner 的产物与最终报告adapters/src/workflow/index.ts:100113
definitions/<id>.json自定义定义只读(adapters/src/workflow/index.ts:188

八个阶段

阶段表写在定义里(definition.ts:49),behavior 只有四种取值(contracts/src/workflow/index.ts:71):

阶段behavior产物做什么
clarifyagentartifacts/01-clarify.md细化目标、假设、验收标准与未决问题,只在阻塞时提问
task_analysisagentartifacts/02-task-analysis.md把任务对到仓库上下文:约束、风险、可能涉及的文件、验证方式与失败路径
arch_decomposeagentartifacts/03-architecture-decompose.md拆成实现与验证节点的依赖图;输出里的 JSON 会被拿去给 exec 播种(definition.ts:72
env_setupagentartifacts/04-env-setup.md检查是否需要环境、依赖、凭据或本地服务
meta_promptagentartifacts/05-meta-prompt.md生成下游需要的执行指令、约束与节点提示词;输出里的 JSON 用来改写 exec 节点的提示词(definition.ts:91
execscheduled_graphartifacts/06-exec.md(调度摘要)与 artifacts/exec/<节点>.md按依赖图执行
final_criticcriticartifacts/07-final-critic.md对照验收标准审查结果,可以要求重开节点
completecompletereport.md写最终报告,把 run 标为完成

主循环按 phaseOrder 顺序走,已完成的阶段直接跳过,这也是恢复时能从断点接着跑的原因(run-loop.ts:3240)。初始图里八个阶段各是一个 phase:<阶段> 节点,首尾相连(apps/zcode-cli/packages/core/src/workflow/expert/prompts.ts:157)。

图表加载中…

默认策略

默认策略随定义一起给出(definition.ts:11):

export const DEFAULT_EXPERT_WORKFLOW_STRATEGY: WorkflowStrategy = {
  clarify: {
    confidenceThreshold: 0.8,
    maxRounds: 3,
    minRounds: 1,
  },
  executor: {
    drainingChangeHours: 1,
    frontierTarget: 3,
    maxConcurrentLoops: 2,
    maxConsecutiveErrors: 3,
    maxPlannerRuns: 10,
  },
  finalCritic: {
    maxIterations: 3,
  },
  reactLoop: {
    maxRounds: 30,
  },
};

建 run 时策略被抄进快照,恢复时用快照里那份(runtime-context.ts:75)。十个参数里只有五个真正被代码读取:

参数默认实际作用
clarify.confidenceThresholdmaxRoundsminRounds0.8、3、1只作为一行文字写进阶段提示词(expert/prompts.ts:38);clarify 阶段实际只跑一次子会话
executor.drainingChangeHours1没有任何代码读取
executor.frontierTarget3可探索集合里 pending 加 active 的节点数达到它、又没有新完成的节点时,不再叫 planner 扩图(apps/zcode-cli/packages/core/src/workflow/scheduler/collection-planner.ts:90
executor.maxConcurrentLoops2同时在跑的节点上限(apps/zcode-cli/packages/core/src/workflow/scheduler.ts:65
executor.maxConsecutiveErrors3一值三用:调度器连续失败多少次就暂停(scheduler.ts:110)、单个节点最多尝试几次(作为 maxAttempts 传给节点执行,scheduler.ts:140)、planner 累计失败多少次就放弃这个集合(collection-planner.ts:109
executor.maxPlannerRuns10单个集合最多扩图几次(collection-planner.ts:102
finalCritic.maxIterations3终审最多几轮(apps/zcode-cli/packages/core/src/workflow/expert/critic-loop.ts:28
reactLoop.maxRounds30只写进提示词(expert/prompts.ts:4079);子会话按普通回合跑,不受它约束

另有一个写死的数字:终审对同一个节点最多重开 2 次(critic-loop.ts:145)。

阶段交给一次性子会话

每个 agent 阶段、每个图节点、每次 planner 调用都是一个“活动”(activity),由门面里的 WorkflowAgentRunner 执行(workflow-facade.ts:83):为这个活动新建一个 AgentRuntime,会话 ID 为 sess_workflow_act_<uuid>workflow-facade.ts:87),只跑一个回合(workflow-facade.ts:116),回合的最终回答就是这个活动的产物。子运行时的配置(workflow-facade.ts:286):

  • agentNamezcode-expert,模式固定 yolo,模型沿用主会话当前的选择,事件存储用内存实现,工作目录是项目目录;
  • taskTypeworkflow_child。这类子会话的交互事件不会镜像到父界面,所以 CreateWorkflowAmendWorkflowSaveWorkflowResumeWorkflowRunResolveWorkflowQuestion 被结构性地拿掉,免得它们在子会话里发出父界面看不到的确认请求(apps/zcode-cli/packages/core/src/runtime/helpers/tool-allowlist.ts:18)。

阶段提示词由 buildPhasePrompt 拼成(expert/prompts.ts:13):阶段名、runId、工作目录、用户任务、上面那几行策略、阶段目标、前序产物清单,结尾要求“输出一份简洁的 Markdown 产物”(expert/prompts.ts:50)。只有 arch_decompose 额外附上一份 JSON 图的格式约定(expert/prompts.ts:20)。从代码看,前序产物的内容并不进提示词,清单里给的是相对 run 目录的路径(如 artifacts/01-clarify.mdapps/zcode-cli/packages/core/src/workflow/expert/phase-runner.ts:139),而提示词没有给出 run 目录本身在哪;子会话的工作目录是项目目录,未必找得到这些文件。

阶段跑完,产物写进 run 目录,快照、图记录与事件各写一笔(phase-runner.ts:103)。随后两步图变换:

  • 播种:从 arch_decompose 的回答里抠 JSON(apps/zcode-cli/packages/core/src/workflow/expert/graph-artifacts.ts:12)。解析很宽容:整段是 JSON、在 json 代码块里、或者取第一个左花括号到最后一个右花括号都行(apps/zcode-cli/packages/core/src/workflow/expert/parsers/json.ts:1),字段名也认多种写法(apps/zcode-cli/packages/core/src/workflow/expert/parsers/graph-seed.ts:106)。没有依赖的根节点会被补一条依赖 phase:meta_prompt 的边,保证任务节点在元提示阶段之后才就绪(graph-seed.ts:32)。节点或集合重名、边指向不存在的节点、成环都会抛错(apps/zcode-cli/packages/core/src/workflow/lifecycle.ts:293311)。解析不出 JSON 就不播种,exec 退化为只有 phase:exec 一个节点,用阶段提示词跑一个子会话(ids.ts:11expert/prompts.ts:59)。
  • 改写节点提示词:从 meta_prompt 的回答里抠 JSON,按节点 ID 更新 exec 节点的标题、描述或提示词(graph-artifacts.ts:57lifecycle.ts:375)。meta_prompt 的提示词里没有这份 JSON 的格式约定,只有子会话碰巧输出了可解析的 JSON 才会生效。

图调度器

runScheduledPhaseexec 阶段构造一个 WorkflowGraphScheduler,节点执行器与 planner 执行器都指向上面那个子会话 runner(apps/zcode-cli/packages/core/src/workflow/expert/scheduled-phase.ts:27)。进入时先把残留的 active 节点复位为 pendingscheduler.ts:55),然后循环:检查集合 planner、判断是否完成、判断是否该暂停、派发就绪节点。派发与等待这段(scheduler.ts:124):

      const readyNodes = orderedReadyExecutableNodes(snapshot.graph, executableNodeIds).filter(
        (node) => !active.has(node.id),
      );
      let dispatched = 0;
      for (const node of readyNodes) {
        if (active.size >= maxConcurrent) break;
        // ...
        active.set(node.id, promise);
        snapshot = (await promise.started).snapshot;
        dispatched++;
      }

      if (dispatched > 0) continue;

      if (active.size > 0) {
        const outcome = await Promise.race(active.values());
        active.delete(outcome.nodeId);
        consecutiveErrors = outcome.ok ? 0 : consecutiveErrors + 1;
        continue;
      }
  • 就绪:节点为 pending,且所有依赖都处于终态。终态包括 failedcontracts/src/workflow/index.ts:590642),所以依赖失败的下游照样会被派发。
  • 顺序:先派普通节点,再在各个可探索集合之间轮流各取一个(apps/zcode-cli/packages/core/src/workflow/scheduler/graph.ts:25)。
  • 派发:每派一个都先等它把 active 状态写进快照,再派下一个(scheduler.ts:144);同时在跑的不超过 2 个。没得派时等最先结束的那个,成功就把连续失败计数清零。
  • 完成:所有可执行节点都是 completedcancelledskipped,并且有节点的可探索集合都已枯竭(graph.ts:73)。注意这里不含 failed:留有失败节点的阶段不会完成。
  • 暂停:连续 3 个失败结果,等在跑的节点收尾后以 error_threshold 暂停(scheduler.ts:110);没有可派的、没有在跑的、又没全部完成,以 deadlock 暂停(scheduler.ts:157)。暂停会让 runScheduledPhase 抛错,run 随之暂停(scheduled-phase.ts:97)。

节点产物写到 artifacts/exec/<节点 ID>.mdapps/zcode-cli/packages/core/src/workflow/scheduler/node-runner.ts:140),就绪集合一变化就发一条 frontier_changed 事件(scheduler.ts:189)。

planner 扩图与集合

集合(collection)是一组节点加上目标与度量。标了 explorable 的集合在执行中可以长出新节点:每轮循环开头,checkCollectionPlanners 逐个检查这些集合要不要叫 planner(collection-planner.ts:24)。刚播种、一个节点都还没完成的集合先不扩(collection-planner.ts:75);之后依次判断:前沿(集合里 pending 与 active 的节点数)已达目标且没有新完成的节点,就不扩(collection-planner.ts:90);已经扩满 maxPlannerRuns 次,或 planner 累计失败达到 maxConsecutiveErrors 次,就把集合判为枯竭(collection-planner.ts:102109)。

这几道关都没拦住(前沿不足 3 个,或者有 planner 还没看过的新完成节点)时,就同步跑一次 planner 子会话;在跑的节点不受影响,但这一轮的派发要等 planner 回来。planner 的提示词要求“只返回 JSON”,并给出节点、边、collectionNodeIdsexhausted 的格式(apps/zcode-cli/packages/core/src/workflow/scheduler/prompts.ts:67),回答存成 artifacts/exec/planners/<集合>-<次数>.mdcollection-planner.ts:249)。新增部分要过校验:节点重名、自环、端点不存在、边重复、成环都判为失败(apps/zcode-cli/packages/core/src/workflow/scheduler/planner-expansion.ts:35124)。

集合状态在 activedrainingexhausted 之间走:除了上面两道上限,planner 自己声明 exhausted,或者连着两次没改图、前沿为零、也没有新完成的节点,也判枯竭(planner-expansion.ts:52)。planner 失败的那一次如果让累计失败数到了上限,当场就判枯竭(collection-planner.ts:327)。

终审循环

final_critic 最多跑 3 轮,每轮先用普通的阶段执行跑一次子会话,再把回答当 JSON 解析(critic-loop.ts:36):

    const phaseRun = await runPhase(ctx, current, definition, options);
    current = phaseRun.snapshot;
    const critic = parseCriticResult(phaseRun.response);

    if (critic.verdict === "pass") {
      // ...
      return current;
    }

    const reopenProposals = dedupeReopenProposals(critic.reopenProposals);
    // ...
    if (reopenProposals.length === 0) {
      return current;
    }
    // ...
    const retryReason = `Final critic reopened node(s): ${reopenedNodeIds.join(", ")}`;
    current = resetPhaseForRetry(ctx, current, execDefinition.phase, retryReason);
    current = resetPhaseForRetry(ctx, current, definition.phase, retryReason);
    await ctx.store.writeSnapshot(current, { signal: options.abortSignal });
    await ctx.appendGraphStatus(current, execDefinition.phase, "pending", options.abortSignal);
    await ctx.appendGraphStatus(current, definition.phase, "pending", options.abortSignal);
    current = await runScheduledPhase(ctx, current, execDefinition, options);
  • 判决字段认 verdict,也认旧写法 passedoverallVerdict;重开建议认 reopenProposalsreopen_proposalsreopenNodesapps/zcode-cli/packages/core/src/workflow/expert/parsers/critic.ts:3857)。
  • 重开只接受 completedfailedskipped 的节点,每个节点最多 2 次,超限的建议被拒并记一条事件(lifecycle.ts:86245critic-loop.ts:135)。被重开的节点回到 pending,它下游已完成的节点不会跟着重跑。
  • fail 但没有重开建议、或建议全被拒时,直接返回。由于阶段执行已经把 final_critic 记为完成,run 会接着走到 complete,状态照样是 completed
  • 跑满 3 轮仍在重开:final_critic 记为 failed,发 critic_iteration_limit_reachedcritic-loop.ts:119),但主循环照常进入 complete,run 同样是 completed,只能在报告的阶段列表里看到终审失败。

从代码看,终审阶段用的是通用阶段提示词,只要求输出 Markdown(expert/prompts.ts:50),没有写明判决 JSON 的格式;而解析器找不到 JSON 或解析不出判决就抛错(parsers/critic.ts:13parsers/json.ts:15)。子会话如果只写了一份 Markdown 审查报告,这个错误会一路抛到主循环,把 run 变成 pausedrun-loop.ts:114)。

失败、暂停与重试

规则出处
节点失败后尝试次数加一,不满 3 次回 pending,满 3 次记 failednode-runner.ts:206
调度器连续 3 个失败结果,或无路可走,暂停scheduler.ts:110157
planner失败 3 次或扩满 10 次,集合判枯竭,不影响阶段继续collection-planner.ts:327102
agent 阶段子会话抛错:阶段记 failed,错误继续上抛phase-runner.ts:155
run抛到主循环的错误:中止信号已触发则记 cancelled,否则记 paused 并附失败信息run-loop.ts:77

paused 时快照里会带上一条失败记录与三个恢复动作 retryretry_with_current_modelcancelapps/zcode-cli/packages/core/src/workflow/expert/failures.ts:46)。失败按错误链归成网络、限流、超时、认证、上下文超限、权限、工具等十一类(failures.ts:72contracts/src/workflow/index.ts:198),认证、取消、配置三类标为不可重试(failures.ts:128)。run 级状态只会走到 pendingrunningpausedcompletedcancelled;schema 里的 failed 没有代码会写(contracts/src/workflow/index.ts:11)。

恢复动作目前只是数据:/expert 没有 retry 子命令,retryWorkflow 也没有调用方。真正的 retry 会把失败与在跑的节点复位为 pending、清掉失败记录(apps/zcode-cli/packages/core/src/workflow/expert/retry-state.ts:7),而用户能用的 resume 只修复 active 的残留(lifecycle.ts:99)。所以一个因节点用满 3 次尝试而暂停的 run,/expert resume 之后那个节点仍是 failed;它的下游会继续跑,但阶段永远凑不齐“全部完成”,从代码看调度器最终会以 deadlock 再次暂停。

快照与恢复

图表加载中…

每次状态变化都覆盖一次 run.json,另往 graph.jsonl 追加一条图记录、往 events.jsonl 追加一条事件(runtime-context.ts:138169)。恢复只读 run.jsonadapters/src/workflow/index.ts:91):reconcileWorkflowSnapshotForResumeactive 的节点改回 pending 并写明原因,active 的阶段改回 pendingactive 的活动记为 cancelledlifecycle.ts:88),然后主循环跳过已完成阶段接着跑。取消则把所有 pendingactive 的节点、阶段、活动一并记为 cancelledlifecycle.ts:163),再中止本进程里这个 run 的控制器(runtime.ts:247)。控制器表只在内存里(runtime-context.ts:22),所以从另一个进程执行 stop 只会改写快照,停不掉正在别处运行的子会话。

与动态工作流的关系

仓库里其实有三条都叫 workflow 的线:

专家工作流脚本工作流动态工作流
谁来编排固定八阶段加图调度器模型提交的脚本,旧 Workflow 工具模型写的 TypeScript 脚本,编译后在沙箱里跑
契约contracts/src/workflow/index.tsapps/zcode-cli/packages/contracts/src/workflow/script.tsapps/zcode-cli/packages/contracts/src/tools/workflow.tsapps/zcode-cli/packages/contracts/src/interfaces/dynamic-workflow-run.port.ts
存储~/.zcode/cli/workflows 下的 JSON 与 JSONLSQLite 的 workflow_*SQLite 的 dwf_*
入口/expertWorkflow 工具,当前不注册CreateWorkflow 等工具、/workflow、桌面端
后台任务类型不登记local_workflowlocal_dynamic_workflow

从代码留下的痕迹看,动态工作流是从脚本工作流长出来的,与专家工作流没有继承关系:

  • 动态工作流的 README 说生产驱动放在 bootstrap,“evolving the existing script-workflow-* substrate”(apps/zcode-cli/packages/dynamic-workflow/README.md:15);它的 run 快照类型建在旧 Workflow 工具端口的 WorkflowTaskSnapshot 上(apps/zcode-cli/packages/contracts/src/interfaces/dynamic-workflow-run.port.ts:200)。
  • workflow_* 表来自 0.13.0 的迁移 0007_workflow_script_runtimeapps/zcode-cli/packages/adapters/src/storage/session-store/migrations.ts:227),dwf_* 表的迁移注释写明“legacy 的 workflow_* 表只是模板不是家”(migrations.ts:801)。专家工作流两套表都不用。
  • /workflow 是随 CLI 打包的 zcode-guide 插件提供的自定义命令,对应的是动态工作流(apps/zcode-cli/packages/bootstrap/src/zcode-protocol/slash-commands.ts:62)。

共用的只有外围:三者的子会话都用 workflow_child 任务类型,共享同一份工具禁用名单,注释里点名了 /workflow/expert 与脚本工作流(tool-allowlist.ts:18);App 接口里脚本工作流的方法沿用了 ExpertWorkflowCommandResult 作返回类型(app/types.ts:549)。另有一处注释与代码不符:内置工具表把旧 Workflow 工具称作“/expert 脚本通道”(apps/zcode-cli/packages/core/src/tool/handlers/index.ts:144),但 /expert 实际走的是本篇的 ExpertWorkflowRuntime,与 Workflow 工具无关。

两者思路上的相近之处——分阶段、每一步一个子会话、持久化以便续跑、事后审查——都是在各自的代码里重新实现的。动态工作流怎样让模型自己写编排脚本,从下一篇开始讲。

下一篇:动态工作流(一):门面与编译器——模型写的 TypeScript 工作流脚本长什么样,编译器怎样从脚本里恢复类型、依赖与站点身份。

本页目录