【コード38行】初心者向き超シンプルなLambdaを用いた生成AIアプリ開発のやり方

# はじめに
現在新卒研修中であり，チームメンバーから「Bedrockの使い方を教えて欲しい」という声がありました．
今回は，簡単に生成AIを用いたアプリケーションを開発することのできる「Bedrock」を用いた簡単なテキストチャットアプリを作成します．
エラーハンドリング等の処理をすべて省き，Bedrockでclaude4-sonnetに質問をしてその返答をreturnするだけのシンプルなLambda関数を作成しました．
はじめてLambdakを触るような人にもわかるように解説していきます．
その関数のコードをもとに，使い方を解説していきます．
また，Bedrockの使用にはAIがよく出力する`InvokeModel`ではなく`converse`を使用します．

# 使用するAWSリソース
- Lambda: Bedrockにプロンプトを入力し，推論結果を返す関数を作成する
- IAM: LambdaにBedrockへのアクセスを許可する
- Bedrock: 使用可能な生成AIのモデルアクセスを選択する

# Lambda関数の準備
### AWS マネージメントコンソールから，Lambda関数を作成する

1. マネージメントコンソール上から，Lambdaを検索し，クリックする．
![スクリーンショット 2025-06-10 17.32.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/f3221c3a-aa7d-42a9-ade9-d67064a37ff3.png)

2. 添付画像右上の[関数の作成]をクリックする．
![スクリーンショット 2025-06-10 17.30.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/78a1c498-06de-41c1-bec8-ddbaae46c345.png)

3. Lambda関数を作成する
添付画像と同じように設定する
![スクリーンショット 2025-06-10 17.37.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/4ee2f562-41cc-493f-9397-af73e9b261b4.png)

```
関数名：simple-bedrock
ランライム：python3.13
アーキテクチャ：x86_64
```
その他は何もいじらなくて大丈夫です．
また，ランタイムはPython3.9以上であればどのバージョンを使用しても問題ありません．

以下のUIになれば大丈夫です．これでLambda関数の準備は終わりました．
![スクリーンショット 2025-06-10 17.40.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/b76815a6-812b-4bae-a79b-485a81fef220.png)

# Bedrockの有効化
1. リージョンを「バージニア北部(us-east-1)」に変更する
Bedrockで使用する生成AIのモデルは，このリージョンで最も安定して動きます．
そのため，東京(ap-northeast-1)リージョンのLambda関数で，
バージニア北部(us-east-1)リージョンにあるBedrockの生成AIモデルを使用します．
![スクリーンショット 2025-06-10 17.45.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/919a6391-2c3a-4bf0-aab1-fec6f30bfd18.png)

2. 左のバーの下部にある**モデルアクセス**をクリックする．
Bedrock configurationsの中にあるはずです．
![スクリーンショット 2025-06-10 17.47.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/ef6a3a23-7ff6-48c0-966c-54be1691e7ce.png)

3. **モデルアクセスを変更**をクリックする．
使用したい生成AIのモデルにチェクマークを入れ，[次へ]から[送信]してください．
今回は全て選択してもらっても大丈夫です．

数分待てば，今申請したモデルが使用可能になり，Bedrockの有効化が完了したことになります．


# LambdaからBedrockにアクセス可能にする
LambdaのIAMロールにBedrockへのアクセスを許可しないと，
Bedrockを呼び出す権限がない．というエラーになります．

1. Lambda関数を開き，[設定]タブから[アクセス権限]に行きます．
![スクリーンショット 2025-06-10 18.03.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/f5aeae32-7dab-4fc1-94d0-0381c0f99d0e.png)

2. [ロール名]の下にリンクがあると思うので，そこからIAMロールというものを設定していきます．
[許可]タブから[許可ポリシー]の[許可を追加]→[インラインポリシーを作成]
JSONを選択し，以下のjsonを貼り付けて，[次へ]．
```
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"bedrock:InvokeModel",
				"bedrock:InvokeModelWithResponseStream"
			],
			"Resource": [
				"arn:aws:bedrock:*:*:foundation-model/anthropic.*",
				"arn:aws:bedrock:*:*:inference-profile/us.anthropic.*",
				"arn:aws:bedrock:*:*:inference-profile/eu.anthropic.*"
			]
		}
	]
}

```
![スクリーンショット 2025-06-16 22.41.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/7b2488cc-ba7f-4c81-890e-b13b6040c77b.png)

3．ポリシー名を決め，[ポリシーの作成]をクリックします．
今回は`simple-bedrock-policy`としましょう．
これでLambdaからBedrockを使用することができるようになります．


# Lambda関数を書く
今回はライブラリにboto3とjsonしか用いないため，コンテナイメージやLambdaレイヤーなどの準備をする必要はないです．このコードを貼り付けて，イベントJSONを入力すれば動きます．
### 完成コード
```lambda_function.py
import json
import boto3


def lambda_handler(event, context):
    # プロンプトを取得
    prompt = event.get("prompt")
    
    # Bedrockクライアントを作成
    bedrock_client = boto3.client('bedrock-runtime', region_name='us-east-1')
    
    # Converseを呼び出し
    response = bedrock_client.converse(
        modelId="us.anthropic.claude-sonnet-4-20250514-v1:0",
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "text": prompt
                    }
                ]
            }
        ],
        inferenceConfig={
            "maxTokens": 1000
        }
    )
    
    # レスポンスから結果を取得
    result = response['output']['message']['content'][0]['text']
    
    # 結果を返す
    return {
        "prompt": prompt,
        "result": result
    }
```
### イベントJSON
```json
{
  "prompt": "日本の三大武将を教えて"
}
```

一度実行してみましょう．
1. [コード]タブの中の[コードソース]の`lambda_function.py`に完成コードを貼り付けます．
1. [Deploy]をクリック，もしくは`Shift + Cmd + U`でデプロイします．
1. [設定]タブの[一般設定]の[編集]をクリックし，[タイムアウト]をデフォルトの3秒から30秒に変更し，保存します．
1. [テスト]タブの[イベントJSON]に上記のjsonを貼り付け，オレンジ色のテストボタンをクリックし，[テスト]を行います．
![スクリーンショット 2025-06-10 21.35.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/e473c0c3-7f1c-4ff8-bc5e-18f190c03dd4.png)

テストで成功するとこのようなUIになります．
![スクリーンショット 2025-06-10 21.45.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/fceebe97-d6fc-4cdc-b069-55232c7a164e.png)
詳細を確認すると，以下のようになっています．
![スクリーンショット 2025-06-10 21.46.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/7229bbf0-6137-445c-ae46-aeba05f0cc1e.png)
```json
{
  "prompt": "日本の三大武将を教えて",
  "result": "日本の三大武将としてよく挙げられるのは、以下の3人です。\n\n1. 源頼朝(1147年-1199年)\n鎌倉幕府を開いた初代将軍。平氏一門を討伐し、武家政権の基礎を築きました。\n\n2. 織田信長(1534年-1582年) \n天下布武を目指した戦国大名。本能寺の変で暗殺されましたが、東西の統一に大きく貢献しました。\n\n3. toyotomi 秀吉(1537年-1598年)\n甲斐武田家の家臣から出世し、関ヶ原の戦いで徳川家康に勝利しました。全国統一を達成した太閤です。\n\nこの3人の武将は、日本の歴史に大きな足跡を残し、時代を画する活躍をしたことから、三大武将と呼ばれています。頼朝は武家政権の祖、信長は統一への布石、秀吉は実際の統一を成し遂げた、という位置付けです。"
}
```
今回は`日本の三大武将を教えて`という内容のプロンプトを推論させました．
イベントJSONの`prompt`に与える文字列を好きに変えてみると，その文字列の回答が`result`として返されます．
## コードの解説
### ライブラリのインポート
```python
import json
import boto3
```
pythonでjsonの操作を行うため，jsonライブラリをインポートしてます．
boto3はpythonでAWSリソースを扱うためのライブラリです．今回の場合，このboto3でbedrockを使用しています．

### lambda_handler関数の宣言
```python
def lambda_handler(event, context):
```
C言語のmain関数と同じです．APIの呼び出しなどによってLambda関数が実行された場合，この`lambda_handler`の中の処理が行われます．
また，2つの引数`event`,`context`があります．
イベントJSONに入っているjsonはこの`event`に渡されます．
`context`は，Lambda関数の関数名やランタイムなどの情報が入っています．

### プロンプトを取得
```python
    prompt = event.get("prompt")
```
生成AIに推論させる内容である`prompt`に，イベントJSONの`prompt`を渡しています．
つまり，今回の場合，`prompt = "日本の三大武将を教えて"`と同義です．

### Bedrockクライアントを作成
```python
    bedrock_client = boto3.client('bedrock-runtime', region_name='us-east-1')
```
クライアントとは，この場合**接続窓口**を表します．
bedrockを使用するため，窓口を作成する必要があります．boto3を使用し，バージニア北部(us-east-1)にあるbedrockの`bedrock-runtime`を使用する宣言をします．`bedrock-runtime`は，AWSが提供するAI推論サービス，つまりBedrockのことです．

### Bedrock呼び出し
```python
    response = bedrock_client.converse(
        modelId="us.anthropic.claude-sonnet-4-20250514-v1:0",
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "text": prompt
                    }
                ]
            }
        ],
        inferenceConfig={
            "maxTokens": 1000
        }
    )
```
少し前に定義した`bedrock_client`を使用し，Bedrockを呼び出します．
`modelId`で使用するモデルを指定します．今回はanthropic社のclaude4-sonnetを使用します．
`modelId`はマネジメントコンソール上のBedrockの`Model catalog`から確認できます．
`messages`の中の`text`にAIに推論してほしい内容を入れます．
推論結果を含めて様々な情報がこの変数`response`に代入されます．

### レスポンスの解析
```python
    result = response_body['output']['message']['content'][0]['text']
```
`response`には，欲しい情報以外にも様々な情報が含まれます．具体的には以下の感じです．
```json
{'ResponseMetadata': {'RequestId': '1234567890',
'HTTPStatusCode': 200, 'HTTPHeaders': {'date': 'Mon, 16 Jun 2025 13:49:27 GMT', 
'content-type': 'application/json', 'content-length': '1197', 
'connection': 'keep-alive', 'x-amzn-requestid': '1234567890'}, 'RetryAttempts': 0}, 
'output': {'message': {'role': 'assistant', 
'content': [{'text': '日本の三大武将として一般的に挙げられるのは以下の3人です：
\n\n## **織田信長（1534-1582）**\n- 戦国時代の革新的な武将
\n- 楽市楽座や兵農分離など革新的な政策を実施\n- 天下統一の基盤を築いた
\n\n## **豊臣秀吉（1537-1598）**\n- 信長の後継者として天下統一を達成
\n- 農民出身から関白まで上り詰めた立身出世の代表例\n- 刀狩りや太閤検地などの政策を実施
\n\n## **徳川家康（1543-1616）**\n- 江戸幕府の初代将軍
\n- 関ヶ原の戦いで勝利し、約260年続く江戸時代の基礎を築いた
\n- 忍耐強い性格で「鳴くまで待とう時鳥」の句で知られる
\n\nこの3人は「戦国三英傑」とも呼ばれ、それぞれが天下統一に向けて重要な役割を果たし、
日本史上最も有名な武将として親しまれています。'}]}},
'stopReason': 'end_turn', 'usage': {'inputTokens': 19, 
'outputTokens': 348, 'totalTokens': 367}, 'metrics': {'latencyMs': 12345}}
```
今回の場合，欲しいのはこの中の`output`の中の`message`の中の`content`の最初の要素の`text`要素だけです．
なので`result = response_body['output']['message']['content'][0]['text']`で`response`の中を読み込み，必要な部分だけを`result`に代入しています．

### 結果を返す
```python
    return {
        "prompt": prompt,# 元の質問
        "result": result # AIのかいとう
    }
```
これが最終的なLambda関数の出力になります．
コードの解説は以上です．

# 発展
## 発展1：クエリパラメータでpromptを受け取る
### 完成コード
```lambda_function.py
import json
import boto3


def lambda_handler(event, context):
    # クエリパラメータからプロンプトを取得
    prompt = event.get("queryStringParameters", {}).get("prompt")
    
    # Bedrockクライアントを作成
    bedrock_client = boto3.client('bedrock-runtime', region_name='us-east-1')
    
    # Converseを呼び出し
    response = bedrock_client.converse(
        modelId="us.anthropic.claude-sonnet-4-20250514-v1:0",
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "text": prompt
                    }
                ]
            }
        ],
        inferenceConfig={
            "maxTokens": 1000
        }
    )
    
    # レスポンスを解析して結果を取得
    result = response['output']['message']['content'][0]['text']
    
    # 結果を返す
    return {
        "prompt": prompt,
        "result": result
    }
```

### API Gatewayの設定をする
このセクションでは，API Gatewayで，このLambda関数をGETメソッドで呼び出して使用できるようにする．
そのため，まずはAPI GatewayでAPIエンドポイントを発行する．

1. マネージメントコンソールで[API Gateway]を検索し，クリックします．
1. [APIを作成]をクリックします．
1. APIタイプを選択で[HTTP API]を選択します．
 ![スクリーンショット 2025-06-16 21.02.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/853098b9-92d0-48bc-9e68-332c6ef0a0cc.png)

1. API名を決めて，[**確認して作成**]をクリックし，作成する．
今回は[simple-bedrock-api]としましょう．
![スクリーンショット 2025-06-16 21.03.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/9fa48550-0033-4f84-b018-a50a3a5f6845.png)

1. [Routes]から[作成]をクリックする．
![スクリーンショット 2025-06-16 21.06.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/df035b3d-cf79-442f-acf2-95baeac4fef9.png)

1. メソッドは[GET]を選択し，[作成]する．
![スクリーンショット 2025-06-16 21.07.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/76855279-62b4-4942-b5b2-8030ba0da4d3.png)
1. [統合をアタッチする]→[統合を作成してアタッチ]で呼び出すLambda関数を選択する．
![スクリーンショット 2025-06-16 21.09.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/4954ab72-0926-492a-be3e-2476311476cd.png)

1. [統合タイプ]にLambda関数を指定し，作成したLambda関数`simple-bedrock`を指定し，[作成]をクリックする．
![スクリーンショット 2025-06-16 21.13.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/f949dfd1-b9e1-4441-b7ae-7ec199513876.png)

1. Lambda関数に戻り，[設定]の[トリガー]を確認すると，APIエンドポイントが発行されている．
このURLを呼び出すと，Lambda関数が実行されるようになった．
![スクリーンショット 2025-06-16 21.14.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/1eb6b520-2f93-4d4d-933c-93ead7da0a84.png)

1. ブラウザを開いて，APIを呼び出してみる．
エンドポイントのURLを貼り付けて，`https://xxxxxx.amazonaws.com/?prompt=日本の有名な武将を教えて`
とし，Enterキーをクリックすると，しばらくして下記画像のように推論結果が返ってくる．
`?prompt=xxxxx`をクエリパラメータという．`エンドポイント?クエリパラメータ`とすることによってそのクエリパラメータの部分がAIに渡すテキストとなり，そのテキストの内容を推論してくれる．クエリパラメータの部分に何も入力しないとエラーになる．
![スクリーンショット 2025-06-16 21.18.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/c0a5555b-318d-4b2f-ac05-5dc5c0978b74.png)

### コードの変更点
before
```python
# プロンプトを取得
    prompt = event.get("prompt")
```
after
```python
# クエリパラメータからプロンプトを取得
    prompt = event.get("queryStringParameters", {}).get("prompt")
```
変更点はこの行だけであり，イベントJSONから`prompt`を受け取っていたのを，クエリパラメータから受け取るように変更しただけである．

## 発展2：POSTメソッドでJSONを受け取り，JSONの中にpromptを格納しておく

### 完成コード
こちらのコードも発展1と同様に，変更したのは`prompt=json.loads`の部分1行だけです．
```python
import json
import boto3


def lambda_handler(event, context):
    # POSTのJSONボディからプロンプトを取得
    prompt = json.loads(event.get("body", "{}")).get("prompt")
    
    # Bedrockクライアントを作成
    bedrock_client = boto3.client('bedrock-runtime', region_name='us-east-1')
    
    # Converseを呼び出し
    response = bedrock_client.converse(
        modelId="us.anthropic.claude-sonnet-4-20250514-v1:0",
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "text": prompt
                    }
                ]
            }
        ],
        inferenceConfig={
            "maxTokens": 1000
        }
    )
    
    # レスポンスを解析して結果を取得
    result = response['output']['message']['content'][0]['text']
    
    # 結果を返す
    return {
        "prompt": prompt,
        "result": result
    }
```

### API Gatewayの設定をする
POSTメソッドを使用することにより，クエリパラメータを使用せず，イベントJSONを用いた時と同じようにして`prompt`を渡すことができます．
発展1で使用したものと同じAPI Gatewayに設定を追加し，POSTで通信できるようにしましょう．

1. 発展1で作成したAPI Gateway「simple-bedrock-api」を開く
1. [Routes]から[作成]をクリックする．
![スクリーンショット 2025-06-16 21.33.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/a7858205-911d-48ea-a6d5-ded007b83785.png)

1. メソッドに[POST]を指定し，発展1で作成したAPIをエンドポイントが被らないように`/post`を指定し，作成します．
![スクリーンショット 2025-06-16 21.34.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/015d8083-8428-48c5-8eef-5f0859bc3f99.png)

1. 発展1と同様に，[統合をアタッチ]で`simple-bedrock`Lambda関数を統合する．
![スクリーンショット 2025-06-16 21.35.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/4106ad4b-217c-4595-bf07-5aca471e3d95.png)

1. Lambda関数に戻り，[設定]の[トリガー]を確認すると，新しく`/post`で終わるAPIエンドポイントが増えている．これを呼び出し，jsonでイベントJSONと同様のものを渡すとプロンプトが返ってくる．
![スクリーンショット 2025-06-16 21.37.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/c4c9be0a-4cb5-4cee-be1b-0206aa293337.png)

1. `curl`コマンドでAPI呼び出しテストを行う
`https:/xxxxxxx`は`/post`で終わるAPIエンドポイントに変えてください．
```bash
curl -X POST https://xxxxxxxx/post \
  -H "Content-Type: application/json" \
  -d '{"prompt": "日本の有名な武将を教えてください"}'
```
しばらくすると，推論結果が返ってくると思います．
自分は`curl`コマンドの形式を覚えていないのでいつもPostmanを使って呼び出しテストを行なっています．

#### Postmanを使用してAPI呼び出しテストを行う．
1. vscodeに拡張機能「Postman」をインストールする．
![スクリーンショット 2025-06-16 21.47.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/ded06f91-6cd8-494a-99c9-525cab2a3b5b.png)

1. インストールが完了すると，画像のように，拡張機能マークの下にPostmanマークが現れると思います．
そのアイコンをクリックし，[New HTTP Request]からAPI呼び出しテストを行うことができます．
![スクリーンショット 2025-06-16 21.49.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/d07ffd0a-b566-4be3-a63e-7a2b3b2605df.png)

1. 各種設定をする．
①POSTメソッドを選択し，APIエンドポイントを貼り付ける．
②[Body]→③[raw]→④[Text]を[JSON]に変更する．
![スクリーンショット 2025-06-16 21.51.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/6bdd53a1-fff0-45f4-bd35-962b38f83afd.png)
⑤イベントJSONと同じようにjsonで`prompt`を指定する．
1. [Send]をクリックする．
しばらく経過すると，下記画像のように推論結果が出力される．
![スクリーンショット 2025-06-16 21.59.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3820079/1449d459-27ac-42e9-8e2e-77b2e129e324.png)

# おわりに
今回はLambda+Bedrockで簡単なチャットが可能なLambda関数を作成しました．
これらのAPIをWebサイトに埋め込み，`fetch`や`axios`で呼び出すことで簡単にチャットアプリを作成することができます．
しかし，今のままではAIと会話を続けていくことはできません．1つ前にどんな会話をしていたかを考慮していないからです．
そのため，次回ではDynamoDBを使用して会話を続けることのできるチャット機能の実装をします．