❤️‍🔥

LambdaからChatGPTのAPIを呼び出す

2024/08/08に公開

背景

ChatGPTが目の前でクイズを作ってくれるライブキッチンみたいなクイズゲームをサーバレスで作りたいなということで、LambdaからChatGPTのAPIを呼び出しました。
僕はクイズゲームに使用しましたが、Slackから呼び出したりとか色々遊べそうな気がします。

Open AIにログイン

Open AIのサイト(https://openai.com/)からAPI keyを取得します。
作業時点ではサイト下部の「API login」から行けました。
たぶんUIは頻繁に更新されます。

API keyの取得

ログイン後、DashBoardからAPI keyを開き、API keyを作成します。
キーは作成時にしか参照できないようなので忘れず控えておきます。

なお、APIを使用するには事前にcreditをチャージする必要があるようです。

API keyの格納

API keyは機密情報ですのでSecrets Managerに格納して暗号化します。
ここはParametor Storeでもよかったかもしれませんね。

lambdaの作成

関数を作成します
ランタイムはPython3.12を使用しています
実行ロールは新規で作成しています

コードの入力

コードを入力します。
プロンプトやパラメータは用途に合わせて修正してください。

import json
import os
import boto3
import openai
from botocore.exceptions import ClientError

def get_secret():
    secret_name = "ここにシークレットの名前"
    region_name = "ap-northeast-1"
    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )
    try:
        get_secret_value_response = client.get_secret_value(SecretId=secret_name)
    except ClientError as e:
        raise e
    secret = get_secret_value_response['SecretString']
    return json.loads(secret)['OpenAIAPIKey']

def lambda_handler(event, context):
    openai.api_key = get_secret()
    
    try:
        response = openai.chat.completions.create(
            model="gpt-4o-mini",
            temperature = 1,
            presence_penalty=2,
            frequency_penalty=2,
            messages=[{
                "role": "user",
                "content": "クイズと答えを作成してください。回答は以下のJSON形式で返してください:{\"quiz\": \"クイズ\", \"answer\": \"答え\"}"
            }]
        )
        
        generated_content = json.loads(response.choices[0].message.content)
        
        return {
            'statusCode': 200,
            'body': json.dumps(generated_content, ensure_ascii=False),
            'headers': {
                'Content-Type': 'application/json; charset=utf-8',
                'Access-Control-Allow-Origin': '*' 
            }
        }
    except Exception as e:
        return {
            'statusCode': 500,
            'body': json.dumps({'error': str(e)}, ensure_ascii=False),
            'headers': {
                'Content-Type': 'application/json; charset=utf-8',
                'Access-Control-Allow-Origin': '*' 
            }
        }

IAMの設定

lambdaがSecrets Managerにアクセスできるよう権限を渡します。
lambdaに紐づいているIAMロールに以下のポリシーをアタッチします。
今回は検証のためリソースを全てにしていますが、該当のシークレットに限定するのが良いと思われます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "*"
        }
    ]
}

Lambda Layerの設定

ここが一番詰まりました。
上記の設定を終えてlambdaをテスト実行すると以下のエラーが出力されます。

"Unable to import module 'lambda_function': No module named 'openai'"

コード内冒頭でインポートしてるopenaiなんて知りませんよってエラーですね。
lambdaではインポートするモジュールをzipにしてlambda Layerにアップロードしてあげる必要があります。

本当はlambdaの実行環境と同様の環境を用意してpip installするのが良いのでしょうが、めんどくさかったのでローカルのWindowsマシンにインストールしました。
pythonインストール済みの環境にて以下を実行します。

mkdir python
pip install -t python openai

この時、作成するフォルダ名は「python」で固定になります。
以下のページで触れられている件ですね。
https://repost.aws/ja/knowledge-center/lambda-import-module-error-python

注: Lambda レイヤーを作成するときは、ライブラリは /python または >python/lib/python3.x/site-packages フォルダーに配置してください。

また、僕の環境では追加で以下のパッケージもインストールする必要がありました。
Windows環境でインストールされた「pydantic-core」というモジュールがlambdaでは使用できなかったようです。
以下のページに記載のある通り、プラットフォームを明示的に指定してインストールする必要があります。
https://stackoverflow.com/questions/76650856/no-module-named-pydantic-core-pydantic-core-in-aws-lambda-though-library-is-i

mkdir python
pip install -t python pip install pydantic-core --platform manylinux2014_x86_64 -t python --only-binary=:all:

作成された二つのpythonフォルダをzipにします。

lambdaのページのレイヤーからレイヤーを作成します。

python.zipが二つあるのでレイヤーも二つ作りました。

lambda関数のコードタブの下部からレイヤーを設定します。
レイヤーの場所いつも忘れちゃいます。設定の中でもいいのになあって。

これでlambdaをテストすればChatGPTから返答が返ってくるはずです。
あとはAPI Gatewayとlambdaを連携させてユーザからの入力をlambdaに渡したり、lambdaを定期的に呼び出してみたりなどいろいろ遊べると思います。

終わりです。

Discussion