1030 lines
48 KiB
Markdown
1030 lines
48 KiB
Markdown
# TAKTデータフロー解析
|
|
|
|
このドキュメントでは、TAKTにおけるデータフロー、特にインタラクティブモードからワークフロー実行に至るまでのデータの流れを説明します。
|
|
|
|
## 目次
|
|
|
|
1. [概要](#概要)
|
|
2. [全体フロー図](#全体フロー図)
|
|
3. [各レイヤーの詳細](#各レイヤーの詳細)
|
|
4. [データフローの段階](#データフローの段階)
|
|
5. [重要な変換ポイント](#重要な変換ポイント)
|
|
|
|
---
|
|
|
|
## 概要
|
|
|
|
TAKTのデータフローは以下の7つの主要なレイヤーで構成されています:
|
|
|
|
1. **CLI Layer** - ユーザー入力の受付
|
|
2. **Interactive Layer** - タスクの対話的な明確化
|
|
3. **Execution Orchestration Layer** - ワークフロー選択とworktree管理
|
|
4. **Workflow Execution Layer** - セッション管理とイベント処理
|
|
5. **Engine Layer** - ステートマシンによるステップ実行
|
|
6. **Instruction Building Layer** - プロンプト生成
|
|
7. **Provider Layer** - AIプロバイダーとの通信
|
|
|
|
---
|
|
|
|
## 全体フロー図
|
|
|
|
```
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 1. CLI Layer (src/app/cli/index.ts) │
|
|
│ ユーザー入力 → 引数パース → コマンド振り分け │
|
|
└────────────────────────────┬────────────────────────────────────┘
|
|
│
|
|
┌────────────┴──────────────┐
|
|
│ │
|
|
Direct Task Input Short Input / No Args
|
|
│ │
|
|
│ ▼
|
|
│ ┌─────────────────────────────────┐
|
|
│ │ 2. Interactive Layer │
|
|
│ │ (interactive.ts) │
|
|
│ │ │
|
|
│ │ ┌─────────────────────┐ │
|
|
│ │ │ User Conversation │ │
|
|
│ │ │ - Clarification │ │
|
|
│ │ │ - Codebase Search │ │
|
|
│ │ │ - AI Response │ │
|
|
│ │ └──────┬──────────────┘ │
|
|
│ │ │ │
|
|
│ │ ▼ │
|
|
│ │ User confirms with /go │
|
|
│ │ │ │
|
|
│ │ ▼ │
|
|
│ │ buildTaskFromHistory() │
|
|
│ │ (会話履歴 → タスク文字列) │
|
|
│ └─────────┬───────────────────────┘
|
|
│ │
|
|
└────────────────────────┘
|
|
│
|
|
│ task: string
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 3. Execution Orchestration Layer │
|
|
│ (selectAndExecute.ts) │
|
|
│ │
|
|
│ ┌──────────────────────┐ │
|
|
│ │ determineWorkflow() │ ← workflow選択 (interactive/override) │
|
|
│ └─────────┬────────────┘ │
|
|
│ │ workflowIdentifier: string │
|
|
│ ▼ │
|
|
│ ┌──────────────────────────────────┐ │
|
|
│ │ confirmAndCreateWorktree() │ │
|
|
│ │ - AI branchname generation │ │
|
|
│ │ - createSharedClone() │ │
|
|
│ └─────────┬────────────────────────┘ │
|
|
│ │ { execCwd, isWorktree, branch } │
|
|
│ ▼ │
|
|
│ ┌──────────────────────────────────┐ │
|
|
│ │ executeTask() │ │
|
|
│ │ - task: string │ │
|
|
│ │ - cwd: string (実行ディレクトリ) │ │
|
|
│ │ - workflowIdentifier: string │ │
|
|
│ │ - projectCwd: string (.takt/在処) │ │
|
|
│ └─────────┬────────────────────────┘ │
|
|
└────────────┼────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 4. Workflow Execution Layer │
|
|
│ (workflowExecution.ts, taskExecution.ts) │
|
|
│ │
|
|
│ ┌────────────────────────────────┐ │
|
|
│ │ loadWorkflowByIdentifier() │ │
|
|
│ │ → WorkflowConfig │ │
|
|
│ └────────┬───────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────┐ │
|
|
│ │ Session Management │ │
|
|
│ │ - loadAgentSessions() │ ← projectCwd or cwd │
|
|
│ │ - generateSessionId() │ │
|
|
│ │ - createSessionLog() │ │
|
|
│ │ - initNdjsonLog() │ │
|
|
│ └────────┬───────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────┐ │
|
|
│ │ WorkflowEngine initialization │ │
|
|
│ │ │ │
|
|
│ │ new WorkflowEngine( │ │
|
|
│ │ config: WorkflowConfig, │ │
|
|
│ │ cwd: string, │ │
|
|
│ │ task: string, │ │
|
|
│ │ options: { │ │
|
|
│ │ onStream, │ ← StreamDisplay handler │
|
|
│ │ initialSessions, │ ← 保存済みセッションID │
|
|
│ │ onSessionUpdate, │ ← セッション更新callback │
|
|
│ │ projectCwd, │ │
|
|
│ │ language, │ │
|
|
│ │ provider, │ │
|
|
│ │ model │ │
|
|
│ │ } │ │
|
|
│ │ ) │ │
|
|
│ └────────┬───────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────┐ │
|
|
│ │ Event Subscription │ │
|
|
│ │ - step:start │ │
|
|
│ │ - step:complete │ │
|
|
│ │ - step:report │ │
|
|
│ │ - workflow:complete │ │
|
|
│ │ - workflow:abort │ │
|
|
│ └────────┬───────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────┐ │
|
|
│ │ engine.run() │ │
|
|
│ └────────┬───────────────────────┘ │
|
|
└───────────┼─────────────────────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 5. Engine Layer (WorkflowEngine.ts) │
|
|
│ │
|
|
│ ┌────────────────────────────────────────┐ │
|
|
│ │ State Machine Loop │ │
|
|
│ │ │ │
|
|
│ │ while (state.status === 'running') { │ │
|
|
│ │ ┌────────────────────────────────┐ │ │
|
|
│ │ │ 1. Iteration & Loop Check │ │ │
|
|
│ │ └────────────┬───────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ▼ │ │
|
|
│ │ ┌────────────────────────────────┐ │ │
|
|
│ │ │ 2. Get Current Step │ │ │
|
|
│ │ │ step = getStep( │ │ │
|
|
│ │ │ state.currentStep │ │ │
|
|
│ │ │ ) │ │ │
|
|
│ │ └────────────┬───────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ▼ │ │
|
|
│ │ ┌────────────────────────────────┐ │ │
|
|
│ │ │ 3. Build Instruction │ │ ← InstructionBuilder
|
|
│ │ │ (if not parallel) │ │ │
|
|
│ │ └────────────┬───────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ▼ │ │
|
|
│ │ ┌────────────────────────────────┐ │ │
|
|
│ │ │ 4. Emit step:start │ │ │
|
|
│ │ └────────────┬───────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ▼ │ │
|
|
│ │ ┌────────────────────────────────┐ │ │
|
|
│ │ │ 5. runStep() │ │ │
|
|
│ │ │ ├─ Normal: StepExecutor │ │ ← 3-phase execution
|
|
│ │ │ └─ Parallel: ParallelRunner │ │ │
|
|
│ │ └────────────┬───────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ │ { response, instruction } │
|
|
│ │ ▼ │ │
|
|
│ │ ┌────────────────────────────────┐ │ │
|
|
│ │ │ 6. Emit step:complete │ │ │
|
|
│ │ └────────────┬───────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ▼ │ │
|
|
│ │ ┌────────────────────────────────┐ │ │
|
|
│ │ │ 7. Handle Blocked │ │ │
|
|
│ │ │ (if status === 'blocked') │ │ │
|
|
│ │ └────────────┬───────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ ▼ │ │
|
|
│ │ ┌────────────────────────────────┐ │ │
|
|
│ │ │ 8. Rule Evaluation │ │ ← RuleEvaluator │
|
|
│ │ │ resolveNextStep() │ │ │
|
|
│ │ └────────────┬───────────────────┘ │ │
|
|
│ │ │ │ │
|
|
│ │ │ nextStep: string │ │
|
|
│ │ ▼ │ │
|
|
│ │ ┌────────────────────────────────┐ │ │
|
|
│ │ │ 9. Transition │ │ │
|
|
│ │ │ - COMPLETE → break │ │ │
|
|
│ │ │ - ABORT → break │ │ │
|
|
│ │ │ - other → update state │ │ │
|
|
│ │ └────────────────────────────────┘ │ │
|
|
│ │ } │ │
|
|
│ └────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
│
|
|
▼ (from runStep)
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 6. Instruction Building & Step Execution Layer │
|
|
│ │
|
|
│ ┌────────────────────────────────────────────────────────────┐│
|
|
│ │ StepExecutor.runNormalStep() ││
|
|
│ │ ││
|
|
│ │ ┌──────────────────────────────────────────────────────┐ ││
|
|
│ │ │ Phase 1: Main Execution │ ││
|
|
│ │ │ │ ││
|
|
│ │ │ InstructionBuilder.build() │ ││
|
|
│ │ │ ├─ Execution Context (cwd, permission) │ ││
|
|
│ │ │ ├─ Workflow Context (iteration, step, report) │ ││
|
|
│ │ │ ├─ User Request ({task}) │ ││
|
|
│ │ │ ├─ Previous Response ({previous_response}) │ ││
|
|
│ │ │ ├─ Additional User Inputs ({user_inputs}) │ ││
|
|
│ │ │ ├─ Instructions (instruction_template) │ ││
|
|
│ │ │ └─ Status Output Rules (tag-based) │ ││
|
|
│ │ │ │ ││
|
|
│ │ │ → instruction: string │ ││
|
|
│ │ │ │ ││
|
|
│ │ │ runAgent(agent, instruction, options) │ ││
|
|
│ │ │ → response: AgentResponse │ ││
|
|
│ │ └──────────────────────┬───────────────────────────────┘ ││
|
|
│ │ │ ││
|
|
│ │ ▼ ││
|
|
│ │ ┌──────────────────────────────────────────────────────┐ ││
|
|
│ │ │ Phase 2: Report Output (if step.report defined) │ ││
|
|
│ │ │ │ ││
|
|
│ │ │ runReportPhase() │ ││
|
|
│ │ │ - Resume session │ ││
|
|
│ │ │ - Write-only tools │ ││
|
|
│ │ │ - ReportInstructionBuilder │ ││
|
|
│ │ └──────────────────────┬───────────────────────────────┘ ││
|
|
│ │ │ ││
|
|
│ │ ▼ ││
|
|
│ │ ┌──────────────────────────────────────────────────────┐ ││
|
|
│ │ │ Phase 3: Status Judgment (if tag-based rules) │ ││
|
|
│ │ │ │ ││
|
|
│ │ │ runStatusJudgmentPhase() │ ││
|
|
│ │ │ - Resume session │ ││
|
|
│ │ │ - No tools (judgment only) │ ││
|
|
│ │ │ - StatusJudgmentBuilder │ ││
|
|
│ │ │ → tagContent: string │ ││
|
|
│ │ └──────────────────────┬───────────────────────────────┘ ││
|
|
│ │ │ ││
|
|
│ │ ▼ ││
|
|
│ │ ┌──────────────────────────────────────────────────────┐ ││
|
|
│ │ │ Rule Evaluation │ ││
|
|
│ │ │ │ ││
|
|
│ │ │ detectMatchedRule(step, content, tagContent) │ ││
|
|
│ │ │ 1. Aggregate (all()/any()) │ ││
|
|
│ │ │ 2. Phase 3 tag ([STEP:N]) │ ││
|
|
│ │ │ 3. Phase 1 tag (fallback) │ ││
|
|
│ │ │ 4. AI judge (ai("...")) │ ││
|
|
│ │ │ 5. AI judge fallback (all conditions) │ ││
|
|
│ │ │ → { index, method } │ ││
|
|
│ │ └──────────────────────┬───────────────────────────────┘ ││
|
|
│ │ │ ││
|
|
│ │ ▼ ││
|
|
│ │ response with matchedRuleIndex & matchedRuleMethod ││
|
|
│ └────────────────────────────────────────────────────────────┘│
|
|
└────────────────────────────┬────────────────────────────────────┘
|
|
│
|
|
▼
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
│ 7. Provider Layer (agents/runner.ts → providers/) │
|
|
│ │
|
|
│ ┌────────────────────────────────────────┐ │
|
|
│ │ runAgent() │ │
|
|
│ │ - Resolve agent spec │ │
|
|
│ │ - Get provider │ │
|
|
│ │ - Call provider.call() │ │
|
|
│ └────────────┬───────────────────────────┘ │
|
|
│ │ │
|
|
│ ▼ │
|
|
│ ┌────────────────────────────────────────┐ │
|
|
│ │ Provider.call() │ │
|
|
│ │ (ClaudeProvider / CodexProvider) │ │
|
|
│ │ │ │
|
|
│ │ - Build system prompt │ │
|
|
│ │ - Call SDK (callClaude / callCodex) │ │
|
|
│ │ - Stream handling (onStream callback) │ │
|
|
│ │ - Error propagation │ │
|
|
│ │ │ │
|
|
│ │ → { status, content, sessionId, ... } │ │
|
|
│ └────────────────────────────────────────┘ │
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 各レイヤーの詳細
|
|
|
|
### 1. CLI Layer (`src/app/cli/index.ts`)
|
|
|
|
**役割**: ユーザー入力の受付とコマンド振り分け
|
|
|
|
**主要な処理**:
|
|
- コマンドライン引数のパース
|
|
- 入力タイプの判定:
|
|
- `isDirectTask()`: 複数単語またはissue参照 → 直接実行
|
|
- 短い単語または引数なし → インタラクティブモード
|
|
- グローバル設定の初期化 (`initGlobalDirs`, `initProjectDirs`)
|
|
- パイプラインモード vs 通常モードの判定
|
|
|
|
**データ入力**:
|
|
- CLI引数: `task`, `--workflow`, `--issue`, など
|
|
|
|
**データ出力**:
|
|
- `task: string` (タスク記述)
|
|
- `workflow: string | undefined` (ワークフロー名またはパス)
|
|
- `createWorktree: boolean | undefined`
|
|
- その他オプション
|
|
|
|
---
|
|
|
|
### 2. Interactive Layer (`src/features/interactive/interactive.ts`)
|
|
|
|
**役割**: タスクの対話的な明確化
|
|
|
|
**主要な処理**:
|
|
1. **会話ループ**:
|
|
- `readLine()`: ユーザー入力を1行ずつ読み込み
|
|
- `callAI()`: AIプロバイダーを呼び出し
|
|
- 履歴管理: `ConversationMessage[]`
|
|
|
|
2. **セッション管理**:
|
|
- `loadAgentSessions()`: 過去のセッションを復元
|
|
- `updateAgentSession()`: セッションIDを更新・保存
|
|
|
|
3. **スラッシュコマンド**:
|
|
- `/go`: タスク確定、実行へ進む
|
|
- `/cancel`: キャンセル
|
|
- `Ctrl+D`: EOF、キャンセル
|
|
|
|
4. **タスク組み立て**:
|
|
- `buildTaskFromHistory()`: 会話履歴を結合してタスク文字列を生成
|
|
|
|
**データ入力**:
|
|
- `initialInput?: string` (CLI引数から)
|
|
- ユーザーの対話入力
|
|
|
|
**データ出力**:
|
|
- `InteractiveModeResult`:
|
|
- `confirmed: boolean`
|
|
- `task: string` (会話履歴全体を結合した文字列)
|
|
|
|
---
|
|
|
|
### 3. Execution Orchestration Layer (`src/features/tasks/execute/selectAndExecute.ts`)
|
|
|
|
**役割**: ワークフロー選択とworktree管理
|
|
|
|
**主要な処理**:
|
|
|
|
1. **ワークフロー決定** (`determineWorkflow()`):
|
|
- オーバーライド指定がある場合:
|
|
- パス形式 → そのまま使用
|
|
- 名前形式 → バリデーション
|
|
- オーバーライドなし → インタラクティブ選択 (`selectWorkflow()`)
|
|
|
|
2. **Worktree作成** (`confirmAndCreateWorktree()`):
|
|
- ユーザー確認 (または `--create-worktree` フラグ)
|
|
- ブランチ名生成 (`summarizeTaskName()` - AIでタスクから英語スラグ生成)
|
|
- `createSharedClone()`: git clone --shared で軽量クローン作成
|
|
|
|
3. **タスク実行開始** (`selectAndExecuteTask()`):
|
|
- `executeTask()` を呼び出し
|
|
- 成功時: Auto-commit & Push
|
|
- PR作成 (オプション)
|
|
|
|
**データ入力**:
|
|
- `task: string`
|
|
- `options?: SelectAndExecuteOptions`:
|
|
- `workflow?: string`
|
|
- `createWorktree?: boolean`
|
|
- `autoPr?: boolean`
|
|
- `agentOverrides?: TaskExecutionOptions`
|
|
|
|
**データ出力**:
|
|
- `{ execCwd, isWorktree, branch }`
|
|
- タスク実行成功/失敗
|
|
|
|
---
|
|
|
|
### 4. Workflow Execution Layer
|
|
|
|
#### 4.1 Task Execution (`src/features/tasks/execute/taskExecution.ts`)
|
|
|
|
**役割**: ワークフロー読み込みと実行の橋渡し
|
|
|
|
**主要な処理**:
|
|
1. `loadWorkflowByIdentifier()`: YAMLまたは名前からワークフロー設定を読み込み
|
|
2. `executeWorkflow()` を呼び出し
|
|
|
|
**データ入力**:
|
|
- `ExecuteTaskOptions`:
|
|
- `task: string`
|
|
- `cwd: string` (実行ディレクトリ、cloneまたはプロジェクトルート)
|
|
- `workflowIdentifier: string`
|
|
- `projectCwd: string` (`.takt/`がある場所)
|
|
- `agentOverrides?: TaskExecutionOptions`
|
|
|
|
**データ出力**:
|
|
- `boolean` (成功/失敗)
|
|
|
|
#### 4.2 Workflow Execution (`src/features/tasks/execute/workflowExecution.ts`)
|
|
|
|
**役割**: セッション管理、イベント購読、ログ記録
|
|
|
|
**主要な処理**:
|
|
|
|
1. **セッション管理**:
|
|
- `generateSessionId()`: ワークフローセッションID生成
|
|
- `loadAgentSessions()` / `loadWorktreeSessions()`: エージェントセッション復元
|
|
- `updateAgentSession()` / `updateWorktreeSession()`: セッション保存
|
|
|
|
2. **ログ初期化**:
|
|
- `createSessionLog()`: セッションログオブジェクト作成
|
|
- `initNdjsonLog()`: NDJSON形式のログファイル初期化
|
|
- `updateLatestPointer()`: `latest.json` ポインタ更新
|
|
|
|
3. **WorkflowEngine初期化**:
|
|
```typescript
|
|
new WorkflowEngine(workflowConfig, cwd, task, {
|
|
onStream: streamHandler, // UI表示用ストリームハンドラ
|
|
initialSessions: savedSessions, // 保存済みセッションID
|
|
onSessionUpdate: sessionUpdateHandler,
|
|
onIterationLimit: iterationLimitHandler,
|
|
projectCwd,
|
|
language,
|
|
provider,
|
|
model
|
|
})
|
|
```
|
|
|
|
4. **イベント購読**:
|
|
- `step:start`: ステップ開始 → UI表示、NDJSON記録
|
|
- `step:complete`: ステップ完了 → UI表示、NDJSON記録、セッション更新
|
|
- `step:report`: レポートファイル出力
|
|
- `workflow:complete`: ワークフロー完了 → 通知
|
|
- `workflow:abort`: ワークフロー中断 → エラー通知
|
|
|
|
5. **SIGINT処理**:
|
|
- 1回目: Graceful abort (`engine.abort()`)
|
|
- 2回目: 強制終了
|
|
|
|
**データ入力**:
|
|
- `WorkflowConfig`
|
|
- `task: string`
|
|
- `cwd: string`
|
|
- `WorkflowExecutionOptions`
|
|
|
|
**データ出力**:
|
|
- `WorkflowExecutionResult`:
|
|
- `success: boolean`
|
|
- `reason?: string`
|
|
|
|
---
|
|
|
|
### 5. Engine Layer (`src/core/workflow/engine/WorkflowEngine.ts`)
|
|
|
|
**役割**: ステートマシンによるワークフロー実行制御
|
|
|
|
**主要な構成要素**:
|
|
|
|
1. **State管理** (`WorkflowState`):
|
|
- `status`: 'running' | 'completed' | 'aborted'
|
|
- `currentStep`: 現在実行中のステップ名
|
|
- `iteration`: ワークフロー全体のイテレーション数
|
|
- `stepIterations`: Map<stepName, count> (ステップごとの実行回数)
|
|
- `agentSessions`: Map<agent, sessionId> (エージェントごとのセッションID)
|
|
- `stepOutputs`: Map<stepName, AgentResponse> (各ステップの出力)
|
|
- `userInputs`: string[] (blocked時のユーザー追加入力)
|
|
|
|
2. **コンポーネント**:
|
|
- `OptionsBuilder`: エージェント実行オプション構築
|
|
- `StepExecutor`: 通常ステップの3フェーズ実行
|
|
- `ParallelRunner`: 並列ステップの実行
|
|
|
|
3. **主要メソッド**:
|
|
|
|
**`run()`**: メインループ
|
|
```typescript
|
|
while (state.status === 'running') {
|
|
// 1. Abort & Iteration チェック
|
|
if (abortRequested) { ... }
|
|
if (iteration >= maxIterations) { ... }
|
|
|
|
// 2. ステップ取得
|
|
const step = getStep(state.currentStep);
|
|
|
|
// 3. ループ検出
|
|
const loopCheck = loopDetector.check(step.name);
|
|
|
|
// 4. インストラクション構築 (非並列の場合)
|
|
const instruction = stepExecutor.buildInstruction(...);
|
|
|
|
// 5. イベント発行
|
|
emit('step:start', step, iteration, instruction);
|
|
|
|
// 6. ステップ実行
|
|
const { response, instruction } = await runStep(step, instruction);
|
|
|
|
// 7. イベント発行
|
|
emit('step:complete', step, response, instruction);
|
|
|
|
// 8. Blocked処理
|
|
if (response.status === 'blocked') { ... }
|
|
|
|
// 9. ルール評価
|
|
const nextStep = resolveNextStep(step, response);
|
|
|
|
// 10. 遷移
|
|
if (nextStep === COMPLETE_STEP) { break; }
|
|
if (nextStep === ABORT_STEP) { break; }
|
|
state.currentStep = nextStep;
|
|
}
|
|
```
|
|
|
|
**`runStep()`**: ステップ実行の委譲
|
|
- 並列ステップ → `ParallelRunner.runParallelStep()`
|
|
- 通常ステップ → `StepExecutor.runNormalStep()`
|
|
|
|
**`resolveNextStep()`**: ルール評価によるステップ遷移決定
|
|
- `response.matchedRuleIndex` を使用
|
|
- `determineNextStepByRules()` で次ステップ名を取得
|
|
|
|
**データ入力**:
|
|
- `WorkflowConfig`
|
|
- `cwd: string`
|
|
- `task: string`
|
|
- `WorkflowEngineOptions`
|
|
|
|
**データ出力**:
|
|
- `WorkflowState` (最終状態)
|
|
- イベント発行 (各ステップの進捗)
|
|
|
|
---
|
|
|
|
### 6. Instruction Building & Step Execution Layer
|
|
|
|
#### 6.1 Step Execution (`src/core/workflow/engine/StepExecutor.ts`)
|
|
|
|
**役割**: 3フェーズモデルによるステップ実行
|
|
|
|
**3フェーズの詳細**:
|
|
|
|
**Phase 1: Main Execution**
|
|
- 目的: エージェントのメインタスク実行
|
|
- Tools: ステップで指定されたツール (ただし `step.report` がある場合は Write を除外)
|
|
- インストラクション: `InstructionBuilder.build()`
|
|
|
|
**Phase 2: Report Output** (オプション、`step.report` がある場合のみ)
|
|
- 目的: レポートファイルへの出力
|
|
- Tools: **Writeのみ**
|
|
- インストラクション: `ReportInstructionBuilder.build()`
|
|
- セッション: Phase 1と同じセッションを継続 (resume)
|
|
|
|
**Phase 3: Status Judgment** (オプション、tag-based rulesがある場合のみ)
|
|
- 目的: ステータスタグの出力
|
|
- Tools: **なし** (判断のみ)
|
|
- インストラクション: `StatusJudgmentBuilder.build()`
|
|
- セッション: Phase 1と同じセッションを継続 (resume)
|
|
- 出力: `[STEP:N]` 形式のタグ
|
|
|
|
**主要メソッド**:
|
|
|
|
**`runNormalStep()`**:
|
|
```typescript
|
|
// Phase 1
|
|
const response = await runAgent(step.agent, instruction, options);
|
|
updateAgentSession(step.agent, response.sessionId);
|
|
|
|
// Phase 2 (if step.report)
|
|
if (step.report) {
|
|
await runReportPhase(step, stepIteration, context);
|
|
}
|
|
|
|
// Phase 3 (if tag-based rules)
|
|
let tagContent = '';
|
|
if (needsStatusJudgmentPhase(step)) {
|
|
tagContent = await runStatusJudgmentPhase(step, context);
|
|
}
|
|
|
|
// Rule evaluation
|
|
const match = await detectMatchedRule(step, response.content, tagContent, {...});
|
|
```
|
|
|
|
**`buildInstruction()`**:
|
|
- `InstructionBuilder` を使用してインストラクション文字列を生成
|
|
- コンテキスト情報を渡す
|
|
|
|
#### 6.2 Instruction Building (`src/core/workflow/instruction/InstructionBuilder.ts`)
|
|
|
|
**役割**: Phase 1用のインストラクション文字列生成
|
|
|
|
**自動注入セクション**:
|
|
|
|
1. **Execution Context** (実行環境メタデータ):
|
|
- Working directory
|
|
- Permission rules (edit mode)
|
|
|
|
2. **Workflow Context**:
|
|
- Iteration (workflow-wide)
|
|
- Step Iteration (per-step)
|
|
- Step name
|
|
- Report Directory/File info
|
|
|
|
3. **User Request** (タスク本文):
|
|
- `{task}` プレースホルダーがテンプレートにない場合のみ自動注入
|
|
|
|
4. **Previous Response** (前ステップの出力):
|
|
- `step.passPreviousResponse === true` かつ
|
|
- `{previous_response}` プレースホルダーがテンプレートにない場合のみ自動注入
|
|
|
|
5. **Additional User Inputs** (blocked時の追加入力):
|
|
- `{user_inputs}` プレースホルダーがテンプレートにない場合のみ自動注入
|
|
|
|
6. **Instructions** (ステップ固有のテンプレート):
|
|
- `step.instructionTemplate` の内容
|
|
- プレースホルダー置換: `{task}`, `{previous_response}`, `{iteration}`, など
|
|
|
|
7. **Status Output Rules** (tag-based rules用):
|
|
- `hasTagBasedRules(step)` の場合のみ
|
|
- `generateStatusRulesFromRules()` で生成
|
|
|
|
**プレースホルダー置換**:
|
|
- `{task}`: ユーザーリクエスト
|
|
- `{previous_response}`: 前ステップの出力
|
|
- `{user_inputs}`: 追加ユーザー入力
|
|
- `{iteration}`: ワークフロー全体のイテレーション
|
|
- `{max_iterations}`: 最大イテレーション
|
|
- `{step_iteration}`: ステップのイテレーション
|
|
- `{report_dir}`: レポートディレクトリ
|
|
|
|
**ロケール対応**:
|
|
- `language: 'en' | 'ja'`
|
|
- セクション見出しや説明文が言語に応じて切り替わる
|
|
|
|
---
|
|
|
|
### 7. Provider Layer
|
|
|
|
#### 7.1 Agent Runner (`src/agents/runner.ts`)
|
|
|
|
**役割**: エージェント仕様の解決とプロバイダー呼び出し
|
|
|
|
**主要な処理**:
|
|
1. **エージェント仕様解決**:
|
|
- ビルトインエージェント (`coder`, `architect`, など)
|
|
- カスタムエージェント (`.takt/agents.yaml`)
|
|
- プロンプトファイル (`.md`)
|
|
|
|
2. **プロバイダー取得**:
|
|
- `getProvider(providerType)`: ClaudeProvider / CodexProvider / MockProvider
|
|
|
|
3. **エージェント呼び出し**:
|
|
- `provider.call(agentName, instruction, options)`
|
|
|
|
**データ入力**:
|
|
- `agent: string` (エージェント名またはパス)
|
|
- `instruction: string` (構築済みインストラクション)
|
|
- `AgentRunOptions`:
|
|
- `cwd: string`
|
|
- `sessionId?: string`
|
|
- `allowedTools?: string[]`
|
|
- `provider?: ProviderType`
|
|
- `model?: string`
|
|
- `onStream?: StreamHandler`
|
|
|
|
**データ出力**:
|
|
- `AgentResponse`:
|
|
- `agent: string`
|
|
- `status: 'success' | 'blocked'`
|
|
- `content: string`
|
|
- `sessionId?: string`
|
|
- `error?: string`
|
|
- `timestamp: Date`
|
|
|
|
#### 7.2 Provider (`src/infra/providers/`)
|
|
|
|
**役割**: AIプロバイダー(Claude, Codex)とのSDK通信
|
|
|
|
**主要なプロバイダー**:
|
|
- `ClaudeProvider`: Claude Code SDK (`@anthropic-ai/claude-agent-sdk`)
|
|
- `CodexProvider`: Codex API
|
|
- `MockProvider`: テスト用
|
|
|
|
**主要メソッド**:
|
|
|
|
**`call()`**:
|
|
```typescript
|
|
async call(
|
|
agentName: string,
|
|
instruction: string,
|
|
options: ProviderCallOptions
|
|
): Promise<AgentResponse>
|
|
```
|
|
|
|
**処理内容**:
|
|
1. システムプロンプト構築
|
|
2. SDK呼び出し (`callClaude()` / `callCodex()`)
|
|
3. ストリーミング処理 (`onStream` callback)
|
|
4. エラーハンドリング
|
|
5. レスポンス変換
|
|
|
|
**データ入力**:
|
|
- `agentName: string`
|
|
- `instruction: string`
|
|
- `ProviderCallOptions`:
|
|
- `cwd: string`
|
|
- `sessionId?: string`
|
|
- `systemPrompt?: string`
|
|
- `allowedTools?: string[]`
|
|
- `model?: string`
|
|
- `onStream?: StreamHandler`
|
|
|
|
**データ出力**:
|
|
- `AgentResponse` (上記と同じ)
|
|
|
|
---
|
|
|
|
## データフローの段階
|
|
|
|
### ステージ1: タスク入力
|
|
|
|
**入力方法**:
|
|
1. **直接タスク**: `takt "Fix the login bug"`
|
|
2. **Issue参照**: `takt #123`
|
|
3. **インタラクティブモード**: `takt` または `takt a`
|
|
|
|
**データ変換**:
|
|
- インタラクティブモード: `ConversationMessage[]` → `task: string`
|
|
- `buildTaskFromHistory()`: 会話履歴を結合
|
|
|
|
**出力**: `task: string`
|
|
|
|
---
|
|
|
|
### ステージ2: 実行環境準備
|
|
|
|
**ワークフロー選択**:
|
|
- `--workflow` フラグ → 検証
|
|
- なし → インタラクティブ選択 (`selectWorkflow()`)
|
|
|
|
**Worktree作成** (オプション):
|
|
- `confirmAndCreateWorktree()`:
|
|
- ユーザー確認または `--create-worktree` フラグ
|
|
- `summarizeTaskName()`: タスク → 英語スラグ (AI呼び出し)
|
|
- `createSharedClone()`: git clone --shared
|
|
|
|
**データ**:
|
|
- `workflowIdentifier: string`
|
|
- `{ execCwd, isWorktree, branch }`
|
|
|
|
---
|
|
|
|
### ステージ3: ワークフロー実行初期化
|
|
|
|
**セッション管理**:
|
|
- `loadAgentSessions()`: 保存済みセッション復元
|
|
- `generateSessionId()`: ワークフローセッションID生成
|
|
- `initNdjsonLog()`: NDJSON ログファイル作成
|
|
|
|
**WorkflowEngine作成**:
|
|
```typescript
|
|
new WorkflowEngine(workflowConfig, cwd, task, {
|
|
onStream,
|
|
initialSessions,
|
|
onSessionUpdate,
|
|
projectCwd,
|
|
language,
|
|
provider,
|
|
model
|
|
})
|
|
```
|
|
|
|
**データ**:
|
|
- `WorkflowState`: 初期状態
|
|
- `currentStep = config.initialStep`
|
|
- `iteration = 0`
|
|
- `agentSessions = initialSessions`
|
|
|
|
---
|
|
|
|
### ステージ4: ステップ実行ループ
|
|
|
|
**各イテレーション**:
|
|
|
|
1. **ステップ取得**: `getStep(state.currentStep)`
|
|
2. **インストラクション構築**: `InstructionBuilder.build()`
|
|
3. **ステップ実行**: 3フェーズ実行
|
|
4. **ルール評価**: `detectMatchedRule()`
|
|
5. **ステップ遷移**: `resolveNextStep()` → 次のステップ名
|
|
|
|
**データ変換**:
|
|
- `task + context` → `instruction: string`
|
|
- `instruction` → `AgentResponse` (via Provider)
|
|
- `AgentResponse + rules` → `matchedRuleIndex`
|
|
- `matchedRuleIndex` → `nextStep: string`
|
|
|
|
---
|
|
|
|
### ステージ5: インストラクション生成
|
|
|
|
**InstructionBuilder処理**:
|
|
|
|
1. **コンテキスト収集**:
|
|
- `task`: 元のユーザーリクエスト
|
|
- `iteration`, `maxIterations`: イテレーション情報
|
|
- `stepIteration`: ステップごとの実行回数
|
|
- `cwd`, `projectCwd`: ディレクトリ情報
|
|
- `userInputs`: blocked時の追加入力
|
|
- `previousOutput`: 前ステップの出力
|
|
- `reportDir`: レポートディレクトリ
|
|
|
|
2. **セクション組み立て**:
|
|
- 自動注入セクション (上記7つ)
|
|
- プレースホルダー置換
|
|
|
|
3. **出力**: 完全なインストラクション文字列
|
|
|
|
---
|
|
|
|
### ステージ6: エージェント実行
|
|
|
|
**Phase 1: Main Execution**:
|
|
- `runAgent()` → `provider.call()`
|
|
- ストリーミング → `onStream` callback → UI表示
|
|
- 結果: `AgentResponse`
|
|
|
|
**Phase 2: Report Output** (オプション):
|
|
- 同じセッションを継続 (resume)
|
|
- Write-only ツール
|
|
- レポートファイル出力
|
|
|
|
**Phase 3: Status Judgment** (オプション):
|
|
- 同じセッションを継続 (resume)
|
|
- ツールなし
|
|
- `[STEP:N]` タグ出力
|
|
|
|
---
|
|
|
|
### ステージ7: ルール評価と遷移
|
|
|
|
**ルール評価** (`detectMatchedRule()`):
|
|
|
|
5段階のフォールバック:
|
|
1. **Aggregate** (`all()`/`any()`) - 並列ステップ用
|
|
2. **Phase 3 tag** - `[STEP:N]` from status judgment
|
|
3. **Phase 1 tag** - `[STEP:N]` from main output (fallback)
|
|
4. **AI judge** - `ai("condition text")` rules
|
|
5. **AI judge fallback** - すべての条件をAIで評価
|
|
|
|
**出力**: `{ index: number, method: RuleMatchMethod }`
|
|
|
|
**遷移**:
|
|
- `determineNextStepByRules()`: `rules[index].next` を取得
|
|
- 特殊ステップ:
|
|
- `COMPLETE`: ワークフロー完了
|
|
- `ABORT`: ワークフロー中断
|
|
- 通常ステップ: `state.currentStep = nextStep`
|
|
|
|
---
|
|
|
|
## 重要な変換ポイント
|
|
|
|
### 1. 会話履歴 → タスク文字列
|
|
|
|
**場所**: `src/features/interactive/interactive.ts`
|
|
|
|
```typescript
|
|
function buildTaskFromHistory(history: ConversationMessage[]): string {
|
|
return history
|
|
.map((msg) => `${msg.role === 'user' ? 'User' : 'Assistant'}: ${msg.content}`)
|
|
.join('\n\n');
|
|
}
|
|
```
|
|
|
|
**重要性**: インタラクティブモードで蓄積された会話全体が、後続のワークフロー実行で単一の `task` 文字列として扱われる。
|
|
|
|
---
|
|
|
|
### 2. タスク → ブランチスラグ (AI生成)
|
|
|
|
**場所**: `src/infra/task/summarize.ts` (呼び出し: `selectAndExecute.ts`, `taskExecution.ts`)
|
|
|
|
```typescript
|
|
await summarizeTaskName(task, { cwd })
|
|
```
|
|
|
|
**処理**:
|
|
- タスク文字列をAIに渡す
|
|
- 英語の短いスラグに要約 (例: `fix-login-bug`)
|
|
- ブランチ名として使用
|
|
|
|
**重要性**: ユーザーが日本語でタスクを書いても、Git-friendlyなブランチ名が自動生成される。
|
|
|
|
---
|
|
|
|
### 3. ワークフロー設定 → WorkflowState
|
|
|
|
**場所**: `src/core/workflow/engine/state-manager.ts`
|
|
|
|
```typescript
|
|
function createInitialState(
|
|
config: WorkflowConfig,
|
|
options: WorkflowEngineOptions
|
|
): WorkflowState {
|
|
return {
|
|
status: 'running',
|
|
currentStep: config.initialStep,
|
|
iteration: 0,
|
|
stepIterations: new Map(),
|
|
agentSessions: new Map(Object.entries(options.initialSessions ?? {})),
|
|
stepOutputs: new Map(),
|
|
userInputs: [],
|
|
};
|
|
}
|
|
```
|
|
|
|
**重要性**: YAMLで定義された静的な設定が、実行時のミュータブルな状態に変換される。
|
|
|
|
---
|
|
|
|
### 4. コンテキスト → インストラクション文字列
|
|
|
|
**場所**: `src/core/workflow/instruction/InstructionBuilder.ts`
|
|
|
|
**入力**:
|
|
- `step: WorkflowStep`
|
|
- `context: InstructionContext` (task, iteration, previousOutput, userInputs, など)
|
|
|
|
**処理**:
|
|
1. 7つのセクションを組み立て
|
|
2. プレースホルダー置換
|
|
3. ロケール対応
|
|
|
|
**出力**: 完全なMarkdown形式のインストラクション文字列
|
|
|
|
**重要性**: 散在するコンテキスト情報が、エージェントが理解できる単一の文字列に統合される。
|
|
|
|
---
|
|
|
|
### 5. AgentResponse → ルールマッチ
|
|
|
|
**場所**: `src/core/workflow/evaluation/RuleEvaluator.ts`
|
|
|
|
**入力**:
|
|
- `step: WorkflowStep`
|
|
- `content: string` (Phase 1 output)
|
|
- `tagContent: string` (Phase 3 output)
|
|
- `state: WorkflowState`
|
|
|
|
**処理**:
|
|
1. タグ検出 (`[STEP:0]`, `[STEP:1]`, ...)
|
|
2. AI判断 (`ai("condition")` ルール)
|
|
3. 集約評価 (`all()`, `any()`)
|
|
|
|
**出力**: `{ index: number, method: RuleMatchMethod } | null`
|
|
|
|
**重要性**: 自然言語の出力が、構造化されたステップ遷移決定に変換される。
|
|
|
|
---
|
|
|
|
### 6. ルールマッチ → 次ステップ名
|
|
|
|
**場所**: `src/core/workflow/engine/transitions.ts`
|
|
|
|
```typescript
|
|
function determineNextStepByRules(
|
|
step: WorkflowStep,
|
|
matchedRuleIndex: number
|
|
): string | null {
|
|
const rule = step.rules?.[matchedRuleIndex];
|
|
return rule?.next ?? null;
|
|
}
|
|
```
|
|
|
|
**重要性**: インデックス番号が、実際に実行すべきステップ名に変換される。
|
|
|
|
---
|
|
|
|
### 7. Provider Response → AgentResponse
|
|
|
|
**場所**: `src/infra/providers/claude.ts`, `src/infra/providers/codex.ts`
|
|
|
|
**入力**: SDKレスポンス (`ClaudeResult`)
|
|
|
|
**処理**:
|
|
- `status` 変換
|
|
- `content` 抽出
|
|
- `error` 伝播 (重要!)
|
|
- `sessionId` 保存
|
|
|
|
**出力**: `AgentResponse` (統一インターフェース)
|
|
|
|
**重要性**: 異なるプロバイダーのレスポンスが統一形式に正規化される。
|
|
|
|
---
|
|
|
|
## まとめ
|
|
|
|
TAKTのデータフローは、**7つのレイヤー**を通じて、ユーザーの自然な入力を段階的に変換し、最終的にAIエージェントの協調的な実行に変えていきます。
|
|
|
|
**主要な設計原則**:
|
|
|
|
1. **Progressive Transformation**: データは各レイヤーで少しずつ変換され、次のレイヤーに渡される
|
|
2. **Context Accumulation**: タスク、イテレーション、ユーザー入力などのコンテキストが蓄積される
|
|
3. **Session Continuity**: エージェントセッションIDが保存・復元され、会話の継続性を保つ
|
|
4. **Event-Driven Architecture**: WorkflowEngineがイベントを発行し、UI、ログ、通知が連携
|
|
5. **3-Phase Execution**: メイン実行、レポート出力、ステータス判断の3段階で、明確な責任分離
|
|
6. **Rule-Based Routing**: ルール評価の5段階フォールバックで、柔軟かつ予測可能な遷移
|
|
|
|
このアーキテクチャにより、TAKTは複雑な多エージェント協調を、ユーザーには透明で、開発者には拡張可能な形で実現しています。
|