What do OpenAI's tool_choice values auto, required and none do?
answer
- Four settings, one field
- The default depends on whether tools were sent
- Forcing a call is not free
- Turn-dependent, not request-template-wide
- 'none' still pays for the definitions
basics
~20 stool_choice auto lets the model decide and is the default when tools are present; required forces at least one tool call this turn; none forbids calling while still sending the definitions; and a named function object pins the call to that one function.
solid answer
~40 s`tool_choice` controls whether the model may, must, or must not emit tool calls on a given request. `"auto"` — the default whenever `tools` is non-empty — lets the model choose between prose and one or more calls. `"required"` guarantees the turn contains at least one tool call and no plain answer, which is useful for a routing step. `"none"` forbids calls entirely, but the tool definitions are still serialized into the prompt and still billed as input tokens, so it is not the same as omitting `tools`. Passing `{"type": "function", "function": {"name": "x"}}` forces exactly that function. The classic bug is leaving `"required"` set on the follow-up request after returning tool results: the model must call again, so the loop never terminates in prose.
code
python · 20 linesimport os
from openai import OpenAI
client = OpenAI()
MODEL = os.environ["OPENAI_MODEL"]
def choice_for(step: int, last: bool) -> object:
if step == 0:
return "required" # the routing turn must call a tool
if last:
return "none" # final turn must answer in prose
return "auto" # ordinary turns: model decides
resp = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": "Weather in Oslo?"}],
tools=tools,
tool_choice=choice_for(step=0, last=False),
)
print(resp.choices[0].finish_reason) # 'tool_calls'go deeper
Remember the three string values and that auto is the default when you supply tools. Say that required makes a call mandatory and none suppresses it.
Explain that none still ships and bills the definitions, that the function-object form pins a specific call, and that required guarantees at least one call rather than exactly one.
Show the turn-dependent policy: required on a routing turn, auto in the middle, none on the final synthesis turn — and diagnose an agent that never answers as a stuck required setting.
Frame it as control-flow policy across a fleet of agents: which turns are allowed to be free-form, how forced routing interacts with cost and cache-prefix stability, and where a structured-output call is the cheaper design than a pinned tool.
## The knob and its four settings `tool_choice` sits alongside `tools` on a Chat Completions request and decides the *shape* of the assistant turn: - **`"auto"`** — the model may answer in prose, may emit one or more tool calls. This is the default whenever you supply a non-empty `tools` array, and it is what you want for almost all conversational turns. - **`"none"`** — the model must answer in prose; it will not emit `tool_calls`. This is the implicit default when no tools are supplied. - **`"required"`** — the model must emit at least one tool call; it cannot answer in prose on this turn. - **A function object**, `{"type": "function", "function": {"name": "get_weather"}}` — the model must call that specific function. Arguments are still model-generated; only the selection is pinned. ## Why 'none' is not the same as omitting tools With `tool_choice: "none"` the definitions are still part of the request, so they are still serialized into the prompt and still counted as input tokens. What changes is only whether the model is permitted to produce a call. That has two practical consequences. It costs you money for capability you have just disabled — if you truly do not want tools this turn, omit the array. And it keeps the prompt prefix byte-identical to your tool-enabled turns, which is exactly what you want when you are relying on prefix caching and only want to suppress calls for the final synthesis step. ## The routing pattern: 'required' `"required"` shines when the tool call *is* the product of the turn. Classifying an inbound support ticket into one of six handlers, extracting parameters for a search, deciding which sub-agent to hand off to — in each case a prose answer is a failure mode, and `"required"` removes it. The model still chooses *which* tool unless you also pin the name. A pinned function object narrows that further and is the honest way to do one-shot argument extraction: you already know what must happen, you are using the model only to fill the schema. At that point compare it with the structured-output route, which produces a typed object without the tool-call round trip; pinning a tool is preferable mainly when the same code path sometimes needs the model to choose. ## The infinite-loop trap The single most common production bug with this parameter: a developer sets `tool_choice: "required"` because the first turn must call a tool, then reuses the same request-building function for every subsequent turn of the agent loop. Now, after you return tool results, the model is *still* forbidden from answering in prose — so it calls another tool, you return another result, and the loop runs until your step cap fires (or your bill does). The fix is to make the setting turn-dependent: `"required"` on the first request, `"auto"` afterwards, and optionally `"none"` on the final step so the model is compelled to produce the user-facing answer instead of asking for one more lookup. ## Interaction with other controls `tool_choice` composes with `parallel_tool_calls`: `"required"` guarantees *at least* one call, not exactly one; if parallel calls are enabled the model may return several in a single assistant message. If you need exactly one, pair `"required"` with `parallel_tool_calls: false`. It also composes with `finish_reason`. Under `"required"` you should expect `"tool_calls"` on every turn, so code that only checks for `"stop"` as the exit condition will hang. Under `"auto"` you must handle both branches on every response — assuming a tool was called because tools existed is a bug that surfaces the first time the user says "thanks". ## Cross-vendor naming The concept is near-universal but the vocabulary is not, and interviews probe whether you have actually read the docs for the vendor you claim to use. Some vendors spell the force-a-call mode `"any"` rather than `"required"`, and some carry the whole setting in a nested config object rather than a top-level field. In OpenAI's Chat Completions API the field is `tool_choice` and the forcing value is `"required"`; reciting a neighbouring vendor's spelling is a tell. ## Deprecated ancestor Older code carries `function_call` alongside a `functions` array — the pre-tools formulation, where forcing looked like `function_call: {"name": "x"}`. That pair is superseded by `tools`/`tool_choice`, which additionally support multiple calls per turn. If you meet it in a codebase, migrating is mechanical: wrap each function definition in `{"type": "function", "function": {...}}` and translate the choice value.
- You want exactly one tool call, never two. How do you guarantee that?Combine tool_choice "required" with parallel_tool_calls set to false. "required" only guarantees at least one call, so with parallel calls enabled the model can legitimately return several in one assistant message. Disabling parallel calls caps the message at a single entry. If you also know which function must run, pin it with the function object form and you have fully determined the shape of the turn.
- Is pinning a function with tool_choice a good way to get structured data out of the model?It works — you get a JSON arguments payload conforming to the function's schema — but it is a round-trip protocol used for a one-way job. If you never intend to execute anything, the response-format route gives you the object directly without the assistant/tool message dance. Pinning is the better fit when the same code path sometimes lets the model choose a tool and sometimes forces one.
- What does finish_reason look like across a loop that starts with required and ends with none?The first response comes back with finish_reason "tool_calls" because prose was forbidden. Intermediate turns under "auto" may be either "tool_calls" or "stop", so branch on whether message.tool_calls is populated rather than on the reason alone. The final turn under "none" comes back "stop" with content, which is your exit signal — never "tool_calls", because calls were disallowed.
saying these in an interview costs you the question
- Saying tool_choice 'none' removes the definitions from the prompt
- Calling the forcing value 'any' — that is another vendor's spelling
- Leaving tool_choice 'required' set on every turn of the loop
- Believing 'required' guarantees exactly one call
- Assuming tools are always called when tool_choice is omitted