【検証】もしかしてClaude 3.5 Sonnetの出力形式変わった？

こんにちは、ふくちと申します。

2024年10月23日にClaudeのアップデートがありましたね！

https://www.anthropic.com/news/3-5-models-and-computer-use

この中で、**Claude 3.5 Sonnet new**が新しく公開されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/a3b2fc8f-e271-8f32-6bfe-d9897c6c6821.png)

旧Claude 3.5 Sonnetよりもコーディング性能がはるかに向上しているようで、更なる性能向上が期待されています。

ただ、この新Sonnetに対して、私は少し違和感を抱きました。

# 個人的に違和感を抱いたきっかけ
Claudeのプレスリリースを日本語に翻訳してもらおうと思い、いつものように「翻訳して」とお願いしました。すると…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/d12af4e7-76cf-3204-013b-5c93f0e08953.png)

はい。お気づきの方がいるかもしれませんが、**「翻訳内容だけ出力されている」** のです。

旧Sonnetでは、翻訳を依頼すると、「わかりました。この文章を翻訳します。」などの言葉が付け足されていることがありました。

(↓は旧Sonnetを使用していた際の出力例です。)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/5b79247d-9b8d-cc0e-8b7a-44a2552e8f8a.png)

アップデートに伴って、賢さ以外にも色々と変化したものがあるのかもしれません。

実際に、[みのるんさんの宇宙最速勉強会](https://www.youtube.com/watch?v=M6UHLFhZX1s)では、「もしかしたらそもそもの出力形式が変わっているのかも？」というお話があったため、少し色々検証してみることにしました。

# 検証内容
旧Sonnetと新Sonnetで、出力形式にどのような違いが出るのかを検証します。
出力内容については、今回は検証対象です。

# 仮説
新Sonnetは、ユーザーの入力により忠実に回答するようトレーニングされているのではないか。
したがって、「翻訳して」と言われたら翻訳結果だけを返却するし、「json形式にして」と言われたらjsonだけを返却するようになっているはず。

# Bedrock上で比較してみる
Amazon Bedrockのコンソール上で、2つのモデルに同じプロンプトを投げて出力させることができます。
まずはそれを使って、翻訳の依頼結果比較をしてみます。

## 1.Claude 3.5 Sonnet v2の有効化
Bedrockのオレゴンリージョンにはすでに新Sonnetが来ているので、まずはそれを有効化しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/7c4f8a11-0c61-6d0d-b55b-a8c7e99adeed.png)

## 2.プレイグラウンドでモデル比較準備
左の項目一覧から、「プレイグラウンド」->「Chat/text」を選択し、その後「モデルを選択」していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/2edae39b-f453-1b16-d3ec-ef17fac83c68.png)

まずは新Sonnetを選択し、「適用」をクリック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/19850a21-b2e9-974f-2ea5-e4be1f902c53.png)

その後、右上に表示される「比較モード」をクリック。続いて再び「モデルを選択」します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/6128f58b-30f0-32a7-6ac7-0adbc6b3e0d8.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/4995f2ce-3466-51ae-a718-a0e3cd4abd79.png)

今回は旧Sonnetを選択。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/65b31fa0-ad41-dfac-1b2f-6da3d244b4bd.png)

左が新Sonnet、右が旧Sonnetです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/48bb33a1-77e0-2875-9a5d-0bb1102b7d32.png)

各種パラメータはどちらもデフォルトで同じ状態にしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/7ed13506-c4d5-52d1-2094-7ef97fd50063.png)

(何気に新Sonnetはまだガードレール対応してないんですね)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/d6896276-9672-b644-8f1a-186e7516f921.png)

ここに、先ほどと同じように翻訳依頼をしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/1ab592f6-b98f-2ea2-dd39-e7f90563185f.png)

あれ？？？
がっっつり「以下が日本語訳です」って表示されてますね…想定と違う結果になりました。

他で試してみると、翻訳内容だけ返ってくることもありましたが…なんだか安定しませんね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/b4ad0f37-00ce-f657-95f2-c7cc52ad4052.png)

※「翻訳結果だけ返して」と依頼すると、どちらもその通りに実行してくれました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/1753d0dd-b37b-b460-1a3a-01c95c5a1890.png)

他にも、json形式の出力実験もしてみましょう。
サンプルプロンプトが用意されているので、それを使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/20bab55c-4d69-223e-1f13-b45729cb9d46.png)

こちらは仮説通りjson形式だけで返ってきますね！一方で旧Sonnetは余計な文言が入っているので変化が伺えます。

もしこの出力が安定すれば、アプリでの自由度も上がるんじゃないでしょうか？

生成AIからの出力をそのまま表示するだけでなく、フロント側で色々整形して表示することができそうです！
***
ということで、翻訳パターンとjsonパターンをそれぞれ10回ずつくらい試してみました！

※対象は新Sonnetです。また、Bedrockだけでなく、Claudeコンソールでの実行結果も参考として載せております。
ちなみにですがBedrockだと2~3回実行しただけですぐToo Many Requestsになるのでご注意を…

| パターン | 結果だけ返却 | 余計な文言も返却 |
|:-|:-:|:-:|
| 翻訳(Bedrock) | 5 | 5 |
| json(Bedrock) | 10 | 0 |
| 翻訳(claude.ai) | 10 | 0 |
| json(claude.ai) | 10 | 0 |

jsonの方はかなり安定して結果だけを返却してくれました。
ただ、Bedrock上だとなぜか翻訳のみ微妙な結果に… ~~「翻訳ならそもそもTranslateで良くない？」というツッコミは見なかったことにします~~

## 3.Agents for Amazon Bedrockでトレース内容を見る
以前に参加したハッカソンで作成したエージェントを用いて実験を行いました。
(旅行計画作成のために旅行者の情報を取得し、RAGからおすすめコンテンツを紹介してくれるようなエージェントです。)

使用するモデルだけ変更し、プロンプト含めその他の条件は同じにして実験を行います。
※東京リージョンで、旧Sonnetを使用
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/a6f099cd-523b-9128-02b0-0b375aff149d.png)

※オレゴンリージョンで、新Sonnetを使用
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/d526cbe0-a851-1259-da66-bac04f22142c.png)

<details><summary>※使用したプロンプトはこちら</summary>

```json
<questions>
1.出発地
2.到着地
3.出発日
4.帰着日
5.大人の人数
6.小児の人数
7.幼児の人数
8.アクティビティの予算
9.アクティビティに関しての好み
</questions>

<example_answer>

{
  "travelBasic": {
    "outbound": {
      "location": "[旅行の時に出発する都道府県]",
      "date": "[旅行の出発日]"
    },
    "inbound": {
      "location": "旅行の目的地がある都道府県",
      "date": "旅行の帰着日"
    },
    "people": {
      "adultsアクティビティは人数]",
      "children": "[小児の人数]",
      "infants": "[幼児の人数]"
    }
  },
  "activities": [
    {
      "name": "洞爺湖カヌーツーリング",
      "description": "[アクティビティ説明]",
      "price": "6,600",
      "https://hitottabi-picture.s3.ap-northeast-1.amazonaws.com/activity/activity1.jpg"
    },
    {
      "name": "青の洞窟ボートクルーズ",
      "description": "[アクティビティ説明]",
      "price": "4,980",
      "image": "https://hitottabi-picture.s3.ap-northeast-1.amazonaws.com/activity/activity2.jpg"
    },
    {
      "name": "支笏湖カヤック体験",
      "description": "[アクティビティ説明]",
      "price": "8,000",
      "image": "https://hitottabi-picture.s3.ap-northeast-1.amazonaws.com/activity/activity3.jpg"
    }
  ],
  "message": "AIがこの3つのアクティビティを選んだ理由"
}


</example_answer>

¥<role>
あなたはユーザーにおすすめのアクティビティをナレッジベースから提案するエージェントです。
<questions>タグの内容をすべてユーザーに質問してください。
追加質問や最終回答は必ず日本語かつフレンドリーに回答してください。
</role>

<instructions>
・必ず<questions>タグの9つの質問をユーザーにしてください。
・<questions>タグのユーザーの回答から、必ずナレッジベースの「knowledge-base-hitottabi-activity-2」の中で条件に合ったアクティビティを3つ探して下さい。
・アクティビティは必ず3つ提案してください。
・<questions>タグ内の情報が全て揃っている場合、<example_answer>タグを参考にあなたが保持する情報の中から条件に合ったおすすめのアクティビティを３つ、必ずJSON形式のみで回答してください。

・descriptionは20文字程度で返してください。

・messageは20文字程度で返してください。
・最終回答内のdateに関しては全てYYYY-MM-DD形式で回答してください。
・最終回答内の人数に関しては数字のみで回答してください。
・最終回答内のpriceに関しては数字のみで回答してください。
・回答がJSON形式のみになるまでチェックし、JSON形式のみでユーザーに回答してください。
</instructions>

Assistant:
{"travelBasic": {"outbound": {"location": "[

```

</details>

### 旧Sonnetを用いたエージェントの出力結果
まずは旧Sonnetから。下記の「トレースを表示」を参照します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/66a82cad-fae8-5f72-63ee-a3beb3d64808.png)

すると、下記のように出力されました。
```json:トレースステップ(一部抜粋)
"rationale": {
  "text": "ユーザーが旅行に行きたいと言っているので、旅行の詳細を聞き出し、おすすめのアクティビティを提案するために必要な情報を収集する必要があります。まずは、<questions>タグにある9つの質問を順番に聞いていきましょう。",
  "traceId": "c7bfe1d3-43e3-40e6-a500-ffc4bc52dacb-0"
},
"observation": [
  {
    "finalResponse": {
      "text": "旅行の計画を立てるのを手伝いますね！まず、出発地はどちらになりますか？"
    },
    "traceId": "c7bfe1d3-43e3-40e6-a500-ffc4bc52dacb-0",
    "type": "ASK_USER"
  }
]
```


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/2d2039c4-cdee-9536-6b51-900a8af4049d.png)


その中でrationaleとobservationがよくわからなかったので調査しました。
#### rationale
エージェントが、アクショングループを実行したり、ナレッジベースから情報を取得したりする根拠として、入力に基づいて使用する推論。

Contents：
- text：
入力に基づいたエージェントの推論や思考プロセス
型：String
※必須パラメータではない
- traceId
トレースステップの一意の識別子
型：String
長さ：最小2文字、最長16文字
※必須パラメータではない

https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_Rationale.html

#### observation
アクショングループやナレッジベースの結果または出力、あるいはユーザーへの応答。

Contents：
- actionGroupInvocationOutput
アクショングループによって呼び出されたAPIから返されたJSON形式の文字列
型：[ActionGroupInvocationOutput](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_ActionGroupInvocationOutput.html) オブジェクト
※必須パラメータではない
- codeInterpreterInvocationOutput
コードインタプリタによって呼び出されたAPIから返されたJSON形式の文字列
型：[CodeInterpreterInvocationOutput](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_CodeInterpreterInvocationOutput.html) オブジェクト
※必須パラメータではない
- finalResponse
ユーザーへの応答に関する詳細を含みます。
型：[FinalResponse](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_FinalResponse.html) オブジェクト
※必須パラメータではない
- knowledgeBaseLookupOutput
ナレッジベースの検索結果に関する詳細
型：[KnowledgeBaseLookupOutput](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_KnowledgeBaseLookupOutput.html) オブジェクト
※必須パラメータではない
- repromptResponse
入力の再プロンプトに対する応答の詳細
型：[RepromptResponse](https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_RepromptResponse.html) オブジェクト
※必須パラメータではない
- traceId
トレースの一意の識別子
型：String
長さ：最小2文字、最長16文字
※必須パラメータではない
- type
エージェントが観測で返す情報の種類
型：String
※必須パラメータではない
有効な値：
  - `ACTION_GROUP` – エージェントがアクショングループの結果を返します
  - `KNOWLEDGE_BASE` – エージェントがナレッジベースからの情報を返します
  - `FINISH` – エージェントがフォローアップなしでユーザーに最終応答を返します
  - `ASK_USER` – エージェントがユーザーに質問をします
  - `REPROMPT` – エージェントが同じ情報について再度ユーザーにプロンプトを出します

https://docs.aws.amazon.com/bedrock/latest/APIReference/API_agent-runtime_Observation.html

要するに、
- **rationaleはエージェントの思考回路**
- **observationは実際に取得した情報や返却する情報**

を指します。

### 新Sonnetを用いたエージェントの出力結果
続いて新Sonnet。下記の「トレースを表示」を参照します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/3d7af053-9028-6078-c55f-9293cddd5e6d.png)

すると、下記のように出力されました。
```json:トレースステップ(一部抜粋)
"observation": [
  {
    "finalResponse": {
      "text": "こんにちは！旅行のプランニングのお手伝いをさせていただきます。最適なアクティビティをご提案するために、いくつか質問させていただきたいと思います。\n\n1. まず、どちらから出発されますか？（都道府県をお教えください）"
    },
    "traceId": "dbc77f75-c280-4eb3-9ae9-aa992400e76e-0",
    "type": "FINISH"
  }
]
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/1e8f240e-baec-f143-c7f2-9a7892ee55dd.png)

**あれ…`rationale`が出力されていない…？**

その後、やり取りを続けても同じように、rationaleは表示されませんでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/4a38b82e-a999-b95f-93c3-1db0c01e51ac.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/9a0b4994-d647-4b9c-7446-ad86ef368b54.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/37178e95-aee5-2319-2669-993b5c8437c5.png)

お、最後のプラン作成のところではrationaleが出力されましたね。

この出力される時とされない時の違いは一体なんなんだ…？

# 検証結果を踏まえての考察
ここまでの検証結果をまとめます。

プレイグラウンドでの検証結果：
| パターン | 結果だけ返却 | 余計な文言も返却 |
|:-|:-:|:-:|
| 翻訳(Bedrock) | 5 | 5 |
| json(Bedrock) | 10 | 0 |
| 翻訳(claude.ai) | 10 | 0 |
| json(claude.ai) | 10 | 0 |

エージェントでの検証結果：
| 使用エージェント | rationaleパラメータ | observationパラメータ |
|:-|:-|:-|
| 旧Sonnet(東京) | 毎回出力される | 毎回出力される |
| 新Sonnet(オレゴン) | プラン作成でのみ出力される | 毎回出力される |

:::note warn
これ以降は完全なる私の妄想です。一切エビデンスなどはありませんので話半分程度にご覧ください。
もし考えがあればぜひコメントで教えてください！
:::

## 仮説1.ユーザーの入力により忠実に回答するようトレーニングされている
プレイグラウンドの結果を踏まえて、以前よりユーザーからの命令に対して忠実になっている印象を受けます。

もしかしたら、その通りにトレーニングされているのかもしれません。

ただ、その場合はエージェントでもっと簡素なやり取りになっている可能性があります。

## 仮説2.より人間に近い思考ができるようになった
検証結果を踏まえ、個人的な考察としては「賢くなりすぎたのでは？」と考えています。

プレイグラウンドでは、ユーザーからの命令に対して忠実に・聞かれたことだけに回答していたり、
エージェントでは、難しいタスクの時にのみ思考回路が出力されたり。

今までは一々考えてからAIっぽく答えを出力していたClaudeが、賢くなりすぎた結果、 **「一部のことは人間のように・無意識的に考えられるようになったのではないか」** と想像しています。

私たちが下記アイコンを見て無意識にChromeだと認識できるように、AIも無意識にできることが増えてきており、


**その結果簡単なタスクは人間と同様に行うことができるようになり、Computer Use機能も実装できた**…のではないかと私は考えています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/b61d65fd-247b-802c-efb0-d0eb65a017c1.png)

私の検討が合っているにしろ間違っているにしろ、AIの進化がより一層進んで、面倒くさい仕事をもっと代わりにやってくれるようになると嬉しいですね！

(予定聞かれた時に、自分の予定表やチャット履歴を参照したり、業務の進捗率を考え、行けるか否かを判断して答えておいてくれる、とかw)

読んでいただきありがとうございました！
また何か分かったら記事にします！