【vLLM】OpenAI互換APIでFunction Callingを実装する方法と「Invalid tools format」エラー解決

問題の概要:vLLMのFunction Callingで発生するエラーと課題

vLLMは、高速なLLM推論エンジンとして広く利用されています。そのOpenAI互換APIサーバーを起動し、Function Calling(関数呼び出し)機能を使用しようとすると、開発者は特に以下の2つの課題によく直面します。

1. 「Invalid tools format」エラー

OpenAIクライアントライブラリを使用してFunction Callingのリクエストを送信した際、以下のようなエラーメッセージが返されることがあります。

openai.BadRequestError: Error code: 400 - {'message': 'Invalid tools format. Got [{'type': 'function', 'function': {'name': 'get_current_weather', 'description': 'Get the current weather in a given location', 'parameters': {'type': 'object', 'properties': {'location': {'type': 'string', 'description': 'The city and state, e.g. San Francisco, CA'}}, 'required': ['location']}}}}]'}

このエラーは、vLLMサーバーが受け取ったツール(関数)の定義の形式を正しく解釈できないことを示しています。

2. 関数定義が無視され、通常のチャット応答のみが返される

エラーは出ないものの、クライアントが定義した関数が完全に無視され、モデルが関数呼び出しを生成せず、通常のテキスト応答だけを返すケースもあります。これは、サーバー起動時の引数やモデルの能力が関係している可能性があります。

原因の解説

これらの問題の根本的な原因は、主に以下の3点に集約されます。

1. vLLMサーバーの起動オプション不足

vLLMのOpenAI APIサーバーをデフォルトの設定で起動した場合、Function Callingに必要なバックエンド処理が有効になっていません。特に--enable-prefix-caching--enforce-eager などのオプションが、一部のモデルや設定においてFunction Callingの安定性に影響を与えることがあります。

2. モデル側のFunction Calling対応

使用する基盤モデル(例:Llama-3, Qwen, GPT-NeoXなど)が、メタデータ(tokenizer.jsonやconfig.json)に関数呼び出し用のツール形式を定義しているか、あるいはその形式を理解できる必要があります。モデルが完全にOpenAIのツール形式をサポートしていない場合、エラーや無視が発生します。

3. クライアント側のリクエスト形式の微妙な違い

OpenAIの公式APIとvLLMの互換APIでは、リクエストのJSON構造の解釈に微妙な差異が生じる場合があります。特に、tools フィールドと廃止予定の functions フィールドの扱い、またはtool_choiceパラメータの指定方法が原因となることが多いです。

解決方法:ステップバイステップ実装ガイド

以下、vLLMを用いてFunction Callingを正常に動作させるための具体的な手順を説明します。

ステップ1:vLLMサーバーの適切な起動

Function Callingをサポートするためには、--chat-template オプションの指定が最も重要です。モデルに適したチャットテンプレートを指定することで、ツール定義のメッセージが正しくモデルに渡されます。

# 例:Llama-3モデルを使用する場合
vllm serve meta-llama/Meta-Llama-3-8B-Instruct 
    --api-key token-abc123 
    --port 8000 
    --chat-template hf-internal-testing/llama-tokenizer  # モデルに合わせて変更必須
    # 必要に応じて以下のオプションも追加
    # --enable-prefix-caching
    # --enforce-eager

ポイント: --chat-template の値は使用するモデルによって異なります。Hugging Faceのモデルページの「tokenizer_config.json」ファイル内のchat_templateを参考にするか、モデル提供元の指示に従ってください。

ステップ2:クライアントコードの作成(修正版)

以下のPythonコードは、OpenAI Pythonライブラリを使用してvLLMサーバーにFunction Callingリクエストを送信する例です。エラーを避けるために、リクエスト形式に注意が必要です。

from openai import OpenAI
import json

# 1. クライアントの設定(vLLMサーバーを指す)
client = OpenAI(
    api_key="token-abc123", # vLLM起動時に指定した--api-key
    base_url="http://localhost:8000/v1" # vLLMサーバーのアドレス
)

# 2. 呼び出し可能な関数(ツール)の定義
tools = [{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "Get the current weather in a given location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. Tokyo, Japan",
                },
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
            },
            "required": ["location"],
        },
    },
}]

# 3. チャット補完リクエストの実行
try:
    response = client.chat.completions.create(
        model="meta-llama/Meta-Llama-3-8B-Instruct", # サーバーでロードしたモデル名
        messages=[{"role": "user", "content": "What's the weather like in Kyoto today?"}],
        tools=tools,
        tool_choice="auto", # "auto", "none", または特定の関数を指定
        max_tokens=150
    )
except Exception as e:
    print(f"リクエストエラー: {e}")
    exit()

# 4. 応答の処理
message = response.choices[0].message
print(f"モデルの応答: {message.content}")

# ツール呼び出しが含まれているか確認
if message.tool_calls:
    print("nモデルが関数呼び出しを要求しました:")
    for tool_call in message.tool_calls:
        func_name = tool_call.function.name
        func_args = json.loads(tool_call.function.arguments)
        print(f"  関数名: {func_name}")
        print(f"  引数: {func_args}")

        # ここで実際の関数を実行するロジックを記述
        # if func_name == "get_current_weather":
        #     result = get_current_weather(**func_args)
        #     ...(省略)...

ステップ3:代替アプローチ – 直接HTTPリクエストを送信

OpenAIクライアントライブラリで問題が解決しない場合、直接HTTPリクエストを送ることで、より細かい制御が可能です。

import requests
import json

url = "http://localhost:8000/v1/chat/completions"
headers = {
    "Authorization": "Bearer token-abc123",
    "Content-Type": "application/json"
}
payload = {
    "model": "meta-llama/Meta-Llama-3-8B-Instruct",
    "messages": [{"role": "user", "content": "大阪の天気を教えて"}],
    "tools": [{
        "type": "function",
        "function": {
            "name": "get_current_weather",
            "description": "指定された場所の現在の天気を取得します",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string", "description": "都市名、例:大阪"}
                },
                "required": ["location"]
            }
        }
    }],
    "tool_choice": "auto"
}

response = requests.post(url, headers=headers, data=json.dumps(payload))
print(response.json())

ステップ4:モデルとバージョンの確認

vLLMとモデルのバージョン互換性を確認してください。Function Callingは比較的新しい機能であり、古いバージョンのvLLMでは完全にサポートされていない可能性があります。

# vLLMのバージョン確認
pip show vllm
# 最新版へのアップグレード
pip install -U vllm

コード例・コマンド例:エラー別対応

ケースA: 「Invalid tools format」エラーが続く場合

ツール定義をシングルクォートではなく、常にダブルクォートを使用した正しいJSON形式で送信しているか確認します。また、tools フィールドの代わりに、古い形式の functions フィールドを試してみてください(非推奨ですが、一部環境で有効)。

# 古い 'functions' 形式を試す(非推奨だが有効な場合あり)
payload = {
    "model": "your-model",
    "messages": [...],
    "functions": [ # 'tools' ではなく 'functions'
        {
            "name": "get_weather",
            "description": "...",
            "parameters": {...}
        }
    ],
    "function_call": "auto" # 'tool_choice' ではなく 'function_call'
}

ケースB: 関数が呼び出されない場合

モデルのシステムプロンプトに関数呼び出しの指示を明示的に含めると改善することがあります。

messages=[
    {"role": "system", "content": "あなたは有能なアシスタントです。ユーザーのリクエストに応じて、適切な関数を呼び出して回答してください。関数が利用可能な場合は、必ず関数呼び出しを行ってください。"},
    {"role": "user", "content": "What's the weather like in Kyoto today?"}
]

まとめ・補足情報

vLLMでOpenAI互換のFunction Callingを成功させるための要点をまとめます。

  • 最重要点はサーバー起動時の --chat-template オプション:モデルに合った正しいチャットテンプレートを指定しないと、ツール定義がモデルに伝わりません。
  • モデルの選択:Function Callingを強くサポートしているモデル(Llama-3 Instruct、Qwen系列など)を選ぶと成功率が高まります。
  • vLLMのバージョン:v0.3.0以降の比較的新しいバージョンを使用することを推奨します。開発が活発なため、バージョンアップで問題が解決されるケースが多々あります。
  • デバッグのコツ:まずは curl や直接HTTPリクエストで最小限のペイロードを送り、サーバーログ(vllm serve を実行したターミナル)にどのようなリクエストが届いているかを確認すると根本原因が見つかりやすいです。

Function Callingは、AIアプリケーションに外部APIやデータベースへの接続能力を与える重要な機能です。vLLMを用いることで、オープンソースモデルを用いた高速で低コストな関数呼び出しシステムの構築が可能になります。本記事で紹介した手順とトラブルシューティングを参考に、実装の成功を目指してください。

この記事は役に立ちましたか?