5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

AWSのKendraとGPTで自社ドキュメントを学習させたLINEbotを作る

5
Posted at

はじめに

慶應大学 理工学部 情報工学科 B3のぽこちゃんです

今回は技術系の記事を書きます!
学校の実験でAWSを一万円分くらい自由に使わせてもらえたので

タイトルにもあるとおり

AWSのKendraとGPTで自社ドキュメントを学習させたLINEbotを作った話

ソースコードも晒すのでぜひ作ってみてね

お金そこそこかかるので気をつけましょう

Xもやってるのでぜひフォローお願いします
フォロバします!

アーキテクチャについて

アーキテクチャ.png

全体的にはこんな感じ

  1. LINEで質問を投げる
  2. S3に保存してあるドキュメント(PDF)からKendraでindexを作る
  3. LambdaでLINEから得た質問をクエリにしてKendraで類似するチャンクを3つ取得する
  4. チャンクのテキストとLINEの質問を組み合わせてGPTに投げる
  5. GPTから得た回答をLINEに出力する

早速作っていきましょうか

Kendraの設定から

AWSのアカウントは用意している前提で話を進めていきます

まずが上部の検索欄から Kendraと調べて下記のページに辿り着きましょう

その後右側のCreate an Indexを押してください

スクリーンショット 2024-01-12 2.02.44.png

設定画面に移るのでこのページで設定するのは二つ

  1. Index name
  2. IAM role

その他はオプションなので必要の応じて設定してください

スクリーンショット 2024-01-07 9.45.12.pngスクリーンショット 2024-01-12 2.04.11.png

IAM roleは青部分で言われているように
Create a new roleが勧められています
IAM roleについて詳しくなければそうした方が便利です
(自分で権限設定しなければいけないので)

スクリーンショット 2024-01-12 1.48.03.png

画面下部のNextを押して次に進みます
Step2,Step3と進んでいきますが特にこだわりがなければデフォルトで大丈夫です

スクリーンショット 2024-01-07 9.45.23.png

Review and createまで進んだら
画面下部のcreateを押してIndexを作成します
これ結構時間かかります

その間にデータソースとなるS3の設定をしましょう

S3の設定

今回はデータソースとしてS3を用います
他にもWebCrawlerの設定もできるので自動でクロールしてきてってのもできます

さっきと同様にS3と検索して
バケットを作成を押しましょう

スクリーンショット 2024-01-12 2.04.26.png

バケットの設定をしていきますが基本的にはデフォルトでいいです
設定したのはバケット名ぐらいですかね

スクリーンショット 2024-01-07 9.49.04.png

バケットを作成したら何かドキュメントをアップロード(pdf)しておきましょう
その際に
s3://hogehoge/
(hogehogeはバケット名)というS3のURIを得ることができますが
後で使うのでメモっておいてください

そろそろKendraのIndex作成が終わった頃だと思うので戻りましょう

KendraとS3のSync

Kendraの画面に戻って左側のメニューを見てください

スクリーンショット 2024-01-12 2.22.41.png

Data sourcesを選択すると
データソースの候補がたくさん出てきますが今回はS3なので
S3 connectorというのを探してください
Add connector をポチッと

スクリーンショット 2024-01-07 9.46.05.png

connectorの設定画面に移るのでStepに従って進めていきます

ここで変えるのは

  • Step1
    • Data souece name (任意の名前)
    • Default language (データソースの言語)

スクリーンショット 2024-01-07 9.51.58.png

  • Step2
    • IAM role (さっきと同じように新規作成で平気です)

スクリーンショット 2024-01-07 9.52.11.png

  • Step3
    • data source location
      • さっき取得したs3://hogehoge/
    • maxfile size
      • テキトーに100 MBにしてます
    • Sync run schedule
      • Run on demandにしています
      • 手動でsync nowボタンを押したら同期する設定にしてます

スクリーンショット 2024-01-07 9.52.36.png

image.png

  • Step4,5
    • デフォルトでOKだがfield mappingした方が精度上がるかも?

Sync now を押して同期をスタート!
これまたかなり時間かかります(30分くらい??)

その間にLambdaの設定をしちゃいましょう!

Lambdaの設定

Lambdaの設定をしていきます
ここでは

  1. 関数名
  2. ランタイム (Python 3.9)
  3. アーキテクチャ (x86_64,arm64どちらでも可)
    を選択します

Pythonのバージョンは3.9にしましょう!
OpenAI,boto,LINESDKと色々importするのですが
各ライブラリの互換が取れるのが3.9だったと思います
3.11でやってたらクソ苦労しました
まじで半日かけた

スクリーンショット 2024-01-12 2.40.01.png

関数ができたらこっから設定していきます

スクリーンショット 2024-01-12 2.48.46.png

Lambdaのコードについて

コードは以下の通り
設定していただく必要があるのは

  1. "LINE_CHANNEL_ACCESS_TOKEN"
  2. "OPNEAIのAPIキー"
  3. 'KendraのindexID'
  4. 種々のプロンプト

KendraのindexIDってなんだって話ですが
kendraに戻っていただいて以下の赤丸で囲った部分です!

スクリーンショット 2024-01-12 3.01.18.png

import boto3
import json
import langchain
import os
import openai
from openai import OpenAI
from linebot import LineBotApi
from linebot.models import TextSendMessage
kendra = boto3.client('kendra')

LINE_CHANNEL_ACCESS_TOKEN = os.environ['LINE_CHANNEL_ACCESS_TOKEN']
LINE_BOT_API = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN)
openai_API_key = "OPNEAIのAPIキー"
os.environ["OPENAI_API_KEY"] = openai_API_key
openai.api_key = os.environ.get('OPENAI_API_KEY')
client = OpenAI()
def lambda_handler(event, context):
    replyToken = event['events'][0]['replyToken']

    
    if event['events'][0]['type'] == 'message':
        if event['events'][0]['message']['type'] == 'text':
            # LINEからのメッセージをクエリにする
            query_text = event['events'][0]['message']['text']

    index_id = 'KendraのindexID'  

    response = kendra.retrieve(
        QueryText=query_text,
        IndexId=index_id,
        AttributeFilter={
            "EqualsTo": {
                "Key": "_language_code",
                "Value": {"StringValue": "ja"},
            },
        },
    )

    # Kendra の応答から最初の3つの結果を抽出
    results = response['ResultItems'][:3] if response['ResultItems'] else []

    extracted_results = []
    for item in results:
        content = item.get('Content')
        document_uri = item.get('DocumentURI')

        extracted_results.append({
            'Content': content,
        })
    print("Kendra extracted_results:" + json.dumps(extracted_results, ensure_ascii=False))
    
    prompt = get_prompt(
        ref_list=extracted_results,
        query=query_text)
        
    completion = client.chat.completions.create(
    model="gpt-4",
    messages=[
    {"role": "user",
    "content":str(prompt)}
        ]
    )
    print(completion.choices[0].message)
    # LINEにChatGPTの回答を送る
    messageText = completion.choices[0].message
    LINE_BOT_API.reply_message(
        replyToken,
        TextSendMessage(text=messageText.content))
    return None

    
def get_Hypothesis(
    query:str
    )->str:
        assert query != None
        hypothesis = hypothesis_dict(query=query)
        completion = client.chat.completions.create(
        model="gpt-4",
        messages=[
        {"role": "user",
        "content":str(hypothesis)}
                ]
                                                    )
        return str(completion.choices[0].message)
def get_prompt(
    ref_list:list,
    query:str
    )->str:
        assert ref_list != None
        refs_template = ""
        for i,item in enumerate(ref_list):
            content = item['Content']
            refs_template += "・{}".format(content) + "\n"
        prompt = prompt_dict(
            refs_template=refs_template,
            query_text=query
            )
        return prompt


def hypothesis_dict(
    query:str
    ) -> str:
        prompt = """
        背景情報を与えるプロンプト
                 """
        prompt.format(query=query)
        return prompt
def prompt_dict(
    refs_template:str,
    query_text:str
    )->str:
    prompt = """
    欲しい回答を与えるプロンプト
            """
                
    return prompt.format(ref_template=refs_template,input_text=query_text)

もしかしたら get_Hypothesis使ってないかもです
API周り友達に任せちゃってたので聞いてみます

Lambdaのレイヤー設定について

コードはこれでOKですがライブラリが入ってないのでこれを追加します
ここがいちばんの難関なんですわ
チームで作る場合はできる人に任せた方がいいです

参考にしたのは記事の以下の部分です

(参考情報) Boto3のバージョンが古い場合の対応方法

基本的には

  1. ローカル環境で仮想環境を用意する
  2. ディレクトリを作成
  3. 2で作成したディレクトリにライブラリを入れる
  4. 3のディレクトリをpythonという名前に変える
  5. 4のディレクトリをzipする
  • 4でpythonという名前にしないとレイヤーで読み込んでくれない
  • zipファイルは大きすぎるとuploadできないのでレイヤー3つぐらいに分けた方がいいです
    • 特にopenaiデカすぎなので

installするものリスト

line-bot-sdk
boto3
autopep8==1.6.0 
certifi==2021.10.8 
charset-normalizer==2.0.7 
click==8.0.3 
et-xmlfile==1.1.0 
idna==3.3 
itsdangerous==2.0.1 
Jinja2==3.0.2 
MarkupSafe==2.0.1 
numpy==1.21.3 
openai==1.3.6 
openpyxl==3.0.9 
pandas==1.3.4 
pandas-stubs==1.2.0.35 
pycodestyle==2.8.0 
python-dateutil==2.8.2 
python-dotenv==0.19.2 
pytz==2021.3 
requests==2.26.0 
six==1.16.0 
toml==0.10.2 
tqdm==4.62.3 
langchain==0.0.344
tiktoken==0.5.1

もしかするといらんもの入ってるかも

image.png

互換性のランタイムの部分はpython3.9を選ぶようにしてください!

LINEBotの設定

ここまですると話長くなってしまうので以下の記事を参考にしてください
あんまり難しくないです
APIGatewayの設定も必要です!

LINEとの接続もできたら完成です!
お疲れ様でした!

まとめ

AWSを用いて自社ドキュメントを学習させたLINEbotを作ってみました!
今回はS3に保存したデータを用いましたが
他にもデータソース選べるので無限にアイデアが膨らんできますね

ただ一点注意事項として
お金まあまあ使うので気をつけてください

Xもやってるのでぜひフォローお願いします

ご読了ありがとうございました!

5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?