ツール呼び出し、最初から最後まで
tool_choice を "auto" にした往復 1 回です。モデルがツールを要求したらコードが実行し、その結果を tool メッセージとして返して最終回答を受け取ります。このレシピは auto のみを扱います。ツールを呼ぶかどうかはモデルが決め、強制的な tool_choice は扱いません。
クイックスタート
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)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
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 に対して実行していません。