---
title: "Dynamic Workflow"
description: "说明 Dynamic Workflow 在子代理执行体之上生成并运行编排脚本，以及 journal 恢复、失败处理和可重放范围。"
---

> Documentation Index
> Fetch the complete documentation index at: https://konghayao.github.io/peri-cool/llms.txt
> Use this file to discover all available pages before exploring further.

# Dynamic Workflow

Dynamic Workflow 是 Peri 的多 Agent 编排机制，一段脚本描述执行计划，脚本由 LLM 在任务现场生成，执行交给独立进程并保持确定性。本文讲四件事，workflow 是什么、为什么说它是动态的、它与子代理和普通回合的边界、journal 与恢复怎么工作，最后是适用边界。

## 编排层的位置

workflow 是用脚本定义多 Agent 执行计划。脚本用 JavaScript 写，`meta` 声明名称与描述，正文调用少量原语完成编排。下面是一个最小脚本（演示示例，不是真实会话），

```javascript
export const meta = {
  name: 'parallel-review',
  description: '从三个视角并行审查一段代码',
}

const [security, perf, bugs] = await parallel([
  () => agent('审查这段代码的安全问题：注入、鉴权、数据暴露。', {
    label: 'security', allowedTools: ['Read', 'Grep'],
  }),
  () => agent('审查这段代码的性能问题：N+1 查询、热点路径。', {
    label: 'perf', allowedTools: ['Read', 'Grep'],
  }),
  () => agent('审查这段代码的边界问题：错误处理、空值分支。', {
    label: 'bugs', allowedTools: ['Read', 'Grep'],
  }),
])

return { security, perf, bugs }
```

使用者不需要写这段脚本。对 Peri 说用 ultracode 并行审查我改过的代码，或直接输入 `/ultracode`，ultracode skill 载入上下文，由 LLM 把任务拆成脚本并通过 Workflow 工具提交执行。需要固定流程（比如每次合并前跑同一套审计）时，才把脚本落盘到 `.claude/workflows/` 下，用 `scriptPath` 引用。

## 计划生成与确定执行

### 现场生成计划

脚本本身是 LLM 生成的工具参数。拆几路并行、每路 `agent()` 的 prompt、模型档位、`allowedTools`、阶段划分，都是在任务当下由 LLM 决定并写进脚本的。这里没有工程师预先写死的 pipeline，也没有工作流作者这个角色——相对 CI 里人维护的静态流水线，动作（actions）来自生成而非手写。

### 运行期注入状态

- 外部数据经 `args` 在启动时注入（审查目录、commit 范围、时间戳）。
- `phase()` 在运行中切换阶段，后续 `agent()` 自动带上当前阶段名。
- 每个 `agent()` 是一次独立的 LLM 推理，各跑各的上下文，不共享状态。

### 固定脚本执行路径

动态指脚本内容，不指执行路径。一旦脚本定型，执行就是可重放的状态机，沙箱拦截 `Date.now()`、`new Date()`（无参数调用）与 `Math.random()` 这类非确定性来源，`import` 也被禁止。真正不可预期的只有 LLM 对每个 `agent()` 的回答，而它们被逐条记进 journal，成为恢复的依据。

> **Note**
>
> 分工粗略概括，动态生成计划，确定性执行计划。脚本可以每次不同，但对同一份脚本而言，重放路径应当可预期。

## 复用子代理执行体

三种执行形态放在一起看，

| 维度 | 普通回合 | 子代理 | Dynamic Workflow |
|------|----------|--------|------------------|
| 决策位置 | 主会话上下文 | 主会话上下文（一次派发） | 脚本（LLM 生成，随后独立执行） |
| 执行体 | 主 ReAct 循环 | 独立 ReAct 循环 | 独立进程内的多个 ReAct 循环 |
| 并发 | 回合内工具并行 | 同步每轮 1 个；后台并发上限 3 | 脚本内 `parallel`，默认并发 3 |
| 可重放 | 否 | 否 | journal 前缀可复用 |
| 结果去向 | 会话历史 | 回传主 Agent 上下文 | 脚本变量，跨阶段显式传递 |

- **普通回合**，主会话的单 ReAct 循环，动作随手在上下文里产生，无法保存、重跑、比对。适合需要连续对话的任务——确认边界、逐轮追问、反复试错。
- **子代理**，主 Agent 在回合里用 Agent 工具派发独立 ReAct 循环，输出回传上下文。派发决策仍在 LLM 手里——同一任务跑两次，拆法可能不同、结果不可比。机制细节见 [Multi Agent 架构](/peri-cool/posts/engineering/multi-agent/)。
- **workflow**，决策权从上下文移到脚本。脚本生成后脱离主会话的推理路径独立执行，主会话只收完成通知；流程的版本、日志、结果都在脚本和磁盘上留痕。

值得注意的复用点，workflow 的每个 `agent()` 最终都落到统一的 SubAgent 执行体——复用 Agent Loop、工具过滤和 token 统计，并继承主会话在 `session/new` 时形成的 owner snapshot。执行体是子代理基础设施，变的是决策者：普通子代理由模型在回合里派发，workflow 由脚本批量声明、runner 统一运行。

边界判据一句话，**这个流程还会重跑吗？** 会，就交给脚本；一次性探索，直接对话加子代理。两者互补，不互相取代。

## 脚本进入执行进程

脚本由 meta 字面量与正文组成。引擎抽出 meta（必须是纯字面量，不能是变量或插值），正文以函数体运行，六个原语连同 `args`、`budget` 以注入参数的方式到达脚本，

```text
agent(prompt, opts)      调度一个子代理执行，返回输出
parallel(thunks)         并发执行一组工厂函数，结果聚合成数组
pipeline(items, stages)  数据项逐个顺序经过多个阶段
phase(title)             切换阶段标记，供面板展示
log(message)             写入面板日志
workflow(ref, args)      运行子工作流（仅一层嵌套）
```

- **不复制控制流原语。** 条件分支、收敛循环这类结构是 JavaScript 自身的 `if` / `while` / `for`，直接在脚本里写。ultracode 文档里的七种模式（[编排模式图解](/peri-cool/docs/features/ultracode-workflow/)）多数依赖脚本语言表达力，而不是原语数量。
- **脱离 ESM。** `import` 在脚本里会直接报错；需要外部依赖时经 `args` 由调用方注入。这既是沙箱的一部分，也让脚本与宿主进程解耦。
- **`phase()` 只影响展示。** 它组织的是可观测性边界，不改变执行顺序；面板按阶段归组，`agent()` 的归属在 `phase()` 与显式 `opts.phase` 之间取运行时当前值。
- **失败语义。** `agent()` 返回 dead 或抛错时整体重试一次；用户取消（killed）与中断（interrupted）直接记为失败，不做重试。`parallel` / `pipeline` 单项失败返回 `null`，不中断整轮；是否接受单项失败，由脚本作者在结构里决定。

> **Caution**
>
> `parallel` 的入参必须是工厂函数 `() => agent(...)`，不能是 `agent(...)` 的执行结果。写后者会被当成普通值静默吞掉、该位返回 `null`，workflow 一路"成功"到底但结果是空的。这是手工写脚本时最容易踩的坑，ultracode skill 里有明示。

执行架构三段式，

1. **进程**，runner 拉起独立 Node.js 进程跑引擎，与 Peri 主进程通过新行分隔 JSON-RPC 通信（stdout 走协议，stderr 留给日志）。脚本在启动时写盘到 `.claude/workflow-runs/<run_id>/script.js`。
2. **回调**，脚本里每次 `agent()` 经 RPC 回到 Peri，WorkflowAgentExecutor 解析 agentType 定义、模型档位、工具白名单，装配完整中间件链，跑一次 ReAct 循环，返回结构化结果（输出、token、模型、阶段、耗时），同时经进度通道实时上报。
3. **投递**，Workflow 工具是 fire-and-forget——调用立即返回 `run_id`，主会话不阻塞；完成时结果经会话消息流通知，快照写 `state.json`。

## 日志支撑恢复

一次 run 的全部证据落在 `.claude/workflow-runs/<run_id>/`，

| 文件 | 内容 |
|------|------|
| `script.js` | 本次执行的脚本副本 |
| `journal.jsonl` | 追加式 agent 调用记录 |
| `state.json` | 结束快照（状态、返回值、错误），原子写入 |
| `outputs/` | 超长输出的落盘 |

journal 每条是 `{ key, seq, result }`，`key` 是 prompt 与参数（剔除 `label`、`phase` 后）的 SHA256，`result` 是该次 `agent()` 的完整结果。恢复时用 `resumeFromRunId` 把旧 run 的 journal 载入引擎，对 agent 调用序列做逐条比较，

- **命中**，`key` 一致，旧结果直接返回，不重新执行；
- **首个未命中**，视为新脚本与旧脚本在此处分歧，该点之后的 journal 失效并从该点重新执行，新结果续写。

`label` 与 `phase` 被剔除出 `key`，意味着改标签、改阶段名不影响命中；恢复宽容的是展示性参数，不宽容 prompt 本身。

确定性沙箱在这里才有价值，脚本能取系统时间或随机数时，同前缀重放就不可预期。需要时间戳的场景由调用方经 `args` 传入。

两点边界要讲清，

- resume 依赖的是**同一套 agent 调用序列（prompt 与参数一致）**的前缀复用，不是通用断点续跑。固定脚本每次展开的调用序列稳定，前缀命中率高；临时生成的脚本每次重写，拆解与 prompt 随之变化，`key` 会对不上——恢复对固定 `scriptPath` 的脚本才可靠，对随机生成的脚本帮助有限。
- 恢复后的执行开销与命中率没有实测数据。

磁盘按 mtime 保留最近 50 个 run，超出自动清理。

## 限制与边界

**适合交给 workflow，**

- 可拆解的独立并行子任务，扇出收益大于单次请求开销
- 同构批量，同一套规则过 N 个文件、N 个 commit，输出可比对
- 阶段质量门禁，plan → code → review → fix 各有验收
- 需要跨进程证据的流程，脚本副本、日志、快照都留在磁盘

**不适合**（ultracode skill 的原话语义），

- 单个 agent 就能做的小任务——直接做，不写脚本
- 需要紧密顺序对话的任务——用普通回合
- 内联做比写脚本更快的任务——脚本化是为固定与批量，不为形式完整

**当前实现的硬上限**（均可回溯代码），

- `maxConcurrency` 默认 3
- 单 run 总 `agent()` 调用上限 1000；`parallel` / `pipeline` 单次 items 上限 4096
- 单个 `agent()` 的 ReAct 循环默认 200 轮（agentType 定义可覆盖）
- 子工作流嵌套仅一层
- token 预算按输出 token 计费，预算耗尽即停；工具参数面未设置 `budgetTotal` 时视为不限

**细节，**

- 模型分层是脚本字段，审计类用 haiku、核心改写用 sonnet，写在哪就那么执行。成本策略随脚本可见、可审查，不藏在 LLM 的临时判断里。
- 监控走 `/workflows` 面板，阶段与 agent 的实时状态、token、工具调用数，数据经 2 秒轮询拉取（拉取间隔为当前实现值，非承诺）。
- 编排模板与委托写法见 [ultracode 使用建议](/peri-cool/docs/features/ultracode-usage/)。

## 相关实现

- [Multi Agent 架构](/peri-cool/posts/engineering/multi-agent/)，workflow 的执行体从哪来，子代理的派发与并发限制
- [RCRA 中断恢复逻辑](/peri-cool/posts/engineering/rcra-interruption-recovery/)，回合级中断恢复与 workflow 的 journal 恢复是两个层次
- [System Prompt 设计](/peri-cool/posts/engineering/system-prompt/)，workflow agent 复用的冻结系统提示词机制

Source: https://konghayao.github.io/peri-cool/posts/engineering/workflow-design/index.mdx
