Files
maestro/docs/tools/spawnsubtask.md
T
oss-sync 57702b4f34
CI / build-and-test (push) Canceled after 0s
sync: update from private repo (2336949a)
2026-09-17 11:29:31 +00:00

124 lines
7.4 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.
# SpawnSubTask
タスクを並列サブタスクに分解して実行する。各サブタスクは独立した worker(ジョブ)で動き、完了後に親タスクが結果を集約する。
> **デフォルト無効(opt-in)**。SpawnSubTask は Bash などと同じセンシティブ扱いのツールで、初期状態ではエージェントに提示されない。使うにはワークスペースの **設定 → ツール** タブで明示的に有効化する。有効化していない場合、SpawnSubTask は呼べず、分解は [`Delegate`](./delegate.md)(直列・インプロセス)で行う。
>
> タスク分解の既定手段は Delegate(直列)。research / general などのピースは Delegate でサブ調査を 1 件ずつこなす。SpawnSubTask は「壁時計時間の短縮のために独立テーマを本当に並列で走らせたい」ワークスペースだけが有効化する。
## 基本
```js
SpawnSubTask({
title: "ローカル LLM 比較調査",
instruction: "Ollama, vLLM, llama.cpp の最新性能ベンチマークを比較する。各ツールについて: 1) 直近6ヶ月の主要ベンチマーク, 2) ハードウェア要件, 3) 対応モデル一覧 を output/report.md にまとめる。",
piece: "research" // 任意。指定しないと自動分類
})
```
呼び出すと `subtasks/{連番}-{ジョブID先頭8桁}/`(例: `subtasks/1-9f0c1a2b/`)にサブタスクのワークスペースが作られ、結果はそこに集約される。ワークスペースはスペース内の全タスクで共有されるため、連番だけでは別タスクのサブタスクと同じディレクトリになってしまう。ジョブ ID を含めることで踏み合いを防いでいる。
## いつ使うか
まず Delegate(直列)で足りるかを検討する。SpawnSubTask は opt-in で有効化したうえで、**並列の別ジョブで壁時計時間を縮めたい**場合に限って使う。
### 並列分解が効果的なケース
- 2 つ以上の **独立したテーマ**(互いに参照しない)
- 各テーマが軽くなく、調査・処理に時間がかかる
- 分解後の各タスクが単独でも意味を持つ成果物になる
- 壁時計時間の短縮が GPU・実行枠の追加消費に見合う
例:
- 「3 つの製品比較レポート」→ 製品ごとに 3 サブタスク
- 「複数 PDF の OCR 処理」→ ファイルごとに分解
- 「複数 SNS の情報収集」→ プラットフォーム別に分解
### 分解しないほうがよいケース
- 単一テーマで論理的に連続する処理(A→B→C のように依存)
- サブタスクが極端に小さい(オーバーヘッドの方が大きい)
- 全体像を見ながら判断する必要がある作業(対話的タスク等)
## instruction の書き方
- **完結した依頼文**で書く(親タスクの文脈を持たないので、サブタスクは instruction だけで判断する)
- 期待する成果物(出力ファイル名・場所)を明示
- 必要な前提情報があれば文中に展開
❌ 「これと同じ調査を別キーワードでやって」
✅ 「キーワード『A』『B』『C』について、各々のメリット・デメリットを比較する独立した調査を行い、output/A-vs-B.md にまとめる」
## piece の指定
- 省略時: 親と同じ classifier ロジックで自動選択
- 明示する場合: `research`, `general`, `office-process` 等の piece 名を指定
## WaitSubTask(完了待ち)
`SpawnSubTask` でサブタスクを起動したあとは、**`WaitSubTask`(引数なし)を 1 回呼んで全サブタスクの完了を待つ**。
```js
SpawnSubTask({ title: "A の調査", instruction: "..." })
SpawnSubTask({ title: "B の調査", instruction: "..." })
SpawnSubTask({ title: "C の調査", instruction: "..." })
WaitSubTask() // ここで全部の完了を待つ
// 再開後、自分が起動したサブタスクの result.md を Read して集約 → complete
```
- `WaitSubTask` は **同期的にブロックしない**。呼ぶとジョブはいったん停止(park)して worker を解放し、全サブタスクが終わると **同じ movement のまま再開** する
- 再開時、各サブタスクの結果は そのサブタスクのワークスペース直下の `result.md` に書き出されている。再開時のシステム通知が自分のサブタスクのパスを列挙するので、そこに挙がったものだけを Read して集約レポートを作る
- 起動した分の成果物は 同じワークスペースの `output/` 以下にも残る
- `subtasks/` 直下には**別タスクのサブタスクも並ぶ**。通知に挙がっていないディレクトリは自分のものではないので読まない
### ワークフロー
1. `SpawnSubTask` を必要な数だけ呼ぶ(並列に起動される)
2. `WaitSubTask()` を 1 回呼ぶ
3. 再開後、通知に挙がった result.md を読んで集約する
4. `complete` で終了する
### auto-park(取りこぼし防止)
サブタスクがまだ走っている最中に `complete`(success)を呼んでも、エンジンが**偽の完了を返さず、自動で park して完了を待つ**。`WaitSubTask` を呼び忘れても結果を取りこぼさない安全網。とはいえ意図を明示するため、原則 `WaitSubTask` を明示的に呼ぶこと。
### orphan の自動キャンセル
親ジョブが**失敗・キャンセル・リトライ**したとき、まだ走っているサブタスクは**自動でキャンセル**される。親が消えたのに子だけ GPU を使い続ける「ゾンビサブタスク」は発生しない。
ただし **ASK(ユーザー回答待ち)で停止した場合は子を残す**。親があとで再開して結果を使うため。
### delegate との違い
| | delegate | SpawnSubTask + WaitSubTask |
|---|---|---|
| 既定 | **デフォルト有効**(分解の既定手段) | **デフォルト無効**(要 opt-in 有効化) |
| 実行 | 直列・インプロセス | 並列・別ジョブ |
| 待機 | 自動(1 ツール呼び出しで完結) | `WaitSubTask` で待つ |
| 負荷 | GPU・実行枠が軽い | 並列ぶん多く消費する |
| 向く用途 | 逐次の分解・重い委譲全般 | 独立テーマを並列で短時間化したいとき |
## 起動の間隔(spawn_stagger_ms)
短時間に大量の `SpawnSubTask` を呼ぶと、その一団が GPU スロットの優先順位を一気に奪ってしまう。これを避けるため、`SpawnSubTask` のキュー投入の間に既定で 1 秒の間隔を入れる。
```yaml
subtasks:
spawn_stagger_ms: 1000 # SpawnSubTask 投入間の間隔(ミリ秒、デフォルト 1000、0 で無効)
```
- `0` にすると間隔を入れず即時に投入する
- エージェントから見た呼び出し方は変わらない(透過的に間隔が入るだけ)
## 結果の参照
サブタスク完了後、親タスクは:
- 各サブタスクのワークスペース(`subtasks/{連番}-{ジョブID先頭8桁}/`)直下の `result.md` に要約結果がある(Read で読む)
- 同じワークスペースの `output/` 以下に成果物がある
- どのディレクトリが自分のものかは、再開時のシステム通知が列挙する
## 制限
- ネスト深さは `subtasks.maxDepth`(デフォルト 2)まで
- サブタスクが waiting_human 等で停止すると親もブロックされる