# ツール呼び出し、最初から最後まで > tool_choice を "auto" にした往復 1 回です。ツールを要求されたら実行し、結果を返します。 Source: https://kiln.bricksum.com/docs/ja/cookbook/tool-calling tool_choice を "auto" にした往復 1 回です。モデルがツールを要求したらコードが実行し、その結果を tool メッセージとして返して最終回答を受け取ります。このレシピは auto のみを扱います。ツールを呼ぶかどうかはモデルが決め、強制的な tool_choice は扱いません。 ## クイックスタート **Python** ```python import json, os from openai import OpenAI client = OpenAI(base_url="https://api.bricksum.com/v1", api_key=os.environ["KILN_API_KEY"]) tools = [{"type": "function", "function": { "name": "get_weather", "description": "Current weather for a city", # required "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}, }}] messages = [{"role": "user", "content": "What's the weather in Seoul?"}] first = client.chat.completions.create(model="qwen3-32b", messages=messages, tools=tools, tool_choice="auto", max_tokens=800) choice = first.choices[0] # The model may answer without calling the tool, so this is a check, not an index. if not choice.message.tool_calls: print(f'no tool call, finish_reason: "{choice.finish_reason}"') print("answer:", choice.message.content) else: # One reply can carry several tool calls; answer every one of them. messages.append(choice.message) for call in choice.message.tool_calls: print("tool call:", call.function.name, call.function.arguments) messages.append({"role": "tool", "tool_call_id": call.id, "content": json.dumps({"temp_c": 27})}) final = client.chat.completions.create(model="qwen3-32b", messages=messages, max_tokens=800) print("final:", final.choices[0].message.content) ``` **TypeScript** ```javascript import OpenAI from "openai"; const client = new OpenAI({ baseURL: "https://api.bricksum.com/v1", apiKey: process.env.KILN_API_KEY }); const tools: OpenAI.ChatCompletionTool[] = [{ type: "function", function: { name: "get_weather", description: "Current weather for a city", // required parameters: { type: "object", properties: { city: { type: "string" } }, required: ["city"] }, }}]; const messages: OpenAI.ChatCompletionMessageParam[] = [ { role: "user", content: "What's the weather in Seoul?" }, ]; const first = await client.chat.completions.create({ model: "qwen3-32b", messages, tools, tool_choice: "auto", max_tokens: 800 }); // The model may answer without calling the tool, so this is a check, not an index. const calls = first.choices[0].message.tool_calls ?? []; if (calls.length === 0) { console.log('no tool call, finish_reason: "' + first.choices[0].finish_reason + '"'); console.log("answer:", first.choices[0].message.content); } else { // One reply can carry several tool calls; answer every one of them. messages.push(first.choices[0].message); for (const call of calls) { if (call.type !== "function") continue; console.log("tool call:", call.function.name, call.function.arguments); messages.push({ role: "tool", tool_call_id: call.id, content: JSON.stringify({ temp_c: 27 }) }); } const final = await client.chat.completions.create({ model: "qwen3-32b", messages, max_tokens: 800 }); console.log("final:", final.choices[0].message.content); } ``` ## 確認 スクリプトを実行します。1 回目の呼び出しの message.tool_calls には、city を含む JSON 引数とともに get_weather が入っています(項目が複数あることがあり、それぞれに tool メッセージを返します)。2 回目の呼び出しの message.content はモデル自身の言い回しのため実行のたびに変わりますが、渡した気温(27)に言及します。 **expected output** ```text tool call: get_weather { "city": "Seoul" } final: The current temperature in Seoul is **27 °C**. ``` ## 設定リファレンス - `tool_choice` = `"auto"` — モデルがツールを呼ぶかどうかを決めます。"required" や関数を指定する強制選択には対応していません。qwen3-32b では、強制的なツール選択が明確なエラーなしに失敗します(2026-09-26 実測)。tool_choice を "required" にすると本文が平文 "Internal Server Error" の HTTP 500 が返り、関数を指定すると arguments が空の 200(finish_reason "stop")が返ります。どちらも 4xx ではないため、再試行では解決しません。tool_choice は "auto" のみを送ってください。 - `tools[].function.description` = `required` — すべてのツールに必要です。ないと 400 で拒否されます。 - `parallel_tool_calls` = `not applied` — モデルページで並列ツール呼び出しが対応となっているモデルでのみ適用され、qwen3-32b は該当しません。gpt-oss-120b での実測では 200 で受け付けられ、モデルは応答ごとに最大 1 つのツールしか呼び出しませんでした。qwen3-32b では parallel_tool_calls の設定が適用されません。これは応答のツール呼び出しが 1 つだけという意味ではありません。parallel_tool_calls: false を送っても、1 つの応答に複数のツール呼び出しが含まれることがあります。tool_calls のすべての項目に、それぞれ tool メッセージで応答してください。 - `max_tokens` = `800` — reasoning トークンもこの上限内で課金されます。値が小さいと、tool_calls が出る前に finish_reason が "length" で終わることがあります。 ## 制限事項 - 強制的な tool_choice("required"、または特定の関数の指定)には対応していません。このレシピは "auto" のみを扱います。qwen3-32b では、強制的なツール選択が明確なエラーなしに失敗します(2026-09-26 実測)。tool_choice を "required" にすると本文が平文 "Internal Server Error" の HTTP 500 が返り、関数を指定すると arguments が空の 200(finish_reason "stop")が返ります。どちらも 4xx ではないため、再試行では解決しません。tool_choice は "auto" のみを送ってください。 - qwen3-32b では parallel_tool_calls の設定が適用されません。これは応答のツール呼び出しが 1 つだけという意味ではありません。parallel_tool_calls: false を送っても、1 つの応答に複数のツール呼び出しが含まれることがあります。tool_calls のすべての項目に、それぞれ tool メッセージで応答してください。 ## トラブルシューティング - `HTTP 500 Internal Server Error` — リクエストの tool_choice が "required" です。qwen3-32b では 4xx ではなく平文の 500 で失敗します(2026-09-26 実測)。 tool_choice を "auto" にしてください。同じリクエストを再試行しても同じ 500 が返ります。 - `"arguments": ""` — リクエストが tool_choice で関数を指定しています。qwen3-32b では arguments が空の 200(finish_reason "stop")が返り(2026-09-26 実測)、JSON.parse / json.loads が失敗します。 tool_choice を "auto" にし、どのツールを期待するかをプロンプトで説明してください。arguments をパースする前に tool_calls を確認してください。 - `"message": "missing or malformed API key"` — KILN_API_KEY が未設定、失効している、またはスクリプトを実行するシェルに export されていません。 コンソール → API キーで新しいキーを発行し、再度 export してください。 - `"code": "model_not_found"` — model フィールドが、自分のキーで GET /v1/models を呼んだときに返る ID ではありません。 モデルページか GET /v1/models で一覧を確認し、そこにある ID(例:"qwen3-32b")を model に設定してください。 - `finish_reason: "length"` — モデルがツールを呼ぶかどうかの reasoning を終える前に max_tokens に達しました。 クイックスタートが送る 800 より大きく、たとえば 1600 に、1 回目の create 呼び出しの max_tokens を上げてください。 メモ このページのスニペットは 2026-09-21 に openai 3.16.2 (Python 3.14.7), openai 7.20.0 (Node 24.12.0) の環境で Kiln API に対して実行し、動作を確認しています。 未確認の項目: この実行は gpt-oss-120b で行いました。ページは現在 qwen3-32b を使っており、このモデルではまだ実行していないため、以下の想定出力と異なる場合があります。両クイックスタートタブのツール非呼び出し分岐はこの実行の後に追加されたもので、ステージングではなくローカルのスタブサーバーでのみ確認しています。すべてのツール呼び出しに応答するループはさらにその後に追加されたもので(#760)、API に対して実行していません。