What does Gemini's tool_config mode ANY do, and when does it trap your loop?
answer
- Three modes on function_calling_config
- AUTO is the default choice-of-the-model
- ANY compels a call this turn
- allowed_function_names narrows the subset
- Forced forever means no final answer
basics
~20 sANY forces Gemini to emit a function call on that turn instead of free text, optionally restricted to allowed_function_names. Left on for the whole conversation it prevents the model from ever writing the final answer, so the loop keeps calling tools until you switch back to AUTO.
solid answer
~50 s`tool_config` carries a `function_calling_config` whose `mode` is AUTO, ANY or NONE. AUTO is the default: the model decides between text and a call. ANY constrains decoding so the turn *must* be a function call, and `allowed_function_names` narrows that to a chosen subset — useful for forcing a first step, for structured extraction, or for a deterministic routing hop. NONE keeps the declarations visible but forbids calling, which is how you force a natural-language wrap-up while the tool definitions stay in context. The trap is that mode is a per-request setting applied uniformly: if you reuse an ANY config for every iteration of your tool loop, the model can never produce the concluding text, so after each FunctionResponse it simply calls again until your iteration cap fires. Set ANY for the turn that needs it, then send the next request with AUTO or NONE.
code
python · 22 linesfrom google.genai import types
# Turn 1: force a call, and only this one
forced = types.GenerateContentConfig(
tools=tools,
tool_config=types.ToolConfig(
function_calling_config=types.FunctionCallingConfig(
mode="ANY", allowed_function_names=["classify_intent"]
)
),
)
# Later turns: let the model decide
free = types.GenerateContentConfig(tools=tools)
# Final turn: force a written answer from what was gathered
wrap_up = types.GenerateContentConfig(
tools=tools,
tool_config=types.ToolConfig(
function_calling_config=types.FunctionCallingConfig(mode="NONE")
),
)go deeper
Know the three modes by name — AUTO, ANY, NONE — and that AUTO, the default, lets the model choose between answering and calling a tool.
Explain that mode is a per-request setting, that allowed_function_names narrows ANY to a subset, and that NONE keeps declarations visible while forbidding calls.
Diagnose the forced-mode loop from its symptoms — repeated near-identical calls, no final text — and describe staging modes across turns to fix it.
Own mode as policy: where forcing a call is structurally required, how phases restrict the tool subset, and how a multi-provider abstraction maps these semantics without leaking one vendor's literals.
## The knob Alongside `tools`, a Gemini request can carry `tool_config`, and inside it `function_calling_config` with a `mode` and an optional `allowed_function_names` list. In the Python SDK that is `types.ToolConfig(function_calling_config=types.FunctionCallingConfig(mode=..., allowed_function_names=[...]))`, attached to `GenerateContentConfig`. **AUTO** is the default. The model reads the user's turn and the tool descriptions and decides for itself whether this turn is text or a call. Almost all normal traffic should run here. **ANY** forces the turn to be a function call. The model is no longer allowed to answer in prose for that request; it must pick a declared function and produce arguments. Combined with `allowed_function_names`, you can force not merely "a call" but "a call to one of these", down to a single name. **NONE** keeps the declarations in the request — so the model still knows what tools exist and what it previously called — while forbidding it to issue a new call. It must answer in words. ## What ANY is genuinely good for - **Deterministic first hop.** In a router, the first turn should always classify or dispatch. ANY plus a single allowed name makes that structural rather than hopeful. - **Structured extraction.** Declaring one function whose parameters are the record you want, and forcing ANY, turns the model into a schema-conformant extractor. (Gemini also has native structured-output support for pure JSON generation; forcing a tool is the right choice when the same call may also perform work.) - **Constraining a stage.** In a multi-stage agent, phase two might only be allowed to use the read-only tools; `allowed_function_names` expresses that far more reliably than a sentence in the system instruction. ## The trap, concretely A tool loop typically builds one config object and reuses it for every iteration. If that config pins ANY, here is what happens: the model calls a tool, you return the result, you re-send with the same config — which still says "you must call a function" — so the model calls something again. The result it just received cannot be turned into an answer, because answering is disallowed. The loop terminates only when your iteration ceiling fires, and the user gets your fallback text instead of a real answer. Symptoms are distinctive: repeated calls to the same tool with near-identical arguments, token spend climbing linearly, and no final text ever. The fix is to treat mode as a per-turn decision, not a client-level setting: 1. Turn 1: ANY (optionally with a single allowed name) to force the first call. 2. Turns 2..n: AUTO, so the model can either continue calling or conclude. 3. Optionally, once you judge you have enough information, or at the last iteration before your cap, send NONE to force a written answer from what has already been gathered. That last move is a useful production pattern in its own right: rather than truncating a runaway loop with a canned apology, flip to NONE and let the model summarise what its tools did return. ## Mode is not a substitute for validation Forcing a call constrains the *shape* of the turn, not the *quality* of the arguments. A model pushed into calling when it lacks information will invent plausible arguments — a customer id it never saw, a date it guessed. If a user says "hello" and your config forces ANY, you get a call with fabricated inputs. So use ANY where a call genuinely must happen, keep validation on the arguments, and prefer AUTO wherever "no tool needed" is a legitimate outcome. ## Cross-vendor confusion to avoid The three-mode idea recurs across vendors, but the vocabulary does not line up, and mixing it up is a visible interview error. Gemini says AUTO / ANY / NONE inside `function_calling_config`. OpenAI-shaped APIs use `tool_choice` with `auto` / `required` / `none`, plus a named-function form. Anthropic uses `tool_choice` with `auto` / `any` / `tool`. If you maintain a provider-agnostic layer, keep the mapping explicit in one place, and remember that "restrict to a subset" is expressed differently in each — Gemini uses a list of allowed names, others name a single function. ## Operational advice Log the mode alongside each request. When you are debugging "why did it call the tool eleven times", the mode in effect is the first thing to check, and it is invisible in the response.
- When would you deliberately send mode NONE with the tools still attached?To force a written conclusion while keeping context. The declarations and the prior calls stay visible, so the model can reason about what it already retrieved, but it cannot start another call. It is the graceful way to end a loop that is approaching its iteration or budget ceiling, in place of a canned fallback message.
- What goes wrong if you force ANY on a turn where no tool is appropriate?The model must still call something, so it fabricates arguments — an invented identifier, a guessed date — to satisfy the constraint. Your dispatcher then executes a meaningless call. Reserve ANY for turns where a call is genuinely required, and keep argument validation in place regardless of mode.
- How does Gemini's mode vocabulary map to other vendors' tool_choice settings?Conceptually AUTO/ANY/NONE line up with the common auto, force-a-call and disable settings other vendors expose on a tool_choice field, but the literal names differ per vendor and the way you restrict to specific functions differs too — Gemini takes a list of allowed_function_names. Keep the translation in one adapter and never assume a literal transfers.
saying these in an interview costs you the question
- Leaving ANY set for every turn of the loop
- Believing ANY guarantees the right tool is chosen
- Thinking NONE removes the tool declarations
- Using OpenAI's 'required' literal in Gemini config
- Assuming mode is a model property, not per request