15分でわかる！AIエージェント開発の最新フレームワーク OpenAI Agents SDK

# はじめに

こんにちは！KIYOラーニングで[スタディング](https://studying.jp/)の開発を担当している @Kumacchiino です！  
今回は、先日OpenAIが新たに公開したAIエージェントのフレームワーク[OpenAI Agents SDK](https://github.com/openai/openai-agents-python)についてご紹介します。

本フレームワークを使うと、AIエージェントの活動を**オーケストレーション**できるようになり、**複数のエージェントが連携**して**複雑な課題に取り組むことができる**ようになります。

この記事では、OpenAI Agents SDKのコアとなる**4つの概念**を取り入れ、Pythonでシンプルなアプリを作成し、フレームワークの使い方や**エージェントシステムの基本的な動き**を解説します。 

記事の最後には、完成したプログラム全体を掲載しています。

## エージェントシステムの全体像

今回作成するエージェントシステムの全体像と仕様は、以下のようになります。

![AIエージェント全体像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3996812/9ebcb4b8-d4d0-4767-8d0f-ed5614549986.png)

ユーザーは以下のようなタスクをエージェントに依頼してくる想定で構築しております。

1. 住宅ローンの計算
2. 旅費の計算
3. 上記以外のタスク

**マネージャーエージェント**は、依頼されたタスクを**分類および評価**し、専門のエージェントへ**委譲**します。  
各エージェントは、**ツール**を**自律的に**使用してタスクを処理し、その結果を返す仕組みです。  
また、想定外のタスクが依頼された場合には、タスクの実行を拒否する**ガードレール**機能も備えています。

## この記事を読むとわかること

この記事では、**OpenAI Agents SDKのコアとなる4つの概念**（以下の項目）の**役割**と**使い方**を解説します。

* **Agents（エージェント）**
* **Handoffs（ハンドオフ）**
* **Guardrails（ガードレール）**
* **Tracing（トレーシング）**

また、プログラムの実行例や全体のコードを通して、実際の動作イメージや実用例についても解説します。

### 前提条件

- OpenAIのAPIキーを発行し、環境変数に設定していること（プログラムを実際に動かす場合）
- Pythonの基本的な知識があること

### 環境構築

まずは、ローカル環境にPythonの仮想環境を作成します。

```bash
python -m venv env
source env/bin/activate
```

その後、OpenAI Agents SDKをpipでインストールします。

```bash
pip install openai-agents
```

必要に応じて、.envファイルからAPIキーを読み込むための[python-dotenv](https://pypi.org/project/python-dotenv/)もインストールしてください。

```bash
pip install python-dotenv
```

## Agents（エージェント）

まずは、**エージェント**についてです。  
エージェントはフレームワークの中核となるコンポーネントであり、名前、役割、使用するモデル、利用するツールなどを設定して定義します。

例として、旅費計算エージェントは以下のように定義します。

```python
travel_expense_agent = Agent(
    name="旅費計算エージェント",
    instructions=(
        "旅費の計算を頼まれたら、ツールを使って回答すること。"
        "計算には、旅行日数と1日あたりの基本費用、任意で追加費用を使用する。"
        "内部計算では、土日の割増、長期旅行割引、消費税加算などを実施する。"
    ),
    model="gpt-4o-mini",
    tools=[travel_expense_calculator],
)
```

エージェントが使用する旅費計算ツールは、以下のように関数として定義します。

```python
@function_tool
def travel_expense_calculator(days: int, base_daily_cost: float, other: float = 0) -> str:
    """
    旅費計算ツール（複雑な計算）:
    旅行日数（days）と1日あたりの基本費用（base_daily_cost）、任意の追加費用（other）から、
    以下の計算を行い、最終的な旅費を求める。
    
    計算内容:
    1. 旅行日数のうち約2/7を土日と仮定し、土日は基本費用の10%増しとする。
    2. 平日は残りの日数とする。
    3. 7日を超える場合は、超過日数に対して基本費用の5%を割引する。
    4. 最終的な小計に10%の消費税を加算する。
    """
    weekend_days = int(days * 2 / 7)
    weekday_days = days - weekend_days
    weekday_cost = weekday_days * base_daily_cost
    weekend_cost = weekend_days * base_daily_cost * 1.10
    discount_days = max(0, days - 7)
    discount = discount_days * base_daily_cost * 0.05
    subtotal = weekday_cost + weekend_cost - discount + other
    total = subtotal * 1.1

    return f"最終的な旅費は {total:.2f} 円です"
```

これで、**旅費計算エージェント**の定義が完了しました。  
直感的で分かりやすいコードにより、エージェントの定義が容易に行えます。

## Handoffs（ハンドオフ）

ハンドオフ機能を利用すると、**エージェントは他のエージェントへタスクを委譲**することができます。  
例えば、専門性の高いエージェントを複数用意し、**マネージャーエージェント**がユーザーの質問を**評価・分類**した上で、適切なエージェントへタスクを振り分ける仕組みです。

今回のプログラムでは、住宅ローン計算エージェントと旅費計算エージェントを用意し、マネージャーエージェントがユーザーの質問を受け取り、各エージェントにタスクを委譲します。  
プログラム上では、エージェントの定義時に`handoffs`引数を使用して、委譲先のエージェントをリスト形式で指定します。

```python
manager_agent = Agent(
    name="マネージャーエージェント",
    instructions=(
        "ユーザーからの質問を受け取り、適切なエージェントに回答を依頼する。"
    ),
    handoffs=[loan_calculator_agent, travel_expense_agent]
)
```

## Guardrails（ガードレール）

**ガードレール**は、エージェントの実行と並行して動作し、ユーザーの入力をチェックおよび検証する機能です。  
今回のケースでは、旅費計算や住宅ローン計算以外のタスクが依頼された場合に、**ガードレールを用いてその遂行を拒否する仕組み**を実装しています。  
この仕組みにより、安全なエージェントシステムの構築が可能になります。

まず、ガードレール用のエージェントを定義し、このエージェントを利用してガードレール機能を構築します。  
ガードレールは、`@input_guardrail`デコレータを用いて関数として定義し、定義したガードレール関数をマネージャーエージェントの`input_guardrails`引数にリスト形式で指定します。

```python
class IntendedTaskOutput(BaseModel):
    # タスクが不適切かどうかの判定結果を保持するクラス
    is_inappropriate_task: bool  # 不適切なタスクかどうかのフラグ
    reasoning: str  # 判定の理由

# ガードレールチェックを行うエージェントの定義
guardrail_agent = Agent( 
    name="ガードレールチェックエージェント",
    instructions="旅費計算やローン計算以外のタスクをチェックして、不適切なタスクかどうかを判定する。",
    model="gpt-4o-mini",
    output_type=IntendedTaskOutput,
)

@input_guardrail
async def task_guardrail( 
    ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem]
) -> GuardrailFunctionOutput:
    # ガードレールエージェントを実行し、タスクの適切性を判定する
    result = await Runner.run(guardrail_agent, input, context=ctx.context)

    return GuardrailFunctionOutput(
        output_info=result.final_output, 
        tripwire_triggered=result.final_output.is_inappropriate_task,
    )

# ユーザーのタスクを適切なエージェントに振り分けるエージェントの定義
manager_agent = Agent(
    name="マネージャーエージェント",
    instructions=(
        "ユーザーからの質問を受け取り、適切なエージェントに回答を依頼する。"
    ),
    model="gpt-4o-mini",
    handoffs=[loan_calculator_agent, travel_expense_agent],  # 振り分け対象のエージェント
    input_guardrails=[task_guardrail],  # タスクの適切性をチェックするガードレール
)
```

## Tracing（トレーシング）

OpenAI Agents SDKには組み込みのトレース機能があり、エージェント実行中にLLMの生成、ツール呼び出し、ハンドオフ、ガードレール、さらにはカスタムイベントなど各種イベントを**自動的に記録して可視化**します。  
これにより、OpenAIの[Tracesダッシュボード](https://platform.openai.com/traces/)を使用して、開発中や運用中のワークフローのデバッグ、可視化、監視が可能になります。  
エージェントシステムが**ブラックボックスにならないよう**、トレース機能を活用してエージェントの動作を可視化することは非常に重要です。

以下の図は、マネージャーエージェントがユーザーからの住宅ローンの計算の質問を受け、ガードレールによるチェックを経て、ハンドオフ機能で適切なエージェントにタスクを委譲しているフローを可視化しています。

![トレース1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3996812/a7b65cfc-e3ff-4e96-8469-2b4dfe8fc5c1.png)

## 実行例

作成したプログラムを実行してみます。
コマンドライン上で、pythonコマンドを使って実行します。
第一引数に依頼するタスク・質問を渡します。

### 住宅ローン計算の例

住宅ローン計算の質問を入力すると、住宅ローン計算エージェントから以下のような回答が得られます。

```bash
$ python app.py 35年の金利0.88%で5550万円のローンを返すには月々いくらの支払いになるか計算してください。
エージェントの回答: 35年の金利0.88%で5550万円のローンを返済する場合、毎月の支払い額は約153,584円になります。
```

### 旅費計算の例

旅費計算の質問を入力すると、旅費計算エージェントから以下の回答が返されます。

```bash
$ python app.py 10日間の旅行を計算している。人数は４人で、１日の予算は20000円。総額でいくらになるか計算してください。
エージェントの回答: 10日間の旅行の総額は884,400円です。
```

トレースを見ると、ツールを使用して回答していることがわかります。

![トレース2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3996812/36789155-e5e6-4abb-b3fc-251561d389f4.png)


### 上記以外のタスクの例

住宅ローン計算や旅費計算以外のタスクが依頼されると、ガードレールが作動し、以下のようなメッセージが返されます。

```bash
$ python app.py 1+1は？
ガードレール: 不適切なタスク依頼です。
```

このように、ガードレールが適切に作動し、**不適切なタスクを拒否している**ことが確認できます。  
また、トレースを確認すると、ガードレールが発火し、不適切なタスクとして認識されている様子が記録されています。

![ガードレール1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3996812/1b2f8b75-5d00-485a-b774-b13f7d2d25bb.png)  
![ガードレール2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3996812/558b7cbf-dd34-43a2-a26d-4d6e34aa867e.png)

---

## 完成したプログラム

以下が、今回作成したプログラム全体です。

:::note info
OpenAI Agents SDKのv0.0.3を使用したコードであり、今後バージョンが更新されると下記のコードは動作しなくなる可能性もありますので、ご注意ください。
:::

```python:app.py
import asyncio
import sys
from pydantic import BaseModel
from agents import (
    Agent,
    GuardrailFunctionOutput,
    InputGuardrailTripwireTriggered,
    RunContextWrapper,
    Runner,
    TResponseInputItem,
    input_guardrail,
    trace,
    function_tool
)
from dotenv import load_dotenv


# .envファイルからAPIキーを読み込む
load_dotenv(override=True)

@function_tool
def loan_calculator(principal: float, annual_rate: float, term_years: int) -> str:
    """
    住宅ローン計算ツール:
    元本(principal)、年利(annual_rate、パーセント)と返済期間(term_years)から、
    毎月の返済額を複利計算を用いて求める。
    """
    monthly_rate = annual_rate / 100 / 12
    months = term_years * 12
    if monthly_rate == 0:
        payment = principal / months
    else:
        payment = principal * monthly_rate * (1 + monthly_rate) ** months / ((1 + monthly_rate) ** months - 1)
    return f"毎月の返済額は {payment:.2f} 円です"

@function_tool
def travel_expense_calculator(days: int, base_daily_cost: float, num_people: int) -> str:
    """
    旅費計算ツール:
    旅行日数(days)、1日あたりの基本費用(base_daily_cost)、人数(num_people)を元に
    総旅費を計算する。
    
    計算内容:
    1. 旅行日数のうち約2/7を土日と仮定し、土日は基本費用の10%増しとする。
    2. 平日は残りの日数とする。
    3. 7日を超える場合は、超過日数に対して基本費用の5%を割引する。
    4. 最終的な小計に10%の消費税を加算する。
    5. 総額は人数分を乗算して算出する。
    """
    # 土日の推定日数（小数点以下切り捨て）
    weekend_days = int(days * 2 / 7)
    weekday_days = days - weekend_days

    # 平日と土日の基本費用
    weekday_cost = weekday_days * base_daily_cost
    weekend_cost = weekend_days * base_daily_cost * 1.10  # 土日は10%増し

    # 長期旅行割引：7日を超える日数には基本費用の5%割引を適用
    discount_days = max(0, days - 7)
    discount = discount_days * base_daily_cost * 0.05

    # 小計
    subtotal = weekday_cost + weekend_cost - discount

    # 消費税10%を加算
    total_per_person = subtotal * 1.1

    # 人数分の総額
    total = total_per_person * num_people

    return f"最終的な旅費は {total:.2f} 円です"

loan_calculator_agent = Agent(
    name="住宅ローン計算エージェント",
    instructions="ローンの計算を頼まれたら、ツールを使って回答すること",
    model="gpt-4o-mini",
    tools=[loan_calculator],
)

travel_expense_agent = Agent(
    name="旅費計算エージェント",
    instructions=(
        "旅費の計算を頼まれたら、ツールを使って回答すること。"
    ),
    model="gpt-4o-mini",
    tools=[travel_expense_calculator],
)

class IntendedTaskOutput(BaseModel):
    is_inappropriate_task: bool
    reasoning: str

guardrail_agent = Agent( 
    name="ガードレールチェックエージェント",
    instructions="旅費計算やローン計算以外のタスクかをチェックして、不適切なタスクかどうかを判定する。",
    model="gpt-4o-mini",
    output_type=IntendedTaskOutput,
)

@input_guardrail
async def task_guardrail( 
    ctx: RunContextWrapper[None], agent: Agent, input: str | list[TResponseInputItem]
) -> GuardrailFunctionOutput:
    result = await Runner.run(guardrail_agent, input, context=ctx.context)

    return GuardrailFunctionOutput(
        output_info=result.final_output, 
        tripwire_triggered=result.final_output.is_inappropriate_task,
    )
    
manager_agent = Agent(
    name="マネージャーエージェント",
    instructions=(
        "ユーザーからの質問を受け取り、適切なエージェントに回答を依頼する。"
    ),
    model="gpt-4o-mini",
    handoffs=[loan_calculator_agent, travel_expense_agent],
    input_guardrails=[task_guardrail],
)

async def main():
    if len(sys.argv) > 1:
        user_input = sys.argv[1]
    else:
        user_input = input("質問を入力してください: ")
    with trace(workflow_name="計算ワークフロー"):
        try:
            result = await Runner.run(manager_agent, input=user_input)
            print("エージェントの回答: " + result.final_output)

        except InputGuardrailTripwireTriggered:
            print("ガードレール: 不適切なタスク依頼です。")

if __name__ == "__main__":
    asyncio.run(main())
```

## まとめ・感想

今回は、OpenAI Agents SDKのコアとなる4つの概念を盛り込んだプログラムをPythonで作成し、その使い方や動作をコードを通して解説しました。  
AIエージェントのフレームワークとしては、[LangGraph](https://www.langchain.com/langgraph)や[CrewAI](https://www.crewai.com/)など、さまざまな選択肢がありますが、それらと比較しても、OpenAIのAgents SDKフレームワークは十分に使いやすいと感じました。今後もアップデートがあると思うので、キャッチアップ出来ればと思います。

---

スタディングでは、AIを活用し、利用者が効率的に新たな知識を習得できるよう、日々サービス向上に努めています。  
ご興味のある方は、ぜひ下記のリンクをご覧ください。  
最後までお読みいただき、ありがとうございました！

----

## KIYOラーニング株式会社について
当社のビジョンは『世界一「学びやすく、分かりやすく、続けやすい」学習手段を提供する』ことです。革新的な教育サービスを作り成長させていく事で、オンライン教育分野でナンバーワンの存在となり、世界に展開していくことを目指しています。

https://kiyo-learning.com/

### プロダクト


* [スタディング](https://studying.jp/)：「学びやすく・わかりやすく・続けやすい」オンライン資格対策講座
* [スタディングキャリア](https://career.studying.jp/)：資格取得者の仕事探しやキャリア形成を支援する転職サービス
* [AirCourse](https://aircourse.com/)：受け放題の動画研修がついたeラーニングシステム（LMS）

### KIYOラーニング株式会社では一緒に働く仲間を募集しています

https://www.wantedly.com/companies/kiyo-learning/projects

https://herp.careers/v1/kiyolearning