跳到主内容
快讯直播
AI智模界
教程

上手 Waymark:编译式工作流引擎搭可重跑长任务流水线

适用场景

这类方案适合手上有一批要跑几十分钟到几小时的长任务的人:批量文件处理、数据清洗、批量模型推理、报表生成等,步骤之间有先后依赖,中途断一次就得从头再来。团队还需要“同样的输入跑出同样的结果”,出了问题能定位到具体是哪一步。

Waymark 这类“编译式”工作流引擎的思路是:先把作业定义编译成一份带哈希的执行计划,再按计划执行;每一步的产物与状态都保存到磁盘,重跑时自动跳过已完成且输入未变的步骤。它要解决的核心问题就是长任务的断点续跑、失败重试和结果可复现。

环境与前置条件

  • 操作系统:Linux 或 macOS;Windows 建议在 WSL2 里操作。
  • 运行时:示例脚本使用 Python 3,请先确认 python3 --version 可正常执行;Waymark 本体按官方文档给出的方式安装,具体版本要求以官方文档当前版本为准。
  • 硬件:没有硬性门槛。CPU 4 核以上、内存 8 GB 起比较从容;如果流水线里含模型推理,按模型自身要求准备显存,并把推理单独拆成一个作业,方便单独限制并发。
  • 磁盘:状态库、缓存、中间产物都会占空间,经验值是原始数据大小的 2~3 倍,长任务要额外预留。
  • 其他:能稳定访问依赖的数据源;一个独立的项目目录;作业定义建议放进 Git 仓库管理。

分步骤部署

第 1 步:安装并确认 CLI 可用

按官方文档的方式安装完成后,先确认命令行工具本身可用:

```bash

waymark --version

waymark --help

```

成功标志:--version 打印出版本信息,--help 列出可用子命令。不同版本的子命令名可能略有差异,下文用到的 compile、run、status、logs 请先和 --help 对一遍,以自己的版本为准。

第 2 步:初始化项目

```bash

mkdir -p ~/work/long-pipeline && cd ~/work/long-pipeline

waymark init --name long-pipeline

```

这一步会生成工作区骨架:作业定义目录(示例里叫 jobs/)、全局配置文件(示例里叫 waymark.yaml)、隐藏的状态目录(.waymark/,存放编译出的计划、运行状态、日志和缓存)。

成功标志:ls -a 能看到这三样;waymark status 不报错,只提示还没有运行记录。

把 jobs/ 纳入版本控制,.waymark/ 下的运行产物不要提交;但计划文件建议单独复制一份归档,方便日后复盘某次结果是怎么算出来的。

第 3 步:定义第一个作业(抽取)

新建 jobs/01-extract.job.yaml:

```yaml

name: extract

description: 把原始文件汇总成清单

inputs:

  • name: source_dir

type: path

default: ./data/raw

outputs:

  • name: manifest

path: ./data/staging/manifest.json

command: |

python3 scripts/extract.py \

--src "${source_dir}" \

--out ./data/staging/manifest.json

timeout: 2h

retry:

max_attempts: 3

backoff: exponential

initial_delay: 30s

resources:

cpu: 1

```

几个要点:

  • inputs 参与步骤键的计算,输入变了这一步就必须重算;outputs 声明产物位置,引擎据此判断产物是否齐全。
  • command 要尽量幂等:同样的输入跑两次,产出同样的结果。做不到幂等就别开自动重试,否则会写出重复数据。
  • timeout 给长任务保留一个上限,避免它一直挂到第二天早上。

第 4 步:定义依赖作业与批处理

新建 jobs/02-infer.job.yaml,用 needs 声明依赖:

```yaml

name: infer

needs: [extract]

inputs:

  • name: manifest

from: extract.manifest

  • name: batch_size

type: int

default: 200

outputs:

  • name: results

path: ./data/out/results/

command: |

python3 scripts/infer.py \

--manifest "${manifest}" \

--out ./data/out/results/ \

--batch-size "${batch_size}"

timeout: 6h

retry:

max_attempts: 2

backoff: exponential

initial_delay: 60s

```

from: extract.manifest 表示这个输入直接引用上游作业的产物,引擎会在执行前校验它是否存在。批处理脚本里建议按批次写文件,例如 results/batch-0001.jsonl,这样中途失败重跑时只需补算缺的批次,而不是整个作业从头来一遍。

再建一个 03-report.job.yaml,写 needs: [infer],把结果汇总成一份报告。三个作业连起来就是一张有向无环图。

第 5 步:编译成执行计划

这一步是“编译式”和边跑边解析的引擎之间的关键差别:

```bash

waymark compile --job report

```

编译阶段会解析全部作业定义,检查依赖是否齐全、有没有环、上下游的输入输出能不能对上,然后把整条链路固化成一份带内容哈希的计划文件,落在 .waymark/plans/ 下。

成功标志:输出类似 plan written to .waymark/plans/<hash>.plan.json,退出码为 0。如果依赖写错、出现循环依赖,错误在这一步就会暴露,不会等到任务跑到一半才出问题。

建议打开严格检查开关(具体参数名以 --help 为准),把警告当错误处理,并把计划文件交给同事 review。执行前就能看懂整条链路,是编译式流程相对解释式流程的主要收益。

第 6 步:干跑校验

```bash

waymark run --job report --dry-run

```

干跑只输出将要执行的步骤顺序、每步是命中缓存还是实际执行,不会真的运行命令。确认顺序是 extract -> infer -> report 再往下走。

第 7 步:正式执行

```bash

waymark run --job report --concurrency 2

```

  • --concurrency 控制并行度。批量推理这类吃显存的步骤,压到 1 或 2;纯 IO 的步骤可以放开一些。
  • 终端会实时打印步骤状态。中途 Ctrl+C 或断网都没关系,状态库里保留的是“哪几步已经成功”。

成功标志:最后一行显示整条流水线成功,waymark status --job report 里所有步骤都是成功状态。

第 8 步:验证可重跑性

不要跳过这一步。立刻原样再跑一次:

```bash

waymark run --job report

```

预期:所有步骤都命中缓存或被跳过,几秒内结束,不会重新执行推理脚本。如果它又把推理跑了一遍,说明步骤键里混进了不确定的东西,常见的是命令里带了当前时间戳、随机数、临时目录路径,或者上游产物写入时文件修改时间参与了哈希。把这些不确定性去掉,可重跑才真正成立。

第 9 步:制造一次失败,验证重试与续跑

不必等真的网络挂掉。把 infer 的 max_attempts 临时改成 1,在 scripts/infer.py 开头加一行 raise SystemExit("simulated failure"),然后:

```bash

waymark run --job report --retry-failed

```

预期:extract 命中缓存直接跳过,infer 失败并记录日志,report 因上游失败被标记为跳过,而不是误报失败。把脚本改回去,再执行一次同样的命令:

```bash

waymark run --job report --retry-failed

```

预期:只有 infer 和 report 实际执行,extract 依旧跳过。这就是断点续跑的实际效果,长任务里代价很高的一种情况——从第一步重来——被避开了。

查看失败现场:

```bash

waymark logs --job report --step infer --tail 200

```

验证部署是否成功

按下面四条逐条确认,全过就算部署完成:

1. 编译能过:waymark compile --job report 退出码为 0,.waymark/plans/ 下出现新的计划文件。

2. 干跑顺序正确:waymark run --job report --dry-run 打印的步骤顺序与依赖一致,没有意外多出的步骤。

3. 重跑命中缓存:完整跑一次后再原样跑一次,第二次在很短时间内结束,状态显示全部跳过。

4. 失败能续跑:人为让中间步骤失败,修好后用 --retry-failed 只补跑失败步骤及其下游,上游不重算。

第 3、4 条是这套方案的核心价值。只过第 1、2 条说明流水线能跑起来,但还不能“可靠地重跑”,先别急着接真实生产数据。

常见报错与解决

1. Error: compiled plan is stale (source changed after compile)

→ 原因:改了作业定义或脚本,但没有重新编译,引擎拒绝用旧计划执行。

→ 解决:

```bash

waymark compile --job report

waymark run --job report

```

建议把“编译”固定进流程,比如在提交前检查里跑一次 compile,避免带着过期计划上生产。

2. Error: dependency cycle detected: extract -> infer -> extract

→ 原因:needs 写成了环,或者两个作业互相引用对方的产物。

→ 解决:把公共部分抽成一个没有上游的独立作业,两边都去 needs 它,然后重新编译验证:

```bash

waymark compile --job report

```

编译通过就说明环解开了。

3. step "infer" failed: exit status 1,日志里是连接超时或 Connection reset

→ 原因:外部数据源或服务抖动,属于典型的可重试错误。

→ 解决:确认脚本幂等后加重试策略,再手动补跑。

```yaml

retry:

max_attempts: 3

backoff: exponential

initial_delay: 60s

```

```bash

waymark run --job report --retry-failed

```

注意:如果失败发生在“文件写了一半”的时刻,先清理该批次的不完整产物再重跑,否则重试可能读到脏数据。

4. Error: state directory is locked by another run

→ 原因:同一个作业同时有两个进程在跑,比如上次的进程没退干净,终端关了但后台还在。

→ 解决:先确认没有真实在跑的任务,再处理锁。

```bash

ps aux | grep waymark

waymark run --job report --run-id manual-$(date +%s)

```

日常建议给定时任务和手动执行用不同的 --run-id,别让两条并发写同一份状态。

5. No space left on device 或产物写入失败

→ 原因:中间产物和缓存把磁盘占满,长任务尤其容易遇到。

→ 解决:先看占用分布,再用引擎自带的清理子命令清掉旧运行记录(具体子命令以 --help 为准),并把 outputs 指到容量更大的挂载点。

```bash

df -h .

du -sh .waymark/* | sort -h

```

后续维护

  • 备份:至少要备份两样东西——作业定义(放 Git 仓库即可)和状态库(.waymark/ 目录或外置数据库)。状态库丢了不影响计算结果正确性,但会让所有缓存失效,长任务等于重跑一遍,这个代价要提前算清楚。按天做快照比较稳妥。
  • 升级:升级 CLI 前先用当前版本跑一次 compile 并留存计划文件,升级后重新 compile 对比差异,确认没有语义变化再跑真实作业。跨大版本升级先在测试目录里跑通再动生产;版本兼容策略以官方文档当前版本为准。
  • 日志:步骤日志按运行 ID 存放,定期归档或清理,并给日志目录设容量上限,避免长任务把盘写满。关键步骤建议在脚本里自己再打一行结构化日志(批次号、耗时、条数),排查时比翻引擎日志更快。
  • 监控:盯三个指标——单步骤耗时(相比上次是否明显变慢)、失败率与重试次数、缓存命中率。缓存命中率突然掉到接近零,通常是输入里混进了时间戳、随机数之类的非确定性内容,发现得越早越省事。
  • 并发与配额:吃资源的步骤单独限制并发,不要整条流水线共用一个并发数;给长步骤设合理 timeout,即便超时也至少能留下可诊断的日志。
  • 文档:在仓库 README 里写清两条规矩——“改完定义先 compile 再 run”“重跑用 --retry-failed”,新人上手会少踩很多坑。

AI 生成本文由 AI 基于公开信息自动生成,仅供参考。