# Nano Banana 2 Thought Signature 错误：上一张图丢了签名怎么修

> Nano Banana 2 第二轮改图报 400，是上一张图被重发时丢了思考签名。改用 previous_interaction_id 续写，或把 content 原样放回历史。

- URL: https://blog.laozhang.ai/zh/posts/nano-banana-2-thought-signature-error
- Published: 2026-03-09
- Updated: 2026-10-05
- Author: LaoZhang AI Team (https://blog.laozhang.ai/zh/about)
- Topic: 故障排查
- Tags: Nano Banana 2, 思考签名, thought_signature, Gemini API, API 错误修复

---
Nano Banana 2（`gemini-3.1-flash-image`）第一轮出图正常，第二轮改图却返回 `400 INVALID_ARGUMENT`，报错里写着 `Image part is missing a thought_signature`：原因几乎都是你把上一轮模型生成的图片重新发回去时，附在那张图片上的思考签名没有一起带上。修法按省事程度排有三种：用 Interactions API 的 `previous_interaction_id` 续写，签名由服务端保存；继续用 generateContent 的话，用 SDK 的 chat 对象，或把模型返回的 `content` 原样追加进历史；问题出在框架或平台时，升级到已修复的版本，或者把上一张图当作新上传的图片重开一轮。

这个错误跟 API 密钥、配额和模型 ID 都无关，换 key 或重试不会好转，同一个请求重发多少次都是 400。

## Image part is missing a thought_signature 指哪一张图

一份公开的开发者报告（[Vercel AI SDK issue #12988](https://github.com/vercel/ai/issues/12988)，2026 年 3 月 2 日，模型为当时的 `gemini-3.1-flash-image-preview`）贴出了完整返回：

```json
{
  "error": {
    "code": 400,
    "message": "Image part is missing a thought_signature in content position 2, part position 1. Please refer to https://ai.google.dev/gemini-api/docs/thought-signatures#model-behavior for more details.",
    "status": "INVALID_ARGUMENT"
  }
}
```

那次请求的历史只有三条：用户的第一句、模型上一轮的回复（一张图片）、用户的第二句。对照下来，`content position 2` 是模型那一轮，`part position 1` 是其中第一个 part，也就是上一张生成的图片。换成你自己的请求，按这两个位置去数，就能找到是哪一轮、哪个 part 少了签名。

报错里给的 `thought-signatures` 链接现在会跳到 Google 的 [Gemini 思考文档](https://ai.google.dev/gemini-api/docs/thinking)，签名的说明都在那一页。如果你用的是 Interactions API，同类问题在[错误码列表](https://ai.google.dev/gemini-api/docs/api-errors)里叫 `missing_thought_signature`，说明是“response is missing a required thought signature”。

## 思考签名是什么：附在 part 上的加密推理状态

Google 的思考文档把思考签名定义为模型内部推理的加密表示，多轮交互要靠它保持推理连贯。Nano Banana 2 和 Nano Banana Pro 都属于 Gemini 3 图片模型，生成前会先推理，过程中还会产生不计费的中间“思考图片”，签名记下的就是这一轮推理的状态。

签名放在哪里，取决于你调用的是哪个接口：

| 接口 | 签名的位置 | 你要做的事 |
| --- | --- | --- |
| Interactions API（有状态，默认） | 服务端保存的思考步骤里 | 下一轮传 `previous_interaction_id`，不用碰签名 |
| Interactions API（无状态） | `thought` 步骤和内置工具的调用、结果块里 | 把收到的思考块原样全部发回，不删不改 |
| generateContent | 作为元数据挂在某个 part 上：`functionCall` part，或回复的最后一个 part（文本或图片） | 把模型那一轮的 part 原样放回 `contents` |

第三行是多轮改图出错的根源。Google Cloud 的[思考签名文档](https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/thinking/thought-signatures)写明，回复的最后一个 part 可以是文本，也可以是 `inlineData`，签名可能就挂在图片 part 上。你回放历史时如果只放回图片字节，生成的是一个“内容相同、但没有签名”的新 part，模型就无法确认这是它自己生成过的内容。

## 为什么第一轮正常、第二轮改图才报 400

第一轮请求里没有模型生成的内容，用不到签名。到了第二轮，你的历史里第一次出现模型生成的 part，签名缺失才会被检查出来。

签名最常丢在“通用消息格式”里。很多框架和聊天平台把对话存成“角色 + 文本 / 文件”的结构，图片存成文件或 base64，下一轮再拼成新的 part；签名不属于这种结构，保存时就已经丢了。前面那份 Vercel 报告的复现代码就是这样：助手消息里只有 `type`、`data`、`filename`、`mediaType` 四个字段，报告者还提到同样的流程在 `gemini-2.5-flash-image` 上一直正常。Dify 的 Gemini 插件也有同样的报告（[dify-official-plugins #2262](https://github.com/langgenius/dify-official-plugins/issues/2262)，2025 年 12 月），报错文本与上面一字不差。

`gemini-2.5-flash-image` 已于 2026 年 10 月 2 日停用，`gemini-3.1-flash-image-preview` 和 `gemini-3-pro-image-preview` 也已于 2026 年 6 月 25 日停用（[Google 停用时间表](https://ai.google.dev/gemini-api/docs/deprecations)）。现在能用的 Nano Banana 2 是 `gemini-3.1-flash-image`，Nano Banana Pro 是 `gemini-3-pro-image`，旧代码迁过来时，多轮历史的处理方式要按下面的修法一起改。

## Nano Banana Pro 不报错、Nano Banana 2 报错：两种模型的校验差异

Google Cloud 的思考签名文档写：Gemini 3 模型对签名的校验比以前的版本严格，缺了必需的签名会返回 400；但 Gemini 3 Pro Image 不执行这项校验，缺签名不会返回 400，只是仍然应该回传，模型才能保持多轮上下文。同一页还写，不在 `functionCall` 上的签名属于“建议回传、省略不会报错”。

Nano Banana 2 的行为，这页文档没有单独写。开发者报告显示，它对图片 part 的签名会直接返回 400。所以在 Nano Banana Pro 上跑得通的多轮改图代码，换成 Nano Banana 2 就可能在第二轮失败，代码本身没变。

反方向的证据也有：上面那份 Dify 报告用的是 2025 年 12 月的 Nano Banana Pro 预览版，同样在第二张图上收到了这条 400。文档和报告并不完全一致，稳妥的做法是不按模型区分：凡是模型生成的 part，都带着签名原样放回去。

![思考签名校验证据差异与按接口保留完整历史的稳妥做法](https://blog.laozhang.ai/posts/zh/nano-banana-2-thought-signature-error/img/content-1.webp)

## 修法一：Interactions API 传 previous_interaction_id

Google 图片生成指南（2026 年 9 月 23 日更新）的多轮改图示例走的就是这条路：第一轮正常创建 interaction，第二轮用 `previous_interaction_id` 指向它。思考文档对这种有状态模式的说明是，服务端自动管理对话状态，包括所有思考块和签名，“你不需要做任何事”。

下面几段代码按 Google 2026 年 10 月的文档写成，没有实际调用 API 运行过。`genai.Client()` 会从环境变量 `GEMINI_API_KEY` 读取密钥。

```python
import base64
from google import genai

client = genai.Client()
MODEL = "gemini-3.1-flash-image"  # Nano Banana 2

first = client.interactions.create(
    model=MODEL,
    input="画一架停在草地上的白色小飞机，扁平插画风格",
)
with open("v1.png", "wb") as f:
    f.write(base64.b64decode(first.output_image.data))

# 第二轮：不重发图片，只引用上一轮的 interaction
second = client.interactions.create(
    model=MODEL,
    input="在机翼上加一只长颈鹿，其他元素保持不变",
    previous_interaction_id=first.id,
)
if second.output_image:
    with open("v2.png", "wb") as f:
        f.write(base64.b64decode(second.output_image.data))
```

这种写法的前提是 interaction 保存在服务端（`store` 保持默认的 true）。如果你出于数据保留要求关掉了 `store`，就进入无状态模式，要自己把完整历史发回去。这时文档的要求很明确：收到的所有思考块必须原样重发，不能删、不能改；Google 搜索这类内置工具的调用和结果块带有各自的签名，也要一起回传。中途换模型时，之前那个模型的思考块照样回传，兼容由后端处理。

## 修法二：generateContent 用 chat，或原样追加 content

继续用 generateContent 也能修好。Google Cloud 文档的说法是：用官方 Gen AI SDK（Python、Node.js、Go、Java）的 chat 历史功能，或者把模型上一轮返回的完整 content 追加进历史，签名会自动处理。

最省事的是 chat 对象：

```python
from google import genai
from google.genai import types

client = genai.Client()
chat = client.chats.create(
    model="gemini-3.1-flash-image",
    config=types.GenerateContentConfig(response_modalities=["TEXT", "IMAGE"]),
)

chat.send_message("画一架停在草地上的白色小飞机，扁平插画风格")
r2 = chat.send_message("在机翼上加一只长颈鹿，其他元素保持不变")

for part in r2.candidates[0].content.parts:
    if part.inline_data:
        with open("v2.png", "wb") as f:
            f.write(part.inline_data.data)
```

需要自己管理 `contents` 时，关键是追加 `response.candidates[0].content` 这个对象本身，而不是从里面取出图片再拼一个新的 part：

```python
config = types.GenerateContentConfig(response_modalities=["TEXT", "IMAGE"])
contents = [
    types.Content(role="user", parts=[types.Part(text="画一架停在草地上的白色小飞机，扁平插画风格")])
]
r1 = client.models.generate_content(
    model="gemini-3.1-flash-image", contents=contents, config=config
)

# 正确：原样追加，签名留在原来的 part 上
contents.append(r1.candidates[0].content)

# 错误：只剩图片字节，签名丢了，下一轮会 400
# img = r1.candidates[0].content.parts[-1].inline_data.data
# contents.append(types.Content(role="model", parts=[types.Part.from_bytes(data=img, mime_type="image/png")]))

contents.append(
    types.Content(role="user", parts=[types.Part(text="在机翼上加一只长颈鹿，其他元素保持不变")])
)
r2 = client.models.generate_content(
    model="gemini-3.1-flash-image", contents=contents, config=config
)
```

对话要存进数据库、下次再接着改时，存的应该是模型那一轮完整的 content，每个 part 连同它的 `thoughtSignature` 字段一起保存，读回时原样放回；只存图片文件，下次就只能当作新图片上传。Google Cloud 文档另有三条手写历史时的规则：签名要放在它原来的 part 里发回；不要把带签名的 part 和不带签名的 part 合并；也不要把两个都带签名的 part 合成一个，签名字符串没法合并。

用流式接口自己拼历史时还要多看一步。Google Cloud 文档提醒，流式输出时签名可能出现在一个文本为空的 part 里，要把所有 part 一直解析到返回 `finish_reason` 为止。实践上，按 `if chunk.text:` 过滤 chunk 的写法会把这个空文本 part 连同签名一起跳过；保留每个 chunk 里的全部 part，再组装模型那一轮，就不会漏掉。

![流式响应保留全部part及可能位于空文本part中的签名](https://blog.laozhang.ai/posts/zh/nano-banana-2-thought-signature-error/img/content-2.webp)



## 修法三：Vercel AI SDK、Dify、n8n 丢签名时升级或绕开

签名丢在你改不动的代码里时，先看框架有没有修过：

- **Vercel AI SDK**：issue #12988 对应的修复“fix missing support for image thought signatures”已发布在 `ai` 6.0.114 和 `@ai-sdk/google` 3.0.38，两个包都要升级。同一个 issue 里，报告者升级后仍然收到同样的报错；从他的复现代码看，助手消息是用保存下来的图片文件重新拼出来的，里面本来就没有签名，SDK 也补不回来。升级之后，让 SDK 回放它自己返回的消息，不要用图片文件重建助手消息（这一点是根据复现代码的推断）。
- **Dify**：上面的 #2262 已于 2026 年 1 月 6 日以“不计划处理”关闭。插件后来的版本是否支持图片签名，issue 里没有说明，升级插件后先用两轮改图测一次。
- **n8n**：公开报告主要集中在 AI Agent 节点调用工具时缺签名，例如 [#23798](https://github.com/n8n-io/n8n/issues/23798) 和 [#31303](https://github.com/n8n-io/n8n/issues/31303)，两者都在 2026 年 7 月 14 日关闭，先把 n8n 升到这之后的版本。[#35393](https://github.com/n8n-io/n8n/issues/35393)（2026 年 8 月，Vertex 上的 `gemini-3.5-flash` 接工具后报 400，去掉工具就正常）截至 2026 年 10 月 5 日仍未关闭，那份报错只有一句“Google request failed with status code 400”，没写是不是签名问题。

暂时改不了框架时，还有一条退路：把上一张图当作你自己上传的参考图，和修改要求一起发一个新的单轮请求。签名只附在模型生成的内容上，你上传的图片不需要签名，所以不会触发这条 400；代价是模型拿不到上一轮的推理，多次连续修改时风格和细节的一致性可能变差。

## Function call is missing a thought_signature：函数调用的签名规则

用 Gemini 3 模型做工具调用或 Agent 流程时，同类报错写作 `Function call is missing a thought_signature in functionCall parts`，Dify 和 n8n 的 issue 标题里都出现过。函数调用的规则比图片更严，Google Cloud 文档写的是：

| 情况 | 签名在哪 | 怎么回传 |
| --- | --- | --- |
| 一步里只调一个函数 | 这个 `functionCall` part 上 | 连同 part 原样放回，再接 `functionResponse` |
| 一轮里先后调多个函数（顺序调用） | 每一步的第一个 `functionCall` part 上 | 当前这一轮所有步骤都会校验，任何一步缺签名都是 400 |
| 一步里同时调多个函数（并行调用） | 只有第一个 `functionCall` part 带签名 | 顺序必须是全部调用在前、全部结果在后：FC1（带签名）、FC2、FR1、FR2；交错成 FC1、FR1、FC2、FR2 会 400 |

“一轮”从最近一条不是 `functionResponse` 的用户消息算起。即使把思考等级调到最低（MINIMAL），签名照样要回传。

通过 Vertex 上的 Chat Completions 兼容接口调用时，签名放在每个 `tool_calls` 元素的 `extra_content.google.thought_signature` 里，回传助手消息时这个字段要保留在原来的 tool call 上。

## skip_thought_signature_validator 能跳过图片签名吗

不能这样用。Google Cloud 文档里，`skip_thought_signature_validator` 只用于一种少见情况：你要提供的 `functionCall` part 不是 API 生成的，所以没有签名，例如从不产生签名的其他模型迁移过来的对话历史。这时可以把 `thought_signature` 设成这个字符串，但文档同时写明它是最后手段，会降低模型表现。

文档没有说它能用在图片 part 上，也没有给图片准备任何占位签名。多轮改图报 400 时，应该回到前面三种修法，让真实的签名回到历史里；确实找不回签名的旧对话，就用“把图片当作新上传的参考图”那条退路重新开始。

## Corrupted thought signature：签名在，但被改动过

报错如果是 `Corrupted thought signature` 或 `signature is not valid` 这一类，而不是 missing，通常说明字段还在，只是内容已经不是模型发出的原样（这是从报错文字作出的判断，文档没有逐条列出原因）。照着前面的规则可以排查几处：自己序列化和反序列化时，签名有没有被重复编码或截断；有没有把带签名的 part 和别的 part 合并；有没有把别的对话、别的轮次的签名挪到这里。最省事的还是改用修法一或修法二，不手动搬运签名。

如果你的请求返回 200 却没有图片，要查的是另一组字段，见 [Nano Banana API 不出图的排查](https://blog.laozhang.ai/zh/posts/nano-banana-2-200-ok-no-image)；429、参数报错等其他接口问题，见 [Gemini 图片生成错误的分类修复](https://blog.laozhang.ai/zh/posts/gemini-image-common-errors-fix)。还没写过第一段调用代码，可以从 [Gemini 图片 API 的现役模型 ID 与 Python 代码](https://blog.laozhang.ai/zh/posts/gemini-image-api-guide-2026)开始。在 Gemini 应用里出图失败、提示 Something went wrong，属于应用端的问题，分别见 [Nano Banana 无法生成图片的修复分支](https://blog.laozhang.ai/zh/posts/nano-banana-not-working-gemini-image-errors)和 [Nano Banana Pro 错误 5](https://blog.laozhang.ai/zh/posts/nano-banana-pro-error-5)。

## 官方文档

接入参数、价格与扣费规则以老张API开发文档为准：[图片生成计费与失败处理](https://docs.laozhang.ai/api-capabilities/image-billing-and-failures)。

*说明：[laozhang.ai 是我们自己的服务](https://blog.laozhang.ai/zh/about#laozhang)，其价格是平台报价，不是独立比较的结论。*

## 参考来源

本文引用的外部页面，按正文出现顺序排列。最后更新于 2026-10-05。

- [Vercel AI SDK issue #12988](https://github.com/vercel/ai/issues/12988) (github.com)
- [Gemini 思考文档](https://ai.google.dev/gemini-api/docs/thinking) (ai.google.dev)
- [错误码列表](https://ai.google.dev/gemini-api/docs/api-errors) (ai.google.dev)
- [思考签名文档](https://docs.cloud.google.com/gemini-enterprise-agent-platform/models/thinking/thought-signatures) (docs.cloud.google.com)
- [dify-official-plugins #2262](https://github.com/langgenius/dify-official-plugins/issues/2262) (github.com)
- [Google 停用时间表](https://ai.google.dev/gemini-api/docs/deprecations) (ai.google.dev)
- [#23798](https://github.com/n8n-io/n8n/issues/23798) (github.com)
- [#31303](https://github.com/n8n-io/n8n/issues/31303) (github.com)
- [#35393](https://github.com/n8n-io/n8n/issues/35393) (github.com)
