概要
ChatGPT 面白いですよね。
いつ仕事奪われるかとドキドキします。
「そんなChatGPTがSlackから使えたら便利かも?」と思い、2023-03-01 に公開されたChatGPTのAPIを利用して作ってみました。
※ご注意
ChatGPTのAPIは従量課金なのでご注意ください。
無料枠があるので一旦は使えます。
アーキテクチャ
Slash Commandで質問 > API Gateway > AWS Lambda(一次受け) > AWS Lambda(本処理) > ChatGPTに問い合わせ > Slackチャンネルに回答
ポイント
Slash Commandは3秒以内にレスポンスを返す必要がある一方、OpenAIのAPIレスポンスは数秒かかるため1プロセスでの実現はできません。
そのため AWS Lambda を2段階に分けて、非同期起動した AWS Lambda で ChatGPT を利用してSlackチャンネルに投げ込む形としました。
手順
- OpenAIアカウントでAPI_KEY取得
- AWS Lambda(本処理)作成
- AWS Lambda(一次受け)作成
- API Gatewayでルート設定し、バックエンドにAWS Lambda(一次受け)を設定
- SlackアカウントでSlash Command作成
順に説明していきます。
1.OpenAIアカウントでAPI_KEY取得
こちらから発行します。
Account API Keys - OpenAI API
https://platform.openai.com/account/api-keys
2.AWS Lambda(本処理)作成
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
logger.info(event)
logger.info(context)
# application/x-www-form-urlencoded で受信するのでデコードする
logger.info(event["body"])
import base64
kv = base64.b64decode(event["body"]).decode("utf-8")
logger.info(kv)
import urllib
parsed = urllib.parse.parse_qs(kv)
# logger.info(parsed)
name = parsed["user_name"][0]
prompt = parsed["text"][0]
response_url = parsed["response_url"][0]
import os
model_url = os.environ["MODEL_URL"]
model = os.environ["MODEL"]
import requests
response = requests.post(model_url,
headers={
"Content-Type": "application/json",
"Authorization": "Bearer %s" % os.environ["OPEN_API_KEY"]
},
json={
"model": model,
"messages": [
{"role": "user", "content": prompt,},
]
}
)
import json
values = json.loads(response.text)
logger.info(values)
payload = {"response_type": "in_channel","text": prompt + "\n\n".join([choice["message"]["content"] for choice in values["choices"]])}
requests.post(response_url,data=json.dumps(payload),headers={"content-type": "application/json"})
return {
"statusCode": 200,
"body": ""
}
環境変数名 | 値 |
---|---|
OPEN_API_KEY | 「1.」で取得したもの |
MODEL_URL | https://api.openai.com/v1/chat/completions |
MODEL | gpt-3.5-turbo |
requestsモジュールを利用しているので同梱するなり、AWS Lambda Layersで読み込むなりしてください。
API仕様はこちらです。
Chat completion - OpenAI API
https://platform.openai.com/docs/guides/chat
ポイント
・呼び出し元チャンネルに回答するためにリクエストに含まれる response_url を利用しています。
・チャンネル内のユーザ全員に見えるようレスポンスに response_type=in_channel を設定しています。
・Slash Commandは他ユーザには見えないのでレスポンスに追加するよう調整しています。
一次受けから本関数を呼び出すためにLambda関数名を控えておきます。
3.AWS Lambda(一次受け)作成
import logging
import os
import json
logger = logging.getLogger()
logger.setLevel(logging.INFO)
import boto3
client = boto3.client("lambda")
def lambda_handler(event, context):
logger.info(event)
res = client.invoke(
FunctionName=os.environ["INVOKE_FUNCTION_NAME"],
InvocationType="Event", # 非同期
Payload=json.dumps(event)
)
logger.info(res)
return {
"statusCode": 200,
"body": "しばし待たれよ"
}
環境変数名 | 値 |
---|---|
INVOKE_FUNCTION_NAME | 「3.」で控えたもの |
起動時間優先でboto3のインスタンス作成をトップレベルで行うようにしました。
4.API Gatewayでルート設定し、バックエンドにAWS Lambda(一次受け)を設定
今回は慣れている HTTP API で実装しました。
AWS Lambda Function URLでも良いです。
エンドポイントを控えておきます。
5.SlackアカウントでSlash Command作成
こちらの「Slack APIの作成」の項目を参考にさせていただきました。
Request URLに「4.」で控えたエンドポイントを設定します。
【小ネタ】SlackのスラッシュコマンドからLambdaを実行させる方法 | DevelopersIO
https://dev.classmethod.jp/articles/lambda-api-slack-command/
結果
課題
・このアーキテクチャでもコールドスタートだとタイムアウトするかもしれません。安定化させたい場合はメモリ割り当てを強化してください。なお費用。
参考URL
API Reference - OpenAI API
https://platform.openai.com/docs/api-reference/introduction
Introducing ChatGPT and Whisper APIs
https://openai.com/blog/introducing-chatgpt-and-whisper-apis
Slack Bot をサーバーレスで運用する時の、タイムアウト対策【小技】 - Qiita
https://qiita.com/saken649/items/b70e462ae41614b72f77
AWS LambdaからLambdaを非同期で呼び出す(Python) - Qiita
https://qiita.com/ume1126/items/8170a10fad6b21f0f54a
「ChatGPTのAPI」と「GPT-3のAPI」は同じものなの?違うものなの?について
https://did2memo.net/2023/02/23/chatgpt-chatgpt-api-vs-gpt-3-api/#chatgptapigpt-3api
余談
これを作るにあたって ChatGPT にも聞いてみました。
ヒントになりました。ありがとう!