Files
2026-07-13 13:30:30 +08:00

604 lines
23 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "sqi5B7V_Rjim"
},
"outputs": [],
"source": [
"# Copyright 2025 Google LLC\n",
"#\n",
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
"# you may not use this file except in compliance with the License.\n",
"# You may obtain a copy of the License at\n",
"#\n",
"# https://www.apache.org/licenses/LICENSE-2.0\n",
"#\n",
"# Unless required by applicable law or agreed to in writing, software\n",
"# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
"# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
"# See the License for the specific language governing permissions and\n",
"# limitations under the License."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "VyPmicX9RlZX"
},
"source": [
"# Intro to thought signatures\n",
"\n",
"<table align=\"left\">\n",
" <td style=\"text-align: center\">\n",
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/generative-ai/blob/main/gemini/thinking/intro_thought_signatures.ipynb\">\n",
" <img width=\"32px\" src=\"https://www.gstatic.com/pantheon/images/bigquery/welcome_page/colab-logo.svg\" alt=\"Google Colaboratory logo\"><br> Open in Colab\n",
" </a>\n",
" </td>\n",
" <td style=\"text-align: center\">\n",
" <a href=\"https://console.cloud.google.com/vertex-ai/colab/import/https:%2F%2Fraw.githubusercontent.com%2FGoogleCloudPlatform%2Fgenerative-ai%2Fmain%2Fgemini%2Fthinking%2Fintro_thought_signatures.ipynb\">\n",
" <img width=\"32px\" src=\"https://lh3.googleusercontent.com/JmcxdQi-qOpctIvWKgPtrzZdJJK-J3sWE1RsfjZNwshCFgE_9fULcNpuXYTilIR2hjwN\" alt=\"Google Cloud Colab Enterprise logo\"><br> Open in Colab Enterprise\n",
" </a>\n",
" </td>\n",
" <td style=\"text-align: center\">\n",
" <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/generative-ai/main/gemini/thinking/intro_thought_signatures.ipynb\">\n",
" <img src=\"https://www.gstatic.com/images/branding/gcpiconscolors/vertexai/v1/32px.svg\" alt=\"Vertex AI logo\"><br> Open in Vertex AI Workbench\n",
" </a>\n",
" </td>\n",
" <td style=\"text-align: center\">\n",
" <a href=\"https://github.com/GoogleCloudPlatform/generative-ai/blob/main/gemini/thinking/intro_thought_signatures.ipynb\">\n",
" <img width=\"32px\" src=\"https://raw.githubusercontent.com/primer/octicons/refs/heads/main/icons/mark-github-24.svg\" alt=\"GitHub logo\"><br> View on GitHub\n",
" </a>\n",
" </td>\n",
"</table>\n",
"\n",
"<div style=\"clear: both;\"></div>\n",
"\n",
"<b>Share to:</b>\n",
"\n",
"<a href=\"https://www.linkedin.com/sharing/share-offsite/?url=https%3A//github.com/GoogleCloudPlatform/generative-ai/blob/main/gemini/thinking/intro_thought_signatures.ipynb\" target=\"_blank\">\n",
" <img width=\"20px\" src=\"https://upload.wikimedia.org/wikipedia/commons/8/81/LinkedIn_icon.svg\" alt=\"LinkedIn logo\">\n",
"</a>\n",
"\n",
"<a href=\"https://bsky.app/intent/compose?text=https%3A//github.com/GoogleCloudPlatform/generative-ai/blob/main/gemini/thinking/intro_thought_signatures.ipynb\" target=\"_blank\">\n",
" <img width=\"20px\" src=\"https://upload.wikimedia.org/wikipedia/commons/7/7a/Bluesky_Logo.svg\" alt=\"Bluesky logo\">\n",
"</a>\n",
"\n",
"<a href=\"https://twitter.com/intent/tweet?url=https%3A//github.com/GoogleCloudPlatform/generative-ai/blob/main/gemini/thinking/intro_thought_signatures.ipynb\" target=\"_blank\">\n",
" <img width=\"20px\" src=\"https://upload.wikimedia.org/wikipedia/commons/5/5a/X_icon_2.svg\" alt=\"X logo\">\n",
"</a>\n",
"\n",
"<a href=\"https://reddit.com/submit?url=https%3A//github.com/GoogleCloudPlatform/generative-ai/blob/main/gemini/thinking/intro_thought_signatures.ipynb\" target=\"_blank\">\n",
" <img width=\"20px\" src=\"https://redditinc.com/hubfs/Reddit%20Inc/Brand/Reddit_Logo.png\" alt=\"Reddit logo\">\n",
"</a>\n",
"\n",
"<a href=\"https://www.facebook.com/sharer/sharer.php?u=https%3A//github.com/GoogleCloudPlatform/generative-ai/blob/main/gemini/thinking/intro_thought_signatures.ipynb\" target=\"_blank\">\n",
" <img width=\"20px\" src=\"https://upload.wikimedia.org/wikipedia/commons/5/51/Facebook_f_logo_%282019%29.svg\" alt=\"Facebook logo\">\n",
"</a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "8MqT58L6Rm_q"
},
"source": [
"| Authors |\n",
"| --- |\n",
"| [Eric Dong](https://github.com/gericdong) |"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "nVxnv1D5RoZw"
},
"source": [
"## Overview\n",
"\n",
"A thought signature is an encrypted representation of the model's internal reasoning process for a given turn in a conversation. By passing this signature back to the model in subsequent requests, you provide it with the context of its previous thoughts, allowing it to build upon its reasoning and maintain a coherent line of inquiry.\n",
"\n",
"Thought signatures provide a powerful mechanism to maintain context in multi-turn conversations, enabling the model to tackle more complex, multi-step tasks that require reasoning and the use of external tools through function calling.\n",
"\n",
"\n",
"### Objectives\n",
"\n",
"This tutorial explores the thought signatures feature of the Gemini API.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "gPiTOAHURvTM"
},
"source": [
"## Getting Started"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "CHRZUpfWSEpp"
},
"source": [
"### Install the Google Gen AI SDK for Python\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "sG3_LKsWSD3A"
},
"outputs": [],
"source": [
"%pip install --upgrade --quiet google-genai"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "HlMVjiAWSMNX"
},
"source": [
"### Authenticate your notebook environment\n",
"\n",
"If you are running this notebook on Google Colab, run the cell below to authenticate your environment."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "12fnq4V0SNV3"
},
"outputs": [],
"source": [
"import sys\n",
"\n",
"if \"google.colab\" in sys.modules:\n",
" from google.colab import auth\n",
"\n",
" auth.authenticate_user()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "be18ac9c5ec8"
},
"source": [
"### Authenticate to Vertex AI on Google Cloud\n",
"\n",
"You'll need to set up authentication by choosing **one** of the following methods:\n",
"\n",
"1. **Use a Google Cloud Project:** (Recommended for most users)\n",
" - See instructions [Set up a project and development environment](https://cloud.google.com/vertex-ai/docs/start/cloud-environment)\n",
"2. **Use a Vertex AI API Key (Express Mode):** For quick experimentation.\n",
" - [Get an API Key](https://cloud.google.com/vertex-ai/generative-ai/docs/start/express-mode/overview)\n",
" - See tutorial [Getting started with Gemini using Vertex AI in Express Mode](https://github.com/GoogleCloudPlatform/generative-ai/blob/main/gemini/getting-started/intro_gemini_express.ipynb).\n",
"\n",
"This tutorial uses a Google Cloud Project for authentication."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "6wXh1aH7BlPl"
},
"outputs": [],
"source": [
"import os\n",
"\n",
"# fmt: off\n",
"PROJECT_ID = \"[your-project-id]\" # @param {type: \"string\", placeholder: \"[your-project-id]\", isTemplate: true}\n",
"# fmt: on\n",
"if not PROJECT_ID or PROJECT_ID == \"[your-project-id]\":\n",
" PROJECT_ID = str(os.environ.get(\"GOOGLE_CLOUD_PROJECT\"))\n",
"\n",
"LOCATION = \"global\""
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "EdvJRUWRNGHE"
},
"source": [
"### Import libraries\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "qgdSpVmDbdQ9"
},
"outputs": [],
"source": [
"from IPython.display import Markdown, display\n",
"from google import genai\n",
"from google.genai.types import (\n",
" Content,\n",
" GenerateContentConfig,\n",
" Part,\n",
" ThinkingConfig,\n",
" Tool,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "uhAZMeVrCcKS"
},
"source": [
"### Connect to the generative AI service on Vertex AI"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "pv_22mKMCjQY"
},
"outputs": [],
"source": [
"client = genai.Client(vertexai=True, project=PROJECT_ID, location=LOCATION)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "n4yRkFg6BBu4"
},
"source": [
"### Supported Models\n",
"\n",
"Learn more about [supported Gemini models](https://cloud.google.com/vertex-ai/generative-ai/docs/models). This tutorial uses the Gemini 2.5 Flash model."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "-coEslfWPrxo"
},
"outputs": [],
"source": [
"MODEL_ID = \"gemini-2.5-flash\" # @param {type: \"string\"}"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "37CH91ddY9kG"
},
"source": [
"## Use Thought Signatures\n",
"\n",
"When thinking is enabled, the API response includes a `thought_signature` field containing an encrypted representation of the model's reasoning. When a function's execution result is sent back to the server, including the `thought_signature` allows the model to restore its previous thinking context, which will likely improve function calling performance.\n",
"\n",
"Example:\n",
"\n",
"```\n",
"thought_signature=b\"\\n\\xa9\\x06\\x01\\xcb...'\n",
"```\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "MMYRh0I5JZu3"
},
"source": [
"### Use Cases\n",
"The primary and most powerful use case for thought signatures is to enable sophisticated, multi-turn interactions with external tools. Imagine a scenario where a user's request requires multiple steps to resolve, such as:\n",
"\n",
"- Finding the current weather.\n",
"- Based on the weather, deciding on an appropriate activity.\n",
"- Booking a reservation for that activity.\n",
"\n",
"Each of these steps might involve calling a different function. Without thought signatures, the model would treat each step as an isolated request, losing the crucial context of the user's initial intent and the results of previous function calls. With thought signatures, the model can chain these calls together, understanding the dependencies and making informed decisions at each step.\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "hlVCCsncIlYJ"
},
"source": [
"## Example: Conditional Thermostat Control\n",
"\n",
"In this scenario, a user wants to set a thermostat based on the current weather. The request is: \"If it's too hot or too cold in London, set the thermostat to a comfortable temperature.\"\n",
"\n",
"This requires the model to:\n",
"\n",
"- Call a tool to get the weather in London.\n",
"- Use the returned weather information to decide if another tool needs to be called.\n",
"-Call the tool to set the thermostat if the condition is met."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "KmEQftHMJY8c"
},
"source": [
"### **Step 1**: Define Functions and Tools\n",
"\n",
"First, we define the two functions the model can use: `get_current_temperature` and `set_thermostat_temperature`."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ifMXpv-rABZk"
},
"outputs": [],
"source": [
"# Mock Function Implementations\n",
"\n",
"\n",
"def get_current_temperature(location: str) -> dict:\n",
" \"\"\"Gets the current weather temperature for a given location.\"\"\"\n",
" print(f\"Tool Call: get_current_temperature(location={location})\")\n",
" # Mocking a real API call.\n",
" return {\"temperature\": 30, \"unit\": \"celsius\"}\n",
"\n",
"\n",
"def set_thermostat_temperature(temperature: int) -> dict:\n",
" \"\"\"Sets the thermostat to a desired temperature.\"\"\"\n",
" print(f\"Tool Call: set_thermostat_temperature(temperature={temperature})\")\n",
" # In a real app, this would interact with a thermostat API.\n",
" return {\"status\": \"success\"}\n",
"\n",
"\n",
"# Function Declarations for the model\n",
"\n",
"\n",
"get_weather_declaration = {\n",
" \"name\": \"get_current_temperature\",\n",
" \"description\": \"Gets the current weather temperature for a given location.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\"location\": {\"type\": \"string\"}},\n",
" \"required\": [\"location\"],\n",
" },\n",
"}\n",
"\n",
"set_thermostat_declaration = {\n",
" \"name\": \"set_thermostat_temperature\",\n",
" \"description\": \"Sets the thermostat to a desired temperature.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\"temperature\": {\"type\": \"integer\"}},\n",
" \"required\": [\"temperature\"],\n",
" },\n",
"}\n",
"\n",
"# Create a single Tool with both functions\n",
"# The model will choose which, if any, to call.\n",
"thermostat_tools = Tool(\n",
" function_declarations=[\n",
" get_weather_declaration,\n",
" set_thermostat_declaration,\n",
" ]\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "vTWbrR1RJP_K"
},
"source": [
"### **Step 2**: First Turn - Get the Weather\n",
"\n",
"We send the initial prompt to the model. We expect it to call the `get_current_temperature` function and return a thought signature to maintain the context of the user's conditional request."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Md-H1xPZAL9m"
},
"outputs": [],
"source": [
"# The user's initial multistep request\n",
"prompt = \"\"\"\n",
"If it's too hot or too cold in London, set the temperature to a comfortable level.\n",
"Make your own reasonable assumption for what 'comfortable' means and do not ask for clarification.\n",
"\"\"\"\n",
"\n",
"contents = [Content(role=\"user\", parts=[Part(text=prompt)])]\n",
"\n",
"# Send the first request\n",
"config = GenerateContentConfig(\n",
" tools=[thermostat_tools],\n",
" thinking_config=ThinkingConfig(\n",
" include_thoughts=True, # Optional, include_thoughts can only be enabled when thinking is enabled\n",
" ),\n",
")\n",
"\n",
"response_turn_1 = client.models.generate_content(\n",
" model=MODEL_ID, config=config, contents=contents\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "fSXaHZLsNSBK"
},
"outputs": [],
"source": [
"def process_tool_call(response):\n",
" \"\"\"Processes a model's response to extract and execute a tool call.\n",
"\n",
" Args:\n",
" response: The response object from the model.\n",
"\n",
" Returns:\n",
" A tuple containing the tool_call object and the result of its execution.\n",
" Returns (None, None) if no function call is found.\n",
" \"\"\"\n",
" tool_call = None\n",
" result = None\n",
"\n",
" # Extract the function call, thought signatures, thought summary\n",
" # from the response parts\n",
" for part in response.candidates[0].content.parts:\n",
" if part.function_call:\n",
" tool_call = part.function_call\n",
" if hasattr(part, \"thought_signature\") and part.thought_signature:\n",
" display(Markdown(f\"**Thought Signature**: {part.thought_signature}\"))\n",
" if part.thought:\n",
" display(Markdown(f\"**Thought Summary**: {part.text}\"))\n",
" else:\n",
" display(Markdown(f\"**Answer**: {part.text}\"))\n",
"\n",
" if tool_call:\n",
" if tool_call.name == \"get_current_temperature\":\n",
" result = get_current_temperature(**tool_call.args)\n",
" display(Markdown(f\"**Tool Response**: {result}\"))\n",
" elif tool_call.name == \"set_thermostat_temperature\":\n",
" result = set_thermostat_temperature(**tool_call.args)\n",
" display(Markdown(f\"**Tool Response**: {result}\"))\n",
" else:\n",
" print(\"No valid tool call found in the response.\")\n",
"\n",
" return tool_call, result"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "uHvIv1LsUZ0R"
},
"outputs": [],
"source": [
"# Process the response\n",
"print(\"--- Turn 1 ---\")\n",
"tool_call_1, result_1 = process_tool_call(response_turn_1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "DOWwBqHuLUQ-"
},
"source": [
"### **Step 3**: Second Turn - Set the Thermostat\n",
"\n",
"Now, we send the model's previous response (containing the thought signature) and the result from our first tool call back to the model. Using this context, the model will decide if it needs to call the `set_thermostat_temperature` function.\n",
"\n",
"There is no additional work for you to do. The `\"thought_signature\"` part will be sent back in the model turn's `Content`, it is also handled transparently by\n",
"automatic function calling."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "w1OIEbyRAQzh"
},
"outputs": [],
"source": [
"# Append the model's function call message from turn 1 (this includes the signature)\n",
"contents.append(response_turn_1.candidates[0].content)\n",
"\n",
"# Append the result of the function execution from turn 1\n",
"contents.append(\n",
" Content(\n",
" role=\"tool\",\n",
" parts=[\n",
" Part.from_function_response(\n",
" name=tool_call_1.name,\n",
" response=result_1,\n",
" )\n",
" ],\n",
" )\n",
")\n",
"\n",
"# Send the second request\n",
"response_turn_2 = client.models.generate_content(\n",
" model=MODEL_ID, config=config, contents=contents\n",
")\n",
"\n",
"# Process the response\n",
"tool_call_2, result_2 = process_tool_call(response_turn_2)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "MH0bJ2QBSCAg"
},
"source": [
"### **Step 4**: Generate a user-friendly response\n",
"\n",
"Generate a user-friendly response, informed by the action it just took."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ktEwDwf5R22J"
},
"outputs": [],
"source": [
"# Append this turn's messages to history for a final response\n",
"contents.append(response_turn_2.candidates[0].content)\n",
"contents.append(\n",
" Content(\n",
" role=\"tool\",\n",
" parts=[\n",
" Part.from_function_response(\n",
" name=tool_call_2.name,\n",
" response=result_2,\n",
" )\n",
" ],\n",
" )\n",
")\n",
"\n",
"# Get the final text response\n",
"final_response = client.models.generate_content(\n",
" model=MODEL_ID, config=config, contents=contents\n",
")\n",
"\n",
"display(Markdown(f\"**Final Response**: {final_response.text}\"))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "wnbAejb8Mtbd"
},
"source": [
"This multi-turn process, held together by passing the model's response history (including the encrypted thought signature) back each time, allows the model to perform complex, sequential tasks."
]
}
],
"metadata": {
"colab": {
"name": "intro_thought_signatures.ipynb",
"toc_visible": true
},
"kernelspec": {
"display_name": "Python 3",
"name": "python3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}