Files
deepseek-harness/docs/subsystems/workflow.zh.md
2026-08-10 16:34:20 +08:00

283 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 工作流
[English](workflow.md) | 中文
工作流 seam 允许 agent智能体运行由模型编写、会启动 subagent 的编排脚本。与 [subagent](subagent.md) 一样,它是**一项可选能力**,不属于 agent loop因此其类型和操作记录在此处而非 [core.md](core.md)。与 bash 一样,每个上下文只允许一个引擎实现提供 `ctx.workflows`;没有命名提供方注册表(第二个引擎通过插件配置替换第一个,而不与它同时运行)。
Service Definition[dsh-workflow](../../packages/workflow/workflow)`ctx.workflows` + 下文词汇。Service provider 是 [dsh-workflow-workerthread](../../packages/workflow/workflow-workerthread)(一个 `node:worker_threads` 引擎——每个 run 一个 worker脚本的 vm 上下文位于其中);面向模型的 Consumer 是 [dsh-tool-workflow](../../packages/workflow/tool-workflow)。提案与设计理由见 [dynamic-workflows Agent Note](../../.agents/notes/implemented/feature/2026-07-05-dynamic-workflows.md)。
源码:[`packages/workflow/workflow/src/types.ts`](../../packages/workflow/workflow/src/types.ts)
## 启动请求
本节定义调用方启动一次运行时提交的请求。普通工作流工具会根据模型的 `{ script, meta, args }` 调用和发起调用的 agent 构建该请求;专用消费方还可以为本次运行选择引擎级 `subagentProvider`,并将 `maxTotalAgents` 调低,但脚本无法观察或替换这两项策略。`meta``args` 是普通 JSON 数据;引擎会用 schema 校验 `meta`,并在任何工作开始前拒绝无效数据。引擎绝不会通过对脚本文本求值来获取它们。`parent` 是必填字段——脚本启动的每个子 agent 都归属于它cwd、谱系与深度通过 [subagent seam](subagent.md) 传递。
```ts type-equiv
/**
* What a caller asks for when starting a workflow run. `meta` and `args` are
* plain JSON DATA by the seam contract (the tool builds both from the model's schema-validated call;
* the engine validates `meta` against its schema and rejects loud
* before anything runs) — an engine never evaluates script text to obtain
* them. `parent` is REQUIRED — every `agent()` the script spawns is
* attributed to it (cwd, lineage, depth flow through the subagent seam).
*/
interface WorkflowStartRequest {
/** The plain-JS script body (top-level await allowed; ends with `return <json-value>`). */
script: string
/** The workflow's identity fields as plain JSON data, validated by the engine. */
meta: WorkflowMeta
/** Optional input exposed verbatim to the script as the `args` global. */
args?: unknown
/**
* Optional engine-wide child-provider override for this run. The workflow
* script cannot observe or replace it; omission uses the engine's configured
* provider.
*/
subagentProvider?: string
/**
* Optional per-run total-child ceiling. Implementations reject values above
* their deployment ceiling before publishing the run.
*/
maxTotalAgents?: number
/** The agent on whose behalf the run executes (parent of every child). */
parent: Agent
/** Cancels the run when aborted (the tool's `exec.signal`). */
signal?: AbortSignal
}
```
## 工作流的身份标识:`WorkflowMeta`
作为数据附在启动请求上的身份块(工具的 `meta` 参数;字段词汇与 Claude Code 动态工作流的 meta 块一致)。`phases` 仅用于进度展示:`phase()` 调用与标题匹配,供观察者使用;不暗示任何执行结构。
```ts type-equiv
/**
* The script's identity block, provided as plain JSON data alongside the
* script body (the model-facing tool carries it as its `meta` parameter) and
* validated by the engine before the body runs. `name`/`description` are
* required; the rest is optional annotation. The field vocabulary matches the
* Claude Code dynamic-workflows meta block.
*/
interface WorkflowMeta {
/** Short kebab-case workflow name (display + persistence key). */
name: string
/** One-line description of what the workflow does. */
description: string
/** Optional guidance on when this workflow applies (shown in listings). */
whenToUse?: string
/** Optional phase declarations matched by `phase()` calls. */
phases?: WorkflowPhase[]
}
```
## 终态结果:`WorkflowResult`
`WorkflowRun.result` 会兑现为一次运行的结果。`value` 是脚本的物化返回值——纯宿主域 JSON 数据(脚本无返回值时为 `null`)——仅在 `completed` 时有意义。`stopReason` 是封闭联合类型(引擎所有;消费方可穷举):`completed` | `cancelled` | `error`。非 `completed` 的原因在 `error` 中携带失败信息,消费方将其映射为 `isError` 工具结果,而非把部分输出当作成功上报。
```ts type-equiv
/**
* The outcome of one run, resolved by {@link WorkflowRun.result}. `value` is
* the script's materialized return value (plain host-realm JSON data; `null`
* when the script returned `undefined`) — meaningful only for `completed`.
* A non-`completed` reason carries the failure in `error`; the consumer maps
* it to an `isError` tool result rather than reporting partial output.
*/
interface WorkflowResult {
/** The script's return value (host JSON data; `null` for no return). */
value: unknown
/** Why the run settled. */
stopReason: WorkflowStopReason
/** The failure message (present iff `stopReason` is not `completed`). */
error?: string
/**
* How many `agent()` calls the run accepted over its whole lifetime. On a
* graceful settlement this is the script-side count (calls still queued for
* a concurrency slot included); on a termination path (grace force-settle,
* worker death) it degrades to the host-observed count — calls queued
* inside a terminated script are unknowable then.
*/
agentsStarted: number
}
```
## 活跃运行:`WorkflowRun`
脚本执行期间消费方持有的句柄。消费方会等待 `result`,可以在运行期间调用 `cancel`,并且必须在每条路径上调用 `dispose`(资源释放)。`result` 不会被拒绝:脚本失败会兑现为 `stopReason: 'error'`。运行被取消后,即使脚本本身永不结算,结果也会在引擎规定的有界宽限期内结算;引擎会强制将其结算为 `cancelled`,随后 worker-thread 引擎会终止脚本所在的 worker。因此等待 `result` 的消费方不会在取消后无限期挂起。`dispose()` 会执行取消、等待有界结算并等待子 agent 完全停稳,不会因脚本卡死而挂起。
```ts type-equiv
/**
* Holder-owned live workflow. `result` never rejects and settles within the
* engine's cancellation grace; failures resolve through `stopReason`. Consumers
* may cancel and must call idempotent `dispose()` on every path to await bounded
* script settlement and child quiescence.
*/
interface WorkflowRun {
readonly id: WorkflowRunId
/** The validated meta block (available before the body runs). */
readonly meta: WorkflowMeta
readonly result: Promise<WorkflowResult>
/** Cancel the run: children abort, pending hooks reject, the script dies at its next await (or is force-settled at the grace). */
cancel(reason?: string): void
/** Cancel + bounded-grace settle; safe to call on every path (idempotent). */
dispose(): Promise<void>
}
```
## 失败纪律:`WorkflowError.fatal`
脚本内部的钩子误用:错误参数、未知或延迟的 `agent()` 选项、超出[结构化输出子集](../../packages/core/tools/README.md)的 schema、触发的上限、seam 启动失败、取消,都会抛出 `fatal: true` 的 `WorkflowError`。`parallel()`/`pipeline()` 组合器对 fatal 错误直接重新抛出,而非将该项映射为 `null`:一个拼写错误的选项必须让脚本大声失败,绝不能消融为看似普通子 agent 失败的结果。逐项的 `null` 保留给子运行失败(非 `completed` 的 stop reason和阶段内的普通脚本错误。
## 事件
`workflow/*` 事件(`workflow/start`、`workflow/phase`、`workflow/log`、`workflow/agent-start`、`workflow/agent-end`、`workflow/end`,见[事件目录](#cordis-surface))是**仅供观察**的 emit携带数据快照每个 payload 以 `WorkflowRunInfo`id + meta开头而非活跃的 `WorkflowRun`,因此订阅者无法获得 `cancel`/`dispose``workflow/end` 刻意省略 result value观察结果的监听器不得收到调用方 result 的可变别名)。每次 emit 对每个监听器隔离:抛出异常的订阅者被记录日志但不传播,不会饿死在它之后注册的监听器;每个监听器收到自己的 payload 克隆,因此修改它既不会损坏引擎也不会影响其他监听器。这种隔离方式与 `subagent/start`/`subagent/end` 一致。
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
<a id="cordis-surface"></a>
## Cordis surface
Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — this section is byte-identical in both language sides of the page. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` surface lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
<a id="ctxworkflows--workflowservice-abstract-seam"></a>
### `ctx.workflows` — `WorkflowService` (abstract seam)
Workflow Service Definition contract. Invalid requests throw before publication; a live run is holder-owned, its result never rejects, cancellation and disposal are bounded, and disposal waits for child cleanup within that bound. Lifecycle listener failures are contained, and `workflow/end` fires exactly once as the result settles.
```ts cordis-catalog
/**
* Parse and execute a workflow script.
* @param request - the script, its `args`, the parent agent, and an
* optional cancel signal.
* @returns the live run; its `result` resolves when the script settles.
*/
abstract start(request: WorkflowStartRequest): WorkflowRun
```
Source: [`packages/workflow/workflow/src/index.ts:159`](../../packages/workflow/workflow/src/index.ts)
<a id="workflow-events"></a>
### `workflow/*` events
<a id="workflowagent-end--emit"></a>
#### `workflow/agent-end` — emit
One `agent()` call settled (clean result, child failure, or run cancellation). Paired with Events['workflow/agent-start'] by `agent.seq`, exactly once per started call on every stop path — on an engine termination path (a worker killed past its grace) the end is engine-synthesized with outcome `'cancelled'`.
```ts cordis-catalog
/**
* One `agent()` call settled (clean result, child failure, or run
* cancellation). Paired with {@link Events['workflow/agent-start']} by
* `agent.seq`, exactly once per started call on every stop path — on an
* engine termination path (a worker killed past its grace) the end is
* engine-synthesized with outcome `'cancelled'`.
* @param info - the run's identity snapshot.
* @param agent - the call identity plus its outcome.
* @mode emit
*/
'workflow/agent-end'(info: WorkflowRunInfo, agent: WorkflowAgentEndInfo): void
```
Source: [`packages/workflow/workflow/src/index.ts:81`](../../packages/workflow/workflow/src/index.ts)
<a id="workflowagent-start--emit"></a>
#### `workflow/agent-start` — emit
One `agent()` call established a published child run. Paired with Events['workflow/agent-end'] by `agent.seq`. A call that never receives a published run from the provider emits neither event in this pair.
```ts cordis-catalog
/**
* One `agent()` call established a published child run. Paired with
* {@link Events['workflow/agent-end']} by `agent.seq`. A call that never
* receives a published run from the provider emits neither
* event in this pair.
* @param info - the run's identity snapshot.
* @param agent - the call's sequence number, label, phase, and child id.
* @mode emit
*/
'workflow/agent-start'(info: WorkflowRunInfo, agent: WorkflowAgentInfo): void
```
Source: [`packages/workflow/workflow/src/index.ts:70`](../../packages/workflow/workflow/src/index.ts)
<a id="workflowend--emit"></a>
#### `workflow/end` — emit
A workflow run settled (any stop reason). Fired when WorkflowRun.result resolves. Paired with Events['workflow/start'].
```ts cordis-catalog
/**
* A workflow run settled (any stop reason). Fired when
* {@link WorkflowRun.result} resolves. Paired with
* {@link Events['workflow/start']}.
* @param info - the run's identity snapshot.
* @param result - the outcome data (stop reason, error, agent count) —
* deliberately WITHOUT the result value (see {@link WorkflowResultInfo}).
* @mode emit
*/
'workflow/end'(info: WorkflowRunInfo, result: WorkflowResultInfo): void
```
Source: [`packages/workflow/workflow/src/index.ts:91`](../../packages/workflow/workflow/src/index.ts)
<a id="workflowlog--emit"></a>
#### `workflow/log` — emit
The script emitted a narration line (a `log(message)` call).
```ts cordis-catalog
/**
* The script emitted a narration line (a `log(message)` call).
* @param info - the run's identity snapshot.
* @param message - the logged message, verbatim.
* @mode emit
*/
'workflow/log'(info: WorkflowRunInfo, message: string): void
```
Source: [`packages/workflow/workflow/src/index.ts:60`](../../packages/workflow/workflow/src/index.ts)
<a id="workflowphase--emit"></a>
#### `workflow/phase` — emit
The script entered a phase (a `phase(title)` call) — progress grouping for observers; no execution semantics.
```ts cordis-catalog
/**
* The script entered a phase (a `phase(title)` call) — progress grouping
* for observers; no execution semantics.
* @param info - the run's identity snapshot.
* @param title - the phase title, verbatim.
* @mode emit
*/
'workflow/phase'(info: WorkflowRunInfo, title: string): void
```
Source: [`packages/workflow/workflow/src/index.ts:53`](../../packages/workflow/workflow/src/index.ts)
<a id="workflowstart--emit"></a>
#### `workflow/start` — emit
A workflow run started — the script's meta block validated, the body about to execute. Paired with Events['workflow/end'].
```ts cordis-catalog
/**
* A workflow run started — the script's meta block validated, the body
* about to execute. Paired with {@link Events['workflow/end']}.
* @param info - the run's identity snapshot (id + meta).
* @mode emit
*/
'workflow/start'(info: WorkflowRunInfo): void
```
Source: [`packages/workflow/workflow/src/index.ts:45`](../../packages/workflow/workflow/src/index.ts)
<!-- END GENERATED cordis-surface -->