Claude Sonnet 4.6からSonnet 5移行後にBedrock Converse APIでエラーが出たので調査してみた

Claude Sonnet 4.6からSonnet 5移行後にBedrock Converse APIでエラーが出たので調査してみた

BedrockのSonnet 5への移行時にKeyError: 'text'に遭遇しました。原因はadaptive thinkingの導入により、レスポンスの構造が変わったこと。その調査過程と修正方法についてお話しします。
2026.07.07

はじめに

最近Amazon BedrockでSonnet 5が使えるようになりましたね。
これを機にSonnet 4.6を5に移行しよう!と思ったら、躓いた話をします。

何が起きたかというと、Sonnet 4.6までは問題なく動いていたコードが、Sonnet 5に移行したところ、KeyError: 'text'というエラーを出すようになりました。
しかも毎回ではなく、数回に1回程度の頻度のこの事象が発生していました。

先に結論

原因はSonnet 5のレスポンス形式の変更によるものでした。
以下のブログも参考になるかと思います。
https://dev.classmethod.jp/articles/bedrock-claude-thinking/

  • temperature / top_p / top_kはSonnet 5で非対応。設定すると400エラーになる
  • Converse APIのcontent配列に入るblockはtextだけではない
  • Claude Sonnet 5はadaptive thinkingが常にオン(無効化不可)であり、reasoningContentcontent[0]に入る場合がある
  • content[0]で決め打ちでtextを取得しようとするとKeyErrorになる可能性がある

AWS公式ブログでもClaudeのSDK for Python (Boto3)のサンプルコードを見るとcontent[0]でtextを取得するサンプルコードが書かれているものもあります。
AIにドキュメントを参考にコードを書かせたりしているとこの記事に書いてあるようなエラーになることが予想されます。

試してみる

今回はシンプルにpythonでBedrockのConverse APIを実行して、レスポンスを解析してみたいと思います。
やることは単純で、Converse APIにプロンプトを投げて、返ってきたcontentからテキストを取り出すだけです。
AIとのチャット機能を持ったアプリではよく見る構成かと思います。

Sonnet 4.6で動いていたコード

import boto3

client = boto3.client("bedrock-runtime", region_name="us-east-1")

response = client.converse(
    modelId="us.anthropic.claude-sonnet-4-6",
    messages=[
        {
            "role": "user",
            "content": [{"text": prompt}],
        }
    ],
    inferenceConfig={"maxTokens": 4096, "temperature": 0.0},
)

raw_text = response["output"]["message"]["content"][0]["text"].strip()

デバッグ用にmodel・prompt・content blockの構造を出力しています。

model: us.anthropic.claude-sonnet-4-6
prompt: S3はすべての操作に対して強整合性(Strong Consistency)を提供しますが、...

content blocks 数: 1
  content[0] keys: ['text']

content[0]['text'] → 成功 (5733 文字)
response: # S3強整合性の誤解と競合状態の問題
## 結論から言うと「データが失われる」...

content blockが1つでtextのみ返ってきてます。
これをcontent[0]["text"]で取り出します。これがAIからの回答テキストで、チャット画面に表示する内容になります。

modelIdだけSonnet 5に変更する

ここからが本題で、BedrockでSonnet 5が使えるようになったので、既存のSonnet 4.6をSonnet 5に変更します。
modelIdus.anthropic.claude-sonnet-5に変えます。

response = client.converse(
    modelId="us.anthropic.claude-sonnet-5",  # ← ここだけ変更
    ...
    inferenceConfig={"maxTokens": 4096, "temperature": 0.0},
)

実行すると、プロンプトの内容に関係なくエラーになりました。

Traceback (most recent call last):
  ...
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling
the Converse operation: The model returned the following errors:
`temperature` is deprecated for this model.

temperatureが廃止されてる?と思ってドキュメントを確認すると、Sonnet 5ではtemperaturetop_ptop_kが非対応になっていました。

サンプリングパラメータ (temperature、top_p、top_k)をデフォルト値以外に設定すると400エラーが返されます。
参考 https://platform.claude.com/docs/en/about-claude/models/whats-new-sonnet-5

Sonnet 4.6まで普通に使っていたパラメータが使えなくなっていました。
細かいパラメータで回答精度を調整しているアプリケーションの場合この変更は辛いかもしれませんね
今回はinferenceConfigからこれらを削除します。

temperatureを削除して再実行

inferenceConfig={"maxTokens": 4096}, 

簡単な質問では成功します

model: us.anthropic.claude-sonnet-5
prompt: S3のバケット名の最大文字数は?

content blocks 数: 1
  content[0] keys: ['text']

content[0]['text'] → 成功 (497 文字)
response: # S3バケット名の最大文字数
**63文字**です。...

一見問題ないように見えます。
直った!と思って質問を続けると...

model: us.anthropic.claude-sonnet-5
prompt: S3はすべての操作に対して強整合性(Strong Consistency)を提供しますが、...

content blocks 数: 2
  content[0] keys: ['reasoningContent']
  content[1] keys: ['text']

Traceback (most recent call last):
  ...
    raw_text = content[0]["text"].strip()
               ~~~~~~~~~~^^^^^^^^
KeyError: 'text'

なぜかエラーが発生しました。
よく見るとcontent blockが2つに増えており、content[0]reasoningContentになっています。
content[1]textは存在しているのに、content[0]["text"]KeyErrorが発生します。

reasoningContentって何だろうとドキュメントを確認してみると、Sonnet 5ではadaptive thinking(思考モード)がデフォルトでオンになっているようで、無効化もできないようです。

理由:サポートされています(適応思考は常に有効で無効にすることはできません。努力レベルは設定可能です)
参考: https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-anthropic-claude-sonnet-5.html

何が起きていたかというとSonnet 5はモデルが推論を必要と判断した場合、その思考過程をreasoningContentとしてcontent配列の先頭に追加します。
今回エラーが出た質問ではadaptive thinkingが必要な難しい問題だったようです。

Sonnet 4.6の実装ではcontent配列の1番目(content[0])が常にtextだったのでcontent[0]["text"]で取り出せていましたが、Sonnet 5ではcontent[0]reasoningContentになるケースがあり、そこがエラーになるポイントでした。

重要なのは毎回ではないということで、
簡単な質問ではtextのみが返るため今までのコードで動いてましたが、Sonnet 5が推論を必要と判断した場合にだけreasoningContentが先頭に入るためエラーになります。

content blockはtextだけではない

Converse APIのレスポンスに含まれるcontentは配列で、要素は複数返ることがあります。
具体的には以下の要素があります。

blockの種類 説明
text テキスト応答
reasoningContent 推論過程(Chain of Thought)
toolUse ツール呼び出しリクエスト
toolResult ツール実行結果
image 画像
document ドキュメント
citationsContent 引用付きテキスト
guardContent ガードレール評価用

公式ドキュメント: ContentBlock - Amazon Bedrock API Reference

今回の場合、実際に返ってきたレスポンスは以下のような構造でした

{
  "output": {
    "message": {
      "content": [
        {
          "reasoningContent": {
            "reasoningText": {
              "text": "ユーザーはS3の強整合性について質問しています...",
              "signature": "WaUjzkypQ2mUEVM36O2TxuC06KN8xyfbJwyem2dw..."
            }
          }
        },
        {
          "text": "# S3の強整合性と並行更新問題\n..."
        }
      ]
    }
  }
}

content[0]reasoningContentcontent[1]text
content[0]["text"]で取り出そうとすれば当然KeyErrorになります。

修正方針

修正の方針としては、content[0]を決め打ちせず、content配列をループしてtextキーを持つblockのみを取り出す形にします。reasoningContenttoolUseなどは無視します。

また、textが1件も取れなかった場合に空文字で処理を続行するとサイレント不具合になるので、例外を投げるようにします。

修正後のコード

from typing import Any

def extract_text_from_converse_response(response: dict[str, Any]) -> str:
    """
    Converse API のレスポンスから text block のみを取り出す。
    reasoningContent / toolUse 等は無視する。
    """
    content = response["output"]["message"]["content"]

    texts: list[str] = []
    for block in content:
        if "text" in block:
            texts.append(block["text"])

    if not texts:
        raise ValueError(
            f"No text block found. keys in each block: "
            f"{[list(b.keys()) for b in content]}"
        )

    return "\n".join(texts).strip()

修正後の実行結果

先ほどKeyErrorになった同じプロンプトを、修正後のコードで実行してみます。

model: us.anthropic.claude-sonnet-5
prompt: S3はすべての操作に対して強整合性(Strong Consistency)を提供しますが、...

content blocks 数: 2
  content[0] keys: ['reasoningContent']
  content[1] keys: ['text']

extract_text_from_converse_response → 成功 (3476 文字)
response: # S3の強整合性とRead-Modify-Writeパターンの問題...

reasoningContentをスキップしてtext blockを正しく取得できました。

まとめ

Sonnet 4.6 → 5への移行で気をつけるポイントとしては、まずtemperature / top_p / top_kが廃止されているのでinferenceConfigから削除する必要があります。
次に、adaptive thinkingが常にオンのためcontent[0]textとは限りません。content[0]で決め打ちでtextを取得しようとすると、reasoningContentが先頭に入ったタイミングでKeyErrorになる可能性があります。
contentをループしてtextblockを探す実装にしましょう。

公式ドキュメントのサンプルコードでもcontent[0]["text"]と書かれていますが、あくまで簡易サンプルです。
Sonnet 5以降のモデルを使う場合は、content blockの構造を意識した実装にしておくと安心かと思います。


Claudeならクラスメソッドにお任せください

クラスメソッドは、Anthropic社とリセラー契約を締結しています。各種製品ガイドから、業種別の活用法、フェーズごとのお悩み解決などサービス支援ページにまとめております。まずはご覧いただき、お気軽にご相談ください。

サービス詳細を見る

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事