NRIネットコム Blog

NRIネットコム社員が様々な視点で、日々の気づきやナレッジを発信するメディアです

Amazon Bedrockで英語の会話を要約してみた

こんにちは!最近人間ドックの結果を受けて禁ラーメン生活しましたが2週間で限界がきてる志水です。人類はラーメンから逃げることなんて出来ないんだなと痛感しております。つけ麺ならいいよね?

はじめに

さて、ついにAmazon BedrockがGAされましたね!
aws.amazon.com

re:Inventでくるのかなー?来年くるのかなー?ともうちょっと先のGAを思ってたので予想外でした。
じゃあBedrockで何か作らねば!と思ったので、以前作ったOtterで書き起こされた文章をChatGPTで要約するスクリプトをBedrockで作り直してみました。

ツールの背景

AWSの大きなイベントであるre:Inventは多くの情報が英語で提供されるため、英語が苦手な方にとってはハードルが高いものとなっています。
私自身も英語が得意ではないため、リアルタイムに英語の会話を文字起こししてくれるOtterを利用し、耳で聞くのではなく目で読むことで理解を深めていました。
ただ、読むだけでもなかなか大変で、難しい単語やフレーズに出くわすと理解が難しくなってしまいました。
そのような時に文字の要約や翻訳が得意なChatGPTやBedrockを利用すればまとめる部分も担ってくれるので理解促進されるのではないかなと思いました。
もともとは日本語が使えそうなClaude2モデルを使いたかったのですが、プレビューはまだ通らず、代わりにJurassic-2のモデルを利用し、翻訳ではなく要約のみを行うことにしました。

構成

今回実装したシステムの構成は以下になります。

この構成は非常にシンプルで、主な流れは以下のようになります:

  1. ユーザーが英語の音声をOtterによりテキスト化します。
  2. テキスト化された内容をダウンロードし、インプット用のS3バケットにアップロードします。
  3. このアップロードアクションにより、Lambda関数が自動的にトリガーされます。
  4. Lambda関数はBedrockのモデルに要約のプロンプトを送信し、要約結果を受け取ります。
  5. 受け取った要約結果を出力用のS3バケットにアップロードします。

この流れにより、ユーザーは英語の音声データを簡単に要約し、それを後で参照できるように保存できます。

作ってみた

CDK

最初に、AWSのリソースをCDK (Cloud Development Kit) で作成します。今回はプログラム言語としてPythonを選択しました。

input_bucket = s3.Bucket(self,
    "BedrockInput",
    versioned=True,
    removal_policy=RemovalPolicy.DESTROY,
)
output_bucket = s3.Bucket(self,
    "BedrockOutput",
    versioned=True,
    removal_policy=RemovalPolicy.DESTROY,
)

# Create a Lambda Layer for boto3
boto3_layer = _lambda.LayerVersion(self,
    "Boto3Layer",
    code=_lambda.Code.from_asset("lambda/layer/boto3_layer.zip"),  # Assume the layer zip is in the "lambda-layer" directory
    compatible_runtimes=[_lambda.Runtime.PYTHON_3_11],
    license="Apache-2.0",
    description="A layer to include the latest version of boto3",
)

# Create a Lambda function
function = _lambda.Function(self,
    "MyFunction",
    runtime=_lambda.Runtime.PYTHON_3_11,
    handler="app.handler",
    code=_lambda.Code.from_asset("lambda/function"),
    layers=[boto3_layer],
    timeout=Duration.seconds(60 * 15),
    environment={
        'OUTPUT_BUCKET': output_bucket.bucket_name,  # Set the output bucket name as an environment variable
    }
)

この実装では、入力と出力用のS3バケットおよびLambda関数を作成しています。また、Lambda Layerも利用しています。Lambda Layerの利用理由は、2023年10月3日現在、Lambdaにデフォルトで含まれているboto3のバージョンではBedrockを操作することができないためです。そのため、最新版のboto3を含むLambda Layerを作成して利用しています。

あとはLambdaのイベントソースと権限の追加を行い、CDKは完了です。

# Add S3 event trigger
function.add_event_source(S3EventSource(input_bucket, events=[s3.EventType.OBJECT_CREATED]))

# Add the necessary policy to the Lambda function's execution role
function.role.add_to_policy(iam.PolicyStatement(
    actions=["bedrock:InvokeModel"],
    resources=["*"]  # Replace with the ARN of the specific resource if known
))

# Grant the Lambda function permissions to read objects from the input bucket
input_bucket.grant_read(function)

# Grant the Lambda function permissions to put objects in the output bucket
output_bucket.grant_put(function)

Lambda

次にLambdaです。まずはBedrockへプロンプトを送信する部分を下記で作成します。モデルはJurassic-2 Ultraを利用しています。プロンプトはSummarize the below text in 5 bullets.と5個のまとめを作ってもらうようにしました。

import boto3
import json
import os

region = os.environ.get('AWS_REGION')
bedrock_runtime_client = boto3.client('bedrock-runtime', region_name=region)

def prompt_bedrock(contents):
    modelId = 'ai21.j2-ultra-v1'
    contentType = 'application/json'
    accept = 'application/json'
    prompt = f"Summarize the below text in 5 bullets.\n---\n{contents}"

    body = json.dumps({
        "prompt": prompt,
        "maxTokens": 800,
        "temperature": 0.7,
        "topP": 0.95
    })
    response = bedrock_runtime_client.invoke_model(
        modelId=modelId,
        contentType=contentType,
        accept=accept, 
        body=body
    )
    response_body = json.loads(response.get('body').read())
    return response_body['completions'][0]['data']['text']

次に、作成した関数を活用して、S3のファイルの内容をBedrockへのプロンプトとして送信します。具体的には、S3バケットからテキストファイルを取得し、その内容をBedrockのモデルに送信するプロンプトとして使用します。Bedrockはこのプロンプトを基に内容を要約し、その要約された結果を別のS3バケットにアップロードします。これにより、要約された情報が出力用のS3バケットに格納され、後で参照できるようになります。

def handler(event, context):
    # S3 event data
    s3_event = event['Records'][0]['s3']
    bucket_name = s3_event['bucket']['name']
    object_key = s3_event['object']['key']

    s3_client = boto3.client('s3')
    response = s3_client.get_object(Bucket=bucket_name, Key=object_key)
    file_content = response['Body'].read().decode('utf-8')
    
    # prompting
    response = prompt_bedrock(file_content)

    # Get the output bucket name from environment variable
    output_bucket_name = os.environ.get('OUTPUT_BUCKET')
    output_file_name = f"summary_{object_key}"
    
    # Upload the response to the output bucket
    s3_client.put_object(
        Bucket=output_bucket_name,
        Key=output_file_name,
        Body=response
    )
    
    return {
        'statusCode': 200,
        'body': json.dumps('Process completed successfully!')
    }

実行結果

作成したものを動かしてみましょう。まず、2022年re:InventのKeynote with Dr. Werner Vogelsのデータを利用します。

www.youtube.com

これをOtterで読み込ませて出力したテキストデータを入力用S3へ配置します。今回はデータを最初の30分までとしています。

オブジェクトをアップロードすると自動でLambdaが起動し、出力用のS3へオブジェクトが配置されていることが分かります。

中身は下記になります。

And so, again, this sort of the architecture is evolvable. You can add more components, you can modify the components, you can change things, you can delete them. And it also gives you suggestions of services to use. And so, for example, in one of our examples, we show you how you can use Step Functions, EventBridge, DynamoDB, S3, Route 53, and CloudWatch to build an end-to-end serverless application.
Now, this shows you, again, how sort of the architecture works. So, again, this is all about simplifying the architecture, simplifying the coding, making it easier for you to build these event-driven architectures. So again, the world is asynchronous. The digital world is asynchronous. The real world is asynchronous. And for systems to work, you have to build asynchronous systems.

和訳したものが下記です。

そしてまた、この種のアーキテクチャは進化可能だ。コンポーネントを追加したり、変更したり、削除したりできる。また、利用すべきサービスを提案してくれる。例えば、Step Functions、EventBridge、DynamoDB、S3、Route 53、CloudWatchを使ってエンド・ツー・エンドのサーバーレス・アプリケーションを構築する方法を紹介します。
この例では、アーキテクチャがどのように機能するかを示している。繰り返しますが、これはすべてアーキテクチャの簡素化、コーディングの簡素化、イベント・ドリブン・アーキテクチャの構築を容易にするためのものです。繰り返しますが、世界は非同期です。デジタルの世界は非同期だ。現実の世界も非同期だ。システムを機能させるためには、非同期システムを構築しなければならない。

5個にまとめてくれていなかったり、少し日本語が不安定だったりしますが、非同期アーキテクチャの話を出していて重要な部分も抜き出せています。出来ていない部分はプロンプトをしっかり考えればフォロー出来る部分かと思っています。また、下記記事でClaude2が非常に高品質な結果で出来ているので、Claude2のモデルを利用するとより良い結果になっていたのではないでしょうか。

qiita.com

Bedrockはこのように複数のモデルを選択できるというのも強みとしてあるので、それを駆使して求めるアーキテクチャやモデルを選別していくのが良いと触っていて感じました。どのモデルを利用すれば良いかは下記の小西先生の記事を見て考えるのが良いかと思います。

tech.nri-net.com

おわりに

やっと出たBedrockを使って会話のデータを要約してみました。他にもたくさんモデルはあり、チューニングも試せるのでいくらでも遊べそうです。
皆さんも是非Bedrockちゃんをいじくり倒しましょう!

執筆者志水友輔

2023 Japan AWS Ambassador / 2021,2023 Japan AWS Top Engineer / 2021-2023 APN ALL AWS Certifications Engineers
大阪でAWSを中心としたクラウドの導入、設計、構築を専門に行っています。CDKと自動テストが大好物

Twitter:https://twitter.com/shimi023

Amazon著者ページ:Amazon.co.jp: 志水友輔:作品一覧、著者略歴