Workflow 模型
workflow-code 的业务代码是自包含的 TypeScript workflow。平台、CLI、server 和 App 会注入一个全局命名空间workflow,业务 workflow 不应该从 "workflow-code" 导入 runtime API,也不应该在 workflow package.json 里声明仓库内部依赖。
Runtime API
当前workflow 全局对象来自 core/runtime-api.ts,包含:
Workflow
Workflow 是业务流程本体,由workflow.defineWorkflow<Input, Output>() 定义:
run 接收 input 和 context,并返回最终 payload。执行期间如果抛出异常,executor 会把它序列化到执行报告中。
Node
Node 是最小可执行单元,分为普通节点、流式节点、分支节点和循环节点:WorkflowOutputPayload.items: WorkflowOutputItem[]。每个 item 都是一个可独立展示和折叠的消息块,包含 id、title、content、可选 icon / iconPreset、contentType、collapsed 和 metadata。contentType 只决定渲染方式:markdown、text、json 或 audio;对象和数组应直接放进 content,不要提前 stringify 成 Markdown code block。audio content 使用 { src, mimeType, name, size, durationMs?, format? },src 可为安全 data:audio/...;base64,... 或 http(s) URL。
workflow.createDetailsOutputNode 是创建独立可折叠 output item 的便捷节点,会把 { title, content, icon } 转成 items: [{ title, content, icon, contentType, collapsed: true }],并仍按 output 标准节点被 CLI、App 和 server 收集。workflow.createTimerNode 会创建标准名为 timer 的普通节点,用于启动业务计时;workflow.endTimer(timer, context, { status, message }) 显式结束计时并写入 report.timers,未结束的 running timer 会在 workflow 收尾前自动标记为 cleared,避免 UI 残留运行态。workflow.runNode 返回节点输出;workflow.runStreamNode 返回 { chunks, output },其中 chunks 是原始流式 chunk 数组,output 是 finalize 或 format 生成的最终 payload。流式 createOutputNode({ stream, format }) 的 format 接收累积后的 { text, items, chunks };字符串 chunk 会自动写入一个默认 Markdown item,结构化输出可使用 workflow.createOutputItemChunk(...) 发送 output_item_start、output_item_delta、output_item_end。output_item_delta 除了合并 content,还可以携带 item patch 更新 title、icon、collapsed、contentType 或 metadata.status,用于展示工具调用从 running 到 success / failed 的状态变化。workflow.runIf 会按顺序检查 if / else-if / else 分支,命中后执行对应 run,并用 branch-node 运行事件记录所选分支。workflow.runFor 和 workflow.runWhile 会把循环本身记录为可追踪节点,让循环体在 App Diagram 中保持可见。
workflow.createUserInputNode 会在 workflow 中插入一个可持久恢复的人工输入点。节点的 params 使用与 executor.params 相同的字段定义,App、server embed 和 server API 会用同一套 Run workspace 表单展示。首次执行时如果没有提交值,runtime 发出 user_input_requested 事件,report 写入 pendingUserInput,run 状态变为 waiting_for_input;提交答案后,同一个 runId 会带着 resolvedUserInputs 恢复执行,并发出 user_input_resolved。CLI 不读取 stdin,遇到该节点时会输出等待报告,需要通过 workflow-code json --run-id <id> --resolved-user-inputs-json '<json>' 注入答案继续。
Branch
workflow.runIf 适合把业务条件写成可追踪的 workflow 分支,而不是只写在普通 TypeScript if 里:
branches 数组中的第一个元素在 Diagram 中显示为 if,后续元素显示为 else-if,else 会显示成 fallback 分支。分支的 run 里可以继续调用 workflow.runNode、workflow.runStreamNode,也可以嵌套另一个 workflow.runIf 形成多层分支。
Loop
workflow.runFor 和 workflow.runWhile 适合把业务循环写成可追踪的 workflow 循环,而不是只写在普通 TypeScript for / while 里:
runFor 会按 items 生成的 iterable 顺序执行,默认最多 1000 次;runWhile 会在每轮开始前检查 condition,默认最多 100 次。两者的 run 都应返回下一轮状态。完成事件的 executionInfo 会记录 loopType、iterations、iterationCount、maxIterations 和 completed。
Diagram 和节点协议
App diagram 使用两类信息展示 workflow:- 静态结构来自 TypeScript 源码分析。所有已知
workflow.*runtime API 调用会写入definitions.apiCalls,并以静态 API 节点展示,例如workflow.createInputNode、workflow.defineWorkflow、workflow.getEnv。这些节点帮助理解源码使用了哪些平台能力,但不会单独执行,也不会串进Input -> Result执行路径。(issue #48) - 运行状态来自
workflow.runNode/workflow.runStreamNode/workflow.runIf/workflow.runFor/workflow.runWhile/workflow.runWorkflow的nodeHooks报告。只有真正被这些 API 执行的节点会显示运行中、成功、失败、耗时和错误。
workflow.runIf 会作为 run-if 分支节点展示,App 会使用分支图标和语义色与 LLM、输出、provider、question-classifier 区分。完成事件的 executionInfo 会记录 selectedBranch、selectedBranchIndex 和 description,用于 Trace 查看实际走到了哪条分支。静态结构分析优先识别对象字面量形式的 branches / else;动态拼装分支仍可运行,但 Diagram 无法完整展开所有静态路径。
workflow.runFor 和 workflow.runWhile 会作为 run-for / run-while 循环节点展示。App Diagram 会用带语义色的矩形背景容器包裹循环体 children,循环头节点仍可点击查看 items、condition、maxIterations 和运行时迭代信息。循环体可以继续嵌套 workflow.runIf、workflow.runFor 或 workflow.runWhile,用于表达 while 内部分支和多层循环。静态结构分析优先识别对象字面量形式的 loop options;动态拼装 options 仍可运行,但 Diagram 无法完整展开循环体。
workflow.runWorkflow 会作为 run-workflow 调用节点展示。子 workflow 不会创建独立 run report,子节点事件会进入父 trace;顶层 executor 入口不会额外插入这个包裹节点。默认 conversationMode: "shared"、kvMode: "shared"、outputVisibility: "visible",只传 name 不会改变会话或输出语义;当父 workflow 传入 conversationMode: "shared-readonly" 时,子 workflow 可以读取父会话快照,但不会追加 transcript 或修改标题。子 workflow 会写专属 KV 状态时,可同时传入 kvMode: "isolated"。
内置节点工厂返回的普通节点继承 BaseNode,流式节点继承 BaseStreamNode;它们共同提供 name、standardName、metadata 和 getExecutionInfo() 等公共信息。Diagram 不直接绑定 BaseNode 类,而是读取 NodeDefinition / StreamNodeDefinition 执行协议和运行报告,因此未来自定义节点只要符合协议,也能被执行与记录。
Provider
当前 executor 的内置 provider 名称只有"llm"。LLM provider 通过 createLLMProviderRef 延迟解析:
createLLMProviderFromEnv 会从 workflow runtime 环境读取 LLM_TYPE、LLM_API_KEY、LLM_BASE_URL 和 LLM_MODEL。
Executor
Executor 是平台发现和运行 workflow 的入口:createInput 会接收 workflowName、workflowDir、args、env、abortSignal 和可选 conversationId。App、CLI、server 和外部 API 最终都通过 executor 把参数转换为 workflow input;取消本地或 server 运行时,abortSignal 会传播到 workflow context,便于长耗时 provider/SDK 调用及时停止。
Registry
Registry 用于单节点调试和结构展示。它必须是静态数组字面量:/api/workflows/{name}/debug/nodes/{nodeName} 会使用 registry 中的节点配置运行单节点调试。
包结构
workflow package 只声明真实第三方依赖,不声明workflow-code。CLI、App 和 server 会提供外层运行时:
追踪
本文档首版由 issue #32 记录。API 列表对齐core/runtime-api.ts 和 core/executor/types.ts。