エージェントのバックグラウンド応答

Microsoft Agent Framework では、完了に時間がかかる可能性がある実行時間の長い操作を処理するためのバックグラウンド応答がサポートされています。 この機能により、エージェントは要求の処理を開始し、結果のポーリングや中断されたストリームの再開に使用できる継続トークンを返すことができます。

Tip

完全な作業例については、 バックグラウンド応答のサンプルを参照してください。

バックグラウンド応答を使用するタイミング

バックグラウンド応答は、次の場合に特に役立ちます。

  • かなりの処理時間を必要とする複雑な推論タスク
  • ネットワークの問題またはクライアントのタイムアウトによって中断される可能性がある操作
  • 実行時間の長いタスクを開始し、後で結果を確認するシナリオ

バックグラウンド応答のしくみ

バックグラウンド応答では、 継続トークン メカニズムを使用して実行時間の長い操作を処理します。 バックグラウンド応答が有効になっているエージェントに要求を送信すると、次の 2 つのいずれかが発生します。

  1. 即時完了: エージェントはタスクをすばやく完了し、継続トークンなしで最終的な応答を返します
  2. バックグラウンド処理: エージェントはバックグラウンドで処理を開始し、最終的な結果ではなく継続トークンを返します

継続トークンには、非ストリーミング エージェント API を使用して完了をポーリングするか、ストリーミング エージェント API で中断されたストリームを再開するために必要なすべての情報が含まれます。 存在しないトークン (.NET内のnull、PythonのNone、または Go の空の文字列) は、操作が完了したか、失敗したか、ユーザー入力が必要な場合など、続行できないことを意味します。

バックグラウンド応答の有効化

バックグラウンド応答を有効にするには、AllowBackgroundResponsesで true プロパティをAgentRunOptionsに設定します。

using Microsoft.Agents.AI;

AgentRunOptions options = new()
{
    AllowBackgroundResponses = true
};

Note

現時点では、OpenAI Responses API を使用するエージェントのみがバックグラウンド応答をサポートしています: OpenAI Responses Agent と Azure OpenAI Responses Agent。

一部のエージェントでは、バックグラウンド応答を明示的に制御できない場合があります。 これらのエージェントは、 AllowBackgroundResponses 設定に関係なく、操作の複雑さに基づいてバックグラウンド応答を開始するかどうかを自律的に決定できます。

ストリーミング以外のバックグラウンド応答

ストリーミング以外のシナリオでは、最初にエージェントを実行するときに、継続トークンが返される場合と返されない場合があります。 継続トークンが返されない場合は、操作が完了したことを意味します。 継続トークンが返された場合は、エージェントがまだ処理中のバックグラウンド応答を開始しており、最終的な結果を取得するためにポーリングが必要であることを示します。

using System;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;

AIAgent agent = new AIProjectClient(
    new Uri("<your-foundry-project-endpoint>"),
    new DefaultAzureCredential())
    .AsAIAgent(
        model: "<deployment-name>",
        instructions: "You are a helpful assistant.");

AgentRunOptions options = new()
{
    AllowBackgroundResponses = true
};

AgentSession session = await agent.CreateSessionAsync();

// Get initial response - may return with or without a continuation token.
AgentResponse response = await agent.RunAsync(
    "Write a very long novel about otters in space.",
    session,
    options);

// Continue to poll until the final response is received
while (response.ContinuationToken is not null)
{
    // Wait before polling again.
    await Task.Delay(TimeSpan.FromSeconds(2));

    options.ContinuationToken = response.ContinuationToken;
    response = await agent.RunAsync(session, options);
}

Console.WriteLine(response.Text);

Warning

DefaultAzureCredential は開発には便利ですが、運用環境では慎重に考慮する必要があります。 運用環境では、待機時間の問題、意図しない資格情報のプローブ、フォールバック メカニズムによる潜在的なセキュリティ リスクを回避するために、特定の資格情報 ( ManagedIdentityCredential など) を使用することを検討してください。

重要なポイント:

  • 最初の呼び出しはすぐに完了するか (継続トークンなし)、バックグラウンド操作を開始する (継続トークンを使用)
  • 継続トークンが返されない場合、操作は完了し、応答には最終的な結果が含まれます
  • 継続トークンが返された場合、エージェントはポーリングを必要とするバックグラウンド プロセスを開始しました
  • 後続のポーリング呼び出しで前の応答の継続トークンを使用する
  • ContinuationTokenがnullされると、操作は完了します

ストリーミング バックグラウンド応答

ストリーミング シナリオでは、バックグラウンド応答は通常のストリーミング応答とよく似ています。エージェントは、すべての更新をリアルタイムでコンシューマーにストリーミングします。 ただし、主な違いは、元のストリームが中断された場合、エージェントは継続トークンによるストリームの再開をサポートすることです。 各更新には、現在の状態をキャプチャする継続トークンが含まれています。これにより、後続のストリーミング API 呼び出しにこのトークンを渡すことで、中断した場所からストリームを再開できます。

using System;
using Azure.AI.Projects;
using Azure.Identity;
using Microsoft.Agents.AI;

AIAgent agent = new AIProjectClient(
    new Uri("<your-foundry-project-endpoint>"),
    new DefaultAzureCredential())
    .AsAIAgent(
        model: "<deployment-name>",
        instructions: "You are a helpful assistant.");

AgentRunOptions options = new()
{
    AllowBackgroundResponses = true
};

AgentSession session = await agent.CreateSessionAsync();

AgentResponseUpdate? latestReceivedUpdate = null;

await foreach (var update in agent.RunStreamingAsync(
    "Write a very long novel about otters in space.",
    session,
    options))
{
    Console.Write(update.Text);

    latestReceivedUpdate = update;

    // Simulate an interruption
    break;
}

// Resume from interruption point captured by the continuation token
options.ContinuationToken = latestReceivedUpdate?.ContinuationToken;
await foreach (var update in agent.RunStreamingAsync(session, options))
{
    Console.Write(update.Text);
}

重要なポイント:

  • 各 AgentResponseUpdate には、再開に使用できる継続トークンが含まれています
  • 中断前に最後に受信した更新からの継続トークンを格納する
  • 保存された継続トークンを使用して中断ポイントからストリームを再開する

Tip

実行可能な完全な例については、 .NET サンプル を参照してください。

Tip

完全な作業例については、 バックグラウンド応答のサンプルを参照してください。

バックグラウンド応答の有効化

バックグラウンド応答を有効にするには、backgroundを呼び出すときに agent.run() オプションを渡します。

session = agent.create_session()
response = await agent.run(
    messages="Your prompt here",
    session=session,
    options={"background": True},
)

Note

OpenAI Responses API、Azure OpenAI Responses API、または Microsoft Foundry によってサポートされるPythonエージェントは、バックグラウンド応答をサポートします。

FoundryChatClientでは、バックグラウンド応答は、プロバイダー側の応答ストレージ (store=True) またはステートレス再生 (store=False) を使用してローカル関数ツールを実行できます。 再開された応答がローカル ツール ループを続行できるように、ポーリング中は同じセッションを保持します。

ストリーミング以外のバックグラウンド応答

ストリーミング以外のシナリオでは、最初に background=Trueを使用してエージェントを実行すると、 continuation_tokenですぐに返されることがあります。 continuation_tokenがNone場合、操作は完了しています。 それ以外の場合は、後続の呼び出しでトークンを渡してポーリングします。

import asyncio
from agent_framework import Agent
from agent_framework.openai import OpenAIChatClient

agent = Agent(
    name="researcher",
    instructions="You are a helpful research assistant.",
    client=OpenAIChatClient(model="o3"),
)

session = agent.create_session()

# Start a background run — returns immediately
response = await agent.run(
    messages="Briefly explain the theory of relativity in two sentences.",
    session=session,
    options={"background": True},
)

# Poll until the operation completes
while response.continuation_token is not None:
    await asyncio.sleep(2)
    response = await agent.run(
        session=session,
        options={"continuation_token": response.continuation_token},
    )

# Done — response.text contains the final result
print(response.text)

重要なポイント

  • 最初の呼び出しはすぐに完了するか (継続トークンなし)、バックグラウンド操作を開始する (継続トークンを使用)
  • 後続のポーリング呼び出しで前の応答の continuation_token を使用する
  • continuation_tokenがNoneされると、操作は完了します

ストリーミング バックグラウンド応答

ストリーミング シナリオでは、バックグラウンド応答は通常のストリーミングと同様に機能し、エージェントはリアルタイムで更新をストリームバックします。 主な違いは、各更新プログラムに continuation_tokenが含まれるので、接続が中断された場合にストリームの再開が有効になります。

session = agent.create_session()

# Start a streaming background run
last_token = None
stream = agent.run(
    messages="Briefly list three benefits of exercise.",
    stream=True,
    session=session,
    options={"background": True},
)

# Read chunks — each update carries a continuation_token
async for update in stream:
    last_token = update.continuation_token
    if update.text:
        print(update.text, end="", flush=True)
    # If interrupted (e.g., network issue), break and resume later

中断されたストリームの再開

ストリームが中断された場合は、最後の continuation_token を使用して中断したところから再開します。

if last_token is not None:
    stream = agent.run(
        stream=True,
        session=session,
        options={"continuation_token": last_token},
    )
    async for update in stream:
        if update.text:
            print(update.text, end="", flush=True)

重要なポイント

  • 各 AgentResponseUpdate には、再開のための continuation_token が含まれています
  • 中断前に最後に受信した更新プログラムのトークンを格納する
  • options={"continuation_token": token}経由で保存されたトークンを渡して再開する

バックグラウンド応答を有効にする

実行を開始するときに agent.AllowBackgroundResponses(true) を渡し、 agent.WithSession(session)を介して明示的なセッションを再利用します。 プロバイダーとデプロイでは、バックグラウンド実行をサポートする必要があります。オプションを設定しても、すべてのエージェントが非同期になるわけではありません。

ストリーミング以外のバックグラウンド応答

操作を開始し、 response.ContinuationToken が空でない間にポーリングします。 継続呼び出しには新しいメッセージを含めてはなりません。RunTextではなく、a.Run(ctx, nil, ...)を続行します。

保持されている Azure OpenAI Responses サンプルは、操作をタイムアウトに制限し、ポーリング間で待機し、取り消し時にすぐに停止します。

// Copyright (c) Microsoft. All rights reserved.

// This sample demonstrates long-running agent operations using the OpenAI
// Responses API "background" option. A background run returns quickly with a
// continuation token instead of the final answer; the caller then polls with
// that token until the operation completes.
//
// Ported from the Python getting-started "background responses" sample.
package main

import (
    "context"
    "time"

    "github.com/microsoft/agent-framework-go/agent"
    "github.com/microsoft/agent-framework-go/examples/internal/demo"
    "github.com/microsoft/agent-framework-go/provider/openaiprovider"
    "github.com/openai/openai-go/v3"
    "github.com/openai/openai-go/v3/azure"
    "github.com/openai/openai-go/v3/option"
)

var logger = demo.NewLogger(
    "Background Responses",
    "Starts a background Responses run and polls with the continuation token until it completes.",
    "Model", demo.Deployment,
)

func main() {
    // Bound the whole sample so a run that never completes (stuck queued, a bad
    // continuation token, etc.) cannot hang indefinitely.
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
    defer cancel()

    token := demo.AzureTokenCredential()

    client := openai.NewClient(
        option.WithBaseURL(demo.Endpoint),
        azure.WithTokenCredential(token),
    )

    researcher := openaiprovider.NewResponsesAgent(
        client,
        openaiprovider.AgentConfig{
            Model:        demo.Deployment,
            Instructions: "You are a helpful research assistant. Be concise.",
            Config: agent.Config{
                Name:        "Researcher",
                Middlewares: []agent.Middleware{logger},
            },
        },
    )

    // Background runs are tied to a session so that follow-up polls target the
    // same operation.
    session, err := researcher.CreateSession(ctx)
    if err != nil {
        demo.Panic(err)
    }

    // Start a background run. It returns quickly with a continuation token
    // rather than the final answer. (If the model or endpoint does not support
    // background execution, the run simply completes inline with no token.)
    resp, err := researcher.RunText(ctx,
        "Briefly explain the theory of relativity in two sentences.",
        agent.WithSession(session),
        agent.AllowBackgroundResponses(true),
    ).Collect()
    if err != nil {
        demo.Panic(err)
    }

    // Poll until the operation completes — i.e. until a run no longer returns a
    // continuation token. Continuation runs must not carry any messages, so use
    // Run with a nil message slice.
    for resp.ContinuationToken != "" {
        // Wait between polls, but stop promptly if the context is cancelled or
        // its deadline is reached rather than sleeping through it.
        select {
        case <-ctx.Done():
            demo.Panic(ctx.Err())
        case <-time.After(2 * time.Second):
        }
        resp, err = researcher.Run(ctx, nil,
            agent.WithSession(session),
            agent.WithContinuationToken(resp.ContinuationToken),
        ).Collect()
        if err != nil {
            demo.Panic(err)
        }
    }

    // The final response holds the completed result.
    demo.Response(resp, nil)
}

このサンプルを実行する前に、AZURE_OPENAI_ENDPOINTとAZURE_OPENAI_DEPLOYMENT_NAMEを設定し、Azure資格情報で認証します。 ポーリングが完了した後、完了した応答が出力されます。 すぐに完了した応答には継続トークンがないため、ポーリングは必要ありません。

バックグラウンド応答のストリーミング

agent.Stream(true)とagent.AllowBackgroundResponses(true)を使用してストリーミング実行を開始します。 受信した最後の更新からの継続トークンを保持します。 接続が中断された場合は、新しいメッセージなしで再開します。

for update, err := range a.Run(
    ctx,
    nil,
    agent.WithSession(session),
    agent.WithContinuationToken(lastToken),
    agent.Stream(true),
) {
    if err != nil {
        panic(err)
    }
    lastToken = update.ContinuationToken
    fmt.Print(update.String())
}

保存されたトークンが空でない場合にのみ再開します。 操作がプロセスの再起動後も存続する必要がある場合は、セッションとトークンの両方をアプリケーション ストレージに保持します。

Harness Agent でバックグラウンド応答を使用する

Harness エージェントは引き続き標準のエージェント フレームワーク エージェントであるため、プロバイダーのバックグラウンド応答では、上記と同じ実行ごとのオプションが使用されます。 ハーネス構築では、プロバイダーのバックグラウンド応答は自動的に有効になりません。.NETでAllowBackgroundResponsesを設定するか、Pythonでoptions={"background": True}するか、実行を開始するときに Go でagent.AllowBackgroundResponses(true)します。 操作がプロセスの再起動後も存続する必要がある場合は、セッションを保持し、継続トークンを保持します。

これは、1 つのプロバイダー要求を続行するのではなく、子エージェントに作業を委任する バックグラウンド エージェントとは別です。

ベスト プラクティス

バックグラウンド応答を使用する場合は、次のベスト プラクティスを検討してください。

  • サービスの負荷を抑えるために適切なポーリング間隔を実装する
  • 操作に予想以上の時間がかかっている場合は、ポーリング間隔に指数バックオフを使用する
  • 不在の継続トークン (null、 None、または空の Go 文字列) を確認して、処理が完了したタイミングを確認します
  • ユーザー セッションにまたがる可能性がある操作のために継続トークンを永続的に格納することを検討する

制限と考慮事項

  • バックグラウンド応答は、実行時間の長い操作をサポートする基になる AI サービスに依存します
  • すべてのエージェントの種類がバックグラウンド応答をサポートしているわけではありません
  • ネットワークの中断またはクライアントの再起動には、継続トークンを保持するための特別な処理が必要になる場合があります

次のステップ

RAG