パイプラインと自動化
パイプラインは Control Center における決定論的な半分です。何を言うかを決めるのはエージェントですが、次に何が起きるかを決めるのはパイプラインであり、それは毎回同じです。パイプラインはステップの DAG であり、エージェントのディスパッチ、スクリプトの実行、条件による分岐、コレクションに対するファンアウト、ステップ間のデータの受け渡しができます。永続化され、再開可能で、コストも計上されます。
パイプラインが存在する理由は 3 つあります。繰り返しの多い複数ステップのプロセスをつなぐこと、複数のエージェントを順番に、または並行して連携させること、人の立ち会いなしに起きた出来事に反応することです。
テンプレートと実行
「テンプレートと実行」というセクションテンプレート(PipelineDefinition)は宣言的な DAG であり、ステップ、宣言された入力、設定からなります。テンプレートはワークスペース自身のデータベースに存在します。実行(PipelineRun)はそのテンプレートの 1 回の実行で、可変のステートバッグ、コストとトークンの合計、ステップごとのステータスを持ちます。
1 つのテンプレートには多数の実行があります。実行は再起動をまたいで生き残ります。エンジンは進行中の実行を再読み込みし、停止した場所から再開します。
意図的に、ステップを再開するのではなく実行全体を失敗させる安全弁が 2 つあります。再開することが停止よりも悪い結果を招くからです。
suspendedStepTimeout(デフォルトでは 24 時間)を超えてsuspendedのままのステップは、終端状態に決して到達しないチケットを待っているため、実行を解放するために失敗とされます。例外はextras['awaitApproval'] == trueの場合で、これはorchestration.awaitApprovalの部分承認ゲートです。human.gateは免除されません。extras: {'idempotent': false}と宣言されたステップが、サーバーの停止時に処理中だった場合、すでに副作用(squash マージ、ワークツリーのクリーンアップ)を適用済みかもしれません。再実行すると二重に適用されるおそれがあるため、エンジンはそのステップを失敗とし、再試行はあなたに委ねます。
種別がスケジュールし、ボディが働く
「種別がスケジュールし、ボディが働く」というセクションステップには直交する 2 つの部分があり、それらを分けておくことがエンジンを小さく保つ秘訣です。
種別(StepKind)は、上流のステップに対してそのステップがいつ実行可能になるかをエンジンに伝えます。スケジューラーが知る必要があるのはこれだけです。種別は 6 つあります: trigger、listen、join、router、forEach、terminal です。ボディ(bodyKey)は、スケジュールされた後にステップが実行する作業です。ボディはサーバー起動時にキーで登録されるクロージャなので、異なる種別の多数のステップが 1 つのボディを共有できます。たとえば、複数の listen ステップがすべて conversation.promptAgent を実行する、といった具合です。
スケジューリングのセマンティクスが 6 つの種別に宿っているため、誰もスケジューラーに触れることなく動作のカタログを増やせます。種別、ボディとその設定の完全なカタログについては、パイプラインステップを参照してください。
テンプレートを手書きする前に知っておく価値のある、レジストリ設計の帰結が 2 つあります:
bodyKeyが登録されていないステップは、テンプレートの読み込み時に警告を出し、その後は決して実行されません。実行時に大きく失敗することはありません。triggerとterminalのステップはこの警告の対象外です。trigger は下流のリスナーを発火させるために何もしないpipeline.triggerボディを実行し、terminalステップはボディを持たず、スケジューラーのセンチネルです。- ノードの
extrasマップ内のキーは完全一致で、それ以外は黙って無視されます。エージェントステップの作業を既存の会話に置けるのはextras['spaceId']だけです。extras['mode'](chat/plan/review)は、ルームを開くノード(messaging.createSpace、デフォルトはreview)で読み取られます。後から来たエージェントステップは、自分が参加した会話の権限を設定し直すことはできません。どちらのキーも、それ以外の綴りは無視されます。
ステートとデータフロー
「ステートとデータフロー」というセクションステップ同士が互いを呼び出すことはありません。それぞれが実行上の共有ステートバッグを読み書きします。ステップはキーで入力を受け取り、実行し、自身の outputKey の下に出力を書き戻します。並列ブランチが同じキーを対象とする場合、StateReducer が同時書き込みをマージし、TemplateRenderer がプロンプトやスクリプト内の {{...}} プレースホルダーを同じステートから置き換えます。
だからこそ、パイプラインは後から検査できます。ステートバッグがその実行の全体を語っているのです。
何が実行を開始するのか
「何が実行を開始するのか」というセクションPipelineTrigger の行が、テンプレートをいつ自動起動するかを宣言します。種別は 4 つあります:
| トリガー | 実行を開始する条件 |
|---|---|
| ドメインイベント | 一致するドメインイベントが発火したとき(例: PullRequestPublished や TicketAssigned) |
| スケジュール | cron/インターバル式の時刻が到来したとき |
| 手動 | 手動で実行したとき。有効な手動トリガーが存在することが、テンプレートを実行ページに表示させる条件です |
| Webhook | 外部システムがサーバーの HTTP ポート上の /webhooks/<token> に POST したとき |
イベントトリガーは閉じた集合です。ディスパッチャーが認識してピッカーに表示するイベント種別は 16 種類(EventPayloadMapper.knownEventTypes)だけで、それ以外がパイプラインを開始することはできません。この 16 種類のうち 14 種類にはペイロードマッピングがあります。残る 2 つ、TicketCreated と TicketStatusChanged にはマッピングがないため、どちらかに設定したトリガーは有効なまま決して発火しません。完全な一覧はドメインイベントを参照してください。
Webhook トークンは HMAC シークレットも兼ねます。リクエストの X-Hub-Signature-256: sha256=<hex> ヘッダーは HMAC-SHA256(body, token) に対して検証され、検証に通らない配信は拒否されて再生できず、重複は dedupe_key によって破棄されます。トリガーの行は(ワークスペース、イベント種別、テンプレート)で一意であるため、1 つのテンプレートが持てる Webhook トリガーは最大 1 つ、ドメインイベント種別ごとのトリガーも最大 1 つです。
組み込みテンプレートは手動トリガーとイベントトリガーを有効な状態でシードし、スケジュールトリガーは無効な状態でシードします。そのため、定期的な一括処理は常にオプトインです。
無効な状態で提供されるテンプレート
「無効な状態で提供されるテンプレート」というセクション13 個のパイプラインテンプレートがすべてのワークスペースにシードされ、そのうち 8 個は無効な状態で提供されます: external_pr_welcome、cross_review、ticket_to_pr、pr_triage、pre_merge_gate、release_notes、dep_audit、pr_digest。
ここが人を驚かせる部分です。これらのトリガーは有効な状態でシードされますが、PipelineEngine.start は無効なテンプレートを拒否して null を返します。そのためトリガーは空しく発火し、実行は現れません。スイッチなのはトリガーではなくテンプレートです。 この 8 個に何かを期待する前に、設定 → ワークスペース → パイプラインテンプレートで有効にしてください。
ワークスペースを再シードしても、あなたが選んだものはそのまま保たれます。isEnabled の設定と既存のトリガー行は残ります。意図的な例外がちょうど 1 つだけあります。pr_merged_cleanup の日次スケジュールは、以前にオフだったとしても強制的に有効化されます。これがガベージコレクションだからです。これがないとワークツリーの行、コードグラフのパーティション、コピーオンライトのコピーが蓄積していきますが、それはそもそもユーザーが選ぶものではありませんでした。
もう 1 つ知っておく価値のある非対称性があります。組み込みの pr_review は、意図的に GitHub へ投稿しなくなりました。レビューの公開はユーザーが制御する別個のステップであり、直接投稿するパイプラインは二重に公開してしまうからです。cross_review、ticket_to_pr、pr_triage は、実行時に今も GitHub へ直接コメントを投稿します。
エンジンが知っていること
「エンジンが知っていること」というセクションPipelineEngine は実行レコードを作成し、実行のイベント種別に一致する開始ノードをスケジュールして、DAG をたどります。ステップが完了するたびに、下流のトリガーを評価し、ルーターブランチを解決し、join を待ち、再試行ポリシーと continue-on-fail を適用し、各ステップでステート、コスト、エラーを永続化します。別の開始ノードから出るエッジはその実行では無視されるため、スケジュールによる発火が手動専用の条件を待つことはありません。
エンジンが知らないのは、チケッティング、ディスパッチ、GitHub、メッセージングに関する一切です。唯一の拡張ポイントは PipelineBodyRegistry です。そこに登録されたボディが AgentDispatchPort、MessagingPort とその兄弟を保持しています。だからこそ、パイプラインに機能を追加してもスケジューラーを変更することには決してならないのです。
エラー処理
「エラー処理」というセクション失敗したステップはエラーメッセージを記録し、failed に移ります。そのステップが continueOnFail を宣言していれば、下流のステップは続行し、エラーは実行のステートに退避されます。そうでなければ実行は失敗します。StepRetryPolicy を持つステップは、これらのいずれかが適用される前に、線形または指数バックオフでまず再試行されます。
関連項目
「関連項目」というセクション- ドメインイベント: イベントトリガーが何を待ち受けるか
- オーケストレーション: あなたが承認したプランが、これらのパイプラインの 1 つにコンパイルされる
- チケットと委任: パイプラインのステップが作成し、完了させるもの
- パイプラインステップ: 種別、ボディ、設定の完全なカタログ
- 初めてのパイプラインを構築
- パイプラインテンプレートを作成
- パイプラインを手動実行
- パイプライン実行を監視
- パイプライントリガーを設定