Python の yield を理解する — return との違いから、AI エージェントへのストリーミング入力パターンまで

Python の yield を理解する — return との違いから、AI エージェントへのストリーミング入力パターンまで

Python の yield はジェネレーターを通じて関数の状態を保持したまま値を返す仕組みです。この記事では return との違いを整理し、async generator を使って実行中の AI エージェントに動的にメッセージを注入するストリーミング入力パターンを解説します。
2026.07.12

はじめに

AI エージェントを構築していると、こんな疑問に出会いませんか?

「エージェントが動いている最中に、新しい情報を差し込みたい。でも、毎回セッションを作り直すのは非効率すぎる……」

この疑問を掘り下げていくと、Python の yield という仕組みに行き着きました。一見シンプルなキーワードですが、理解すると AI エージェントのアーキテクチャ設計に直結する重要な概念でした。この記事では、yield の基本から、エージェントへの「ストリーミング入力」パターンまでを整理します。

return と yield — 制御フローの根本的な違い

return は「即座に退場」

return は関数の最終出力を返し、その場で関数を終了します。関数内のローカル変数はすべて破棄され、次に呼び出すときは最初の行からやり直しです。

def get_greeting():
    name = "Alice"
    return f"Hello, {name}"
    # ここで関数は完全に終了。name も破棄される

result = get_greeting()  # 毎回最初から実行

yield は「一時停止して再開」

yield を使うと、関数はジェネレーターに変わります。値を返しつつ、関数の状態(変数、実行位置)をすべて保持したまま一時停止します。次に値を要求されると、停止した行から実行を再開します。

def count_up():
    n = 1
    while True:
        yield n       # n を返して一時停止
        n += 1        # 再開時、ここから続く

counter = count_up()
print(next(counter))  # 1
print(next(counter))  # 2(n=1 の状態を覚えている)
print(next(counter))  # 3

python-yield-async-generator-agent-streaming-input-return-vs-yield

比較まとめ

特性 return yield
関数の終了 即座に終了 一時停止(再開可能)
状態の保持 破棄される 保持される
呼び出しモデル 毎回最初から実行 前回の停止位置から再開
例えるなら 手紙を送る(1通完結) 電話をかける(回線を維持)

エージェントへのストリーミング入力 — async iterator パターン

yield の「状態を保持したまま値を逐次的に返す」という特性は、長時間動作する AI エージェントへの入力パターンに直結します。

問題: 単発リクエストの限界

通常の API 呼び出しでは、リクエスト時点ですべての入力が揃っている必要があります。

# 従来: すべての入力を最初に渡す
response = await client.query(prompt="このデータを分析して")

しかし、長時間動くエージェントでは、実行途中で新しい情報を差し込みたい場面があります。毎回セッションを作り直すと、過去の会話履歴をすべて再送する必要があり、非効率です。

解決: 非同期ジェネレーターによるストリーミング入力

Python の async ジェネレーターをプロンプトとして渡すと、SDK はセッションを「オープン」な状態に保ち、後から届くメッセージを動的に流し込めます。

import asyncio

async def message_streamer():
    # 最初の指示を送信
    yield "エージェントを起動します。S3 のログを分析してください。"

    # 外部キュー(SQS など)からイベントを待機して随時注入
    while True:
        event = await queue.get()
        yield event.message
        if event.is_last:
            break

# SDK にイテレーターを渡す — セッションが維持される(疑似コード)
response = await client.query(prompt=message_streamer())

ポイントは、yield のおかげで関数が終了せず、キューにイベントが届くまで「待機状態」になることです。イベントが届けば再開し、新しいメッセージをエージェントに送り込みます。

python-yield-async-generator-agent-streaming-input-dataflow

streaming=True との違いに注意

多くの SDK で見かける streaming=True は、出力(レスポンス) をストリーミングするための設定です。一方、async iterator をプロンプトに渡すのは 入力 をストリーミングする仕組みです。方向が逆なので混同しないよう注意が必要です。

設定 方向 用途
streaming=True 出力のストリーミング レスポンスをトークン単位で逐次受信
async iterator をプロンプトに渡す 入力のストリーミング 実行中のエージェントに動的にメッセージを注入

ユースケース — いつストリーミング入力が必要か

1. Human-in-the-Loop(HITL)

エージェントが「このファイルを削除してもよいですか?」と確認を求める場面で、セッションを維持したまま人間の応答を待てます。

async def hitl_streamer():
    yield "本番データベースのクリーンアップを開始してください。"

    # エージェントが確認を求めてきたら、UI から人間の回答を受け取る
    approval = await wait_for_human_approval()
    yield f"承認: {approval}"

エージェントを「ウォーム」な状態に保ったまま、人間の判断を挟めるのがメリットです。

2. リアルタイム外部イベントの注入

サポートボットが顧客の問題を分析している最中に、「顧客がプランをアップグレードした」というシステムイベントを差し込むケースです。

async def event_injector():
    yield "顧客 #12345 のサポートチケットを分析してください。"

    async for event in system_event_stream:
        yield f"[SYSTEM EVENT: {event.description}]"

エージェントはイベントを受け取り、回答の方向性をリアルタイムに調整できます。

3. 段階的な指示の追加

100ページのドキュメントを分析中に「やっぱりこの観点も追加して」と指示を差し込むケースです。セッションを作り直す必要がないため、それまでの分析コンテキストが維持されます。

Node.js での実装 — async function*

Python だけでなく、Node.js でも同じパターンが使えます。構文は async function*(アスタリスク付き)と yield です。

// Node.js の非同期ジェネレーター
async function* messageStreamer() {
    yield "Initializing agent...";

    while (true) {
        const event = await waitForNextEvent();
        yield event.text;

        if (event.type === 'close') break;
    }
}

// SDK にイテレーターを渡す(疑似コード)
const client = new AgentClient();
await client.query({ prompt: messageStreamer() });

Python vs Node.js 比較

項目 Python Node.js
構文 async def func(): + yield async function* func() + yield
消費方法 async for item in gen: for await (const item of gen)
ジェネレーター判定 yield の有無で自動判定 function* のアスタリスクで明示
エコシステム asyncio ベース EventEmitter / Promise ベース

Node.js では関数宣言に * を付ける必要がある点が Python と異なりますが、概念は同一です。どちらの言語でも「関数を終了させず、状態を保持したまま値を逐次的に送り出す」という動作は変わりません。

まとめ

  • yield は関数を終了させずに値を返す。 return が「手紙」なら、yield は「電話」。回線を維持したまま情報をやり取りできる
  • 長時間エージェントへのストリーミング入力に直結。 async iterator をプロンプトに渡すことで、実行中のエージェントに動的にメッセージを注入できる
  • HITL、外部イベント注入、段階的指示追加など、実務で頻出するパターンを実現。 セッション再作成のオーバーヘッドを回避できる
  • Python と Node.js で同じ概念が使える。 構文の差異はあるが、設計思想は共通

yield は単なる言語機能ではなく、AI エージェントの設計パターンを支える基盤です。長時間動作するエージェントを構築する際は、ストリーミング入力パターンを検討してみてください。

この記事をシェアする

関連記事