DeepSeek・Gemini・Qwenの複数ターンのツール呼び出しを小規模テスト
合成データを使い、DeepSeek・Gemini・Qwenのツール呼び出しから再試行までを検証。実際の出力、トークン使用量、JSONの妥当性を比較します。
DeepSeek V4.1 Flash、Gemini 3.8 Flash、Qwen3.8 Flashは、いずれも合成注文の照会を完了し、再試行可能なツールエラーを1回注入したケースから復旧しました。これは有用な出発点ですが、信頼性で優位なモデルを特定する結果にはなりませんでした。各モデルは各ケースを1回ずつ実行し、最終回答はいずれも、JSONのみではなくMarkdownのコードフェンスを含んでいました。
したがって、実際にどのモデルを選ぶかは、アプリケーションの要件に沿ってツールの一連の処理を検証して決めるべきです。引数が正しいこと、ツールの結果が正しく対応付けられていること、再試行回数に上限があること、最終回答が許容できること、使用量が記録されることを確認します。モデルがツール呼び出しを返すのは、その処理の始まりにすぎません。
この記事では、タスク、観測結果、実行可能なテストキットを紹介します。DeepSeek、Gemini、Qwenの一般的な比較を、範囲を限定した実際のAPI検証で補う内容です。注文データとエラーは合成したものですが、モデルへのリクエストは実際に行いました。
検証したワークフロー
ユーザーが注文 A17 について問い合わせます。利用できるツールは lookup_order のみで、文字列の order_id を受け取ります。ローカル関数は、固定された機微情報を含まないレコードを返します。
{"order_id":"A17","status":"shipped","amount_eur":42,"next_action":"track_delivery"}
通常ケースでは、最初の呼び出しが成功します。エラーケースでは、最初のツール結果が次の内容になります。
{"error":"temporarily_unavailable","retryable":true}
次に有効な照会を行うと、注文情報が返ります。これはアプリケーション層のツールエラーであり、HTTP 429やプロバイダーの障害を模擬したものではありません。この違いを明確にしておけば、ここで再試行に成功したことを、モデルサービスの障害復旧を示す証拠として誤って宣伝せずに済みます。
プロンプトでは、ツールを使うこと、推測を避けること、再試行可能なエラーに対して1回だけ再試行すること、指定された4つのフィールドを含むJSONを返すことをモデルに求めています。ハーネスはケースごとに最大4回のモデルリクエストを許可します。カードへの課金、メッセージの送信、注文の変更を行うツールは用意していません。

10月12日に撮影した、実際のモデルページのコード画面です。基本的な接続例であり、複数ターンの評価を撮影したものではありません。
送信内容と測定項目
3モデルすべてで、Ofoxの /v1/chat/completions ルートを使い、max_tokens: 1800 を指定しました。名前空間を含む正確なIDは次のとおりです。
deepseek/deepseek-v4.1-flash
google/gemini-3.8-flash
qwen/qwen3.8-flash
temperatureや推論設定は明示的に指定していません。これはゲートウェイの現在のデフォルト設定をテストするもので、推論予算をそろえた比較ではありません。また、プロバイダーのネイティブな直接エンドポイントもテストしていません。
| モデル | 通常ケース:ツール呼び出し回数 / リクエスト時間の合計 | 再試行ケース:ツール呼び出し回数 / リクエスト時間の合計 | 報告された合計トークン数(通常 / 再試行) |
|---|---|---|---|
| DeepSeek V4.1 Flash | 1 / 3.14 s | 2 / 5.03 s | 899 / 1,474 |
| Gemini 3.8 Flash | 1 / 4.52 s | 2 / 6.40 s | 532 / 945 |
| Qwen3.8 Flash | 1 / 3.36 s | 2 / 4.35 s | 1,008 / 1,527 |
時間は、各ワークフローにおけるモデルリクエストのクライアント側計測時間を合計したものです。レイテンシのパーセンタイルでも、モデルの推論時間のみを示す値でもありません。トークン数は、履歴の繰り返し分を含め、各ターンのAPI使用量を合計しています。トークナイザーや計上方法には違いがあるため、報告トークン数が少ないからといって、処理量が少ない、あるいは請求額が安いとは限りません。
完了したテスト一式を実行する前に、DeepSeekの初回試行でトランスポート層のプロキシ切断が発生しました。これは別途記録し、この表からは除外しています。この除外を、失敗が一度もなかったことを示す結果と取り違えないでください。この表が示すのは完了したケースであり、準備中に発生したネットワーク試行のすべてではありません。
成功した点と、成功しなかった点
完了した通常ケースでは、いずれも有効な照会が1回行われました。エラーケースでは、注入した最初のエラーから復旧するため、有効な照会が2回行われました。最終回答にはすべて、期待した注文情報が含まれていました。成功した結果を受け取る前に、注文状況を作り上げたモデルはありませんでした。
一方で、6件すべての最終回答がMarkdownのコードフェンスで囲まれていました。DeepSeekの再試行ケースでは、再試行について説明する文も末尾に追加されていました。アプリケーションがテキスト全体を1つのJSON値として解析する必要がある場合、値自体が正しくても、厳密な要件には適合しません。
自動化に使うモデルを選ぶ際には、この点が重要です。人が読むサポートアシスタントなら、コードフェンス付きの回答を許容できるかもしれません。一方、JSONオブジェクトを期待するバックグラウンドワーカーでは、許容できない可能性があります。測定前に合格条件を定め、意味上の正しさ、構文、ツールの動作を別々の項目として扱いましょう。
| 合格判定項目 | 完了したケースでの観測結果 |
|---|---|
| 許可されたツールの使用と、正しい注文ID | 6件すべて合格 |
| 注入したエラー後に、照会をちょうど1回追加 | エラーケース3件すべて合格 |
| 最終回答に期待した注文情報が含まれる | 6件すべてに含まれる |
| コードフェンスや説明文のないJSONのみの出力 | 6件すべて不合格 |
| 本番環境での一般的な信頼性 | 未測定 |
ターン間でassistantメッセージを保持する
ハーネスは、返されたassistantメッセージを追加してから、各ツール結果を追加します。最終的に画面に表示されたテキストだけから履歴を再構成しないでください。次のターンでは、ツール呼び出しIDやプロバイダー固有の継続フィールドが必要になる場合があります。このゲートウェイテストでは継続用メタデータが含まれていたため、次のリクエストでも保持しました。
基本的な流れは次のとおりです。
message = response["choices"][0]["message"]
messages.append(message)
for call in message.get("tool_calls", []):
# Validate name and arguments before executing any application action.
result = lookup_validated_order(call)
messages.append({
"role": "tool",
"tool_call_id": call["id"],
"content": json.dumps(result),
})
lookup_validated_order はアプリケーションの検証境界を表すもので、モデルが返した名前を無条件に実行するものではありません。ダウンロード可能な実装で許可されているのは、lookup_order と、厳密に {"order_id":"A17"} という引数の組み合わせだけです。本番環境では、認証済みユーザーが対象レコードにアクセスできるかどうかも確認してください。
プロバイダーのネイティブな連携では、このOpenAI互換形式をそのまま使わず、各プロバイダー独自のメッセージとツール結果の仕様に従ってください。DeepSeekのツール呼び出しガイドとGeminiの関数呼び出しドキュメントでは、それぞれ別のインターフェースが説明されています。このゲートウェイテストに合格しても、実行していないネイティブアダプターの動作が保証されるわけではありません。
テストを再現してから拡張する
テストキットをダウンロードして解凍し、スクリプトを確認して、隔離したPython環境に requests をインストールします。通常のシークレット管理方法で OFOX_API_KEY を設定してください。このスクリプトは課金対象となるモデルリクエストを送信します。無料クレジットが利用できることは前提にしていません。
ツールテスト一式を実行し、新しいディレクトリに結果を保存します。
python3 -m venv .venv
source .venv/bin/activate
python -m pip install requests
python tool_suite.py --output ./tool-results
スクリプトは設定したターン数の上限で各ケースを停止し、HTTPエラーまたはトランスポートエラーを記録して、失敗したケースも保持します。HTTP 200をアプリケーション上の合格と見なさず、レスポンス本文を読み、構造を検証してください。結果を共有する場合は、生の継続用メタデータや認証情報を公開しないでください。提供する証拠データでは、推論フィールドを削除しています。
次に、単一のテスト用データを自分のラベル付きケースに置き換えます。存在しない注文、引数の欠落、複数のツール呼び出し、不正なJSON引数、再試行できないエラー、タイムアウトするツールを追加してください。プロトコルを確立する間は、読み取り専用のツールを使いましょう。副作用のあるツールには、冪等性や明示的な認可など、追加の制御が必要です。再試行の指示だけでは安全性は確保できません。
合格したタスクをもとに選ぶ
小規模な連携テストの候補としては、3モデルとも妥当です。今回の特定のワークフローは、いずれも完了しました。観測された処理時間の順位は不安定すぎるため、本番向けのランキングにはできません。アプリケーションで使う予定の入力長、同時実行数、推論設定でテストを繰り返してください。
合格したタスクの割合、エンドツーエンドのレイテンシ、リクエスト数、照合済みの支出を記録します。最初の試行の失敗、形式修正の試行、フォールバックもタスクのコストに含めてください。低コストでも無効な結果が見かけ上の成功率を押し上げないよう、ユーザーに提示する結果の正しさは別に測定します。
デプロイ前に、アプリケーション側で最大ターン数とツールごとの再試行回数を制限してください。モデルは今回の1回再試行という指示に従いましたが、サービスをその動作が常に続くことに依存させるべきではありません。プロバイダーのエラー、ツールのエラー、出力検証の失敗は分けて扱ってください。それぞれ必要な対応が異なります。
次に取り組むのがツール利用ではなく構造化抽出の問題なら、Haikuの移行評価も参考になります。そこでも、構文、形式、事実関係を別々に確認する必要性が示されています。
よくある質問
- ツール利用で最も信頼性の高いモデルはどれですか?
- この6ケースのテストでは判断できません。3モデルとも、通常のワークフローとエラーを注入したワークフローをそれぞれ1回完了しました。信頼性で優劣を決める前に、代表性のあるラベル付きデータセットでテストし、失敗も報告してください。
- プロバイダーのレート制限からの復旧をテストしましたか?
- いいえ。注入したエラーは、ローカルの合成注文ツールで発生させたものです。プロバイダーからのHTTPエラー、レート制限、障害ではありません。
- モデルが要求したツールは何でも実行してよいですか?
- いいえ。実行前にツール名を許可リストと照合し、引数を検証し、対象レコードへのアクセス権を確認してください。このテスト用ツールができるのは、合成注文1件の読み取りだけで、外部への操作はできません。


