SPB Git

Repository Public

thought-signatures.md 28.3 KB 849 lines markdown

1> [!IMPORTANT]2> **Important:** **Thought signatures are handled automatically** when you use the official [Google Gen AI SDKs](https://ai.google.dev/gemini-api/docs/libraries) and append the full model response object directly to history. **You3> only need to work with thought signatures directly when using the REST API** , or if you are *manually extracting and returning parts history in multi-turn4> conversations*.56Thought signatures are encrypted representations of the model's internal thought7process and are used to preserve reasoning context across multi-step8interactions.9When using thinking models (such as the Gemini 3 and 2.5 series), the API may10return a `thoughtSignature` field within the [content parts](https://ai.google.dev/api/caching#Part)11of the response (e.g., `text` or `functionCall` parts).1213As a general rule, if you receive a thought signature in a model response,14you should pass it back exactly as received when sending the conversation15history in the next turn.16**When using Gemini 3 models, you must pass back thought signatures during17function calling, otherwise you will get a validation error** (4xx status code).18This includes when using the `minimal`19[thinking level](https://ai.google.dev/gemini-api/docs/thinking#thinking-levels) setting for Gemini 320Flash.2122## How it works2324The graphic below visualizes the meaning of "turn" and "step" as they pertain to25[function calling](https://ai.google.dev/gemini-api/docs/function-calling) in the Gemini API. A "turn"26is a single, complete exchange in a conversation between a user and a model. A27"step" is a finer-grained action or operation performed by the model, often as28part of a larger process to complete a turn.2930![Function calling turns and steps diagram](https://ai.google.dev/static/gemini-api/docs/images/fc-turns.png)3132*This document focuses on handling function calling for Gemini 3 models. Refer33to the [model behavior](https://ai.google.dev/gemini-api/docs/generate-content/thought-signatures#model-behavior) section for discrepancies with 2.5.*3435Gemini 3 returns thought signatures for all model responses (responses from36the API) with a function call. Thought signatures show up in the following37cases:3839- When there are [parallel function](https://ai.google.dev/gemini-api/docs/function-calling#parallel_function_calling) calls, the first function call part returned by the model response will have a thought signature.40- When there are sequential function calls (multi-step), each function call will have a signature and you must pass all signatures back.41- Model responses without a function call will return a thought signature inside the last part returned by the model.4243The following table provides a visualization for multi-step function calls,44combining the definitions of turns and steps with the concept of signatures45introduced above:4647|---|---|---|---|---|48| **Turn** | **Step** | **User Request** | **Model Response** | **FunctionResponse** |49| 1 | 1 | `request1 = user_prompt` | `FC1 + signature` | `FR1` |50| 1 | 2 | `request2 = request1 + (FC1 + signature) + FR1` | `FC2 + signature` | `FR2` |51| 1 | 3 | `request3 = request2 + (FC2 + signature) + FR2` | `text_output <br /> ` `(no FCs)` | None |5253## Signatures in function calling parts5455When Gemini generates a `functionCall`, it relies on the `thought_signature`56to process the tool's output correctly in the next turn.5758- **Behavior** :59  - **Single Function Call** : The `functionCall` part will contain a `thought_signature`.60  - **Parallel Function Calls** : If the model generates parallel function calls in a response, the `thought_signature` is attached **only to the first** `functionCall` part. Subsequent `functionCall` parts in the same response will **not** contain a signature.61- **Requirement** : You **must** return this signature in the exact part where it was received when sending the conversation history back.62- **Validation** : Strict validation is enforced for all function calls within the current turn . (Only current turn is required; we don't validate on previous turns)63  - The API goes back in the history (newest to oldest) to find the most recent **User** message that contains standard content (e.g., `text`) ( which would be the start of the current turn). This will not **be** a `functionResponse`.64  - **All** model `functionCall` turns occurring after that specific use message are considered part of the turn.65  - The **first** `functionCall` part in **each step** of the current turn **must** include its `thought_signature`.66  - If you omit a `thought_signature` for the first `functionCall` part in any step of the current turn, the request will fail with a 400 error.67- **If proper signatures are not returned, here is how you will error out**68  - Gemini 3 models: Failure to include signatures will result in a 400 error. The verbiage will be of the form:69    - Function call `<Function Call>` in the `<index of contents array>` content block is missing a `thought_signature`. For example, *Function70      call `FC1` in the `1.` content block is missing a `thought_signature`.*7172### Sequential function calling example7374This section shows an example of multiple function calls where the user asks a75complex question requiring multiple tasks.7677Let's walk through a multiple-turn function calling example where the user asks78a complex question requiring multiple tasks: `"Check flight status for AA100 and79book a taxi if delayed"`.8081|---|---|---|---|---|82| **Turn** | **Step** | **User Request** | **Model Response** | **FunctionResponse** |83| 1 | 1 | `request1="Check flight status for AA100 and book a taxi 2 hours before if delayed."` | `FC1 ("check_flight") + signature` | `FR1` |84| 1 | 2 | `request2 = request1 + FC1 ("check_flight") + signature + FR1` | `FC2("book_taxi") + signature` | `FR2` |85| 1 | 3 | `request3 = request2 + FC2 ("book_taxi") + signature + FR2` | `text_output <br /> ` `(no FCs)` | `None` |8687The following code illustrates the sequence in the above table.8889**Turn 1, Step 1 (User request)**9091    {92      "contents": [93        {94          "role": "user",95          "parts": [96            {97              "text": "Check flight status for AA100 and book a taxi 2 hours before if delayed."98            }99          ]100        }101      ],102      "tools": [103        {104          "functionDeclarations": [105            {106              "name": "check_flight",107              "description": "Gets the current status of a flight",108              "parameters": {109                "type": "object",110                "properties": {111                  "flight": {112                    "type": "string",113                    "description": "The flight number to check"114                  }115                },116                "required": [117                  "flight"118                ]119              }120            },121            {122              "name": "book_taxi",123              "description": "Book a taxi",124              "parameters": {125                "type": "object",126                "properties": {127                  "time": {128                    "type": "string",129                    "description": "time to book the taxi"130                  }131                },132                "required": [133                  "time"134                ]135              }136            }137          ]138        }139      ]140    }141142**Turn 1, Step 1 (Model response)**143144    {145    "content": {146            "role": "model",147            "parts": [148              {149                "functionCall": {150                  "name": "check_flight",151                  "args": {152                    "flight": "AA100"153                  }154                },155                "thoughtSignature": "<Signature A>"156              }157            ]158      }159    }160161**Turn 1, Step 2 (User response - Sending tool outputs)** Since this user turn162only contains a `functionResponse` (no fresh text), we are still in Turn 1. We163must preserve `<Signature_A>`.164165    {166          "role": "user",167          "parts": [168            {169              "text": "Check flight status for AA100 and book a taxi 2 hours before if delayed."170            }171          ]172        },173        {174            "role": "model",175            "parts": [176              {177                "functionCall": {178                  "name": "check_flight",179                  "args": {180                    "flight": "AA100"181                  }182                },183                "thoughtSignature": "<Signature A>" //Required and Validated184              }185            ]186          },187          {188            "role": "user",189            "parts": [190              {191                "functionResponse": {192                  "name": "check_flight",193                  "response": {194                    "status": "delayed",195                    "departure_time": "12 PM"196                    }197                  }198                }199            ]200    }201202**Turn 1, Step 2 (Model)** The model now decides to book a taxi based on the203previous tool output.204205    {206          "content": {207            "role": "model",208            "parts": [209              {210                "functionCall": {211                  "name": "book_taxi",212                  "args": {213                    "time": "10 AM"214                  }215                },216                "thoughtSignature": "<Signature B>"217              }218            ]219          }220    }221222**Turn 1, Step 3 (User - Sending tool output)** To send the taxi booking223confirmation, we must include signatures for **ALL** function calls in this loop224(`<Signature A>` + `<Signature B>`).225226    {227          "role": "user",228          "parts": [229            {230              "text": "Check flight status for AA100 and book a taxi 2 hours before if delayed."231            }232          ]233        },234        {235            "role": "model",236            "parts": [237              {238                "functionCall": {239                  "name": "check_flight",240                  "args": {241                    "flight": "AA100"242                  }243                },244                "thoughtSignature": "<Signature A>" //Required and Validated245              }246            ]247          },248          {249            "role": "user",250            "parts": [251              {252                "functionResponse": {253                  "name": "check_flight",254                  "response": {255                    "status": "delayed",256                    "departure_time": "12 PM"257                  }258                  }259                }260            ]261          },262          {263            "role": "model",264            "parts": [265              {266                "functionCall": {267                  "name": "book_taxi",268                  "args": {269                    "time": "10 AM"270                  }271                },272                "thoughtSignature": "<Signature B>" //Required and Validated273              }274            ]275          },276          {277            "role": "user",278            "parts": [279              {280                "functionResponse": {281                  "name": "book_taxi",282                  "response": {283                    "booking_status": "success"284                  }285                  }286                }287            ]288        }289    }290291### Parallel function calling example292293Let's walk through a parallel function calling example where the users asks294`"Check weather in Paris and London"` to see where the model does validation.295296| **Turn** | **Step** | **User Request** | **Model Response** | **FunctionResponse** |297|---|---|---|---|---|298| 1 | 1 | `request1="Check the weather in Paris and London"` | FC1 ("Paris") + signature FC2 ("London") | FR1 |299| 1 | 2 | `request 2 = request1 + FC1 ("Paris") + signature + FC2 ("London")` | text_output (no FCs) | None |300301The following code illustrates the sequence in the above table.302303**Turn 1, Step 1 (User request)**304305    {306      "contents": [307        {308          "role": "user",309          "parts": [310            {311              "text": "Check the weather in Paris and London."312            }313          ]314        }315      ],316      "tools": [317        {318          "functionDeclarations": [319            {320              "name": "get_current_temperature",321              "description": "Gets the current temperature for a given location.",322              "parameters": {323                "type": "object",324                "properties": {325                  "location": {326                    "type": "string",327                    "description": "The city name, e.g. San Francisco"328                  }329                },330                "required": [331                  "location"332                ]333              }334            }335          ]336        }337      ]338    }339340**Turn 1, Step 1 (Model response)**341342    {343      "content": {344        "parts": [345          {346            "functionCall": {347              "name": "get_current_temperature",348              "args": {349                "location": "Paris"350              }351            },352            "thoughtSignature": "<Signature_A>"// INCLUDED on First FC353          },354          {355            "functionCall": {356              "name": "get_current_temperature",357              "args": {358                "location": "London"359              }// NO signature on subsequent parallel FCs360            }361          }362        ]363      }364    }365366**Turn 1, Step 2 (User response - Sending tool outputs)** We must preserve367`<Signature_A>` on the first part exactly as received.368369    [370      {371        "role": "user",372        "parts": [373          {374            "text": "Check the weather in Paris and London."375          }376        ]377      },378      {379        "role": "model",380        "parts": [381          {382            "functionCall": {383              "name": "get_current_temperature",384              "args": {385                "city": "Paris"386              }387            },388            "thought_signature": "<Signature_A>" // MUST BE INCLUDED389          },390          {391            "functionCall": {392              "name": "get_current_temperature",393              "args": {394                "city": "London"395              }396            }397          } // NO SIGNATURE FIELD398        ]399      },400      {401        "role": "user",402        "parts": [403          {404            "functionResponse": {405              "name": "get_current_temperature",406              "response": {407                "temp": "15C"408              }409            }410          },411          {412            "functionResponse": {413              "name": "get_current_temperature",414              "response": {415                "temp": "12C"416              }417            }418          }419        ]420      }421    ]422423## Signatures in non `functionCall` parts424425Gemini may also return `thought_signatures` in the final part of the response426in non-function-call parts.427428- **Behavior** : The final content part (`text, inlineData...`) returned by the model may contain a `thought_signature`.429- **Recommendation** : Returning these signatures is **recommended** to ensure the model maintains high-quality reasoning, especially for complex instruction following or simulated agentic workflows.430- **Validation** : The API does **not** strictly enforce validation. You won't receive a blocking error if you omit them, though performance may degrade.431432### Text/In-context reasoning (No validation)433434**Turn 1, Step 1 (Model response)**435436    {437      "role": "model",438      "parts": [439        {440          "text": "I need to calculate the risk. Let me think step-by-step...",441          "thought_signature": "<Signature_C>" // OPTIONAL (Recommended)442        }443      ]444    }445446**Turn 2, Step 1 (User)**447448    [449      { "role": "user", "parts": [{ "text": "What is the risk?" }] },450      {451        "role": "model", 452        "parts": [453          {454            "text": "I need to calculate the risk. Let me think step-by-step...",455            // If you omit <Signature_C> here, no error will occur.456          }457        ]458      },459      { "role": "user", "parts": [{ "text": "Summarize it." }] }460    ]461462## Signatures for OpenAI compatibility463464The following examples shows how to handle thought signatures for a chat465completion API using [OpenAI compatibility](https://ai.google.dev/gemini-api/docs/openai).466467### Sequential function calling example468469This is an example of multiple function calling where the user asks a complex470question requiring multiple tasks.471472Let's walk through a multiple-turn function calling example where the user asks473`Check flight status for AA100 and book a taxi if delayed` and you can see what474happens when the user asks a complex question requiring multiple tasks.475476|---|---|---|---|---|477| **Turn** | **Step** | **User Request** | **Model Response** | **FunctionResponse** |478| 1 | 1 | `request1 = "Check flight status for AA100 and book a taxi 2 hours before if delayed."` | `FC1 ("check_flight") + signature` | `FR1` |479| 1 | 2 | `request2 = request1 + FC1 ("check_flight") + signature + FR1` | `FC2("book_taxi") + signature` | `FR2` |480| 1 | 3 | `request3 = request2 + FC2 ("book_taxi") + signature + FR2` | `text_output <br /> ` `(no FCs)` | `None` |481482The following code walks through the given sequence.483484**Turn 1, Step 1 (User Request)**485486    {487      "model": "google/gemini-3.1-pro-preview",488      "messages": [489        {490          "role": "user",491          "content": "Check flight status for AA100 and book a taxi 2 hours before if delayed."492        }493      ],494      "tools": [495        {496          "type": "function",497          "function": {498            "name": "check_flight",499            "description": "Gets the current status of a flight",500            "parameters": {501              "type": "object",502              "properties": {503                "flight": {504                  "type": "string",505                  "description": "The flight number to check."506                }507              },508              "required": [509                "flight"510              ]511            }512          }513        },514        {515          "type": "function",516          "function": {517            "name": "book_taxi",518            "description": "Book a taxi",519            "parameters": {520              "type": "object",521              "properties": {522                "time": {523                  "type": "string",524                  "description": "time to book the taxi"525                }526              },527              "required": [528                "time"529              ]530            }531          }532        }533      ]534    }535536**Turn 1, Step 1 (Model Response)**537538    {539          "role": "model",540            "tool_calls": [541              {542                "extra_content": {543                  "google": {544                    "thought_signature": "<Signature A>"545                  }546                },547                "function": {548                  "arguments": "{\"flight\":\"AA100\"}",549                  "name": "check_flight"550                },551                "id": "function-call-1",552                "type": "function"553              }554            ]555        }556557**Turn 1, Step 2 (User Response - Sending Tool Outputs)**558559Since this user turn only contains a `functionResponse` (no fresh text), we are560still in Turn 1 and must preserve `<Signature_A>`.561562    "messages": [563        {564          "role": "user",565          "content": "Check flight status for AA100 and book a taxi 2 hours before if delayed."566        },567        {568          "role": "model",569            "tool_calls": [570              {571                "extra_content": {572                  "google": {573                    "thought_signature": "<Signature A>" //Required and Validated574                  }575                },576                "function": {577                  "arguments": "{\"flight\":\"AA100\"}",578                  "name": "check_flight"579                },580                "id": "function-call-1",581                "type": "function"582              }583            ]584        },585        {586          "role": "tool",587          "name": "check_flight",588          "tool_call_id": "function-call-1",589          "content": "{\"status\":\"delayed\",\"departure_time\":\"12 PM\"}"                 590        }591      ]592593**Turn 1, Step 2 (Model)**594595The model now decides to book a taxi based on the previous tool output.596597    {598    "role": "model",599    "tool_calls": [600    {601    "extra_content": {602    "google": {603    "thought_signature": "<Signature B>"604    }605                },606                "function": {607                  "arguments": "{\"time\":\"10 AM\"}",608                  "name": "book_taxi"609                },610                "id": "function-call-2",611                "type": "function"612              }613           ]614    }615616**Turn 1, Step 3 (User - Sending Tool Output)**617618To send the taxi booking confirmation, we must include signatures for ALL619function calls in this loop (`<Signature A>` + `<Signature B>`).620621    "messages": [622        {623          "role": "user",624          "content": "Check flight status for AA100 and book a taxi 2 hours before if delayed."625        },626        {627          "role": "model",628            "tool_calls": [629              {630                "extra_content": {631                  "google": {632                    "thought_signature": "<Signature A>" //Required and Validated633                  }634                },635                "function": {636                  "arguments": "{\"flight\":\"AA100\"}",637                  "name": "check_flight"638                },639                "id": "function-call-1d6a1a61-6f4f-4029-80ce-61586bd86da5",640                "type": "function"641              }642            ]643        },644        {645          "role": "tool",646          "name": "check_flight",647          "tool_call_id": "function-call-1d6a1a61-6f4f-4029-80ce-61586bd86da5",648          "content": "{\"status\":\"delayed\",\"departure_time\":\"12 PM\"}"                 649        },650        {651          "role": "model",652            "tool_calls": [653              {654                "extra_content": {655                  "google": {656                    "thought_signature": "<Signature B>" //Required and Validated657                  }658                },659                "function": {660                  "arguments": "{\"time\":\"10 AM\"}",661                  "name": "book_taxi"662                },663                "id": "function-call-65b325ba-9b40-4003-9535-8c7137b35634",664                "type": "function"665              }666            ]667        },668        {669          "role": "tool",670          "name": "book_taxi",671          "tool_call_id": "function-call-65b325ba-9b40-4003-9535-8c7137b35634",672          "content": "{\"booking_status\":\"success\"}"673        }674      ]675676### Parallel function calling example677678Let's walk through a parallel function calling example where the users asks679`"Check weather in Paris and London"` and you can see where the model does680validation.681682|---|---|---|---|---|683| **Turn** | **Step** | **User Request** | **Model Response** | **FunctionResponse** |684| 1 | 1 | `request1="Check the weather in Paris and London"` | `FC1 ("Paris") + signature <br /> ` `FC2 ("London")` | `FR1` |685| 1 | 2 | `request 2 = request1 + FC1 ("Paris") + signature + FC2 ("London")` | `text_output <br /> ` `(no FCs)` | `None` |686687Here's the code to walk through the given sequence.688689**Turn 1, Step 1 (User Request)**690691    {692      "contents": [693        {694          "role": "user",695          "parts": [696            {697              "text": "Check the weather in Paris and London."698            }699          ]700        }701      ],702      "tools": [703        {704          "functionDeclarations": [705            {706              "name": "get_current_temperature",707              "description": "Gets the current temperature for a given location.",708              "parameters": {709                "type": "object",710                "properties": {711                  "location": {712                    "type": "string",713                    "description": "The city name, e.g. San Francisco"714                  }715                },716                "required": [717                  "location"718                ]719              }720            }721          ]722        }723      ]724    }725726**Turn 1, Step 1 (Model Response)**727728    {729    "role": "assistant",730            "tool_calls": [731              {732                "extra_content": {733                  "google": {734                    "thought_signature": "<Signature A>" //Signature returned735                  }736                },737                "function": {738                  "arguments": "{\"location\":\"Paris\"}",739                  "name": "get_current_temperature"740                },741                "id": "function-call-f3b9ecb3-d55f-4076-98c8-b13e9d1c0e01",742                "type": "function"743              },744              {745                "function": {746                  "arguments": "{\"location\":\"London\"}",747                  "name": "get_current_temperature"748                },749                "id": "function-call-335673ad-913e-42d1-bbf5-387c8ab80f44",750                "type": "function" // No signature on Parallel FC751              }752            ]753    }754755**Turn 1, Step 2 (User Response - Sending Tool Outputs)**756757You must preserve `<Signature_A>` on the first part exactly as received.758759    "messages": [760        {761          "role": "user",762          "content": "Check the weather in Paris and London."763        },764        {765          "role": "assistant",766            "tool_calls": [767              {768                "extra_content": {769                  "google": {770                    "thought_signature": "<Signature A>" //Required771                  }772                },773                "function": {774                  "arguments": "{\"location\":\"Paris\"}",775                  "name": "get_current_temperature"776                },777                "id": "function-call-f3b9ecb3-d55f-4076-98c8-b13e9d1c0e01",778                "type": "function"779              },780              {781                "function": { //No Signature782                  "arguments": "{\"location\":\"London\"}",783                  "name": "get_current_temperature"784                },785                "id": "function-call-335673ad-913e-42d1-bbf5-387c8ab80f44",786                "type": "function"787              }788            ]789        },790        {791          "role":"tool",792          "name": "get_current_temperature",793          "tool_call_id": "function-call-f3b9ecb3-d55f-4076-98c8-b13e9d1c0e01",794          "content": "{\"temp\":\"15C\"}"795        },    796        {797          "role":"tool",798          "name": "get_current_temperature",799          "tool_call_id": "function-call-335673ad-913e-42d1-bbf5-387c8ab80f44",800          "content": "{\"temp\":\"12C\"}"801        }802      ]803804## FAQs8058061. **How do I transfer history from a different model to Gemini 3 with a807   function call part in the current turn and step? I need to provide function call808   parts that were not generated by the API and therefore don't have an associated809   thought signature?**810811   While injecting custom function call blocks into the request is strongly812   discouraged, in cases where it can't be avoided, e.g. providing information813   to the model on function calls and responses that were executed814   deterministically by the client, or transferring a trace from a different815   model that does not include thought signatures, you can set the following816   dummy signatures of either `"context_engineering_is_the_way_to_go"` or817   `"skip_thought_signature_validator"` in the thought signature field to skip818   validation.8192. **I am sending back interleaved parallel function calls and responses and the820   API is returning a 400. Why?**821822   When the API returns parallel function calls "FC1 + signature, FC2", the823   user response expected is "FC1+ signature, FC2, FR1, FR2". If you have them824   interleaved as "FC1 + signature, FR1, FC2, FR2" the API will return a 400825   error.8263. **When streaming and the model is not returning a function call I can't find827   the thought signature**828829   During a model response not containing a FC with a streaming request, the830   model may return the thought signature in a part with an empty text content831   part. It is advisable to parse the entire request until the `finish_reason`832   is returned by the model.833834## Thought signatures for different models835836[Gemini 3 models](https://ai.google.dev/gemini-api/docs/models#gemini-3) and Gemini 2.5 models837behave differently with thought signatures in function calls:838839- If there are function calls in a response,840  - Gemini 3 will always have the signature on the first function call part. It is **mandatory** to return that part.841  - Gemini 2.5 will have the signature in the first part (regardless of type). It is **optional** to return that part.842- If there are no function calls in a response,843  - Gemini 3 will have the signature on the last part if the model generates a thought.844  - Gemini 2.5 won't have a signature in any part.845846Refer to the [Thinking](https://ai.google.dev/gemini-api/docs/thinking#signatures) page for more847comparison details.848For Gemini 3 Image models see the thinking process section of the849[Image generation](https://ai.google.dev/gemini-api/docs/image-generation#thinking-process) guide.
Files main
Menu

Theme