---
title: "ADK 2.0とは？Workflowのグラフ実行と1.xからの移行手順【Python版ADK 2.11対応】"
url: "https://www.issoh.co.jp/tech/details/12402/"
published: 2026-06-08
updated: 2026-10-12
categories: ["AIエージェント・MCP"]
publisher: "株式会社一創"
---

# ADK 2.0とは？Workflowのグラフ実行と1.xからの移行手順【Python版ADK 2.11対応】

ADK 2.0は、GoogleのAIエージェント開発フレームワーク「Agent Development Kit」のメジャー版です。Python版は2026年5月19日にGAとなり、処理の順番をノードとエッジのグラフで書く`Workflow`が加わりました。この記事では、2.0で何が変わったか、Workflowで直列・分岐・並列・ループ・人の入力待ちをどう書くか、1.xのコードを上げるときにどこが壊れるかを、Python版2.11.0で実行したコードとあわせて説明します。

ADKそのものの位置づけや、インストールから`adk web`で動かすまでの手順は[ADKとは？GoogleのAgent Development Kitの仕組みと始め方](/tech/details/7936/)で扱っています。

## まとめ：ADK 2.0の要点

- 2.0の中心は`Workflow`。エージェント・関数・ツールをノードとして並べ、エッジで実行順と分岐を決める。モデルに手順を任せず、コードで経路を固定できる
- 2.0はPython・TypeScript・Goで提供。最新版はPython 2.11.0、TypeScript 2.2.1、Go v2.5.0（2026年10月9日時点）。JavaとKotlinは1.x系のまま
- SequentialAgent・ParallelAgent・LoopAgentは2.0.0から非推奨。まだ動くが、新しい処理フローはWorkflowで書く
- Eventに`node_info`と`output`が増えた。列を固定した独自のセッションDBはスキーマ変更が要り、2.0が書いたセッションは1.28より前の1.xでは読めない
- 移行の要点は、セッションDBの確認、イベントの直接追加の廃止、広すぎる`except`の削除、実行ロジックのコールバックへの移動の4つ

以下はPython 3.11とgoogle-adk 2.11.0で検証した処理の抜粋です。検証ではモデルをスタブに差し替え、Runnerとセッション作成処理を追加しています。入力待ちの例は、停止イベントから取得した呼び出しIDを使い、同じセッションで再開します。

## ADK 2.0で変わったことと言語別の提供状況

公式の2.0ガイドは、2.0を「階層型のエージェント実行から、グラフベースの実行エンジンへの移行」と説明しています。1.xでは親エージェントが子エージェントを呼ぶ木構造で処理が進みましたが、2.0ではエージェントもツールも関数も、グラフ上の1つのノードとして評価されます。2.0.0のREADMEは新機能を2つ挙げています。

- **Workflow Runtime**：分岐、並列実行と合流、ループ、再試行、状態管理、動的ノード、人の入力待ち、入れ子のワークフローを扱うグラフ実行エンジン
- **Task API**：エージェント間の委任を構造化する仕組み。`LlmAgent`の`mode`に`chat`・`task`・`single_turn`を指定し、ユーザーと対話するか、1回で結果だけ返すかを決める

2.0の提供時期は言語ごとに異なります。各レジストリで確認した版は次のとおりです。

| 言語         | 2.0.0のGA   | 最新版    | 最新版の公開日    |
| ---------- | ---------- | ------ | ---------- |
| Python     | 2026-05-19 | 2.11.0 | 2026-10-02 |
| Go         | 2026-06-30 | v2.5.0 | 2026-09-30 |
| TypeScript | 2026-08-21 | 2.2.1  | 2026-10-06 |
| Java       | 未提供        | 1.11.0 | 2026-10-02 |
| Kotlin     | 未提供        | 1.3.1  | 2026-10-05 |

Pythonは2週間ほどの間隔でマイナー版が出ており、5月の2.0.0から10月の2.11.0までにマイナー版が11回上がっています。Goはモジュールパスが`google.golang.org/adk/v2`に変わったため、import文の書き換えが必要です。Javaの1.11.0を使っている場合、2.0の機能はまだ使えません。

## Workflowの書き方：ノード・エッジ・ルート

`Workflow`は`edges`にタプルを並べてグラフを作ります。開始経路のタプルは`"START"`から書き、途中の接続は接続元ノードから書きます。単純な直列タプルでは、続くノードが左から順に実行されます。ノードには`Agent`（`LlmAgent`）、普通のPython関数、ツール、別の`Workflow`を置けます。関数はADKが自動で`FunctionNode`に包みます。

### 直列ノード間の出力と入力の受け渡し

前のノードが返した値は、次のノードの`node_input`引数に入ります。セッション状態へ書き込まなくても値が流れる点が、1.xの`output_key`を使った受け渡しとの違いです。

```
from google.adk import Agent, Event, Workflow

summarizer = Agent(
    name="summarizer",
    model="gemini-3.5-flash",
    instruction="ユーザーの問い合わせを1文で要約する",
)

def add_ticket_no(node_input: str):
    return f"[#1024] {node_input}"

def notify(node_input: str):
    return Event(message=f"起票しました: {node_input}")

root_agent = Workflow(
    name="ticket_flow",
    edges=[("START", summarizer, add_ticket_no, notify)],
)
```

実行すると、イベントの`node_info.path`が`ticket_flow@1/summarizer@1`、`ticket_flow@1/add_ticket_no@1`の順に並びます。関数が普通の値を返すとイベントの`output`に入り、`Event(message=...)`を返すとユーザーに見えるメッセージになります。

### 条件分岐：Event(route=…)と辞書のルーティング

分岐させるノードは`Event(route=...)`を返します。次の要素に「ルート値→行き先ノード」の辞書を置くと、一致したノードだけが動きます。ルート値をリストで返すと、一致した行き先がすべて実行されます。

```
from google.adk import Event, Workflow
from google.adk.workflow import DEFAULT_ROUTE

def classify(node_input: str):
    labels = [x.strip() for x in node_input.split(",")]
    return Event(route=labels)

def handle_bug():
    return Event(message="開発チームへ回す")

def handle_billing():
    return Event(message="経理チームへ回す")

def handle_other():
    return Event(message="一次窓口で対応する")

root_agent = Workflow(
    name="router",
    edges=[
        ("START", classify, {
            "BUG": handle_bug,
            "BILLING": handle_billing,
            DEFAULT_ROUTE: handle_other,
        }),
    ],
)
```

入力が`BUG`なら`handle_bug`だけ、`BUG,BILLING`なら2つとも、どれにも当たらない`SPAM`なら`DEFAULT_ROUTE`の`handle_other`が動きました。実務では`classify`の前に分類用の`Agent`を置き、モデルの判定結果をルート値に変換します。判定はモデルに任せ、どこへ進むかはコードで決める分担です。

### 並列と合流：JoinNode

`"START"`から複数のノードへ枝を出すと並列に動きます。結果を1つにまとめるには`JoinNode`を挟みます。`JoinNode`は上流のノードがすべて終わるまで待ち、ノード名をキーにした辞書を次へ渡します。

```
from google.adk import Event, Workflow
from google.adk.workflow import JoinNode

def search_docs():
    return "docs: 3件"

def search_tickets():
    return "tickets: 5件"

def report(node_input: dict):
    return Event(message=f"{node_input['search_docs']} / {node_input['search_tickets']}")

join = JoinNode(name="join")
root_agent = Workflow(
    name="research",
    edges=[
        ("START", search_docs, join),
        ("START", search_tickets, join),
        (join, report),
    ],
)
```

`join`の出力は`{'search_docs': 'docs: 3件', 'search_tickets': 'tickets: 5件'}`でした。上流の関数が`None`を返した場合も、2.11.0の`JoinNode`は止まらず、その枝の値を`None`として後続へ渡しました。欠けた値の扱いは後続ノード側で決めておく必要があります。外部APIを呼ぶ枝の一時的な失敗には、後述の`RetryConfig`が使えます。ただし更新系のAPIは、再実行で二重登録が起きないよう、冪等キーや処理済みの判定と組み合わせてください。同時に動くノード数の上限は`Workflow`の`max_concurrency`で決められます。

### ループの条件付きエッジと終了条件

ループは、後ろのノードから前のノードへ戻るエッジで表します。ルートの付かないエッジだけで輪を作ると、2.11.0では`Unconditional cycle detected`の検証エラーになり、`Workflow`を作る時点で止まります。循環経路には少なくとも1つ、ルート条件付きのエッジを含めます。この例では戻るエッジに条件を付けています。

```
from google.adk import Event, Workflow

def write_draft(ctx, node_input):
    n = ctx.state.get("rounds", 0) + 1
    ctx.state["rounds"] = n
    return f"draft v{n}"

def review(ctx, node_input: str):
    if ctx.state["rounds"] >= 3:
        return Event(route="ok", output=node_input)
    return Event(route="retry", output=node_input)

def publish(node_input: str):
    return Event(message=f"公開: {node_input}")

root_agent = Workflow(
    name="draft_loop",
    edges=[
        ("START", write_draft, review),
        (review, {"retry": write_draft, "ok": publish}),
    ],
)
```

実行すると`write_draft@1`から`@3`まで3周し、`publish`で終わりました。パスの`@`の後ろの数字は、そのノードの実行回数を表します。この例は新規セッションを前提とし、同じセッションで再度実行すると状態の`rounds`が残るため、毎回3周させる場合は開始時に初期化します。ルート付きの輪は検証を通りますが、回数の上限は自動では付きません。この例のように状態で回数を数え、必ず抜ける条件を書いてください。

### RequestInputによる入力待ちと再開

ノードが`RequestInput`を返すと、ワークフローはそこで止まり、`adk_request_input`という名前の関数呼び出しイベントを出します。モデルを使わずに止まるので、承認や金額入力のような人の判断を確実に挟めます。

```
from google.adk import Event, Workflow
from google.adk.events import RequestInput
from google.genai import types

def ask_amount():
    return RequestInput(message="返金額を入力してください")

def confirm(node_input):
    return Event(message=f"{node_input}円の返金を登録しました")

root_agent = Workflow(name="refund", edges=[("START", ask_amount, confirm)])

# 再開：止まったときの関数呼び出しIDに対して応答を送る
reply = types.Content(role="user", parts=[types.Part(
    function_response=types.FunctionResponse(
        name="adk_request_input", id=call_id, response={"result": 3000},
    )
)])
```

`call_id`には、止まったときのイベントに含まれる`function_call.id`を入れます。同じセッションに`reply`を送ると、`confirm`が`3000円の返金を登録しました`を返しました。`RequestInput`には`payload`と`response_schema`も渡せますが、`response_schema`は返答を変換してくれません。決まった形で受け取りたい場合は、画面側で入力形式を固定するか、後ろに整形用のエージェントを置きます。

ツールの実行前に確認を挟む`require_confirmation`や、セッションを止めて後で再開する`ResumabilityConfig`は、[ADKのコンテキスト圧縮と停止・再開の使い方](/tech/details/9292/)で扱っています。

### 失敗時の再試行：RetryConfig

2.0ではノードで起きた例外をフレームワークが受け取り、`RetryConfig`に従って同じノードを再実行します。

```
from google.adk import Workflow
from google.adk.workflow import FunctionNode, RetryConfig

attempts = {"n": 0}

def call_external_api():
    attempts["n"] += 1
    if attempts["n"] < 3:
        raise ConnectionError("timeout")
    return "200 OK"

fetch = FunctionNode(
    func=call_external_api,
    retry_config=RetryConfig(max_attempts=3, initial_delay=0.5, jitter=0.0),
)
root_agent = Workflow(name="sync", edges=[("START", fetch)])
```

2回失敗して3回目に成功する関数で試すと、3回目の結果が`output`に入りました。ソースに書かれた既定値は、試行回数5回、初回待機1.0秒、最大待機60秒、待機時間の倍率2.0です。失敗のたびに`retry count is not persisted across resuming`という警告が出ます。人の入力待ちで止めて再開すると、試行回数は0から数え直しになります。

## グラフ型・動的ワークフロー・従来のワークフローエージェントの使い分け

公式ドキュメントは、複数ステップの処理の組み方を3通りに分けています。

| 方式       | 書き方                       | 向く処理            |
| -------- | ------------------------- | --------------- |
| グラフ型     | edgesにノードとルートを書く          | 手順が決まった業務フロー    |
| 動的ワークフロー | @nodeの関数内でctx.run\_node() | 回数や分岐がデータで変わる処理 |
| 従来の3クラス  | SequentialAgentなど         | 既存コードの維持        |

新しく書くなら、まずグラフ型を選びます。ループの回数や枝の数が実行時のデータで決まり、グラフに書き切れないときだけ動的ワークフローにします。動的ワークフローでは、普通のPythonの`for`や`if`の中で`ctx.run_node()`を使って子ノードを呼びます。子ノードを呼ぶ関数には`@node(rerun_on_resume=True)`を付けます。人の入力待ちから再開したとき関数が頭から再実行され、終わっている子ノードは記録済みの結果で飛ばされます。

SequentialAgent・ParallelAgent・LoopAgentは、2.0.0で非推奨のデコレーターが付き、インスタンスを作ると`DeprecationWarning`が出ます。2.11.0の警告文には「WorkflowはまだLlmAgentのサブエージェントとして使えない」と添えられています。親の`LlmAgent`が`sub_agents`で処理フローへ処理を渡す構成は、今のところ従来クラスのまま残すしかありません。それ以外の構成は、新規のコードからWorkflowに寄せるべきです。

リモートのエージェントをA2Aで呼ぶ構成は[A2Aリモートエージェントとは？ADKでの公開・呼び出し](/tech/details/6732/)、A2Aの仕様そのものは[A2A（Agent2Agent）プロトコルとは？](/tech/details/6732/)で解説しています。

## ADK 1.xから2.0への破壊的変更

[公式の2.0移行ガイド](https://adk.dev/2.0/)は「2.0は1.xのエージェントと互換を保つよう設計した」としつつ、次の非互換を挙げています。影響が大きい順に並べます。

### Eventスキーマとセッションの読み書き

2.0ではEventに`node_info`（どのノードが出したか）と`output`（ノードの出力値）が加わりました。2.0.0のREADMEによると、2.0が書いたセッションは1.28以降の1.xなら読めますが（増えた項目は無視される）、それより古い1.xとは互換がありません。

影響を受けるのは、`BaseSessionService`を自作し、イベントを列ごとにSQLやNoSQLへ保存している場合です。2.0のイベントを1.xの表にそのまま入れると、挿入やデシリアライズで失敗します。イベントをJSONの塊で保存していれば、スキーマ変更は要りません。API Gatewayやフロントエンドで`additionalProperties: false`の厳格なJSONスキーマ検証をしている場合も、検証側の定義を先に直します。

### BaseAgentがBaseNodeを継承する変更とカスタムエージェント

2.0では`BaseAgent`が`BaseNode`を継承し、エージェントもグラフのノードとして扱われます。公式ガイドは「`_run_async_impl()`や`generate_content()`の上書きは正しい実行方法ではなくなり、Workflowエンジンはこれを迂回する。上書きで入れたテレメトリや状態管理は黙って無視される」と警告し、`BeforeAgentCallback`と`AfterAgentCallback`への移行を求めています。

ただし、google-adk 2.0.0と2.11.0で`BaseAgent`と`LlmAgent`を継承して`_run_async_impl()`を上書きし、単体と`Workflow`の中の両方で実行したところ、4通りとも上書きしたメソッドが呼ばれました。2.11.0のソースでも、ノードとしての実行（`_run_impl`）は`run_async`を経由して`_run_async_impl`を呼んでいます。現行版で既存のカスタムエージェントがすぐ止まるわけではありません。とはいえ公式が非推奨の経路と明言している以上、ログ出力や状態の書き込みはコールバックへ移し、`_run_async_impl`の上書きは、エージェントの動き自体を変える必要がある場合に限るのが安全です。

### イベントの直接追加の禁止

1.xでは`context.session.events.append(custom_event)`でセッションにイベントを足すコードが書けました。2.0ではWorkflowエンジンがイベントの保存・ルーティング・ストリーミングを管理するため、直接の追加はエンジンを迂回し、実行の再現性を壊します。`enqueue_event`の直接呼び出しも同じ扱いです。ノードやエージェントの中で`yield`して返す形に書き換えます。

### 広すぎるexceptによる再試行と入力待ちへの影響

1.xにはフレームワークの自動再試行がなかったため、ツールの中を`try`と`except Exception:`で囲んで握りつぶすコードがよくありました。2.0でこれを残すと、例外がフレームワークに届かず、`RetryConfig`の再試行が効きません。

`except BaseException:`はさらに危険です。ワークフローを止めるための`NodeInterruptedError`は`BaseException`の子クラスなので、これを捕まえると人の入力待ちで止まれなくなります。捕まえる場合は必ず再送出してください。

### 依存パッケージとPythonの要件

PyPIのメタデータで比べると、必須の依存パッケージは1.34.0の45個から2.0.0では23個に減り、`mcp`は必須から外れました。MCPのツールを使うコードは`pip install "google-adk[mcp]"`で入れないと動きません。A2Aは`[a2a]`、LiteLLM経由で他社モデルを使う機能は`[extensions]`で入れます。2.0.0ではREADMEがPython 3.11以上、パッケージのメタデータが3.10以上と不一致でしたが、2.11.0では配布パッケージのREADMEとメタデータがともに3.10以上です。一方、GitHubのmainブランチのREADMEは3.11以上を掲げており、今後の版で引き上げられる可能性があります。新しく環境を作るなら3.11以上が無難です。

## 1.xから2.xへの移行手順

移行は「影響箇所の洗い出し→検証環境での実行→セッションDBの切り替え」の順で進めます。2.0が書いたセッションは古い1.xで読めないため、本番のDBへ書き込む前に戻せる状態を作っておくことが要になります。

1. すぐ上げない環境は`pip install "google-adk~=1.0"`のように1.x系へ固定し、意図しない2.x化を防ぐ
2. リポジトリを検索し、2.0で扱いが変わる書き方の場所を一覧にする（下のコマンド）
3. 独自のセッションサービスがあれば、`node_info`と`output`を保存できるようにスキーマを直す。読み手の1.xアプリは先に1.28以上へ上げる
4. 検証環境で2.11.0を入れ、MCP・A2A・LiteLLMを使う場合は対応するextrasも指定する
5. 1.xの会話ログを再生し、最終応答、ツール呼び出しの順番、セッション状態のキーが一致するかを比べる
6. 新しく書く処理フローからWorkflowを使い、SequentialAgentなどは動作確認が取れた範囲で置き換える

```
grep -rnE "_run_async_impl|generate_content\(|session\.events\.append|enqueue_event|except (BaseException|Exception)|SequentialAgent|ParallelAgent|LoopAgent|McpToolset" --include="*.py" .
```

見つかった箇所のうち、`session.events.append`と`except BaseException`は2.0の機能を直接壊すので必ず直します。`_run_async_impl`の上書きと従来の3クラスは2.11.0でも動くため、後回しにできます。`McpToolset`が見つかったら、extrasの指定漏れがないかを確認します。

## 2.xへ上げるべき場面と1.xに留める場面

2.xへ上げる価値が大きいのは、処理の順番をプロンプトに書いていて、モデルが手順を飛ばしたり分岐を間違えたりしている場合です。分類はモデル、行き先の決定はコード、という分担をWorkflowで書けば、どの経路を通ったかが`node_info.path`に残り、障害時の追跡も楽になります。承認待ちを挟む業務フローや、外部APIの失敗を再試行したい処理も、2.0の機能をそのまま使えます。

反対に、1つのエージェントがツールを数個呼ぶだけの構成なら、急いで上げる理由は薄いです。次の条件に当てはまる場合は1.xに固定したまま、計画を立ててから移行してください。

- 独自のセッションDBを列ごとの表で持ち、1.28より前の1.xアプリが同じDBを読んでいる
- Javaで開発している（2026年10月時点でJava版は1.11.0で、2.0の機能は無い）
- 親のLlmAgentが`sub_agents`で処理フローへ処理を渡す構成が中心（Workflowをサブエージェントにできない）

Gemini APIを直接呼ぶSDK側の移行（`google-generativeai`から`google-genai`へ）は、ADKとは別の話です。[Google Gen AI SDKとは？google-genaiの導入とAgent Platform切替・旧SDK移行](/tech/details/8438/)で整理しています。

## よくある質問

### ADK 2.0はいつリリースされましたか？

Python版の2.0.0は2026年5月19日、Go版は2026年6月30日、TypeScript版は2026年8月21日にGAとなりました。2026年10月9日時点の最新版はPython 2.11.0、Go v2.5.0、TypeScript 2.2.1です。

### ADK 2.0でSequentialAgentはまだ使えますか？

使えます。2.0.0から非推奨となり、インスタンスを作ると警告が出ますが、2.11.0でも動作します。将来の版で削除予定のため、新しい処理フローはWorkflowで書いてください。

### ADK 1.xのセッションDBのまま2.0を使えますか？

イベントをJSONの塊で保存している場合はそのまま使えます。列ごとの表で保存している場合は、`node_info`と`output`を受け取れるようにスキーマを直す必要があります。2.0が書いたセッションは1.28より前の1.xでは読めません。

### ADKのWorkflowで条件分岐やループは書けますか？

書けます。分岐は`Event(route=...)`を返すノードとルート値の辞書で、ループはルート付きのエッジで前のノードへ戻して表します。ルートの付かないエッジだけの輪は検証エラーになります。

### ADK 2.0はJavaでも使えますか？

2026年10月時点では使えません。Java版の最新は1.11.0で、公式の2.0ガイドが対象に挙げているのはPython・TypeScript・Goの3言語です。

## 関連記事

- [ADKとは？GoogleのAgent Development Kitの仕組みと始め方【2.11対応】](/tech/details/7936/)
- [ADKのコンテキスト圧縮と停止・再開の使い方｜EventsCompactionConfig・ResumabilityConfig・HITL【2.11対応】](/tech/details/9292/)
- [A2Aリモートエージェントとは？ADKでの公開・呼び出しとCloud Run・Agent Runtimeへのデプロイ【ADK 2.11対応】](/tech/details/6732/)
- [A2A（Agent2Agent）プロトコルとは？v1.0の仕組み・MCPとの違い・実装手順](/tech/details/6732/)
- [Google Gen AI SDKとは？google-genaiの導入とAgent Platform切替・旧SDK移行【2026年10月】](/tech/details/8438/)

---

出典: [ADK 2.0とは？Workflowのグラフ実行と1.xからの移行手順【Python版ADK 2.11対応】](<https://www.issoh.co.jp/tech/details/12402/>)（株式会社一創）
