自律ゴールループ(Goal Loop)
会話の中でゴールを1つ渡すだけで、AI従業員は自律的に計画・実行・自己検収し、完了または行き詰まったときに戻ってきて知らせてくれます。このページでは、チャンネルから /goal を使う方法、自律度(AutonomyLevel)の段階、関連する設定キー、そして人による対応が必要になったときのボタンの意味を説明します。
ディスパッチエンジンは v1.59 以降デフォルトで有効です(ゴールタスクがないときは定期的な SQLite ポーリングのみを行い、タスクが実際に review に入って初めて LLM 呼び出しが発生します——後述の「二段階検収判定」を参照)。通常の一問一答の会話には一切影響しません。不要な場合は config.toml に [dispatch] enabled = false を設定するか、ダッシュボードの「設定 → 自動化」で「ディスパッチエンジン(派工引擎)」のスイッチを切ってください(再起動不要でホットリロードされます)。
/goal コマンド
Section titled “/goal コマンド”接続済みのどのチャンネル(Telegram / Discord / Slack / LINE / …)からでも、AI従業員に対して次のように入力します。
| コマンド | 動作 |
|---|---|
/goal <ゴールの説明> |
現在の会話の AI従業員に割り当てられた自律ゴールタスクを作成します。検収基準を別途指定しない場合は、ゴールの説明そのものが検収基準になります。 |
/goal <ゴール> || <検収基準> |
|| で区切ります。前半がゴール、後半が検収基準(ジャッジが照合する基準)です。 |
/goal <ゴール> || <検収基準> || outcome:<spec> |
さらに構造化された成果物検収の層を追加します(後述の「構造化された成果物検収」を参照)。納品前にゼロコストの決定的チェックを実行し、基準未達ならジャッジを呼ばずにそのまま修正へ差し戻します。 |
/goal status |
現在の AI従業員が進行中のゴールタスクを一覧表示します(短縮コード/状態/何ラウンド目か)。 |
/goal |
使い方を表示します。 |
例
/goal この顧客データをまとめて月次レポートを作り、送信して || レポートには月次売上グラフを含め、boss@example.com に送ること/goal Q3の月次レポートを作成 || 月次売上グラフを含めること || outcome:files:report.docx作成すると、タスクの短縮コード、ラウンド上限、そして「完了または行き詰まったらここで通知します」という確認メッセージが返信されます。タスクの進捗と人的対応が必要な通知は、AI従業員の [proactive] 通知チャンネルだけでなく、あなたがゴールを作成したこの会話(発信元チャンネル)にも押し戻されます。
ディスパッチエンジンが無効(
[dispatch] enabled = false)の場合でもタスクは作成されますが、確認メッセージで自動的には実行が始まらない旨が案内されます。
ゴール契約:作成時に凍結され、後からこっそり変更できない
Section titled “ゴール契約:作成時に凍結され、後からこっそり変更できない”ゴールを作成した瞬間、検収基準は不変のベースライン(acceptance_criteria_baseline)として凍結されます。以後のすべての判定——一次評価器、MAV検収ジャッジ——はこの凍結されたベースラインのみを読み、後から編集される可能性のあるフィールドは決して参照しません。これは両方向の「契約のこっそり変更」を防ぐためのものです。AI従業員がタスク実行中に自分で検収基準を緩めることはできず、また運用者もダッシュボードのフィールドを編集しただけで判定基準そのものを変更したと誤解することがありません。
- AI従業員は変更できない:エージェント身分で MCP の
tasks_updateを呼び出し、自分のゴールタスクのacceptance_criteriaを変更しようとしても、その呼び出しはまるごと拒否され、監査ログに1件記録されます(理由:goal_contract_frozen)。ゴールタスクのtitleとdescriptionはジャッジが読むゴールそのものなので、AI従業員のtasks_updateがどちらかを変更しようとした場合も同じように拒否されます。タスクの制御用タグ(outcome:…、grant:…、auto-research)も、AI従業員は追加・削除・並べ替えできません(理由:reserved_tag_change)。 - 運用者は表示用のコピーを編集できるが、判定基準そのものは変わらない:ダッシュボードの
tasks.updateはタスク上に表示されるacceptance_criteria(人が読むための補足など)を編集できますが、凍結されたベースライン値は連動して変わりません。ジャッジと評価器は引き続き、タスク作成時に設定された基準で判定を続けます。本当に検収基準を変えたいなら、それは新しいゴールを作ることと同じです。 - 凍結ベースラインを持たない古いタスク(この仕組みが導入される前に作成されたもの)は、変更可能なフィールドを読む従来通りの挙動にフォールバックします。
検収基準を指定しなかった場合のガイダンス
Section titled “検収基準を指定しなかった場合のガイダンス”/goal <ゴールの説明>(|| なし)でもいつも通りタスクは作成され、ゴールの説明そのものが検収基準になりますが、確認メッセージには次回に向けてより明確にするためのヒントが1段付け加わります。
💡 今回は検収基準を別途指定していません。次回はこの4点を考えておくとより的確になります。 ・ゴール:何を達成したいか ・入力:どんなデータや素材が必要か ・出力形式:成果物がどんな形か(例:Word/Excel/文章/画像) ・制約:スタイル、期限などの制限、そして「何をもって完了とするか」 具体的で結果が確認できる検収基準を3〜5個用意してみてください(「よくできている」ではなく「レポートに月次売上グラフを含む」のように)。 /goal 説明 || 基準 の形式で再提出すればOKです。|| で検収基準を明示している場合、このヒントは表示されません。planner_enabled を有効にしたサブタスク分解も同じ規律に従います。各検収基準は「結果」を書き「やり方」は書かない(HOW を凍結すると、別の正しい経路で作ったのに誤って失敗判定される可能性があります)、3〜5個に絞る(契約が重くなるほどサブタスクは収束しにくくなります)、範囲外の事項は検収基準に無理やり詰め込まず Non-goals として扱う、という点です。
検収台帳(検収基準の項目別管理)
Section titled “検収台帳(検収基準の項目別管理)”凍結された検収基準は一つのテキストです。ジャッジには項目ごとに確認するよう指示していますが、すべての項目が実際に扱われたことをプログラムで確かめる手段はありませんでした。検収台帳はそれを補います。
作成されるタイミング。 ダッシュボードまたは MCP tasks_create kind="goal" で作成したゴールは、作成時に凍結基準から台帳を持ちます。空でない行ごとに1項目、C1、C2… と順に番号が振られます。追跡は最大20項目で、21行目以降は注記付きで C20 に統合され、捨てられることはありません。システムは各項目に安定した id(canonical_id(タスク id, "criterion", 番号, 本文のフィンガープリント))も付けるので、モデルは短い番号を返すだけで、自分で id を作ることはありません。この機能より前に作成されたゴールや、モードが off のときに作成されたゴールには台帳がなく、従来どおりに動きます。チャットの /goal コマンドや、ゴール提案の確認(「立為目標任務」と「想一想」の両方)から作成したゴールにも同じ方法で台帳が作られます。autopilot ルールが作成したゴールと、プランナーが分解したサブタスクには台帳がありません。
実行者に見えるもの。 各ラウンドのディスパッチには <state> ブロックの直後に ## 驗收帳本 セクションが入り、項目ごとに1行で現在の状態と報告方法が示されます。実行者は tasks_complete の結果サマリーの最後にタグを付けます。
<criteria_status>[{"id": "C1", "status": "covered", "evidence": ["reports/summary.md を作成"], "unresolved": []}, {"id": "C2", "status": "blocked", "evidence": [], "unresolved": ["メール送信の権限がない"]}]</criteria_status>タグの中身はちょうど一つの JSON 配列でなければならず(前後に文章を置かない、余計なフィールドを入れない)、すべての番号がちょうど一回ずつ必要です。status は covered(evidence 必須、unresolved は空)、blocked(unresolved 必須)、candidate(完了したと考えているが確認が必要、evidence 必須)のいずれかです。evidence と unresolved の各項目は500文字で切られ、各フィールド最大8項目です。どれか一つでも違反した報告は丸ごと破棄されます。台帳はそのまま、invalid_reports が1増え、security_audit.jsonl に criteria_status_invalid イベント(違反内容と、マスク後のタグ本文最大200文字)が1件書かれます。タグのないラウンドは何も変えず、カウントもされません。ジャッジが読むテキストとチャットに返す ✅ メッセージからは、このタグが取り除かれます。
モード(config.toml [goal_loop] criteria_ledger、毎ラウンド読み込み、再起動不要):
| モード | 台帳 | ジャッジ |
|---|---|---|
off |
作成・注入・解析しない | 変更なし |
report(デフォルト) |
作成・注入・解析し、人に表示 | 実行者の自己申告による台帳を「自己申告であり証拠ではない」と明記した参考ブロックとして受け取る。返答形式は変わらない |
enforce |
report と同じ |
パネルは criteria: [{"id": "C1", "pass": true, "reason": "..."}] も返す必要があり、各番号はちょうど一回。欠落や重複はその項目の FAIL。correctness はすべての項目が合格し、かつその観点自体も合格したときだけ合格 |
不明な値は report として扱われます。enforce への切り替えは strict_reply_parsing と同じ観察期間の規律に従い、実際のジャッジラウンドを見てから自分で切り替えてください。enforce でも外部ジャッジ(judge = "external")には項目別の判定を求めず、その pass/fail がそのまま使われます。
確認できるもの。 tasks.timeline は criteria_ledger(台帳がなければ null)を返します。中身は mode(現在有効なモード)、units[](id、handle、text、status、evidence、unresolved、updated_round)、last_report_round、invalid_reports です。台帳を持つゴールが needs_human になると、通知カードに 驗收帳本:1/3 條已回報達成;C2 受阻(沒有寄信權限);C3 尚未回報 のような1行が加わります。各ラウンドの台帳はそのラウンドの iteration 記録にも残ります。
needs_human の pause_reason と台帳は別の問い(ループがなぜ止まったか/どの項目がまだ終わっていないか)に答えるもので、統合はしません。
外側の進捗ボード
Section titled “外側の進捗ボード”ゴールタスクの状態が遷移するたびに、発信元の会話へ短い(1〜3行程度の)進捗メッセージが押し戻されます。
- 実行開始/再試行(上限中の第Nラウンド)
- 検収中
- 却下 → 修正して再試行(ジャッジのフィードバック要約付き)
- 完了 ✅(結果の要約付き)
- 行き詰まり → あなたの判断が必要(同時に承認ボタンを送信、一時停止理由の分類付き——後述の「needs_human ボタンの意味」を参照)
同じタスク・同じ状態は重複して通知されません。発信元の会話が存在しない場合は AI従業員の [proactive] チャンネルにフォールバックし、両方とも存在しない場合はダッシュボードの Activity Feed に書き込まれるだけで、あなたを煩わせません。
タイムアウト時の進捗レポート
Section titled “タイムアウト時の進捗レポート”タスクが認領(in_progress)された後、[goal_loop] progress_report_minutes(デフォルト10分)を超えても観測可能な進捗シグナルが一切ない場合(Activity Feed のイベントを基準とします——updated_at フィールドは lease renewer によって定期的に更新されるだけなので「動いている」証拠にはなりません)、ドライバーは「実行開始からX分経過しても進捗報告がなく、まだ実行中です」という通知を1件送信します(Activity Feed +発信元の会話)。同一ラウンド内では最大1回のみです。
これはあくまで通知であり、介入ではありません。再ディスパッチも、エスカレーションも、タスクのキャンセルも行いません。実際に手を動かすのは従来通り stalled_secs(再ディスパッチ)、iteration_cap(人的対応へ)、wall_clock_hours(人的対応へ)の各ガードだけです。0(または負数)に設定するとこの機能全体がオフになり、オフの間は追加のクエリも一切発生しません。
ツール連打アドバイザリー(tool streak)
Section titled “ツール連打アドバイザリー(tool streak)”自律ループの中で、AI従業員が「同じツールを同じパラメータで何度も繰り返し呼び出す」ループにはまることがあります。結果はとっくに得られているのに、自分が繰り返していることに気づいていない状態です。これは後述の「停滞検知」とは異なります。停滞検知は連続する2ラウンド(ディスパッチ→検収)が揃って初めて行き詰まりを検知しますが、ツール連打アドバイザリーは1ラウンド内のツール呼び出しの並びを見ており、ジャッジが介入する前に先に注意を促せます。
判定基準は同じツール・同じマスク済みパラメータ(監査ログですでに行っている機密マスキングを再利用)の連続呼び出し回数で、閾値ごとに警告の強さが上がっていきます。
| 連続回数 | 警告内容 |
|---|---|
| 3回 | 前回の実行結果を読み直し、必要な情報がすでに得られていないか確認することを提案し、無駄な繰り返しでラウンドを消費しないよう促す |
| 5回 | 現在のやり方では進展していない可能性があると指摘し、別の方法や角度を試すよう提案する |
| 8回 | 繰り返しを止めるよう強く提案する。現時点で得られている結果をまとめて報告するか、tasks_block を呼んで何に阻まれているかを説明し助けを求める |
このリマインダーは次のラウンドのディスパッチの <state> ブロックに注入され、LLMコストはゼロで純粋にアドバイザリーです。ツール呼び出しやディスパッチをブロックしたり、リトライさせたり、拒否したりすることは一切なく、従うかどうかは AI従業員自身の判断に委ねられます。リマインダー自体は意図的に state_hash から除外されており、既存の(状態、行動)振動検知を妨げません。
config.toml [goal_loop] tool_streak_advisory(デフォルト true)でこの機能全体をオフにできます。
AutonomyLevel:自律度5段階
Section titled “AutonomyLevel:自律度5段階”各 AI従業員の自律度は agent.toml [capabilities] autonomy_level という1つのダイヤルで制御されます。未設定または解析不能な場合はデフォルトの Approver(保守的:行き詰まったときや人的対応が必要なときだけ質問する)になります。
| レベル | 動作 |
|---|---|
operator |
ループはまったく自律駆動しません。タスクは作成後静止し、人が手動で進めます。 |
collaborator |
最初のディスパッチ前に人による承認(キックオフ承認)が必要です。承認後は完了まで自律的にリトライします。 |
consultant |
collaborator と同じキックオフ承認が必要です。 |
approver |
デフォルト。キックオフのゲートはなく、行き詰まったとき、または本当に人的対応が必要なときだけエスカレーションします。 |
observer |
完全自動。人的対応が必要な場合も通知するだけで待機しません(タスクは自動的に終了します)。 |
[capabilities]autonomy_level = "approver"タスク単位の権限付与(scoped_tools、v1.41)
Section titled “タスク単位の権限付与(scoped_tools、v1.41)”高リスクなツールは「権限付与がなければ使えない」と宣言できます。scoped_tools に列挙されたツールは、AI従業員が有効な権限付与(grant)を得るまで一律拒否され、しかも権限は単一タスクのライフサイクルの中でのみ生きています。そのタスクが終了(受理・却下・人的対応へ・キャンセル)した瞬間にすべて自動的に取り消され、次のタスクへ持ち越されることはありません。
[capabilities]scoped_tools = ["shared_wiki_delete", "odoo_execute"] # これらのツールはタスクごとの権限付与が必要grant_ttl_secs = 3600 # 権限付与が生存できる秒数の上限、デフォルト 3600権限付与を得る方法は2つあります。
- AI従業員自身が申請する:MCP ツール
capability_request { tool, reason, task_id? }を呼び出すと承認リクエストに変換され(他の承認と同じ通知/ダッシュボードの導線を使います)、あなたが承認すれば権限が有効になります。期限までに判断されなければ拒否扱いになります。 - ゴールタスクの開始時にまとめて付与する:ゴールタスクの tags に
grant:<ツール名>を追加しておくと、キックオフ承認(collaborator/consultant レベル)が承認された時点で原子的に付与され、タスク終了時に自動的に回収されます。
判定は常に fail-closed です。権限データベースが読めない場合は「権限なし」として扱われます。scoped_tools に列挙されていないツールはまったく影響を受けません。
config.toml(グローバル)
Section titled “config.toml(グローバル)”[dispatch]enabled = true # 自律ディスパッチエンジン(goal loop ドライバーを含む)を有効化。デフォルト falsepolicy = "fixed_hierarchy" # ディスパッチポリシー(どの AI従業員がタスクを引き受けるか)。後述「ディスパッチポリシー」を参照。デフォルト fixed_hierarchygrounding_precheck_enabled = true # 検収前のグラウンディング事前チェック(「グラウンディング事前チェック」を参照)。デフォルト truetwo_stage_judge = true # 検収前に安価な一次評価を実行するか(「二段階検収判定」を参照)。デフォルト truestrict_reply_parsing = "shadow" # ジャッジ応答の厳格 JSON 契約:off / shadow / enforce(「ジャッジ応答の厳格契約」を参照)。デフォルト shadowjudge = "mav" # 誰が検収を判定するか(「検収ジャッジの差し替え」を参照)。mav / external(evaluator_only / human_only は v1.69.0 で削除)。デフォルト mavjudge_provider = "antigravity" # 任意:ジャッジを別のランタイムで実行(「ジャッジを別のモデルで実行する」を参照)。未設定 ⇒ デフォルトのユーティリティ用ランタイムjudge_model = "gemini-3-pro-preview" # 任意:そのランタイム内のジャッジ用モデル id。未設定 ⇒ デフォルトのユーティリティ用モデルadmission = "queue" # エフェメラルな子エージェント(ephemeral spawn)が並行数上限に達したときの扱い、"queue" または "fail"。デフォルト queue(後述「エフェメラル spawn の受け入れキュー」を参照)
[task_forward_model] # タスク層のフォワードモデル(同名セクションを参照)。v1.54 以降デフォルトでオンenabled = true
[goal_loop]iteration_cap = 5 # 難しいゴールのディスパッチ回数のハード上限。超えると人的対応へ。デフォルト 5iteration_cap_simple = 3 # 簡単なゴールのディスパッチ回数上限(動的ジャッジ深度による)。デフォルト 3wall_clock_hours = 24 # 作成時点からの経過時間の予算(時間)。超えると人的対応へ。デフォルト 24max_concurrent = 3 # 同時に飛んでいるゴールタスクの上限(spawn の暴走防止)。デフォルト 3tick_secs = 30 # ドライバーのポーリング間隔(秒)。デフォルト 30stalled_secs = 600 # ディスパッチ後にこの秒数だけ認領がなければ停滞とみなし再ディスパッチ可能にする。デフォルト 600planner_enabled = false # 有効にすると、ゴールを依存関係付きのサブタスク DAG に分解できる(「並列サブタスク」を参照)。デフォルト falseresume_on_restart = "pause" # gateway 再起動時の進行中ゴールタスクの扱い、"auto" または "pause"(「再起動時の挙動」を参照)。デフォルト pause、ダッシュボードの「設定 → 自動化」で切り替え可能progress_report_minutes = 10 # 認領済みタスクが進捗シグナルなしにどれだけ経過したら1回通知するか、`0` で無効化(「タイムアウト時の進捗レポート」を参照)。デフォルト 10tool_streak_advisory = true # 同じツールを同じパラメータで3/5/8回連続呼び出した際にリマインダーを注入するか(「ツール連打アドバイザリー」を参照)。デフォルト truecriteria_ledger = "report" # 検収基準の項目別台帳:off / report / enforce(「検収台帳」を参照)。デフォルト reportsteering_enabled = false # タスクページの「次のラウンドへの指示」(「タスクページの指示と停止」を参照)。デフォルト false
[dispatch_guard] # フィードバック経路のサーキットブレーカー(自己増幅型の無限ループを防ぐ)window_secs = 60 # スライディングウィンドウの長さ(秒)。デフォルト 60max_in_window = 20 # 1ウィンドウ内で許容されるディスパッチ回数、超えるとトリップ。デフォルト 20cooldown_secs = 60 # トリップ後、ディスパッチを拒否するクールダウン秒数。デフォルト 60max_hop_depth = 5 # 委任チェーンをまたぐプロセス間 re-spawn の深さ上限。デフォルト 5すべてのブロックは省略可能で、未設定または一部だけ設定されている項目は上表のデフォルト値にフォールバックします。未知の policy 値は常に fixed_hierarchy にフォールバックし、警告を1件記録します。
並列サブタスク(依存関係 DAG)
Section titled “並列サブタスク(依存関係 DAG)”[goal_loop] planner_enabled = true にすると、ゴール作成時にまず AI従業員が依存関係を注釈したサブタスクの集合に分解することを「試みます」(例:2つのデータソースをそれぞれ調べてから統合する、など)。分解されたサブタスクはそれぞれ Task Board 上に載り、depends_on がすべて満たされたサブタスクは並行して実行され、それぞれ独立に検収されます。並行度は依然として max_concurrent と dispatch_guard サーキットブレーカーの制約を受け、それを迂回することはありません。
- 強制ではありません:モデルが分解不要と判断した場合(またはその返答が解析できなかった場合)は単一タスクにフォールバックし、この機能をオフにしたときと完全に同じ挙動になります。
- 循環依存の防止:分解された計画に循環依存(またはインデックス範囲外の参照)が含まれる場合、計画全体が破棄され単一タスクにフォールバックし、警告が記録されます。壊れた DAG が着地することは決してありません。
- 上流の行き詰まりは下流を孤児化しない:あるサブタスクの上流の依存先が
failed/cancelled/needs_humanになった場合(または依存先が存在しない場合)、下流のサブタスクはエスカレーションを継承して同じく人的対応へ移行し、行き詰まった一連の枝全体が見えるようになります。上流がまだ実行中なだけであれば、下流はそのラウンドは凍結され、次のラウンドで改めて判断されます。
想定される効果が最も大きいのは「複数データソースの照会」型のゴールです。独立した再テストではおよそ 1.25 倍の高速化が確認されました(論文が自己申告する 3.7 倍ではありません)。一般化する前に、eval による実測を基準にしてください。
ディスパッチポリシー(DispatchPolicy)
Section titled “ディスパッチポリシー(DispatchPolicy)”[dispatch] policy は「どの AI従業員がゴールタスクを引き受けるか」を決めます。デフォルトの fixed_hierarchy は従来と完全に同じ挙動です(タスクに元から割り当てられている相手にディスパッチします)。
| ポリシー | 動作 |
|---|---|
fixed_hierarchy |
デフォルト。タスク既存の assigned_to にそのままディスパッチします。LLM コストはゼロで、完全に決定的です。 |
round_robin |
「タスクのカテゴリ」(タグがあれば最初のタグ、なければ優先度)に基づき、名簿を順番に回します。状態はメモリ上にのみ存在し、再起動でリセットされます。 |
llm_select |
LLM がツール呼び出しを通じて名簿の中から最適な AI従業員を選びます。fail-closed:出力が名簿に存在しない場合、あるいは解析や LLM 呼び出しが失敗した場合は、常に fixed_hierarchy の結果にフォールバックし、存在しない AI従業員にディスパッチすることは決してありません。モデル名はハードコードされておらず、設定されたユーティリティ用ランタイムを使用します。 |
role_team |
選ばれるのは fixed_hierarchy とまったく同じく AI従業員です。ロールはその従業員の内部にあります(後述の「チームラウンド」を参照)。この設定は「このデプロイはチームを編成する」ことをログとテレメトリで見えるようにするためのもので、タスクの割り当て先は変わりません。 |
名簿 = <home>/agents/ 配下の AI従業員ディレクトリ。名簿が空の場合、round_robin と llm_select はいずれも元の割り当てにフォールバックします(タスクを孤児化しません)。再割り当てはタスクの assigned_to に書き戻されるため、ハートビートの取得とアクティビティログの整合性が保たれます。
チームラウンド(Team-as-Agent)
Section titled “チームラウンド(Team-as-Agent)”AI従業員は、ゴールの1ラウンドを自分の内部の小さなチームとして進めることができます。計画 → 実行 → レビューの各ロールが、それぞれ自分のベンダーのモデルで動きます。v1.66 以降、[team] enabled のデフォルトは true ですが、実際にチームを成立させるのは [team.roles] で2社目のベンダーを指定することであり、このフラグではありません。ロールを何も書かなければ、実行とレビューの両方が従業員自身のモデルにカスケードして同じモデルファミリーになり、相関除去ルールがこの仕様を拒否します。タスクは静かに Solo で動き、監査行も残りません。全体像は 56-team-as-agent.md を参照してください。このセクションでは goal loop に関わる部分だけを扱います。
経路全体(プランナー、エグゼキューター、ベリファイア、そしてそれらの間でパケットを運ぶ
team_handoffツール)は実装済みで、ジャッジが受理したライブラウンドで実際に動かしています。ただしこれは1回の統合結果であり、Solo に勝ったという測定結果ではありません。[team.roles]は、監視できるデプロイでのみ設定してください。enabled = false(フリート全体または従業員単位)とgate = "always_solo"は、どちらも1行で元に戻せます。
会話には新しいものは何も現れません。従業員は1つの声で答え、進捗は同じボードに届き、あなたの対応が必要なタスクは引き続き6つの一時停止分類のいずれかを持ちます。内部では、1回の起動メッセージの代わりに3つのステージでラウンドが進み、検収ジャッジに届くのはエグゼキューターの最終成果物だけです。そのジャッジは、これまでと同じ二段階評価器と3方向パネルです。チームが変えるのは「誰が作業するか」であり、「誰が完了と判断するか」ではありません。
チームタスクがあなたの手元に落ちてくる新しい理由が2つあります。
| 一時停止 | 何が起きたか |
|---|---|
判断が必要(blocked_needs_decision) |
計画ステージがサブタスクを1つも返しませんでした。ループは文章から分解を推測せず、そのゴールが本当に分割できるのか、それとも単独の従業員としてそのまま実行すべきかをあなたに尋ねます。 |
予算切れ(budget_exhausted) |
タスクが一部を消費した後でロールメンバーの spawn 予算を使い切りました。ほかの予算エスカレーションと同じく、それまでに出せた最善のラウンドを渡します。劣化させた1ラウンドすら予算で賄えなかったタスクは、代わりに Solo で動きます。まだ始まってもいない作業のために停止されるより、通常の方法でやってしまう方がましだからです。 |
各ラウンドの前に、LLM コストゼロのルールセット(crates/duduclaw-core/src/team_gate.rs)が Solo か Team かを決めます。意図的に Solo に寄せてあります。
- 強制的に Solo:従業員が
[container] sandbox_enabled = trueにしている場合(always_teamを含むすべてのモードより先に確認されるため、サンドボックスを有効にした従業員はチームを組みません)、ライブのチャンネルターン、あなたの承認待ちのままの plan-first ゴール、計画に不可逆なアクションが含まれる場合、残り予算が 3 ラウンド未満の場合。 - 4 つのシグナルを数える:bulk(独立して進められる作業項目が 4 つ以上あり、依存関係のハブがない)、context overflow(推定入力がモデルのコンテキストウィンドウを超える)、capability gap(ロール行列の差が、行列で宣言された MDE 以上)、long horizon(検収基準が 3 つ以上あり、タスクが成果物を作る)。測定できないシグナルは発火しません。
- 判定:3 つ以上でチームを編成し、0 または 1 つなら Solo、ちょうど 2 つならグレーゾーンです。
グレーゾーンでは、ループは計画ステージを 1 回実行し(タスクがどのみち必要としていた呼び出しです)、同じルールをもう一度適用します。今回は bulk シグナルを、プランナーが実際に書いたサブタスクのパケットから測定します。計画ができる前の bulk シグナルには測定対象がないため、グレーゾーンは常に残り 3 つのシグナルのうち 2 つが発火した状態です。2 回目の判定でチームが編成されるのは、計画に互いの依存関係のハブがないサブタスクが 4 つ以上ある場合だけで、それ以外は通常の単独従業員ラウンドに戻ります。プランナーのロールがないチーム仕様では 2 回目の判定ができないため、Solo で動きます。
すべての判定は、発火したシグナルとともに team_gate_decision として監査ログに書かれるため、デプロイの Solo/Team の比率は逸話ではなく測定できます。
仕様はタスクごとに凍結される
Section titled “仕様はタスクごとに凍結される”タスクが使うチームは作成時に1度だけ決まり、タスクに保存されます。後からの [team] の変更は次のタスクに影響します。検証に失敗した仕様(最も多いのは、ベリファイアがエグゼキューターとモデルファミリーを共有している場合)では、チームはまったく編成されません。タスクは Solo で動き、部分的なチームは存在しません。
この拒否が目立つかどうかは、誰がチームを要求したかによります。enabled = true と書いた運用者、またはロールを設定したのに検証に失敗した運用者には、ロールと理由を添えた team_refused として監査された拒否が返ります。[team] にまったく触れていないデプロイには debug! の1行だけが出て、監査ログには何も残りません。この拒否は v1.66 のデフォルト反転以降、すべてのインストールの既定状態であり、あらゆる場所のすべてのゴールタスクに行を刻むと、本物の拒否が見つけられなくなるからです。
model が未設定のロールは、従業員の [model] preferred にフォールスルーする前に、測定済みの能力マトリクス(<DUDUCLAW_HOME> 内の role_model_matrix.toml。duduclaw eval --matrix が書き出します)から埋めることもできます。そのロール自身のランタイムの resolved セルだけがカウントされ、同点の場合は何も選ばれず、明示的に設定されたモデルが常に優先されます。
予算と劣化チェーン
Section titled “予算と劣化チェーン”[dispatch.team_budget]max_spawns_per_task = 12 # 4 ロール x 3 ラウンド。下限は 3 にクランプmax_turns_per_role = 3degrade_order = ["utility", "verifier_second_pass", "executor_replica"]1ラウンドの課金は planner? + executors + verifier + repair? です。ベリファイアは scaffold ではなくユーティリティ呼び出しですが、独自の台帳行を書き込み、予算はメンバー id を持つ行をそのまま数えるため、ほかのステージと同じくスロットを1つ消費します。下限の 3 は、プランナー + エグゼキューター + ベリファイアです。
spawn 予算が減ると、機能はこの順で手放されます。まず合成、次にベリファイアの1回の修復パス、その次にファンアウトが単一のエグゼキューターへ縮退し、その後でタスクが budget_exhausted としてエスカレーションします。認識できないエントリは警告とともに破棄され、リスト全体が使えない場合はデフォルトのチェーンが維持されます。
最初のチームラウンドの前に、ループは [dispatch] ephemeral_max_active が、この設定で必要になりうる並行ロールメンバー数(max_concurrent × iteration_cap × roles。デフォルトの上限 32 に対して 45)を収容できるかも1度だけ確認し、引き上げるべき数値を添えて警告します。何も強制はされません。上限を超えるとロールの spawn はキューに入り、期限切れになることもあり、そうなると、あるラウンドからなぜかベリファイアが欠けているという形でずっと後になって表面化するためです。
ロールメンバーの spawn は専用のサーキットブレーカーのバケットと予算([dispatch_guard] role_team_max_in_window、デフォルト 60)で動くため、チームを編成中の従業員が、その従業員自身のサブエージェント spawn を守るブレーカーをトリップさせることはありません。
チームラウンドの証拠:従業員とそのメンバー
Section titled “チームラウンドの証拠:従業員とそのメンバー”ラウンド以降のすべて、つまりチーム自身のベリファイア、グラウンディング事前チェック、MAV パネルの <tool_activity> ダイジェストは、タスクの認領から検収までの時間窓におけるツール呼び出しの監査証跡を読みます。Solo ラウンドでは、この証跡は1つの agent id に属します。チームラウンドでは違います。作業はそれぞれ自分の id を持つ短命なロールメンバーが行い、従業員自身の時間窓には、ラウンドを開いた帳簿上の呼び出しだけしか残っていない場合があります。
そう読むと、チームラウンドは、作業をしたと主張しながら何もしなかったタスクとまったく同じに見えます。ライブラウンド3で実際に起きたのがこれで、ベリファイアも settle の評価器も、実際に行われた作業を「ファイル作成を裏付けるツール活動がない」として却下しました。そこで、チームラウンドの証拠セットは、どちらの経路でも role_turns.jsonl から取った従業員 ∪ そのラウンドのロールメンバーになります。
- チームのベリファイアの
<tool_activity>ブロック - settle 経路のグラウンディング事前チェックとジャッジダイジェスト
重複する id は統合され、ロールメンバーのいないタスクは id を追加しないため、Solo タスクの見る証拠は以前とビット単位で同一です。時間窓は変わらないので、ラウンド番号による検索がタスク全体にフォールバックする場合でも(ループの進行中のイテレーションカウンターと settle 経路のリビジョンカウンターは別物で、gateway の再起動をまたぐと食い違うことがあります)、他のラウンドのメンバーは何も寄与しません。
ロールメンバーは、自分専用の使い捨て scaffold ではなく従業員のワークスペースで作業するため、メンバーが書き込んだファイルは、ベリファイアが尋ねたときにもそこに残っています。ファイルを書き込むロールは現時点で claude と codex に限られます。56-team-as-agent.md を参照してください。
id の和集合を取るだけでは必要でしたが十分ではありませんでした。ライブラウンド8の後にさらに2つの証拠ソースが加わり、どちらもチームのベリファイアと settle 経路が共有するため、同じラウンドについて両者が異なる説明で判断することはありません。
- ネイティブツールの作業が永続化されます。 ネイティブツール(codex の
shell、Claude のWrite)で作業するメンバーは MCP 呼び出しを行わないため、以前はその作業が監査証跡にまったく残りませんでした。ラウンド8では、実在することが明らかな3つのファイルが却下されました。現在は、各ネイティブツールイベントがメンバーの id の下でtool_calls.jsonlの1行として書かれ、マスク済みの呼び出し入力と結果テキスト、さらにsource = "native"とそれを生成したランタイム/モデルを持ちます。セルフエコーのツールは MCP ライターと同じく出力が抑制されたままなので、ロールが自分でエコーしたパケットを根拠に主張を裏付けることはできません。 <artifact_receipts>:パケットが宣言するartifacts[].pathはすべて、従業員のワークスペースに対して stat とハッシュが取られ、その結果(<path> <bytes>B sha256=<hex> exists|missing|mismatch)がプロンプトブロックとartifact_receipt監査行の両方になります。宣言されているがハッシュがない場合は、バイト列から補われます。宣言されたハッシュが一致しない場合は宣言どおりに保持され、team_packet_artifact_mismatchイベントとともにmismatchとして記録されるため、すり替えは黙って訂正されず、見える形で残ります。確認としてカウントされるのはexistsだけです。
成果物を宣言しないタスクではブロックは追加されず、ネイティブツールのメンバーがいない Solo ラウンドが見る内容は以前とまったく同じです。
監査とロールごとの記録
Section titled “監査とロールごとの記録”| イベント | 意味 |
|---|---|
team_gate_decision |
Solo / Team / グレーゾーン。発火したシグナル付き |
team_refused |
有効化された仕様が検証に失敗し、タスクは Solo で動く |
team_round_started |
3ステージのラウンドが開始。ロールと劣化ステップ付き |
team_member_spawned |
ロールメンバーが1つ作成された(ロール、ランタイム、モデル) |
team_stage_failed |
あるステージが次のステージに必要なものを生成しなかった、またはまったく実行できなかった。role、runtime、model とエラー付き |
team_handoff |
あるロールがパケットを提出した(leg、ラウンド、サイズ、宛先、ファイル) |
team_packet_skipped |
パケットファイルが読めない、ラベルが誤っている、または無効で、無視された |
team_packet_artifact_refused |
パケットが従業員のワークスペース外に解決される成果物パスを宣言した |
team_packet_fidelity_corrected |
パケット自己申告の証拠グレードが観測結果と食い違い、上書きされた |
パケットの置き場所
Section titled “パケットの置き場所”各ステージは team_handoff を呼んで引き継ぎます。このツールはファイルパスを受け取らず、導出します。
~/.duduclaw/team_packets/<task_id>/r<round>/planner-to-executor.json~/.duduclaw/team_packets/<task_id>/r<round>/planner-to-executor.01.json … up to .99ゴールを4つのサブタスクに分解するプランナーは、同じ leg に4つのパケットを書きます。そのため新しいパケットは次の番号付きスロットを使い、同じ packet_id を再提出すると自分自身のファイルを上書きします(タイムアウト後の再試行でサブタスクが重複しません)。次のステージは、それらのスロットを数値順に読み戻します。標準ファイルが先で、続いて .01、.02、…の順です。パケット自身の from_role / to_role / タスク / ラウンドが置かれたファイルと食い違う、または検証に失敗した場合、そのパケットはスキップされ team_packet_skipped として監査されます。1つの不良ファイルのせいで、そのステージが残りのパケットを失うことはありません。
各ステージは <home>/role_turns.jsonl にも1行を追記します。ロール、ランタイム、モデル、effort、生成したパケット、観測の証拠グレード、終了の仕方です。パーミッション、ロック、ローテーションは tool_calls.jsonl と同じです。
エフェメラル spawn の受け入れキュー
Section titled “エフェメラル spawn の受け入れキュー”ゴール分解や委任などの経路では、短命な子エージェント(ephemeral spawn)を立ち上げる必要がある場合があります。並行数上限(ephemeral_max_active、デフォルト 32)に達したとき、config.toml [dispatch] admission が上限を超えたリクエストの扱いを決めます。
| 値 | 動作 |
|---|---|
queue |
デフォルト。上限付き FIFO キュー。リクエストは決して消えてなくなることはなく、空きが出れば順番に実行されます。各キュー項目は TTL(queue_item_ttl_secs、デフォルト 600秒)を持ち、期限を過ぎると破棄され監査ログに1件記録されます。キュー自体にも深さの上限(queue_max_depth、デフォルト 64)があり、満杯の場合ははっきりと拒否されます(無制限のキューがそれ自体新たな暴走リスクになるのを避けるためです)。リクエストを発行した turn/session が終了すると、それに属するキュー項目もまとめて無効化されるため、すでに終わったプロセスから遅れて子エージェントが飛び出してくることはありません。 |
fail |
旧来の挙動:上限を超えたら即座に拒否し、キューには入れません。 |
ephemeral_max_active 自体は「調整可能だがゼロにはできない」というルールに従います。0 に設定すると 1 にクランプされ警告が記録され、並行数上限が完全にオフになることは決してありません。
[dispatch]admission = "queue" # "queue"(デフォルト)または "fail"queue_max_depth = 64 # キューの深さ上限、超えると拒否queue_item_ttl_secs = 600 # キュー項目が生存できる秒数、期限切れは破棄され監査されるephemeral_max_active = 32 # 並行数上限、0 は 1 にクランプされる構造化された成果物検収(outcome schema、WP2.4)
Section titled “構造化された成果物検収(outcome schema、WP2.4)”/goal … || outcome:<spec> を使うと、自由記述の検収基準に加えて機械的に検証可能な成果物契約をもう1層追加できます。AI従業員が完了を報告しタスクが review に入ると、この契約は決定的で LLM コストがゼロのチェックを検収ジャッジより前に実行します。
- チェック不合格 → タスクは即座に
revisingに差し戻され、フィードバックには具体的な不足点(どのフィールドが欠けているか、どのファイルがないか)が示されます。ジャッジは一切呼び出しません。これはジャッジの偽陽性を防ぐ防御線です。構造的に明らかに不合格な成果物が過度に寛容なジャッジに通されることはなく、ジャッジ呼び出しも1回も無駄になりません。 - チェック合格 → ここで初めてジャッジに到達し、ジャッジのプロンプトには「構造化された成果物検収はすでに決定的チェックに合格済み」という注記が付き、ジャッジは品質面に集中できます。
3種類の spec があります。
| spec | 意味 |
|---|---|
outcome:text |
デフォルト。構造化契約なし。outcome を付けなかった場合とまったく同じ挙動(永続化されず、ジャッジの前にチェックも走りません)。 |
outcome:json:<JSON Schema> |
JSON Schema のサブセット(object / array / string / number / integer / boolean、properties / required / items に対応)。AI従業員の最終返信中の ```json ブロックをチェックします(fenced ブロックが見つからない場合は返信全体を解析します)。フィールドの欠落や型の不一致はすべて具体的な不足点として列挙されます。 |
outcome:files:<glob,glob> |
AI従業員の作業ディレクトリ配下に、各 glob(*/? 対応)に一致する成果物ファイルが存在することを表明します。例:outcome:files:report.docx, out/*.pdf。 |
例