なぜADK for Go 2.0が今注目なのか

現実のエージェントアプリケーションは、単一のプロンプトで終わりません。分類、分岐、並列実行、人間の承認待ち、リトライ、ループといった複雑なオーケストレーションが必要です。これらをアドホックな制御フローで実装すると、すぐにメンテナンス不能になります。

ADK for Go 1.0は、強力な型システム、iter.Seq2イベントストリーム、既存のGoサービスに自然に統合できるランタイムで高く評価されました。そして、2.0が登場しました。

キーワードは「グラフ」です。

ADK 2.0は、マルチエージェントアプリケーションを構成するための第一級の方法として、グラフベースのワークフローエンジンを導入しました。Human-in-the-loop(HITL)、動的オーケストレーション、LLMエージェントモードも加わり、単一エージェントとフルグラフが同一の実行モデル上で動作します。

Python ADK 2.0をご存知の方なら、同じ方向性とお考えください。ただし、Go開発者向けに「Goらしい」APIとして設計されている点が最大の特徴です。

この記事では、ADK for Go 2.0のコアコンセプトとコード例を通じて、実際のプロジェクトへの適用方法を解説します。

根拠資料: Google Developers Blog - Announcing ADK for Go 2.0

Go developers using ADK 2.0 graph workflow to compose multi-agent applications on a terminal Development Concept Image

グラフベースワークフローの核心:コードで理解する

ADK 2.0の最大の変更点は、アプリケーションの形状をノード(Node)とエッジ(Edge)で構成されるグラフとして記述し、スケジューラがそれを並行実行し、状態を保持し、人間の介入のために一時停止し、後で再開できるようにした点です。

基本チェーン:最もシンプルなグラフ

package main

import (
	"context"
	"fmt"
	"google.golang.org/adk/v2/workflow"
	"google.golang.org/adk/v2/agent"
)

// 大文字に変換するノード
func upperFn(ctx agent.Context, in string) (string, error) {
	return strings.ToUpper(in), nil
}

// 文字列にサフィックスを追加するノード
func suffixFn(ctx agent.Context, in string) (string, error) {
	return in + " - 完了!", nil
}

func main() {
	upper := workflow.NewFunctionNode("upper", upperFn, workflow.NodeConfig{})
	suffix := workflow.NewFunctionNode("suffix", suffixFn, workflow.NodeConfig{})

	// 開始 -> upper -> suffix の順に接続
	edges := workflow.Chain(workflow.Start, upper, suffix)

	wf, err := workflowagent.New(workflowagent.Config{
		Name:  "simple_sequence_workflow",
		Edges: edges,
	})
	if err != nil {
		panic(err)
	}

	// wfはただの agent.Agent インターフェースです
	// 既存のRunner、Launcher、Consoleでそのまま実行可能!
	fmt.Println("ワークフロー作成完了")
}

重要なポイント: wfagent.Agentインターフェースを実装しています。したがって、既存のRunner、Launcher、Consoleで特別な変更なしにそのまま実行できます。グラフ自体が1つのエージェントなのです。

ルーティング:条件分岐

エッジはルーティング条件を持つことができます。ノードがルーティング値を出力すると、一致するエッジが実行されます。この単純なアイデアで、あらゆる制御フローを表現できます。

b := workflow.NewEdgeBuilder()
b.AddRoutes(router, map[string]workflow.Node{
	"question":    answerNode,
	"statement":   commentNode,
	"exclamation": reactNode,
})
b.AddFanOut(planner, researchA, researchB, researchC) // 並列分岐
b.AddFanIn(join, researchA, researchB, researchC)    // 結果収集
  • 順次チェーン、条件付きルーター、Fan-out/Fan-in、ネストされたサブグラフ、さらにはループ(完了したノードを再実行可能)まで、すべてエッジとルートで実現します。
  • 標準ルートとしてStringRouteIntRouteBoolRouteMultiRouteDefault(その他すべて)を提供します。

LLMをルーターとして活用:最も有用なパターン

// ユーザーメッセージを分類するLLMエージェント
// -> 質問ならanswerNode、感嘆ならreactNode、発言ならcommentNodeにルーティング

モデルが決定を下し、グラフがその決定を**信頼性(Reliable)、観測可能性(Observable)、再開可能性(Resumable)**にします。このパターンはexamples/workflow/routing/llm/で確認できます。

動的ノード(Dynamic Node):実行順序をランタイムに決定

greeter := workflow.NewDynamicNode("greeter_workflow",
	func(nc agent.Context, in string, emit func(*session.Event) error) (string, error) {
		return workflow.RunNode[string](nc, greeterNode, in)
	},
	workflow.NodeConfig{},
)

ループ、条件分岐、動的リストに対するFan-outなどを、慣れ親しんだGoコードでオーケストレーションできます。WithRunIDWithUseSubBranchWithUseAsOutputWithIsolationScopeオプションで、子ノードのアイデンティティ、履歴の分離、出力委任を精密に制御できます。

Human-in-the-loop(HITL):人間の承認を待つワークフロー

event := workflow.NewRequestInputEvent(ctx, session.RequestInput{
	InterruptID:    "approve_refund",
	Message:        "200ドルの払い戻しを承認しますか? (yes/no)",
	ResponseSchema: schema,
})
// イベントを出力すると、ノードは "waiting" 状態に遷移

人間が後で応答すると、ワークフローが再開されます。実行状態はセッションに保存され、プロセス再起動後でも再開可能です。Python ADKと割り込み形式を共有しているため、異なるランタイム間でも再開が可能です。

リトライポリシー:組み込みの指数バックオフ

cfg := workflow.NodeConfig{
	RetryConfig: workflow.DefaultRetryConfig(),
}
// 5回試行、初回遅延1秒、最大60秒、2倍バックオフ、フルジッター

TimeoutWithMaxConcurrency(n)でグラフ全体の並行性を制限し、分離された並列ブランチを構成できます。スケジューラがgoroutine、チャネル、バックプレッシャー、キャンセルをすべて処理します。

Cloud infrastructure diagram showing ADK 2.0 agent orchestration with human-in-the-loop System Abstract Visual

LLMエージェントモードと統合されたランタイム

ADK 2.0はLLMエージェント向けのモード(ChatTaskSingleTurn)を導入しました。コーディネーターはユーザーとチャットしながら、サブエージェントは静かにTaskを完了したりSingle-shotで実行されます。各エージェントの役割に応じて、適切なヘルパーツール(finish_tasksingle_turntask)が自動的にインストールされます。

最大の変更点: Runnerが通常のLlmAgentをワークフローと同じノードランタイムで駆動するようになりました。結果として、単一エージェントアプリとフルグラフが1つの実行モデルを共有し、HITLが通常のLLMエージェントでも動作します(ワークフロー内部だけでなく)。

APIの簡素化:ToolContextとCallbackContextが1つに

  • ToolContextCallbackContextが廃止され、agent.Contextに統合されました。ツール、コールバック、ワークフローノードはすべてagent.Contextを直接受け取ります。
  • ノード/エージェントの実行が1つの一貫したテレメトリスパンツリーで表示され、グラフが何をしたかを正確に追跡できます。

マイグレーションガイド(核心のみ):

// 変更前: func(ctx agent.InvocationContext, in string) (string, error)
// 変更後: func(ctx agent.Context, in string) (string, error)

InvocationContextを埋め込んでいるため、既存のメソッドはすべて使用可能です。agent/context_mock.goStrictContextMockをテストダブルとして使用できます。

すぐに試す

go run ./examples/workflow/basic/
go run ./examples/workflow/routing/llm/   # LLM-as-router
go run ./examples/workflow/dynamic/hitl/ # 動的 + HITL
go run ./examples/workflow/hitl_rerun/   # 再エントリー再開HITL
go run ./examples/workflow/complex/      # 大規模マルチグラフ

Developer interacting with LLM agent router inside ADK 2.0 graph workflow Programming Illustration

日本開発コミュニティにおける適用コンテキスト

日本では、SIerや金融機関を中心に、長時間実行プロセス(Long-running Process)人間承認ワークフローが非常に重要なドメインです。

  • 例: 保険金請求処理、金融取引承認、ローン審査など。
  • ADK 2.0のHITLとdurable resume機能は、これらの要件に完全に合致します。特にプロセス再起動後も状態が維持される点は、金融系システムで極めて重要な要素です。
  • ただし、国内クラウド環境でGoランタイムを運用する際は、メモリプロファイリングgoroutineリークに常に注意が必要です。ADK 2.0のグラフスケジューラは多数のgoroutineを生成する可能性があるため、WithMaxConcurrencyで適切に制限することを推奨します。

この技術の限界または注意点

  • 学習曲線: グラフの概念自体は直感的ですが、複雑なワークフローを設計する際は、ノード間のデータフローを明確に定義する必要があります。特に動的ノードを乱用すると、かえってメンテナンスが難しくなります。
  • テストの複雑さ: グラフ全体を結合テストするよりも、個々のノード単位で集中テストする戦略が効果的です。session.Eventの新しいフィールド(IsolationScope、Output、Routesなど)に注意してください。
  • Python ADKとの差異: Go 2.0はPython 2.0と同じ方向性を共有しますが、Go固有の静的型システムとiter.Seq2を活用します。Pythonのように動的型変換を期待してはいけません。

次のステップとしての学習方向

  1. 公式サンプルの実行: 上記で紹介したgo run ./examples/workflow/コマンドで基本パターンを実際に実行してみてください。
  2. マイグレーションガイドの習得: ADK Go 2.0マイグレーションガイドを参照し、1.0コードを2.0に移行する練習をしてみてください。
  3. 複合パターンの設計: 実際の業務ドメインで1つのワークフローをグラフとして設計し、HITLが必要なポイントを特定してみてください。

合わせて読みたい記事

本コンテンツは、信頼性の高い情報源をもとにAIツールを活用して作成され、編集者によるレビューを経て公開されています。専門家によるアドバイスの代替となるものではありません。