更新日:2026年9月24日
18分で読めます
YAML設定を完全に機能するLangGraphフローへコンパイルするコンポーネントベースのフレームワーク「Flow Registry」によって、GitLabはエージェント型フロー1つあたりのコード量を45%削減し、スケーラビリティや信頼性を高めました。その設計原則と得られた教訓を詳しく解説します。

GitLab Duo Agent Platformは、エージェント型フローによって複雑なタスクをオーケストレーションし、自動化します。その中核を担うのが、再利用可能なコンポーネントで構成され、YAMLを完全に機能するLangGraphフローへとコンパイルする宣言型の設定フレームワーク「Flow Registry」。Flow Registryを使えば、GitLabのエンジニアもお客様も、場当たり的なPythonコードを繰り返し書くことなく、宣言型のYAML設定でエージェントを構築できます。エージェントごとに重複していた独自の状態管理やエージェント間の接続処理も、エージェントビルダーが利用できる再利用可能なコンポーネントとプリミティブへと集約されました。
Flow Registryの導入により、GitLab社内ではエージェント型フロー1つあたりのコード量を45%削減できました。これによって得られた具体的な効果は、次のとおりです。
これらのメリットはすべて、GitLab Duo Agent Platformでエージェントを構築・オーケストレーションするお客様にもご利用いただけます。
本記事では、この取り組みから得たアーキテクチャの原則と教訓、そしてそれを皆さまの環境に応用する方法をご紹介します。
GitLab Duoのコード提案とGitLab Duo Chatをリリースした後、私たちは当時まだ新しい技術だった、自律的に動作するAIエージェントの研究に着手しました。利用可能なAIフレームワークを調査した結果、GitLab Duo Agent Platformの基盤として選んだのが、LangChainが提供するエージェントランタイム兼低レベルのオーケストレーションフレームワーク「LangGraph」。
LangGraphは、幅広いモデルアダプター、永続的な実行(durable execution)、トレーサビリティといった豊富な機能を備えています。エンジニアリング上の自由度も高く、GitLab Duo Agent Platformの開発に着手するうえで求めていた構成要素がすべてそろっていました。
最初の数か月間、GitLabのエンジニアはLangGraphを活用してDuo Agent Platformの基盤を迅速に構築し、ほどなくして4つのエージェント型フローをリリースしました。
一方で、LangGraphのような低レベルのフレームワークでは解決できない重大な課題にも、すぐに気づきました。それは、大規模な開発を一貫して進めるための構造が欠けているという点です。
当時はフローが4つしかなく、GitLab Duo Agent Platformに携わるエンジニアチームも小規模でしたが、コードベースは急速に膨らんでいきました。各フローはその場しのぎの有向グラフとして実装され、ノードとエッジが複雑に絡み合う状態に。初期のDuo Agent Platformのコードベースには、再利用性も組み合わせ可能性(コンポーザビリティ)もなく、共通の標準もほとんどありませんでした。新機能の開発は非常に難しくなり、プラットフォーム全体に関わる横断的な変更は不可能に思えるほどでした。
どのフローも最低450行のその場しのぎのPythonコードで書かれ、この例のような状態でした。複雑さと密結合に圧倒されたエンジニアは変更を加えるのに苦労し、チームの開発速度は低下していきました。
グラフの複雑さは、テストスイートにも波及しました。各テストケースがグラフ全体を通した実行に依存していたため、品質を守るセーフティネットであるはずのテストは、誰も触れたがらない厄介者になっていました。
この低い抽象化レベルでグラフを最小単位の構成要素として扱うのは、プラットフォームの実装には不向きであることが明らかになりました。構想していた規模を支えるには、複雑さを分解し、プロジェクトを保守するプラットフォームエンジニアの認知負荷を軽減する、より小さな単位を導入する必要があったのです。
さらに、モデルAPIの呼び出しや、モデルが生成した関数呼び出しの実行を管理する低レベルのノードで構成されたグラフは、AIエンジニアにとっても適切な抽象化ではありませんでした。AIエンジニアには、エージェントやエージェントオーケストレーションといった用語のほうがなじみ深いからです。
この迷路から抜け出すため、私たちは新たな抽象化レイヤーを導入し、AIエンジニアリングとプラットフォーム開発という2つの関心事を分離することにしました。まず着手したのは、既存のグラフを見直し、繰り返し現れる構造(たとえば、エージェントループを実装する、大規模言語モデル(LLM)の呼び出しとツール実行を行き来するサイクルなど)を特定して抽出する作業。このリファクタリングで最も複雑なファイルが小さな部品に分割され、それらが新しい抽象化レイヤーを形成したことで、状況はある程度改善しました。
しかし、プラットフォームがスケーラブルと呼べる状態には、まだ程遠いのが実情でした。抽出したグラフの部品は、残念ながらそれぞれ独自の状態構造で動作し、元のフローと密結合していたため、異なるフロー間で再利用できなかったのです。加えて懸念されたのが、このままでは新しいフローを作るたびに、既存の部品を組み合わせるのではなく、独自の部品セットが新たに生まれてしまうこと。協調的でも効率的でもないこのシステムを、長期にわたって維持することはできませんでした。
この気づきによって、進むべき道がはっきりしました。求められたのは、さらに力を注いでアーキテクチャを進化させ続けること、そして明確な開発ガイドラインを整備すること。当時すでに、Duo Agent Platformのフローは他のプロダクトチームだけが構築するものではなく、より広いGitLabコミュニティにもコントリビューションを呼びかける予定であることがわかっていました。
これまでの経験を生かし、野心的な目標に背中を押されるように、私はチームメイトのAlexander Chueshevとともに原点に立ち返り、システムを再考しました。目指したのは、高い協調性と組み合わせ可能性を備え、AI開発の効率に最適化されたソリューションです。
この新しいイテレーションでは、LangGraphの低レベルな実装の詳細を隠し、開発者がノードやエッジに煩わされないようにしたいと考えました。システムが話すべきなのは開発者の言語、つまりエージェントを中心に据えたAIエンジニアリングの言語です。
AI開発における思考の単位は、モデルを呼び出したりツールを実行したりするノードではなく、エージェントであることは明らかでした。前回のイテレーションから得た教訓をもとに、新しいフレームワークは次の3つの柱で構成することに決めました。
これらをうまく設計できれば、ユーザーが構築したいと考えるあらゆるAIフローに対応できる柔軟なフレームワークになると確信していました。
コンポーネントは、Flow Registryの中心となる最も重要な柱です。対象となるのは、エージェント、ヒューマンインザループのチェックポイント、固定ロジックのステップといった共通のプリミティブ。この柱によって、抽象化レベルがAIエンジニアリング分野で使われる用語に合わせて引き上げられました。コンポーネントのおかげで、エージェントビルダーはこれらのプリミティブを一から再実装する必要がなくなり、次の例のようなYAMLスニペットで宣言するだけで済みます。
- type: AgentComponent
name: "developer_agent"
prompt_id: "developer_agent_prompt"
inputs:
- from: "context:goal"
as: "goal"
- from: "context:project_id"
as: "project_id"
toolset:
- "read_file"
- "find_files"
- "edit_file"
- "run_command"
- "create_merge_request"
内部的には、AgentComponentも依然としてLangGraphのグラフの一部であり、その簡略化した構造は下図のとおりです。ただし、実装の複雑さはエージェントビルダーからは隠され、ビルダーはよりなじみのあるプリミティブを扱えるようになりました。同じアーキテクチャ上の境界がもたらすメリットは、フレームワークの保守担当者にとっても大きなもの。基盤となる実装を変更・拡張する自由度が高まり、その変更はフローに透過的に反映されます。
flowchart LR
%% External input/output
input((inputs<br>from<br>shared state)) --> LLMCall
End --> output((outputs<br>to shared state))
%% Prompts
Prompt["You are expert<br>software<br>engineer ..."] --> LLMCall
subgraph Prompts
direction TB
style Prompts stroke-dasharray: 4 4, stroke:#3CB371
Prompt
end
%% LLM and internal component
LLMCall --> End
LLMCall --> RunTools
RunTools --> LLMCall
subgraph Component
direction LR
LLMCall[LLM Call]
RunTools[Run Tools]
End[END]
end
%% Tools
EditFile --> RunTools
ReadFile --> RunTools
subgraph Tools
direction LR
style Tools stroke-dasharray: 4 4, stroke:#1E90FF
EditFile[Edit file]
ReadFile[Read file]
end
サブエージェントに作業を委任できる「コンポーネントとしてのエージェント」は、柱1だけで実現できる強力な基盤。現在の多くのエージェントプラットフォームでは、これだけで完結した十分な機能と見なされています。しかし、GitLabはDuo Agent Platformにより大きな目標を掲げており、Flow Registryは残る2つの柱でそれを実現しています。
Flow Registryのルーターを使うと、エージェントビルダーは複数の専門エージェント、さらにはエージェントチームを1つのフローにオーケストレーションし、非常に複雑なビジネスプロセスやソフトウェア開発プロセスをモデル化できます。現在の最大規模のモデルは単独でも複雑なタスクをこなせるほど強力ですが、最近のサブエージェントアーキテクチャの普及が示すように、複数のエージェントを組み合わせて1つのタスクに協力して取り組ませることには多くのメリットがあります。
実践的な例として、GitLabの基本フローの1つであるCI/CDパイプライン修正フローを見てみましょう。このフローは、失敗したCIパイプラインを自動トリガーでトリアージし、修正するための構成。CIパイプラインは非常に複雑になりうるため、すべての失敗がコードの変更で解決されるわけではありません。たとえば依存先のサービスが一時的に応答していないだけなら、単純にリトライすれば解決することも。こうした2通りの対応に備えるため、フローは判定役のエージェントの判断に基づいて早い段階で分岐します。判定エージェントが「失敗は対処が必要なものか」を裁定し、Flow Registryのルーターがその結果をもとにフローの実行を適切なブランチへ導く仕組みです。
routers:
- from: "fix_pipeline_context"
condition:
input: "context:fix_pipeline_context.final_answer.decision"
routes:
"add_comment": "fix_pipeline_add_comment"
"create_plan": "fix_pipeline_checkout_existing_branch"
"direct_code_suggestions": "fix_pipeline_code_suggestions"
"no_action": "end"
"default_route": "end"
確かに、最先端のモデルであれば同様の判断を下し、同時にそれを実行に移すことも可能なはずです。それでもマルチエージェントアーキテクチャを採用することで、エージェントビルダーは次のようなメリットを得られます。
柱2によって、エージェントビルダーは選択肢を得られます。強力なモデルでシンプルなフロー構成を採用するか、モデルのプロンプトが担っていた複雑さを明示的なフロー構造へと移すか。幅広いユースケース、コスト目標、リスクプロファイルに対応できる柔軟性です。
Flow Registryの3つ目にして最後の柱は、コンポーネント間の通信プロトコルとして機能する共有状態構造です。これがなければコンポーネントどうしが確実にやり取りする手段がなく、先の2つの柱も機能しません。CI/CDパイプライン修正フローの例に戻って考えてみてください。判定エージェントの裁定も、Flow Registryのルーターに確実に渡せなければほとんど意味がありません。より広く見ても、あるエージェントが生成したデータは後続のエージェントで必要になることが多く、明確に定義された通信契約が欠かせないのです。
Flow Registryの状態構造にはcontextという特別な汎用属性があり、ネストされたキーバリューストア(またはJSONオブジェクト)のように、コンポーネントへ柔軟なストレージ領域を提供します。さらに、context属性の柔軟性を補完するため、ドット記法によるcontextへの宣言的なアクセスも導入。共有キーバリューストレージへのアクセスを静的な設定内で表現する必要がある他の分野(GitLab CI Functionsなど)でもおなじみの規約です。
3つ目の柱を完成させるには、規約が欠かせません。Flow Registryでは、先ほどのドット記法を使って共有状態から柔軟に読み取りを行えますが、書き込みはすべて厳格なルールに従います。その結果として得られるのが、エージェントビルダーが信頼できる、安定した予測可能な出力。実際の動作を確認するため、もう1つの基本フローであるコードレビューのFlow Registry設定の一部を見てみましょう。
# …
# Step 5: Fetch lightweight MR metadata (file paths + custom instructions)
- name: "fetch_mr_metadata"
type: DeterministicStepComponent
tool_name: "build_review_merge_request_context"
# ….
# Step 6: Transform prescan results into structured JSON for review consumption
- name: "analyze_prescan_results"
type: AgentComponent
prompt_id: "analyze_prescan_codebase_results"
prompt_version: "^1.0.0"
inputs:
- from: "context:fetch_mr_metadata.tool_responses"
as: "mr_context"
- from: "context:prescan_codebase.tool_responses"
as: "prescan_tool_responses"
optional: True
toolset: []
ui_log_events:
- "on_agent_final_answer"
# …..
routers:
# ….
- from: "fetch_mr_metadata"
to: "analyze_prescan_results"
コードレビューフローのエージェント analyze_prescan_results は、その前段にある固定ステップのアクション fetch_mr_metadata が取得したデータを必要とします。この依存関係は、analyze_prescan_results エージェントに宣言された inputs によって表現されています。
inputs:
- from: "context:fetch_mr_metadata.tool_responses"
as: "mr_context"
このように、ドット記法と厳格な出力規約が連携することで、フロー内のコンポーネント間でデータを受け渡すための安定した予測可能なプロトコルがエージェントビルダーに提供されます。
Flow Registryは宣言型のYAML設定を使っていますが、設計と再アーキテクチャはPythonで始め、宣言型の設定APIは将来のイテレーションに先送りしていました。しかし、Flow Registryの3つの柱が1つのPythonブロックの中で組み合わさったとき、それらの宣言をYAML設定に変換するのはあと一歩だと気づき、その一歩を踏み出すことにしたのです。
この変更によって、Flow RegistryフレームワークはPythonとLangGraphから完全に切り離され、宣言的にAIフローを作成するための高レベルな抽象化構文が実現しました。LangGraphを基盤として実装されたままのプラットフォームと、外部に公開されるフレームワークの宣言型インターフェースとの間に明確な境界が生まれ、AIエンジニアはより慣れ親しんだ概念で作業できるようになっています。
GitLab Duo Agent Platformの基盤としてFlow Registryフレームワークを導入したことで、システム全体が、ユースケースごとの素のLangGraph実装から、宣言型のYAML設定へと大きく進化しました。現在本番環境で稼働しているGitLab Duoデベロッパーフローを支えるこの設定も、その一例です。
AIエンジニア向けに設計されたフレームワークAPIからプラットフォームが切り離されたことで、基盤となるPythonコードベースはすべてのフローで共有できるようになりました。エンジン自体に加えた改善はすべてのフローに行き渡るため、明確な分離による効率向上がいっそう際立ちます。
さらに、フローを追加するたびに、フローあたりのコードコストは下がっていきます。本記事の執筆時点で、Flow Registryの導入によりフローあたりのPythonソースコードの比率は45%削減。この数値は、新しいフローが構築されるたびにさらに改善していきます。
そして何より、AIエンジニアやドメインエキスパートは、基盤となるプラットフォームの実装の詳細を理解する必要も、独自のテストスイートや保守が必要になるボイラープレートのPythonコードを繰り返し書く必要もなくなりました。それを実証したのが、今年初めに開催されたGitLab AIハッカソン。約7,000人の開発者が参加登録し、600以上のエージェントとフローが提出されました。
私たちが得た重要な気づきの1つは、現代のAIエンジニアリングがソフトウェア開発の中でもまだ非常に若い分野で、共通のアーキテクチャパターンやパラダイムが十分に形成されていないという点。しかし、だからといって、すでに確立された優れたソフトウェアエンジニアリングのプラクティスをAIエンジニアリングに適用できないわけではありません。むしろFlow Registryの事例が示すとおり、繰り返し現れるコードを特定し、システム内で明確な役割を持つ名前付きエンティティとして抽出し、そこから抽象化レイヤーを形成するといった、既存のソフトウェアエンジニアリングのパラダイムやプラクティスに立ち返ることで、大きな成果が得られるのです。
このほかにも、エージェント型システムを構築するあらゆるチームに広く当てはまる教訓と、LangGraphのような低レベルのフレームワークを扱うチームにとって実践的な出発点となる教訓をいくつかご紹介します。
実際の動作を体感するには、AIカタログにアクセスしてみてください。AIカタログでは、この取り組みから生まれたFlow Registryオーケストレーションフレームワークが公開されており、誰でもカスタムフローを構築できます。
無料の
30日間のGitLabトライアルを開始
クレジットカードなしでご登録いただけます。
このブログ記事を楽しんでいただけましたか?ご質問やフィードバックがあればお知らせください。GitLabコミュニティフォーラムで新しいトピックを作成してあなたの声を届けましょう。
フィードバックを共有する