Azure AI Agent Service でストリーム出力を行う (Python)

Azure OpenAI Service では、API からのレスポンスを完全な回答（Completion）が生成される前に、逐次ストリームでトークンを取得する方法がよく使われます。しかし、2025年2月5日時点で、Azure AI Agent Service における同様の方法について記載されたドキュメントが見当たりませんでした。そこで、ここにメモとして残しておきます。

```py
# Azure AI Foundry Project のクライアントを作成する
project = AIProjectClient.from_connection_string(
    conn_str=os.environ["PROJECT_CONNECTION_STRING"],
    credential=DefaultAzureCredential(),
)

# Azure AI Foundry の接続リソースの ID を名前から取得する関数を定義
def get_connection_id(connection_name: str) -> str:
    connections = [c for c in project.connections.list() if c.name == connection_name]
    assert len(connections) == 1, f"Connection {connection_name} not found"
    return connections[0].id

# Azure AI Agent Service の使用するツールを定義 (今回は Bing Search のツールを利用)
toolset = ToolSet()
toolset.add(BingGroundingTool(connection_id=get_connection_id("Bing_Search_API")))

# エージェントを作成
agent = project.agents.create_agent(
    model="gpt-4o",
    name="sample_agent",
    instructions="Answer questions using Bing search engine.",
    toolset=toolset,
    headers={"x-ms-enable-preview": "true"},
)

# スレッドを作成
thread = project.agents.create_thread()

# メッセージを作成
project.agents.create_message(
    thread_id=thread.id,
    role="user",
    content="梅原大吾について詳しく教えてください。ちゃんとBing検索で調べて回答してください",
)

# ここでメッセージの処理を開始 (ストリーム形式で取得する方式とする)
stream = project.agents.create_stream(assistant_id=agent.id, thread_id=thread.id)

# ストリームで返ってくるレスポンスを処理する
for event_type, data, func_rt in stream.event_handler:
    if event_type == "thread.message.delta":
        for c in data.delta.content:
            print(c.text.value, end="")
    elif event_type == "thread.message.completed":
        for c in data.content:
            print(c.text.annotations)
```

ストリーム形式で取得するためのキーポイントは、メッセージ処理を開始するところを ```create_and_process_run```関数ではなく、```create_stream```関数を使用すること、またそのレスポンスを```stream.event_handler```のイテレータとして処理すること、でしょうかね。  

```stream.event_handler``` の中身は"イベントタイプ", "生成結果", "イベント関数の返却([EventFunctionReturn](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/azure/ai/projects/models/_patch.py#L1398))" です。  
どんなイベントタイプがあるか？については、[ここ](https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/ai/azure-ai-projects/azure/ai/projects/models/_enums.py#L50)を見ればわかります。大体以下の様な流れで動いています。
```
thread.run.created
thread.run.queued
thread.run.in_progress
thread.run.step.created
thread.run.step.in_progress
thread.run.step.delta
thread.run.step.completed
thread.run.step.created
thread.run.step.in_progress
thread.run.step.delta
thread.run.step.completed
thread.run.step.created
thread.run.step.in_progress
thread.message.created
thread.message.in_progress
thread.message.delta
thread.message.delta
thread.message.delta
...
thread.message.delta
thread.message.completed
thread.run.step.completed
```

回答結果をストリームで UI に表示するだけであれば、イベントタイプ"thread.message.delta" の内容を使用するだけで良いかと思います。  
また、Bing Search ツール等の外部情報を基に回答している場合、参照情報(```annotations```)を完了時に返してくれます。フロントエンド処理で引用情報へのリンクを張るような処理を行うと良いかと思います。


> Azure AI Agent Service での Bing Search ツールの使用方法はこれをご参考に：
> [Azure AI Agent Service の Bing 検索を使用してグラウンディングを使用する方法 - Azure OpenAI | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/ai-services/agents/how-to/tools/bing-grounding?tabs=python&pivots=code-example)


```json
{
  "type": "thread.message.completed",
  "data": {
    "id": "msg_mfXKkCE3jt5kjCyq0VQasOZ7",
    "object": "thread.message",
    "created_at": 1738738857,
    "assistant_id": "asst_ZlNvEquX09coN0vO9YG8zw0M",
    "thread_id": "thread_a6y4onDh2hiZJGgUtXDdSIV3",
    "run_id": "run_s068Pgb7uyzXAn6SLTJqfi6Z",
    "status": "completed",
    "completed_at": 1738738859,
    "role": "assistant",
    "content": [
      {
        "type": "text",
        "text": {
          "value": "梅原大吾は、著名な日本のプロゲーマーであり、「ウメハラ」としても広く知られています。彼はストリートファイターシリーズでの卓越した技術と功績により、世界的な名声を得ています。最近では、2024年の日本eスポーツアワードでeスポーツ功労賞を受賞しました。この賞は、長年のeスポーツ界での貢献を認められた結果であり、「好きなことを続ければ、いつか評価される」という強い信念を持ちながら活動を続けていることが語られています【3†source】。",
          "annotations": [
            {
              "type": "url_citation",
              "text": "【3†source】",
              "start_index": 207,
              "end_index": 217,
              "url_citation": {
                "url": "https://note.com/phantomyo96_/n/n683139696cc9",
                "title": "eスポーツアワード2024にてプロゲーマーの梅原大吾さんが功労賞を受賞。御本人曰く「もっと早く貰えてもよかった。」との事。ゲームに取り組んで ..."
              }
            }
          ]
        }
      }
    ],
    "attachments": [],
    "metadata": {}
  }
}
```