Amazon Bedrock で Interpreter を開発 !

~ 日本語だけで AWS Lambda のコード生成とデプロイとテストまで完結 ~

2023-11-01
デベロッパーのためのクラウド活用方法

Author : 呉 和仁、和田 雄介

Hello Amazon Bedrock!

Builder の皆様こんにちは ! 機械学習ソリューションアーキテクトの呉です。

2023 年 9 月 28 日 (日本時間) に大手 AI 企業が提供する高性能な基盤モデル (FM) を単一の API で選択できるフルマネージド型サービス「Amazon Bedrock」が一般提供開始となり、AWS のアカウントを持っていれば、API を叩くだけで誰でも 生成 AI を試せるようになりました。(ちなみに前回の 記事 を読んでいただければ、生成 AI とは何 ? や、API ではなくそもそも生成 AI をどうやって動かせるのか、どうやって Fine Tune するのか、みたいなことを学べるので、ぜひご一読いただけると幸いです)。

自前でモデルを用意せずとも、API を叩くだけで生成 AI を使えるということは、機械学習の経験がないアプリケーション開発者でも簡単に生成 AI を組み込んだアプリケーションを開発できるということです。この記事では、Amazon Bedrock の基本的な使い方と、アプリケーションに組み込んだソリューションの紹介、そしてソリューションのカスタマイズの紹介をします。

アプリケーションのカスタマイズですが、私はアプリケーション開発を得意としていない (というか苦手) ので、プロトタイピングエンジニアの和田さんを召喚して、私が作りたい機能を全部作っても・・・ゲフンゲフン・・・お手伝いしていただきました。

出来上がったものがこちらです。どうやって作ったのかをこの記事では紹介します。

Amazon Bedrock の Bedrock には岩盤という意味があるそうです。この記事は固い岩盤をゆるゆる試す記事ですので、通勤時間などの合間にゆるく読んでいただけると幸いです。

このクラウドレシピ (ハンズオン記事) を無料でお試しいただけます »

毎月提供されるクラウドレシピのアップデート情報とともに、クレジットコードを受け取ることができます。 


1. プロローグ

機械学習ソリューションアーキテクト呉は憤っていた。なぜやりたいことが明確なのに実装 (コーディング) にこんな時間がかかるのか、と。そして呉は待ち望んでいた。Amazon Bedrock が一般提供開始となり、やりたいことからコード生成ができるようになるのを。

しかし、単にコード生成だけでは能がないとも思っていた。それでは巷の Code を Interpreter (大規模言語モデルにコードを生成させてそのままコードを実行) する機能となんら変わらない。せっかく AWS のソリューションアーキテクトなのだから、やりたいことの記述から AWS のサービスのデプロイまでを自動化したい。しかし呉にはそんなアプリを作る能力がないと嘆いていた。

そこで呉はプロトタイピングエンジニアの和田さんを召喚した。難しい開発だろう。『それでも和田なら・・・。』 『和田ならきっと何とかしてくれる・・・!!』 そういう目を(呉が)している・・・!!


和田
「なんですかこのイントロは。バスケマンガに影響されすぎですよ。」

「たまにはいいじゃないですか。映画見て久しぶりに通しでマンガを読んだ上、Amazon Bedrock が一般提供開始されてテンション高いんですから。」

和田「そして、また雑にやりたいことを書いてますけど、せめて仕事を依頼するならちゃんと要件書いてくださいよ。」

「いや、ほら、こう、チャット欄に『AWS にこんなことして欲しい !!』って書いたらいい感じにサービスがデプロイされたりリソースが作成できると。」

和田(『やりたいことが明確なのに』をネタにした高度なギャグなのかな ?) ちょっと考えて提案を持ってくるので正座して待っててください。」

「オナシャッス」

――12,000 ミリ秒後の地球――

和田「これで行きましょう。反論は許しません。」

  • 自然言語で入力した処理したい内容をコードに変換し AWS で実現する。ただし、今回の対象とする AWS サービスは AWS Lambda のみとする。
  • ユーザーは使用する Lambda の Runtime を選択し、 Lambda 関数で実現したいことを日本語で入力して submit すると、アプリは Lambda のコードを出力する。(例: 「整数を投げたらその数値番目までのフィボナッチ数列を返却するコードを生成する。」という入力からフィボナッチ数列生成の Lambda 関数のコードが生成され、リクエストに 7 を入れたらレスポンスは [1,1,2,3,5,8,13] を返すイメージ)
  • 出力されたコードをユーザーが確認して問題なければ、ユーザーは Lambda の関数名とロールを入力し submit すると、アプリは Lambda 関数を生成して生成結果と Lambda 関数のテストコードを生成して画面に返却する。生成された Lambda 関数のコードがイマイチだったら追加で説明文を日本語で入力して再度コードを生成できるようにする。
  • Lambda 関数が生成されたらアプリは Lambda 関数のテストデータを出力し、ユーザーは実行ボタンを押すと、アプリはテストの結果を返す

「オナシャッス。」

和田「日本語の入力から Lambda 関数作成まで自動でやって欲しそうにこっちを見てましたが、それは事故が怖すぎるので却下して、ユーザーがコードを確認して実行という形にしました。」

「(チッ・・・) デプロイはユーザーに任せる感じですね。この感じで IAM のユーザーやロール、ポリシーも作ってくれると嬉しいですねぇ。」

和田「ご自身でお願いします。」

「ど、読者からの投稿を待つこととします・・・。」

和田は不安を抱えつつ呉に丸投げされた開発を始めるのであった・・・。


とはいえ、アプリ開発の前に Amazon Bedrock の使い方を皆様と一緒に学びましょう。AWS マネジメントコンソールを使う方法、API を使う方法をそれぞれ紹介します。最初はマネジメントコンソールです。


2. AWS マネジメントコンソールから Amazon Bedrock を使ってみよう

まずは AWS マネジメントコンソール にアクセスしてログインし、検索窓に Amazon Bedrock と打ち込みましょう。Amazon Bedrock のリンクが表示されます。

クリックすると拡大します

使用するリージョンは 北部バージニア(us-east-1) とします。2023 年 10 月現在、使用できるモデルの種類が一番多いためです。

クリックすると拡大します

Amazon Bedrock では Text to Text, Text to Image, Text to Vector (Text Embedding) などのタスクを解くモデルが用意されています。まずはモデル一覧を確認しましょう。

左側のペインから「Base Models」リンクをクリックします。

クリックすると拡大します

すると現在 Bedrock で利用可能なモデル一覧が表示されます。

2023 年 10 月時点で Text to Text のタスクでは、Anthropic 社の Claude というモデルが日本語を使用できます。この記事では Claude v2 というモデルを使うこととします。Claude v2 の特徴として、日本語での精度が非常に高いことと、入力できるトークン(≒単語数)数が最大 100K (10 万) と非常に大きいことにあります。例えば論文を丸々入れた上で要約や翻訳タスク、次の論文の案の提案をさせたり、長い会話履歴を考慮した応答をすることができますね。

早速 Claude v2 のラジオボタンにチェックを入れて、「Open in playground」というボタンをクリックしましょう。

クリックすると拡大します

ちなみに Claude は初期状態だとアクセスできない可能性があります。その場合は「Model Access」というリンクからアクセス申請をしてください。

クリックすると拡大します

Open in Playground」をクリックするとチャットの画面に遷移します。

クリックすると拡大します

最下部のテキストボックスに Claude に話しかけたいことを入力し、右の「Run」ボタンをクリックすると、Claude が返答してくれます。

今回フロントエンドの開発に挑戦しますが私 (呉のみ) はド素人なので、学習方法を聞いてみることとしました。

クリックすると拡大します

良さそうなアドバイスをいただけましたね (出力結果は毎回変わります)。しかし、私の機械学習ソリューションアーキテクトであるというコンテキストが考慮されていません。具体的にはバージョン管理システムの Git については学習済 (できるとは言っていない) です。また、出力が途中で止まっているように見えます。

これは出力設定のせいです。Update inference configurations をクリックして設定の確認と更新を行います。

クリックすると拡大します

設定がいくつかありますが、ここでは重要な Temperature, Top P, Top K, Maximum length について簡単に紹介 します。

出力内容を制御するパラメータ

  • Temperature : 応答のランダム性を減らすには、低い値を使用する
  • Top P : 確率の低い選択肢を無視するには、低い値を使用します
  • Top K : モデルが次のトークンを生成するために使用するトークンの選択肢の数を指定します

といってもイメージしづらいのでざっくりいうと、AI に面白い回答をさせたければ Temperature とTop P, Top K の値を上げ、堅実な回答をさせたければ下げます

出力量を制御するパラメータ

  • Maximum length : 回答のトークン上限数を設定します。

今回はこの数値が 300 で上限に引っかかったため出力が途中で止まってしまったわけです。Maximum length を 1,000 にして、 Update した上で、コンテキストを追加して質問をしてみます。

クリックすると拡大します

より具体的な回答を引き出すことができました。このように AWS のアカウントさえあれば、マネジメントコンソールからすぐに Text to Text の生成 AI を試すことができます。

2-1. Amazon Bedrock の API を叩いてみよう

さて、巷によくあるチャット機能はすぐに試せることがわかりました。しかし、AWS はあくまで Builder のためのサービスであり、自前のアプリを開発して利用者に使ってもらいたいところです。当然 Bedrock には API が用意されており、自前のアプリケーションに Bedrock を組み込むことも簡単です。ここでは API で Bedrock を利用する方法を紹介します。機械学習エンジニアは Python を利用することが多いですが、バックエンドの開発では TypeScript を使うことが多いかと思います。それぞれの言語でどうやって使うのかを紹介します。

2-2. 環境準備

Python や TypeScript が実行できてかつ、 AWS を利用できる環境であればどこでも構いません。今回は Python については みんな大好き Amazon SageMaker Notebooks を、TypeScript は手元の mac で動かすことを前提とします。

ポリシーをアタッチ

実行するロールや IAM ユーザーに以下のインラインポリシーをアタッチします。Bedrock のすべてのアクションをできるようになります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "BedrockTest",
            "Effect": "Allow",
            "Action": "bedrock:*",
            "Resource": "*"
        }
    ]
}

Bedrock 対応済の SDK をインストール

Amazon SageMaker Notebooks で Amazon Linux 2 を使用している場合はすでに Bedrock が使える環境 (Boto3) が整っているため、特に何もする必要がありません (北部バージニアリージョンで確認)。

TypeScript の場合は、Bedrock に対応した aws-sdk が入っている必要があります。mac であれば以下手順で実行環境を準備しましょう。ここでは、@aws-sdk/client-bedrock-runtime@3.422.1 を利用することとします。

新規のフォルダを作成していただき、以下の package.json を格納した上で、npm install コマンドでインストールし、ts-node -T {任意のファイル名でコードを保存}.ts で以下の TypeScript のコードを実行できます。

{
  "name": "bedrock-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@aws-sdk/client-bedrock": "^3.422.1",
    "@aws-sdk/client-bedrock-runtime": "^3.422.1"
  },
  "devDependencies": {
    "ts-node": "^10.9.1"
  }
}

2-3. Bedrock の主要な API を叩いてみる

Bedrock には様々な API がありますが、ここでは、使用できるモデル一覧を表示する list_foundation_models API と、モデルにリクエストを投げて生成結果を受け取る invoke_model API, invoke_model_with_response_stream API を紹介します。

invoke_modelinvoke_model_with_response_stream の違いは前者はレスポンスを一括で受け取る API、後者は生成結果を逐次受け取るストリーミング用の API です。生成結果を一括で受け取りたい (実装が楽) か、徐々に受け取る (Web 画面の場合一般的にユーザー体験がいい) かで選んでください。

list_foundation_models API

早速モデル一覧を表示してみましょう。以下のコードをそれぞれの言語で実行してみてください。

Python

import boto3
bedrock = boto3.client('bedrock')
bedrock.list_foundation_models()

出力結果

{(中略)
 'modelSummaries': [{'customizationsSupported': ['FINE_TUNING'],
   'inferenceTypesSupported': ['ON_DEMAND'],
   'inputModalities': ['TEXT'],
   'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-tg1-large',
   'modelId': 'amazon.titan-tg1-large',
   'modelName': 'Titan Text Large',
   'outputModalities': ['TEXT'],
   'providerName': 'Amazon',
   'responseStreamingSupported': True},
   (中略)
  {'customizationsSupported': [],
   'inferenceTypesSupported': ['ON_DEMAND'],
   'inputModalities': ['TEXT'],
   'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-g1-text-02',
   'modelId': 'amazon.titan-embed-g1-text-02',
   'modelName': 'Titan Text Embeddings v2',
   'outputModalities': ['EMBEDDING'],
   'providerName': 'Amazon'},
   (中略)
   'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/stability.stable-diffusion-xl',
   'modelId': 'stability.stable-diffusion-xl',
   'modelName': 'Stable Diffusion XL',
   'outputModalities': ['IMAGE'],
   'providerName': 'Stability AI'},
   (中略)
  {'customizationsSupported': [],
   'inferenceTypesSupported': ['ON_DEMAND'],
   'inputModalities': ['TEXT'],
   'modelArn': 'arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2',
   'modelId': 'anthropic.claude-v2',
   'modelName': 'Claude',
   'outputModalities': ['TEXT'],
   'providerName': 'Anthropic',
   'responseStreamingSupported': True},(後略)]}

TypeScript

import { Bedrock } from "@aws-sdk/client-bedrock";
const bedrock = new Bedrock({});
bedrock.listFoundationModels({}, function (err, data) {
  if (err) console.log(err, err.stack);
  else console.log(data);
});

出力結果

{(中略)
  modelSummaries: [
    {
      customizationsSupported: [Array],
      inferenceTypesSupported: [Array],
      inputModalities: [Array],
      modelArn: 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-tg1-large',
      modelId: 'amazon.titan-tg1-large',
      modelName: 'Titan Text Large',
      outputModalities: [Array],
      providerName: 'Amazon',
      responseStreamingSupported: true
    },
    (中略)
    {
      customizationsSupported: [],
      inferenceTypesSupported: [Array],
      inputModalities: [Array],
      modelArn: 'arn:aws:bedrock:us-east-1::foundation-model/amazon.titan-embed-g1-text-02',
      modelId: 'amazon.titan-embed-g1-text-02',
      modelName: 'Titan Text Embeddings v2',
      outputModalities: [Array],
      providerName: 'Amazon',
      responseStreamingSupported: null
    },
    (中略)
    {
      customizationsSupported: [],
      inferenceTypesSupported: [Array],
      inputModalities: [Array],
      modelArn: 'arn:aws:bedrock:us-east-1::foundation-model/stability.stable-diffusion-xl',
      modelId: 'stability.stable-diffusion-xl',
      modelName: 'Stable Diffusion XL',
      outputModalities: [Array],
      providerName: 'Stability AI',
      responseStreamingSupported: null
    },
    (中略)
    {
      customizationsSupported: [],
      inferenceTypesSupported: [Array],
      inputModalities: [Array],
      modelArn: 'arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-v2',
      modelId: 'anthropic.claude-v2',
      modelName: 'Claude',
      outputModalities: [Array],
      providerName: 'Anthropic',
      responseStreamingSupported: true
    },(中略)
}

このように出力フォーマット (インデントや改行のタイミングなど) の違いはあれど、両言語とも list_foundation_models API を叩くと Bedrock で利用できるモデル一覧が表示されます。invoke_modelinvoke_model_with_response_stream API を使用する際は、modelId を引数に入れる必要があるので、確認しておきましょう。Claude v2 の場合は、anthropic.claude-v2 という文字列を引数に使用します。

また、inputModalitiesoutputModalities キーにはそのモデルに何を入れたら何が返ってくるのか、が記載されています。2023 年 10 月現在だと、Text to Text, Text to Vector (Embedding), Text to Image がサポートされています。

customizationsSupported というキーには、どんなモデルのカスタマイズができるかが記載されています。例えば amazon.titan-tg1-large というモデルだと Fine Tuning をサポートしていることがわかります。

最後に、responseStreamingSupported というキーに streaming が使えるかどうかが記載されているのでチェックしておきましょう。Claude v2 は使用することができますので、後ほど invoke_model_with_response_stream を試してみましょう。

invoke_model API

いよいよ Claude v2 を使用します。以下のコードを実行してみましょう。

Python

import boto3
import json
bedrock_runtime = boto3.client('bedrock-runtime')
prompt = '''

Human:私は機械学習ソリューションアーキテクトに従事しておりますが、フロントエンドの開発ができるようになりたいです。ラーニングパスを教えてください。
Assistant:'''
request = {
  "modelId": "anthropic.claude-v2",
  "contentType": "application/json",
  "accept": "*/*",
  "body": json.dumps({
      'prompt':prompt,
      'max_tokens_to_sample':1000,
      'temperature':1,
      'top_k':250,
      'top_p':0.999,
      'stop_sequences':['\n\nHuman:'],
      'anthropic_version':'bedrock-2023-05-31'
  })
}

response = bedrock_runtime.invoke_model(
    **request
)
print(json.loads(response['body'].read().decode('utf-8'))['completion'])

出力結果 (実行ごとに変わりますのであくまで参考です)

 フロントエンド開発を学ぶには、以下のようなステップがおすすめです。

1. HTML、CSS、JavaScriptの基礎を学ぶ
- HTMLでWebページの構造を、CSSでスタイルを、JavaScriptで動的な機能を実装します。コードアカデミーやUdemy等のオンライン学習サイトを活用すると良いでしょう。

2. JavaScriptのフレームワークを学ぶ
- React、Vue.js、Angular等のフレームワークを1つ選択して学習しましょう。複雑なアプリケーション開発ができるようになります。

3. Webアプリケーションの開発
- 簡単なTodoアプリやSNSアプリ等を開発することで、実践的なスキルが身につきます。GitHub上にあるサンプルプロジェクトを参考にするとよいでしょう。

4. UI/UX設計の技術を学ぶ
- ユーザーインターフェースとユーザーエクスペリエンスの設計手法を学び、使いやすいアプリを目指します。

5. 最新のフロントエンド技術を追随
- Web技術は日進月歩で変化していくため、ブログやSNSを通じて技術動向を把握することも大切です。

機械学習の知識とフロントエンドを組み合わせることで、インパクトある製品開発ができるでしょう。一歩ずつ着実に学習を進めていきましょう。

TypeScript

import {BedrockRuntime} from "@aws-sdk/client-bedrock-runtime"
const bedrockRuntime = new BedrockRuntime();

const prompt= `

Human:私は機械学習ソリューションアーキテクトに従事しておりますが、フロントエンドの開発ができるようになりたいです。ラーニングパスを教えてください。
Assistant:`

const request = {
  "modelId": "anthropic.claude-v2",
  "contentType": "application/json",
  "accept": "*/*",
  "body": JSON.stringify({
      'prompt':prompt,
      'max_tokens_to_sample':1000,
      'temperature':1,
      'top_k':250,
      'top_p':0.999,
      'stop_sequences':['\n\nHuman:'],
      'anthropic_version':'bedrock-2023-05-31'
  })
}

const main = async () => {
  const response = await bedrockRuntime.invokeModel(request)
  const body = JSON.parse(Buffer.from(response.body).toString('utf-8'));
  console.log(body.completion)
}
main()

出力結果 (毎回出力内容は異なります)

フロントエンド開発を学ぶには、以下のような学習パスがおすすめです。

まずHTML、CSS、JavaScriptの基礎をしっかり学ぶことから始めましょう。これらはフロントエンドの基本です。特にJavaScriptは重要で、深く学ぶ必要があります。

次に、JavaScriptのフレームワークを学びましょう。今最も主流なのはReactです。Reactの概念と使い方をできるだけ実践を通して身につけることが大切です。

その上で、CSSフレームワークも併せて学ぶとよいでしょう。styled-componentsやTailwind CSSなどが人気です。レスポンシブデザインもकवसになる分野です。 

ビルドツールとしてWebpackの使い方を習得し、ES6+の記法に慣れることも大切です。

実践的には、ToDoアプリやSNSなどのシンプルなアプリから作り始め、徐々にコンポーネントを複雑化させながら進めるのがおすすめです。

このように、基礎から実践まで段階的に学んでいけば、フロントエンドエンジニアとして通用するスキルが身につくはずです。web開発に興味がある方は、是非フロントエンドを目指してみてください。

Claude v2 からありがたいアドバイスをもらえましたね。
invoke_model の引数にいろいろ書きましたが、どこからこれが出てきた !? と思う方が多いと思います。AWS の Doc (開発者ガイド①, 開発者ガイド②, Python, Node.js) を読んでいただくのが一番ですが、手っ取り早くリクエストを作りたい場合は、マネジメントコンソールを使うと簡単です。

以下のように左側のペインから「Text」をクリックし、真ん中のテキストボックスに Claude に訪ねたい内容 (prompt) を入力し、最後に右下にある「View API request」をクリックしてみてください。

クリックすると拡大します

すると、以下のように、マネジメントコンソールで行った際のリクエストの内容を表示することができます。

クリックすると拡大します

こちらを参考に invoke_model の引数を設定するのが簡単でしょう。このように invoke_model API を使用することで、すぐに Bedrock で使用できるモデルを使うことができます。

invoke_model_with_response_stream API

さて、今度は streaming API の invoke_model_with_response_stream API を使ってみます。invoke_model と同じリクエストパラメータを使用し、使用する API だけを変更します。

Python

import boto3
import json
bedrock_runtime = boto3.client('bedrock-runtime')
prompt = '''

Human:私は機械学習ソリューションアーキテクトに従事しておりますが、フロントエンドの開発ができるようになりたいです。ラーニングパスを教えてください。
Assistant:'''
request = {
  "modelId": "anthropic.claude-v2",
  "contentType": "application/json",
  "accept": "*/*",
  "body": json.dumps({
      'prompt':prompt,
      'max_tokens_to_sample':1000,
      'temperature':1,
      'top_k':250,
      'top_p':0.999,
      'stop_sequences':['\n\nHuman:'],
      'anthropic_version':'bedrock-2023-05-31'
  })
}

response = bedrock_runtime.invoke_model_with_response_stream(
    **request
)

stream = response.get('body')
if stream:
    for event in stream:
        chunk = event.get('chunk')
        if chunk:
            print(json.loads(chunk.get('bytes').decode()))

出力結果(毎回出力内容は異なります)

{'completion': ' ', 'stop_reason': None}
{'completion': 'フロントエンド開発を学', 'stop_reason': None}
{'completion': 'ぶには、以下のような学習パスがお', 'stop_reason': None}
{'completion': 'すすめです。\n\nまずHTML、', 'stop_reason': None}
{'completion': 'CSS、JavaScriptの基本をしっ', 'stop_reason': None}
{'completion': 'かり学ぶことから始めまし', 'stop_reason': None}
(中略)
{'completion': '続的に学習を進', 'stop_reason': None}
{'completion': 'めていけば、フロントエンド開', 'stop_reason': None}
{'completion': '発者として力をつ', 'stop_reason': None}
{'completion': 'けることができると思', 'stop_reason': None}
{'completion': 'います。', 'stop_reason': 'stop_sequence'}

TypeScript

import { BedrockRuntime } from "@aws-sdk/client-bedrock-runtime"
const bedrockRuntime = new BedrockRuntime();
const prompt = `

Human:私は機械学習ソリューションアーキテクトに従事しておりますが、フロントエンドの開発ができるようになりたいです。ラーニングパスを教えてください。
Assistant:`
const request = {
  "modelId": "anthropic.claude-v2",
  "contentType": "application/json",
  "accept": "*/*",
  "body": JSON.stringify({
    'prompt': prompt,
    'max_tokens_to_sample': 1000,
    'temperature': 1,
    'top_k': 250,
    'top_p': 0.999,
    'stop_sequences': ['\n\nHuman:'],
    'anthropic_version': 'bedrock-2023-05-31'
  })
}

const main = async () => {
  const response = await bedrockRuntime.invokeModelWithResponseStream(request)
  if (!response.body) {
    return;
  }
  for await (const streamChunk of response.body) {
    if (!streamChunk.chunk?.bytes) {
      break;
    }
    const body = JSON.parse(
      new TextDecoder('utf-8').decode(streamChunk.chunk?.bytes)
    )
    if (body.completion) {
      console.log(body);
    }
  }
}
main()

出力結果 (毎回出力内容は異なります)

{ completion: ' ', stop_reason: null }
{ completion: 'フロ', stop_reason: null }
{ completion: 'ントエンド開', stop_reason: null }
{ completion: '発を学ぶ', stop_reason: null }
{ completion: 'には以下のような学習', stop_reason: null }
(中略)
{ completion: 'キ', stop_reason: null }
{ completion: 'ャ', stop_reason: null }
{ completion: 'リアの幅', stop_reason: null }
{ completion: 'を広げることができるでし', stop_reason: null }
{ completion: 'ょう。頑張ってください!', stop_reason: 'stop_sequence' }

responsebody に streaming データが逐次格納されているので、中身を出力することで completion というキーを含む json 文字列が徐々に出力されてきます。リアルタイムに出力結果を表示したい場合はinvoke_model_with_response_stream API を使うと良いでしょう。


3. Bedrock を用いたソリューション をデプロイして遊んでみよう

さて、実は Bedrock を用いたソリューションを実は和田と愉快な仲間たちが作成してました(呉は仲間たち第 99 号です)。この記事はこのソリューションの宣伝記事でもあったわけです。
https://github.com/aws-samples/generative-ai-use-cases-jp

詳しいことは、README.md を読んで欲しいのですが、このソリューションでは生成 AI でこんなことをしてみては ? というユースケースごとに画面を作成して使えるようにしています。ポピュラーなチャットや要約、メッセージの自動生成、情報抽出などがあります。まずはソリューションをデプロイしてそれらの使い方を紹介しつつ、このソリューションに機能を追加する形で AWS の Interpreter を開発していくこととします。

3-1. デプロイする

最初にデプロイです。デプロイ方法については、README.md#デプロイ に記述がありますが、せっかくなので動画で紹介します。10 分程度でデプロイできます (人間の作業時間は 3 分くらいです)。

3-2. 使ってみる

使用方法についても、README.md#ユースケース一覧 にありますが、せっかくなので動画で紹介します。

動画作成後にもどんどん機能が追加されており(私の更新の怠慢とも言う)、今では、テキスト編集者向けの校正機能が増えていたり、

Kendra を組み合わせた RAG だったり、

生成 AI の業務における具体的な使い方を現在進行系でガンガン開発が進んでいる熱いソリューションです。ぜひ社内で紹介していただいて、生成 AI を業務に導入しませんか !? と偉い人に提案してみましょう。きっとウケがいいはずです (知らんけど)。また、偉い人はご自身でデプロイして、部下に使わせると「おおっ !? DX !?」と驚かれることウケ合いです (知らんけど)。


4. Bedrock を用いたソリューション をカスタマイズしてみよう

さて、ここから本題の AWS の Interpreter を開発していくこととします。完成版はこちらからダウンロードしてください。
generative-ai-use-cases-jp-aws-interpreter.zip

この zip は main ブランチの 602cee5 というコミットで git init し直して再度 commit (ID: 1b367d7)し、interpreter の開発完了時のアーティファクトでコミット(ID: 6bdcb00)した .git を含むファイルです。git diff 1b367d7 6bdcb00 などで差分を簡単に確認できるようにしています。

※注意) ブログ執筆時はもちろん上記コードが動きましたがメンテナンスされませんのでご注意ください。この成果物は後述の通り main ブランチを追従しないのと、main ブランチにもマージされません。お試しや開発のヒントとしてお使いください。

まず出来上がったものをデプロイし、使ってみて、そのあとにどうやって開発したのか、という流れで紹介します。
デプロイは zip ファイルを解凍したあと、元々のソリューション同様、npm ci したあと、 npm run cdk:deploy でデプロイできます。

4-1. 使ってみる

さて、実際の使用の様子を動画にしたのでしばしご鑑賞ください (冒頭の動画と同一です)。

4-2. 開発のお話

使用感がわかったところで、この機能をどうやって開発したのかについて紹介します。

プロンプトの検討

AWS Lambda のお作法を復習しましょう。AWS Lambda で関数を作るには以下が必要です。

  • 関数のコード (を zip アーカイブしたもの)
  • 関数の名前
  • 関数にアタッチするロール

これらを Bedrock で、名前を考えてもらい、ロールを作成するコードを書いてもらって作成し、Lambda 関数を作成する !・・・こともできなくはないですが、今回 Bedrock には関数のコードとテストデータを生成してもらうことに注力させることとし、関数の名前やロールは予めユーザーが用意し、コードを zip 化したり create_function API を実行する部分は予めアプリケーション側で準備しておくこととします。

Lambda 関数を生成するにあたって生成プロンプトを検討してみましょう。試しに Hello World を返してもらうコードを生成してみます。今回は TypeScript で開発することとします。

import { BedrockRuntime } from "@aws-sdk/client-bedrock-runtime"

const bedrockRuntime = new BedrockRuntime({ 'region': 'us-east-1' });

const runtime = 'Node.js 18.x'

const context = `あなたはAWS Lambda関数を生成するAIアシスタントです。
以下の手順でLambda関数のコードを生成してください。手順以外のことは絶対にしないでください。

# Lambda関数の生成手順
* ユーザがチャットで関数作成のコンテキストを与えるので、それに従ってください。ただし、「# Lambda関数の生成手順」は厳守してください。例外はありません。
* ユーザがチャットで指示した「# 関数のコンテキスト」を理解してください。この内容を元にコードを生成します。ここに書かれていない処理は、絶対にコードとして生成してはいけません。例外はありません。
* Lambdaのランタイムは「${runtime}」を利用するので、ランタイムに合ったコードを生成してください。
* Lambda関数のハンドラーの名前は、「handler」としてください。
* コードに対して解説を行いたい場合は、コード内のコメントブロックとして出力してください。
* 生成したコードだけを出力してください。その他の文言は一切出力しないでください。例外はありません。`

const requirement = `リクエストが来たら Hello World と返してください。`

const prompt = `

Human: ${context}
#  関数のコンテキスト
${requirement}
Assistant:`

const request = {
  "modelId": "anthropic.claude-v2",
  "contentType": "application/json",
  "accept": "*/*",
  "body": JSON.stringify({
    'prompt': prompt,
    'max_tokens_to_sample': 1024,
    'temperature': 0,
    'top_k': 250,
    'top_p': 0.999,
    'stop_sequences': ['\n\nHuman:'],
    'anthropic_version': 'bedrock-2023-05-31'
  })
}
const main = async () => {
  const response = await bedrockRuntime.invokeModel(request)
  const body = JSON.parse(Buffer.from(response.body).toString('utf-8'));
  console.log(body.completion)
}
main()

出力結果

```js
exports.handler = async (event) => {
  // リクエストが来たらHello Worldを返す
  return 'Hello World';
};
```

無事リクエストが来たら Hello World を返すコードが出力されました。あとは runtimerequirement 変数に格納する文字列を画面から受け取れば良いですね。また、出力が バッククオート 3 つで囲まれています。こちらは画面表示上は便利ですが、Lambda 関数を作る際は不要なので、create_function API をコールする際は除去する必要があることもわかりました。

続いてテストデータ生成のプロンプトを作成しましょう。Claude v2 にコードを生成してもらっているので、会話の続きでテストデータを作成すれば良さそうです。先程の会話の続きのプロンプトを作ってみましょう。

import { BedrockRuntime } from "@aws-sdk/client-bedrock-runtime"
const bedrockRuntime = new BedrockRuntime({ 'region': 'us-east-1' });
const runtime = 'Node.js 18.x'

const previousPrompt = `
Human: あなたはAWS Lambda関数を生成するAIアシスタントです。
以下の手順でLambda関数のコードを生成してください。手順以外のことは絶対にしないでください。
# Lambda関数の生成手順
* ユーザがチャットで関数作成のコンテキストを与えるので、それに従ってください。ただし、「# Lambda関数の生成手順」は厳守してください。例外はありません。
* ユーザがチャットで指示した「# 関数のコンテキスト」を理解してください。この内容を元にコードを生成します。ここに書かれていない処理は、絶対にコードとして生成してはいけません。例外はありません。
* Lambdaのランタイムは「Node.js 18.x」を利用するので、ランタイムに合ったコードを生成してください。
* Lambda関数のハンドラーの名前は、「handler」としてください。
* コードに対して解説を行いたい場合は、コード内のコメントブロックとして出力してください。
* 生成したコードだけを出力してください。その他の文言は一切出力しないでください。例外はありません。
#  関数のコンテキスト
リクエストが来たら Hello World と返してください。
Assistant:`
const previousResponse = ` \`\`\`js
exports.handler = async (event) => {
  // リクエストが来たらHello Worldを返す
  return 'Hello World';
};
\`\`\``
const prompt = `
${previousPrompt}${previousResponse}
Human: 以下の手順でテストデータを生成してください。手順以外のことは絶対にしないでください。
# テストデータの生成手順
* 先ほどあなたが生成したLambdaのコードに対するテストデータを生成します。これまでのやり取りをすべて理解してください。
* Lambda関数の品質を担保することのできるテストデータを考えてください。
* 「# テストデータのフォーマット」のJSON形式でテストデータを出力してください。データは必ず配列で設定してください。その他の文言は一切出力しないでください。例外はありません。
* JSON以外の文字列は一切出力しないでください。見出しや説明文は一切出力してはいけません。例外はありません。
# テストデータのフォーマット
{
  describe: "テストデータの解説",
  input: "Lambda関数のINPUT",
  output: "Lambda関数のOUTPUT"
}[]
Assistant:
`
const request = {
  "modelId": "anthropic.claude-v2",
  "contentType": "application/json",
  "accept": "*/*",
  "body": JSON.stringify({
    'prompt': prompt,
    'max_tokens_to_sample': 1024,
    'temperature': 0,
    'top_k': 250,
    'top_p': 0.999,
    'stop_sequences': ['\n\nHuman:'],
    'anthropic_version': 'bedrock-2023-05-31'
  })
}
const main = async () => {
  const response = await bedrockRuntime.invokeModel(request)
  const body = JSON.parse(Buffer.from(response.body).toString('utf-8'));
  console.log(body.completion)
}
main()

出力結果

```json
[
  {
    "describe": "正常系のテストデータ", 
    "input": {},
    "output": "Hello World"
  },
  {
    "describe": "異常系のテストデータ",
    "input": {
      "error": true 
    },
    "output": "Hello World"  
  }
]
```

出力結果の異常系について、任意の文字列としたので boolean を入れてエラーを起こそう、という意図が伝わりますね(テキストからの入力だと機能しないのが悲しいところですが)。
ある程度機能しそうなプロンプトが揃ったので実際の開発へと進みます。

開発環境を整える

フロントエンドの開発をするにあたって最初に開発環境を整えましょう。ソリューションのデプロイは済なので、今回は開発体験も考慮して、ローカル開発用サーバーを建てて、ローカルでフロントを確認できるようにします。Unix 系コマンドが使えるユーザーであれば (Linux や MacOS 等)、以下のコマンドを実行するだけで、ローカルの環境が立ち上がります。 

npm run web:devw

実行後以下のような出力が出た後、ブラウザで http://localhost:5173 にアクセスすれば CloudFront でホストした画面と同じ画面が表示されます。

 VITE v4.4.9 ready in 848 ms
  ➜ Local:  http://localhost:5173/
  ➜ Network: use —host to expose
  ➜ press h to show help

ログインすれば同様に使えますので、この画面を利用して開発しましょう。

Windows 等をご利用の方のローカル環境の立ち上げ方はこちらをご参照ください。

開発の様子

ここからは実際に開発する様子を和田と呉の対談形式でお届けします。

「開発のやり方を教えてください !」

和田「ざっくりすぎて何を回答すればいいのやら ?」

「まずこういった開発ってどこから手をつけるのがいいんですか ?」

和田「人によると思いますが、私はフロントエンドの開発から入ることが多いですね。画面イメージや操作感などをまず固めて、そこから逆算的に必要な API を割り出してバックエンドを作ることが多いです。」

「フロントエンドもバックエンドもできるフルスタックエンジニアだ・・・! ちなみに私は完全にスタック (動かなくなるの意) したエンジニアという意味でフルスタックエンジニアです。」

和田「つまらない冗談はやめてとっとと開発しましょう。開発の解説をするにあたって、今回追加実装した部分にフォーカスしたいので、修正ファイルの Diff を取って解説していきましょう。」

「今回修正したファイル達はこれらですね。主要な変更箇所をさらっていきましょう。」

package-lock.json
packages/cdk/lambda/createLambdaFunction.ts
packages/cdk/lambda/getLambdaArn.ts
packages/cdk/lambda/invokeLambdaFunction.ts
packages/cdk/lambda/text.ts
packages/cdk/lambda/updateLambdaFunction.ts
packages/cdk/lib/construct/interpreter.ts
packages/cdk/lib/construct/web.ts
packages/cdk/lib/generative-ai-use-cases-stack.ts
packages/cdk/package.json
packages/types/src/protocol.d.ts
packages/web/package.json
packages/web/src/@types/interpreter.d.ts
packages/web/src/App.tsx
packages/web/src/components/ExpandedField.tsx
packages/web/src/components/InputText.tsx
packages/web/src/components/Select.tsx
packages/web/src/components/Textarea.tsx
packages/web/src/hooks/useHttp.ts
packages/web/src/hooks/useInterpreter.ts
packages/web/src/main.tsx
packages/web/src/pages/InterpreterPage.tsx
packages/web/src/prompts/interpreter-prompt.ts
packages/web/src/vite-env.d.ts
setup-env.sh

メニューを追加する

呉「まず画面にメニューを生やしたいです」

クリックすると拡大します

和田「するとまずここを追記する必要があります。」

packages/web/src/App.tsx

     icon: <PiChatCircleText />,
   },
+  {
+    label: 'AWS Interpreter',
+    to: '/interpreter',
+    icon: <PiTerminal />,
+  },
   {
     label: '要約',

「ここを書くと画面に PiTerminal という React のアイコンが追加され、interpreter というリンクが設定されるわけですね。」

画面を作る

「次に interpreter の画面を作るにはどうすればいいでしょうか。」

クリックすると拡大します

和田packages/web/src/main.tsx で新しい画面を作る設定を追記し、実際の画面を packages/web/src/pages/InterpreterPage.tsx で作成します。」

packages/web/src/main.tsx

  import RagPage from './pages/RagPage.tsx';
+ import InterpreterPage from './pages/InterpreterPage.tsx';

  const router = createBrowserRouter([
         path: '/kendra',
         element: <KendraSearchPage />,
       },
+      {
+        path: '/interpreter',
+        element: <InterpreterPage />,
+      },
       {
         path: '*',
         element: <NotFound />,

和田InterpreterPage.tsx は長いので抜粋して解説します。主な機能として、React で各パーツを生成し、各パーツの動作に対して動作をバインドしています。例えば、関数の内容を記述する部分の場合、React の以下のようなコードでテキストエリアを作成します。」

packages/web/src/pages/InterpreterPage.tsx

(前略)
          <Textarea
            label="関数の処理内容"
            value={context}
            hint="処理内容をできるだけ詳細に記載してください。"
            onChange={setContext}
          />
(後略)

「ここの部分ですね ?」

画像をクリックすると拡大します

和田「はい。そして、テキストエリアに変更があった場合に、onChange={setContext} 」で setContext をバインドしてます。」

packages/web/src/pages/InterpreterPage.tsx

(前略)
    context: `## 処理の概要

## 関数のINPUT

## 関数のOUTPUT
`,
    setContext: (s: string) => {
      set(() => ({
        context: s,
      }));
    },
(後略)

和田「このようにして context というキーに変更が加わるたびにテキストエリアの文字列が格納されていきます。同様のことを、runtime や 関数名などもセットしていきます。」

「セットされた値はどうやって使うんです ?」

和田「セットされた値は当然 API に渡す必要があります。例として実行ボタンの実装を見てみましょう。」

画像をクリックすると拡大します

packages/web/src/pages/InterpreterPage.tsx

(前略)
          <div className="flex justify-end gap-3">
            <Button onClick={onClickExec}>実行</Button>
          </div>
(後略)

「クリックしたら onClickExec が動くんですね ?」

和田「少し読めるようになってきましたね。onClickExec を見てみましょう。」

packages/web/src/pages/InterpreterPage.tsx

(前略)
import interpreterPrompt from '../prompts/interpreter-prompt';
(中略)
  const { postChat, messages, isEmpty } = useChat(pathname);
(中略)
  const onClickExec = useCallback(() => {
    postChat(interpreterPrompt.generationContext(context));
  }, [context, postChat]);
(後略)

「実行ボタンを押したら、onClickExec を実行する、そしてその中身は先程作成した context を入れて postChat を実行する、ですかね ?」

和田「はい。先程作成した context を入れて実行します。useChat は他の機能 (要約や普通のチャット) と共用なので、機能を拡張する際にそんなに意識をする必要ないですが、中身は Lambda を通して Bedrock にリクエストを投げるものです。ここで出てきた interpreterPrompt というのが先程作成したプロンプトのテンプレートを管理するものなので見てみましょう。」

packages/web/src/prompts/interpreter-prompt.ts

import { TestCaseType } from '../@types/interpreter';

export default {
  systemContext: (runtime: string) => {
    return `あなたはAWS Lambda関数を生成するAIアシスタントです。
以下の手順でLambda関数のコードを生成してください。手順以外のことは絶対にしないでください。

# Lambda関数の生成手順
* ユーザがチャットで関数作成のコンテキストを与えるので、それに従ってください。ただし、「# Lambda関数の生成手順」は厳守してください。例外はありません。
* ユーザがチャットで指示した「# 関数のコンテキスト」を理解してください。この内容を元にコードを生成します。ここに書かれていない処理は、絶対にコードとして生成してはいけません。例外はありません。
* Lambdaのランタイムは「${runtime}」を利用するので、ランタイムに合ったコードを生成してください。
* Lambda関数のハンドラーの名前は、「handler」としてください。
* コードに対して解説を行いたい場合は、コード内のコメントブロックとして出力してください。
* 生成したコードだけを出力してください。その他の文言は一切出力しないでください。「ありがとう」「すみません」などという雑談も一切不要です。例外はありません。
`;
  },
  generationContext: (context: string) => {
    return `# 関数のコンテキスト
${context}`;
  },
  generationTestData: () => {
    return `以下の手順でテストデータを生成してください。手順以外のことは絶対にしないでください。

# テストデータの生成手順
* 先ほどあなたが生成したLambdaのコードに対するテストデータを生成します。これまでのやり取りをすべて理解してください。
* Lambda関数の品質を担保することのできるテストデータを考えてください。
* 「# テストデータのフォーマット」のJSON形式でテストデータを出力してください。データは必ず配列で設定してください。その他の文言は一切出力しないでください。例外はありません。
* JSON以外の文字列は一切出力しないでください。見出しや説明文は一切出力してはいけません。例外はありません。

# テストデータのフォーマット
{
  describe: "テストデータの解説",
  input: "Lambda関数のINPUT",
  output: "Lambda関数のOUTPUT"
}[]
`;
(後略)

「先程作成した関数生成プロンプトとテストデータ生成プロンプトを TypeScript でプレースホルダーで後から埋め込めるようにしただけですね !」

和田「その通りです。そして先程の context キーから埋め込むわけです。」

デプロイボタンの開発

useChat ではまかえない、例えば関数を作成する部分などの解説もできれば・・・(スリスリ 」

和田「デプロイボタンですね。そろそろ自分で読んでください。」

画像をクリックすると拡大します

目にしただけで頭がズキンとして・・・。スクロールすると熱が出て目が回って吐き気がして。数行で意識不明。」

和田四次元ポケットは出てきませんので、どこかのあやとりと射撃名人みたいなこと言わずにとっととやってください。」

「スクロールせずに行きましょう。InterpreterPage.tsx で “デプロイ” で検索っと。ここですね!」

packages/web/src/pages/InterpreterPage.tsx

(中略)
                  <Button
                    disabled={functionName === ''}
                    loading={loadingDeploy}
                    onClick={onClickDeploy}>
                    デプロイ
                  </Button>
(中略)

onClick={onClickDeploy} とあるので、クリックしたときに走る onClickDeploy 関数がどこかにあるはずですね。検索ゥ ! 検索ゥ ! WRYYYYY !

packages/web/src/pages/InterpreterPage.tsx

(前略)
import useInterpreter from '../hooks/useInterpreter';
(中略)
  const {
    createFunction,
    updateFunction,
    existsFunction,
    invokeFunction,
    generateTestData,
  } = useInterpreter();
  const onClickDeploy = useCallback(() => {
    const code =
      /```.*\n(?<code>(.|\n)+)\n```$/.exec(
        messages[messages.length - 1].content
      )?.groups?.code ?? '';

    setLoadingDeploy(true);
    if (shouldUpdate) {
      updateFunction({
        functionName: functionName,
        code: code,
      }).finally(() => {
        setLoadingDeploy(false);
      });
    } else {
      createFunction({
        functionName: functionName,
        code: code,
        role: roleArn,
        runtime: runtime,
      }).finally(() => {
        setLoadingDeploy(false);
      });
    }
  }, [
    createFunction,
    functionName,
    messages,
    roleArn,
    runtime,
    shouldUpdate,
    updateFunction,
  ]);
(後略)

わかりませんが !?

和田「ここは少し複雑なのですが、同名の関数があったら更新、なければ新規作成、という作りをしています。」

「そこまで実装してくださったんですね。」

和田「関数有無の状態管理の説明はめんどくださいので、新規作成に絞ってみてみましょうか。」

「すると、きっと createFunction を読んでる部分ですね。functionName , code, role, runtime を引数にしていますし、間違いないですね。createFunction 自体は useInterpreter を展開して作られているので、useInterpreter を見てみましょう。import useInterpreter from '../hooks/useInterpreter'; とあるので、useInterpreter.ts を見ればいいわけか ! 読める ! 読めるぞ !

和田(城が落ちるのかな ?)

packages/web/src/hooks/useInterpreter.ts

(前略)
    createFunction: (params: CreateLambdaFunctionRequest) => {
      return http.post<object, CreateLambdaFunctionRequest>(
        'interpreter/lambda',
        params
      );
    },
(後略)

「これは interpreter/lambda にpost してますね。API Gateway を見てみますか。」

画像をクリックすると拡大します

「お、GenerativeAiUseCasesStack-InterpreterCreateLambdaxxxxxx というLambda 関数を読んでますね。CDK のコードを確認してみましょう。」

packages/cdk/lib/construct/interpreter.ts

(前略)
    const createFunction = new NodejsFunction(this, 'CreateLambdaFunction', {
      runtime: Runtime.NODEJS_18_X,
      entry: './lambda/createLambdaFunction.ts',
      timeout: Duration.minutes(15),
    });
(中略)
    lambdaResource.addMethod(
      'POST',
      new LambdaIntegration(createFunction),
      commonAuthorizerProps
    );
(後略)

「CDK で createFunction という関数を./lambda/createLambdaFunction.ts で作成し、lambdaResource を使って API Gateway から createFunction という関数をキックするようにしているのがわかります。」

和田「他にも Lambda のロールをどうするだとかいくつかトピックはありますが、だいたいの動きは把握できましたね。最後に Lambda の createFunction 関数を見ておきましょう。」

ファ、ファ、ファ、ファンクションッ!すみません、関数アレルギーでくしゃみが。

和田・・・。

packages/cdk/lambda/createLambdaFunction.ts

(前略)
    const req: CreateLambdaFunctionRequest = JSON.parse(event.body!);
    const setZipFileName = (runtime: string): string => {
      if (runtime.startsWith('nodejs')) {
        return 'index.js';
      } else if (runtime.startsWith('python')) {
        return 'index.py';
      } else {
        throw new Error("Unknown Runtime.");
      }
    }
    const zipFileName = setZipFileName(req.runtime);
    zip.file(zipFileName, req.code);
    const zipFile = await zip.generateAsync({ type: 'uint8array' });

    const client = new LambdaClient({});
    const command = new CreateFunctionCommand({
      FunctionName: req.functionName,
      Code: {
        ZipFile: zipFile,
      },
      Runtime: req.runtime,
      Role: req.role,
      Handler: 'index.handler',
    });
(後略)

和田「リクエストにある code を zip 化し、CreateFunctionCommand API で Lambda の関数を作成していることがわかりますよね (圧) ? こんな感じで各パーツの動作も作り込んでいるので、ぜひ私も開発したい ! という人は他の動作の部分のコードも読み込んでいただけると嬉しいですね。」

「フロントエンドと Lambda を書いたことがある人であれば、生成 AI に携わったことがなくても簡単に拡張できそうですね ! 私以外のみなさん、ぜひ手を動かして拡張してみてくださいね !

4-3. 今回開発した Interpreter 機能は AWS 用いた開発業務に使えるのか ?

プロダクション環境では正直しんどいです。

いきなり否定的ですみません。今回テスト的に作って「うまく言ったらあわよくばソリューションの main ブランチにマージして、みんなで自然言語で AWS をがんがん使いましょうね ! その第一歩で Lambda からだ !」と意気込んでいたのですが、今回開発したものだけだと、AWS のあるべき使い方と相容れないところがあります。

一番大きなところは、作成した Lambda に固定のロール & 固定のポリシー (AWSLambdaExecute) を割り当てているため、Lambda から他の AWS のサービスを操作できないのが大きいです。一般にプロダクション環境で Lambda 単体で使うことは少なく、Amazon S3 や Amazon SQS などと連携したりすることが多いですが、固定のポリシーだとそれができません (セキュリティに目を瞑って AdministratorAccess など許されませんよ !?)。とすると、Lambda で単純な算術演算くらいしか使えなくなってしまい、AWS らしさがなくなってしまい、ただの関数を再利用できる Code Interpreter です。また、リソースを削除する機能もないので、なかなか業務利用は難しいです (今回ほとんど触れられませんでしたが、同名関数があった場合上書きすることはできます)。

とはいえ、このような Interpreter 機能を Lambda だけでなく AWS IAM 等でも実装し、組み合わせればもっとよいものができるかもしれません。もしくはそもそも AWS CDK のコードを出力するような形にして実行、というだけでも実用性が上がるかもしれません。

何かを開発する時は、実際に手を動かして試すことが大切です。理論だけでは気づかない点が表れるので、積極的に実践していくことが重要です。失敗しても諦めずに、そこから学び続けることが大切です。試行錯誤しながら前に進むのが、開発の醍醐味だと思います。(丸投げしてきた呉に対する和田の熱いメッセージでもあります)

よい案や実装ができあがったと思ったらぜひ Issue, Pull Request などの形でいただけると幸甚です。


5. おわりに

「結局和田さんに開発を全部やってもらってしまいましたね(ゲスな顔をしながら計画通りと思っている)。和田さんは普段このような活動をされているとか ?」

和田「プロトタイプの開発や GitHub の aws-samples に公開するソリューションの開発とかいろいろ手を動かすことをやっているんですよ。」

「もし読者の皆様がこのような活動に興味を持ったら、まずは こちら からセールスに問い合わせていただけるとよろしいかと思います。」

和田「ソリューション自体は活発に動いており、 Interpreter 機能を開発している間にも main ブランチにどんどん機能追加されているので、頻繁に覗いていただけるとうれしいですね。Issue, PR もお待ちしています。」

「今回は和田さんありがとうございました (引き続き丸投げ先としてオナシャス)。また、読者の皆様には新しく AWS の仲間に加わった Amazon Bedrock をぜひ触っていただきたいです ! ソリューション含め試してください !」


builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

呉 和仁 (Go Kazuhito / @kazuneet)
アマゾン ウェブ サービス ジャパン合同会社
機械学習ソリューションアーキテクト。

IoT の DWH 開発、データサイエンティスト兼業務コンサルタントを経て現職。
プログラマの三大美徳である怠惰だけを極めてしまい、モデル構築を怠けられる AWS の AI サービスをこよなく愛す。

和田 雄介 (Wada Yusuke)
アマゾン ウェブ サービス ジャパン合同会社
プロトタイピングエンジニア

プロトタイピングを通して、技術的にお客様を支援しています。
愛犬と一緒に走るのが好き。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する