【Rails】Amazon Bedrockを使用してみた

どうもこんにちは。

今回はRailsアプリケーションからAmazon BedrockをAPIで接続してみたので紹介します。

# Amazon Bedrockって何？

公式ページから引用すると、、、

> Amazon Bedrockは、主要なAIスタートアップとAmazonが提供するFMをAPI経由で利用可能にするフルマネージドサービスであるため、幅広いFMから自分のユースケースに最適なモデルを選ぶことができる。Bedrockのサーバーレス・エクスペリエンスを利用すれば、インフラを管理することなく、AWSツールを使ってFMをアプリケーションに簡単に統合し、デプロイすることができます。

ものすごく簡単にいうと、「すでに存在している基盤モデルを使用して生成系AIアプリケーションを構築できるサービス」です。

# 使用方法

使用方法は、RailsサーバーからAPIを叩くだけです。

ただ、AWSコンソール上で言語モデルを使用するための設定をする必要があるので、以下のサイトを参考に設定をしてみてください。

https://weel.co.jp/media/amazon-bedrock

今回使用したモデルは「Amazon Titan Embeddings G1 - Text」というモデルです。

このモデルは、チャットGPTのような会話はできないが、入力された文字列を数値化してくれるモデルのようです。公式の説明によると、数値ひとつひとつに意味が設定されているとかされていないとか。。。（私は実際に動かしてみるまで数値化ってどういうこと？と思ってやってました。）

# 実装

## 0. AWSコンソール上で必要な設定を行う

この時に、自分のアカウントのアクセスキーとシークレットアクセスキーを入手しておいてください。

## 1. Gemで必要なライブラリを追加する

Gemfileに以下を記述します。

```Gemfile
gem 'aws-sdk-bedrockruntime'
```

`aws-sdk-bedrock`というGemを入れると、モデルとのやり取りというよりもモデルのチューニングなどを行うことができるようになるようです。

## 2. bundle install

みなさんお馴染みの`bundle install`をします。

```
$ bundle install
```

## 3. 処理を書く

処理は自分の入れたいところに入れてもらってOKです。
自分は、Railsアプリケーション内のコメント機能でコメントが入力された時に言語モデルにリクエストを投げたいので、コメントが作成される処理に入れ込みます。

先に自分が実装したコードを示します。

```ruby
comment = 'これはテストコメントです。'
access_key = 'XXXXXXXXXXXXX'  # アクセスキー
secret_access_key = 'YYYYYYYYYYYYYYYYYY'　　# シークレットアクセスキー
begin
    credentials = Aws::Credentials.new(access_key, secret_access_key)
    bedrock_client = Aws::BedrockRuntime::Client.new(region: 'ap-northeast-1', credentials: credentials)

    data = {
      "inputText": "次の文章を英語に翻訳してください。「#{comment}」"
    }

    body = data.to_json

    response = bedrock_client.invoke_model({
        accept: "*/*",
        content_type: "application/json",
        body: body,
        model_id: "amazon.titan-embed-text-v1",
    })

    string_io_object = response.body

    string_io_object.rewind
    string_io_object.each_line do |line|
      puts line
    end
rescue => e
    p e.class
    p e.message
    p e.backtrace
end
```

まず、`Aws::BedrockRuntime::Client.new`でBedrockのインスタンスを作成します。

次に、以下の形式でリクエストを投げられるようにコードを書きます。

```ruby
{
    accept: "*/*",
    content_type: "application/json",
    body: {
        "inputText": "テキスト"
    },
    model_id: "amazon.titan-embed-text-v1",
}
```

ただ、このまま「テキスト」のところに文字列を入力すると、「`"expected params[:body] to be a String or IO like object that supports read, rewind, and size, got class Hash instead."`」とエラーが出て怒られてしまうので、bodyの値は`String`型に直してからリクエストを投げるようにした方が良いと思います。

次に、`invoke_model`メソッドでリクエストを投げます。response変数でBedrockから返ってきたデータを格納します。

```ruby
response = bedrock_client.invoke_model({
    accept: "*/*",
    content_type: "application/json",
    body: body,
    model_id: "amazon.titan-embed-text-v1",
})
```

次にレスポンスを見たいわけですが、response.bodyの中身は`StringIO`クラスになっているのでちょっと工夫が必要です。

```ruby
string_io_object = response.body

string_io_object.rewind
string_io_object.each_line do |line|
  puts line
end
```

上記コードのように、StringIOクラスのメソッドを使用して中身を出力します。

## 4. 処理結果

そうすると以下のような結果が出ました。

```
{"embedding":[0.8203125,-0.11425781,-0.10498047,-0.036376953,-0.390625,0.1953125,-0.11621094,1.2207031E-4,0.25,-0.18554688,-0.095214844,-0.35742188,0.3046875,0.4765625,-0.58203125,-0.3515625,0.30273438,0.15917969,-0.41992188,0.1015625,-0.37109375,0.4765625,-0.515625,0.55859375,0.43359375,0.7421875,0.36914062,-1.0390625,-0.016845703,0.27929688,-0.5859375,1.1328125,0.24414062,-0.65234375,0.28125,-0.16503906,-0.203125,0.19335938,0.052001953,0.20996094,-0.39257812,0.24023438,0.04321289,0.095214844,0.31640625,0.123046875,0.49609375,-0.24804688,0.6640625,0.57421875,0.28515625,0.80859375,-0.3359375,-0.011413574,0.23632812,0.671875,-0.3828125,-0.46289062,0.5859375,-0.037109375,-0.50390625,-0.025390625,-0.33007812,0.045166016,0.029785156,0.18652344,0.19042969,-0.43945312,-0.4375,0.3984375,-0.2578125,-0.26171875,0.625,0.118652344,-0.107910156,0.34570312,-0.44921875,-0.24609375,-0.075683594,0.031982422,-0.43945312,0.092285156,-0.43359375,-0.12451172,-0.59375,-0.1796875,-0.34179688,0.30664062,......]}
```

これが、「Amazon Titan Embeddings G1 - Text」というモデルで出力される結果のようです。

## まとめ

今回はrailsでAmazon Bedrockを使用する方法をまとめました。

他のモデルでも実装してより良いアプリになると良い〜

Embeddingsモデルでも何か使い道がないか考えてみようと思います。

以上