Mastering Function Calling: Deep Dive into tools, tool_choice, and parallel_tool_calls for LLMs
This guide explains the three core Function Calling parameters—tools, tool_choice, and parallel_tool_calls—showing how to design tool schemas, control model autonomy, choose parallel execution, and avoid common pitfalls, with concrete Python and JavaScript examples and a cross‑platform comparison.
Function Calling turns LLMs from pure text generators into action‑oriented agents
Without tool calls a model can only generate text based on the prompt. It can describe how to fetch weather data but cannot actually retrieve the data or push results into a system. Function Calling lets the model propose a structured tool_call, which the application executes and feeds the result back, completing a closed‑loop interaction.
The three parameters that control Function Calling
tools – an array of JSON‑schema tool definitions that describe what external capabilities the model may use.
tool_choice – a string or object that controls whether the model may call a tool, and which one.
parallel_tool_calls – a boolean that decides if the model can return multiple tool_calls in a single round.
1. tools – the model’s tool catalog
Each entry is a JSON schema with three important fields:
Name – use a verb‑object pattern (e.g. get_weather, create_order) so the model instantly understands the purpose.
Description – explain both what the tool does and when it should be used; the model relies solely on this text.
Parameters – a strict JSON schema (types, enums, required fields, field‑level descriptions). The tighter the schema, the less the model will hallucinate arguments.
Example of a well‑crafted tool definition:
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Fetch real‑time weather for a specified city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. 'Beijing' or 'Beijing'"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "description": "Temperature unit"}
},
"required": ["city"]
}
}
}2. tool_choice – autonomy control
Common values: "auto" – let the model decide whether to call a tool (default for open‑ended assistants). "none" – forbid tool calls, forcing a pure text response. "required" – require the model to call at least one tool before answering (useful for workflow entry points).
Object {"type":"function","function":{"name":"get_weather"}} – force a specific tool (deterministic single‑step tasks).
3. parallel_tool_calls – execution rhythm
Set to true when the tasks are independent (e.g., fetch weather for three cities in one round). Keep false when later steps depend on earlier results, because parallelism can break the logical chain.
Full Function‑Calling flow
The end‑to‑end process consists of four steps:
Step 1: Send request with <code>tools</code>
Step 2: Model returns <code>tool_calls</code>
Step 3: Application executes the tool(s) and sends the result(s) back
Step 4: Model produces the final natural‑language answerThe model never executes the tool itself; it only proposes a structured call.
Minimal Python demo
import json, openai
client = openai.OpenAI()
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Fetch real‑time weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string", "description": "City name"}},
"required": ["city"]
}
}
}]
messages = [{"role": "user", "content": "Beijing weather today?"}]
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto"
)
assistant_msg = response.choices[0].message
if assistant_msg.tool_calls:
for call in assistant_msg.tool_calls:
args = json.loads(call.function.arguments)
if call.function.name == "get_weather":
city = args["city"]
weather = f"{city} is sunny, 25°C"
messages.append({"role": "tool", "tool_call_id": call.id, "content": weather})
final = client.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)
print(final.choices[0].message.content)
else:
print(assistant_msg.content)JavaScript / Vercel AI SDK demo
import { generateText, tool } from "ai";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
const { text, toolCalls } = await generateText({
model: openai("gpt-4o"),
messages: [{ role: "user", content: "Beijing weather?" }],
tools: {
get_weather: tool({
description: "Fetch real‑time weather",
parameters: z.object({ city: z.string().describe("City name") }),
execute: async ({ city }) => `${city} is sunny, 25°C`
})
},
toolChoice: "auto",
maxSteps: 5
});
console.log(text);Multi‑tool collaboration modes
Parallel – independent tasks (e.g., weather for Beijing, Shanghai, Guangzhou) can be issued in one round with parallel_tool_calls=true.
Sequential – later steps depend on earlier results (e.g., fetch recent order ID then fetch its logistics). Keep parallel_tool_calls=false and let the model call the next tool after receiving the previous result.
Chain – complex multi‑round workflows. Use a loop with a hard maxSteps limit to avoid infinite recursion.
Platform support (summary)
OpenAI – full support for tools, tool_choice (auto/none/required/object), and parallel_tool_calls. Strict schema mode available.
DeepSeek, Kimi, Yi (Zero‑One) – high compatibility with OpenAI‑style APIs; all three parameters work similarly.
Anthropic Claude – supports tool calls but returns tool_use blocks instead of OpenAI’s tool_calls. Requires custom handling of the message structure.
Google Gemini – uses function_declarations for tool definitions; supports auto/none/object choices; parallel support depends on the endpoint.
MiniMax, Meta‑Llama‑based services – support the three parameters with minor differences in response format.
Common pitfalls and how to avoid them
Overly broad tool schemas – vague names and descriptions cause the model to pick the wrong tool. Use precise verb‑object names and detailed field descriptions.
Missing error propagation – if a tool fails, return a clear error message as a tool result so the model can decide to retry, degrade, or inform the user.
Infinite loops – unbounded loops or missing maxSteps lead to runaway calls. Enforce a hard iteration limit and ensure each tool returns a definitive completion signal.
Overusing tools – not every request needs an external call. For pure explanations, summarizations, or translations, keep the model in pure generation mode ( tool_choice="none").
When to use Function Calling
Use it when the user needs real‑time data, side‑effects, or multi‑system orchestration (e.g., fetching weather, creating tickets, querying a database, invoking a rule engine). For simple explanations, summarizations, or rewrites, stick to plain generation.
Key takeaways
toolsdefines the capability boundary; high‑quality schemas directly improve call accuracy. tool_choice balances flexibility and control – auto for open assistants, required or explicit objects for deterministic flows. parallel_tool_calls governs execution rhythm; enable it only when tasks are independent.
The interaction loop is a closed feedback cycle: model proposes → application executes → result fed back → model finalizes.
Production failures usually stem from loose schemas, unhandled errors, or missing stop conditions.
Avoid unnecessary tool calls to keep systems simple, cost‑effective, and stable.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Qborfy AI
A knowledge base that logs daily experiences and learning journeys, sharing them with you to grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
