[Rust] Amazon BedrockでClaude 3.7 Sonnet の拡張思考をやってみる

[Rust] Amazon BedrockでClaude 3.7 Sonnet の拡張思考をやってみる

Clock Icon2025.02.27

Introduction

ここで解説しているように、BedrockでClaude 3.7が使えるようになりました。
ここではRust用SDKを使って、BedrockでClaude3.7の拡張思考推論を実行してみます。

Environment

  • MacBook Pro (14-inch, M3, 2023)
  • OS : MacOS 14.5
  • Rust : 1.83.0

AWSアカウントはセットアップ済みとします。

Setup

2025年2月現在、Claude3.7が使えるのはus-east-1(バージニア)、us-east-2(オハイオ)、us-west-2(オレゴン)です。

まずはAWSコンソールこのあたりを参考に、Claude3.7のモデルリクエストを実施して使える状態にしましょう。

なお、(現在)このモデルはクロスリージョン推論を通じてのみアクセス可能です。
aws cliでモデルの情報をみてみると、Claude3.7はON_DEMAND
でなくINFERENCE_PROFILE(推論プロファイル)であることがわかります。

% aws bedrock list-foundation-models --region us-east-1

・・・

{
            "modelArn": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-7-sonnet-20250219-v1:0",
            "modelId": "anthropic.claude-3-7-sonnet-20250219-v1:0",
            "modelName": "Claude 3.7 Sonnet",
            "providerName": "Anthropic",
            ・・・
            "inferenceTypesSupported": [
                "INFERENCE_PROFILE"
            ],
            "modelLifecycle": {
                "status": "ACTIVE"
            }
        },

なので、プログラムではモデルIDでなくinferenceProfileArnを指定する必要があります。
inferenceProfileArnは以下コマンドで取得できます。

% aws bedrock list-inference-profiles --region us-east-1

クロスリージョン推論についてはこちらを参照してください。

Try

cargo newで適当なプロジェクトを作ったら依存ライブラリを指定します。

#Cargo.toml
[dependencies]
aws-config = "1.5.1"
aws-sdk-bedrockruntime = "1.75.0"
tokio = { version = "1.43.0", features = ["full"] }
serde = { version = "1.0.218", features = ["derive"] }
serde_json = "1.0.139"
aws-sdk-bedrock = "1.75.0"
aws-smithy-types = "1.2.13"

ではBedrockで拡張思考で推論してみます。
やることはシンプルで、モデル実行時のpayloadに以下の要素を追加するだけです。

thinking: {
    type: "enabled",
    budget_tokens: <トークン>
}

以下の質問を拡張思考のありなしで比較してみましょう。

私は目に見えないけれど、誰もが持っている。
夜に訪れることが多いけれど、昼間に現れることもある。
現実ではあり得ないことが起こり、時に美しく、時に恐ろしい。
記憶に残ることもあれば、すぐに忘れてしまうこともある。
目を閉じると見えることがあるが、目を開けると消えてしまう。

私は何?

まずは通常の推論。普通に答えてます。

======================================================
拡張思考モード: 無効
推論時間: 4.71秒
トークン使用量: 入力=138, 出力=189
======================================================

【回答】
この謎かけの答えは「夢」です。

夢は目に見えませんが誰もが持っています。主に夜の睡眠中に訪れますが、
昼寝の際にも見ることがあります。現実ではあり得ない不思議な出来事が起こり、
美しい夢や怖い悪夢など様々です。朝起きると鮮明に覚えている夢もあれば、
すぐに忘れてしまう夢もあります。そして目を閉じた状態(睡眠中)に見えるものですが、
目を開けて目覚めると消えてしまいます。

拡張思考モードでの推論。
時間が3倍近くかかってますが、思考プロセスと回答を表示しています。

======================================================
拡張思考モード: 有効
推論時間: 12.97秒
トークン使用量: 入力=166, 出力=679
======================================================

【拡張思考プロセス】
この問題は日本語の謎かけですね。特徴を整理してみましょう:

1. 目に見えないが、誰もが持っている
2. 夜に訪れることが多いが、昼間に現れることもある
3. 現実ではあり得ないことが起こる
4. 美しくも恐ろしくもなりうる
5. 記憶に残ることも忘れることもある
6. 目を閉じると見えることがあるが、目を開けると消える

これらの特徴を総合すると、「夢」のことを指していると考えられます。

- 夢は物理的に目に見えないものですが、誰でも夢を見る能力を持っています
- 夢は主に夜寝ている間に見るものですが、昼寝をすれば昼間にも見ることができます
- 夢の中では現実ではあり得ない不思議なことが起こります
- 夢は美しい楽しい内容のこともあれば、悪夢のように恐ろしいこともあります
- 夢の内容をはっきり覚えていることもあれば、すぐに忘れてしまうこともあります
- 目を閉じて寝ると夢を見ることがあり、目を開けて起きると夢は消えてしまいます

したがって答えは「夢」だと考えられます。

【回答】
これは「夢(ゆめ)」ですね。

夢は目に見えない精神的な現象ですが、誰もが見る能力を持っています。
主に夜の睡眠中に訪れますが、昼寝の時にも見ることがあります。
夢の中では、空を飛んだり、不思議な生き物に会ったりと、現実ではあり得ない体験ができます。

美しい夢に心が躍ることもあれば、悪夢に恐怖を感じることもあります。
朝起きた時に鮮明に覚えている夢もあれば、すぐに忘れてしまう夢もあります。
そして、目を閉じて眠りにつくと夢の世界が広がりますが、目を覚まして開けると現実に戻り、
夢は消えてしまいます。

拡張思考モードでのレスポンスJSONは以下。
contentの下に「thinking」という要素があり、これが思考プロセスを表しています。

{
    "id": "xxx",
    "type": "message",
    "role": "assistant",
    "model": "claude-3-7-sonnet-20250219",
    "content": [
        {
            "type": "thinking",
            "thinking": "この問題は日本語の謎かけですね。特徴を整理してみましょう:\n\n1. 目に見えないが、誰もが持っている\n2. 夜に訪れること・・・・・・・・・",
            "signature": "・・・"
        },
        {
            "type": "text",
            "text": "これは「夢(ゆめ)」ですね。\n\n夢は目に見えない精神的な現象ですが、誰もが見る能力を持っています。主に夜の睡眠中に訪れますが、昼寝の時にも見るこ・・・・・"
        }
    ],
    ・・・
}

実行したプログラムは以下。

use aws_config::BehaviorVersion;
use aws_sdk_bedrockruntime::Client as RuntimeClient;
use aws_sdk_bedrockruntime::primitives::Blob;
use serde_json::json;
use std::error::Error;
use std::time::{Duration, Instant};

/// Amazon Bedrock Runtimeクライアントをセットアップするための関数
async fn setup_bedrock_client() -> Result<RuntimeClient, Box<dyn Error>> {
    let config = aws_config::load_defaults(BehaviorVersion::latest()).await;
    Ok(RuntimeClient::new(&config))
}

/// Claude-3モデルへのリクエストペイロードを構築する関数
fn build_claude_payload(prompt: &str, use_thinking: bool) -> serde_json::Value {
    let mut payload = json!({
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": 2000,
        "messages": [
            { 
                "role": "user", 
                "content": [
                    { "type": "text", "text": prompt }
                ]
            }
        ]
    });

    // 拡張思考を使用する場合は追加
    if use_thinking {
        payload["thinking"] = json!({"type": "enabled", "budget_tokens": 1600});
    }

    payload
}

/// Claude-3モデルを呼び出す関数
async fn invoke_claude_model(
    client: &RuntimeClient,
    model_id: &str,
    payload: serde_json::Value,
) -> Result<(String, Duration), Box<dyn Error>> {
    // 開始時間を記録
    let start_time = Instant::now();

    // モデルを呼び出し
    let output = client
        .invoke_model()
        .model_id(model_id)
        .content_type("application/json")
        .body(Blob::new(serde_json::to_vec(&payload)?))
        .send()
        .await?;

    // 経過時間を計算
    let elapsed = start_time.elapsed();

    // レスポンスをUTF-8文字列に変換
    let result = String::from_utf8(output.body.into_inner().to_vec())?;
    Ok((result, elapsed))
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    // 質問例
    //let prompt = "量子コンピューティングが従来のコンピューティングと比較してどのような利点と課題があるか説明してください。";
    let prompt = "私は目に見えないけれど、誰もが持っている。夜に訪れることが多いけれど、昼間に現れることもある。現実ではあり得ないことが起こり、時に美しく、時に恐ろしい。記憶に残ることもあれば、すぐに忘れてしまうこともある。目を閉じると見えることがあるが、目を開けると消えてしまう。私は何?";

    // Claude-3モデルのARN
    let model_id = "<ARN>";

    // Bedrockクライアントをセットアップ
    let client = setup_bedrock_client().await?;

    println!("質問: {}\n", prompt);

    // 拡張思考なしで実行
    println!("Claude-3に問い合わせ中(拡張思考なし)...");
    let payload_without_thinking = build_claude_payload(&prompt, false);
    let (response_without_thinking, elapsed_without_thinking) = 
        invoke_claude_model(&client, model_id, payload_without_thinking).await?;

    // 拡張思考ありで実行
    println!("\nClaude-3に問い合わせ中(拡張思考あり)...");
    let payload_with_thinking = build_claude_payload(&prompt, true);
    let (response_with_thinking, elapsed_with_thinking) = 
        invoke_claude_model(&client, model_id, payload_with_thinking).await?;

    //結果を表示
    println!("response_with_thinking:{}", response_with_thinking);
    println!("elapsed_with_thinking:{}", elapsed_with_thinking);
    Ok(())
}

Summary

今回はClaude 3.7の拡張思考をつかってみました。
要素を追加するだけで使えますが、出力トークンと実行時間が増えるので、
そのあたりに注意しながらつかいましょう。

References

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.