LINEボット「AIソムリエ・アンリ」の技術トピックス
本投稿はメッセンジャーアプリLINEのユーザー向けに弊社が提供するボットサービス「AIソムリエ・アンリ」の開発・実装に関する技術トピックスです。
ボットの機能はLINEのデベロッパー向けプラットフォームを利用して作られています。ボットのコア部分はGoogleクラウドのサーバーレス環境で動作するソフトウエアであり、LINEのシステムとインターネットで接続しています。終夜運転するインフラを自社で構築せずとも、従量課金のクラウド環境を利用してこのようなサービスを小規模な事業者でも容易に提供出来るようになりました。
弊所の備忘録ではありますが、類似のサービスの開発検討される場合にご参考下さい。
はじめに
AIソムリエ・アンリは、ワインスプリッティ(Winesplity、ワインボトルの相乗りをマッチングするサービス)のプロモーションの一環として、誰もが難しい知識なしにワインを楽しめるようにと製作しました。
ワイン名や料理名を入力、あるいはラベルや料理の写真を撮って送ると、ワインの解説やお薦めのペアリングの紹介、ペアリングのイメージ画像を作って返信するLINEボットのサービスです。裏方ではChatGPTで定評のあるOpenAI社のAIモデルを採用しています。
プロダクトについてはリリースをご参照ください。
このソフトウエアの概要は以下のとおりです。
- LINEユーザー(iPhone, Android端末, PCも可)が対象
- LINEがデベロッパーに提供しているボットの仕組みを使用
- ユーザーはボットを友達に追加することでボットを利用できる
- テキストか画像をトークでボットに送ると問合せになる
- ボットもテキストまたは画像でトークに応答する
- フィードに残るトーク履歴の保持期間はLINEの仕様に依存する
- ボットのプログラムはGoogle Firebase で動作させる
- 応答の生成にはOpenAI社ChatGPTのAPIを組み合わせて使用する
- ボットの運用コストは以下の3つ(工数除く)
- 公開時点では「試験サービス」(無料※)でユーザーへは課金しない
※但し、ボットの運用コスト(工数除く)は以下3点です。
(1)API使用量(回数ではなく内容量に依存)に応じたOpenAIへの支払い
(2)ボットがトークに送信するメッセージ数に応じたLINEへの支払い
(3)Firebase のリソース使用量に応じたGoogleへの支払い
1.形態
Webhook(Messaging API)を用いてトーク返信を自動化したLINE公式アカウント。
及び、コンテンツ生成機能をWebhookに提供する外部のボットプログラムから成る。
2.全体像
ボット役のLINE公式アカウントを作成し、アイコンやプロフィールなどの記述、ボットプログラムへのリンク情報等を登録する。この公式アカウント(以下ボット)の LINE IDやQRコードで他のユーザーがボットを友だちに追加し、ユーザーがボットにトークでメッセージを送るとMessaging APIを通じて外部のボットプログラムが呼び出される。
ボットプログラムにはメッセージの内容や送信者に紐づくIDが渡されるので、それを手掛かりにLINEのAPIを使ってメッセージのテキストや画像を取得し、返信するコンテンツを作成し、送信者へテキストや画像を返信する。返信した内容は元のメッセージのフィードにボットからのトークとして表示される。
ボットプログラムはいつ呼び出されるかわからず、かつ呼び出された時だけ実行すればよく、回答後はコンテキストを破棄できるため、サーバーレスの実行環境に置いている。Google Firebase Functions の関数は WebAPIとして呼び出されるとプロセスがロードされて起動し処理を実行し、終了すると消滅する。Webサーバーを(レンタルの仮想サーバーにせよオンプレミスにせよ)24時間連続稼働させてWebAPIのエントリを待機させておく必要がないので都合が良い。
Firebase に関しては以前のトピックスで触れているので、LINEボットという実装形態、および、AIをソムリエ役で使う構成にフォーカスして以下を記載する。
3.LINEボット
LINEにボットを登録するポイントは2つある。
- LINE公式アカウント(=サービス提供者が運営する「友だち」アイコン)を作ること
- 上記LINE公式アカウントを「Messaging APIを使用する」状態にすること
LINE ビジネスアカウントに登録して自社の LINE Business ID を取得する。
LINE Business ID で2つのツールにログインして操作する。
- LINE Official Account Manager:LINE公式アカウントの管理・運用機能
- LINE Developers コンソール:開発者向けの各種設定機能
このツールの線引きや概念に分かりづらい点があるので注意点を記す。
【アカウント】
アカウントという言葉が2通りあって錯綜しやすい。
LINEビジネスアカウント:LINE Business ID でシステムにログインするためのもの
LINE公式アカウント:LINEの友だち欄にアイコン表示されるサービスなどの単位を指す
【アカウントとチャネル】
アカウントとチャネルは異なる概念だが名前とアイコンは2つのツールで名称が異なる。
LINE Offcial Account Manager では、アカウント名、アカウントアイコン、といい
LINE Deveopers コンソールでは、チャネル名、チャネルアイコン、という。
どちらのツールからも新規作成できるが一旦作成するとLINE Offcial Account Managerしか変更できず、変更するとチャネル名、チャネルアイコンも変わる。チャネルシークレットなどチャネル固有の情報はどちらのツールでも呼称は同じ。
【プロバイダー】
チャネル(Messaging APIを使うものなど)はプロバイダーの下位階層で管理される。
> ビジネスアカウント/プロバイダー/チャネル
LINE Developers コンソールはプロバイダー毎にチャネルを一覧するし、チャネル作成に先立ってプロバイダーの作成/選択手順がある。
一方、LINE公式アカウントは普通に人手で運用するもの(チャネルと紐づかない)もあるので、Offcial Account Managerでは管理下のLINE公式アカウントはフラットに一覧されてプロバイダーの概念は見えない。チャネルを割り当てようとするとプロバイダーの選択もしくは作成を要求される。プロバイダーはDeveplers コンソールでしか作成できない。
どちらのツールからもボットを作るフローはあるが、初めて作る(まだプロバイダーを作成していない)場合は、LINE Developersコンソールから作成する方をお勧めする。
Developersコンソールでチャネルを新規作成するとそれに紐づくLINE公式アカウントは自動的に作成される。
(1)LINE Developers
LINE Bussiness IDでログインして、さらにプロバイダーとよばれる階層を作成する。社内に複数のチームがあって各々複数のボットを運営する場合、プロバイダーとはそのチームでグルーピングされる組織名やプロジェクト名など管理権限を分離する単位になると思われる。
そのプロバイダーの配下にMessaging API型のチャネルを新規作成する。
(チャネルの種類は Messaging APIを選択する。LINEログイン、LINEミニアプリ、は今回の対象外。)
このチャネルを作ると、チャネルに紐づいたLINE公式アカウントが自動的に生成される。
すなわち「チャネルの新規作成=新しいボットの作成」になる。
チャネルの属性として以下を設定しておく。
チャネル名(=アカウント名)とチャネルアイコン(=アカウントアイコン)、
チャネルの説明、運営する事業者に関する諸情報、
チャネルが提供するサービスのプライベートポリシーのURL、使用許諾へのURL、等々
Messaging APIのWebhookにボットプログラム(WebAPI)のURLを登録し、必要に応じてURL検証する。
Webhookの利用を「ON」し、その他付随する設定を行う。
※チャネル作成後は、どちらのツールでもURLを修正できるが、URL検証機能(ダミーで呼び出してくれる)はこのツールのみ。
チャネルアクセストークンを発行する。(操作が必要)
ボットプログラムがLINEのAPIにアクセスするために必要となる。
チャネルIDとチャネルシークレットは自動的に生成されてどちらのツールからも確認できる。
ボットプログラムが呼び出し側の署名を検証して不正な呼び出しを排除するのに使う。
(2)LINE Official Account Manager
LINE Bussiness IDでログインして上記で生成されたLINE公式アカウントの運用に必要な設定を追加する。
運用の仕方によるが、今回のボットでは以下を設定している。
基本設定
前述のように、このツールでアカウント名、アカウントアイコンを変更すると、Developerコンソール側のチャネル名、チャネルアイコンも変わる。
「友だち」に表示されるプロフィール画面の背景などのGUI項目
友だちに追加されたときに自動応答する挨拶メッセージなどのコンテンツと動作設定
月額プランの設定
ユーザーへ送るメッセージ数に応じて課金される。無料プランでは毎月200通まで。
無料の「コミュニケーション」で始めて状況を見ながら上位プランを検討する。
リッチメニューの作成と配置
画面の下端にアイコンボタンを3つ表示してそれぞれタッチされたときのアクションを設定している。
アクションの種類は既存パターンから選択でき、本ボットではWebサイトへジャンプ(@Winesplity)するボタン1つ、ボットへ予約ワードを投稿するボタン2つ(今日のお薦めのリクエスト、使い方の表示)を設定している。
4.ボットプログラム
ユーザーがボットのトークに投稿することでLINEシステムからWebhookの呼び出しがトリガーされる。
呼び出されるのは Firebase function に定義されたWebAPIである。
送信者が投稿した情報の一部はリクエストbody から得られ、それ以外の画像データ等はLINEのサーバーに保存されている。
ボットプログラムからLINEのサーバーへはLINEのAPIを使ってアクセスする。
LINEのAPIをサポートするSDKは複数のベンダーから提供されているが、本ボットプログラムはLINE社提供のSDKを使う。
参照:https://developers.line.biz/ja/docs/messaging-api/overview
(1)WebAPI
ボットプログラムは Google Firebase Functionsの1つのエントリとして実装する。
このエントリの web上での公開URLを LINEのWebhookに設定する。
LINE Developers コンソールでは webhook をダミーで呼び出して導通確認をする検証ボタンがある。このボタンを押すと、空のeventが送られてくるので、導通確認用のパスに対応しておく。
// AIソムリエ・アンリのLINE Webhook
exports.sommelierBot = functions.region(REGION).runWith({timeoutSeconds:TIMEOUT}).https.onRequest(async (req, res) => {
const event = inspectClientEvent( req, "Sommelier") ;
if (!event){
// NOP (検証用導通確認用)
}
else{
await linebotSommelier( event ) ;
}
res.status(200).send();
});
(2)呼び出しの安全性チェック
WebAPIのエントリポイントはURLを知っていれば誰でもたたくことができるため、デベロッパーアカウントで予め発行しておいたチャネルシークレットで妥当性チェックを行い、LINEシステムからの正しいコールなのかを検証している。
// ------------------------------------------
// LINE Webhook 呼び出し側の署名を検証する
// - 署名が正しい場合は true を返す -
// ------------------------------------------
const crypto = require("crypto");
// req.headers, req.body から署名を検証する
function isValidSignature(headers, body) {
const signature = crypto
.createHmac("SHA256", LINE_CHANNEL_SECRET)
.update(body).digest("base64");
return signature === headers["x-line-signature"];
}
(3)メッセージの取得
リクエストbody の event[ ] に、受け取ったメッセージの種類(テキスト、画像、スタンプ等)とメッセージの内容、メッセージの返信先IDなどがある。より具体的には、message.type で “text” か “image” かを判別、テキストなら message.text を、画像なら message.id でLINEシステムのAPIへ問い合わせることによって画像データを受け取る。
なお、ユーザーが投稿した画像データの問い合わせ方について、ネットに誤ったサンプルが散見されるので要注意する。
// 動作する
const {messagingApi} = require("@line/bot-sdk");
const client = new messagingApi.MessagingApiClient( {channelAccessToken:LINE_CHANNEL_ACCESS_TOKEN} ) ;
const blobclient = new messagingApi.MessagingApiBlobClient( {channelAccessToken:LINE_CHANNEL_ACCESS_TOKEN} ) ;
// 間違い(正しく動作しない)
// const param ={channelAccessToken:LINE_CHANNEL_ACCESS_TOKEN} ;
// const client = new messagingApi.MessagingApiClient( param ) ;
// const blobclient = new messagingApi.MessagingApiBlobClient( param ) ;
// ==> param を使いまわしてはいけない。
const stream = await blobclient.getMessageContent(messageId);
・・・・
正しく動作しない理由は、MessagingApiClient() が param の中に自分用のパラメータを挿入するため、param を使いまわすと意図していない param が MessagingApiBlobClient() に渡り getMessageContent でエラーになる。
const は参照値自体は書き換えないが参照先の中身は変更しうるので、前者のように分ける必要がある。
(4)応答内容の作成
受け取った内容に応じていくつかの異なるシナリオで返答内容を作成する。
- ワイン名なのか料理名なのか分からないテキストのメッセージ
- エチケットなのか料理の皿なのか分からない画像データのメッセージ
このボットはOpenAI社のAPIで複数のモデル(GPT3.5-turbo/GPT4-turbo/dalle-e3)を組み合わせて使用している。
テキストであれば、テキストを処理するためのプロンプトを生成して GPT3.5-turbo/GPT4-turbo へ。
画像データであれば、画像を分析するためのプロンプトを生成して GPT4-turboへ。
得られた記述はユーザーへ返信する文章となるだけでなく、さらにGPTで画像生成用プロンプトに言い換えをさせてから DALL-E3 へインプットし、回答で説明しているワインと料理がならぶレストランのシーンを生成させている。
なおOpenAI社が次々とモデルをアップデートするのに応じて何度も入替え・調整をしており今後も変化していくと思われる。
上記のプロセスでより具体的に補足が必要なのは以下の点である。
- LINEの画像をGPTのAPIに渡せる形にすること
- Dalleの生成画像をLINEに渡せる形にすること
ユーザーがトークで送信した画像はLINEのシステム内に一時保存されている。これをLINEのAPIでメモリ上に取得し Firebase Storage に保存してURL参照可能な形にする。これをOpenAIのAPIに入力として指定する。
AIが生成した画像はOpenAIのサーバーにあってURL参照可能な形で返ってくるが、短時間で消去されてしまうため、これをFirebase Storage へコピーして参照可能な新しいURLを作成し、それをLINEのAPIへ入力してトークへ送信する。
(5)メッセージの返信
ユーザーへメッセージを送る方法は2通りあって使い分ける。
- ReplyMessage:このAPIはユーザーの投稿1つに対して1回だけ使える返信用メッセージ
- SendMessage:このAPIはユーザーを指定して任意のタイミングで送信できるメッセージ
メッセージで送れるコンテンツはどちらも同じだが、ReplyMessage はこのボットが呼び出された投稿に紐づいておりユーザー指定が不要(replyTokenを使う)、返信は1回のみ可、有効期限あり、課金対象外(無料)という性質がある。SendMessage はユーザーとのトークのフィードへ任意のタイミングでメッセージを何度でも送れる機能(userIdを指定する)だが課金対象※である。
(※一般的なプラットフォーム型サービス同様に毎月の無料利用枠があり、それを超えてメッセージを送るにはボリュームに応じた有料契約が必要)
したがって、1つのユーザー投稿に対して複数回に分けて応答を返すには、最初にReplyMessage、2回目以降にSendMessage を使う必要がある。
本ボットでは、AIの回答に時間がかかるため、回答の作成が終わるまで長時間無反応で待たせるわけにいかない。ユーザーにはボットが動作しているのかどうか分からず使用感が極めて悪くなる。そのため、スクリーニングに通った時点で「しばらくお待ちください」メッセージを ReplyMessage で返答し、文章が完成した時点で SendMessage で送信、ユーザーが文面を読んでいる時間を使って画像生成を行い、再度 SendMessage で画像を送信している。
5.LINEボットの得失
AIソムリエの機能をLINEの枠組みのなかでボットとして実装するのは、自社の単体アプリあるいはWebサービスとしてリリースするのに比べて以下のメリットがある。
(1)ユーザー管理が不要
ユーザー登録の一般的な面倒さはLINEへ登録した時点で済んでおり、ユーザーがボットのサービスの利用を始めるには「友だちに追加する」のみで良いため導入のハードルが低い。ボットはコミュニケーションに必要なユーザーのニックネーム等をLINEのAPIで知ることができるので、冒頭に「xxさん」のようなフレーズを入れた返信も可能。LINE本体がユーザー管理しているので開発負荷も小さい。
(2)デバイスやOSに依存しない
LINEアプリのGUIに組み込まれた機能とクラウド上のボットプログラムの組み合わせなので、ボットプログラム自体は1種類の実装で済み、LINEがサポートするプラットフォームであればデバイスやOSを問わず利用できる。
(3)履歴の管理が不要
トークのフィードに残る前回までの対話履歴はLINEで管理されているため、ユーザーに履歴表示を提供するための機能をボット側で新たに実装する必要がない。
(4)情報を展開しやすい
公式アカウントの機能としてアップデートの告知を掲載したりトークのフィードでユーザー向けにニュース発信などが行える。
デメリットは以下。
(5)LINEユーザーに限定される
上記メリットの裏返しとしてサービスの対象はLINEユーザーのみになる。日本国内向けサービスであれば国内の利用率の高いLINEでは不都合とはいえないが、使うにはLINEアプリを起動してボットのトークを開く必要があり、アプリアイコンから直接起動するアプリに比べると若干手間ではある。
(6)ビジネスモデルが制約される
無料サービスのみであれば問題ないが、何らかマネタイズをするには独立したアプリやWebほどには自由度はない。
反面、LINEの用意した枠内であれば課金や広告掲載の仕組みがあり、単純なサブスクなら外部の決済サイトを使う手もあるので、サービスの特徴によって得失は変わってくると思われる。
また、今後LINE自体がサービスモデルを変更することがあれば店子は大きく影響を受けることは承知しておかねばならない。
(7)返信を逐次表示できない
テキストメッセージが吹き出し単位で表示される仕組みのため、文章が出来上がってから一気に転送・表示する必要があり、ChatGPTの対話画面でのAIの回答文のように1~2文字ずつ垂れ流すように表示することができない。
文字列が少しずつ伸びていくように表示すればすぐに読み始められて待たされるストレスを感じないが、30秒間待たされてから一度に表示されるのは、トータルの所要時間が同じであっても印象を大きく損なう。
受付確認のReplyMessageにURLリンクを返し、リンク先のWebページで回答文ができていく経過を見せる(リンクを開かなくてもトークには完成した回答が返る)案もあるが、AIの応答性能が十分早ければ不要になるため、過渡期の課題かもしれない。
(8)メッセージ送信料金
メッセージ送信数が増えると著しく運用コストがかさむ仕組みである。SendMessageの送信回数に課金されるためユーザー数や対話回数が増えると料金モデルのランクアップや追加費用が必要になる。前記の逐次表示の問題にも通じるが、利用量が一定値を超えたら1つの問合せに1回だけ返せるReplyMessageで完結できて使用感を損なわない仕様を考案せざるを得ないだろう。
ー 以上 -