| name | statemachine-use |
| description | 「ステートマシンを実行して」「ステートマシンを作成/作って」「YAMLワークフローを動かして」「ワークフローを回して」「エージェントループを起動して」「このYAMLを実行して」などで発動。作成モード(手順を.statemachine/{名前}/に生成)と実行モード(YAMLをLLM駆動で実行)を持つ。 |
| metadata | {"version":"2.1.0","tier":"experimental","category":"workflow","tags":["statemachine","yaml-workflow","agent-loop","hybrid-execution"]} |
YAML ステートマシン スキル
YAMLと外部マークダウンファイルで定義されたLLM駆動ステートマシンを作成・実行します。
モードの選択
| ユーザーの意図 | モード |
|---|
| 「〜という手順でステートマシンを作って」 | 作成モード |
| 「〜を実行して」「〜を動かして」「YAMLを回して」 | 実行モード |
作成モード
ユーザーが自然言語で説明した手順を .statemachine/{名前}/ フォルダ以下のYAML+マークダウンに落とし込む。
ステップ1: 利用可能なスキルを調査する
ls .github/skills/
出力されたスキル名を記録する。アクション定義でスキル呼び出しを活用できる場合に参照する。
ステップ2: 手順を状態遷移として分解する
LLM読み飛ばし防止の設計原則(重要)
- ルーティングロジックをアクションに書かない — 分岐判断はトランジション条件に書く
- 出力形式を強制する — 条件が評価しやすいキーワード出力を要求する(例:
PASS / FAIL)
- 将来のステートをヒントとして含めない — アクションは現在のステートの作業のみを指示する
- アクションの末尾に単一指示を付与する — 全アクションmdの末尾に必ず下記を追記する:
この指示に従ってタスクを実行してください。
完了後、指定された形式で出力のみを返してください。次のステップは別途指示されます。
- スクリプトは原則作成しない — スキル(ステップ1で確認)や他のAI機能でアクションを実行する
- スキルへ移譲するときはスキル名を明記する — アクション本文に
`skill-name` スキル と書く。この記法が無いと実行ハーネスはスキルを読み込まず、スクリプトの場所も分からない
- 成功条件を
output_validator で定義する — 「第1行が OK か FAILED」のような機械が判定できる出力契約を states に書く。書かないとアクションの成否を確認できず、失敗したまま次のステートへ進む
- 成果物の正しさは
check で測る — output_validator が見るのは書式だけで、「OK」と書くのはモデル自身である。成果物が実際に仕様どおり動くかを見るには、ハーネスが実行する検査コマンドを宣言する(下記)
check — 遷移の材料を自己申告から実測へ移す
states:
implement:
action_file: actions/implement.md
output_validator: "startswith:OK"
check: "python3 -m pytest tests/test_x.py -q"
check_retries: 2
transitions:
- from: implement
to: review
condition_rule: "equals:check_ok:true"
検査が落ちたら、測った不一致を課題文へ足して同じステートをやり直す。再投入を使い切っても
落ちるなら、実行を止めて escalate(この段では解けない = 上位の段へ回すシグナル)を返す。
ローカルモデルで定型作業を回すなら、これが受入率を決める唯一のレバーである。実測では
検知を伴わない分解は受入を下げ(0/3)、決定的な検知 + 再投入で 3/3 になった。逆に通る課題に
ゲートは課金しない(呼び出し回数は増えない)。書式・使える宣言の形・失敗時の動作は
references/schema.md の「決定的検査 (check)」を参照。作例は examples/gated_implement.yaml。
検査を置ける単位で割る — ステートを細かく割ること自体に効果は無い(実測では逆に下がる)。
分解の目的は検査を差し込む場所を作ることである。1 つのステートを設計するとき、
「このステートの成果は、どのコマンドの終了コードで測れるか」を先に決める。決められないなら、
そのステートはまだ割り方が正しくない。
実行できるスクリプトの範囲(ハーネスが強制する。定義側もこれに合わせて書く):
| 決まり | 意味 |
|---|
アクション本文が名指しした .py / .js / .sh、または移譲先スキルの SKILL.md に載っている .py / .js / .sh のみ | scripts/ に置いてあるだけの下請けは呼べない |
固定インタプリタで実行する(.py→python / .js→node / .sh→bash または sh) | shebang や実行ビットで走らせるものを決めさせない |
| スキル名は実行コマンドではない | `demo` スキル の demo を command に置いても動かない。スクリプトのパスを書く |
bash -c などの任意シェルは使えない | シェル経由の合成コマンドは拒否される |
| コマンドの stdout が空でも exit 0 なら成功 | 出力の有無で成否を判定しない。空の結果は正常な空結果 |
パターンの自動検出 — 詳細テンプレートは references/patterns.md を参照:
| 手順の特徴 | 適用するパターン |
|---|
| 「同時に」「並列で」「〜と〜を一緒に」 | Fan-out/Fan-in |
| 処理後に次があるか確認してループ | ContinueAsNew Loop |
| 副作用の大きい操作(変更・デプロイ等)の後 | ゲートステート |
| 複雑な判断・推論を含むステート | ReActアンカリング |
| 「失敗したら元に戻す」「ロールバック」 | Saga |
| 5ステート以上の長いワークフロー | マイルストーンアンカー |
ステップ3: scaffold で骨組みを生成する
フォルダとファイルを手で書かない。ステップ2で決めた状態列を scaffold へ渡す:
python .github/skills/statemachine-use/scripts/scaffold.py {名前} \
--state "first_state:説明" --state second_state
--state ID[:説明] を実行順に並べる。終端は --terminal ID[:説明](省略時は complete を自動で足す)。
.statemachine/{名前}/ に workflow.yaml と actions/*.md スタブを生成し、生成直後に検証する
(通らない骨組みは残さない)。
- 骨組みは直列遷移。分岐・ループ・複雑な条件(
conditions/{from}_to_{to}.md)はステップ4で足す。
- 新スキーマの口(
output_validator / check / check_retries / check_on_exhausted / write)は
コメント付きで含まれる——ステップ2で決めた検査コマンドのコメントを外して実値にする。
ステップ4: スタブを埋める
生成された workflow.yaml と actions/*.md を以下の形へ埋める。全フィールドの仕様は references/schema.md を参照:
name: "ワークフロー名"
initial_state: first_state
context:
config:
max_steps: 30
states:
state_id:
description: "ラベル"
action_file: actions/state_id.md
output_key: result_key
terminal: false
transitions:
- from: state_id
to: other_id
condition: "自然言語条件"
priority: 1
actions/{state_id}.md:
## [state_id: 何をするか]
(スキル呼び出しや具体的な指示)
**入力:** {{input}}
**前のステートの出力:** {{last_output}}
**出力形式:** XXX または YYY の一語のみで回答してください。
この指示に従ってタスクを実行してください。
完了後、指定された形式で出力のみを返してください。次のステップは別途指示されます。
conditions/{from}to{to}.md(複雑な条件のみ):
以下の条件をYES/NOで評価してください:
- {{retry_count}} が {{max_retries}} 未満である、かつ
- 最後の出力が RETRY で始まる
両方を満たす場合のみ YES と回答してください。
作成例
name: "コードレビュー"
initial_state: analyze
states:
analyze:
action_file: actions/analyze.md
output_key: analysis_result
approve:
action_file: actions/approve.md
terminal: true
request_revision:
action_file: actions/request_revision.md
terminal: true
transitions:
- from: analyze
to: approve
condition: "analysis_result が PASS で始まる"
priority: 1
- from: analyze
to: request_revision
condition: "analysis_result が PASS 以外で始まる"
priority: 2
<!-- .statemachine/review_code/actions/analyze.md -->
## [analyze: コード品質を分析する]
以下のコードを品質の観点で分析してください。
**対象コード:** {{input}}
確認項目: バグ、コードの臭い、エラーハンドリング漏れ、パフォーマンス問題
**出力形式:** 最初の行に PASS / MINOR / MAJOR / CRITICAL のいずれか一語、その後に問題点を列挙してください。
この指示に従ってタスクを実行してください。
完了後、指定された形式で出力のみを返してください。次のステップは別途指示されます。
定義のメンテナンス — migrate
スキーマは加算的に拡張される(check → check_on_exhausted → write)。手持ちの定義は
migrate で検査し、追随させる:
python .github/skills/statemachine-use/scripts/migrate.py path/to/workflow.yaml
python .github/skills/statemachine-use/scripts/migrate.py .statemachine --apply
検出項目: check 宣言の無いステートからの check_* 分岐(検証エラー・修正案の提示)、
シェル記号入り check(投入前に落ちる)、write 未割付(編集対象が一意に決まる場合だけ提案)、
check_on_exhausted の暗黙既定の明示化。後ろ 2 つはコメントを保ったまま --apply で書き換える。
正規化・検証は engine.py の 1 実装を使うので、判定が実行系とずれることはない。
実行モード
⛔ ハーネス実行プロトコル — 禁止行動(違反時は即座に停止して再確認)
| 禁止行動 | 代替行動 |
|---|
| アクション実行前に条件リストを取得する | ① 実行 → 出力確定 → ② 条件取得 の順を守る |
| 現在のステート以外の作業を実行する | 現在のステートの作業のみ実行する |
## [現在のステート: {state_id}] 宣言を省略する | 毎ステートの冒頭で必ず宣言する |
| 条件を評価せずに遷移先を独断で決める | 必ず ④ の Python スクリプトで遷移先を確定する |
| 複数ステートをまとめて実行する | 1ステート = 1ターンを厳守する |
Step 0: 検証と開始ステートの取得
python .github/skills/statemachine-use/scripts/run_machine.py .statemachine/{名前}/workflow.yaml --dry-run
python .github/skills/statemachine-use/scripts/next_state.py {名前} --initial-state
出力された state_id を現在のステートとして実行を開始する。
Step 1〜N: ステートループ(terminal まで繰り返す)
現在のステートに入ったことを宣言する(毎ステート必須):
## [現在のステート: {state_id}]
① アクションを実行する(LLM)
現在のステートのアクションプロンプトを実行し、出力を last_output として記録する。
重要: アクション実行前に条件を確認してはならない。出力が確定してから条件リストを取得する。
② 条件を自動評価する(Python)
状態値は --context の JSON オブジェクトで渡す(last_output と各 output_key)。
python .github/skills/statemachine-use/scripts/next_state.py {名前} \
--state {現在のstate_id} --auto-eval \
--context '{"last_output":"{last_outputの第1行}"}'
遷移先がここで確定する応答は 2 形。どちらも ③④ を飛ばして ⑤ へ進む:
auto_advance: true — 無条件トランジション。conditions は返らず next_state が遷移先。
resolved が null 以外 — condition_rule だけで確定。
auto_advance が省くのは条件評価だけ。① のアクション実行と output_validator による成功確認は省略しない。
アクションが失敗したステートから遷移してはならない。
resolved が null の場合のみ needs_llm_eval: true の条件を LLM で評価する。
旧ハーネス互換として --list-conditions / --last-output / --output KEY=VALUE も受け付ける。
旧引数は --context に無いキーの補完としてのみ効く。新規の呼び出しでは使わない。
③ 残った条件を評価する(LLM)
needs_llm_eval: true の条件のみ last_output に対して YES / NO で評価し、JSON を構築する:
{"1": false}
(needs_llm_eval: false の条件インデックスは省略可。--eval 渡し時に自動上書きされる)
④ 遷移先を確定する(Python)
python .github/skills/statemachine-use/scripts/next_state.py {名前} \
--state {現在のstate_id} --eval '{"1": false}' \
--context '{"last_output":"{last_outputの第1行}"}'
出力: 次の state_id、NONE(一致なし)、TERMINAL(終端)
condition_rule がある条件は --context から自動評価され、--eval の値を上書きする。
⑤ 完了を記録する
## [ステート {state_id} 完了]
- 出力: {last_outputの第1行}
- 遷移先: {次のstate_id または TERMINAL}
⑥ 遷移 or 終了
- 次の
state_id → そのステートへ移動して Step 1 に戻る
TERMINAL → 実行完了、最終出力を表示
NONE → on_no_transition 設定に従う(デフォルト: エラー)