AI Agent Workstationをコンポーネント単位に再編し、DeploymentとRunnerの責務を分離する
実行時契約の整理からRepository全体の再編へ
前回の記事では、Todo SchedulerのTask GitとRunner契約を中心に、AI Agent Workstationの実行時の整合性を改善した内容をまとめました。
主な変更は次のとおりです。
- Task Planの
required_outputsを構造化してRunnerから報告する - Background AIのPlanningとExecutionを明確に分ける
- Task単位のGit worktreeと段階的なmergeを管理する
- terminal requestに冪等性を持たせる
- SchedulerとRunner間の契約不一致をテストで検出する
- clean-cluster E2Eの実行基盤を整備する
今回の変更では、これらの仕組みを構成するRepository全体を再編しました。
これまでのinfraには、KubernetesやCiliumなどの基盤構築処理に加え、AI Runner、Package Approver、Mirror、Git Bare、Proxy、Deployment Script、テストが配置されていました。
そのため、異なる責務の実装とテストが同じディレクトリに含まれていました。
今回の再編では、Application、AI Runtime、Deployment、Platformをそれぞれ独立した領域へ分割しています。
前回からの主な変更
| 項目 | 前回 | 今回 |
|---|---|---|
| Repository構成 | appとinfraを中心に管理 | 責務単位のコンポーネントへ分割 |
| Backend / Frontend | app配下 | root直下へ分離 |
| AI Runner | Infra配下のMJS実装 | workstation/runnerへ移動 |
| Package Guard | Infra内のApprover / Mirror | guard/へ独立 |
| Git Bare | Infra側のGitサービス | git-bare/へ独立 |
| Proxy | Infra内で管理 | proxy/へ独立 |
| Kubernetes Manifest | Infra側を中心に配置 | deployment/へ集約 |
| OpenAPI | OpenAPIとConsumer側の契約定義を個別管理 | BackendのGo宣言から生成 |
| Container Image | SchedulerとRunnerのBuild責務が混在 | Backend ImageとRunner Imageを分離 |
| Runner Source | Monorepo全体を取得 | Ownership Policyに従い公開範囲を制限 |
| テスト | Infra側に複数の責務が混在 | 各コンポーネントとDeploymentへ再配置 |
Repositoryを責務単位へ分割する
再編後の主なディレクトリ構成は次のとおりです。
root/
├── backend/ # Scheduler / Control Plane
├── frontend/ # Scheduler UI
│
├── workstation/ # AI実行環境
│ ├── runner/ # AI Jobの実行・検証・結果報告
│ ├── images/ # Runner用Container Image
│ └── capabilities/ # Runnerの実行能力
│
├── guard/ # Package取得制御
│ ├── shared/
│ ├── npm/
│ ├── pypi/
│ └── go/
│
├── git-bare/ # 内部Git Repositoryサービス
├── proxy/ # Scheduler向けmTLS Proxy
│
├── deployment/ # システム全体の配置と構成
│ ├── components/
│ ├── profiles/
│ ├── network/
│ ├── workspace/
│ ├── dependencies/
│ ├── repositories/
│ ├── scripts/
│ └── tests/
│
└── infra/ # Cluster / Platform
├── bootstrap/
├── cilium/
├── certs/
└── storage/
各ディレクトリが扱う責務を次のように分離しました。
| ディレクトリ | 責務 |
|---|---|
backend | Task、Job、Workspace、Runner API、状態管理 |
frontend | Scheduler UIとBackend APIの利用 |
workstation | AI実行、Git操作、Validation、結果報告 |
guard | Package Approver、Mirror、依存取得の制御 |
git-bare | 内部Git Repositoryの提供 |
proxy | Scheduler向けmTLS通信の中継 |
deployment | 各サービスの配置、設定、依存関係、NetworkPolicy |
infra | Cluster bootstrap、Cilium、証明書、Storage |
BackendとFrontendの内部では、以前導入したレイヤー分割を引き続き使用しています。
DeploymentをApplication実装から分離する
今回の再編では、Applicationの実装とKubernetes上の配置定義を分離しました。
例えばBackendは、SchedulerのAPI、Domain、Application、Persistenceなどを実装します。
一方、次の情報はdeployment/で管理します。
- Deployment / StatefulSet
- Service
- NetworkPolicy
- PVC
- Container Imageの指定
- Runtime Profile
- 環境別設定
- Component間の配置依存関係
- apply / verify / teardown手順
配置定義を各ApplicationのSource配下へ分散させず、システム全体のCompositionとして管理する構成です。
backend/ Scheduler実装
frontend/ UI実装
workstation/ AI Runtime実装
│
▼
deployment/ 配置定義・構成・実行手順
│
▼
infra/ Cluster / Platform
これにより、Application実装と特定のKubernetes環境における配置設定が別々に管理されます。
DeploymentとInfraの境界
infra/は、Clusterを成立させるための処理を担当します。
主な対象は、bootstrap、Cilium、証明書、Storageです。
deployment/は、用意されたPlatformへAI Agent Workstationの各コンポーネントを配置するための定義と手順を担当します。
例えばCilium自体の導入処理はInfra側に配置し、BackendからGit Bareへの通信を許可するNetworkPolicyはDeployment側で管理します。
WorkspaceのProvisioningをDeploymentへ集約する
新しいWorkspaceやRunnerを追加する際の配置設定も、deployment/配下に整理しました。
deployment/profiles/ではRunnerごとのSource Directory、Build Context、Dockerfile、Manifestなどを管理します。
deployment/workspace/には、Workspaceの準備に関する処理を配置しています。
主な操作は次のように分かれています。
Cluster bootstrap
↓
Platform配置
↓
Certificate準備
↓
Scheduler配置
↓
Runner Provisioning
↓
WorkspaceとRunnerのBinding
Runnerの配置とWorkspaceのBindingは、別の操作として扱います。
また、Deployment側では各コンポーネントの配置に必要な依存関係を管理しています。
Application側の実装コードには、Namespace、NetworkPolicy、PVCなどの配置定義を含めない構成になっています。
BackendのGoコードからOpenAPIを生成する
前回までの実装では、SchedulerとRunner間のAPI契約をBackend OpenAPIとRunner側の定義で管理していました。
この構成では、同じRequestやResponseに対して異なるfield定義が存在する可能性があります。
実際に、以前のContract TestではProgressRequestのjob_idに関する定義差分が検出されていました。
今回の変更では、BackendのGoコードをAPI契約の正本として扱う構成に変更しました。
主な宣言は次のディレクトリに配置しています。
backend/internal/presentation/runnercontract/
├── types.go
├── schemas.go
├── operations.go
└── document.go
ここでは次の情報を定義します。
- Request / Response DTO
- Fieldの必須・任意
- Nullable
- Schemaの条件分岐
- HTTP MethodとPath
- Status Code
- Error Response
- 認証条件
これらの宣言からOpenAPIを生成します。
Backend Go宣言
↓
OpenAPI生成
↓
Runner用Contract Metadata生成
↓
Workstation Runner
生成物は次の場所に配置されます。
backend/openapi/
└── runner-v1.openapi.json
workstation/runner/
└── runner-contract.generated.mjs
RunnerはBackendのGo実装を直接参照せず、生成されたContract Metadataを利用します。
Contractの生成と検証
Contract関連の操作として、次のCommandを用意しています。
npm run contract:generate
npm run contract:check
npm run test:contract
contract:generateは契約生成物を更新します。
contract:checkはGo宣言から生成した結果とコミット済みの生成物を照合します。
test:contractでは、BackendのAPI契約とRunner側のConsumer契約を検証します。
これにより、BackendとRunnerのContractを個別に手書きする箇所を減らしています。
SchedulerとRunnerのContainer Imageを分離する
Container ImageのBuild定義も責務ごとに分離しました。
以前はWorkstation側のDockerfileが、SchedulerとRunnerの両方のBuildに関係していました。
現在、Scheduler ImageはBackend側に配置しています。
backend/
└── images/
├── Dockerfile.scheduler
└── entrypoint.sh
Runner ImageはWorkstation側で管理します。
workstation/
└── images/
└── Runner用Dockerfile群
Scheduler Imageには、Schedulerの実行に必要なApplicationと依存物を含めます。
Runner Imageには、AI CLI、Git、Validation Toolsなど、AI Jobの実行に必要なToolchainを含めます。
SchedulerのBuildとRunnerのBuildを分けることで、各Imageの依存関係も分離されています。
なお、ImageのBuild定義と、そのImageをどのKubernetes環境に配置するかという設定は別の領域で管理しています。
Runnerへ公開するGit Sourceを制限する
ディレクトリ分割に加えて、Runnerが取得できるGit Sourceの範囲も変更しました。
Monorepoを開発上の正本として維持しつつ、Runnerごとに公開するRepositoryを分離しています。
現在の主な公開範囲は次のとおりです。
| Runner | Repository | 公開範囲 |
|---|---|---|
| App Runner | app.git | backend/、frontend/、go.mod、go.sum |
| Infra Runner | infra.git | infra/ |
公開範囲の定義は次のファイルで管理します。
deployment/repositories/ownership.json
Git履歴も公開範囲に合わせて再構成する
GitのSparse Checkoutでは、Working Treeに表示されるファイルを限定できます。
ただし、Sparse CheckoutだけではRepositoryの履歴やObject Databaseに含まれる情報まで制限できません。
そこで、Runner公開用Repositoryの生成では、Ownership Policyで許可されたSourceだけを抽出し、独立したGit履歴を作成します。
Monorepo
↓
Ownership Policy
↓
許可されたBlob / Treeを抽出
↓
独立したCommitを生成
↓
app.git / infra.git
↓
Runner
元のMonorepo Commitを公開用Commitの親として引き継がない方式です。
そのため、Runnerが取得するRepositoryにMonorepo全体の過去履歴を含めずに済みます。
公開対象には、BranchだけでなくTagやTask Git Refも含まれるため、到達可能なGit Objectの範囲を確認します。
Source Ownershipの検証
Source公開処理では、次の条件を検査します。
- 許可範囲外のSourceを公開しない
- 元のMonorepo履歴を公開しない
- 所有範囲外へ到達するSymlinkを拒否する
- 不正なSubmoduleを拒否する
- RepositoryとSource Scopeの不一致を拒否する
- 未取り込みのRunner変更がある場合は再公開を拒否する
- DirtyなSource Treeを拒否する
Runnerで作成された変更をMonorepoへ戻す際にも、公開時のBaseと現在のSourceを比較します。
公開時のBase
+
Runnerの変更
+
現在のMonorepo
↓
所有範囲・競合検証
↓
変更取り込み
取り込み時には、Runnerの所有範囲外にある変更を保持し、競合がある場合は操作を拒否します。
この仕組みは、Task Gitによる変更の作成・検証・取り込みと、Deploymentによる実環境への反映を別操作として扱っています。
Package Guard、Git Bare、Proxyを独立させる
Runner以外のサービス実装もInfraから分離しました。
Package Guard
Package取得制御はguard/へ移動しています。
guard/
├── shared/
├── npm/
├── pypi/
└── go/
npm、PyPI、GoのApproverとMirrorを管理します。
Git Bare
内部Git Repositoryサービスはgit-bare/へ移動しています。
Git daemon、Repository初期化、Hookなど、Gitサービスの実装を管理します。
Proxy
Proxyはproxy/へ分離しました。
現在の実装は、Scheduler向けのmTLS通信を中継するProxyです。
いずれもKubernetesへの配置定義とは分離し、サービス実装をそれぞれのディレクトリで管理しています。
テストの責務も再編する
Repositoryの変更に合わせて、テストをコンポーネントの責務ごとに再配置しました。
| テスト領域 | 検証対象 |
|---|---|
| Backend | API、認証、状態遷移、業務規則 |
| Workstation | AI実行、Lease、Validation、Result Reporting |
| Frontend | UI、Backend API Adapter |
| Guard | Package Approver、Mirror、公開制御 |
| Deployment | Image構成、CLI配置、Source Ownership、Manifest |
| E2E | 複数コンポーネントを接続した実行 |
OpenAPI Consumer Testの重複実行も整理しています。
契約そのものの検証と、契約を利用する実装がContainer Imageへ正しく含まれるかという検証は、異なるテストとして扱います。
また、Deployment側ではManifestやNetworkPolicyの変更を検出するFingerprint検証を用意しています。
テスト結果
実行した主なテストの結果は次のとおりです。
| 対象 | 結果 |
|---|---|
| OpenAPI Consumer Contract | 3 / 3 PASS |
| Workstation Runner | 106 / 106 PASS |
| Guard | 63 / 63 PASS |
| Frontend | 133 / 133 PASS |
| Deployment Node Tests | 50 / 50 PASS |
| Workspace Python Tests | 29 / 29 PASS |
| Workspace Shell Tests | 4スクリプト成功 |
| Source Ownership | 12 / 12 PASS |
Source Ownershipの検証には、範囲外Sourceの公開拒否、Git履歴の制限、所有範囲外の変更の保持、競合時の拒否などを含みます。
なお、これらの結果にはGoテスト全体と実Browser E2Eの成功件数は含めていません。
E2E設計カタログは300ケースまで拡張されていますが、設計カタログの件数と実環境で実行済みの件数は区別しています。
まとめ
今回、AI Agent WorkstationのRepositoryを責務単位に再編しました。
主な変更は次のとおりです。
- BackendとFrontendをroot直下へ分離
- AI Runnerを
workstation/へ移動 - Package Guard、Git Bare、Proxyを独立したディレクトリへ移動
- Kubernetes Manifestと配置手順を
deployment/へ集約 infra/をCluster / Platformの構築責務へ整理- BackendのGo宣言からOpenAPIとRunner用Contract Metadataを生成
- SchedulerとRunnerのContainer Imageを分離
- Runnerへ公開するGit Sourceと履歴をOwnership Policyで制限
- Runnerの変更を取り込む際の所有範囲・競合検証を追加
- テストをコンポーネントと契約の責務ごとに再配置
前回はTask GitとRunner間の実行時契約を整理しました。
今回は、その仕組みを構成するRepository、Container Image、Deployment、Git Sourceの所有範囲を整理しています。
これにより、Backend、Workstation、Deployment、Infraがそれぞれ異なる責務を持つ構成となり、API契約とGit Sourceの公開範囲についても機械的な生成・検証を行う形へ変更されました。