Zenn
🦌

【最新】OpenAIのResponses API/Agents SDKで実現する次世代AIエージェント開発🚀【コード付き完全解説!】

2025/03/17に公開
10

「Responses API」と「Agents SDK」を試してわかったこと

こんにちは。LLM界隈は連日発表されるモデルの性能やAIエージェントアプリばかりに注目が集まりがちですが、2025年はAIエージェント開発における「開発者向けのインテグレーション機能」がより大きなイノベーションを引き起こすと考える方も少なくないでしょう。
そんな中、OpenAIが2025年3月12日2時ごろ(日本時間)に発表したResponses APIAgents SDKは、まさにこういう機能を待っていたと言わざるを得ないアップデートでした。

参考:公式プレスリリース

これまでChatCompletion APIやLangChainなどで抱えていた、

  • 「最新情報を扱うために外部検索機能を組み込むのが面倒…」
  • 「会話履歴を全部自前で管理するのが大変…」
  • 「マルチエージェント開発はLangChain/LangGraphが強いけど抽象化が深くて学習コスト高い…」
    といった課題が、OpenAI公式の強力なフレームワークによって一気に解消されるかもしれません。今回の記事では、Responses APIAgents SDKの概要と使い方を整理しつつ、実際のコード例を交えながら、試しつつ深堀りしていきたいと思います。

1. はじめに:Responses APIとAgents SDKは何が新しい?

OpenAIはGPT-4やChatGPTで有名ですが、実は開発者向けの周辺機能も積極的にアップデートしています。今回のリリースで注目すべきポイントは次の二つです。

Responses API

  • 従来のChatCompletion APIからさらに進化し、「ツール使用」「サーバ側での会話履歴管理」などを公式にサポートしたチャット用API。
  • Web検索やファイル検索、コンピュータ操作などが標準ツールとして提供され、toolsパラメータで指定するだけでモデルが自律的に活用。
  • 過去の会話履歴をサーバー側で保管でき、マルチターンのやり取りがコード最小限で継続できる。
  • 従来のAssistants APIベータで試されていた機能が正式に統合された位置づけ(Assistants APIは2026年半ばを目処にサポート終了予定)。
  • デフォルト設定でリクエストログのダッシュボードを利用可能。

Agents SDK

  • 複数エージェント(複数のLLM)を連携させるマルチエージェント開発用のPythonフレームワーク。
  • ハンドオフ(Handoff)機能により、問い合わせ内容やタスクの進捗に応じて別のエージェントへ制御を移すことが可能。
  • ガードレール(Guardrail)機能やFunctionツールなどを公式に用意しており、複雑なワークフローや安全対策を少ないコードで実装できる。
  • 新しく追加されたトレーシング画面をデフォルト設定で利用可能。
  • 実験的なSDK「Swarm」で得た知見をもとに開発された正式版ツール。
  • 現時点ではPython版のみ提供、今後Node.js版もリリース予定。

これらによって、LLMが自律的にツールを使いこなし、複数エージェントが連携して複雑なタスクをこなす時代がすぐそこに来ています。特にResponses APIはChatCompletion APIの上位互換として、個人的には今後主流になる可能性が高いと見ています。

2. 環境準備とセットアップ

インストール

pip install openai openai-agents
  • openai:OpenAIのPythonライブラリ
  • openai-agents:Agents SDKのPythonクライアント

あとはAPIキーを設定しておきます。

export OPENAI_API_KEY="sk-あなたのAPIキー"

簡単な動作確認

from openai import OpenAI

client = OpenAI()  # 環境変数からAPIキーを自動読み込み

response = client.responses.create(
    model="gpt-4o",
    input="未来のテクノロジートレンドを3つ挙げてください"
)

print(response.output_text)
出力例
未来のテクノロジートレンドとして、以下の3つが挙げられます。

1. **人工知能と機械学習の進化**
   - 人工知能(AI)と機械学習は、より高度で人間のような理解と未来予測が可能になっています。自然言語処理、画像認識、予測分析など、様々な分野での応用が拡大し続けています。

2. **量子コンピューティング**
   - 量子コンピュータは従来のコンピュータでは難解な計算を短時間で解く能力を持っています。新薬開発、暗号解析、ビッグデータ分析などで革命的な変化をもたらす可能性があります。

3. **バイオテクノロジーと健康テクノロジーの進化**
   - 遺伝子編集(CRISPR技術など)や個別化医療の進化により、新しい治療法や病気予防のアプローチが可能になっています。ウェアラブルデバイスを活用した健康管理や遠隔医療も普及しています。

これらの技術は社会や産業に大きな影響を与え、生活の質を向上させると期待されています。

これで何らかのテキストが返ってくれば準備完了です。なお、Playgroundや公式のNext.jsサンプルアプリでも機能を簡単に試すことができます。では早速、Responses APIを試していきましょう。

3. Responses APIを触ってみる

3.1 基本的な使い方

Responses APIは従来のChatCompletion APIと似たインターフェースですが、以下の点が大きく異なります。

  1. ツール使用がネイティブサポート
    toolsパラメータを与えると、モデルが「Web検索」や「ファイル検索」などを自律的に呼び出して回答を形成。

  2. 会話履歴の自動管理
    store=Trueを指定すればサーバ側にコンテキストが保存され、返ってきたresponse_idを渡すだけで継続会話が可能。

  3. パラメータ設計の変更
    今までのmessagesという配列ではなく、Instruction Tuningの形式のようにinstructionsinputの二つを渡す設計になっています。

参考として、Responses APIとChatCompletion APIの相違点の詳細はこちらの公式ドキュメントにあります。

まずは通常のQA例。model="gpt-4o"を指定し、単純な質問を投げます。

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-4o",
    input="宇宙望遠鏡の最新研究進展について教えてください。"
)
print(response.output_text)
出力例
最近の宇宙望遠鏡の研究進展についてお話しします。特に注目されるのはジェームズ・ウェッブ宇宙望遠鏡(JWST)です。この望遠鏡は、赤外線観測を主に行い、宇宙の初期の銀河や恒星の形成過程を解明することを目指しています。

最近の研究では、JWSTが非常に古い銀河や惑星系の詳細な観測を行い、宇宙初期の構造や星の形成について新たな知見を提供しています。また、大気を持つ系外惑星の分析も進んでおり、生命存在の可能性を探る研究が活発化しています。

さらに、ハッブル宇宙望遠鏡も依然として活躍しており、可視光や紫外線を使った観測で、星や銀河の進化を理解する手助けを続けています。

このように、最新の宇宙望遠鏡のデータは天文学者にとって貴重な情報源となっており、宇宙の歴史や構造に関する理解が深まっています。

結果はChat API同様、GPT-4oモデルのテキスト回答が返ってきます。いわば"ChatCompletion API + α"の形ですが、真価はツール使用時に発揮されます。

次に、instructionsinputを組み合わせて使ってみましょう:

response = client.responses.create(
    model="gpt-4o-mini",
    instructions="指定された都市をテーマに歴史や文化を取り入れた短い詩を作成してください。古典的な言い回しを使い、都市の雰囲気を表現してください。",
    input="京都",
)
print(response.output_text)
出力例
古の都に揺るる春風、  
桜の花びら舞い散りて、  
千年の夢、石畳の道、  
静寂の庭に心を寄せる。

金閣の影、月夜に映えて、  
琉璃の如く光を放つ。  
歴史の響き、時を超えて、  
文化の香り、今尚息づく。

嵐山の山々、秋色に染まり、  
川の音色に耳を傾けて、  
祈りの声、千年の約束、  
古都京都よ、汝の美を讃えん。

3.2 Web検索ツールを使う

Responses APIの目玉機能のひとつが、Web検索ツールをワンライナーで使える点。従来ならLangChain等で外部検索用のクラスを組む必要がありましたが、Responses APIでは以下のようにtools=[{"type": "web_search"}]を付けるだけです。

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-4o",
    tools=[{"type": "web_search"}],
    input="2025年に開催予定の重要な国際会議を3つ紹介してください"
)
print(response.output_text)
出力例
2025年に開催予定の重要な国際会議を3つご紹介いたします。

1. **第9回サステナブル・ブランド国際会議 2025 東京・丸の内**

   2025年3月18日から19日にかけて、東京国際フォーラムおよび明治安田ヴィレッジ 丸の内で開催されます。この会議では、持続可能な開発や経済成長、技術革新などのグローバルな課題について、世界中のリーダーや専門家が議論を行います。 ([sb-tokyo.com](https://sb-tokyo.com/2025/?utm_source=openai))

2. **世界自然保護会議**

   2025年10月にアラブ首長国連邦のアブダビで開催される予定です。この会議は、国際自然保護連合(IUCN)が主催し、自然保護や持続可能な開発に関する国際的な議論の場となります。 ([prtimes.jp](https://prtimes.jp/main/html/rd/p/000000011.000055440.html?utm_source=openai))

3. **第10回アジア太平洋エイズ重感染症会議(APACC 2025)**

   2025年6月12日から14日にかけて、東京国際交流館プラザ平成で開催されます。この会議は、エイズなどの感染症に関する最新の研究結果を共有し、アジア太平洋地域の関係者が情報交換を行う場となります。 ([businesseventstokyo.org](https://businesseventstokyo.org/ja/news_press_releases/20250207_press-release.html?utm_source=openai))

これらの会議は、各分野における国際的な課題解決や協力強化に向けた重要な場となることが期待されています。

実行するとモデルが内部でWeb検索し、国際会議の情報を引用つきで列挙する、といった回答を返すことがあります。このツールのバックエンドにはChatGPTで利用できるWeb検索と同じモデルが使われています。

ちょっと面白いプロンプト例:
「日本の最新サステナビリティ政策について解説して、情報源も添えてください」
→ 実際に検索を行い、政策名や公式サイトのURLを回答に含めてくれる。

出力例
日本は近年、持続可能な社会の実現に向けて、サステナビリティ政策を積極的に推進しています。以下に主な取り組みを紹介します。

**1. サステナブルファイナンスの推進**

金融庁は、持続可能な社会を支える金融システムの構築を目指し、「サステナブルファイナンス有識者会議」を設置しました。この会議では、サステナブルファイナンスの課題や対応策について議論が行われ、2024年7月9日には第四次報告書が公表されています。 ([fsa.go.jp](https://www.fsa.go.jp/policy/sustainable-finance/?utm_source=openai))

**2. サステナビリティ情報開示の強化**

2023年3月、金融庁は有価証券報告書におけるサステナビリティ関連情報の開示を義務付ける新たな規則を施行しました。これにより、すべての上場企業は、TCFD(気候関連財務情報開示タスクフォース)の枠組みに基づき、戦略、指標と目標、ガバナンス、リスク管理の観点から情報を開示する必要があります。 ([ey.com](https://www.ey.com/ja_jp/insights/sustainability/whats-next-for-japanese-sustainability-disclosure-standards?utm_source=openai))

**3. グリーンファイナンスとトランジション・ファイナンスの推進**

環境省は、グリーンファイナンスの促進を目的とした「グリーンファイナンスポータル」を開設し、関連情報の提供や支援を行っています。また、経済産業省は、脱炭素社会への移行を支援するトランジション・ファイナンスの環境整備に取り組んでおり、2023年5月には「クライメート・トランジション・ファイナンスに関する基本指針」を確定しました。 ([fsa.go.jp](https://www.fsa.go.jp/policy/sustainable-finance/?utm_source=openai))

**4. インパクト投資の推進**

金融庁は、社会的課題の解決を目的としたインパクト投資の推進にも力を入れています。2024年3月29日には、「インパクト投資(インパクトファイナンス)に関する基本的指針」を公表し、投資家や企業に対して指針を示しました。 ([fsa.go.jp](https://www.fsa.go.jp/policy/sustainable-finance/?utm_source=openai))

これらの取り組みを通じて、日本は持続可能な社会の実現に向けた政策を積極的に推進しています。

注意点:

  • Web検索は利用回数に応じた追加料金がかかります(おおむね$30/1000検索前後)。
  • 現時点ではgpt-4oとgpt-4o-miniの二つのモデルでのみ利用可能です。
  • Chat Completionsでもモデルとしてのみ利用可能ですが、他のツールと組み合わせることはできません。

3.3 ファイル検索ツールを試す

次はファイル検索ツール。いわゆるRAG(Retrieval Augmented Generation)のように、アップロード済みドキュメント群をモデルが検索→回答する仕組みを標準提供しています。

まず、Files APIでファイルをアップロードし、ベクトルストアに追加するという下準備が必要です:

from openai import OpenAI
import time

client = OpenAI()

# テキストファイルをアップロード
file = client.files.create(
    file=("store_policy.txt", "当店の返品ポリシーは購入後30日以内、未使用・未開封の商品に限ります。".encode("utf-8")),
    purpose="assistants",
)

# ベクトルストアを作成し、上のファイルを初期データとして登録
vector_store = client.vector_stores.create(
    name="store-policies",
    file_ids=[file.id],
)

# ベクトルストアが利用可能になるまで待つ
while client.vector_stores.retrieve(vector_store.id).status != "completed":
    print("ベクトルストア準備中...")
    time.sleep(0.5)

# ファイル検索ツールを使用
response = client.responses.create(
    model="gpt-4o",
    tools=[{"type": "file_search", "vector_store_ids": [vector_store.id]}],
    input="返品は何日以内ならできますか?"
)
print(response.output_text)
出力例
ベクトルストア準備中...
返品は購入後30日以内で、未使用・未開封の商品に限られます。

これまでAssistants (beta)でのみ利用可能だったベクトルストア機能が、メタデータのフィルタリングなどいくつかの機能追加を経て正式版としてリリースされました。

登録できるファイルの種類やサイズ、数には制限があるため、詳細はドキュメントを参照してください。社内マニュアルやFAQの検索Botを作る際に極めて便利で、LangChainと比較すると初期セットアップが短時間で済むのが強みです。

3.4 会話の状態管理(連続会話)

Responses APIでは、サーバ側で会話履歴を自動的に保持できる点が大きな特徴です。以下は複数ターンの会話例:

from openai import OpenAI

client = OpenAI()

# 一回目の会話
res1 = client.responses.create(
    model="gpt-4o",
    instructions="あなたは旅行アドバイザーです。各国の観光名所や文化について詳しく教えてください。",
    input="フランスで訪れるべき場所を3つ教えて",
    store=True,  # 会話履歴をサーバー側に保存
)
print("一回目の会話の結果:")
print(res1.output_text)

# 二回目の会話 - 前回の会話を継続
res2 = client.responses.create(
    model="gpt-4o",
    input="次はイタリアではどうですか?",
    previous_response_id=res1.id,  # 前回の会話IDを指定するだけ
)
print("二回目の会話の結果:")
print(res2.output_text)
出力例
一回目の会話の結果:
フランスで訪れるべき場所を3つご紹介します。

1. **パリ(エッフェル塔、ルーヴル美術館、ノートルダム大聖堂)**  
   パリはフランスの首都で、多くの有名な観光名所があります。エッフェル塔はその象徴的な存在で、街を一望できる絶景スポットです。ルーヴル美術館では「モナ・リザ」を始め、たくさんの貴重な芸術作品を鑑賞できます。また、ノートルダム大聖堂はゴシック建築の傑作として知られています。

2. **プロヴァンス(ラベンダー畑、アヴィニョン、エクス=アン=プロヴァンス)**  
   プロヴァンス地方は美しい自然と歴史的な景観で有名です。夏には広大なラベンダー畑が一面に広がり、リラックスした雰囲気が楽しめます。アヴィニョンには歴史的な教皇庁宮殿があり、エクス=アン=プロヴァンスはアートと文化の街として知られています。

3. **シャモニー=モン=ブラン(モンブラン登山、スキーリゾート、自然散策)**  
   アルプス山脈のふもとに位置するシャモニーは、アウトドア愛好者に人気のスポットです。モンブランはヨーロッパ最高峰であり、登山やスキーが楽しめます。また、四季を通じて自然観察やハイキングの拠点としても最適です。

どれも異なる魅力を持つ場所なので、フランス旅行のプランにぜひ加えてください。
二回目の会話の結果:
イタリアで訪れるべき場所を3つご紹介します。

1. **ローマ(コロッセオ、バチカン市国、トレビの泉)**  
   ローマは歴史と文化が豊富な都市です。コロッセオは古代ローマ時代の壮大な円形闘技場で、訪れる人々を魅了します。バチカン市国のサン・ピエトロ大聖堂やシスティーナ礼拝堂は必見です。また、トレビの泉にコインを投げて願いを込めるのも楽しみです。

2. **フィレンツェ(ウフィツィ美術館、ドゥオーモ、美しい街並み)**  
   フィレンツェはルネサンス文化の中心地として知られています。ウフィツィ美術館にはボッティチェリやレオナルド・ダ・ヴィンチの作品が展示されています。ドゥオーモ(サンタ・マリア・デル・フィオーレ大聖堂)はその壮大な建築が印象的です。街全体がアートで溢れています。

3. **ヴェネツィア(ゴンドラツアー、サン・マルコ広場、カーニバル)**  
   ヴェネツィアは運河の街として有名で、ゴンドラに乗って美しい運河を巡るのは特別な体験です。サン・マルコ広場は観光の拠点で、周辺には美しい建物や美術館があります。ヴェネツィアのカーニバルは華やかな仮面と衣装が特徴で、多くの観光客が訪れます。

どの都市もそれぞれにユニークな魅力を持っていますので、イタリア旅行にぜひ訪れてみてください。

従来のChatCompletion APIだと過去メッセージをすべて送る必要がありましたが、それが不要になります。複数ターンのQ&Aでもコードがスッキリし、開発者体験が格段に改善される点は大きいですね。

3.5 画像・PDFファイルの扱い

いつの間にやら今回の発表と同時に、画像イメージファイルだけでなくPDFファイルを参照したコールレスポンスもできるようになりました。ファイルを参照させるには二種類の方法があります:

  1. Base64エンコードでの埋め込み:
import base64
from openai import OpenAI

client = OpenAI()

with open("./OpenAI2025年3月新発表まとめ.pdf", "rb") as f:
    data = f.read()
    base64_string = base64.b64encode(data).decode("utf-8")
    response = client.responses.create(
        model="gpt-4o",
        input=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "input_file",
                        "filename": "research_report.pdf",
                        "file_data": f"data:application/pdf;base64,{base64_string}",
                    },
                    {"type": "input_text", "text": "レポートの主要な発見事項を箇条書きで教えてください"},
                ],
            },
        ],
    )
    print(response.output_text)
出力例
レポートの主要な発見事項は以下の通りです:

- **エージェント構築基盤の進化**: OpenAIは開発者向けに新しいエージェント構築基盤ツールを発表。
- **主要コンポーネント**:
  - **Responses API**: ウェブ検索、ファイル、コンピュータ操作を統合。
  - **Agents SDK**: マルチエージェントワークフローの管理を簡素化。
  - **可観測性ツール**: 実行追跡と検査機能を提供。
- **主要な利点**:
  - 開発の簡素化と生産性向上。
  - ウェブ検索で最大90%の精度。
- **エージェント構築の課題**:
  - プロンプトの調整やカスタムロジックが複雑。
  - 可視性の欠如と統合の複雑性。
- **新ツールの目的**:
  - エージェントアプリケーション開発の簡素化。
  - データの安全な保存によるパフォーマンス評価の容易化。
- **実際のユースケース**:
  - カスタマーサポートの自動化や研究支援。
  - 企業でのデータ検索エージェントの活用。
- **今後の展望**:
  - API間の深い統合と新しいデプロイツールの開発。
  - 安全性と信頼性の強化。

今回参照させたpdfファイルは、本記事の最後の章のおまけとして載せてあります。(生成AIで生成した資料です。)

  1. Files APIでのアップロード:
from openai import OpenAI

client = OpenAI()

file = client.files.create(
    file=open("./research_report.pdf", "rb"),
    purpose="assistants",
)

response = client.responses.create(
    model="gpt-4o",
    input=[
        {
            "role": "user",
            "content": [
                {"type": "input_file", "file_id": file.id},
                {"type": "input_text", "text": "レポートの主要な発見事項を箇条書きで教えてください"},
            ],
        },
    ],
)
print(response.output_text)
出力例
以下はレポートの主要な発見事項です:

- **新ツール発表:** 開発者とエンタープライズ向けに信頼性の高いAIエージェントを構築するためのプラットフォームを進化。

- **主要コンポーネント:** 
  - Responses APIでウェブ検索、ファイル検索、コンピュータ操作が可能。
  - 組み込みツールである検索やファイル機能。
  - Agents SDKによるエージェントワークフロー管理。

- **主要な利点:**
  - 開発の簡素化と生産性向上。
  - 信頼性のあるウェブ検索(最大90%の精度)。
  - 複雑タスクの自律実行。

- **課題と新ツールの目的:**
  - プロンプトの調整やカスタムオーケストレーションの複雑さに対応。
  - データの安全な保存と簡便な評価機能を提供。

- **Responses API:** 
  - Chat CompletionsとAssistants APIを組み合わせ、様々なタスクを処理可能。

- **Agents SDK:** 
  - エージェントワークフローの簡素化を目指し、カスタマーサポートや研究支援などに活用。

- **今後の展望:** 
  - API間の統合、新しいデプロイと評価ツールの開発。
  - シームレスなプラットフォーム体験と安全性強化の重視。

- **市場展望:** 
  - AIの活用で自律システムの需要が拡大中。

これは3.3で紹介した「ファイル検索」ツールと異なり、特定のファイルそのものを直接参照する方法です。ファイルを添付している場合も、その内容をログの確認画面で見ることができます。

3.6 構造化出力(Strucutured Outputs)

続いて、構造化出力(Strucutured Outputs)を試してみました。
構造化出力とは、応答が指定されたJSONスキーマに従うことを保証する仕組みです。JSONは、アプリケーション間でデータを交換するために世界中で広く使用されているフォーマットの一つです。構造化出力を利用することで、モデルが常に提供されたJSONスキーマに従った応答を生成することが可能になります。これにより、必要なキーが省略されたり、無効な値が生成されたりする心配がなくなります。

from openai import OpenAI
import json

client = OpenAI()

response = client.responses.create(
    model="gpt-4o-2024-08-06",
    input=[
        {"role": "system", "content": "製品レビューを要約してください。"},
        {
            "role": "user",
            "content": "このスマートフォンは非常に使いやすく、バッテリーの持ちが良いです。カメラの性能も素晴らしいです。",
        },
    ],
    text={
        "format": {
            "type": "json_schema",
            "name": "product_review_summary",
            "schema": {
                "type": "object",
                "properties": {
                    "usability": {"type": "string"},
                    "battery_life": {"type": "string"},
                    "camera_quality": {"type": "string"},
                },
                "required": ["usability", "battery_life", "camera_quality"],
                "additionalProperties": False,
            },
            "strict": True,
        }
    },
)

review_summary = json.loads(response.output_text)
print(review_summary)
出力例
{'usability': 'このスマートフォンは非常に使いやすいです。', 'battery_life': 'バッテリーの持ちが良いです。', 'camera_quality': 'カメラの性能も素晴らしいです。'}

といった具合に、構造化出力は非常に便利かつ有用です。
構造化出力の利点には以下があります:

  • 信頼性のある型安全性:不正確にフォーマットされた応答を検証したり再試行したりする必要がありません。
  • 明示的な拒否:安全性に基づくモデルの拒否がプログラム的に検出可能です。
  • 簡単なプロンプト:一貫したフォーマットを達成するために強い言葉のプロンプトを必要としません。

3.7 StreamingとReasoning

Streaming(ストリーミング出力)ReasoningについてもResponses APIは対応しています。

少し雑な説明ですが、以下のようにResponsesエンドポイントへのリクエストにstream=TrueをセットしてあげるだけでStreaming出力が可能となります。

ストリーミング出力
stream = client.responses.create(
    model="gpt-4o",
    input=[
        {
            "role": "user",
            "content": "生麦生米生卵を言ってください。",
        },
    ],
    stream=True,
)

Reasoningについては、Reasoning effortパラメータについて説明します。このパラメータは、モデルがプロンプトに応答する前にどれだけの推論トークンを生成するかを決めるガイドのようなものです。このパラメータには、lowmediumhighのいずれかを指定できます。lowはスピードとトークンの節約を重視し、highはより深い推論を行いますが、その分トークンが多くなり、応答が遅くなります。デフォルトはmediumで、スピードと推論の正確さのバランスを取っています。
では、以下がReasoningでのResponses APIの使用例となります。

Reasoning
from openai import OpenAI

client = OpenAI()

prompt = """
あなたはバッシュスクリプトの達人です。次のような形式の行列 '[1,2],[3,4],[5,6]' を受け取り、
その転置を同じ形式で出力するスクリプトを書いてください。ただし、スクリプトの最後に「行列の転置は魔法のように簡単です!」とジョークを入れてください。
"""

response = client.responses.create(
    model="o3-mini", reasoning={"effort": "medium"}, input=[{"role": "user", "content": prompt}]
)

print(response.output_text)
出力例
#!/bin/bash
#
# このスクリプトは、例えば '[1,2],[3,4],[5,6]' のような行列形式の文字列を受け取り、
# 行列の転置を同じ形式で出力します。
#
# 使い方: ./transpose.sh '[1,2],[3,4],[5,6]'
#

# 入力チェック
if [ "$#" -ne 1 ]; then
    echo "使い方: $0 '[1,2],[3,4],[5,6]'"
    exit 1
fi

input="$1"

# まず入力から各行を取り出します。
# 入力例: [1,2],[3,4],[5,6]
# 「],[」で区切るため、これをセミコロンに置き換え、さらに残っている [ と ] を削除します
rows=$(echo "$input" | sed 's/],[/;/g; s/\[\|\]//g')

# 各行を配列に格納 (例: "1,2" "3,4" "5,6")
IFS=';' read -r -a rowArray <<<"$rows"

# 行数
numRows=${#rowArray[@]}

# 2次元配列のように処理するために、すべての要素を1次元配列 "matrix" に格納します
# 最初の行を分割して列数を算出します
IFS=',' read -r -a firstCols <<<"${rowArray[0]}"
numCols=${#firstCols[@]}

# 行列の全要素を格納する配列
matrix=()

for row in "${rowArray[@]}"; do
    IFS=',' read -r -a cols <<<"$row"
    # 行によって列数の不一致がある場合はエラー
    if [ "${#cols[@]}" -ne "$numCols" ]; then
        echo "エラー: 行ごとの列数が一致しません."
        exit 1
    fi
    for col in "${cols[@]}"; do
        matrix+=("$col")
    done
done

# 転置行列を作成
# 元の行列は numRows x numCols なので、転置行列は numCols x numRows となる
transposed=""

for (( j=0; j<numCols; j++ )); do
    rowStr="["
    for (( i=0; i<numRows; i++ )); do
        index=$(( i * numCols + j ))
        rowStr+="${matrix[index]}"
        if [ $i -lt $((numRows-1)) ]; then
            rowStr+=","
        fi
    done
    rowStr+="]"
    if [ $j -lt $((numCols-1)) ]; then
        transposed+="${rowStr},"
    else
        transposed+="${rowStr}"
    fi
done

# 結果を出力
echo "$transposed"
echo "行列の転置は魔法のように簡単です!" 

# 使い方の例:
# $ ./transpose.sh '[1,2],[3,4],[5,6]'
# [1,3,5],[2,4,6]
# 行列の転置は魔法のように簡単です!

おお!すごいちゃんとできてますね(笑)

4. Agents SDKを触ってみる

次にお待ちかねのAgents SDKです。こちらは複数のLLMを連携させたり、Python関数を「ツール」として自由に呼び出せるようにしたりと、"自律エージェント"の開発を強力にサポートします。

実はAgents SDKは突然登場したわけではありません。以前OpenAIは「Swarm」という実験的なフレームワークを開発していました。Swarmは教育目的が中心で本番環境には適していませんでしたが、そこから得た知見を活かして生まれたのが現在のAgents SDKです。エージェント、ハンドオフ、ガードレールといった機能を中核に据え、これまでの課題を解決しています:

複数エージェント間の調整が複雑だった → シンプルな連携の仕組みを提供
標準化されたフレームワークがなかった → 一貫した開発手法を確立
本番環境での運用が難しかった → スケーラブルな設計を実現

それでは実際に触ってみましょう。以下のリポジトリとドキュメントを参考にしながら詳細を見てい来たいと思います。

Githubリポジトリ

https://github.com/openai/openai-agents-python

Agents SDK公式ドキュメント

https://openai.github.io/openai-agents-python/

4.1 SDKの内部構造と7つの主要インターフェイス

Agents SDKのコードを詳細に分析すると、その内部構造には7つの主要なインターフェイスが存在することがわかります。これらは単なるAPIの集まりではなく、エージェントの開発に必要な概念を体系化したものです。(参考記事)

  1. エージェント(Agent)インターフェイス:システムプロンプトを受け取り、タスクを実行する主体
  2. ツール(Tool)インターフェイス:エージェントから呼び出される機能モジュール
  3. ガードレール(Guardrail)インターフェイス:入出力を検証・制御する安全装置
  4. ハンドオフ(Handoff)インターフェイス:異なるエージェント間でタスクを委譲する仕組み
  5. モデル(Model)インターフェイス:LLMとの通信を抽象化する層
  6. ランナー(Runner)インターフェイス:エージェント実行の全体フローを制御
  7. トレース(Trace)インターフェイス:実行プロセスを記録・分析する機能

この設計思想は「単一のAIアシスタント」から「協調するAIエージェント群」へとパラダイムシフトを表しています。特に3, 4, 7のインターフェイスは他のフレームワークにはない特徴的な機能です。

4.2 単一エージェントの例

まずは基本的な単一エージェントの例を見てみましょう:

from agents import Agent, Runner

wellness_coach = Agent(
    name="WellnessCoach",
    instructions="""あなたは健康とウェルネスのコーチです。
    バランスの取れた生活習慣、ストレス管理、適切な運動について
    科学的根拠に基づいたアドバイスを提供してください。
    医学的なアドバイスではなく、一般的な健康促進の提案をします。"""
)

result = Runner.run_sync(wellness_coach, "デスクワークが多い仕事でも続けられる簡単なエクササイズはありますか?")
print(result.final_output)
出力例
もちろん、デスクワーク中でもできる簡単なエクササイズはいくつかあります。これらは体の緊張を和らげ、健康促進に役立ちます。

1. **ネックロール**: ゆっくりと首を右回り、次に左回りに回します。数回繰り返して、首の緊張をほぐします。

2. **ショルダーシュラッグ**: 肩を上に持ち上げて数秒間キープし、ゆっくりと下げます。これを10回繰り返します。

3. **シーテッドトーリフト**: 椅子に座ったままつま先を上げ、かかとを床につけたまま数秒キープします。ふくらはぎの筋肉を刺激します。

4. **レッグエクステンション**: 座った状態で片足を前方に伸ばし、膝を伸ばして数秒キープします。10回ずつ行いましょう。

5. **リストストレッチ**: 両手の指を絡めて手のひらを外に向け、そのまま腕を前に伸ばします。手首の緊張を和らげます。

6. **キャットカウストレッチ(座ったまま)**: 背筋を伸ばし、息を吸いながら背中を反らせ、吐きながら丸めます。背骨を柔軟に保ちます。

これらの簡単なエクササイズを取り入れて、定期的に体を動かしましょう。それに加えて、1時間に一度は立ち上がってストレッチをすることを心がけると良いです。
result = await Runner.run(wellness_coach, "デスクワークが多い仕事でも続けられる簡単なエクササイズはありますか?")

この例では、Agentクラスがシステムプロンプト(instructions)を受け取り、エージェントの役割を定義しています。Runnerクラスがエージェントとユーザー入力を受け取り、実行プロセスを管理します。

4.3 ツールの実装とFunction Calling

Agent SDKでは、Python関数を「ツール」として登録し、エージェントに提供できます。これにより、エージェントは自律的に適切なタイミングでツールを呼び出し、外部データの取得や計算などの機能を活用しながら、より高度なタスクを遂行できるようになります。

ツールの種類

Agent SDKでは、3種類のツールをサポートしています:

  1. ホステッドツール(Hosted Tools)(組み込みツール(Built-in Tools)):

    • OpenAIのサーバー上でAIモデルと並行して実行される組み込みツール
    • 例:WebSearchTool(ウェブ検索)、FileSearchTool(ファイル検索)、ComputerTool(コンピュータ操作)
    • 今後 Code Interpreterも提供される予定とのこと。
  2. 関数ツール(Function Tools)

    • 開発者が作成したPython関数をツールとして使用
    • @function_toolデコレータを使って簡単に実装可能
  3. エージェントをツールとして(Agents as Tools)

    • 他のエージェントをツールとして呼び出す機能
    • ハンドオフとは異なり、制御を完全に移管せず、呼び出し元のエージェントが処理を継続

以下では、栄養情報を提供するツールを作成する基本的な例を見ていきます:

from agents import Agent, Runner, function_tool

@function_tool
def get_nutritional_info(food_item: str) -> str:
    """
    指定した食品の栄養情報を提供する
    - 'apple': リンゴの栄養情報
    - 'banana': バナナの栄養情報
    - 'spinach': ほうれん草の栄養情報
    - 'salmon': サーモンの栄養情報
    """
    nutrition_db = {
        'apple': "リンゴ(中サイズ1個): カロリー95kcal、炭水化物25g、食物繊維4g、ビタミンC(推奨量の14%)、カリウム(推奨量の6%)",
        'banana': "バナナ(中サイズ1本): カロリー105kcal、炭水化物27g、食物繊維3g、ビタミンB6(推奨量の20%)、マンガン(推奨量の15%)",
        'spinach': "ほうれん草(生/100g): カロリー23kcal、タンパク質2.9g、鉄分(推奨量の15%)、カルシウム、ビタミンK、ビタミンA",
        'salmon': "サーモン(100g): カロリー208kcal、タンパク質20g、脂質13g(オメガ3脂肪酸を含む)、ビタミンB群、セレン、カリウム"
    }
    
    # 指定された食品の情報があれば返す、なければ一般的な回答
    return nutrition_db.get(food_item.lower(), f"{food_item}の詳細な栄養情報はデータベースにありませんが、一般的な栄養価や調理法についてお答えできます。")

nutrition_agent = Agent(
    name="NutritionAdvisor",
    instructions="""あなたは栄養アドバイザーです。
    健康的な食事や栄養バランスについてアドバイスしてください。
    質問に応じてget_nutritional_infoツールを使って食品の栄養情報を提供します。""",
    tools=[get_nutritional_info]
)

res = Runner.run_sync(nutrition_agent, "りんごの栄養価について教えてください")
print(res.final_output)
出力例
リンゴの栄養価は以下の通りです(中サイズ1個):

- **カロリー**: 95kcal
- **炭水化物**: 25g
- **食物繊維**: 4g
- **ビタミンC**: 推奨量の14%
- **カリウム**: 推奨量の6%

リンゴはビタミンCや食物繊維が豊富で、健康的なおやつとして最適です。

ホステッドツールの活用例:Web検索ツール

OpenAIが提供するホステッドツールのうちWebSearchToolを試してみたいと思います。このツールを使うと、エージェントがリアルタイムにウェブ検索を行い、最新の情報に基づいて回答を生成できます。

以下は、ローカルスポーツニュースを検索して興味深い更新情報を提供するエージェントの例です:

import asyncio
from agents import Agent, Runner, WebSearchTool, trace

async def main():
    # WebSearchToolを使用したエージェントを作成
    news_search_agent = Agent(
        name="スポーツニュースリサーチャー",
        instructions="""あなたは最新スポーツニュース検索の専門家です。
        ユーザーの要求に応じて、WebSearchToolを使用して最新のスポーツニュースを検索し、
        簡潔かつ興味深い形式で情報を提供してください。
        特に地域に関連したニュースが求められた場合は、指定された地域の情報を優先してください。
        """,
        tools=[WebSearchTool(user_location={"type": "approximate", "city": "Tokyo"})],
    )
    
    # トレーシングを使用して実行プロセスを記録
    with trace("スポーツニュース検索ワークフロー"):
        result = await Runner.run(
            news_search_agent,
            "大谷に関する最新情報を1文で教えてください。"
        )
        print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())
参考:Jupyter Notebookでの実装例
# 必要なライブラリをインポート
import nest_asyncio
import asyncio
from agents import Agent, Runner, WebSearchTool, trace

# Jupyter Notebookでasync/awaitを使えるようにする
nest_asyncio.apply()

# WebSearchToolを使用したエージェントを作成
news_search_agent = Agent(
    name="スポーツニュースリサーチャー",
    instructions="""あなたは最新スポーツニュース検索の専門家です。
    ユーザーの要求に応じて、WebSearchToolを使用して最新のスポーツニュースを検索し、
    簡潔かつ興味深い形式で情報を提供してください。
    特に地域に関連したニュースが求められた場合は、指定された地域の情報を優先してください。
    """,
    tools=[WebSearchTool(user_location={"type": "approximate", "city": "Tokyo"})],
)


# 非同期関数を定義
async def search_news():
    # トレーシングを使用して実行プロセスを記録
    with trace("スポーツニュース検索ワークフロー"):
        result = await Runner.run(
            news_search_agent,
            "大谷に関する最新情報を1文で教えてください。",
        )
        return result.final_output


# Jupyter Notebookで非同期関数を実行
result = asyncio.run(search_news())
print(result)
出力例
大谷翔平選手は、3月15日に東京で行われた読売ジャイアンツとのプレシーズン試合で2ランホームランを放ち、ロサンゼルス・ドジャースの5-1の勝利に貢献しました。 ([reuters.com](https://www.reuters.com/sports/spring-training-roundup-grand-slam-helps-astros-rally-past-marlins-2025-03-16/?utm_source=openai))

このエージェントの特徴:

  1. 地域を考慮した検索user_locationパラメータで検索地域を指定できます
  2. トレーシングとの統合trace()を使用して実行プロセスを記録・可視化
  3. 簡潔な出力:複雑なウェブ検索結果を統合し、ユーザーにとって価値のある形で提供

このようなWeb検索機能を持つエージェントは、ニュース要約、市場調査、観光情報提供など、常に最新の情報が必要なシナリオで特に威力を発揮します。

function_toolデコレータの詳細

@function_toolデコレータは、Python関数を簡単にエージェントのツールとして登録するための機能です:

  • 関数名:自動的にツール名として使用されます(オーバーライド可能)
  • docstring:ツールの説明として使用されます
  • 引数の型アノテーション:自動的にツールの入力スキーマとして解析されます
  • 戻り値の型アノテーション:ツールの出力型として使用されます
from agents import function_tool
from typing import Dict, List

@function_tool(name_override="fetch_recipe")  # ツール名をオーバーライド
def get_recipe(dish_name: str, servings: int = 2) -> Dict[str, List[str]]:
    """
    指定した料理のレシピ情報を取得します。
    
    Args:
        dish_name: 料理名
        servings: 何人前か(デフォルト:2人前)
    
    Returns:
        ingredients: 材料リスト
        steps: 調理手順
    """
    # 実際の実装...
    return {
        "ingredients": ["材料1", "材料2", "材料3"],
        "steps": ["手順1", "手順2", "手順3"]
    }

複雑な型の扱い

Pydanticモデルやカスタム型を使用して、より複雑な入力パラメータを定義することもできます:

from pydantic import BaseModel
from typing import List, Optional
from agents import function_tool

class Location(BaseModel):
    latitude: float
    longitude: float
    city: Optional[str] = None

@function_tool
def get_weather_forecast(location: Location, days: int = 3) -> List[str]:
    """
    指定した場所の天気予報を取得します。
    
    Args:
        location: 緯度・経度を含む位置情報
        days: 予報を取得する日数(デフォルト:3日間)
    """
    # 実際には天気APIと連携
    return [
        f"{location.city or '指定位置'}の1日目の天気: 晴れ",
        f"{location.city or '指定位置'}の2日目の天気: 曇り",
        f"{location.city or '指定位置'}の3日目の天気: 雨"
    ]

カスタム関数ツールの手動作成

より細かい制御が必要な場合は、FunctionToolクラスを直接使用してツールを定義することもできます:

from agents import FunctionTool, RunContextWrapper
from pydantic import BaseModel

class QueryParams(BaseModel):
    search_term: str
    max_results: int = 5

async def search_database(ctx: RunContextWrapper, args_json: str) -> str:
    """データベース検索を実行するカスタムツール"""
    # JSONをPydanticモデルに変換
    params = QueryParams.model_validate_json(args_json)
    
    # 実際のデータベース検索ロジック
    results = [f"結果{i}: {params.search_term}に関する情報" for i in range(params.max_results)]
    
    return "\n".join(results)

# ツールを手動で作成
db_search_tool = FunctionTool(
    name="search_database",
    description="内部データベースから情報を検索します",
    params_json_schema=QueryParams.model_json_schema(),
    on_invoke_tool=search_database
)

エージェントをツールとして使用

他のエージェントをツールとして使用することで、メインエージェントが処理を委譲せず、必要に応じて専門エージェントの能力を活用できます:

from agents import Agent

# 翻訳を担当する専門エージェント
translation_agent = Agent(
    name="TranslationAgent",
    instructions="ユーザーの文章を指定された言語に翻訳します。"
)

# メインエージェント
assistant_agent = Agent(
    name="AssistantAgent",
    instructions="""ユーザーの質問に答え、必要に応じて翻訳ツールを使用してください。""",
    tools=[
        translation_agent.as_tool(
            tool_name="translate_text",
            tool_description="テキストを別の言語に翻訳します。"
        )
    ]
)

エラー処理の戦略

ツールの実行中にエラーが発生した場合の処理方法をカスタマイズできます:

from agents import function_tool

# デフォルトのエラーハンドラー:エラーメッセージがLLMに返される
@function_tool
def risky_function(param: str) -> str:
    """実行中にエラーが発生する可能性のある関数"""
    if param == "trigger_error":
        raise ValueError("パラメータが無効です")
    return f"パラメータ {param} での処理が成功しました"

# カスタムエラーハンドラー
def custom_error_handler(exception):
    return f"カスタムエラー: {str(exception)}。別のパラメータを試してください。"

@function_tool(failure_error_function=custom_error_handler)
def another_function(param: str) -> str:
    """カスタムエラーハンドリングを使用する関数"""
    if param == "bad_input":
        raise ValueError("不正な入力")
    return "正常に処理されました"

# エラーを呼び出し元に伝播させる
@function_tool(failure_error_function=None)
def propagating_function(param: str) -> str:
    """エラーを呼び出し元に伝播させる関数"""
    # エラーが発生すると、エージェント実行ではなくPythonコードレベルで例外が発生
    raise NotImplementedError("この機能はまだ実装されていません")

ツール使用のベストプラクティス

  1. 明確なdocstringを書く:LLMがツールの目的と使用方法を理解するために重要です。

  2. 適切な型アノテーションを使用:正確なスキーマが生成され、エラーを減らせます。

  3. ツールは単一責任の原則に従う:各ツールは明確に定義された1つのタスクを担当させます。

  4. エラー処理を適切に行う:特に外部APIやデータベースと連携する場合、堅牢なエラー処理が重要です。

  5. ログ記録と監視:ツールの使用状況や性能を監視し、継続的に改善します。

  6. セキュリティ考慮事項:ユーザー入力が直接関数に渡されるため、適切な検証とサニタイズが必要です。

ツールを使いこなすことで、エージェントの能力を大幅に拡張できます。基本的なデータ取得から、複雑な計算、外部APIとの連携まで、様々なタスクをエージェントに自律的に実行させることが可能になります。また、複数のツールを組み合わせることで、より複雑で高度なワークフローを実現できます。

4.4 複数エージェントの連携(ハンドオフ)

Agent SDKの最も特徴的な機能の一つがハンドオフです。これは、専門性や役割の異なるエージェント間でタスクの「バトンタッチ」を行う仕組みであり、複雑な問題解決や特化したサービス提供を実現します。

ハンドオフの仕組みと基本概念

以下の図は、ハンドオフの内部機構と処理の流れを示しています。特に注目すべきは、トリアージエージェントからの制御の完全な移管と、会話履歴がどのように次のエージェントに引き継がれるかという点です。

ハンドオフでは、あるエージェント(「トリアージエージェント」)が質問内容を分析し、適切な専門エージェントにタスクを委譲します。このプロセスでは、単に質問を転送するだけでなく、会話の文脈や履歴情報も適切に引き継がれます。

ハンドオフは内部的にはLLMに「ツール」として表示されます。例えば、「RefundAgent」という名前のエージェントにハンドオフする場合、ツール名は自動的にtransfer_to_refund_agentのような形式で提供されます。

ハンドオフの実装例

以下は、健康相談に関するエージェントシステムの例です。ユーザーの質問内容に応じて、メンタルヘルス専門家または身体的健康専門家にハンドオフします:

from agents import Agent, Runner

mental_health_specialist = Agent(
    name="MentalHealthSpecialist",
    instructions="""
    あなたはメンタルヘルスの専門家です。
    ストレス管理、マインドフルネス、メンタルウェルビーイングについて
    エビデンスに基づいた情報を提供してください。
    """
)

physical_health_specialist = Agent(
    name="PhysicalHealthSpecialist",
    instructions="""
    あなたは身体的健康の専門家です。
    運動、食事、睡眠など身体的な健康に関する質問に
    科学的根拠に基づいて回答してください。
    """
)

wellness_router = Agent(
    name="WellnessRouter",
    instructions="""
    ユーザーの質問内容からメンタルヘルス関連か身体的健康関連かを判断し、
    適切な専門家にハンドオフしてください。
    質問が両方の領域にまたがる場合は、主な焦点がどちらかを判断してください。
    """,
    handoffs=[mental_health_specialist, physical_health_specialist]
)

# メンタルヘルスに関する質問
res1 = Runner.run_sync(wellness_router, "仕事のストレスで夜眠れません。何か良い対処法はありますか?")
print("【メンタルヘルス回答】", res1.final_output)

# 身体的健康に関する質問
res2 = Runner.run_sync(wellness_router, "デスクワークが多いですが、どのような運動をすべきですか?")
print("【身体的健康回答】", res2.final_output)
出力例
【メンタルヘルス回答】 仕事のストレスで眠れないときには、次の対処法を試してみてください。

1. **就寝前のリラックスルーチン**:
   - 就寝30分から1時間前にリラックスできる活動(読書、軽いストレッチ、瞑想)を取り入れましょう。

2. **定期的な運動**:
   - デイリーの運動はストレスを軽減し、睡眠の質を改善します。できれば日中に行うと良いです。

3. **マインドフルネス瞑想**:
   - 簡単な呼吸法やボディスキャンなどのマインドフルネス瞑想を行うことで、心を落ち着けることができます。

4. **カフェインとアルコールの摂取を控える**:
   - 就寝前の数時間はカフェインやアルコールを避けると、睡眠の質を向上させることができます。

5. **環境の整備**:
   - クールで静かな寝室を保ち、電子機器を避け、心地よい寝具を使用することで、睡眠環境を最適化しましょう。

6. **スケジュール設定**:
   - 毎日同じ時間に寝起きすることで体内時計を調整し、眠りやすくします。

7. **ストレス管理テクニック**:
   - 計画を立てる、タスクを優先順位で整理する、時間管理を工夫するなどの方法で、仕事のストレスを軽減できます。

8. **プロフェッショナルなサポートを検討**:
   - 必要であれば、カウンセラーやセラピストに相談することも有益です。

心身の健康を大切にしながら、これらの方法を試してみてください。
【身体的健康回答】 デスクワークが多い場合、以下のような運動が効果的です。

1. **ストレッチ**:
   - 腕や肩、首などを軽く伸ばすストレッチを1時間に1回程度行いましょう。これにより血流が促進され、筋肉の緊張が軽減されます。

2. **ウォーキング**:
   - 定期的に立ち上がって短時間のウォーキングを取り入れることが重要です。例えば、昼休みに散歩をする、電話をしながら歩くなど。

3. **体幹トレーニング**:
   - プランクやブリッジなどの体幹を鍛えるトレーニングは、長時間のデスクワークによる姿勢の悪化を防ぐのに役立ちます。

4. **エクササイズバンド**:
   - チューブやバンドを使っての軽いエクササイズは、デスク周りでも行いやすく、全身の筋力を向上させます。

5. **バランスボール**:
   - バランスボールを椅子代わりに使って座ることで、自然に体幹を鍛えることができます。

これらの運動を日常生活に取り入れることで、デスクワークによる身体への負担を軽減できます。また、こまめに姿勢を変えることや十分な水分補給も重要です。

ハンドオフのカスタマイズオプション

ハンドオフは単純に実装できる一方で、様々な高度なカスタマイズも可能です:

from agents import Agent, handoff, RunContextWrapper
from pydantic import BaseModel

# ハンドオフ時に渡すデータの型を定義
class EscalationData(BaseModel):
    reason: str
    priority: int

# ハンドオフ発生時に実行されるコールバック関数
async def on_handoff_callback(ctx: RunContextWrapper, input_data: EscalationData):
    print(f"エスカレーション理由: {input_data.reason}")
    print(f"優先度: {input_data.priority}")
    # ここでログ記録や通知送信などの処理を追加できる

# エスカレーション専門のエージェント
escalation_agent = Agent(
    name="EscalationSpecialist",
    instructions="複雑な問題をより詳細に分析し、解決策を提案します。"
)

# カスタムハンドオフを作成
custom_handoff = handoff(
    agent=escalation_agent,
    on_handoff=on_handoff_callback,
    input_type=EscalationData,
    tool_name_override="escalate_issue",
    tool_description_override="複雑な問題を専門チームにエスカレーションします。理由と優先度を指定してください。"
)

入力フィルターの活用

ハンドオフでは、前の会話履歴をどのように受け渡すかをカスタマイズすることも可能です。例えば、特定の情報を除外したり、重要な部分のみを抽出したりできます:

from agents import handoff
from agents.extensions import handoff_filters

# ツール呼び出しの履歴を削除するフィルターを適用
filtered_handoff = handoff(
    agent=specialist_agent,
    input_filter=handoff_filters.remove_all_tools
)

# 最新の会話のみを渡すカスタムフィルター
def last_two_messages_filter(input_data):
    # 最新の2メッセージのみを保持
    if len(input_data.messages) > 2:
        input_data.messages = input_data.messages[-2:]
    return input_data

custom_filtered_handoff = handoff(
    agent=another_agent,
    input_filter=last_two_messages_filter
)

ツールとハンドオフの違い

Agent SDKでは、「ツール」と「ハンドオフ」という二つの異なる概念がありますが、その違いを理解することが重要です:

ツール:

  • エージェント内の「機能実行用モジュール」と考えられる
  • 特定のタスク(データ取得、計算など)を実行し結果を即座に返す
  • エージェントの制御下で動作し、外部リソースへのアクセスを提供
  • エージェントは複数のツールを呼び出しながら、自身が主体となって処理を続行する

ハンドオフ:

  • エージェント間の「処理の委譲」を実現する仕組み
  • 会話の流れや文脈を含めて別のエージェントに処理を移管
  • 異なる専門知識や役割を持つエージェント間の協調を促進
  • タスクの流れ全体を別のエージェントに移し、委譲元のエージェントは処理から離れる

ハンドオフのベストプラクティス

  1. 明確な専門分野の定義: 各エージェントの役割と得意分野を明確に定義し、適切なハンドオフが行われるようにします。

  2. プロンプトの最適化: ハンドオフをスムーズに行うためのプロンプトを工夫します。公式にはハンドオフ指示用のプロンプトテンプレートも提供されています:

    from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
    
    agent = Agent(
        name="RouterAgent",
        instructions=f"""{RECOMMENDED_PROMPT_PREFIX}
        あなたは質問内容を分析し、適切な専門家にハンドオフするルーターです。..."""
    )
    
  3. エージェントのクローン活用: 類似した役割のエージェントを効率的に作成するには、基本エージェントをクローンして設定を一部変更する方法が有効です:

    base_specialist = Agent(
        name="Specialist",
        instructions="専門的な情報を提供します",
        model="gpt-4o"
    )
    
    finance_specialist = base_specialist.clone(
        name="FinanceSpecialist",
        instructions="財務に関する専門的な情報を提供します"
    )
    
  4. 継続的な会話設計: ハンドオフ後も継続的な会話が必要な場合は、適切な会話コンテキストの保持と受け渡しを考慮します。

ハンドオフ機能により、複雑な業務フローや専門分野ごとのエージェント連携が可能になり、特定の領域に特化した高品質な回答をユーザーに提供できます。これは、カスタマーサポート、医療相談、教育支援、法律相談など多岐にわたる分野で活用できる強力な機能です。

4.5 ガードレール機能:安全性と制御

ガードレールは、AI エージェントシステムにおける「安全装置」として機能する重要な機能です。ユーザー入力やエージェント出力を検証し、不適切なリクエストの処理を防止したり、出力内容が特定の基準を満たしているかを確認したりできます。これにより、システムの安全性、コスト効率、およびユーザー体験の品質を向上させることができます。

ガードレールの種類と仕組み

ガードレールには主に2種類あります:

  1. 入力ガードレール: ユーザーからの最初の入力を検証します。不適切な質問や要求を検出し、エージェントがそれらを処理する前に遮断します。
  2. 出力ガードレール: エージェントが生成した最終出力を検証します。特定の条件に合わない回答を検出し、ユーザーに送信される前に修正または遮断します。

これらは異なるタイミングで動作しますが、どちらも「tripwire(トリップワイヤー)」メカニズムを使用して問題を検出した際に処理を停止します。

以下の図は、入力ガードレールと出力ガードレールの内部処理フローと、それらがどのようにエージェントの実行を制御するかを示しています。

ガードレールは3つの主要なステップで動作します:

  1. 検証用の関数を実行して入力または出力を評価
  2. 検証が失敗した場合に通知する GuardrailFunctionOutput を生成(tripwire_triggered=True)
  3. ガードレールがトリガーされた場合、SDKは即座にガードレール例外(InputGuardrailTripwireTriggered または OutputGuardrailTripwireTriggered)を発生させる

入力ガードレールの実装例

以下は、プライバシー関連の個人情報リクエストを検出して遮断する入力ガードレールの例です:

from pydantic import BaseModel
from agents import (
    Agent,
    GuardrailFunctionOutput,
    InputGuardrailTripwireTriggered,
    RunContextWrapper,
    Runner,
    input_guardrail,
)

# ガードレールの出力タイプを定義
class PersonalDataRequest(BaseModel):
    contains_personal_data: bool
    reasoning: str

# 個人情報リクエストを検出するためのガードレールエージェント
privacy_checker = Agent(
    name="PrivacyChecker",
    instructions="""
    ユーザーのリクエストが個人情報(氏名、住所、電話番号、クレジットカード情報など)を
    求めるものか判断してください。センシティブな個人情報の要求は検出してフラグを立ててください。
    """,
    output_type=PersonalDataRequest
)

# 入力ガードレール関数の定義
@input_guardrail
async def privacy_guardrail(
    ctx: RunContextWrapper, agent: Agent, input_data: str
) -> GuardrailFunctionOutput:
    # ガードレールエージェントを実行して入力を分析
    result = await Runner.run(privacy_checker, input_data, context=ctx.context)
    final_output = result.final_output_as(PersonalDataRequest)
    
    # GuardrailFunctionOutputを返し、トリップワイヤーがトリガーされるかどうかを示す
    return GuardrailFunctionOutput(
        output_info=final_output,
        tripwire_triggered=final_output.contains_personal_data,
    )

# メインの安全なアシスタントエージェント
secure_assistant = Agent(
    name="SecureAssistant",
    instructions="あなたは一般的な質問に答えるアシスタントです。個人情報に関する質問には回答しないでください。",
    input_guardrails=[privacy_guardrail],
)

# 使用例
async def main():
    print("安全な質問を試します...")
    try:
        # 通常の質問 - ガードレールは作動しない
        result = await Runner.run(secure_assistant, "東京の人口はどれくらいですか?")
        print(f"回答: {result.final_output}")
        
        # プライバシーに関わる質問 - ガードレールが作動する
        print("\n個人情報に関する質問を試します...")
        result = await Runner.run(secure_assistant, "あなたのクレジットカード情報を教えてください")
        print(f"回答: {result.final_output}")  # この行は実行されないはず
        
    except InputGuardrailTripwireTriggered:
        print("申し訳ありませんが、個人情報に関する質問にはお答えできません。プライバシー保護のため、別の質問をお願いします。")

# asyncio.run(main()) で実行
出力例
安全な質問を試します...
回答: 東京の人口は約1,400万人です。ただし、正確な数字は最新の統計を確認する必要があります。

個人情報に関する質問を試します...
ガードレール発動: 申し訳ありませんが、個人情報に関する質問にはお答えできません。

出力ガードレールの実装例

出力ガードレールは、エージェントの回答内容を検証し、特定の条件に合わない場合は出力を遮断します。以下は、出力に電話番号が含まれていないかをチェックする例です:

from pydantic import BaseModel, Field
from agents import (
    Agent,
    GuardrailFunctionOutput,
    OutputGuardrailTripwireTriggered,
    RunContextWrapper,
    Runner,
    output_guardrail,
)

# エージェントの出力タイプを定義
class MessageOutput(BaseModel):
    reasoning: str = Field(description="回答に至った思考プロセス")
    response: str = Field(description="ユーザーへの回答")

# 出力ガードレール関数の定義
@output_guardrail
async def sensitive_data_check(
    ctx: RunContextWrapper, agent: Agent, output: MessageOutput
) -> GuardrailFunctionOutput:
    # 電話番号のパターンを検出(例として「650」を含む場合をチェック)
    phone_number_in_response = "650" in output.response
    phone_number_in_reasoning = "650" in output.reasoning
    
    return GuardrailFunctionOutput(
        output_info={
            "phone_number_in_response": phone_number_in_response,
            "phone_number_in_reasoning": phone_number_in_reasoning,
        },
        tripwire_triggered=phone_number_in_response or phone_number_in_reasoning,
    )

# ガードレール付きのアシスタントエージェント
data_safe_agent = Agent(
    name="DataSafeAssistant",
    instructions="あなたは役立つアシスタントです。ただし、個人情報を出力に含めないでください。",
    output_type=MessageOutput,
    output_guardrails=[sensitive_data_check],
)

# 使用例
async def main():
    # 通常の質問 - ガードレールは作動しない
    print("通常の質問を試します...")
    result = await Runner.run(data_safe_agent, "カリフォルニア州の州都はどこですか?")
    print(f"回答: {result.final_output.response}")
    
    # 電話番号を含む可能性のある質問 - ガードレールが作動する可能性がある
    print("\n電話番号を含む質問を試します...")
    try:
        result = await Runner.run(
            data_safe_agent, "私の電話番号は650-123-4567です。どこに住んでいると思いますか?"
        )
        print(f"回答: {result.final_output.response}")
    except OutputGuardrailTripwireTriggered as e:
        print("出力ガードレールがトリガーされました。センシティブデータが検出されました。")
        print(f"詳細情報: {e.guardrail_result.output.output_info}")

# asyncio.run(main()) で実行
出力例
通常の質問を試します...
回答: カリフォルニア州の州都はサクラメントです。

電話番号を含む質問を試します...
出力ガードレールがトリガーされました。センシティブデータが検出されました。
詳細情報: {'phone_number_in_response': True, 'phone_number_in_reasoning': False}

ガードレールのベストプラクティス

  1. 特定目的の検証: 各エージェントの目的に合わせた具体的なガードレールを設計しましょう。
  2. 明確なメッセージ: ユーザーのリクエストが遮断された場合は、その理由を明確に伝えましょう。
  3. 効率性の考慮: 高速で軽量なガードレールを使用して、コストの高いモデル実行を避けましょう。
  4. 段階的な検証: 複数のガードレールを組み合わせて、異なるタイプの問題に対応しましょう。
  5. モニタリングとデバッグ: ガードレールの動作を定期的に確認し、必要に応じて調整しましょう。

ガードレールを適切に実装することで、AIエージェントシステムはより安全に、より効率的に、そしてユーザーにとってより価値のある形で動作することができます。

4.6 トレーシング:デバッグと分析

Agent SDKではトレーシング機能が標準で提供されており、エージェントの実行過程を詳細に記録・分析できます。この機能を使えば、LLMの生成プロセス、ツール呼び出し、ハンドオフのフロー、ガードレールのチェックなど、エージェント実行の全てのステップを可視化できるため、複雑なエージェントシステムのデバッグや性能最適化に非常に役立ちます。

トレーシングの基本概念

トレーシングは次の2つの主要な概念から構成されています:

  1. トレース (Trace)

    • 「ワークフロー」や「エンドツーエンドの操作」を表す単位
    • 複数のスパンから構成される
    • 主な属性:
      • workflow_name:ワークフローの論理名(例:「顧客サポートフロー」)
      • trace_id:一意の識別子(trace_<32文字の英数字>形式)
      • group_id:複数のトレースをリンクするためのオプションID(例:会話スレッドID)
      • metadata:トレースに関する追加情報
  2. スパン (Span)

    • トレース内の個別の操作(LLM生成、ツール呼び出しなど)
    • 開始時間と終了時間を持つ
    • 親子関係を持ち、ネスト構造を形成できる
    • 主な属性:
      • 開始・終了タイムスタンプ
      • trace_id:所属するトレースへの参照
      • parent_id:親スパンへの参照(ネスト構造用)
      • span_data:操作の詳細情報

デフォルトのトレーシング

Agent SDKでは、デフォルトで以下の要素がトレースされます:

  • Runner.run()/run_sync()/run_streamed() の実行全体
  • エージェントの実行(agent_span()でラップ)
  • LLMの生成プロセス(generation_span()
  • 関数ツールの呼び出し(function_span()
  • ガードレールのチェック(guardrail_span()
  • ハンドオフ操作(handoff_span()

トレーシングはデフォルトで有効になっていますが、必要に応じて無効にすることもできます:

# 環境変数で全体的に無効化
# OPENAI_AGENTS_DISABLE_TRACING=1

# または特定の実行のみ無効化
from agents import Runner, RunConfig

result = await Runner.run(
    agent, 
    "質問内容", 
    run_config=RunConfig(tracing_disabled=True)
)

トレースの作成と使用

基本的なトレーシングの例
from agents import Agent, Runner, RunConfig, gen_trace_id
from openai import OpenAI

client = OpenAI()

# トレーシング設定(RunConfigを使用)
run_config = RunConfig(
    tracing_disabled=False,  # トレーシングを有効化(デフォルトでも有効)
    trace_include_sensitive_data=True,  # センシティブデータも含める
    workflow_name="研究調査プロセス",  # ワークフロー名を指定
    group_id="user_session_123",  # グループIDを指定
)

# エージェント定義
research_agent = Agent(
    name="ResearchAssistant", instructions="あなたは研究助手です。学術的なトピックについて情報を提供してください。"
)

# 実行(トレース情報がOpenAIプラットフォームに送信される)
result = await Runner.run(
    research_agent, "量子コンピューティングの最近の進展について教えてください", run_config=run_config
)

print(f"トレースID: {gen_trace_id()}")  # トレースIDが表示される
print(result.final_output)
出力例
トレースID: trace_*********
量子コンピューティングの最近の進展は非常に興味深いもので、いくつかの重要なポイントがあります。

1. **量子超越性の達成**: 2019年にGoogleが量子超越性を達成したと報告し、特定の計算を古典的なコンピュータよりも高速に実行できることを示しました。これに続き、他の企業や研究機関も同様の成果を目指しています。

2. **ハードウェアの改善**: IBM、Rigetti、IonQなど、多くの企業が安定した量子ビット(キュービット)の開発を進めています。特にエラーレートの低減やキュービット数の増加が注目されています。

3. **誤り訂正技術**: 量子誤り訂正の手法が進化しており、エラー耐性を高めながら大規模な量子コンピュータの実現に向けた研究が進行中です。

4. **応用研究の進展**: 量子コンピューティングを使った新しいアルゴリズムや応用分野の研究が進んでいます。特に量子化学や機械学習への応用が注目されています。

5. **量子ネットワークの開発**: 量子通信とネットワーク技術の発展も進んでおり、量子安全通信プロトコルの開発や量子インターネットの基礎技術に繋がっています。

これらの進展はまだ初期段階ですが、今後さらに多くの可能性を開くと期待されています。

実行後、OpenAIのプラットフォーム上(https://platform.openai.com/traces/trace_*********)でトレースを確認できます。

複数のエージェント実行を一つのトレースにまとめる

複数のRunner.run()呼び出しを単一のトレースとしてグループ化したい場合は、trace()関数をコンテキストマネージャーとして使用できます:

from agents import Agent, Runner, trace
import asyncio

async def main():
    joke_agent = Agent(
        name="JokeGenerator",
        instructions="面白いジョークを考えてください。"
    )

    # 複数のエージェント実行を単一のワークフローとしてトレース
    with trace("ジョークワークフロー", group_id="会話123"):
        # 1回目の実行
        joke_result = await Runner.run(joke_agent, "短いジョークを考えてください")
        
        # 2回目の実行(前回の結果を利用)
        rating_result = await Runner.run(
            joke_agent, 
            f"このジョークを5点満点で評価してください: {joke_result.final_output}"
        )
        
        print(f"ジョーク: {joke_result.final_output}")
        print(f"評価: {rating_result.final_output}")

# asyncio.run(main())

このコードでは、「ジョークワークフロー」という名前の単一トレースの中に、2つのエージェント実行のスパンが含まれます。group_idを使用することで、異なる会話セッションのトレースをグループ化できます。

カスタムスパンとセンシティブデータ

カスタムスパンの作成

標準のスパン以外に、特定の操作をトラッキングするためのカスタムスパンも作成できます:

from agents import custom_span

# データベースクエリのパフォーマンスを追跡
with custom_span("データベースクエリ"):
    # データベース操作
    results = database.execute_query("SELECT * FROM users")
    
    # 結果処理
    processed_data = process_results(results)
センシティブデータの扱い

トレースには、LLMの入出力や関数呼び出しの詳細など、センシティブなデータが含まれる可能性があります。特にプライバシーやコンプライアンスが重要な環境では、センシティブデータのトレースを無効にすることができます:

from agents import RunConfig, Runner

await Runner.run(
    agent=my_agent,
    input="ユーザーの個人情報を含む質問",
    run_config=RunConfig(trace_include_sensitive_data=False)
)

カスタムトレースプロセッサーの活用

デフォルトでは、トレース情報はOpenAIのバックエンドに送信されますが、独自のトレースプロセッサーを追加・置換することもできます:

トレースプロセッサーの追加(OpenAIに加えて使用)
from agents import add_trace_processor, TraceProcessor, Span, Trace

class MyCustomProcessor(TraceProcessor):
    async def process_span(self, span: Span):
        print(f"スパン処理: {span.name}, {span.started_at} - {span.ended_at}")
        # ログやモニタリングシステムに送信することも可能

    async def process_trace(self, trace: Trace):
        print(f"トレース処理: {trace.workflow_name}, ID: {trace.trace_id}")
        # トレース完了時の集計や通知処理など

# OpenAIのバックエンドに加えて独自のプロセッサーを使用
add_trace_processor(MyCustomProcessor())
デフォルトプロセッサーの置換
from agents import set_trace_processors

# OpenAIバックエンドの代わりに独自のプロセッサーのみを使用
set_trace_processors([MyCustomProcessor()])

利用可能な外部トレースプロセッサーには、以下のようなものがあります:

  • Braintrust
  • Pydantic Logfire
  • AgentOps
  • Scorecard
  • Keywords AI

トレースの開始と終了

trace関数を使用するとき、2つの方法があります:

  1. コンテキストマネージャーとして使用(推奨)

    with trace("ワークフロー名") as my_trace:
        # このブロック内の処理がトレースされる
        result = await Runner.run(agent, "入力")
    
  2. 手動で開始と終了を管理

    my_trace = trace("ワークフロー名")
    my_trace.start(mark_as_current=True)
    
    # 処理を実行
    result = await Runner.run(agent, "入力")
    
    # トレースを終了
    my_trace.finish(reset_current=True)
    

現在のトレースはPythonのcontextvarを使って追跡されているため、非同期処理でも自動的に機能します。

トレーシングのベストプラクティス

  1. 開発中はトレーシングを常に有効に:複雑なエージェントフローの問題解決が容易になります。

  2. 明確なワークフロー名を使用workflow_nameに意味のある名前を付けることで、後から検索やフィルタリングが簡単になります。

  3. 会話やセッションにはgroup_idを活用:関連するトレースをグループ化することで、ユーザーの体験全体を追跡できます。

  4. カスタムスパンで重要なイベントを追跡:データベースクエリや外部APIコールなど、パフォーマンスに影響する操作を個別に計測します。

  5. センシティブデータに注意:プロダクション環境では必要に応じてtrace_include_sensitive_data=Falseを設定します。

  6. トレースデータを定期的に分析:パフォーマンスのボトルネックやエラーパターンを特定し、継続的に改善します。

  7. 高度な分析には外部ツールを検討:専門のトレース分析ツールを使用することで、より深いインサイトを得られます。

トレーシングを効果的に活用することで、複雑なエージェントシステムの開発・デバッグ・最適化プロセスが大幅に効率化され、より堅牢で信頼性の高いAIアプリケーションを構築できます。

4.7 コンピュータ操作ツール

Agent SDKと組み合わせて利用できる特殊なツールとして、コンピュータ操作があります。このツールは、ブラウザ操作を指示できるプロダクト「Operator」と同様にCUA (Computer-Using Agent) モデルを利用しています。マウス・キーボードの操作を実行できるので、ブラウザ操作だけでなくOS上の任意の操作をエージェントに依頼することができます。

初期リリース時点ではTier 3〜5の開発者の方にのみ提供されていますが、使い方は簡単です:

git clone https://github.com/openai/openai-cua-sample-app.git
cd openai-cua-sample-app
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
export OPENAI_API_KEY=sk-proj-...
python cli.py --computer local-playwright

このツールを実行すると、Chromiumブラウザが起動し、LLMエージェントがあなたの指示に従って情報を検索してくれます。例えば「今日のニュースで最も重要なトピックを3つ教えて」と質問すると、ニュース記事を自動的に閲覧・要約してくれます。Playwright、Browserbase、Scrapybaraなど複数の実装例が提供されています。

5. 実装パターンとユースケース

Agents SDKとResponses APIを組み合わせることで、様々な実装パターンを実現できます。ここでは代表的なパターンとユースケースを紹介します。

5.1 専門知識エージェントネットワーク

複数の専門エージェントを連携させ、各分野のエキスパートがそれぞれの得意分野で回答するシステムを構築できます。

# 金融エージェント、法律エージェント、技術エージェントなどを作成
finance_agent = Agent(name="FinanceExpert", instructions="...")
legal_agent = Agent(name="LegalExpert", instructions="...")
tech_agent = Agent(name="TechExpert", instructions="...")

# ルーターエージェントが質問内容を分析し適切なエージェントにハンドオフ
router_agent = Agent(
    name="ExpertRouter",
    instructions="質問内容を分析し、最適な専門家にハンドオフしてください。",
    handoffs=[finance_agent, legal_agent, tech_agent]
)

このパターンは、コールセンター、カスタマーサポート、社内ヘルプデスクなどに適しています。

5.2 段階的処理フロー

複数のエージェントが異なる処理ステップを担当し、順番に処理を行うワークフローを構築できます。

# データ収集、分析、レポート生成の各段階を担当するエージェント
data_collector = Agent(name="DataCollector", instructions="...")
data_analyzer = Agent(name="DataAnalyzer", instructions="...")
report_generator = Agent(name="ReportGenerator", instructions="...")

# 調査プロセスを管理するオーケストレーターエージェント
orchestrator = Agent(
    name="ResearchOrchestrator",
    instructions="""調査プロセスを管理します。まずデータ収集を行い、
    次にデータ分析、最後にレポート生成を行います。""",
    handoffs=[data_collector, data_analyzer, report_generator]
)

このパターンは、研究調査、データ分析プロジェクト、文書作成ワークフローなどに適しています。

5.3 安全なエージェントシステム

ガードレールを使用して、安全性と適切性を確保したエージェントシステムを構築できます。

# 複数のガードレールを組み合わせる
ethical_agent = Agent(
    name="EthicalAssistant",
    instructions="倫理的で安全な情報提供を行うアシスタントです。",
    input_guardrails=[
        InputGuardrail(guardrail_function=harmful_content_filter),
        InputGuardrail(guardrail_function=personal_data_detector)
    ],
    output_guardrails=[
        OutputGuardrail(guardrail_function=content_quality_checker),
        OutputGuardrail(guardrail_function=bias_detector)
    ]
)

このパターンは、教育アプリケーション、公共サービス、医療情報システムなどの安全性が重要な場面で役立ちます。

6. 実践例:金融アナリストアシスタントの構築

理論を学んだところで、Agents SDKを使った実践的なマルチエージェントシステムの構築例を見ていきましょう。ここでは、株式市場のデータを取得し、最新のニュースと組み合わせて包括的な投資分析を行う「金融アナリストアシスタント」を構築します。

このシステムでは複数のエージェントが連携し、それぞれが専門的な役割を担います:

  1. コーディネーターエージェント:全体の調整役
  2. データ取得エージェント:株価や財務情報の取得を担当
  3. ニュース検索エージェント:市場関連のニュースを検索
  4. 分析エージェント:収集したデータを統合し、洞察と提案を生成

6.1 環境設定と必要なライブラリ

まずは必要なライブラリをインストールし、環境変数を設定します。

pip install openai-agents
import os
import asyncio
from agents import (
    Agent, 
    Runner, 
    WebSearchTool,
    function_tool, 
    InputGuardrail, 
    GuardrailFunctionOutput, 
    trace
)

# APIキーの設定
os.environ["OPENAI_API_KEY"] = "あなたのOpenAI APIキー"

注意:実際の本番環境ではAPIキーは環境変数や安全な方法で管理すべきです。

6.2 ホステッドツールを活用したデータ検索

外部金融APIと連携する代わりに、デモとしてOpenAIのWebSearchToolを活用します。これにより、設定の手間を省きながらも実用的なデモが可能になります。

# WebSearchToolのインスタンスを作成
web_search = WebSearchTool(
    # オプションで日本からの検索としてロケーションを設定
    user_location={"type": "approximate", "city": "Tokyo"}
)

# 金融データの検索をWebSearchToolでラップした関数ツール
@function_tool
def search_stock_data(symbol: str) -> str:
    """
    指定した銘柄の株価、財務情報、企業概要などを検索します。
    
    Args:
        symbol: 銘柄コード
    """
    search_query = f"{symbol} 株価 財務情報 最新 企業概要"
    # 実際にはここでWebSearchToolを使うコードが入りますが、
    # このデモでは省略し、エージェント自体がWebSearchToolを直接使用します
    return f"{symbol}の株価と財務情報を検索しました。"

# 市場ニュースの検索をWebSearchToolでラップした関数ツール
@function_tool
def search_market_news(company: str, days: int = 7) -> str:
    """
    指定した企業に関する最新の市場ニュースを検索します。
    
    Args:
        company: 企業名または銘柄コード
        days: 何日前までのニュースを対象とするか
    """
    search_query = f"{company} 市場ニュース 株価 最新 {days}日"
    # 実際にはここでWebSearchToolを使うコードが入りますが、
    # このデモでは省略し、エージェント自体がWebSearchToolを直接使用します
    return f"{company}に関する最新の市場ニュースを検索しました。"

実装メモ:実際の開発では、専用の金融APIと連携することで、より正確なデータを取得できます。このデモでは説明を簡略化するためにWebSearchToolを使用しています。

6.3 金融関連クエリを検証するガードレール

金融関連のクエリのみを処理するためのガードレールを実装します。

async def finance_guardrail(ctx, agent, input_data: str) -> GuardrailFunctionOutput:
    """
    入力が金融関連かどうかを検証するガードレール
    """
    # 金融関連キーワード
    finance_keywords = [
        "株", "投資", "銘柄", "株価", "企業", "市場", "金融", 
        "収益", "財務", "株式", "stock", "investment", "financial"
    ]
    
    # いずれかのキーワードが含まれているか確認
    is_finance_related = any(keyword in input_data.lower() for keyword in finance_keywords)
    
    return GuardrailFunctionOutput(
        output_info={"is_finance_related": is_finance_related},
        tripwire_triggered=not is_finance_related  # 金融関連でない場合にtripwireを発動
    )

6.4 専門エージェントの定義

各役割を担当する専門エージェントを定義します。Web検索ツールを直接利用することで、外部APIを使わずにデータを取得できます。

# 1. データ取得エージェント
data_agent = Agent(
    name="データ取得エージェント",
    instructions="""
    あなたは株式市場データの取得専門家です。
    ユーザーの質問から銘柄コードを抽出し、WebSearchToolを使用して
    株価情報と企業情報を取得してください。
    
    以下の情報を必ず含めてください:
    1. 現在の株価と変動率
    2. 主要な財務指標(PER、EPS、配当利回りなど)
    3. 企業概要(セクター、主要製品/サービスなど)
    4. 最近の業績トレンド
    
    日本語の銘柄名が含まれる場合は、対応する銘柄コードに変換してください。
    """,
    tools=[web_search],
    model="gpt-4o"
)

# 2. ニュース検索エージェント
news_agent = Agent(
    name="ニュース検索エージェント",
    instructions="""
    あなたは市場ニュースの検索専門家です。
    ユーザーの質問から企業や銘柄を特定し、WebSearchToolを使用して
    最新の市場ニュースを検索してください。
    
    以下の情報を中心に検索してください:
    1. 企業の最新発表(決算、新製品、戦略変更など)
    2. 業界動向やトレンド
    3. アナリストの見解や予測
    4. 株価に影響を与える可能性のあるニュース
    
    結果は新しいものから順に、簡潔なサマリーで提供してください。
    各ニュースのソースと日付も含めてください。
    """,
    tools=[web_search],
    model="gpt-4o"
)

# 3. 分析エージェント
analysis_agent = Agent(
    name="分析エージェント",
    instructions="""
    あなたは金融アナリストです。データ取得エージェントとニュース検索エージェントから
    提供された情報を統合し、以下の内容を含む包括的な分析を行ってください:
    
    1. 主要な財務指標の要約と評価
    2. 関連するニュースの要約と株価への潜在的影響
    3. 技術的分析(可能であれば)と短期・中期・長期の見通し
    4. 個人投資家向けの具体的な分析レポートの提供(数値を含める)
    
    専門用語をわかりやすく説明し、客観的かつ根拠に基づいた分析を提供してください。
    どの情報が事実で、どの部分が推測や意見かを明確に区別してください。
    """,
    model="gpt-4o"
)

# 4. コーディネーターエージェント
coordinator_agent = Agent(
    name="金融分析レポートコーディネーター",
    instructions="""
    あなたは金融分析ワークフローの中央調整役です。
    
    1. まず、入力が金融関連かどうかを検証してください。
    2. 次に、「データ取得エージェント」に財務データの取得を依頼してください。
    3. 並行して、「ニュース検索エージェント」に関連ニュースの検索を依頼してください。
    4. 最後に、収集した情報を「分析エージェント」に渡して最終レポートを生成してください。
    
    各エージェントの出力を監視し、必要に応じて追加情報を要求してください。
    最終的な分析は簡潔で実用的であることを確認してください。
    """,
    handoffs=[data_agent, news_agent, analysis_agent],
    input_guardrails=[InputGuardrail(guardrail_function=finance_guardrail)],
    model="gpt-4o"
)

6.5 ワークフローの実行

最後に、トレース機能を使用してワークフローを実行し、結果を出力します。

async def main():
    # ユーザークエリ
    query = """
    XXXX(企業名)の最新の株価と企業情報を教えてください。
    また、XXXXの最近の市場動向に関するニュースも含めて、分析レポートをお願いします。
    """
    
    # トレース機能を使用してワークフロー全体を記録
    with trace(workflow_name="金融分析ワークフロー", group_id="finance_analysis"):
        # コーディネーターエージェントを実行
        result = await Runner.run(coordinator_agent, query)
    
    print("【金融分析レポート】")
    print(result.final_output)
    
    # オプション:トレースIDの表示(OpenAIダッシュボードでの確認用)
    # print(f"\nトレースID: {result.trace_id}")

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

6.6 実行結果例

実行すると、以下のような金融分析レポートが生成されます。
【注意:以下は技術デモのための完全な架空データです。実在する企業や証券の情報ではありません】

出力例
【金融分析レポート】
XXXX(ティッカーシンボル:---)は、米国を代表するテクノロジー企業であり、ソフトウェア、クラウドサービス、ハードウェアなど多岐にわたる製品とサービスを提供しています。

## Stock market information for XXXX Corporation (---)
- XXXX Corporation is a equity in the USA market.
- The price is 3885.6 USD currently with a change of 95.9 USD (0.03%) from the previous close.
- The latest open price was 3796.2 USD and the intraday volume is 199528460.
- The intraday high is 3894.3 USD and the intraday low is 3796.2 USD.
- The latest trade time is Saturday, March 15, 00:15:00 UTC.


**最新の株価情報(2025年3月15日現在)**:

- **株価**:3885.6ドル
- **前日比**:+95.9ドル(+2.53%)
- **日中高値**:3894.3ドル
- **日中安値**:3796.2ドル
- **取引量**:199,528,460株

**主要な財務指標**:

- **PER(株価収益率)**:約35倍
- **EPS(一株当たり利益)**:約12ドル
- **配当利回り**:約0.9%

**企業概要**:

XXXXは、~~~など、多様な製品とサービスを展開しています。特に~~~は、企業のデジタルトランスフォーメーションを支援する主要なプラットフォームとして成長を続けています。

**最近の業績トレンド**:

2025年3月7日までの株価推移を振り返ると、XXXXの株価は以下のように推移しています:

- **2025年3月7日**:終値393.1ドル
- **2025年3月6日**:終値3968.9ドル
- **2025年3月5日**:終値4010.2ドル
- **2025年3月4日**:終値3886.1ドル
- **2025年3月3日**:終値3884.9ドル

これらのデータから、株価は4000ドル前後で推移しており、安定した動きを示しています。 

**最近の市場動向に関するニュース**:

専門家の予測によれば、XXXXの株価は2025年に平均5028.7ドルに達すると見込まれています。これは現在の株価から約29%の上昇傾向を示しています。また、2030年には平均8685.4ドルに達する可能性があるとされています。

**まとめ**:

XXXXは、安定した財務基盤と多様な製品ポートフォリオを持つ企業であり、長期的な成長が期待されます。特に~~~~サービスの成長が業績を牽引しています。しかし、株価は既に高水準にあり、短期的な利益を追求する投資家にとっては慎重な判断が求められます。今後は、定期的な市場動向のチェックと、ポートフォリオの分散を心掛けることが重要です。

6.7 マルチエージェントアプローチの利点

この実装例から、マルチエージェントアプローチの主な利点がわかります:

  1. モジュール性:各エージェントは特定の役割に集中し、全体システムの複雑さを低減
  2. 専門性:各エージェントが自分の得意分野に特化した処理が可能
  3. 拡張性:新しい情報源や分析要素を追加する際に、一部のエージェントのみ修正すればOK
  4. 柔軟性:アプリケーションに応じてエージェントの構成を簡単に変更可能
  5. エラー隔離:一つのエージェントの問題が全体システムに及ぼす影響を限定できる

実装メモ:実際のプロダクション環境では、WebSearchToolではなく専用の金融APIと連携することで、より正確かつ詳細なデータが取得できます。例えば、Yahoo Finance API、Alpha Vantage、Financial Modeling Prepなどの金融データAPIと連携することで、信頼性の高い金融分析システムを構築できます。本デモはコンセプト実証の目的で簡略化しています。

マルチエージェントアーキテクチャは、金融分析以外にも多くの実用シナリオ(カスタマーサポート、研究アシスタント、コンテンツ作成など)に適用できます。Agents SDKのツール、ハンドオフ、ガードレールなどの機能を組み合わせることで、複雑な業務プロセスを自動化する強力なAIシステムを構築できるのです。

7. LangGraphとの比較:何がどう違う?

エージェント開発フレームワークとして、OpenAIのAgents SDKとLangChain/LangGraphは現在の主要な選択肢として挙げられます。これらのフレームワークを比較することで、それぞれの強みや使い所を理解し、プロジェクトに最適なツールを選択する参考になります。

LangChain/LangGraphとは

LangChainは、LLMアプリケーション開発のための広範なエコシステムを提供するオープンソースのフレームワークです。ドキュメント検索、外部ツール連携、複雑なチェーン(処理の流れ)の構築などの機能を提供しています。

LangGraphはLangChainの拡張として開発され、より複雑なエージェントのワークフローを有向グラフとして定義・実行できる仕組みを提供しています。状態管理や条件分岐、並列処理など、高度なフロー制御が可能です。

主な設計思想の違い

Agents SDKは「シンプルさ」と「OpenAIエコシステムとの統合」を重視し、AIエージェントの自律性を最大限に活かす設計になっています。LLMの判断に任せる部分が多く、コード量も比較的少なくて済みます。

一方、LangGraphは「細かな制御」と「複雑なワークフロー管理」に重点を置いており、開発者が詳細なフロー制御を行えますが、その分学習コストや実装の複雑さが増す傾向があります。

どのような場合にどちらを選ぶべきか

  • Agents SDKは、OpenAIモデルを中心としたシンプルなエージェントシステムを素早く構築したい場合や、特にResponses APIなどのOpenAI提供機能と深く統合したい場合に適しています。

  • LangGraphは、複数のLLMベンダーを使いたい場合や、非常に複雑なビジネスロジックを組み込んだワークフローが必要な場合、また既存のLangChainベースのコードがある場合に選ぶとよいでしょう。

それでは、両フレームワークを詳細に比較した表を見ていきましょう(あくまで私見です):

比較項目 Agents SDK LangChain/LangGraph
開発元 OpenAI公式 OSSコミュニティ主導
設計思想 シンプルでPython特化、LLMに自律判断を任せる 抽象度高く多機能、細かい状態遷移の制御が可能
学習コスト 比較的低い(OpenAIユーザー向け) やや高い(抽象化レベルが深い)
LLM対応 OpenAIモデルに最適化 多様なLLMプロバイダーに対応
ツール連携 Responses APIなど新機能との統合がスムーズ 多様なデータベースや外部APIとの連携が豊富
フロー制御 基本的なハンドオフと条件分岐 グラフ構造での複雑な並列処理や状態遷移
標準機能 ガードレール、トレーシングが標準提供 エコシステムが広く、プラグイン多数
向いている用途 OpenAIエコシステム内での効率的な開発 複雑なワークフローや多様なLLM活用
運用監視 デフォルトでトレーシング画面を提供 自前での実装が必要な場合が多い
内部設計 ハンドオフ中心のエージェント連携 グラフベースの状態遷移管理
コミュニティ OpenAI主導の開発 広範なOSSコミュニティの支援

相互運用性と併用の可能性

実際のプロジェクトでは、両方のフレームワークの長所を活かした併用も検討できます。例えば、主要なエージェント処理はAgents SDKで実装し、特に複雑な処理が必要な部分はLangGraphのコンポーネントを統合するといったアプローチも可能です。Agents SDKではChat Completions互換の外部サービスを利用したコードもAgentとして組み込めるため、柔軟性が確保されています。

総合すると、Agents SDKはOpenAIエコシステム内でのエージェント開発に最適化されており、LangChain/LangGraphは多様なLLM・複雑フロー管理に強いという住み分けがあると言えるでしょう。プロジェクトの要件、チームの技術スタック、スケジュールなどを考慮して最適な選択をすることが重要です。

8. 気になるコスト面と対策

Responses APIとAgents SDKを実際の開発・運用に組み込む際には、コスト面の考慮が重要になります。OpenAIの公式価格体系を理解し、効率的な使用方法を検討しましょう。

参考:公式ドキュメント(価格表)
https://platform.openai.com/docs/pricing#built-in-tools

8.1 組み込みツールの価格体系

組み込みツールを使用する場合、選択したモデルのトークン料金に加えて、以下のツール使用料がかかります:

ツール 価格
コード解釈器 (Code Interpreter) $0.03/セッション
ファイル検索ストレージ $0.10/GB/日(1GB無料)
ファイル検索ツール呼び出し
(Responses APIのみ*)
$2.50/1,000回呼び出し
(※Assistants APIでは適用されません)
Web検索 モデルと検索コンテキストサイズによって異なる(詳細は下記)

8.2 Web検索ツールの詳細価格

Web検索ツールは、使用するモデルと検索コンテキストサイズに応じて料金が変動します:

モデル 検索コンテキストサイズ 価格
gpt-4o または gpt-4o-search-preview $30.00/1,000回呼び出し
中(デフォルト) $35.00/1,000回呼び出し
$50.00/1,000回呼び出し
gpt-4o-mini または gpt-4o-mini-search-preview $25.00/1,000回呼び出し
中(デフォルト) $27.50/1,000回呼び出し
$30.00/1,000回呼び出し

注意点: 上記の料金はツール使用料のみであり、基本のモデル使用料(トークン単価 × 使用トークン数)は別途かかります。

8.3 コスト最適化のための対策

開発・運用コストを抑えるための実践的な対策をいくつか紹介します:

  1. 適切なモデル選択

    • 簡単な質問応答には軽量のgpt-4o-miniを使用し、複雑な分析が必要な場合のみgpt-4oを使用する
    • 検索機能もgpt-4o-miniで十分な場合が多く、コスト削減になる
  2. Web検索の最適化

    • 「特定の場合にのみ検索を使用する」という明示的な指示をプロンプトに含める
    • 検索コンテキストサイズは必要に応じて「低」に設定(デフォルトは「中」)
    • 検索結果のキャッシュ機構を実装し、頻出クエリに対する再検索を防ぐ
  3. ファイルストレージの最適化

    • 不要なドキュメントは定期的に削除
    • 大規模ファイルは事前に要約・分割処理を行い、必要部分のみをアップロード
  4. 会話履歴管理の工夫

    • 長時間の会話では定期的に履歴をクリアまたは要約
    • ユーザー行動パターンに基づき、必要な場合にのみ会話履歴を保持
  5. ガードレールによるフィルタリング

    • 軽量モデルを使用したガードレールで、高コストなツール使用が必要なリクエストを事前にフィルタリング
    • 単純な質問には単純な応答を返し、不必要な処理を避ける仕組みを設計

コスト管理は開発初期段階から考慮することが重要です。「自前で外部API連携を実装する場合も結局コストはかかるし、メンテナンスも大変」という観点を踏まえると、公式ツールの適切な利用は開発効率と運用コストのバランスを取る上で有効ですが、運用規模に応じた監視と最適化が不可欠です。

9. OpenAI Agents SDKの内部設計思想と将来展望

Agents SDKの内部コードを分析すると、単なるAPIラッパーではなく、エージェントベースのAIシステム開発のための体系的なフレームワークとして設計されていることがわかります。

9.1 エージェント協調の設計思想

Agents SDKの設計には、以下のような思想が反映されています:

  1. 自律性と協調性のバランス:個々のエージェントは自律的に動作しながらも、他のエージェントと協調できる仕組みが組み込まれています。

  2. 責任分離の原則:各エージェントは特定の責任領域を持ち、その領域での専門性を発揮します。これによりシステム全体の複雑性が管理しやすくなります。

  3. 安全性の組み込み:ガードレールによる入出力の検証が設計の中核に位置付けられており、安全性を後付けでなく最初から考慮しています。

  4. 可観測性の重視:トレーシング機能が標準提供され、複雑なエージェントシステムの内部動作を可視化できます。

この設計思想は、今後のAIシステム開発の方向性を示唆しています。単一の「万能」AIアシスタントから、専門化された複数のエージェントが協調する分散型AIシステムへの移行が進んでいくでしょう。

9.2 将来の拡張可能性

Agents SDKは現時点でも強力ですが、以下のような方向での拡張が予想されます:

  1. メモリとパーソナライゼーション:エージェントの長期記憶や個人化された情報管理の機能

  2. マルチモーダル対応の拡充:画像、音声、動画などを処理できるエージェントの標準化

  3. 自己進化メカニズム:エージェントが自身のパフォーマンスを評価し改善する仕組み

  4. エージェント市場の形成:特定タスク用の事前訓練済みエージェントを共有・利用できるエコシステム

これらの拡張により、Agents SDKはより柔軟で強力なAIシステム開発のプラットフォームになっていくでしょう。

10. まとめと考察

OpenAIのResponses APIAgent SDKは、エージェント開発のハードルを大きく下げる可能性を秘めたアップデートです。

  • Responses API

    • 従来のChatCompletion APIをほぼ置き換える拡張機能を実装。
    • ツール呼び出し(Web検索、ファイル検索、RPA的操作)がネイティブ対応。
    • サーバサイドで会話履歴を管理し、マルチターン・マルチステップ対話を簡単にハンドリング。
    • 画像だけでなくPDFファイルも直接参照可能に。
    • Chat Completionsは互換性のある新モデルの提供を含め、今後もサポートを継続。
    • Assistants (beta)は2026年半ばを目処にサポート終了予定(詳細な移行ガイドを提供)。
  • Agent SDK

    • 7つの主要インターフェイス(エージェント、ツール、ガードレール、ハンドオフ、モデル、ランナー、トレース)を提供。
    • 複数エージェント間でタスクを回すハンドオフ機能が強力。
    • @function_tool でPython関数をツール化し、LLMが自律的にコールするワークフローを実装。
    • ガードレールやログトレースなどの安全・可視化機能を標準装備。
    • トレーシング画面により開発・デバッグ・運用が容易に。
    • 現時点ではPython版のみ提供、今後Node.js版もリリース予定。
    • LangGraphほど大規模・複雑なフロー制御は現時点でやや弱いが、今後のアップデートに期待。

開発者としての感想は「やっとOpenAI公式が"LLM+ツール連携"を真剣にサポートし始めたな」という印象です。
従来はLangChain等で頑張って外部検索を組み込んだり、エージェント間連携をカスタム実装していましたが、公式フレームワークならではのスムーズさと安心感が大きいです。特に会話履歴管理やツール連携が標準機能として提供されることで、開発者が本当に重要なビジネスロジックに集中できるようになりました。

また、トレーシング機能やオブザーバビリティツールが標準提供されることで、大規模な実用システムへの採用障壁も下がっています。一方でLLM任せが多いため、「厳密なルール管理や状態遷移が必要なケースではもう少し踏み込んだ設計が要る」という課題も残っています。

ユースケースとしては、カスタマーサポート、自動検索・調査エージェント、文書要約・分析ツール、社内ナレッジベース検索など、これまでLangChainやLangGraphで実装していたケースの多くがより少ないコードで実装できるようになるでしょう。

2025年は「自律エージェント元年」と言われるほどマルチエージェント活用が盛り上がっています。日本でも製造業や金融、小売り、医療など多くの産業でLLMエージェントの活用が広がりつつあります。Responses APIAgents SDKが普及すれば、業務や研究の効率化、あるいはまったく新しいサービスの創出が急加速するかもしれません。

開発者視点では、ついにOpenAIのエコシステムが「モデル提供」から「エージェント開発プラットフォーム」へと進化した瞬間を目の当たりにしているようです。興味を持たれた方は、ぜひPoCや小規模プロジェクトから試してみてください。また、Agents SDKのコードを一度読んでみることで、エージェントベースAIシステムの設計思想への理解が深まるでしょう。

最後までお読みいただきありがとうございました。ご質問やご意見などありましたら、ぜひコメントやフィードバックをお寄せください。この記事がエージェント開発の第一歩となることを願っています!

11. Azure版の展望:Responses APIとComputer-Using Agent

OpenAIの今回のアップデートは、Microsoftの Azure プラットフォームにも順次展開される予定です。Microsoftは公式ブログで「Azure AI Foundry」における「Responses API」と「Computer-Using Agent (CUA)」の導入を発表しました。

https://azure.microsoft.com/en-us/blog/announcing-the-responses-api-and-computer-using-agent-in-azure-ai-foundry/

Azure版の主な特徴

  • Responses API on Azure:

    • Azure OpenAI ServiceでChatCompletions APIの機能とAssistants APIの高度な機能を統合
    • エンタープライズ向けデータプライバシーとセキュリティを強化
    • Azure AI Agent Serviceとのシームレスな統合
  • Computer-Using Agent (CUA):

    • OpenAIのコンピュータ操作ツールと同様の機能を提供
    • GUI操作、アプリケーションナビゲーション、マルチステップタスクの自動化
    • Windows 365やAzure Virtual Desktopとの連携を検討中
  • エンタープライズ向け安全対策:

    • モデル・システム・デプロイメントの各レベルでの多層的なセキュリティ
    • Microsoftの「Trustworthy AI」フレームワークによるリアルタイム監視
    • 企業コンプライアンスとガバナンスツールの統合

利用可能時期

Microsoftの発表によると、Azure AI Foundryにおける「Responses API」と「CUA」は「今後数週間のうち」にエンタープライズ顧客向けに提供開始される予定です。

エンタープライズ環境では、OpenAIの直接利用よりもAzure OpenAI Serviceを通じた利用が、セキュリティやコンプライアンス、既存システムとの統合などの理由で好まれることが多いため、今回のアップデートがAzure側でも利用可能になることは多くの企業にとって朗報といえるでしょう。特に大規模な導入を検討している企業は、Azure版のリリースを待ってから本格展開を進めることも選択肢の一つとなります。

Microsoftの公式発表全文はこちらで確認できます。

2025/3/27追記:プレビュー版が利用可能となりました。解説記事↓
https://zenn.dev/chips0711/articles/e86e4ea89d04c4

12. おまけ

今回の公式プレスリリースのテキスト文のみを使って、プロンプトのみで一発出しした資料を貼っておきます。人間ノータッチの割には、いい感じのスライドになってませんか?
※ 細かいところや内容の正確性はAIが作成したということでご了承ください。
※ 申し訳ないですがプロンプトなどの技術的詳細は非公開とさせてください。

参考リンク集


免責事項

本記事は情報提供を目的としており、2025年3月16日時点の情報に基づいています。内容の正確性・完全性は保証されず、公式ドキュメントで最新情報をご確認ください。記事内のコードサンプルは自己責任でご利用ください。金融分析の例示は技術デモのみを目的とし、投資判断の根拠として使用すべきではありません。金融商品の投資判断・助言には金融商品取引業の登録が必要であり、投資判断は必ず専門家にご相談ください。APIキー等の機密情報は適切に管理し、公開環境での使用時はセキュリティに十分ご注意ください。本記事内容の利用によって生じたいかなる損害(サービスの中断、データ損失、営業損失等を含む)についても、著者は一切の責任を負いません。OpenAIおよびMicrosoftの製品・サービスは各社の利用規約に従ってご利用ください。

10

Discussion

ログインするとコメントできます