tool_choice Options
On this page
Deciding Who Decides
Passing tools to the API doesn’t mean the model must use them, or even that it’s allowed to. Between these two extremes, the tool_choice parameter acts as the arbiter. Depending on the value you assign, you can leave the model free to judge, force it to produce a structured call, or completely revoke its access to the tools you just declared. It’s a subtle setting, but it radically changes how the same code behaves when faced with the same question.
| Value | Behavior | Use Case |
|---|---|---|
auto | The model freely decides whether to use a tool or respond in text. This is the default mode. | Conversational assistants, general-purpose chatbots |
any | The model is forced to call at least one tool. It cannot respond with text alone. | Automated pipelines, structured data extraction |
none | The model cannot use tools, even if they are defined. It responds in text only. | Summarizing results without new calls, rephrasing a response |
required | Synonym for any. The model must use at least one tool. | Equivalent to any, for compatibility |
auto — Let the Model Judge
This is the default value, the one you use without realizing it whenever you omit the parameter.
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools,
tool_choice="auto" # Optional, this is the default value
)
With auto, the model evaluates the request before acting. If the question demands external data it doesn’t possess, it calls a function; if the query is general or relational, it responds directly in text. This switch is what makes an assistant feel natural: the user doesn’t need to know there are tools behind the screen.
# The model will call a function
messages = [{"role": "user", "content": "Status of payment T1001?"}]
# → tool_call: retrieve_payment_status(transaction_id="T1001")
# The model will respond directly
messages = [{"role": "user", "content": "Hello, how are you?"}]
# → content: "Hello! I am a payment assistant. How can I help you?"
The second example deserves attention: forcing a function call on a simple greeting would produce a retrieve_payment_status with a made-up ID. That’s precisely what auto prevents, which is why this mode remains the most common in production.
any — Enforce Structured Output
Conversely, there are contexts where free-form text has no value to you. An ingestion pipeline processing ten thousand documents expects usable JSON, not a polite sentence. With any, the model must call at least one function, even if the question doesn’t seem to require it.
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools,
tool_choice="any"
)
Interestingly, the most frequent use case repurposes function calling from its original intent: you aren’t declaring a function to execute it, but to guarantee an output schema. Here, extract_entities will never be called on the server side; we care about the JSON contract.
# Structured extraction: force the model to extract data
tools = [{
"type": "function",
"function": {
"name": "extract_entities",
"description": "Extracts named entities from text",
"parameters": {
"type": "object",
"properties": {
"persons": {"type": "array", "items": {"type": "string"}},
"organizations": {"type": "array", "items": {"type": "string"}},
"dates": {"type": "array", "items": {"type": "string"}}
},
"required": ["persons", "organizations", "dates"]
}
}
}]
messages = [{"role": "user", "content": "Sophia Yang from Mistral presented "
"function calling on March 15, 2026."}]
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools,
tool_choice="any" # Force extraction
)
# → extract_entities(persons=["Sophia Yang"],
# organizations=["Mistral"],
# dates=["March 15, 2026"])
Note required in passing: it’s a synonym for any, included for compatibility with code written for other APIs. Both values produce the same behavior; choose the one that makes your code most readable and stick with it.
none — Disable Tools Without Removing Them
The third value addresses a less obvious need. You’ve already retrieved your data via several tool_calls, and now you want the model to synthesize them. If you continue the conversation in auto mode, nothing stops the model from calling a function again “just to check,” which increases latency and can trigger an infinite loop. none forbids any new calls while keeping the tool definitions in context, helping the model speak accurately about the data it already has.
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools,
tool_choice="none"
)
Three use cases come up constantly: requesting a summary after a series of function calls, rephrasing a response without triggering new calls, and testing in development how the model behaves when deprived of its tools, which quickly reveals questions it thinks it can answer from memory.
# After retrieving data via tool_calls...
# Request a summary without new calls
messages.append({
"role": "user",
"content": "Summarize the information from all the payments you found."
})
response = client.chat.complete(
model="mistral-large-latest",
messages=messages,
tools=tools,
tool_choice="none" # The model summarizes with what it already has
)
Adjust the Setting Throughout the Conversation
You don’t have to set tool_choice once at startup. In production, it’s common to calculate it at each turn based on context: the first message of the conversation, intent detected in the request, or the number of calls already consumed. The function below illustrates this logic in its simplest form—the model remains free by default, except when the user explicitly requests a summary.
def get_tool_choice(user_input: str, turn_count: int) -> str:
"""Determines tool_choice based on context."""
# First turn: let the model decide
if turn_count == 0:
return "auto"
# If the user explicitly requests a summary
if any(word in user_input.lower() for word in ["summarize", "recap", "synthesis"]):
return "none"
# Default
return "auto"
In practice, keep auto as your baseline setting: it’s the most natural and flexible. Reserve any for structured data extraction pipelines, and none for moments when you must prevent infinite tool_call loops. One constraint is absolute: never change tool_choice in the middle of processing a tool_call. First complete the cycle by returning all expected tool messages, then adjust the parameter on the next turn—otherwise, the history becomes inconsistent and the API rejects the request.
Key Takeaways
tool_choicecontrols when the model uses functionsauto= the model decides freely (default, recommended for chatbots)any= the model is forced to call a tool (pipelines, structured extraction)none= tools are disabled (summaries, rephrasing)- Adapt
tool_choicedynamically based on conversation context