7
2

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.

Google Cloud Functionsで、LangchainのMemory機能つきchatGPTを用いたLINEチャットボットを作る

Last updated at Posted at 2023-07-01

LINE用チャットボットを、Google Cloud Functionsで作りました。chatGPT使ってます。

TL;DR

  • LINE用チャットボットをGoogle Cloud Functionsを使って作りました。
  • chatGPT(Langchain)を、会話生成に使っています
  • LangchainのMemory機能を使うことにより、会話履歴を簡単に実装(ユーザーごととかの処理はいれてない)

前提

  • Google Cloud Functionsの第1世代を利用。北米でつくると、いろいろ無料になりやすいらしい(無料枠上限あり)
  • 以下のKeyは、環境変数にて設定。
    • OPENAI_API_KEY:openAIのKey
    • LINE_ACCESS_TOKEN:LINEのアクセストークン
    • LINE_CHANNEL_SECRET:LINEのシークレットキー
  • 設定済みのLINEチャットボットがある。

コード

会話生成部分は別ライブラリにして、pythonのクラスとかいうのを作って、main.pyから呼び出しています。
以下の3ファイルで構成

  • main.py:LINEとのやりとり
  • callGPT.py:会話文生成
  • requirements.txt

chatGPT部分

以下が、会話生成部分です。
プロンプトは長いので割愛。LangChainを使って、お手軽に会話履歴を実装してます。今まで、GAS使っていたときは、環境変数を使って雑に独自実装して、無理やり文脈把握させていたので、ずいぶんと楽に実装できました。(GAS版でも、それなりにいい感じに動きましたが)

callGPT.py
import openai
import os
from langchain.memory import ConversationBufferWindowMemory
from langchain.llms import OpenAI
from langchain.chains import ConversationChain
from langchain.prompts import PromptTemplate


template="""\
あなたはChatbotとして、かわいい猫のロールプレイを行います。
以下の制約条件を厳密に守ってロールプレイを行ってください。 

制約条件: 
* Chatbotの自身を示す一人称は、ねこです。 
* Chatbotの名前は、ねこです。 
* ねこは2歳の雄です。 
 
~中略~

{chat_history}
human: {input}
AI:"""

MEMORY_KEY="chat_history"
INPUT_KEY="input"

prompt = PromptTemplate(
    input_variables=[MEMORY_KEY, INPUT_KEY], template=template
)

def env_vars(key,default=None):
    return os.environ.get(key, default)

class ConversationBufferWindowMemoryClass:
    """会話文生成クラス
    """
    def __init__(self, model="gpt-3.5-turbo", temperature=1, max_tokens=4000, top_p=1, frequency_penalty=0.0, presence_penalty=0.6, k_num=5, v_setting = False):
        
        # get a chat completion from the formatted messages
        self.conversation_with_summary = ConversationChain(
            llm=OpenAI(temperature=temperature,
                       model_name=model,
                       max_tokens=ax_tokens,
                       frequency_penalty=frequency_penalty,
                       presence_penalty=presence_penalty
                       ), 
            prompt=prompt,

            # We set a low k=2, to only keep the last 2 interactions in memory
            memory=ConversationBufferWindowMemory(k=self.k_num,memory_key=MEMORY_KEY), 
            verbose=v_setting
        )      
        
    def call_gpt(self,message=None):
        response = self.conversation_with_summary.predict(input=message)
        return response

ConversationBufferWindowMemoryClassの使い方は、こんな感じ。

    instance = ConversationBufferWindowMemoryClass(v_setting = True)
    response = instance.call_gpt("こんにちわ")
    print(response)
    instance = ConversationBufferWindowMemoryClass(v_setting = True)
    response = instance.call_gpt("ねこじゃらしで遊ぼう")
    print(response)

main.py

LINEとやりとりするとこです。ちなみに、main関数がエントリポイントになるので、Google Cloud Functionsのエントリポイントはmainとなります。

main.py
import os
import json
from callGPT import ConversationBufferWindowMemoryClass,env_vars
from flask import abort
from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage
)

LINE_CHANNEL_ACCESS_TOKEN = env_vars("LINE_ACCESS_TOKEN")
LINE_CHANNEL_SECRET = env_vars("LINE_CHANNEL_SECRET")

line_bot_api = LineBotApi(LINE_CHANNEL_ACCESS_TOKEN)
handler = WebhookHandler(LINE_CHANNEL_SECRET)

def create_GPT_instance():
    return ConversationBufferWindowMemoryClass()
instance = create_GPT_instance()

def main(request):
    signature = request.headers['X-Line-Signature']
    body = request.get_data(as_text=True)

    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        abort(400)
 
    return 'OK'

@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    response = instance.call_gpt(event.message.text)

    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=response)
        )

ポイントは、ConversationBufferWindowMemoryClassのインスタンスのグローバル変数化です。ストレージを使った過去会話の管理をしていないので、LangChainのMemory機能だのみの会話履歴管理してます。なので、グローバル変数としてインスタンス作らないとだめなのですが、直接インスタンスをつくるのではなく、create_GPT_instance関数として定義して呼び出さないと、Deployのときにこけるようです。

エラーがでるのは、エントリポイントがらみかなと、ぼんやり想像してます。

このグローバル変数関連の話は、以下サイトが助かります。
https://cloud.google.com/functions/docs/bestpractices/tips?hl=ja#use_global_variables_to_reuse_objects_in_future_invocations

requirementsの設定

requirements.txt
openai
line-bot-sdk==2.4.2
langchain

webhook関連のライブラリがimportできないとか、langchain.chainsライブラリまわりのエラーがでたりしたので、バージョン制限をつけたりはずしたりしたら直りました。
現時点では、これで動いているけど、将来的に動く保証が全然ない。全部ちゃんとバージョン付けるべき。

デプロイ

あとは、LINEから上記プログラムのAPIをたたいて、chatGPTに会話文を生成させられるはず。やることは、以下あたりか。

  • Google Cloud Functionでデプロイ
  • LINEの設定画面のwebhookに、トリガーURLを設定

所感

・Langchain便利だけど、openaiライブラリそのもの使う方がわかりやすい気がしてきました。
・ライブラリの更新が早いので、昨日作ったプログラムが、今日、新ライブラリのバグに引っかかってうごかないということがあります。バージョンの設定は大事ですね。
・今回は、第1世代つかっていますが、第2世代の方がコスパよさそう。そのうち第2世代バージョンをつくれたら作りたい。

過去の関連記事

参考にしたページ

以下、参考にいたしました。ありがとうございました。
https://github.com/line/line-bot-sdk-python
https://note.com/strictlyes/n/nbeab033cdecb
https://github.com/hwchase17/langchain

以上

7
2
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
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?