Amazon BedrockをCloud9のJavaから呼び出してみた

# はじめに
みなさん、こんにちは。2023年9月末に「Amazon Bedrock」が一般提供されました。  
多くの方が、PythonからAmazon BedrockのAPIを呼び出したりしていると思います。  
今回は、JavaからAmazon BedrockのAPIを呼んでみましたので手順を紹介します。

なお、本記事ではCloud9やAWS SDK for Javaの詳細なセットアップの説明は割愛します。

# Cloud9を準備する
Javaの拡張機能を有効にするには、2 GiB以上のメモリが必要[^1]なため、インスタンスタイプは m5.large を選択しました。  
プラットフォームはAmazon Linux2で作成しました。

[^1]: https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/enhanced-java.html

# AWS SDK for Javaを準備する
[Cloud9のユーザーガイド]に従って、Cloud9でAWS SDK for Javaを使用できるようにします。  
今回は、Mavenを使用しました。

[Cloud9のユーザーガイド]: https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/sample-java.html#sample-java-sdk

# Amazon Bedrockを準備する
基盤モデルが多数サポートされているバージニア北部リージョンで試してみました。  
基盤モデルはAnthropic社のClaudeを使用します。Amazon BedrockでClaudeモデルを使用するには申請が必要です。  
申請が通った後、Claudeモデルが有効化できるようになります。

# Javaのサンプルコードを書いてみる
SDKを使ってコードを書いてみます。  
コマンドライン引数にプロンプトと基盤モデルのIDを指定して実行できるようにしました。

## 実装
Amazon Bedrock Runtimeにアクセスするためのサービスクライアントを作成します。
```java
BedrockRuntimeClient runtime = BedrockRuntimeClient.builder()
    .region(Region.US_EAST_1)
    .build();
```

リクエストを組み立てます。  
Claudeモデルを使用する場合、プロンプトは`\n\nHuman:`と`\n\nAssistant:`で挟む必要があります。  
`max_tokens_to_sample`パラメータは必須なので指定しておきます。  
パラメータの詳細については[Anthropic社のページ]が参考になります。

[Anthropic社のページ]: https://docs.anthropic.com/claude/reference/complete_post

```java
String prompt = args[0];

JSONObject jsonBody = new JSONObject()
    .put("prompt", "\n\nHuman:" + prompt + "\n\nAssistant:")
    .put("max_tokens_to_sample", 1024);
```

コマンドラインの第二引数から取得したClaudeのモデルIDと、SdkBytes型に変換したリクエストボディを引数に渡します。
```java
String modelId = args[1];
        
SdkBytes body = SdkBytes.fromUtf8String(jsonBody.toString());

InvokeModelRequest request = InvokeModelRequest.builder()
    .modelId(modelId)
    .body(body)
    .build();
```

Amazon Bedrockにリクエストを送信し、レスポンスをJSONObjectに格納します。
```java
InvokeModelResponse response = runtime.invokeModel(request);

JSONObject jsonObject = new JSONObject(response.body().asUtf8String());
```

Amazon Bedrockからの回答を表示します。
```java
String completion = jsonObject.getString("completion");
System.out.println(completion);
```
<details><summary>[ご参考]サンプルコード全体</summary>

```java:App.java
package com.example.app;

import org.json.JSONObject;

import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.bedrockruntime.BedrockRuntimeClient;
import software.amazon.awssdk.services.bedrockruntime.model.InvokeModelRequest;
import software.amazon.awssdk.services.bedrockruntime.model.InvokeModelResponse;

public class App 
{
    public static void main( String[] args )
    {
        BedrockRuntimeClient runtime = BedrockRuntimeClient.builder()
            .region(Region.US_EAST_1)
            .build();
            
        String prompt = args[0];
        
        JSONObject jsonBody = new JSONObject()
            .put("prompt", "\n\nHuman:" + prompt + "\n\nAssistant:")
            .put("max_tokens_to_sample", 1024);

        String modelId = args[1];
        
        SdkBytes body = SdkBytes.fromUtf8String(jsonBody.toString());

        InvokeModelRequest request = InvokeModelRequest.builder()
            .modelId(modelId)
            .body(body)
            .build();

        InvokeModelResponse response = runtime.invokeModel(request);

        JSONObject jsonObject = new JSONObject(response.body().asUtf8String());

        String completion = jsonObject.getString("completion");
        System.out.println(completion);
    }
}
```

</details>

# サンプルコードのビルド

## 依存関係の設定
使用するJarファイルをpom.xmlのdependenciesに追加しておきます。
```xml:pom.xml
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>bedrockruntime</artifactId>
    <version>2.20.162</version>
</dependency>
<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20231013</version>
</dependency>
```
その後、Mavenでビルドします。

# サンプルコードの実行

## モデルIDの確認
サンプルコードを実行する前に、AWS CLIでClaudeのモデルIDを確認します。
```console
aws bedrock list-foundation-models --region us-east-1 
--query "modelSummaries[].[providerName,modelId,inputModalities[0],outputModalities[0]]" 
--output text | grep "Anthropic" | tr '\t' ',' | column -s, -t | sort
```

実行結果は次の通りです。2列目にモデルIDが表示されます。
```console
Anthropic  anthropic.claude-instant-v1:2:100k  TEXT  TEXT
Anthropic  anthropic.claude-instant-v1         TEXT  TEXT
Anthropic  anthropic.claude-v1:3:100k          TEXT  TEXT
Anthropic  anthropic.claude-v1:3:18k           TEXT  TEXT
Anthropic  anthropic.claude-v1                 TEXT  TEXT
Anthropic  anthropic.claude-v2:0:100k          TEXT  TEXT
Anthropic  anthropic.claude-v2:0:18k           TEXT  TEXT
Anthropic  anthropic.claude-v2:1:18k           TEXT  TEXT
Anthropic  anthropic.claude-v2:1:200k          TEXT  TEXT
Anthropic  anthropic.claude-v2:1               TEXT  TEXT
Anthropic  anthropic.claude-v2                 TEXT  TEXT
```

## 実行
では、サンプルコードを実行してみます。
コマンドラインの第一引数にプロンプト、第二引数にモデルIDを指定します。
```
java -cp <MavenのdependenciesのJarファイル> クラスファイル名 "プロンプト" "モデルID"
```
例えば、次のように実行します。
```console
java -cp target/qiita-test-1.0-SNAPSHOT-jar-with-dependencies.jar com.example.app.App "AWSについて説明してください。" "anthropic.claude-v2:1"
```

## 実行結果
実行結果の例です。
```console
AWS(Amazon Web Services)は、アマゾンが提供するクラウドコンピューティングサービスです。

主な特徴は以下の通りです。

- サーバーやストレージ、データベース、ネットワーク等のITインフラをインターネット経由で提供している
- 資源の調達から設定、運用・監視までを自動化しているため、迅速かつ容易に利用できる
- 使用した分の課金なので初期コストがかからず、スケーラブル
- グローバルなデータセンターを持っているため、世界中から低レイテンシーでアクセスできる

提供サービスは大きくコンピューティング、ストレージ、データベース、ネットワーキング、分析、アプリケーションサービスなど多岐にわたっています。例えばEC2(仮想マシン)、S3(オブジェクトストレージ)、RDS(リレーショナルDB)等が有名です。

クラウドへの移行が進む中、グローバルで最大シェアを誇るAWSは安定稼働と幅広いサービスが特徴的で、多くの企業で採用されています。
```

# おわりに
本記事では、JavaからAmazon Bedrockを呼び出す方法を紹介しましたが、いかがでしたでしょうか？  
今回の記事がAmazon Bedrockを利用する上で参考になれば幸いです。

なお、本記事のコードはあくまでサンプルであり、動作を保証するものではございませんのでご注意ください。

---
- AWS は、米国その他の諸国における Amazon.com, Inc. またはその関連会社の商標です。
- Javaは、Oracle、その子会社および関連会社の米国およびその他の国における登録商標です。
- Maven は、Apache Software Foundation の米国およびその他の国における登録商標もしくは商標です。
- その他、本資料に記述してある会社名、製品名は、各社の登録商品または商標です。