Azure OpenAI APIを前提としたAgents SDKの検証（Jupyter Notebook環境）

### **1. 概要**  
#### **執筆のモチベーション**：カスタムモデル（例：Azure OpenAI）の活用  
2025年3月11日、OpenAI社は新たに「Agents SDK」を発表しました。これにより、エージェントの構築や実行がより簡単かつ柔軟に行えるようになりました。しかしながら、公式ドキュメントは主にOpenAI APIの利用を前提としており、他のLLMやAPIを使用する場合には、**カスタムモデル**の設定が必要になります。本記事では、Agents SDKの基本的な使い方を紹介するとともに、Azure OpenAIをバックエンドに利用した実装例を交えながら、実践的な活用方法を解説します。
#### **Agents SDK の概要**  
  - OpenAI Agents SDK は、最小限の構成要素（Primitives）で構成されており、シンプルながら柔軟性の高いエージェントアプリケーションを構築できます。SDKで提供される主なコンポーネントは以下の通りです：
    - **Agents**：LLM（大規模言語モデル）に指示とツールを与え、タスクを実行させる基本単位  
    - **Handoff**：エージェント間でタスクを受け渡す機構。役割分担や連携を可能にします  
    - **Guardrails**：ユーザーの入力またはエージェントの出力を検証し、安全性や妥当性を担保する制御機構
  - これらを Python 上で組み合わせることで、複雑な処理フローや対話パターンを**シンプルな構文で記述**できます。さらに、Agents SDKはエージェントの動作を可視化・評価するトレーシング機能も備えており、本番運用にも適した設計となっています。

#### **他のエージェントフレームワークとの違い**  
  - **LangChainとの比較**:  
  LangChainは「多様なLLMとの連携」と「複雑なプロセス設計」に優れる一方、  
  Agents SDKは「シンプルなツール連携」や「迅速な開発」に特化しています。高度なカスタマイズにはLangChainとの併用が推奨されます。

  - **LangGraphとの比較**:  
  LangGraphは「状態遷移や条件分岐が多いアプリケーション」に最適ですが、  
  Agents SDKは「直線的なタスク処理」に適しています。複雑な分岐やループ処理にはLangGraphなどの併用が必要です。  
  
  - **AutoGenとの比較**：  
  AutoGenは「複数エージェントの自動協調」に特化し、議論型タスクや分散型意思決定を支援します。
  一方、Agents SDKは「単一エージェントの柔軟なカスタマイズ」に強みがあり、複数エージェント間の自律的協調には外部ライブラリや手動設計が必要です。  

#### ⚠️**注意点**  
今回ご紹介するコードは、**Jupyter Notebook 上での実行**を前提としています。
  - `await Runner.run(...)` を使って非同期実行していますが、これは Jupyter のセル内で `await` が許可されているため可能です。
  - 公式ドキュメントでは Python スクリプト（`.py`ファイル）を前提とした記述が多く、**`asyncio.run(...)` を用いた構文**となっている場合があります。

---

### **2. カスタムモデルの基本設定**  
#### **前提条件**
  - Azure OpenAIモデルの準備とカスタムモデルの認証設定（APIキー、エンドポイントなど）  
  - 必要なライブラリのインストール
    ```python
    %pip install openai-agents, dotenv
    ```
#### **設定手順** 
  - **.env**ファイル設定  
    **.env**ファイルとは、**APIキーやエンドポイントなどの機密情報**を環境変数で管理しています。これらの情報は、直接コードに書かず、.env ファイルにまとめておくのが一般的です。.envの中身の例は以下の通り：
    ```python  
    AZURE_OPENAI_API_KEY="xxxxxxxxxxxxxxxxxxxxx"  
    AZURE_OPENAI_ENDPOINT="https://xxxxx.openai.azure.com/"
    AZURE_OPENAI_API_VERSION="2024-10-21"
    AZURE_OPENAI_DEPLOYMENT_NAME="gpt-4"
    ```
  - エージェント初期化時のカスタムモデル指定例  
    ```python  
    # 必要なモジュールをインポート
    import os  # 環境変数を取得するための標準モジュール
    from dotenv import load_dotenv  # .envファイルから環境変数を読み込むためのライブラリ
    from agents import (
        AsyncOpenAI,  # 非同期でOpenAI APIを呼び出すクライアントクラス
        OpenAIChatCompletionsModel,  # チャット用のモデルインターフェース
        set_default_openai_client,  # デフォルトのOpenAIクライアントを設定する関数
        set_tracing_disabled,  # トレース（ログ）機能の有効/無効を切り替える関数
    )

    # .envファイルから環境変数を読み込む
    load_dotenv()
    # .envファイルから各種設定を取得 
    AZURE_OPENAI_API_KEY = os.getenv("AZURE_OPENAI_API_KEY")  # APIキー
    AZURE_OPENAI_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")  # エンドポイントURL
    AZURE_OPENAI_DEPLOYMENT_NAME = os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME")  # デプロイメント名（※今回はモデル名と同一として使用）
    AZURE_OPENAI_API_VERSION = os.getenv("AZURE_OPENAI_API_VERSION")  # APIバージョン

    # OpenAI APIの非同期クライアントを作成
    custom_client = AsyncOpenAI(
        api_key=AZURE_OPENAI_API_KEY,  # Azure OpenAIのAPIキー
        base_url=f"{AZURE_OPENAI_ENDPOINT}/openai/deployments/{AZURE_OPENAI_DEPLOYMENT_NAME}",  # APIエンドポイント
        default_headers={"api-key": AZURE_OPENAI_API_KEY},  # APIキーをヘッダーに設定
        default_query={"api-version": AZURE_OPENAI_API_VERSION},  # APIバージョンをクエリパラメータとして設定
    )
    # モデルインスタンスを作成（カスタムクライアントを使って呼び出す設定）
    custom_model = OpenAIChatCompletionsModel(
        model=AZURE_OPENAI_DEPLOYMENT_NAME,  # 使用するモデル（＝デプロイメント名と同一）
        openai_client=custom_client,  # 上記で作成したカスタムクライアントを使用
    )
    # デフォルトのOpenAIクライアントを設定
    set_default_openai_client(client=custom_client, use_for_tracing=False)
    # トレース機能を無効化（設定しないとエラーメッセージが出る）
    set_tracing_disabled(disabled=True)
    ```  

  - **注意点**：  
    カスタムモデルを使用する場合、**Tracing** 機能を有効にするには **OpenAI APIキー（AzureではなくOpenAI本家のキー）** が必要です。使用例は以下の通りです：
    ```python
    from agents import set_tracing_export_api_key

    # Tracing機能を有効化するには、本家OpenAIのAPIキーが必要
    set_tracing_export_api_key(os.getenv("OPENAI_API_KEY"))
    ```
---

### **3. Hello World Example**  
カスタムモデルの基本設定（APIキー、エンドポイントなどの構成）が完了したら、以下のサンプルコードを実行することで、エージェントが正しく動作することを確認できるはずです。
- **カスタムモデルを使用する例**  
  ```python  
  from agents import Agent, Runner

  # エージェントの作成
  agent = Agent(
      name="Assistant",  # エージェントの名前
      instructions="あなたは親切なアシスタントです。日本語で応答してください。",  # エージェントの指示（役割）
      model=custom_model  # 先に定義したカスタムモデルを使用
  )

  # 非同期でエージェントに対し「自己紹介」を作成させる
  # Notebook環境では `Runner.run_sync` は適さないため、非同期実行
  result = await Runner.run(agent, "自己紹介をしてください。")

  # 最終的な出力を表示
  print(result.final_output)
  ```  
- **出力例（実行結果）**：
  ```python
  こんにちは！私は親切で助けになるAIアシスタントです。日常生活の質問から専門的な相談まで、幅広い分野でお手伝いします。わかりやすい説明や丁寧なサポートを心掛けていますので、どうぞお気軽に何でもお尋ねください！ 😊
  ```

---

### **4. 主要コンポーネント（Primitives）のカスタムモデル活用例**  
以下では、これらのプリミティブを活用した実装例を通じて、各コンポーネントの役割と使いどころを丁寧に解説していきます。

#### **Agents（エージェント）**  
  先の Hello World サンプルコードでは `Agent` の基本的な使い方をご紹介しました。  
  `Agent` を定義する際には、以下の3つの要素を指定する必要があります：

  - `name`: エージェントの識別名（ロールプレイやログ管理に便利）
  - `instructions`: エージェントがどう振る舞うべきかを自然言語で指示（システムプロンプトのような役割）
  - `model`: 使用するカスタムモデルを指定します。Azure OpenAI のカスタム構成を使用する場合はこの指定が必要です（本家 OpenAI API のみを使う場合は省略可能）。

#### **Handoff（タスク委任）**  
`Handoff` は、あるエージェントが処理すべきでないタスクを、**別の適切なエージェントに引き渡す仕組み**です。この機能によって、複数のエージェントが役割を分担しながら、**協調的にタスクを処理**することが可能になります。
  - **仕組みの概要**
    - `Handoff=[agentA, agentB, ...]` のようにして、**委任先となる複数のエージェントを指定**できます。
    - 指定されたエージェントの中から、入力されたプロンプトに最も適したエージェントが自動的に選ばれます。
    - 選定基準は主に各エージェントの `handoff_description`（委任説明）や `instructions`（振る舞い指示）に基づきます。

  - **例**：ユーザーの質問に基づいて適切なエージェント（歴史または数学）に振り分け
    ```python  
    # エージェントの作成（歴史の質問に特化したエージェント）
    history_tutor_agent = Agent(
        name="History Tutor",  # エージェントの名前（歴史チューター）
        handoff_description="歴史の質問に特化したエージェント",  # 他のエージェントとの切り替え時の説明
        instructions="歴史に関する質問に対応します。重要な出来事や背景を分かりやすく説明してください。",  # エージェントの指示（役割）
        model=custom_model
    )

    # エージェントの作成（数学の質問に特化したエージェント）
    math_tutor_agent = Agent(
        name="Math Tutor",  # エージェントの名前（数学チューター）
        handoff_description="数学の質問に特化したエージェント",  # 他のエージェントとの切り替え時の説明
        instructions="数学の問題を解く際にサポートします。各ステップの考え方を説明し、具体的な例を示してください。",  # エージェントの指示（役割）
        model=custom_model
    )

    # エージェントの作成（質問の内容に応じて適切なエージェントを選択するエージェント）
    triage_agent = Agent(
        name="Triage Agent",  # エージェントの名前（振り分けエージェント）
        instructions="ユーザーの宿題の質問内容を分析し、適切なエージェント（歴史または数学）に振り分けます。",  # エージェントの指示（役割）
        handoffs=[history_tutor_agent, math_tutor_agent],  # 振り分け対象のエージェントリスト
        model=custom_model
    )

    # === 実行例（歴史の質問） ===
    result = await Runner.run(triage_agent, "最初のアメリカ合衆国大統領は誰か。短く答えてください。")
    print(result.final_output)
    ```  

  - **出力例（実行結果）**：  
    歴史に関する質問は回答してくれます（👇実際の回答）。  
    また、不適切な質問や回答を避けたい内容に対しては、Guardrails（ガードレール） を活用することで、適切に応答を制御することも可能です 
    ```python
    最初のアメリカ合衆国大統領はジョージ・ワシントンです。
    ```

#### **Guardrails（ガードレール）による質問制御の例**：スマホに関する質問をフィルタリング 

**Guardrails** は、エージェントとの対話において、**特定のトピック**に関するユーザーからの入力やエージェントからの出力を検知し、それらを **弾く**（許可しない）ための仕組みです。これにより、不適切または意図しない方向への会話の流れを未然に防ぎ、エージェントの安全性や目的に沿った利用を促進します。
  - ガードレールには2種類あります：
    - 入力ガードレール（Input Guardrail）は初期のユーザー入力に対して実行されます
    - 出力ガードレール（Output Guardrail）は最終的なエージェントの出力に対して実行されます（今回は割愛）
  - `入力ガードレール`は、**エージェントに渡される入力内容を事前にチェックしてフィルタリングする仕組み**です。今回は「**宿題に関する質問かどうか**」を判定する例を説明します。構成のポイントは以下の通りです。
    - **`HomeworkOutput`**: エージェントの判定結果の型を定義（`pydantic` を使用）
    - **`guardrail_agent`**: 質問が宿題に関するかどうかを判定する専用エージェント
    - **`homework_guardrail`**: 実行時に呼び出されるガードレール関数（宿題関連の質問であればトリガー）
    - **`simple_agent`**: 通常の質問を担当する、設定がシンプルなエージェント
    - **`InputGuardrail`**: 入力に対して `homework_guardrail` を紐づけてバリデーションを実行
  - 実装例：
  
    ```python
    from pydantic import BaseModel
    from agents import (
        Agent, GuardrailFunctionOutput, InputGuardrailTripwireTriggered,
        Runner, InputGuardrail
    )
    # 宿題かどうかを判定するための出力型
    class HomeworkOutput(BaseModel):
        is_homework: bool
        reasoning: str

    # 判定用エージェント
    guardrail_agent = Agent(
        name="Guardrail check",
        instructions="ユーザーが宿題について尋ねているか確認してください。",
        output_type=HomeworkOutput,
        model=custom_model
    )

    # ガードレール関数
    async def homework_guardrail(ctx, agent, input_data):
        result = await Runner.run(guardrail_agent, input_data, context=ctx.context)
        final_output = result.final_output_as(HomeworkOutput)
        return GuardrailFunctionOutput(
            output_info=final_output,
            tripwire_triggered=final_output.is_homework  # 宿題の質問ならトリガー
        )

    # ガードレールを適用したエージェント
    simple_agent = Agent(
        name="Assistant",  # エージェントの名前
        instructions="You are a helpful assistant",  # エージェントの指示（役割）
        model=custom_model,  # 先に定義したカスタムモデルを使用
        input_guardrails=[
        InputGuardrail(guardrail_function=homework_guardrail),
        ],
    )
    # テスト実行関数
    async def run_test(question: str):
        print(f"テスト開始: '{question}'")
        try:
            result = await Runner.run(simple_agent, question)
            print("結果:", result.final_output)
            print("ガードレールがトリガーされませんでした")
        except InputGuardrailTripwireTriggered:
            print("宿題に関する質問のため、ガードレールがトリガーされました")
        finally:
            print("-" * 50)

    # テスト質問一覧
    test_questions = [
        "アメリカ合衆国初代大統領は誰ですか？短い回答にしてください。",
        "2x + 5 = 15 の x を求めてください",
        "宿題を手伝ってもらえますか？",
        "何か教えてもらえますか？",
    ]

    # 非同期でテスト実行
    for question in test_questions:
        await run_test(question)
    ```
  - **出力例（実行結果）**：  

    ```python
    テスト開始: 'アメリカ合衆国初代大統領は誰ですか？短い回答にしてください。'
    結果: ジョージ・ワシントンです。
    ガードレールがトリガーされませんでした
    --------------------------------------------------
    テスト開始: '2x + 5 = 15 の x を求めてください'
    宿題に関する質問のため、ガードレールがトリガーされました
    --------------------------------------------------
    テスト開始: '宿題を手伝ってもらえますか？'
    宿題に関する質問のため、ガードレールがトリガーされました
    --------------------------------------------------
    テスト開始: '何か教えてもらえますか？'
    結果: もちろんです！どんなことを知りたいですか？歴史、科学、料理のレシピ、語学、技術、趣味、または日常生活のヒントなど、何でも聞いてください！あなたの関心に合わせてお手伝いします。 😊
    ガードレールがトリガーされませんでした
    --------------------------------------------------
    ```

#### **Tracing（トレーシング）**  
`Tracing` は、エージェントの実行フローや内部処理の流れを可視化・分析するためのログ機能です。開発・デバッグ時に、どのエージェントがどのような判断を行ったかを追跡できるため、複雑なエージェント構成の可視化に便利です。  
今回は **OpenAI API を使用しない前提**のため、Tracing 機能は **利用できません**。公式ドキュメントでは OpenAI API キーを使ったセットアップが前提です。

---

### **5. Toolsのカスタムモデル活用例**  
Agents SDK は、用途や構造に応じて以下の3タイプのツールを提供しています：
#### **Hosted Tools（ホストツール）**  
OpenAIが提供するホスト型ツールには、WebSearchTool（Web検索）、FileSearchTool（OpenAIベクトルストアからの情報取得）、ComputerTool（PC操作の自動化）などがあります。これらは OpenAIResponsesModel を使用する際に利用可能ですが、Azure OpenAI経由での利用はサポートされていないため、本記事では詳細な説明を省略します。

#### **Function Tools（関数ツール）**  
- Python関数をツールとして登録する際、**@function_tool**デコレーターを使用することで、エージェントはその関数の構造を自動的に理解し、LLM に適切なインターフェースとして提示することができます。
- **関数のdocstring**は特に重要な役割を果たします。
  - **ツールの説明**: 関数の docstring が、そのまま Agents SDK におけるツールの説明として利用されます。エージェントは、この説明を基に、どのような状況でそのツールを使用すべきかを判断します。
  - **引数の説明**: 関数の引数に docstring 内で説明を記述することで、Agents SDK はその情報を入力スキーマの一部として利用します。これにより、エージェントがツールを使用する際に必要な引数とその意味を理解するのに役立ちます。
- **カスタム関数ツールの例**：  
    ```python  
    import os
    from dotenv import load_dotenv
    load_dotenv()
    from tavily import TavilyClient  # Tavily の Python クライアントライブラリをインポート
    TAVILY_API_KEY = os.getenv("TAVILY_API_KEY")  # 環境変数から API キーを取得

    from agents import Agent, Runner, function_tool  # Agents SDK の主要コンポーネントをインポート

    # ツールとして登録される関数の定義
    @function_tool  # このデコレーターにより、関数がエージェントのツールとして利用可能になる
    async def tavily_search(query: str) -> dict:
        """
        指定されたクエリに基づき Tavily 検索を実行し、結果を返すツール関数。
        
        Args:
            query (str): 検索するキーワードやフレーズ。
        
        Returns:
            dict: 検索結果のリスト（要約などを含む）。
        """
        tavily_client = TavilyClient(api_key=TAVILY_API_KEY)  # Tavily クライアントを初期化
        print(f"Tavily search with query {query}")  # 実行ログを出力（デバッグ用）
        response = tavily_client.search(query)  # Tavily API を呼び出して検索を実行
        return response['results']  # 結果のうち 'results' 部分だけを返す

    # エージェントの定義
    tavily_search_agent = Agent(
        name="Assistant",  # エージェントの名前
        instructions="日本語でシンプルな回答をしてください。",  # モデルに与える基本指示
        tools=[tavily_search],  # エージェントが利用できるツールとして `tavily_search` を指定
        model=custom_model,  # 使用する LLM モデル（外部で定義済みのカスタムモデル）
    )

    # エージェントの実行
    result = await Runner.run(
        tavily_search_agent,  # 実行対象のエージェント
        "本日と明日の日経平均について"  # ユーザーからの入力プロンプト（日本語での質問）
    )

    # 出力の表示
    print(result.final_output)  # エージェントが生成した最終出力を表示
    ```  
- **出力例（実行結果）**：
    ```python
    Tavily search with query 本日の日経平均
    Tavily search with query 明日の日経平均
    本日の日経平均株価は上昇し、38,963.70円 (+0.42%) でした（[詳細を見る](https://nikkeiyosoku.com/nikkei_forecast/daily/)）。  
    明日の日経平均株価予測については、[こちらをご確認ください](https://robo.andtrader.com/News/tomorrow_future_forecast)。
    ```
#### **Agents as Tools**（エージェントをツールとして使用）：  
エージェント自身をツール化することで、別のエージェントから関数のように呼び出すことができます。  
これは Handoff（タスク委譲）とは異なり、「直接呼び出して結果を受け取る」という関数呼び出しに近い動作です。  
- 以下の例では、**翻訳専用エージェント**を他のエージェントから呼び出すことができます：
  ```python  
  from agents import Agent, Runner  # Agents SDK からエージェントと実行ランナーをインポート
  import asyncio  # 非同期処理のための標準モジュール

  # スペイン語翻訳エージェント
  spanish_agent = Agent(
      name="Spanish agent",  # エージェント名
      instructions="ユーザーのメッセージをスペイン語に翻訳してください。",  # 翻訳タスクの指示
      model=custom_model  # 使用するカスタムLLM（定義済み）
  )

  # フランス語翻訳エージェント
  french_agent = Agent(
      name="French agent",
      instructions="ユーザーのメッセージをフランス語に翻訳してください。",
      model=custom_model
  )

  # オーケストレーターエージェント（翻訳管理）
  orchestrator_agent = Agent(
      name="orchestrator_agent",
      instructions=(
          "あなたは翻訳を担当するエージェントです。提供されたツールを使って翻訳を行ってください。"
          "もし複数の言語への翻訳を求められた場合は、適切なツールを順番に使用してください。"
          # 指示：ツールを使って翻訳し、複数言語が求められたら対応するツールを順に呼び出す
      ),
      model=custom_model,
      tools=[
          # エージェントをツール化して登録（名前と説明付き）
          spanish_agent.as_tool(
              tool_name="translate_to_spanish",  # ツールとしての内部呼び出し名
              tool_description="ユーザーのメッセージをスペイン語に翻訳します。",  # 機能の説明
          ),
          french_agent.as_tool(
              tool_name="translate_to_french",
              tool_description="ユーザーのメッセージをフランス語に翻訳します。",
          ),
      ],
  )

  #  オーケストレーターエージェントを実行
  result = await Runner.run(
      orchestrator_agent,  # 実行対象の親エージェント
      input="『こんにちは、お元気ですか？』をスペイン語で言ってください。"  # 翻訳対象の入力（スペイン語への翻訳を要求）
  )

  # 最終結果を出力
  print(result.final_output)  # 翻訳された文が出力される

  ```  
- **出力例（実行結果）**：
  ```python
  スペイン語では、「こんにちは、お元気ですか？」は「Hola, ¿cómo estás?」と言います。
  ```
- **Handoffとの違い**：
  - **Handoff:** 複数のエージェントが**順番に**タスクを引き継ぎながら処理を進める方式。各エージェントが対話の主導権を交代しながら会話を構成します。  
  - **Agents as Tools:** あるエージェントが、必要に応じて他のエージェントの特定機能を**道具として呼び出す**方式。主導権は常に呼び出し元のエージェントにあります。

  | 特徴         | Handoff                                 | Agents as Tools                                      |
  |--------------|------------------------------------------|------------------------------------------------------|
  | **目的**     | タスクの委譲と会話の引き継ぎ                   | 他エージェントの能力を部分的・補助的に活用              |
  | **会話履歴** | 新しいエージェントが全体の会話履歴を引き継ぐ     | 呼び出し元のエージェントが会話の流れを維持、結果のみ受け取る |
  | **処理の流れ** | 完全に主導権が移譲され、次のエージェントが主担当に | 呼び出し元が処理を主導し、呼び出し先は一時的・補助的な役割 |
  | **LLMの認識** | `transfer_to_<agent_name>` のようなツールとして認識 | Function calling の形式でツールとして認識                |
  | **使用コスト** | 会話履歴の引き継ぎが多いため高コストになりやすい | 必要なときだけ呼び出すため、コスト効率がよい                |
  | **想定使用シーン** | タスクを段階的に分担するような複雑な対話         | メインエージェントは安価なLLMを使用し、専門処理のみ高性能LLMを利用 |

---

### **6. 注意点と課題**

#### **カスタムモデルの制約**
  - OpenAI API を使わない構成では、`FileSearchTool` や `WebSearchTool` など **組み込みの Hosted Tool が使えない** ケースが多くなります（※これらは OpenAI のサーバーで動作するため）。  
  - そのため、**自前で同等機能を関数ツールとして実装する必要がある**場面もあります（例：独自の検索関数やローカルベクトルストアとの連携など）。

#### **コスト管理**
  - **すべてのエージェントが同じモデルを使用する構成では、無駄なトークン消費が起きやすい**ため、処理の性質に応じて使い分けるのが有効です。
  - **個人的提案：用途別にモデルを使い分ける構成**
    - **軽量モデル（低コスト）**：  
      ガードレールやツール起動用のエージェントなど、**シンプルな判断・処理のみを担う部分に使用**。応答の品質よりも速度・コストが重視されるケース。
    - **高精度モデル（高コスト）**：  
      ユーザーへの最終的な応答を生成するメインエージェントや、エージェントをツール化した複雑な処理に使用。**正確な翻訳・要約・創造的出力など、品質重視の場面に限定する**。

#### **非同期処理の管理**
  - `Runner.run()` は基本的に `await` が必要となるため、**Jupyter Notebook 上以外の実行環境（FastAPIなど）では適切な非同期制御が必要**になります。

---

### **7. 実践的な使用例**（簡単なアプリ開発するタスク自動化）
ユーザーが入力したアプリの要件をもとに、

- 設計案の提案

- （ユーザーによる確認・フィードバック）

- コードの自動生成

- コードレビュー（改善提案を含む）

…といった一連の開発プロセスを、エージェントが順に担当することで、小規模な開発フローを自動化できます。
特に、各フェーズごとに異なる役割のエージェント（例：設計担当・実装担当・レビュー担当）を設けることで、人間のチーム開発に近い分担と連携が実現可能です。※なお、出力結果は非常に長くなるため、本文では割愛しています。
```python
from agents import Agent, Runner
import asyncio

# 各工程を担当するエージェントを定義

# 設計エージェント：ユーザーの要望をもとにシステム設計を提案
design_agent = Agent(
    name="DesignAgent",
    instructions="ユーザーのアプリ要件に基づいて、システム設計案を提案してください。",
    model=custom_model,
)

# コード生成エージェント：設計に基づいてPythonコードを生成
code_gen_agent = Agent(
    name="CodeGenAgent",
    instructions="承認された設計に基づいて、Pythonのコードを生成してください。",
    model=custom_model,
)

# コードレビュ―エージェント：生成されたコードをレビューし、改善提案や修正コードを提示
code_review_agent = Agent(
    name="CodeReviewAgent",
    instructions="コードの問題点、改善点、ベストプラクティスをレビューし、修正済みコードを提案してください。",
    model=custom_model,
)


# 開発プロセス全体を実行する非同期関数（ワークフロー定義）
async def main_workflow():
    # 1️⃣ ユーザーからアプリの要望をヒアリング
    user_input = input("アプリの要望を入力してください：\n")

    # 2️⃣ 設計フェーズ：要望に基づいて設計案を生成
    design_result = await Runner.run(design_agent, input=user_input)
    print("\n[設計案]:\n", design_result.final_output)

    # 3️⃣ 設計確認フェーズ：ユーザーが「OK」するまでフィードバックループ
    while True:
        feedback = input("\nこの設計で進めますか？（OK または フィードバックを入力）：\n")
        if feedback.lower() == "ok":
            break
        else:
            # フィードバックを含めて再度設計提案
            design_result = await Runner.run(design_agent, input=f"{user_input}\nユーザーからのフィードバック：{feedback}")
            print("\n[修正後設計案]:\n", design_result.final_output)

    # 4️⃣ コード生成フェーズ：確定した設計に基づきコードを生成
    code_result = await Runner.run(code_gen_agent, input=f"以下の設計について、コードを生成：{design_result.final_output}")
    print("\n[生成されたコード]:\n", code_result.final_output)

    # 5️⃣ コードレビュー：生成されたコードをレビューし、改善点と修正コードを提示
    review_result = await Runner.run(code_review_agent, input=f"以下のコードについて、レビューを行い、修正後のコードを生成：{code_result.final_output}")
    print("\n🕵️‍♂️ [コードレビュー結果]:\n", review_result.final_output)

    # ✅ 開発完了
    print("\n✅ 開発プロセス完了！")


# Jupyter Notebook 等で直接実行できるように await で起動
await main_workflow()

```
### 8.参考資料
- [OpenAI Agents SDK 公式ドキュメント](https://openai.github.io/openai-agents-python/)
- [Qiita記事：【サンプルコードあり】OpenAI Agents SDKをAzure OpenAI Serviceで動かす](https://zenn.dev/k_zumi_dev/articles/137ba63037051e)
- [Qiita記事：【最新】OpenAIのResponses API/Agents SDKで実現する次世代AIエージェント開発🚀【コード付き完全解説！】](https://zenn.dev/chips0711/articles/2876f478164f28#%E3%80%8Cresponses-api%E3%80%8D%E3%81%A8%E3%80%8Cagents-sdk%E3%80%8D%E3%82%92%E8%A9%A6%E3%81%97%E3%81%A6%E3%82%8F%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8)