Skip to main content

Tool Use / Function Calling

Have the model output structured call arguments based on tool (function) signatures you define. You execute the actual function on the backend and feed the result back to the model to close the conversation loop. This is the standard pattern for Agent workflows, automation, and external system integration.

When to Use

  • The model needs to call your business systems (look up orders, place orders, send messages, change configuration)
  • The model needs to access real-time data (databases, search, internal APIs)
  • You require strict schema on the output (better suited for dynamic dispatch than plain response_format JSON)
  • Building Agent / multi-step task loops

Don't need tool calling and just want output that strictly conforms to a schema? Use Structured Output instead—it's lighter weight.


Basic Usage (OpenAI Protocol / Chat Completions)

Three-step loop: define → model decides to call → you execute and return the result.

from openai import OpenAI

client = OpenAI(
api_key="your-api-key",
base_url="https://live-turing.cn.llm.tcljd.com/api/v1",
)

tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a specified city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}]

# Turn 1: model decides to call the tool
r1 = client.chat.completions.create(
model="turing/gpt-5.4-mini",
messages=[{"role": "user", "content": "What's the temperature in Shanghai today?"}],
tools=tools,
)

tool_call = r1.choices[0].message.tool_calls[0]
args = json.loads(tool_call.function.arguments)
# args == {"city": "Shanghai", "unit": "celsius"}

# Turn 2: execute the real function and return the result
r2 = client.chat.completions.create(
model="turing/gpt-5.4-mini",
messages=[
{"role": "user", "content": "What's the temperature in Shanghai today?"},
r1.choices[0].message, # contains tool_calls
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": '{"temp_c": 22, "condition": "Sunny"}',
},
],
tools=tools,
)

print(r2.choices[0].message.content)
# -> "It's 22°C and sunny in Shanghai today."
curl $TURING_BASE_URL/chat/completions \
-H "Authorization: Bearer $TURING_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "turing/gpt-5.4-mini",
"messages": [{"role": "user", "content": "What'\''s the temperature in Shanghai today?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a specified city",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}
}]
}'

Controlling Tool Selection (tool_choice)

ValueBehavior
"auto" (default)The model decides whether to call a tool
"none"Disables tool calling; forces a normal conversational reply
"required"The model must call at least one tool
{"type": "function", "function": {"name": "X"}}Forces a call to the specified tool X

Parallel Tool Calls

Most modern models (GPT-4.1+, Claude 3.5+, Gemini 2.0+, Qwen-Plus) support issuing multiple parallel tool calls in a single response by default. To disable: parallel_tool_calls: false.


Anthropic Messages Protocol (Claude Native)

The Claude native protocol uses different field names for tool definitions—input_schema instead of parameters—and tool results are returned via a tool_result content block.

from anthropic import Anthropic

client = Anthropic(
base_url="https://live-turing.cn.llm.tcljd.com/api/v1",
auth_token="your-api-key",
)

tools = [{
"name": "get_weather",
"description": "Get the current weather for a specified city",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
},
"required": ["city"]
}
}]

# Turn 1
r1 = client.messages.create(
model="turing/claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "What's the temperature in Shanghai today?"}],
tools=tools,
)

# Find the tool_use block
tool_use = next(b for b in r1.content if b.type == "tool_use")

# Turn 2: return the result
r2 = client.messages.create(
model="turing/claude-sonnet-5",
max_tokens=1024,
messages=[
{"role": "user", "content": "What's the temperature in Shanghai today?"},
{"role": "assistant", "content": r1.content},
{
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": '{"temp_c": 22, "condition": "Sunny"}'
}],
},
],
tools=tools,
)

Claude Native tool_choice

{"type": "auto"} // default
{"type": "any"} // must call a tool
{"type": "tool", "name": "X"} // force a specific tool
{"type": "none"} // disable tool calling

Provider Differences at a Glance

ProviderSchema FieldTool Result ReturnForce CallParallelNotes
OpenAIparameters"role": "tool" + tool_call_id + contenttool_choice: "required" or explicit function objectFull JSON Schema support
Anthropicinput_schematool_result content blocktool_choice: "any" or explicit tool objectSupports cache_control for caching tool definitions
Geminiparameterstool role messagetool_choice: "any"Gemini 3: multi-turn requires thought_signature (see Gemini Thought Signatures)
Qwenparameters"role": "tool"explicit tool_choice objectRecommended: qwen-plus / qwen-max series; some smaller models have poor JSON stability
DeepSeekparameters"role": "tool"tool_choiceEnabling reasoning mode in DeepSeek R1 may reduce tool call stability

Cross-provider best practice: When using the Chat Completions protocol, Turing automatically maps OpenAI-style tools to each provider's native format—just write to the OpenAI spec and you won't need to worry about provider differences in most cases. For Claude, you can also use the Messages protocol for finer-grained control.


Parameter Reference

For full request/response schemas, see:


Billing Impact

Tool definitions themselves consume input tokens (description, parameters, and input_schema are all encoded). The more tools you define—and the more verbose they are—the higher your input costs. Recommendations:

  • Once tool definitions stabilize, use Prompt Caching to cache the tools block (Claude supports cache_control)
  • Keep description concise to avoid repeating long text on every request
  • Keep the number of tools per request under 20; more than that degrades model accuracy

FAQ

  • The model never calls the tool → Change tool_choice from auto to required, or emphasize "use tools when necessary" in the system prompt
  • Malformed argument JSON / hallucinated parameters → More likely with lightweight or near-deprecated models; upgrade to a more capable recent model, or add stronger constraints in description
  • Gemini 3 multi-turn returns 400 → Missing thought_signature; see Gemini Thought Signatures
  • Not receiving all results from parallel calls → Ensure Turn 2 messages include a corresponding tool message for every tool_call_id

See also