単一ステップのAI操作の構築は簡単です:入力を受け取り、LLMを呼び出し、出力を返す。ブランチ、ループ、並列実行、ヒューマン承認ゲート、自動リトライをサポートするマルチステップワークフローエンジンの構築はまったく別種の問題です。
この記事ではJieGouのワークフローエンジンのアーキテクチャ — 実行モデル、8つのステップタイプ、ステップ間のデータフロー、承認による一時停止と再開、信頼性を保つガードレールを解説します。
実行モデル
ワークフローはステップの有向グラフです。実行はexecuteWorkflow()で開始し、WorkflowRunレコードを作成、共有StepExecutionContextを構築、executeStepList()を呼び出してステップを順次処理します。
コンテキストはpreviousStepOutputs Mapを運びます — 各完了ステップが下流ステップが参照できるよう出力を格納するキーバリューストア。ステップBは{{step.stepA.fieldName}}のようなテンプレート構文でステップAの出力を参照可能。
8つのステップタイプ
レシピステップ
主力。再利用可能なプロンプトテンプレートをexecuteRecipe()で実行し、パース済み出力をpreviousStepOutputsに格納。
条件ステップ
ブール式を評価し、thenStepsまたはelseStepsを再帰的に実行。両方のパスに任意のステップタイプ(ネストされた条件を含む)を含められる真のブランチ。
ループステップ
コレクションを反復し、各アイテムに対してサブステップのリストを実行。各反復が独自のloopContext Mapを取得し、サブステップが{{loop_item.fieldName}}経由で現在のアイテムを参照可能。
パラレルステップ
Promise.allSettled()経由で複数のブランチを同時実行。各ブランチは独立したステップリスト。
承認ステップ
アーキテクチャ的に最も興味深いステップタイプ。実行が承認ステップに到達するとApprovalPauseErrorをスロー。制御された例外 — クラッシュではありません。WorkflowRunがpending_approvalステータスで永続化され、実行が完全に停止。承認者がアクションを取ると、resumeWorkflowFromApproval()が永続化された実行をロードし、previousStepOutputs Mapを再構築して次のステップから実行を再開。
Write-to-KBステップ
ステップの出力をキャプチャしてナレッジベースドキュメントに書き込み。ファイアアンドフォーゲット。
ハンドオフステップ
ターゲット部門のユーザーにメールとアプリ内通知で通知。実行のノーオプ — 出力を生成せずパイプラインをブロックしない。
ブラウザアクションステップ
ブラウザ拡張経由でMCPツールコールを実行。
データフロー:テンプレート解決
ステップは実行時に解決されるテンプレート構文で相互参照:
{{workflow_input.fieldName}}— ワークフローの入力データを参照{{step.stepId.path.to.value}}— ドット記法で前ステップの出力を参照{{loop_item.fieldName}}— ループ反復の現在のアイテムを参照
リトライとエラーハンドリング
リトライ戦略はジッター付き指数バックオフ:Math.min(30000, 2000 * 2^attempt + ランダムジッター)。約2秒、4秒、8秒、16秒、30秒上限。デフォルト最大試行回数3、ステップごとに設定可能。
同時実行制御
各アカウントはRedisセマフォによるLLM同時呼び出し10回制限。MCP接続はLRUエビクション付きプールクライアント(最大20接続、60秒アイドルタイムアウト)を使用。両方ともRedisエラー時のフェイルオープンセマンティクス。
オブザーバビリティ
3レイヤーのオブザーバビリティが同時実行:
Prometheusメトリクスがステータス別のworkflowExecutionsTotalとworkflowDurationを追跡。
OpenTelemetryスパンがワークフローレベルとステップレベルのスパンでトレース階層を作成。
実行トレースがFirestoreにファイアアンドフォーゲットで永続化されるカスタム階層スパンツリー。UIの詳細実行インスペクターを駆動。
学んだ教訓
例外としての承認ゲートがアーキテクチャを簡素化。 ワークフローは承認に当たるまで前進、スロー、永続化、停止。再開は状態を再構築して続行。複雑な状態遷移の管理不要。
再帰的出力再構築は複雑さに見合う。 永続化されたステップ実行からpreviousStepOutputsを再構築するには、すべてのネストレベル — 条件、ループ、パラレルブランチ — を走査する必要があります。しかし任意のネスト深度で承認が正しく動作します。
ステップごとのタイムアウトがカスケード遅延を防止。 単一の遅いLLMコールがワークフロー全体のタイムアウトを食い尽くすべきではありません。
ファイアアンドフォーゲットのオブザーバビリティがホットパスを高速に保つ。 実行トレース、Webhookデリバリー、デスティネーション出力はすべてメイン実行完了後に非同期でディスパッチ。