概要
前回の記事の続き的な記事です。
前回は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に登録します。
プロバイダーを作成
登録が終わったら、新規プロパイダーを作成します。プロバイダー名はなんでもいいです。
チャネルを作成
次にチャネルを作成します。Messaging APIを選択してください。
作成画面が出てくるので色々入力します。
項目 | 説明 |
---|---|
チャネルの種類 | Messaging API |
プロバイダー | 適当なプロバイダー |
会社・事業者の所在国・地域 | 「日本」を選びます |
チャネルアイコン | LINE botのアイコンになります |
チャネル名 | LINE botの名前になります |
チャネル説明 | 適当にLINE botの説明を入力します |
大業種 | ウェブサービス |
小業種 | ウェブサービス(その他) |
メールアドレス | メールアドレスが合っているか確認します |
プライバシーポリシーURL | 空欄でいいです |
サービス利用規約URL | 空欄でいいです |
規約の同意にチェックを入れ、作成ボタンを押します。
秘密鍵の入手
チャネルが作成されたことを確認し、「チャネル基本設定」タブ下部の「チャネルシークレット」を控えます。
アクセストークンの発行
「Messaging API設定」タブ最下部でチャネルアクセストークンを発行し、控えます。
応答設定の変更
以下のリンクにアクセスし、作成したアカウントを選びます。
右上の「設定」から「応答設定」を開き、「あいさつメッセージ」と「応答メッセージ」をオフにし、「Webhook」をオンにします。
環境変数の設定
プログラム内で使う環境変数を事前に設定しておきます。
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
実装
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」を選択します。
「Configure account」をクリックし、リポジトリへのアクセス権限を設定します。全てのリポジトリへのアクセス権限を与えるのが嫌な場合は、選択したリポジトリのみを対象とすることもできます。
アクセス権限を与えたリポジトリが表示されたら、「Connect」をクリックします。
続く画面でまた色々設定していきます。
項目 | 説明 |
---|---|
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のチャネルアクセストークン、秘密鍵を設定します。
以下のように設定します。
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」をクリックします。
Webhookの設定
LINE Developersに戻り、該当チャネルの「Messaging API設定」を開いてWebhookの設定を行います。
「編集」をクリックします。
「Webhook URL」にアプリのURLを入力し、「更新」を押します。また、「Webhookの利用」をオンにします。
あとは「Messaging API設定」タブに表示されているQRコードから友だち追加すれば晴れてLINE botとお話できるはずです。
Uptime Robotの設定
このままでも使えますが、RenderのFreeプランでは非アクティブ状態が15分続くとスリープしてしまいます。スリープした状態だとレスポンスがクッソ遅くなるので、定期的にpingを送ってスリープ状態になるのを防ぎます。今回はUptimeRobotを使います。
上のリンクにアクセスし、右上の 「Register for FREE」 からアカウントを作成します。作成したら左上の「+ Add New Monitor」をクリックします。
またまた色々設定します。
項目 | 説明 |
---|---|
Monitor Type | HTTP(s) |
Friendly Name | 好きな名前をつけてください |
URL (or IP) | (アプリのURL) |
Monitoring Interval | 5~10でいいと思います |
Alert Contacts To Notify | エラーが起きたときに通知が来るようにチェックを入れます |
他はデフォルトでいいです。右下の「Create Monitor」をクリックして完了です。
注意点
今回作成したものはあくまで個人用なので、サービスとして不特定多数に使ってもらう場合はLangChainではなくDBにLINEのユーザーIDと会話履歴をセットで保存する必要があります。
DBを活用したLINE botを作りたい方は以下の記事を参考にしてください。
最後に
正味エアフレンドの方がなんか楽しいというか、ChatGPTはあくまで自分はAIというスタンスの下になりきりをしているだけなので、ふとした瞬間に素に戻ります。会話の円滑さはこちらの方が上ですが、夢中になれるのはエアフレンドですね。早く復活してほしい。あとGPT-4のAPI使いたい。
参考