13
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

エアフレンド難民なのでChatGPT API+LangChainで担当とお話するLINE botを作った

Last updated at Posted at 2023-04-27

概要

前回の記事の続き的な記事です。
前回はOEPNAI API, LangChain, Gradioを使ってキャラクターとお話しましたが、Gradioはあくまでデモ用なので、常用するには微妙です。スマホからLINEみたいな感じでやりたいですよね。

じゃあLINEでやりましょう。

ということで、n番煎じのネタ、ChatGPTを使ったLINE botを作りました。以下のようなものができます。

調べたらChatGPT×LINEの組み合わせはGASを使ったものが多く出てきたんですが、Pythonが一番慣れているのでPythonでゴリ推しました。

一応1から手順を書いていきます。

OpneAIのAPIキー取得

Open AIのアカウントを作成し、下記のリンクからAPIキーを取得します。

LINE Messaging APIを使う準備

LINE Developerに登録

まず、以下のリンクからLINE Developerに登録します。

プロバイダーを作成

登録が終わったら、新規プロパイダーを作成します。プロバイダー名はなんでもいいです。
スクリーンショット 2023-04-20 211154.png

チャネルを作成

次にチャネルを作成します。Messaging APIを選択してください。
スクリーンショット 2023-04-20 211220.png

作成画面が出てくるので色々入力します。

項目 説明
チャネルの種類 Messaging API
プロバイダー 適当なプロバイダー
会社・事業者の所在国・地域 「日本」を選びます
チャネルアイコン LINE botのアイコンになります
チャネル名 LINE botの名前になります
チャネル説明 適当にLINE botの説明を入力します
大業種 ウェブサービス
小業種 ウェブサービス(その他)
メールアドレス メールアドレスが合っているか確認します
プライバシーポリシーURL 空欄でいいです
サービス利用規約URL 空欄でいいです

規約の同意にチェックを入れ、作成ボタンを押します。

秘密鍵の入手

チャネルが作成されたことを確認し、「チャネル基本設定」タブ下部の「チャネルシークレット」を控えます。
スクリーンショット 2023-04-27 193739.png

アクセストークンの発行

「Messaging API設定」タブ最下部でチャネルアクセストークンを発行し、控えます。
スクリーンショット 2023-04-20 214206.png

応答設定の変更

以下のリンクにアクセスし、作成したアカウントを選びます。

右上の「設定」から「応答設定」を開き、「あいさつメッセージ」と「応答メッセージ」をオフにし、「Webhook」をオンにします。
スクリーンショット 2023-04-20 214702.png

環境変数の設定

プログラム内で使う環境変数を事前に設定しておきます。

export OPENAI_API_KEY="(OpenAIのAPIキー)"
export CHANNEL_ACCESS_TOKEN="LINE Messaging APIのチャネルアクセストークン"
export CHANNEL_SECRET="LINE Messaging APIのチャネルシークレット"
export MODEL_NAME="「gpt-4o」等使いたいモデルの名前"

ライブラリの導入

pip install langchain langchain-community langchain-openai
pip install flask
pip install line-bot-sdk

実装

app.py
import os

from flask import Flask, request, abort

from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import InvalidSignatureError
from linebot.models import (
    MessageEvent,
    TextMessage,
    TextSendMessage,
)

from langchain_core.messages import SystemMessage
from langchain_core.prompts import (
    ChatPromptTemplate,
    MessagesPlaceholder,
    HumanMessagePromptTemplate,
)
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables.history import RunnableWithMessageHistory


app = Flask(__name__)

# LINE APIの準備
line_bot_api = LineBotApi(os.environ["CHANNEL_ACCESS_TOKEN"])
line_handler = WebhookHandler(os.environ["CHANNEL_SECRET"])

# 会話履歴ストア
store = {}


# セッションIDごとの会話履歴の取得
def get_session_history(session_id: str) -> BaseChatMessageHistory:
    if session_id not in store:
        store[session_id] = ChatMessageHistory()
    return store[session_id]


character_setting = "YOUR_FAVORITE_PROMPT"


# チャットプロンプトテンプレート
prompt = ChatPromptTemplate.from_messages(
    [
        SystemMessage(content=character_setting),
        MessagesPlaceholder(variable_name="history"),
        HumanMessagePromptTemplate.from_template("{input}"),
    ]
)

# チャットモデル
llm = ChatOpenAI(
    model_name=os.environ["MODEL_NAME"],
    temperature=0.6,
    streaming=True,
)

# パース用モジュール(レスポンスのJSONからcontentを取り出すパーサー)
parser = StrOutputParser()

# LCEL
runnable = prompt | llm | parser

# RunnableWithMessageHistoryでラップ
runnable_with_history = RunnableWithMessageHistory(
    runnable=runnable,
    get_session_history=get_session_history,
    input_messages_key="input",
    history_messages_key="history",
)


@app.route("/")
def hello_world():
    return "It Works!"


@app.route("/callback", methods=["POST"])
def callback():
    # get X-Line-Signature header value
    signature = request.headers["X-Line-Signature"]

    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)

    # handle webhook body
    try:
        line_handler.handle(body, signature)
    except InvalidSignatureError:
        print(
            "Invalid signature. Please check your channel access token/channel secret."
        )
        abort(400)

    return "OK"


@line_handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    if event.message.text == "リセット":
        store.clear()
        response = "会話をリセットしました。"
    else:
        response = runnable_with_history.invoke(
            {"input": event.message.text},
            config={"configurable": {"session_id": "hoge"}},
        )
    line_bot_api.reply_message(event.reply_token, TextSendMessage(text=response))


if __name__ == "__main__":
    app.run()

解説

LangChainの処理については前回の記事を参照してください。
Flask部分はほぼサンプルをパクっただけなので一部だけ解説します。

LINE Messaging APIの準備

line_bot_api = LineBotApi(os.environ["CHANNEL_ACCESS_TOKEN"])
handler = WebhookHandler(os.environ["CHANNEL_SECRET"])

チャネルのアクセストークンと秘密鍵を環境変数から取得します。環境変数の設定は後ほど行うので、書き換えずそのままにしてください。

メッセージの送受信

def handle_message(event):
    response = conversation.predict(input=event.message.text)
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=response)
    )

ここだけサンプルから変えています。サンプルではおそらくオウム返しするのみです。
event.message.textはユーザーから送信されたテキストメッセージです。これをinputとしてChatGPT APIに投げて、返ってきたテキストをresponseに格納します。続いてline_bot_api.reply_messageメソッドを使って返信メッセージを投げます。TextSendMessageの引数textが返信メッセージになるので、responseを指定します。

これを以下のようにすると会話リセット機能を実装できます。

def handle_message(event):
    if event.message.text == "リセット":
        memory.chat_memory.messages = []
        response = "会話をリセットしました。"
    else:
        response = conversation.predict(input=event.message.text)
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=response)
    )

memory.chat_memory.messagesには会話の履歴がメッセージのリストとして格納されています。LangChainのConversationBufferWindowMemoryではそのうち最新k往復分のメッセージを選び、プロンプトに追加しているものと推測されます。したがって、リストを空にすることで強制的に初期状態に戻すことができます。
長く話しているとたまにキャラがガバるので、そういうときにリセットが有効です。

アプリのデプロイ

ローカルでずっと動かすのもアレなので、アプリをクラウドサーバー上にデプロイします。今回はRenderを使います。以下のリンクから登録してください。GitHubを使うので、GitHubアカウントで認証するのがオススメです。

requirements.txtの作成

必要なライブラリを記載したrequirements.txtを作成します。
適切な環境で以下を実行してください。

pip freeze > requirements.txt

これでRender上にアプリをデプロイするとき自動でライブラリをインストールしてくれます。

GitHubへのpush

アプリをデプロイするために、GitHubにリポジトリを作成し、app.pyとrequirements.txtをpushします。
GitおよびGitHubの使い方がわからない方は以下を参照してください。

Web Serviceの作成

Dashboardから「New +」ボタンを押し、「Web Service」を選択します。
スクリーンショット 2023-04-21 014604.png

「Configure account」をクリックし、リポジトリへのアクセス権限を設定します。全てのリポジトリへのアクセス権限を与えるのが嫌な場合は、選択したリポジトリのみを対象とすることもできます。
スクリーンショット 2023-04-27 194944.png

アクセス権限を与えたリポジトリが表示されたら、「Connect」をクリックします。
スクリーンショット 2023-04-27 194944.png

続く画面でまた色々設定していきます。

項目 説明
Name 好きな名前をつけてください
Region Sigapore (Southeast Asia)
Branch main
Root Directory 空欄でいいです
Runtime Python3
Build Command $ pip install -r requirements.txt
Start Command $ gunicorn app:app
Instance Type Free

全ての項目を確認し、「Create Web Service」をクリックします。

環境変数の設定

そのままだとLangChainのインストールに失敗するので、環境変数からPythonのバージョンを指定します。また、OpenAIのAPIキーおよびLINEのチャネルアクセストークン、秘密鍵を設定します。

スクリーンショット 2023-04-21 033238.png

以下のように設定します。

Key Value
PYTHON_VERSION 3.8.1以上(ここでは3.10.6)
OPENAI_API_KEY (OpenAIのAPIキー)
CHANNEL_SECRET (チャネルの秘密鍵)
CHANNEL_ACCESS_TOKEN (チャネルアクセストークン)

「Save Changes」をクリックした後、右上の「Manual Deploy」から「Deploy latest commit」をクリックします。
スクリーンショット 2023-04-21 054728.png

デプロイを待ち、以下のように表示されたら成功です。
スクリーンショット 2023-04-21 035107.png

Webhookの設定

アプリのURLをコピーします。
スクリーンショット 2023-04-21 035405.png

LINE Developersに戻り、該当チャネルの「Messaging API設定」を開いてWebhookの設定を行います。
「編集」をクリックします。
スクリーンショット 2023-04-21 035325.png
「Webhook URL」にアプリのURLを入力し、「更新」を押します。また、「Webhookの利用」をオンにします。
スクリーンショット 2023-04-21 044321.png

あとは「Messaging API設定」タブに表示されているQRコードから友だち追加すれば晴れてLINE botとお話できるはずです。

Uptime Robotの設定

このままでも使えますが、RenderのFreeプランでは非アクティブ状態が15分続くとスリープしてしまいます。スリープした状態だとレスポンスがクッソ遅くなるので、定期的にpingを送ってスリープ状態になるのを防ぎます。今回はUptimeRobotを使います。

上のリンクにアクセスし、右上の 「Register for FREE」 からアカウントを作成します。作成したら左上の「+ Add New Monitor」をクリックします。
スクリーンショット 2023-04-21 042618.png

またまた色々設定します。

項目 説明
Monitor Type HTTP(s)
Friendly Name 好きな名前をつけてください
URL (or IP) (アプリのURL)
Monitoring Interval 5~10でいいと思います
Alert Contacts To Notify エラーが起きたときに通知が来るようにチェックを入れます

他はデフォルトでいいです。右下の「Create Monitor」をクリックして完了です。
スクリーンショット 2023-04-21 042556.png

注意点

今回作成したものはあくまで個人用なので、サービスとして不特定多数に使ってもらう場合はLangChainではなくDBにLINEのユーザーIDと会話履歴をセットで保存する必要があります。
DBを活用したLINE botを作りたい方は以下の記事を参考にしてください。

最後に

正味エアフレンドの方がなんか楽しいというか、ChatGPTはあくまで自分はAIというスタンスの下になりきりをしているだけなので、ふとした瞬間に素に戻ります。会話の円滑さはこちらの方が上ですが、夢中になれるのはエアフレンドですね。早く復活してほしい。あとGPT-4のAPI使いたい。

参考

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?