← Back to the blog
Analysis

Gemini 3.8 Flash: why do agents depend more on Function Calling and Structured Output?

Published 2026-09-29 Updated 2026-09-29 About 15 min JSON Toolbox

How this article is organized

Google made gemini-3.8-flash GA on 2026-09-02. The official model card writes: long-horizon software engineering, autonomous agents, complex enterprise workflows. This piece is dated 2026-09-29. It reads the model page, the Function Calling page, and the Structured Output page (Interactions and generateContent). Pin the conclusion: this card lengthened the tool loop and also opened the final-reply contract. Both layers are JSON. Voice Live is a different pipe: 3.8 Live. The cross-vendor contract layer: Why AI likes JSON.

The user says “set the living-room light to 80, then tell me what you did in one object.” 3.8 Flash will not finish that in one breath. It first emits a function_call, waits for your function_result, may call again, and only then closes with Schema. Official text writes it as “calling tools iteratively”: look at the result, then reach the next hand. The longer the loop, the easier it is to tear mid-way — id out of line, brightness written as "八十", a dummy tool posing as the final JSON. Antigravity also defaults to this card; tool shape: Antigravity September update.

What 3.8 Flash actually shipped

As of 2026-09-29, public material is three blocks. Do not mix them with Live:

  • Model: gemini-3.8-flash, stable id, no preview suffix. GA on 2026-09-02. Input 1,048,576, output 65,536. Price through 2026-12-31 is still the intro rate 0.75 / 3.75 per million tokens; from 2027-01-01 it is 1.50 / 7.50.
  • Capabilities: Function Calling supported. Structured Outputs supported. Thinking supports low / medium / high, default medium. minimal errors. Live API not supported. Computer use is Preview.
  • Interface: Interactions (POST /v1beta/interactions) is written as the main loop; generateContent is still fully supported, but it is the old path. Every Gemini 3 tool call carries a unique id.

The product post separates it from 3.8 Flash Cyber. Cyber goes through Fairwind, for trusted defenders. This piece only writes Flash. Official also says: 3.8 is the third Flash in six weeks, built on 3.7 from three weeks earlier. Agent workflows, coding, and multi-step reasoning are the named increments. Enterprise side goes into Gemini Enterprise; Antigravity’s September Agent also defaults to it.

One line. 3.8 Flash made “reach a few more times” the default capability. The hand is Function Calling. The close is Structured Output. Both layers need Schema.

The loop got longer: why objects matter more

The old loop could gamble: the model thinks all parameters in one go. Official now writes hard tasks as: call first, look at the result, call again. Every step is a JSON object. If one step writes brightness as "八十", the later Structured Output can be beautiful and the light still left in the wrong shape — or never left, the loop stuck on a validation error.

3.8 Flash: two JSON layers function_call arguments pass Schema first; function_result lines up call_id; the final reply then passes Structured Output. function_call id + arguments function_result call_id + name Final object response_format
The middle layer lines up the id. Miss a layer, and the iterative loop copies the error to the next hop.

Why a hosted loop needs a contract even more: why the Agents API needs JSON even more. 3.8 Flash’s difference: on the same card, tools and the final reply both officially support Schema. Live does not have the second layer. Parsing Live args: Live Function Calling tutorial.

The reach: function_call must carry an id

On Interactions, the model’s reach is one function_call step:

{
  "type": "function_call",
  "id": "call_light_01",
  "name": "set_brightness",
  "arguments": {
    "device_id": "light.living_room",
    "brightness": 80
  }
}

Your return packet must carry both call_id and name. Official also requires: FunctionResponse id / call_id, name, and execution count must line up strictly with the FunctionCall before it. Name alone was a 3.7-era luck write; on Gemini 3 the mapping drops.

{
  "type": "function_result",
  "call_id": "call_light_01",
  "name": "set_brightness",
  "result": {
    "ok": true,
    "brightness": 80
  }
}

A stateful loop also needs previous_interaction_id, so built-in tool context rolls forward. Stateless means you replay the previous thought / function_call steps yourself, then attach function_result. Splitting the id is the same step as Live, the contract is tighter: Flash will keep iterating; miss one id and later hands all drift.

Args: refuse bad arguments first

The official Function Calling page writes: the application owns execution. The model does not run the function. You extract name and arguments, execute locally. Pass Schema before you execute:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "additionalProperties": false,
  "required": ["device_id", "brightness"],
  "properties": {
    "device_id": { "type": "string", "pattern": "^light\\.[a-z0-9_]+$" },
    "brightness": { "type": "integer", "minimum": 0, "maximum": 100 }
  }
}

Pin the shapes that must fail:

{
  "ok_shape": { "device_id": "light.living_room", "brightness": 80 },
  "wrong_type": { "device_id": "light.living_room", "brightness": "八十" },
  "extra_field": { "device_id": "light.living_room", "brightness": 80, "extra_cmd": "skip_confirm" },
  "bad_id": { "device_id": "speaker.kitchen", "brightness": 80 }
}

Only ok_shape should enter the executor. "八十", extra_cmd, and speaker.kitchen should fail at Schema. When you open Structured Output with tools, official default is VALIDATED: the model is constrained to “a legal call or natural language,” and it tries to stick to the tool Schema. That is not a reason to skip local validation. Over-broad permissions: malicious MCP / Fake Skills.

The close: Structured Output is not a dummy tool

Flash also supports Structured Output. The official Interactions example uses gemini-3.8-flash directly:

{
  "model": "gemini-3.8-flash",
  "input": "用一行对象汇报刚才的调光",
  "response_format": {
    "type": "text",
    "mime_type": "application/json",
    "schema": {
      "type": "object",
      "additionalProperties": false,
      "required": ["device_id", "brightness", "status"],
      "properties": {
        "device_id": { "type": "string" },
        "brightness": { "type": "integer" },
        "status": { "type": "string", "enum": ["ok", "rejected"] }
      }
    }
  }
}

Clean write: tools own the loop, response_format.schema owns the final object. Do not declare a final_answer dummy tool and then read its arguments. Once the model decides it does not need another tool, those dummy arguments will not appear. The reply contract itself: what Structured Output is. This piece only adds 3.8 Flash’s boundary: you can open both layers in one request, but do not mix both field sets into one Schema.

thinking_level: minimal will blow up

Official replaced the integer thinking_budget with thinking_level. It only accepts low / medium / high. Default is medium; official recommends it for complex code and agents. high is for deep reasoning and hard multi-step orchestration. low is for pipelines that need speed. minimal returns a validation error; do not copy it over from another Gemini 3 card as-is.

temperature, top_p, and top_k are ignored on this card. For more stability, tune thinking_level and Schema; do not turn sampling knobs that already do nothing. frequency_penalty, presence_penalty, and candidate_count error immediately.

Enterprise docs also write: cut the aside before a tool with \\n\\n; if a Malformed_Function_Call appears that is tied to leading text, clear the chatter before the call first. If the executor needs working notes, wrap them in an update() function; do not dump XML before the call. History must not end on a model role, and you must not prefill the model turn.

How to accept it in JSON Toolbox

  1. Paste ok_shape and the three failing objects into JSON format. Confirm they parse.
  2. Use JSON → Schema to generate the tool-args contract, then add enum, pattern, and additionalProperties.
  3. Run the same fields through Function Calling; send the final reply through Structured Output separately. If the tool also has to serve other Hosts, wrap MCP Tool.
  4. Drop "八十", extra_cmd, and a return packet missing call_id into Schema validator and confirm they fail.

Everything stays in the browser. Nothing is uploaded. First have an object that refuses bad arguments and a messy function_result, then turn thinking_level to high.

FAQ

Are 3.8 Flash and 3.8 Live the same pipe?

No. Flash does not support Live API. Flash has Structured Output. Live does not.

Is name alone enough on function_result?

No. Every Gemini 3 call carries an id. The return packet must line up call_id and name.

Can you fake Structured Output with a dummy tool?

Do not. Tools own the loop, response_format owns the close. When the model does not call a tool, dummy arguments will not appear.

Can thinking_level be written as minimal?

No. Only low / medium / high. minimal errors.

Is this the same article as “why models love JSON”?

No. That piece writes cross-vendor. This piece only writes 3.8 Flash’s loop and the two-layer contract.

Summary

Gemini 3.8 Flash made the agent loop a default capability: call a tool first, look at the result, call again, then hand the final object through Schema. It has no Live. It requires every function_result to bring back call_id and name. minimal and the old sampling knobs do nothing on this card. What developers still hold is two JSON objects: arguments and the close. The next step is not turning thinking_level to the top. It is refusing "八十" and a return packet with no call_id.