ワークフロー(workflow.yml)
ステップ・入力と出力・型付きの値、分岐・ループ・並列実行、チェックポイント、v1 からの移行。
lait run <FILE> [PROMPT] は、YAML で書いたワークフローを実行します。ワークフローは
ステップ(steps:)を上から順に実行し、各ステップの結果(値)が次のステップの入力に
なります。LLM 呼び出し・エージェント・コマンド・jq による変換・人間への確認・分岐・並列・
ループを同じ形式のステップとして組み合わせられます。
このページはワークフローファイル バージョン 2 の仕様です。バージョン 1(nodes: と
use: を使う形式)からの書き換えは バージョン 1 からの移行 を
参照してください。エディタで補完・検証を効かせたい場合は lait schema workflow
が出力する JSON Schema を使えます。
最小構成
lait.config.yml の default.model が設定済みであれば、次の1ステップだけで動きます。
# workflow.yml
steps:
- prompt: "次の文章を3行で要約してください。\n{{ input }}"
lait run workflow.yml "要約したい文章"
ステップを並べると、前のステップの結果が次のステップの {{ input }} になります。
steps:
- id: summary
prompt: "次の文章を3行で要約してください。\n{{ input }}"
- prompt: "次の要約を英訳してください。\n{{ input }}"
- write: summary.en.txt
設計の考え方
- 1ステップ = 1つの種類: 各ステップは
prompt:/agent:/run:などの 種類キーを ちょうど1つ 持ち、そのキーでステップの種類が決まります。種類ごとに使えるフィールドが 決まっており、他の種類のフィールドを書くと読み込み時にエラーになります。 - 値は型付き: ステップ間を流れるのは JSON の値です。LLM のテキスト応答やコマンドの 出力は文字列、構造化出力や jq の結果はその JSON のまま次のステップに渡ります。文字列が たまたま JSON に見えても勝手に解釈し直されることはありません。
- 入出力の宣言: ワークフローが受け取る名前付きパラメータは
inputs:で宣言し、型を JSON Schema で指定します。未宣言の入力や型違いは実行前にエラーになります。結果はoutput:で整形できます。 - 読み込み時の検証: ステップの形、フィールドの組み合わせ、id の重複、スキーマ参照、
jq・テンプレートの構文、
break/stop/ask/writeを置ける場所は、最初のステップを 実行する前にすべて検査されます。
ファイルの構成
| キー | 内容 |
|---|---|
version |
任意。2 のみ(省略時は 2)。1 や nodes: を持つファイルは移行案内付きのエラーになります。 |
name / description |
任意。実行時に進捗表示へ出力されます。 |
inputs |
名前付き入力の宣言(入力と出力)。 |
output |
ワークフローの結果を計算する jq 式(入力と出力)。 |
timeout |
このファイル1回の実行にかけられる秒数の上限(エラー処理)。 |
default |
LLM ステップ(prompt/agent)の既定値(モデル設定)。 |
models |
このファイルで使うモデルエイリアス(lait.config.yml の models: と同じ形式)。 |
schemas |
名前付き JSON Schema(スキーマ)。 |
agents |
インラインのエージェント定義(agent)。 |
steps |
実行するステップの列(必須、1つ以上)。 |
version: 2
name: triage
description: 問い合わせを分類して返信を下書きする
inputs:
tone: { type: string, default: 丁寧, description: 返信の口調 }
output: '{category: $steps.classify.category, reply: .}'
default:
model: local
schemas:
classification:
type: object
properties:
category: { type: string, enum: [bug, question, other] }
required: [category]
additionalProperties: false
steps:
- id: classify
prompt: "次の問い合わせを分類してください。\n{{ input }}"
output_schema: classification
- prompt: |
{{ inputs.tone }}な口調で、{{ steps.classify.category }} への返信を書いてください。
問い合わせ: {{ steps.classify }}
ステップ
ステップの種類
| 種類キー | 分類 | 内容 |
|---|---|---|
prompt |
アクション | プロンプトテンプレートで LLM を呼び出す |
agent |
アクション | エージェント定義(システムプロンプト・スキーマ・設定の組)で LLM を呼び出す |
run |
アクション | コマンドを実行する(シェルを経由しない) |
decide |
アクション | Jev 互換 API に型付きの質問(yes/no・選択・スコア)を送り、確率つきの答えを得る |
jq |
アクション | jq 式で値を変換する |
ask |
アクション | 端末で人間の回答を受け取る |
write |
アクション | 値をファイルに書き出す(値はそのまま次へ) |
workflow |
アクション | 別のワークフローファイルを子として実行する |
group |
制御 | ステップ列を1つのステップとしてまとめる |
switch |
制御 | 条件に最初に一致したケースを実行する |
parallel |
制御 | 複数のステップ列を同時に実行する |
for_each |
制御 | 配列の各要素に対してステップ列を実行する |
while / until |
制御 | 条件を満たす間/満たすまで繰り返す |
stop / break |
制御 | ワークフロー/ループを打ち切る |
種類キーが無い、または2つ以上あるステップは読み込み時にエラーになります。
共通フィールド
すべてのステップは種類キーに加えて次のフィールドを持てます(stop/break は
retry/timeout/on_error を除く)。
| フィールド | 内容 |
|---|---|
id |
このステップの値を steps.<id>(テンプレート)/$steps.<id>(jq)として記録します。ファイル内で一意である必要があります。 |
when |
jq の条件式。偽(false/null)ならステップを丸ごとスキップし、値はそのまま次へ渡ります。 |
input |
jq 式。ステップが処理する値を、流れてきた値から計算します(省略時は .)。 |
output |
jq 式。ステップの結果(.)を、このステップの値に変換します。 |
retry |
失敗時の再試行(max_attempts/delay_seconds/backoff)。 |
timeout |
1回の試行の秒数上限。 |
on_error |
再試行を使い切っても失敗したときに代わりに実行するステップ列。 |
1つのステップは次の順で処理されます。
whenを評価し、偽ならスキップinput→ 種類ごとの処理 →output(この3つがまとめてretry/timeoutの対象)- 失敗したら
on_errorを実行し、その結果をステップの値にする idがあれば値を記録する
id は英字または _ で始まり、英数字・_・- だけを含む名前です($steps で - を含む
id を参照するときは $steps["my-id"] と書きます)。id を持たないステップの値は記録されず、
進捗表示では step-<番号> (<種類>) と表示されます。
steps:
- id: fetch
run: [curl, -s, "https://example.com/api/items"]
output: fromjson # 文字列の JSON を値に変換
retry: { max_attempts: 3, delay_seconds: 2, backoff: 2.0 }
timeout: 30
- when: '.items | length > 0'
input: '.items[0]' # 最初の要素だけを処理する
prompt: "次の項目を説明してください: {{ input.title }}"
値とテンプレート・jq
値の流れ
- 最初のステップの入力は
PROMPT(CLI 引数、または標準入力)で、文字列 です。inputs:を宣言したワークフローはPROMPTを省略でき、その場合の最初の入力はnullです。 - 各ステップの値が次のステップの入力になります。値の種類はステップによって決まります。
| ステップ | 値 |
|---|---|
prompt/agent(output_schema なし) |
応答テキスト(文字列) |
prompt/agent(output_schema あり) |
応答をパースした JSON(スキーマで検証済み) |
run |
標準出力(文字列、末尾の改行1つを除去) |
decide |
応答の answers(質問 id をキーにしたオブジェクト、宣言順) |
ask |
回答(文字列) |
jq |
jq 式の結果 |
write |
入力そのまま |
workflow |
子ワークフローの結果 |
group/switch/while/until |
最後に実行されたステップの値 |
parallel |
branch 名をキーにしたオブジェクト(宣言順) |
for_each |
各要素の結果の配列(要素順) |
テキストが必要な場所(ユーザーメッセージ、コマンドの標準入力、write の内容、lait run の
最終出力)では、値は テキスト形式 に変換されます。文字列はそのまま、それ以外はコンパクトな
JSON です。文字列の JSON を値として扱いたい場合は output: fromjson のように明示的に変換します。
テンプレート(handlebars)
prompt/system/ask/run の各引数/write のパス/files/images、エージェントの
システムプロンプトは handlebars テンプレートです。
| 変数 | 内容 |
|---|---|
{{ input }} |
このステップの入力 |
{{ steps.<id> }} |
記録済みのステップの値 |
{{ inputs.<name> }} |
ワークフローの入力 |
{{ loop.index }} / {{ loop.item }} |
最も内側の for_each(index と item)・while/until(index)の情報(0 始まり) |
{{ x }}は値のテキスト形式を出力します。オブジェクトや配列はコンパクトな JSON に なります([object]にはなりません)。{{ json x }}は常に JSON として出力します (文字列も引用符付き)。フィールドは{{ input.city }}のようにアクセスできます。- 存在しない変数・フィールドを参照するとエラーになります(strict mode)。
{{#each}}/{{#if}}などの handlebars の組み込みヘルパーも使えます。
jq 式
when/input/output/jq/switch の when/for_each/while/until/with/
トップレベルの output は jq 式です。. が対象の値で、グローバル変数
$steps/$inputs/$loop がテンプレートの steps/inputs/loop に対応します。
- jq 式は ちょうど1つの値 を返す必要があります。0個・複数個はエラーです(ストリームを
まとめたいときは
[.items[]]のように配列で囲みます)。 - 条件(
whenなど)は jq の真偽判定に従い、false/nullだけが偽です。 - 未記録の
$steps.xや未宣言の$inputs.xは(jq の通常の挙動どおり)nullになります。lait lintはこうした参照を検出します。
入力と出力
inputs: と --input
inputs: は入力名から JSON Schema への対応です。default を持つ入力は省略可能、それ以外は
必須です。型名だけを書く短縮形(text: string)も使えます。
inputs:
text: string
lang: { type: string, default: ja, description: 出力言語 }
max_items: { type: integer, default: 5 }
tags: { type: array, items: { type: string }, default: [] }
steps:
- prompt: "{{ inputs.lang }} で {{ inputs.max_items }} 件にまとめてください: {{ inputs.text }}"
lait run summarize.yml --input text="$(cat article.md)" --input lang=en --input tags='["a","b"]'
--input KEY=VALUEは繰り返し指定でき、同じキーは後の指定が勝ちます。type: stringと宣言した入力は値をそのまま文字列として受け取ります(--input id=0012は"0012")。それ以外の入力は、JSON として解釈できれば JSON の値として、できなければ文字列と して受け取ります。- 値は宣言したスキーマで検証されます。宣言されていない名前を渡すとエラーになります。
- テンプレートからは
{{ inputs.<name> }}、jq からは$inputs.<name>で参照します。 子ワークフローには呼び出し側の入力は見えません(with:で明示的に渡します)。
output:
トップレベルの output: は、最後のステップの値(.)・$steps・$inputs からワークフローの
結果を計算する jq 式です。省略すると最後の値がそのまま結果になります。stop で終了した場合も
適用されます。
output: '{summary: $steps.summary, translated: .}'
アクションステップ
prompt — LLM を呼び出す
prompt: はユーザーメッセージのテンプレートです。
steps:
- id: extract
prompt: "次の文章から都市名を抽出してください。\n{{ input }}"
system: "あなたは正確な情報抽出器です。"
model: cloud
temperature: 0.2
output_schema:
type: object
properties: { city: { type: string } }
required: [city]
additionalProperties: false
| フィールド | 内容 |
|---|---|
system |
システムプロンプトのテンプレート。省略時は default.system。 |
model / reasoning_effort / temperature / top_p / max_tokens |
モデルとサンプリング設定(モデル設定)。 |
mcp / max_tool_rounds / skills / subagents / tools |
ツール連携(ツール連携)。 |
files |
内容を添付するテキストファイル(テンプレート)。ファイル名付きのコードブロックとしてユーザーメッセージの後ろに追記されます。 |
images |
添付する画像のパスまたは http(s):// URL(テンプレート)。vision 対応モデル向けです。 |
input_schema |
入力の検証に使うスキーマ(スキーマ)。 |
output_schema |
Structured Outputs で要求するスキーマ。応答は JSON としてパースされ、スキーマで検証されます。 |
schema_name |
Structured Outputs のスキーマ名。省略時は schemas: の名前、それもなければ structured_output。 |
files/images のパスはコマンドを実行したカレントディレクトリからの相対パスです。
agent — エージェント定義で呼び出す
agent: は、システムプロンプト・入出力スキーマ・モデル設定・ツールをまとめた エージェント
定義 を使って LLM を呼び出します。ユーザーメッセージはステップの入力(テキスト形式)です。
エージェントのシステムプロンプトは入力を {{ input }} として(steps/inputs/loop も
参照可能)レンダリングされます。
agent: の値は次のいずれかです。
- このファイルの
agents:に定義した名前 - エージェント Markdown ファイルへのパス(
/を含むか.mdで終わるもの。ワークフロー ファイルからの相対パス) lait.config.ymlのagents:に登録した名前
agents:
reviewer:
model: cloud
tools: [ripgrep]
output_schema: { file: schemas/review.json }
system: |
あなたはコードレビュアーです。次の差分をレビューしてください。
{{ input }}
steps:
- id: review
agent: reviewer
- agent: ./agents/summarize.md # ファイル
temperature: 0.3 # ステップ側で上書き
- agent: researcher # lait.config.yml の agents: に登録済み
agents:の各エントリは エージェント Markdown ファイル の frontmatter と 同じフィールドに、本文に相当するsystem:(必須)を加えたものです。- ステップ側に書けるのは
model/サンプリング設定/ツール連携/files/imagesです。いずれも ステップ → エージェント定義 → ワークフローのdefault:→lait.config.ymlのdefault:の 順にフォールバックします。 - エージェントが
input_schemaを持つ場合、入力はそのスキーマで検証されます。output_schemaを持つ場合、結果はパース済みの JSON になります。
run — コマンドを実行する
run: は実行するプログラムと引数の配列です。シェルを経由せず直接実行 するため、
テンプレートの展開結果が別の引数やコマンドとして解釈されることはありません。
inputs:
base: { type: string, default: main }
steps:
- run: [git, diff, --stat, "{{ inputs.base }}"]
- run: [jq, -r, .title] # 入力は標準入力に渡されます
- run: [sh, -c, 'wc -l | tr -d " "']
output: tonumber
- 入力のテキスト形式が標準入力に渡され、標準出力(UTF-8、末尾の改行を1つ除去)が値に
なります。JSON を出力するコマンドは
output: fromjsonで値に変換できます。 - 終了コードが 0 以外なら失敗です(標準エラー出力がエラーメッセージに含まれます)。
- 標準出力・標準エラーはそれぞれ最大 16 MiB まで保持します。超えるとコマンドを停止して 失敗にします。
- プログラムのパスはカレントディレクトリ基準(または
PATHから検索)です。
env/cwd で、コマンドから見える環境変数と作業ディレクトリを制限できます。意味は
カスタムシェルツールの tools: の同名フィールドと同じです。
| フィールド | 説明 |
|---|---|
env(任意) |
子プロセスに渡す環境変数の許可リスト。1件以上指定すると、コマンドはここに列挙した変数だけを見ます(lait 自身が継承している API キーなどは引き継がれません)。省略時は lait の環境変数をそのまま継承します。値は ${VAR_NAME} で環境変数を参照できます(テンプレート {{ }} は展開しません)。 |
cwd(任意) |
コマンドの作業ディレクトリ。省略時は lait のカレントディレクトリを継承します。${VAR_NAME} 展開に対応します(テンプレートは展開しません)。 |
steps:
- run: [gh, pr, list, --json, title]
env:
PATH: "${PATH}" # command[0] の名前解決に必要
GH_TOKEN: "${GH_TOKEN}"
cwd: "${HOME}/src/project"
env を1件でも指定すると完全な許可リストになるため、プログラムを PATH から探す場合は
PATH も含めてください。参照した環境変数が未定義の場合、そのステップはエラーになります。
decide — Jev 互換 API で判定する
decide: は、入力を state として Jev 互換 API(lait.config.yml の jev: で
設定)に送り、型付きの質問への答えを値にします。文章は生成しません。
steps:
- id: triage
decide:
urgent: { type: noul, instructions: 今日中に対応が必要か }
team:
type: choice
criteria: { billing: 支払い・請求書, sales: null }
- switch:
- when: .urgent.noul > 0.8
steps:
- jq: '"escalate to " + $steps.triage.team.choice'
else:
- jq: '"queue"'
- 質問の型は
noul(はい/いいえの確率)・choice(選択肢)・score(順序つきレベル)です。 質問の書き方、model:フィールド、応答の形は Jev 互換 API で判定する を参照して ください。 default.retry/default.timeoutが適用されます。--replay中は実行できません。
jq — 値を変換する
steps:
- jq: '{title: .title, tags: [.tags[] | ascii_downcase]}'
モデルを呼び出さずに値を変換します。結果の値が次のステップに渡ります。
ask — 人間に尋ねる
ask: は質問のテンプレートです。回答(文字列)がステップの値になります。
steps:
- id: draft
prompt: "次の依頼への返信を下書きしてください: {{ input }}"
- id: confirm
ask: "この下書きで送信しますか?\n{{ steps.draft }}"
choices: ["yes", "no"]
default: "no"
- when: '. == "no"'
stop: true
| フィールド | 内容 |
|---|---|
choices |
回答をこのいずれかと完全一致に制限します。一致しなければエラーです(再入力は求めません)。 |
default |
標準入力が端末でないとき(CI・パイプ実行など)に使う回答。choices があればそのいずれかである必要があります。 |
multiline |
true なら1行ではなく EOF まで読み取ります。 |
- 標準入力が端末のときは質問を標準エラー出力に表示して1行(末尾の改行を除く)を読みます。
- 標準入力が端末でないときは 何も読まず、
defaultがあればそれを、なければエラーにします。 parallelの branch や並行for_eachの中(子ワークフローを含む)では使えません。
write — ファイルに書き出す
write: は書き出し先のパスのテンプレートです。入力のテキスト形式をファイルに書き、
入力をそのまま 次のステップへ渡します。
inputs:
name: string
steps:
- id: summary
prompt: "要約してください: {{ input }}"
- write: "out/{{ inputs.name }}.md"
- prompt: "英訳してください: {{ input }}" # 要約がそのまま渡る
- パスはカレントディレクトリからの相対パスです。既存ファイルは上書きし、親ディレクトリは 作成しません。
- 並行
for_eachの中では、固定のパス(テンプレートを含まないパス)には書けません。要素ごとに 異なるパス(例:out/{{ loop.index }}.md)にしてください。
workflow — 別のワークフローを呼び出す
workflow: は別のワークフローファイルを子として実行し、その結果(子の output)をこの
ステップの値にします。
inputs:
lang: { type: string, default: ja }
steps:
- id: summary
workflow: ./shared/summarize.yml
with: '{lang: $inputs.lang, max_lines: 3}'
# shared/summarize.yml
inputs:
lang: string
max_lines: { type: integer, default: 5 }
output: '{summary: .}'
steps:
- prompt: "{{ inputs.lang }} で {{ inputs.max_lines }} 行に要約: {{ input }}"
workflow:の値は、ワークフローファイルへのパス(/を含むか.yml/.yamlで終わるもの。 呼び出し側ファイルからの相対パス)か、lait.config.ymlのworkflows:に登録した名前です。- 子の最初の入力は、このステップの入力です。
with:は子のinputs:に渡すオブジェクトを計算する jq 式です。子の入力として宣言され、 スキーマで検証されます。- 子の
default:/models:は呼び出し側の値より優先され、子に無い項目は呼び出し側の値に フォールバックします。schemas:/agents:/stepsの id はファイルごとに独立しています。 - 子の中の
stopは子の実行だけを終了します。子のtimeout:は子の実行時間を制限します。 - 循環呼び出しはエラーになり、ネストの深さにも上限があります。
制御ステップ
group — ステップをまとめる
group: はステップ列を1つのステップとして実行します。複数のステップにまとめて when/
retry/timeout/on_error を適用したいときに使います。
inputs:
url: string
steps:
- group:
- id: fetch
run: [curl, -sf, "{{ inputs.url }}"]
- prompt: "要約してください: {{ input }}"
retry: { max_attempts: 2 }
on_error:
- jq: '"取得に失敗しました: \(.error)"'
switch — 条件分岐
switch: はケースの配列です。上から順に when を評価し、最初に真になったケースの steps
を実行します。どれも一致しなければ else: を実行し、else: が無ければエラーになります。
steps:
- id: triage
prompt: "緊急度を判定してください: {{ input }}"
output_schema: { type: object, properties: { severity: { type: string } }, required: [severity] }
- switch:
- when: '.severity == "high"'
steps:
- prompt: "緊急対応メモを書いてください: {{ input }}"
- when: '.severity == "medium"'
steps:
- prompt: "返信文を下書きしてください: {{ input }}"
else:
- jq: '"対応不要"'
parallel — 並列実行
parallel: は branch 名からステップ列への対応です。すべての branch が同じ入力を受け取って
同時に実行され、結果は branch 名をキーにしたオブジェクト(宣言順)になります。
steps:
- id: analysis
parallel:
sentiment:
- prompt: "感情を一言で: {{ input }}"
keywords:
- prompt: "キーワードを3つ: {{ input }}"
output: '"感情: \(.sentiment) / キーワード: \(.keywords)"'
- どれか1つの branch が失敗すると
parallel全体が失敗します。 - branch の中で記録した
steps.<id>はその branch の中でだけ有効です。branch の外へは 結果のオブジェクトを通して渡します。 - branch の中で
stopは使えません。breakは branch の中のループに対してだけ使えます。askは使えません。 - 進捗表示は
[branch 名] [番号] ラベルの形式で行が入り交じります。
for_each — 配列の各要素に対して実行
for_each: は配列を返す jq 式です。各要素を入力として steps を実行し、結果を配列
(要素の順)にします。
steps:
- for_each: '.items'
max_concurrency: 4
steps:
- prompt: "{{ loop.index }} 番目の項目を要約: {{ input }}"
- write: "out/item-{{ loop.index }}.md"
output: 'map(select(. != ""))'
for_eachの式は配列を1つ返す必要があります(.items[]のようなストリームではなく.items)。空配列なら0回実行され、結果は空配列です。- 本体では
{{ loop.item }}/$loop.item(要素)と{{ loop.index }}/$loop.index(0 始まりの位置)が使えます。 max_concurrency(既定 1)が2以上なら要素を並行に処理します。その場合、本体はparallelの branch と同じ扱いで、steps.<id>は要素ごとに独立し、stop/break/askは使えず、writeはパスにテンプレートを含む必要があります。逐次実行(既定)では記録したsteps.<id>は要素をまたいで引き継がれ、breakで打ち切れます。
while / until — 条件ループ
while:/until: は条件の jq 式で、steps(本体)と max_iterations(必須、1以上)を
持ちます。各反復の結果が次の反復の入力になります。
schemas:
validation_result:
type: object
properties:
valid: { type: boolean }
text: { type: string }
required: [valid, text]
steps:
- until: '.valid == true'
max_iterations: 5
steps:
- prompt: "前回の結果を見直して修正し、妥当かどうかも判定してください: {{ input }}"
output_schema: validation_result
whileは各反復の 前 に評価し、偽なら終了します(0回もありえます)。untilは各反復の 後 に評価し、真になったら終了します(必ず1回以上実行されます)。max_iterationsに達しても条件を満たさない場合は エラー です(黙って打ち切りません)。- 本体では
{{ loop.index }}/$loop.index(0 始まりの反復回数)が使えます。
stop / break — 早期終了
stop: true はワークフロー(このファイル)を、その時点の値を結果として正常終了します。
break: true は最も内側の for_each/while/until を、その時点の値で打ち切ります。
どちらも when と組み合わせて使うのが基本で、output で値を整形することもできます。
schemas:
judgement:
type: object
properties:
sufficient: { type: boolean }
answer: { type: string }
required: [sufficient, answer]
steps:
- id: check
prompt: "情報が十分か判定し、十分なら回答してください: {{ input }}"
output_schema: judgement
- when: '.sufficient'
output: '.answer'
stop: true
- prompt: "不足している情報を尋ねる質問を1つ作ってください: {{ input }}"
breakはループの本体(switch/group/on_errorを経由してもよい)の中でだけ使えます。parallelの branch や並行for_eachをまたぐことはできません。stopはparallelの branch や並行for_eachの中では使えません。falseは指定できません(stop: true/break: trueのみ)。
スキーマ(schemas:)
JSON Schema は次の3通りで指定できます。
| 書き方 | 意味 |
|---|---|
output_schema: city |
schemas: に定義した名前 |
output_schema: { type: object, ... } |
スキーマ本体を直接記述 |
output_schema: { file: schemas/city.json } |
JSON ファイル(このワークフローファイルからの相対パス) |
schemas:
city:
type: object
properties: { city: { type: string } }
required: [city]
additionalProperties: false
weather: { file: schemas/weather.json }
schemas:に無い名前を参照すると読み込み時にエラーになります。{file: ...}以外のオブジェクトはスキーマ本体として扱います。- 入力・出力の検証では
type(型の配列を含む)・enum・requiredを、properties/itemsを通して再帰的に確認します。format・pattern・数値の範囲・additionalProperties・oneOf/anyOf/allOf・$refは検証しません(Structured Outputs の制約はサーバー側で 適用されます)。
モデル設定と既定値(default: / models:)
default: は LLM ステップ(prompt/agent)の既定値です。
default:
model: local
reasoning_effort: medium
temperature: 0.7
system: "あなたは日本語で簡潔に答えるアシスタントです。"
mcp: [filesystem]
retry: { max_attempts: 3, delay_seconds: 1 }
timeout: 60
| キー | 内容 |
|---|---|
model / reasoning_effort / temperature / top_p / max_tokens |
モデルとサンプリング設定 |
system |
prompt ステップの既定のシステムプロンプト |
mcp / max_tool_rounds / skills / subagents / tools |
ツール連携 |
retry / timeout |
モデルを呼び出すステップ(prompt/agent/decide)の既定の再試行・タイムアウト |
- 各項目はそれぞれ独立して、ステップ → (
agentならエージェント定義)→ ワークフローのdefault:→lait.config.ymlのdefault:の順にフォールバックします(system/retry/timeoutはlait.config.ymlにはフォールバックしません)。 retryはフィールドごとではなく、まとまり全体でフォールバックします。temperature(0.0〜2.0)・top_p(0.0〜1.0)・max_tokens(1以上)の範囲外の値は 読み込み時にエラーです。
models: は lait.config.yml の models: と同じ形式で、ワークフロー内のエイリアスが同名の
設定ファイル側のエイリアスより優先されます。provider.api_key/provider.base_url には
${VAR_NAME} で環境変数を埋め込めます(設定ファイル)。
models:
local:
- provider: { base_url: http://localhost:1234/v1 }
model_id: local-model
cloud:
- provider:
base_url: https://api.example.com/v1
api_key: ${CLOUD_API_KEY}
model_id: cloud-model
ツール連携(mcp / skills / subagents / tools)
LLM ステップ・エージェント定義・default: には、lait.config.yml に登録した名前で
ツールを渡せます。モデルがツール呼び出しを返すたびに lait が実行して結果を返す、という
やり取りを最大 max_tool_rounds 回(既定 8)まで繰り返します。
| キー | 内容 | 詳細 |
|---|---|---|
mcp |
MCP サーバーのツール(<サーバー名>__<ツール名>) |
MCP |
skills |
システムプロンプトの末尾に追記するスキル | スキル |
subagents |
エージェントをツールとして呼び出す(agent__<名前>) |
サブエージェント |
tools |
カスタムシェルツール(tool__<名前>) |
カスタムシェルツール |
steps:
- prompt: "{{ input }} について調べて要約してください。"
mcp: [filesystem]
subagents: [researcher]
max_tool_rounds: 12
output_schemaと併用できます。ツールを呼び出している間はresponse_formatを送らず、 最後のラウンドだけ付けて再送します。--streamとも併用できます。- ツール呼び出しはステップの
retryの単位に含まれます(再試行すると副作用のある ツール呼び出しもやり直されます)。
エラー処理(retry / timeout / on_error)
steps:
- id: call
prompt: "{{ input }}"
timeout: 30
retry:
max_attempts: 3 # 初回を含む総試行回数(必須、1以上)
delay_seconds: 1 # 最初の再試行までの待機秒数(既定 0)
backoff: 2.0 # 再試行のたびに待機時間に掛ける倍率(既定 1.0)
on_error:
- jq: '{fallback: true, reason: .error}'
retry/timeoutはinput→ 処理 →outputをまとめた1回の試行に対して働きます。 タイムアウトした試行も失敗として再試行の対象になります。待機時間は最大1時間です。on_errorは、再試行を使い切っても失敗したときに、ワークフローを失敗させる代わりに実行 されます。入力は{"error": "<失敗内容と原因のチェーン>", "input": <このステップに流れてきた値>}で、on_errorの結果がこのステップの値になります(idがあれば記録されます)。- 制御ステップ(
group/switch/parallel/for_each/while/until)にもretry/timeout/on_errorを指定できます。本体全体が1つの単位として扱われます。 default.retry/default.timeoutはモデルを呼び出すステップ(prompt/agent/decide)にだけ適用されます。- 実行全体(
lait runの中断や上位のtimeout)がキャンセルされた場合はon_errorは 実行されません。
ワークフロー全体のタイムアウト(timeout:)
トップレベルの timeout: は、このファイル1回の実行にかけられる秒数の上限です。超過すると
実行中のステップがキャンセルされ、エラーで終了します(--checkpoint を付けていれば、その
時点までの状態が保存されます)。子ワークフローとして実行される場合も、その子の実行時間に
適用されます。
パスの解決
| 対象 | 基準 |
|---|---|
agent: のファイルパス、workflow: のファイルパス、{file: ...} のスキーマ(schemas:・ステップ・agents:) |
そのワークフローファイルのディレクトリ |
エージェント Markdown ファイル内の {file: ...} のスキーマ |
そのエージェントファイルのディレクトリ |
files/images/write、run のプログラム |
コマンドを実行したカレントディレクトリ |
ワークフローを構成するファイル(エージェント・子ワークフロー・スキーマ)はワークフローと 一緒に置けば、どこから実行しても同じように解決されます。実行時のデータ(添付・出力先)は 実行した場所が基準です。
並行実行の制約
parallel の branch と max_concurrency が2以上の for_each の本体は並行に実行される
ため、次の制約があります。いずれも読み込み時(子ワークフローはその読み込み時)に検査されます。
| 対象 | 制約 |
|---|---|
stop |
使えません |
break |
本体の中のループに対してだけ使えます |
ask |
使えません(子ワークフローの中も含む) |
write |
並行 for_each の中では、テンプレートを含まない固定のパスに書けません(子ワークフローの中も含む) |
steps.<id> |
並行に実行される単位ごとに独立し、外には伝わりません |
実行計画の表示(lait run --dry-run)
--dry-run を付けると、モデルの呼び出し・MCP サーバーの起動・コマンドの実行を一切行わずに、
実行計画を標準出力に表示します。
lait run workflow.yml "本文" --input lang=en --dry-run
- 各ステップの順序と種類、LLM ステップの解決後のモデル ID と
base_url、実効のretry/timeout、制御構造の中身を表示します。モデルが解決できない場合は実行時と同じエラーになります。 - テンプレートは値が分かっている範囲(
inputsと、最初のステップのinput)でレンダリング され、それ以外は[unrendered: ...]付きの原文で表示されます。 - 子ワークフローは展開されません(その子に対して
--dry-runを実行してください)。
グラフ出力(lait graph)
lait graph <FILE> はステップの流れを Mermaid(既定)または DOT(--format dot)の
グラフとして出力します。
lait graph workflow.yml
lait graph workflow.yml --format dot | dot -Tsvg > workflow.svg
アクションは矩形、制御ステップは菱形、stop/break は端点として描かれ、switch の各
ケースには条件が、ループ本体・branch・on_error にはサブグラフが付きます。子ワークフローは
展開されません。
実行の再開(lait run --checkpoint / --resume)
--checkpoint を付けると、トップレベルのステップが完了するたびに状態(現在の値・記録済みの
steps・inputs)を .lait/runs/<run-id>.json に保存します。失敗した実行は --resume で
最後に完了したトップレベルステップの直後から再開できます。
lait run workflow.yml "本文" --checkpoint
# note: run checkpointed as '20260903-141233-123456789-12345-0'; resume with `lait run workflow.yml --resume 20260903-141233-123456789-12345-0`
lait run workflow.yml --resume 20260903-141233-123456789-12345-0
lait runs list
lait runs show 20260903-141233-123456789-12345-0
- 再開の単位はトップレベルのステップです。制御ステップは中断した場合、最初からやり直されます
(中の
run/writeなどの副作用が二重に起きることがあります)。 --resumeではFILEは保存時と同じである必要があり、PROMPTは使われません。--inputを指定しなければ記録されたinputsを使い、指定すればその値で進めて記録も 上書きします。チェックポイントには入力値がそのまま保存されるので、機密情報の扱いに注意して ください(.gitignoreには.lait/が含まれています)。- 完了済みのステップ列(
id、無ければstep-<位置>)が現在のファイルと異なる場合は再開を 拒否します。未実行の範囲だけが変わっている場合は警告して再開します。 --resumeは--checkpointを含意します。正常に完了した実行はstatus: completedとして 残り、再開はできません。- 以前の形式(バージョン 1 のワークフロー用)のチェックポイントは再開できません。
そのほか
<FILE>はパスの代わりにlait.config.ymlのworkflows:に登録した名前でも指定できます (設定ファイル)。- 通常は最終結果のテキスト形式だけを標準出力に出します。
--outputでファイルに、--jsonで CLI 用の JSON として出力します。進捗は標準エラー出力に出ます。 - 設定・ワークフロー・子ワークフロー・エージェント・スキーマの読み込み中も Ctrl-C で中断 できます。実行時に読み込む各ファイルは最大 16 MiB です。
lait lintは、未宣言のinputs.*参照・存在しないsteps.*参照・読み込めない スキーマ/エージェント/子ワークフロー・未登録のツール名・使われていないschemas:/agents:を報告します(lint)。
バージョン 1 からの移行
バージョン 2 は互換性のない変更です。バージョン 1 のファイル(nodes: を持つもの、または
version: 1)は読み込み時に移行を促すエラーになります。主な対応は次のとおりです。
| バージョン 1 | バージョン 2 |
|---|---|
nodes: に定義して steps: から use: |
ステップに直接書く。再利用したい LLM 設定は agents: に、処理のまとまりは子ワークフローに |
type: prompt / prompt: / system_prompt: |
prompt: / system: |
system_prompt だけのノード(入力をそのまま送る) |
prompt: "{{ input }}" と system: |
type: agent / agent:(カレントディレクトリ基準) |
agent:(ワークフローファイル基準、または agents:/設定ファイルの名前) |
type: command / command: |
run: |
type: transform / jq: |
jq: |
ノードの jq:(出力の加工) |
ステップの output: |
ノードの write_file: |
後続の - write: <パス> ステップ(パスはテンプレート可) |
type: ask / prompt: |
ask: |
type: workflow / workflow: |
workflow:(with: で入力を渡せる) |
json_schemas: の {schema: ...} / {file_path: ...} |
schemas: にスキーマ本体 / {file: ...} |
output_schema: <パス>(名前が無ければパス扱い) |
output_schema: {file: <パス>}(文字列は常に schemas: の名前) |
エージェントの structured_output: true + output_schema |
output_schema のみ |
switch: {cases: [{id, when, steps}], else} |
switch: [{when, steps}] と else: |
parallel: {branches: [{id, steps}], join} |
parallel: {<名前>: [...]} と output: |
loop: {while/until, max_iterations, steps} |
while:/until: と max_iterations:・steps: |
for_each: {items, steps, join, max_concurrency} |
for_each: <式> と steps:・max_concurrency:・output: |
on_error: {steps: [...]} |
on_error: [...] |
use: と stop: true を同じステップに |
アクションの後に - stop: true(条件付きなら group でまとめる) |
default.system_prompt / default.workflow_timeout |
default.system / トップレベルの timeout: |
lait run --var KEY=VALUE / {{ vars.x }} / $vars.x |
inputs: で宣言して --input KEY=VALUE / {{ inputs.x }} / $inputs.x |
| 出力は常に文字列で、次の入力で JSON として再解釈 | 型付きの値。文字列の JSON は output: fromjson で変換 |
| jq の複数出力は改行で連結 | 1つの値のみ。[...] で配列にまとめる |
{{ input }} にオブジェクトを渡すとエラー |
コンパクトな JSON として出力 |
ノードを使わないステップの id は省略時ノード id |
id を書いたステップだけが記録される |
書き換えの例です。
# バージョン 1
json_schemas:
city:
schema: { type: object, required: [city] }
nodes:
extract:
type: prompt
prompt: "都市名を抽出: {{ input }}"
output_schema: city
jq: '.city'
write_file: city.txt
steps:
- id: extract
use: extract
- loop:
until: '. != ""'
max_iterations: 3
steps:
- use: extract
# バージョン 2
schemas:
city: { type: object, required: [city] }
steps:
- id: extract
prompt: "都市名を抽出: {{ input }}"
output_schema: city
output: '.city'
- write: city.txt
- until: '. != ""'
max_iterations: 3
steps:
- prompt: "都市名を抽出: {{ input }}"
output_schema: city
output: '.city'