ブログに戻る

APIとSDKガイド

OpenAI 互換 API を使った Python での関数呼び出し

tools を送信し、tool_calls を読み取り、role tool で結果を返す:MiniMax-M2.7 を使った Python での2ステップの関数呼び出しループ。

投稿者: CheapTokens Team公開日 更新日 読了時間 7 分

2ステップのツール呼び出しループの図:tools を送信し、tool_calls を読み取り、結果をツールメッセージとして返す
このページの内容
  1. 関数呼び出しの仕組み
  2. クライアントのセットアップ
  3. 関数を宣言して最初のリクエストを送信する
  4. 表示されるべきもの
  5. 複数の呼び出しとより長いループ
  6. MiniMax-M2.7 がツールループに適している理由
  7. うまくいかない場合
  8. 次のステップ

Function calling(ツール呼び出し)を使うと、モデルはあなたのコードに対して関数の実行を要求できます。リクエストと一緒に tools リストを送ると、モデルはテキストではなく tool_calls を返します。あなた自身が関数を実行し、その結果を role: tool を持つメッセージで返します。その後、モデルが最終的な回答を書きます。このガイドでは、OpenAI 互換エンドポイント https://api.cheaptokens.dev/v1/chat/completions に対して MiniMax-M2.7(id MiniMaxAI/MiniMax-M2.7、180K コンテキスト、回答あたり最大 8,192 出力トークン)を使い、Python でループ全体を示します。

ツール呼び出しはカタログ内のすべてのモデルでサポートされていると記載されています(CheapTokens docs、2026年10月22日確認)。API キーが必要です。まだリクエストを行っていない場合は、your first API request から始めてください。

関数呼び出しの仕組み#

モデルは何も実行しません。モデルは「get_weather を {\"city\": \"Paris\"} で呼び出す」という構造化されたリクエストを生成するだけです。あなたのプログラムが実際の処理を行い、結果を報告します。これにより、2つのリクエストからなるループになります:

  1. リクエスト 1。 ユーザーメッセージと tools リストを送信します。モデルはテキストで回答するか、1つ以上の tool_calls を返します。
  2. あなたのコード。 各呼び出しを解析し、対応する関数を実行して結果を収集します。
  3. リクエスト 2。 会話全体を再度送信します:元のメッセージ、tool_calls を含むアシスタントメッセージ、そして呼び出しごとに1つの role: tool メッセージです。モデルはその結果を使って最終的な回答を書きます。

すべてのリクエストは履歴全体を再送信するため、ツールループは単一の質問よりも多くの入力トークンを使用します。これが初めての場合は、what a token is をお読みください。

クライアントのセットアップ#

OpenAI SDK(pip install openai)をインストールし、キーはソースコードではなく環境変数に入れてください:

bash
export CHEAPTOKENS_API_KEY="your-key-here"

ベース URL は https://api.cheaptokens.dev/v1 です。モデル id は CheapTokens のドキュメントから取得してください。プロバイダーによって同じモデルの名前が異なります。このガイドでは MiniMaxAI/MiniMax-M2.7 を使用します。

関数を宣言して最初のリクエストを送信する#

関数は tools 配列で記述します。各エントリには type: function と、name、description、および JSON Schema として書かれた parameters を持つ function オブジェクトがあります。説明はモデルが関数をいつ呼び出すかを判断するために読むものなので、短く具体的な文で書いてください。

以下は完全なスクリプトです。天気関数は偽のデータを使うスタブなので、この例には追加のサービスは不要です:

python
import json
import os

from openai import OpenAI

client = OpenAI(
    base_url='https://api.cheaptokens.dev/v1',
    api_key=os.environ['CHEAPTOKENS_API_KEY'],
)
MODEL = 'MiniMaxAI/MiniMax-M2.7'


def get_weather(city: str) -> dict:
    # Stub with fake data: replace with a real lookup
    fake = {'Paris': 14, 'Tokyo': 19}
    return {'city': city, 'temp_c': fake.get(city, 10)}


FUNCTIONS = {'get_weather': get_weather}

tools = [
    {
        'type': 'function',
        'function': {
            'name': 'get_weather',
            'description': 'Get the current temperature in Celsius for a city.',
            'parameters': {
                'type': 'object',
                'properties': {
                    'city': {'type': 'string', 'description': 'City name, for example Paris'},
                },
                'required': ['city'],
            },
        },
    }
]

messages = [{'role': 'user', 'content': 'What is the weather in Paris right now?'}]

# Step 1: the model decides whether to call a function
first = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
message = first.choices[0].message

if not message.tool_calls:
    print(message.content)
    raise SystemExit

# Step 2: run every requested call and send the results back
messages.append(message)
for call in message.tool_calls:
    args = json.loads(call.function.arguments)
    result = FUNCTIONS[call.function.name](**args)
    messages.append({
        'role': 'tool',
        'tool_call_id': call.id,
        'content': json.dumps(result),
    })

final = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
print(final.choices[0].message.content)

ループが機能するかどうかを決める3つの詳細があります:

  • call.function.arguments はJSON 文字列であり、辞書ではありません。json.loads で解析してください。
  • 各 role: tool メッセージは、それが応答する呼び出しの tool_call_id を必ず含める必要があり、それは call.id です。
  • ツールメッセージの content は文字列です。構造化された結果には json.dumps を使用してください。

表示されるべきもの#

最初のリクエストの後にフィールドを出力すると、次のようになります(呼び出し id は実行ごとに異なります):

text
finish_reason: tool_calls
call.id: call_abc123
call.function.name: get_weather
call.function.arguments: {"city": "Paris"}

その後、スクリプトは最終的な回答を出力します。これは「It is currently 14 °C in Paris.」のような平易な文です。文言は実行ごとに変わりますが、数値はあなたの関数から来ます。

複数の呼び出しとより長いループ#

モデルは1つのレスポンスで複数の呼び出しを返すことがあります。たとえば、2つの都市の天気を尋ねた場合です。そのため、この例では message.tool_calls をループし、各呼び出しにそれぞれの id で応答します。OpenAI の function calling ガイドも同じ動作を説明しています:デフォルトの tool_choice が auto の場合、モデルは0個、1個、または複数の関数を呼び出すことがあります(OpenAI docs、2026年10月22日確認。このページには最終更新日がなく、より新しい Responses API の形式を説明していますが、このガイドでは /v1/chat/completions エンドポイントの Chat Completions 形式を使用しています)。

エージェントはモデルがツールを要求しなくなるまで2つのステップを繰り返します。リクエストをループで囲み、必ずステップ制限を設定してください:

python
for _ in range(5):  # safety limit against endless loops
    response = client.chat.completions.create(model=MODEL, messages=messages, tools=tools)
    message = response.choices[0].message
    if not message.tool_calls:
        print(message.content)
        break
    messages.append(message)
    for call in message.tool_calls:
        args = json.loads(call.function.arguments)
        result = FUNCTIONS[call.function.name](**args)
        messages.append({'role': 'tool', 'tool_call_id': call.id, 'content': json.dumps(result)})

回答をトークンごとに表示したい場合は、これを streaming in Python と組み合わせてください。

MiniMax-M2.7 がツールループに適している理由#

ツールループは履歴が増えながら多くのリクエストを送信します。そのパターンに対して CheapTokens は MiniMax-M2.7 を提供しています:180K コンテキスト、回答あたり最大 8,192 出力トークン、ピーク時間なしでいつでも同じ価格、すべてのモデルで1つの残高。入力と出力を合計すると、OpenRouter の参考価格より安価です(CheapTokens 価格比較、2026年10月31日確認)。

現在の 1M トークンあたりの価格:入力 $0.05、出力 $0.15。例としてのワークロード(実測ではありません)では、上記の2リクエストの往復で合計約 2,700 入力トークンと 350 出力トークンの場合、$0.00019 かかります。完全な価格表は pricing page にあります。

別のモデルを試すには、id だけを変更してください。id の一覧は models page にあります。

うまくいかない場合#

  • message.tool_calls が None である。 モデルがテキストで回答しました。関数 description をより具体的にし、質問が実際に関数を必要としていることを確認してください。
  • arguments で json.JSONDecodeError。 モデルが無効な JSON を生成しました。json.loads を try/except で囲み、エラーテキストをツール結果として返してください。そうすればモデルは修正した引数で再試行できます。
  • 2番目のリクエストが失敗する、またはモデルが結果を無視する。 すべての呼び出しに一致する tool_call_id を持つ role: tool メッセージがちょうど1つあり、それらの前に tool_calls を含むアシスタントメッセージが履歴にあることを確認してください。
  • FUNCTIONS[...] で KeyError。 モデルが登録していない関数を指定しました。クラッシュする代わりに、短いエラー文字列をツール結果として返してください。
  • 回答が途中で切れる。 finish_reason を確認してください:値 length は出力トークン制限に達したことを意味します。why an AI answer stops mid-sentence を参照してください。
  • 401 または認証エラー。 キーが CHEAPTOKENS_API_KEY にあり、ベース URL が /v1 で終わっていることを確認してください。

次のステップ#

スタブを実際の関数に置き換えてください:データベースクエリ、検索呼び出し、または計算機です。tools と FUNCTIONS にさらにエントリを追加し、各関数の結果は短く保ってください。なぜなら、それは後続の各リクエストで履歴に戻されるからです。キーを取得して最初の呼び出しを行うには、CheapTokens docs を開いてください。